Download - How to Create an Operating System
1. Introduction2. Introductionaboutthex86architectureandaboutourOS3. Setupthedevelopmentenvironment4. FirstbootwithGRUB5. BackboneoftheOSandC++runtime6. Baseclassesformanagingx86architecture7. GDT8. IDTandinterrupts9. Theory:physicalandvirtualmemory10. Memorymanagement:physicalandvirtual11. Processmanagementandmultitasking12. Externalprogramexecution:ELFfiles13. Userlandandsyscalls14. Modulardrivers15. Somebasicsmodules:console,keyboard16. IDEHarddisks17. DOSPartitions18. EXT2read-onlyfilesystems19. StandardClibrary(libC)20. UNIXbasictools:sh,cat21. Luainterpreter
TableofContents
HowtomakeanOperatingSystem
2
OnlinebookabouthowtowriteacomputeroperatingsysteminC/C++fromscratch.
Caution:Thisrepositoryisaremakeofmyoldcourse.ItwaswrittenseveralyearsagoasoneofmyfirstprojectswhenIwasinHighSchool,I'mstillrefactoringsomeparts.TheoriginalcoursewasinFrenchandI'mnotanEnglishnative.I'mgoingtocontinueandimprovethiscourseinmyfree-time.
Book:Anonlineversionisavailableathttp://samypesse.gitbooks.io/how-to-create-an-operating-system/(PDF,MobiandePub).ItwasbeengeneratedusingGitBook.
SourceCode:Allthesystemsourcecodewillbestoredinthesrcdirectory.Eachstepwillcontainlinkstothedifferentrelatedfiles.
Contributions:Thiscourseisopentocontributions,feelfreetosignalerrorswithissuesordirectlycorrecttheerrorswithpull-requests.
Questions:Feelfreetoaskanyquestionsbyaddingissues.Pleasedon'temailme.
YoucanfollowmeonTwitter@SamyPesseorsupportmeonFlattrorGittip.
ThegoalistobuildaverysimpleUNIX-basedoperatingsysteminC++,notjusta"proof-of-concept".TheOSshouldbeabletoboot,startauserlandshell,andbeextensible.
HowtoMakeaComputerOperatingSystem
WhatkindofOSarewebuilding?
HowtomakeanOperatingSystem
3Introduction
HowtomakeanOperatingSystem
4Introduction
Thetermx86denotesafamilyofbackwardcompatibleinstructionsetarchitecturesbasedontheIntel8086CPU.
Thex86architectureisthemostcommoninstructionsetarchitecturesinceitsintroductionin1981fortheIBMPC.Alargeamountofsoftware,includingoperatingsystems(OS's)suchasDOS,Windows,Linux,BSD,SolarisandMacOSX,functionwithx86-basedhardware.
Inthiscoursewearenotgoingtodesignanoperatingsystemforthex86-64architecturebutforx86-32,thankstobackwardcompatibility,ourOSwillbecompatiblewithournewerPCs(buttakecautionifyouwanttotestitonyourrealmachine).
ThegoalistobuildaverysimpleUNIX-basedoperatingsysteminC++,butthegoalisnottojustbuilda"proof-of-concept".TheOSshouldbeabletoboot,startauserlandshellandbeextensible.
TheOSwillbebuiltforthex86architecture,runningon32bits,andcompatiblewithIBMPCs.
Specifications:
CodeinC++x86,32bitarchitectureBootwithGrubKindofmodularsystemfordriversKindofUNIXstyleMultitaskingELFexecutableinuserlandModules(accessibleinuserlandusing/dev/...):
IDEdisksDOSpartitionsClockEXT2(readonly)BochVBE
Userland:APIPosixLibC"Can"runashellorsomeexecutables(e.g.,lua)
Chapter1:Introductiontothex86architectureandaboutourOS
Whatisthex86architecture?
OurOperatingSystem
HowtomakeanOperatingSystem
5Introductionaboutthex86architectureandaboutourOS
Thefirststepistosetupagoodandviabledevelopmentenvironment.UsingVagrantandVirtualbox,you'llbeabletocompileandtestyourOSfromalltheOSs(Linux,WindowsorMac).
Vagrantisfreeandopen-sourcesoftwareforcreatingandconfiguringvirtualdevelopmentenvironments.ItcanbeconsideredawrapperaroundVirtualBox.
Vagrantwillhelpuscreateacleanvirtualdevelopmentenvironmentonwhateversystemyouareusing.ThefirststepistodownloadandinstallVagrantforyoursystemathttp://www.vagrantup.com/.
OracleVMVirtualBoxisavirtualizationsoftwarepackageforx86andAMD64/Intel64-basedcomputers.
VagrantneedsVirtualboxtowork,Downloadandinstallforyoursystemathttps://www.virtualbox.org/wiki/Downloads.
OnceVagrantandVirtualboxareinstalled,youneedtodownloadtheubuntulucid32imageforVagrant:
vagrantboxaddlucid32http://files.vagrantup.com/lucid32.box
Oncethelucid32imageisready,weneedtodefineourdevelopmentenvironmentusingaVagrantfile,createafilenamedVagrantfile.Thisfiledefineswhatprerequisitesourenvironmentneeds:nasm,make,build-essential,grubandqemu.
Startyourboxusing:
vagrantup
Youcannowaccessyourboxbyusingsshtoconnecttothevirtualboxusing:
vagrantssh
ThedirectorycontainingtheVagrantfilewillbemountedbydefaultinthe/vagrantdirectoryoftheguestVM(inthiscase,UbuntuLucid32):
cd/vagrant
ThefileMakefiledefinessomebasicsrulesforbuildingthekernel,theuserlibcandsomeuserlandprograms.
Build:
Chapter2:Setupthedevelopmentenvironment
InstallVagrant
InstallVirtualbox
Startandtestyourdevelopmentenvironment
Buildandtestouroperatingsystem
HowtomakeanOperatingSystem
6Setupthedevelopmentenvironment
makeall
Testouroperatingsystemwithqemu:
makerun
ThedocumentationforqemuisavailableatQEMUEmulatorDocumentation.
Youcanexittheemulatorusing:Ctrl-a.
HowtomakeanOperatingSystem
7Setupthedevelopmentenvironment
Whenanx86-basedcomputeristurnedon,itbeginsacomplexpathtogettothestagewherecontrolistransferredtoourkernel's"main"routine(kmain()).Forthiscourse,weareonlygoingtoconsidertheBIOSbootmethodandnotit'ssuccessor(UEFI).
TheBIOSbootsequenceis:RAMdetection->Hardwaredetection/Initialization->Bootsequence.
Themostimportantstepforusisthe"Bootsequence",wheretheBIOSisdonewithitsinitializationandtriestotransfercontroltothenextstageofthebootloaderprocess.
Duringthe"Bootsequence",theBIOSwilltrytodeterminea"bootdevice"(e.g.floppydisk,hard-disk,CD,USBflashmemorydeviceornetwork).OurOperatingSystemwillinitiallybootfromthehard-disk(butitwillbepossibletobootitfromaCDoraUSBflashmemorydeviceinfuture).Adeviceisconsideredbootableifthebootsectorcontainsthevalidsignaturebytes0x55and0xAAatoffsets511and512respectively(calledthemagicbytesofMasterBootRecord(MBR),Thissignatureisrepresented(inbinary)as0b1010101001010101.Thealternatingbitpatternwasthoughttobeaprotectionagainstcertainfailures(driveorcontroller).Ifthispatternisgarbledor0x00,thedeviceisnotconsideredbootable)
BIOSphysicallysearchesforabootdevicebyloadingthefirst512bytesfromthebootsectorofeachdeviceintophysicalmemory,startingattheaddress0x7C00(1KiBbelowthe32KiBmark).Whenthevalidsignaturebytesaredetected,BIOStransferscontroltothe0x7C00memoryaddress(viaajumpinstruction)inordertoexecutethebootsectorcode.
ThroughoutthisprocesstheCPUhasbeenrunningin16-bitRealMode(thedefaultstateforx86CPUsinordertomaintainbackwardscompatibility).Toexecutethe32-bitinstructionswithinourkernel,abootloaderisrequiredtoswitchtheCPUintoProtectedMode.
GNUGRUB(shortforGNUGRandUnifiedBootloader)isabootloaderpackagefromtheGNUProject.GRUBisthereferenceimplementationoftheFreeSoftwareFoundation'sMultibootSpecification,whichprovidesauserthechoicetobootoneofmultipleoperatingsystemsinstalledonacomputerorselectaspecifickernelconfigurationavailableonaparticularoperatingsystem'spartitions.
Tomakeitsimple,GRUBisthefirstthingbootedbythemachine(aboot-loader)andwillsimplifytheloadingofourkernelstoredonthehard-disk.
GRUBisverysimpletouseMakeitverysimpletoload32bitskernelswithoutneedsof16bitscodeMultibootwithLinux,WindowsandothersMakeiteasytoloadexternalmodulesinmemory
GRUBusestheMultibootspecification,theexecutablebinaryshouldbe32bitsandmustcontainaspecialheader(multibootheader)inits8192firstbytes.OurkernelwillbeaELFexecutablefile("ExecutableandLinkableFormat",acommonstandardfileformatforexecutablesinmostUNIXsystem).
Chapter3:FirstbootwithGRUB
Howthebootworks?
WhatisGRUB?
WhyareweusingGRUB?
HowtouseGRUB?
HowtomakeanOperatingSystem
8FirstbootwithGRUB
ThefirstbootsequenceofourkerneliswritteninAssembly:start.asmandweusealinkerfiletodefineourexecutablestructure:linker.ld.
ThisbootprocessalsoinitializessomeofourC++runtime,itwillbedescribedinthenextchapter.
Multibootheaderstructure:
structmultiboot_info{
u32flags;
u32low_mem;
u32high_mem;
u32boot_device;
u32cmdline;
u32mods_count;
u32mods_addr;
struct{
u32num;
u32size;
u32addr;
u32shndx;
}elf_sec;
unsignedlongmmap_length;
unsignedlongmmap_addr;
unsignedlongdrives_length;
unsignedlongdrives_addr;
unsignedlongconfig_table;
unsignedlongboot_loader_name;
unsignedlongapm_table;
unsignedlongvbe_control_info;
unsignedlongvbe_mode_info;
unsignedlongvbe_mode;
unsignedlongvbe_interface_seg;
unsignedlongvbe_interface_off;
unsignedlongvbe_interface_len;
};
Youcanusethecommandmbchkkernel.elftovalidateyourkernel.elffileagainstthemultibootstandard.Youcanalsousethecommandnm-nkernel.elftovalidatetheoffsetofthedifferentobjectsintheELFbinary.
Thescriptdiskimage.shwillgenerateaharddiskimagethatcanbeusedbyQEMU.
Thefirststepistocreateahard-diskimage(c.img)usingqemu-img:
qemu-imgcreatec.img2M
Weneednowtopartitionthediskusingfdisk:
fdisk./c.img
#SwitchtoExpertcommands
>x
#Changenumberofcylinders(1-1048576)
>c
>4
#Changenumberofheads(1-256,default16):
>h
>16
#Changenumberofsectors/track(1-63,default63)
>s
Createadiskimageforourkernelandgrub
HowtomakeanOperatingSystem
9FirstbootwithGRUB
>63
#Returntomainmenu
>r
#Addanewpartition
>n
#Chooseprimarypartition
>p
#Choosepartitionnumber
>1
#Choosefirstsector(1-4,default1)
>1
#Chooselastsector,+cylindersor+size{K,M,G}(1-4,default4)
>4
#Togglebootableflag
>a
#Choosefirstpartitionforbootableflag
>1
#Writetabletodiskandexit
>w
Weneednowtoattachthecreatedpartitiontotheloop-device(whichallowsafiletobeaccesslikeablockdevice)usinglosetup.Theoffsetofthepartitionispassedasanargumentandcalculatedusing:offset=start_sector*bytes_by_sector.
Usingfdisk-l-uc.img,youget:63*512=32256.
losetup-o32256/dev/loop1./c.img
WecreateaEXT2filesystemonthisnewdeviceusing:
mke2fs/dev/loop1
Wecopyourfilesonamounteddisk:
mount/dev/loop1/mnt/
cp-Rbootdisk/*/mnt/
umount/mnt/
InstallGRUBonthedisk:
grub--device-map=/dev/null<<EOF
device(hd0)./c.img
geometry(hd0)41663
root(hd0,0)
setup(hd0)
quit
EOF
Andfinallywedetachtheloopdevice:
HowtomakeanOperatingSystem
10FirstbootwithGRUB
losetup-d/dev/loop1
GNUGRUBonWikipediaMultibootspecification
SeeAlso
HowtomakeanOperatingSystem
11FirstbootwithGRUB
AkernelcanbeprogrammedinC++,itisverysimilartomakingakernelinC,exceptthatthereareafewpitfallsyoumusttakeintoaccount(runtimesupport,constructors,...)
ThecompilerwillassumethatallthenecessaryC++runtimesupportisavailablebydefault,butaswearenotlinkinginlibsupc++intoyourC++kernel,weneedtoaddsomebasicfunctionsthatcanbefoundinthecxx.ccfile.
Caution:Theoperatorsnewanddeletecannotbeusedbeforevirtualmemoryandpaginationhavebeeninitialized.
Thekernelcodecan'tusefunctionsfromthestandardlibrariessoweneedtoaddsomebasicfunctionsformanagingmemoryandstrings:
voiditoa(char*buf,unsignedlongintn,intbase);
void*memset(char*dst,charsrc,intn);
void*memcpy(char*dst,char*src,intn);
intstrlen(char*s);
intstrcmp(constchar*dst,char*src);
intstrcpy(char*dst,constchar*src);
voidstrcat(void*dest,constvoid*src);
char*strncpy(char*destString,constchar*sourceString,intmaxLength);
intstrncmp(constchar*s1,constchar*s2,intc);
Thesefunctionsaredefinedinstring.cc,memory.cc,itoa.cc
Duringthenextstep,wearegoingtousedifferenttypesinourcode,mostofthetypeswearegoingtouseunsignedtypes(allthebitsareusedtostoredtheinteger,insignedtypesonebitisusedtosignalthesign):
typedefunsignedcharu8;
typedefunsignedshortu16;
typedefunsignedintu32;
typedefunsignedlonglongu64;
typedefsignedchars8;
typedefsignedshorts16;
typedefsignedints32;
typedefsignedlonglongs64;
Compilingakernelisnotthesamethingascompilingalinuxexecutable,wecan'tuseastandardlibraryandshouldhavenodependenciestothesystem.
OurMakefilewilldefinetheprocesstocompileandlinkourkernel.
Forx86architecture,thefollowingsargumentswillbeusedforgcc/g++/ld:
Chapter4:BackboneoftheOSandC++runtime
C++kernelrun-time
BasicC/C++functions
Ctypes
Compileourkernel
HowtomakeanOperatingSystem
12BackboneoftheOSandC++runtime
#Linker
LD=ld
LDFLAG=-melf_i386-static-L./-T./arch/$(ARCH)/linker.ld
#C++compiler
SC=g++
FLAG=$(INCDIR)-g-O2-w-trigraphs-fno-builtin-fno-exceptions-fno-stack-protector-O0-m32-fno-rtti-nostdlib-nodefaultlibs
#Assemblycompiler
ASM=nasm
ASMFLAG=-felf-o
HowtomakeanOperatingSystem
13BackboneoftheOSandC++runtime
NowthatweknowhowtocompileourC++kernelandbootthebinaryusingGRUB,wecanstarttodosomecoolthingsinC/C++.
WearegoingtouseVGAdefaultmode(03h)todisplaysometexttotheuser.Thescreencanbedirectlyaccessedusingthevideomemoryat0xB8000.Thescreenresolutionis80x25andeachcharacteronthescreenisdefinedby2bytes:oneforthecharactercode,andoneforthestyleflag.Thismeansthatthetotalsizeofthevideomemoryis4000B(80B25B2B).
IntheIOclass(io.cc),:
x,y:definethecursorpositiononthescreenreal_screen:definethevideomemorypointerputc(charc):printauniquecharacteronthescreenandmanagecursorpositionprintf(char*s,...):printastring
WeaddamethodputctotheIOClasstoputacharacteronthescreenandupdatethe(x,y)position.
/*putabyteonscreen*/
voidIo::putc(charc){
kattr=0x07;
unsignedchar*video;
video=(unsignedchar*)(real_screen+2*x+160*y);
//newline
if(c=='\n'){
x=0;
y++;
//backspace
}elseif(c=='\b'){
if(x){
*(video+1)=0x0;
x--;
}
//horizontaltab
}elseif(c=='\t'){
x=x+8-(x%8);
//carriagereturn
}elseif(c=='\r'){
x=0;
}else{
*video=c;
*(video+1)=kattr;
x++;
if(x>79){
x=0;
y++;
}
}
if(y>24)
scrollup(y-24);
}
Wealsoaddausefulandveryknownmethod:printf
/*putastringinscreen*/
voidIo::print(constchar*s,...){
va_listap;
charbuf[16];
Chapter5:Baseclassesformanagingx86architecture
Printingtothescreenconsole
HowtomakeanOperatingSystem
14Baseclassesformanagingx86architecture
inti,j,size,buflen,neg;
unsignedcharc;
intival;
unsignedintuival;
va_start(ap,s);
while((c=*s++)){
size=0;
neg=0;
if(c==0)
break;
elseif(c=='%'){
c=*s++;
if(c>='0'&&c<='9'){
size=c-'0';
c=*s++;
}
if(c=='d'){
ival=va_arg(ap,int);
if(ival<0){
uival=0-ival;
neg++;
}else
uival=ival;
itoa(buf,uival,10);
buflen=strlen(buf);
if(buflen<size)
for(i=size,j=buflen;i>=0;
i--,j--)
buf[i]=
(j>=
0)?buf[j]:'0';
if(neg)
print("-%s",buf);
else
print(buf);
}
elseif(c=='u'){
uival=va_arg(ap,int);
itoa(buf,uival,10);
buflen=strlen(buf);
if(buflen<size)
for(i=size,j=buflen;i>=0;
i--,j--)
buf[i]=
(j>=
0)?buf[j]:'0';
print(buf);
}elseif(c=='x'||c=='X'){
uival=va_arg(ap,int);
itoa(buf,uival,16);
buflen=strlen(buf);
if(buflen<size)
for(i=size,j=buflen;i>=0;
i--,j--)
buf[i]=
(j>=
0)?buf[j]:'0';
print("0x%s",buf);
}elseif(c=='p'){
uival=va_arg(ap,int);
itoa(buf,uival,16);
size=8;
buflen=strlen(buf);
if(buflen<size)
for(i=size,j=buflen;i>=0;
i--,j--)
HowtomakeanOperatingSystem
15Baseclassesformanagingx86architecture
buf[i]=
(j>=
0)?buf[j]:'0';
print("0x%s",buf);
}elseif(c=='s'){
print((char*)va_arg(ap,int));
}
}else
putc(c);
}
return;
}
AlargenumberofinstructionsareavailableinAssemblybutthereisnotequivalentinC(likecli,sti,inandout),soweneedaninterfacetotheseinstructions.
InC,wecanincludeAssemblyusingthedirective"asm()",gccusegastocompiletheassembly.
Caution:gasusestheAT&Tsyntax.
/*outputbyte*/
voidIo::outb(u32ad,u8v){
asmv("outb%%al,%%dx"::"d"(ad),"a"(v));;
}
/*outputword*/
voidIo::outw(u32ad,u16v){
asmv("outw%%ax,%%dx"::"d"(ad),"a"(v));
}
/*outputword*/
voidIo::outl(u32ad,u32v){
asmv("outl%%eax,%%dx"::"d"(ad),"a"(v));
}
/*inputbyte*/
u8Io::inb(u32ad){
u8_v;\
asmv("inb%%dx,%%al":"=a"(_v):"d"(ad));\
return_v;
}
/*inputword*/
u16Io::inw(u32ad){
u16_v;\
asmv("inw%%dx,%%ax":"=a"(_v):"d"(ad));\
return_v;
}
/*inputword*/
u32Io::inl(u32ad){
u32_v;\
asmv("inl%%dx,%%eax":"=a"(_v):"d"(ad));\
return_v;
}
Assemblyinterface
HowtomakeanOperatingSystem
16Baseclassesformanagingx86architecture
ThankstoGRUB,yourkernelisnolongerinreal-mode,butalreadyinprotectedmode,thismodeallowsustouseallthepossibilitiesofthemicroprocessorsuchasvirtualmemorymanagement,pagingandsafemulti-tasking.
TheGDT("GlobalDescriptorTable")isadatastructureusedtodefinethedifferentmemoryareas:thebaseaddress,thesizeandaccessprivilegeslikeexecuteandwrite.Thesememoryareasarecalled"segments".
WearegoingtousetheGDTtodefinedifferentmemorysegments:
"code":kernelcode,usedtostoredtheexecutablebinarycode"data":kerneldata"stack":kernelstack,usedtostoredthecallstackduringkernelexecution"ucode":usercode,usedtostoredtheexecutablebinarycodeforuserprogram"udata":userprogramdata"ustack":userstack,usedtostoredthecallstackduringexecutioninuserland
GRUBinitializesaGDTbutthisGDTisdoesnotcorrespondtoourkernel.TheGDTisloadedusingtheLGDTassemblyinstruction.ItexpectsthelocationofaGDTdescriptionstructure:
AndtheCstructure:
structgdtr{
u16limite;
u32base;
}__attribute__((packed));
Caution:thedirective__attribute__((packed))signaltogccthatthestructureshoulduseaslittlememoryaspossible.Withoutthisdirective,gccincludesomebytestooptimizethememoryalignmentandtheaccessduringexecution.
NowweneedtodefineourGDTtableandthenloaditusingLGDT.TheGDTtablecanbestoredwhereverwewantinmemory,itsaddressshouldjustbesignaledtotheprocessusingtheGDTRregistry.
TheGDTtableiscomposedofsegmentswiththefollowingstructure:
Chapter6:GDT
WhatistheGDT?
HowtoloadourGDT?
HowtomakeanOperatingSystem
17GDT
AndtheCstructure:
structgdtdesc{
u16lim0_15;
u16base0_15;
u8base16_23;
u8acces;
u8lim16_19:4;
u8other:4;
u8base24_31;
}__attribute__((packed));
WeneednowtodefineourGDTinmemoryandfinallyloaditusingtheGDTRregistry.
WearegoingtostoreourGDTattheaddress:
#defineGDTBASE0x00000800
Thefunctioninit_gdt_descinx86.ccinitializeagdtsegmentdescriptor.
voidinit_gdt_desc(u32base,u32limite,u8acces,u8other,structgdtdesc*desc)
{
desc->lim0_15=(limite&0xffff);
desc->base0_15=(base&0xffff);
desc->base16_23=(base&0xff0000)>>16;
desc->acces=acces;
desc->lim16_19=(limite&0xf0000)>>16;
desc->other=(other&0xf);
desc->base24_31=(base&0xff000000)>>24;
return;
}
Andthefunctioninit_gdtinitializetheGDT,somepartsofthebelowfunctionwillbeexplainedlaterandareusedformultitasking.
voidinit_gdt(void)
{
default_tss.debug_flag=0x00;
default_tss.io_map=0x00;
default_tss.esp0=0x1FFF0;
default_tss.ss0=0x18;
/*initializegdtsegments*/
init_gdt_desc(0x0,0x0,0x0,0x0,&kgdt[0]);
HowtodefineourGDTtable?
HowtomakeanOperatingSystem
18GDT
init_gdt_desc(0x0,0xFFFFF,0x9B,0x0D,&kgdt[1]);/*code*/
init_gdt_desc(0x0,0xFFFFF,0x93,0x0D,&kgdt[2]);/*data*/
init_gdt_desc(0x0,0x0,0x97,0x0D,&kgdt[3]);/*stack*/
init_gdt_desc(0x0,0xFFFFF,0xFF,0x0D,&kgdt[4]);/*ucode*/
init_gdt_desc(0x0,0xFFFFF,0xF3,0x0D,&kgdt[5]);/*udata*/
init_gdt_desc(0x0,0x0,0xF7,0x0D,&kgdt[6]);/*ustack*/
init_gdt_desc((u32)&default_tss,0x67,0xE9,0x00,&kgdt[7]);/*descripteurdetss*/
/*initializethegdtrstructure*/
kgdtr.limite=GDTSIZE*8;
kgdtr.base=GDTBASE;
/*copythegdtrtoitsmemoryarea*/
memcpy((char*)kgdtr.base,(char*)kgdt,kgdtr.limite);
/*loadthegdtrregistry*/
asm("lgdtl(kgdtr)");
/*initiliazthesegments*/
asm("movw$0x10,%ax\n\
movw%ax,%ds\n\
movw%ax,%es\n\
movw%ax,%fs\n\
movw%ax,%gs\n\
ljmp$0x08,$next\n\
next:\n");
}
HowtomakeanOperatingSystem
19GDT
Aninterruptisasignaltotheprocessoremittedbyhardwareorsoftwareindicatinganeventthatneedsimmediateattention.
Thereare3typesofinterrupts:
Hardwareinterrupts:aresenttotheprocessorfromanexternaldevice(keyboard,mouse,harddisk,...).Hardwareinterruptswereintroducedasawaytoreducewastingtheprocessor'svaluabletimeinpollingloops,waitingforexternalevents.Softwareinterrupts:areinitiatedvoluntarilybythesoftware.It'susedtomanagesystemcalls.Exceptions:areusedforerrorsoreventsoccurringduringprogramexecutionthatareexceptionalenoughthattheycannotbehandledwithintheprogramitself(divisionbyzero,pagefault,...)
Whentheuserpressedakeyonthekeyboard,thekeyboardcontrollerwillsignalaninterrupttotheInterruptController.Iftheinterruptisnotmasked,thecontrollerwillsignaltheinterrupttotheprocessor,theprocessorwillexecutearoutinetomanagetheinterrupt(keypressedorkeyreleased),thisroutinecould,forexample,getthepressedkeyfromthekeyboardcontrollerandprintthekeytothescreen.Oncethecharacterprocessingroutineiscompleted,theinterruptedjobcanberesumed.
ThePIC(Programmableinterruptcontroller)isadevicethatisusedtocombineseveralsourcesofinterruptontooneormoreCPUlines,whileallowingprioritylevelstobeassignedtoitsinterruptoutputs.Whenthedevicehasmultipleinterruptoutputstoassert,itassertsthemintheorderoftheirrelativepriority.
ThebestknownPICisthe8259A,each8259Acanhandle8devicesbutmostcomputershavetwocontrollers:onemasterandoneslave,thisallowsthecomputertomanageinterruptsfrom14devices.
Inthischapter,wewillneedtoprogramthiscontrollertoinitializeandmaskinterrupts.
TheInterruptDescriptorTable(IDT)isadatastructureusedbythex86architecturetoimplementaninterruptvectortable.TheIDTisusedbytheprocessortodeterminethecorrectresponsetointerruptsandexceptions.
OurkernelisgoingtousetheIDTtodefinethedifferentfunctionstobeexecutedwhenaninterruptoccurred.
LiketheGDT,theIDTisloadedusingtheLIDTLassemblyinstruction.ItexpectsthelocationofaIDTdescriptionstructure:
structidtr{
u16limite;
u32base;
}__attribute__((packed));
TheIDTtableiscomposedofIDTsegmentswiththefollowingstructure:
structidtdesc{
u16offset0_15;
u16select;
u16type;
Chapter7:IDTandinterrupts
Thekeyboardexample:
WhatisthePIC?
WhatistheIDT?
HowtomakeanOperatingSystem
20IDTandinterrupts
u16offset16_31;
}__attribute__((packed));
Caution:thedirective__attribute__((packed))signaltogccthatthestructureshoulduseaslittlememoryaspossible.Withoutthisdirective,gccincludessomebytestooptimizethememoryalignmentandtheaccessduringexecution.
NowweneedtodefineourIDTtableandthenloaditusingLIDTL.TheIDTtablecanbestoredwhereverwewantinmemory,itsaddressshouldjustbesignaledtotheprocessusingtheIDTRregistry.
Hereisatableofcommoninterrupts(MaskablehardwareinterruptarecalledIRQ):
IRQ Description
0 ProgrammableInterruptTimerInterrupt
1 KeyboardInterrupt
2 Cascade(usedinternallybythetwoPICs.neverraised)
3 COM2(ifenabled)
4 COM1(ifenabled)
5 LPT2(ifenabled)
6 FloppyDisk
7 LPT1
8 CMOSreal-timeclock(ifenabled)
9 Freeforperipherals/legacySCSI/NIC
10 Freeforperipherals/SCSI/NIC
11 Freeforperipherals/SCSI/NIC
12 PS2Mouse
13 FPU/Coprocessor/Inter-processor
14 PrimaryATAHardDisk
15 SecondaryATAHardDisk
ThisisasimplemethodtodefineanIDTsegment
voidinit_idt_desc(u16select,u32offset,u16type,structidtdesc*desc)
{
desc->offset0_15=(offset&0xffff);
desc->select=select;
desc->type=type;
desc->offset16_31=(offset&0xffff0000)>>16;
return;
}
Andwecannowinitializetheinterupts:
#defineIDTBASE0x00000000
#defineIDTSIZE0xFF
idtrkidtr;
Howtoinitializetheinterrupts?
HowtomakeanOperatingSystem
21IDTandinterrupts
voidinit_idt(void)
{
/*Initirq*/
inti;
for(i=0;i<IDTSIZE;i++)
init_idt_desc(0x08,(u32)_asm_schedule,INTGATE,&kidt[i]);//
/*Vectors0->31areforexceptions*/
init_idt_desc(0x08,(u32)_asm_exc_GP,INTGATE,&kidt[13]);/*#GP*/
init_idt_desc(0x08,(u32)_asm_exc_PF,INTGATE,&kidt[14]);/*#PF*/
init_idt_desc(0x08,(u32)_asm_schedule,INTGATE,&kidt[32]);
init_idt_desc(0x08,(u32)_asm_int_1,INTGATE,&kidt[33]);
init_idt_desc(0x08,(u32)_asm_syscalls,TRAPGATE,&kidt[48]);
init_idt_desc(0x08,(u32)_asm_syscalls,TRAPGATE,&kidt[128]);//48
kidtr.limite=IDTSIZE*8;
kidtr.base=IDTBASE;
/*CopytheIDTtothememory*/
memcpy((char*)kidtr.base,(char*)kidt,kidtr.limite);
/*LoadtheIDTRregistry*/
asm("lidtl(kidtr)");
}
AfterintializationofourIDT,weneedtoactivateinterruptsbyconfiguringthePIC.ThefollowingfunctionwillconfigurethetwoPICsbywrittingintheirinternalregistriesusingtheoutputportsoftheprocessorio.outb.WeconfigurethePICsusingtheports:
MasterPIC:0x20and0x21SlavePIC:0xA0and0xA1
ForaPIC,thereare2typesofregistries:
ICW(InitializationCommandWord):reinitthecontrollerOCW(OperationControlWord):configurethecontrolleronceinitialized(usedtomask/unmasktheinterrupts)
voidinit_pic(void)
{
/*InitializationofICW1*/
io.outb(0x20,0x11);
io.outb(0xA0,0x11);
/*InitializationofICW2*/
io.outb(0x21,0x20);/*startvector=32*/
io.outb(0xA1,0x70);/*startvector=96*/
/*InitializationofICW3*/
io.outb(0x21,0x04);
io.outb(0xA1,0x02);
/*InitializationofICW4*/
io.outb(0x21,0x01);
io.outb(0xA1,0x01);
/*maskinterrupts*/
io.outb(0x21,0x0);
io.outb(0xA1,0x0);
}
Theregistrieshavetobeconfiguredinorder.
PICICWconfigurationsdetails
HowtomakeanOperatingSystem
22IDTandinterrupts
ICW1(port0x20/port0xA0)
|0|0|0|1|x|0|x|x|
||+---withICW4(1)orwithout(0)
|+-----onecontroller(1),orcascade(0)
+---------triggeringbylevel(level)(1)orbyedge(edge)(0)
ICW2(port0x21/port0xA1)
|x|x|x|x|x|0|0|0|
|||||
+-----------------baseaddressforinterruptsvectors
ICW2(port0x21/port0xA1)
Forthemaster:
|x|x|x|x|x|x|x|x|
||||||||
+------------------slavecontrollerconnectedtotheportyes(1),orno(0)
Fortheslave:
|0|0|0|0|0|x|x|x|pourl'esclave
|||
+--------SlaveIDwhichisequaltothemasterport
ICW4(port0x21/port0xA1)
Itisusedtodefineinwhichmodethecontrollershouldwork.
|0|0|0|x|x|x|x|1|
|||+------mode"automaticendofinterrupt"AEOI(1)
||+--------modebufferedslave(0)ormaster(1)
|+----------modebuffered(1)
+------------mode"fullynested"(1)
YoushouldhavenoticedthatwhenI'minitializingourIDTsegments,I'musingoffsetstosegmentthecodeinAssembly.Thedifferentfunctionsaredefinedinx86int.asmandareofthefollowingscheme:
%macroSAVE_REGS0
pushad
pushds
pushes
pushfs
pushgs
pushebx
movbx,0x10
movds,bx
popebx
%endmacro
%macroRESTORE_REGS0
popgs
WhydoidtsegmentsoffsetourASMfunctions?
HowtomakeanOperatingSystem
23IDTandinterrupts
popfs
popes
popds
popad
%endmacro
%macroINTERRUPT1
global_asm_int_%1
_asm_int_%1:
SAVE_REGS
push%1
callisr_default_int
popeax;;aenleversinon
moval,0x20
out0x20,al
RESTORE_REGS
iret
%endmacro
Thesemacroswillbeusedtodefinetheinterruptsegmentthatwillpreventcorruptionofthedifferentregistries,itwillbeveryusefulformultitasking.
HowtomakeanOperatingSystem
24IDTandinterrupts
InthechapterrelatedtotheGDT,wesawthatusingsegmentationaphysicalmemoryaddressiscalculatedusingasegmentselectorandanoffset.
Inthischapter,wearegoingtoimplementpaging,pagingwilltranslatealinearaddressfromsegmentationintoaphysicaladdress.
Pagingwillallowourkernelto:
usethehard-driveasamemoryandnotbelimitedbythemachinerammemorylimittohaveauniquememoryspaceforeachprocesstoallowandunallowmemoryspaceinadynamicway
Inapagedsystem,eachprocessmayexecuteinitsown4gbareaofmemory,withoutanychanceofeffectinganyotherprocess'smemory,orthekernel's.Itsimplifiesmultitasking.
Thetranslationofalinearaddresstoaphysicaladdressisdoneinmultiplesteps:
1. TheprocessorusetheregistryCR3toknowthephysicaladdressofthepagesdirectory.2. Thefirst10bitsofthelinearaddressrepresentanoffset(between0and1023),pointingtoanentryinthepages
directory.Thisentrycontainsthephysicaladdressofapagestable.3. thenext10bitsofthelinearaddressrepresentanoffset,pointingtoanentryinthepagestable.Thisentryispointing
toa4kopage.4. Thelast12bitsofthelinearaddressrepresentanoffset(between0and4095),whichindicatesthepositioninthe4ko
page.
Chapter8:Theory:physicalandvirtualmemory
Whydoweneedpaging?
Howdoesitwork?
HowtomakeanOperatingSystem
25Theory:physicalandvirtualmemory
Thetwotypesofentries(tableanddirectory)looklikethesame.OnlythefieldingraywillbeusedinourOS.
P:indicateifthepageortableisinphysicalmemoryR/W:indicateifthepageortableisaccessibleinwritting(equals1)U/S:equals1toallowaccesstonon-preferredtasksA:indicateifthepageortablewasaccessedD:(onlyforpagestable)indicateifthepagewaswrittenPS(onlyforpagesdirectory)indicatethesizeofpages:
0=4kb1=4mb
Note:Physicaladdressesinthepagesdiretcoryorpagestablearewrittenusing20bitsbecausetheseaddressesarealignedon4kb,sothelast12bitsshouldbeequalto0.
Apagesdirectoryorpagestableused1024*4=4096bytes=4kApagestablecanaddress1024*4k=4MbApagesdirectorycanaddress1024(10244k)=4Gb
Formatforpagestableanddirectory
Howtoenablepagination?
HowtomakeanOperatingSystem
26Theory:physicalandvirtualmemory
Toenablepagination,wejustneedtosetbit31oftheCR0registryto1:
asm("mov%%cr0,%%eax;\
or%1,%%eax;\
mov%%eax,%%cr0"\
::"i"(0x80000000));
Butbefore,weneedtoinitializeourpagesdirectorywithatleastonepagestable.
Withtheidentitymappingmodel,thepagewillapplyonlytothekernelasthefirst4MBofvirtualmemorycoincidewiththefirst4MBofphysicalmemory:
Thismodelissimple:thefirstvirtualmemorypagecoincidetothefirstpageinphysicalmemory,thesecondpagecoincidetothesecondpageonphysicalmemoryandsoon...
IdentityMapping
HowtomakeanOperatingSystem
27Theory:physicalandvirtualmemory
ThekernelknowsthesizeofthephysicalmemoryavailablethankstoGRUB.
Inourimplementation,thefirst8megabytesofphysicalmemorywillbereservedforusebythekernelandwillcontain:
ThekernelGDT,IDTetTSSKernelStackSomespacereservedtohardware(videomemory,...)Pagedirectoryandpagestableforthekernel
Therestofthephysicalmemoryisfreelyavailabletothekernelandapplications.
Theaddressspacebetweenthebeginningofmemoryand0x40000000addressisthekernelspace,whilethespacebetweentheaddress0x40000000andtheendofthememorycorrespondstouserspace:
Memorymanagement:physicalandvirtual
VirtualMemoryMapping
HowtomakeanOperatingSystem
28Memorymanagement:physicalandvirtual
Thekernelspaceinvirtualmemory,whichisusing1Gbofvirtualmemory,iscommontoalltasks(kernelanduser).
Thisisimplementedbypointingthefirst256entriesofthetaskpagedirectorytothekernelpagedirectory(Invmm.cc):
/*
*KernelSpace.v_addr<USER_OFFSETareaddressedbythekernelpagestable
*/
for(i=0;i<256;i++)
pdir[i]=pd0[i];
HowtomakeanOperatingSystem
29Memorymanagement:physicalandvirtual