Table of Contents - gti.bh · Chapter 2: Setup the development environment The first step is to...
Transcript of Table of Contents - gti.bh · Chapter 2: Setup the development environment The first step is to...
1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
1.10
1.11
1.12
1.13
1.14
1.15
1.16
1.17
1.18
1.19
1.20
1.21
TableofContentsIntroduction
Introductionaboutthex86architectureandaboutourOS
Setupthedevelopmentenvironment
FirstbootwithGRUB
BackboneoftheOSandC++runtime
Baseclassesformanagingx86architecture
GDT
IDTandinterrupts
Theory:physicalandvirtualmemory
Memorymanagement:physicalandvirtual
Processmanagementandmultitasking
Externalprogramexecution:ELFfiles
Userlandandsyscalls
Modulardrivers
Somebasicsmodules:console,keyboard
IDEHarddisks
DOSPartitions
EXT2read-onlyfilesystems
StandardClibrary(libC)
UNIXbasictools:sh,cat
Luainterpreter
2
HowtoMakeaComputerOperatingSystemOnlinebookabouthowtowriteacomputeroperatingsysteminC/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).ItwasgeneratedusingGitBook.
SourceCode:Allthesystemsourcecodewillbestoredinthesrcdirectory.Eachstepwillcontainlinkstothedifferentrelatedfiles.
Contributions:Thiscourseisopentocontributions,feelfreetosignalerrorswithissuesordirectlycorrecttheerrorswithpull-requests.
Questions:Feelfreetoaskanyquestionsbyaddingissuesorcommentingsections.
YoucanfollowmeonTwitter@SamyPesseorGitHub.
WhatkindofOSarewebuilding?
ThegoalistobuildaverysimpleUNIX-basedoperatingsysteminC++,notjusta"proof-of-concept".TheOSshouldbeabletoboot,startauserlandshell,andbeextensible.
Introduction
3
Introduction
4
Chapter1:Introductiontothex86architectureandaboutourOS
Whatisthex86architecture?
Thetermx86denotesafamilyofbackwardcompatibleinstructionsetarchitecturesbasedontheIntel8086CPU.
Thex86architectureisthemostcommoninstructionsetarchitecturesinceitsintroductionin1981fortheIBMPC.Alargeamountofsoftware,includingoperatingsystems(OS's)suchasDOS,Windows,Linux,BSD,SolarisandMacOSX,functionwithx86-basedhardware.
Inthiscoursewearenotgoingtodesignanoperatingsystemforthex86-64architecturebutforx86-32,thankstobackwardcompatibility,ourOSwillbecompatiblewithournewerPCs(buttakecautionifyouwanttotestitonyourrealmachine).
OurOperatingSystem
ThegoalistobuildaverysimpleUNIX-basedoperatingsysteminC++,butthegoalisnottojustbuilda"proof-of-concept".TheOSshouldbeabletoboot,startauserlandshellandbeextensible.
TheOSwillbebuiltforthex86architecture,runningon32bits,andcompatiblewithIBMPCs.
Specifications:
CodeinC++x86,32bitarchitectureBootwithGrubKindofmodularsystemfordriversKindofUNIXstyleMultitaskingELFexecutableinuserlandModules(accessibleinuserlandusing/dev/...):
IDEdisksDOSpartitionsClockEXT2(readonly)BochVBE
Userland:
Introductionaboutthex86architectureandaboutourOS
5
APIPosixLibC"Can"runashellorsomeexecutables(e.g.,lua)
Introductionaboutthex86architectureandaboutourOS
6
Chapter2:SetupthedevelopmentenvironmentThefirststepistosetupagoodandviabledevelopmentenvironment.UsingVagrantandVirtualbox,you'llbeabletocompileandtestyourOSfromalltheOSs(Linux,WindowsorMac).
InstallVagrant
Vagrantisfreeandopen-sourcesoftwareforcreatingandconfiguringvirtualdevelopmentenvironments.ItcanbeconsideredawrapperaroundVirtualBox.
Vagrantwillhelpuscreateacleanvirtualdevelopmentenvironmentonwhateversystemyouareusing.ThefirststepistodownloadandinstallVagrantforyoursystemathttp://www.vagrantup.com/.
InstallVirtualbox
OracleVMVirtualBoxisavirtualizationsoftwarepackageforx86andAMD64/Intel64-basedcomputers.
VagrantneedsVirtualboxtowork,Downloadandinstallforyoursystemathttps://www.virtualbox.org/wiki/Downloads.
Startandtestyourdevelopmentenvironment
OnceVagrantandVirtualboxareinstalled,youneedtodownloadtheubuntulucid32imageforVagrant:
vagrantboxaddlucid32http://files.vagrantup.com/lucid32.box
Oncethelucid32imageisready,weneedtodefineourdevelopmentenvironmentusingaVagrantfile,createafilenamedVagrantfile.Thisfiledefineswhatprerequisitesourenvironmentneeds:nasm,make,build-essential,grubandqemu.
Startyourboxusing:
vagrantup
Youcannowaccessyourboxbyusingsshtoconnecttothevirtualboxusing:
Setupthedevelopmentenvironment
7
vagrantssh
ThedirectorycontainingtheVagrantfilewillbemountedbydefaultinthe/vagrantdirectoryoftheguestVM(inthiscase,UbuntuLucid32):
cd/vagrant
Buildandtestouroperatingsystem
ThefileMakefiledefinessomebasicsrulesforbuildingthekernel,theuserlibcandsomeuserlandprograms.
Build:
makeall
Testouroperatingsystemwithqemu:
makerun
ThedocumentationforqemuisavailableatQEMUEmulatorDocumentation.
Youcanexittheemulatorusing:Ctrl-a.
Setupthedevelopmentenvironment
8
Chapter3:FirstbootwithGRUB
Howthebootworks?
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(calledthemagicbytesoftheMasterBootRecord,alsoknownastheMBR).Thissignatureisrepresented(inbinary)as0b1010101001010101.Thealternatingbitpatternwasthoughttobeaprotectionagainstcertainfailures(driveorcontroller).Ifthispatternisgarbledor0x00,thedeviceisnotconsideredbootable.
BIOSphysicallysearchesforabootdevicebyloadingthefirst512bytesfromthebootsectorofeachdeviceintophysicalmemory,startingattheaddress0x7C00(1KiBbelowthe32KiBmark).Whenthevalidsignaturebytesaredetected,BIOStransferscontroltothe0x7C00memoryaddress(viaajumpinstruction)inordertoexecutethebootsectorcode.
ThroughoutthisprocesstheCPUhasbeenrunningin16-bitRealMode,whichisthedefaultstateforx86CPUsinordertomaintainbackwardscompatibility.Toexecutethe32-bitinstructionswithinourkernel,abootloaderisrequiredtoswitchtheCPUintoProtectedMode.
WhatisGRUB?
GNUGRUB(shortforGNUGRandUnifiedBootloader)isabootloaderpackagefromtheGNUProject.GRUBisthereferenceimplementationoftheFreeSoftwareFoundation'sMultibootSpecification,whichprovidesauserthechoicetobootoneofmultipleoperatingsystemsinstalledonacomputerorselectaspecifickernelconfigurationavailableonaparticularoperatingsystem'spartitions.
FirstbootwithGRUB
9
Tomakeitsimple,GRUBisthefirstthingbootedbythemachine(aboot-loader)andwillsimplifytheloadingofourkernelstoredonthehard-disk.
WhyareweusingGRUB?
GRUBisverysimpletouseMakeitverysimpletoload32bitskernelswithoutneedsof16bitscodeMultibootwithLinux,WindowsandothersMakeiteasytoloadexternalmodulesinmemory
HowtouseGRUB?
GRUBusestheMultibootspecification,theexecutablebinaryshouldbe32bitsandmustcontainaspecialheader(multibootheader)inits8192firstbytes.OurkernelwillbeaELFexecutablefile("ExecutableandLinkableFormat",acommonstandardfileformatforexecutablesinmostUNIXsystem).
ThefirstbootsequenceofourkerneliswritteninAssembly:start.asmandweusealinkerfiletodefineourexecutablestructure:linker.ld.
ThisbootprocessalsoinitializessomeofourC++runtime,itwillbedescribedinthenextchapter.
Multibootheaderstructure:
FirstbootwithGRUB
10
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.
Createadiskimageforourkernelandgrub
Thescriptdiskimage.shwillgenerateaharddiskimagethatcanbeusedbyQEMU.
Thefirststepistocreateahard-diskimage(c.img)usingqemu-img:
qemu-imgcreatec.img2M
Weneednowtopartitionthediskusingfdisk:
FirstbootwithGRUB
11
fdisk./c.img
#SwitchtoExpertcommands
>x
#Changenumberofcylinders(1-1048576)
>c
>4
#Changenumberofheads(1-256,default16):
>h
>16
#Changenumberofsectors/track(1-63,default63)
>s
>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-deviceusinglosetup.Thisallowsafiletobeaccesslikeablockdevice.Theoffsetofthepartitionispassedasanargumentandcalculatedusing:offset=start_sector*bytes_by_sector.
Usingfdisk-l-uc.img,youget:63*512=32256.
FirstbootwithGRUB
12
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:
losetup-d/dev/loop1
SeeAlso
GNUGRUBonWikipediaMultibootspecification
FirstbootwithGRUB
13
Chapter4:BackboneoftheOSandC++runtime
C++kernelrun-time
AkernelcanbewritteninC++justasitcanbeinC,withtheexceptionofafewpitfallsthatcomewithusingC++(runtimesupport,constructors,etc).
ThecompilerwillassumethatallthenecessaryC++runtimesupportisavailablebydefault,butaswearenotlinkinglibsupc++intoyourC++kernel,weneedtoaddsomebasicfunctionsthatcanbefoundinthecxx.ccfile.
Caution:Theoperatorsnewanddeletecannotbeusedbeforevirtualmemoryandpaginationhavebeeninitialized.
BasicC/C++functions
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
Ctypes
Inthenextstep,we'regoingtodefinedifferenttypeswe'regoingtouseinourcode.Mostofourvariabletypesaregoingtobeunsigned.Thismeansthatallthebitsareusedtostoretheinteger.Signedvariablesusetheirfirstbittoindicatetheirsign.
BackboneoftheOSandC++runtime
14
typedefunsignedcharu8;
typedefunsignedshortu16;
typedefunsignedintu32;
typedefunsignedlonglongu64;
typedefsignedchars8;
typedefsignedshorts16;
typedefsignedints32;
typedefsignedlonglongs64;
Compileourkernel
Compilingakernelisnotthesamethingascompilingalinuxexecutable,wecan'tuseastandardlibraryandshouldhavenodependenciestothesystem.
OurMakefilewilldefinetheprocesstocompileandlinkourkernel.
Forx86architecture,thefollowingsargumentswillbeusedforgcc/g++/ld:
#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-protecto
r-O0-m32-fno-rtti-nostdlib-nodefaultlibs
#Assemblycompiler
ASM=nasm
ASMFLAG=-felf-o
BackboneoftheOSandC++runtime
15
Chapter5:Baseclassesformanagingx86architectureNowthatweknowhowtocompileourC++kernelandbootthebinaryusingGRUB,wecanstarttodosomecoolthingsinC/C++.
Printingtothescreenconsole
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.
Baseclassesformanagingx86architecture
16
/*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];
inti,j,size,buflen,neg;
unsignedcharc;
intival;
unsignedintuival;
va_start(ap,s);
while((c=*s++)){
Baseclassesformanagingx86architecture
17
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)
Baseclassesformanagingx86architecture
18
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--)
buf[i]=
(j>=
0)?buf[j]:'0';
print("0x%s",buf);
}elseif(c=='s'){
print((char*)va_arg(ap,int));
}
}else
putc(c);
}
return;
}
Assemblyinterface
AlargenumberofinstructionsareavailableinAssemblybutthereisnotequivalentinC(likecli,sti,inandout),soweneedaninterfacetotheseinstructions.
InC,wecanincludeAssemblyusingthedirective"asm()",gccusegastocompiletheassembly.
Caution:gasusestheAT&Tsyntax.
Baseclassesformanagingx86architecture
19
/*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;
}
Baseclassesformanagingx86architecture
20
Chapter6:GDTThankstoGRUB,yourkernelisnolongerinreal-mode,butalreadyinprotectedmode,thismodeallowsustouseallthepossibilitiesofthemicroprocessorsuchasvirtualmemorymanagement,pagingandsafemulti-tasking.
WhatistheGDT?
TheGDT("GlobalDescriptorTable")isadatastructureusedtodefinethedifferentmemoryareas:thebaseaddress,thesizeandaccessprivilegeslikeexecuteandwrite.Thesememoryareasarecalled"segments".
WearegoingtousetheGDTtodefinedifferentmemorysegments:
"code":kernelcode,usedtostoredtheexecutablebinarycode"data":kerneldata"stack":kernelstack,usedtostoredthecallstackduringkernelexecution"ucode":usercode,usedtostoredtheexecutablebinarycodeforuserprogram"udata":userprogramdata"ustack":userstack,usedtostoredthecallstackduringexecutioninuserland
HowtoloadourGDT?
GRUBinitializesaGDTbutthisGDTisdoesnotcorrespondtoourkernel.TheGDTisloadedusingtheLGDTassemblyinstruction.ItexpectsthelocationofaGDTdescriptionstructure:
AndtheCstructure:
GDT
21
structgdtr{
u16limite;
u32base;
}__attribute__((packed));
Caution:thedirective__attribute__((packed))signaltogccthatthestructureshoulduseaslittlememoryaspossible.Withoutthisdirective,gccincludesomebytestooptimizethememoryalignmentandtheaccessduringexecution.
NowweneedtodefineourGDTtableandthenloaditusingLGDT.TheGDTtablecanbestoredwhereverwewantinmemory,itsaddressshouldjustbesignaledtotheprocessusingtheGDTRregistry.
TheGDTtableiscomposedofsegmentswiththefollowingstructure:
AndtheCstructure:
structgdtdesc{
u16lim0_15;
u16base0_15;
u8base16_23;
u8acces;
u8lim16_19:4;
u8other:4;
u8base24_31;
}__attribute__((packed));
HowtodefineourGDTtable?
WeneednowtodefineourGDTinmemoryandfinallyloaditusingtheGDTRregistry.
WearegoingtostoreourGDTattheaddress:
#defineGDTBASE0x00000800
GDT
22
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.
GDT
23
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]);
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]);/*descripteur
detss*/
/*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");
}
GDT
24
Chapter7:IDTandinterruptsAninterruptisasignaltotheprocessoremittedbyhardwareorsoftwareindicatinganeventthatneedsimmediateattention.
Thereare3typesofinterrupts:
Hardwareinterrupts:aresenttotheprocessorfromanexternaldevice(keyboard,mouse,harddisk,...).Hardwareinterruptswereintroducedasawaytoreducewastingtheprocessor'svaluabletimeinpollingloops,waitingforexternalevents.Softwareinterrupts:areinitiatedvoluntarilybythesoftware.It'susedtomanagesystemcalls.Exceptions:areusedforerrorsoreventsoccurringduringprogramexecutionthatareexceptionalenoughthattheycannotbehandledwithintheprogramitself(divisionbyzero,pagefault,...)
Thekeyboardexample:
Whentheuserpressedakeyonthekeyboard,thekeyboardcontrollerwillsignalaninterrupttotheInterruptController.Iftheinterruptisnotmasked,thecontrollerwillsignaltheinterrupttotheprocessor,theprocessorwillexecutearoutinetomanagetheinterrupt(keypressedorkeyreleased),thisroutinecould,forexample,getthepressedkeyfromthekeyboardcontrollerandprintthekeytothescreen.Oncethecharacterprocessingroutineiscompleted,theinterruptedjobcanberesumed.
WhatisthePIC?
ThePIC(Programmableinterruptcontroller)isadevicethatisusedtocombineseveralsourcesofinterruptontooneormoreCPUlines,whileallowingprioritylevelstobeassignedtoitsinterruptoutputs.Whenthedevicehasmultipleinterruptoutputstoassert,itassertsthemintheorderoftheirrelativepriority.
ThebestknownPICisthe8259A,each8259Acanhandle8devicesbutmostcomputershavetwocontrollers:onemasterandoneslave,thisallowsthecomputertomanageinterruptsfrom14devices.
Inthischapter,wewillneedtoprogramthiscontrollertoinitializeandmaskinterrupts.
WhatistheIDT?
IDTandinterrupts
25
TheInterruptDescriptorTable(IDT)isadatastructureusedbythex86architecturetoimplementaninterruptvectortable.TheIDTisusedbytheprocessortodeterminethecorrectresponsetointerruptsandexceptions.
OurkernelisgoingtousetheIDTtodefinethedifferentfunctionstobeexecutedwhenaninterruptoccurred.
LiketheGDT,theIDTisloadedusingtheLIDTLassemblyinstruction.ItexpectsthelocationofaIDTdescriptionstructure:
structidtr{
u16limite;
u32base;
}__attribute__((packed));
TheIDTtableiscomposedofIDTsegmentswiththefollowingstructure:
structidtdesc{
u16offset0_15;
u16select;
u16type;
u16offset16_31;
}__attribute__((packed));
Caution:thedirective__attribute__((packed))signaltogccthatthestructureshoulduseaslittlememoryaspossible.Withoutthisdirective,gccincludessomebytestooptimizethememoryalignmentandtheaccessduringexecution.
NowweneedtodefineourIDTtableandthenloaditusingLIDTL.TheIDTtablecanbestoredwhereverwewantinmemory,itsaddressshouldjustbesignaledtotheprocessusingtheIDTRregistry.
Hereisatableofcommoninterrupts(MaskablehardwareinterruptarecalledIRQ):
IDTandinterrupts
26
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
Howtoinitializetheinterrupts?
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;
IDTandinterrupts
27
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)
IDTandinterrupts
28
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);
}
PICICWconfigurationsdetails
Theregistrieshavetobeconfiguredinorder.
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:
IDTandinterrupts
29
|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)
WhydoidtsegmentsoffsetourASMfunctions?
YoushouldhavenoticedthatwhenI'minitializingourIDTsegments,I'musingoffsetstosegmentthecodeinAssembly.Thedifferentfunctionsaredefinedinx86int.asmandareofthefollowingscheme:
IDTandinterrupts
30
%macroSAVE_REGS0
pushad
pushds
pushes
pushfs
pushgs
pushebx
movbx,0x10
movds,bx
popebx
%endmacro
%macroRESTORE_REGS0
popgs
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.
IDTandinterrupts
31
Chapter8:Theory:physicalandvirtualmemoryInthechapterrelatedtotheGDT,wesawthatusingsegmentationaphysicalmemoryaddressiscalculatedusingasegmentselectorandanoffset.
Inthischapter,wearegoingtoimplementpaging,pagingwilltranslatealinearaddressfromsegmentationintoaphysicaladdress.
Whydoweneedpaging?
Pagingwillallowourkernelto:
usethehard-driveasamemoryandnotbelimitedbythemachinerammemorylimittohaveauniquememoryspaceforeachprocesstoallowandunallowmemoryspaceinadynamicway
Inapagedsystem,eachprocessmayexecuteinitsown4gbareaofmemory,withoutanychanceofeffectinganyotherprocess'smemory,orthekernel's.Itsimplifiesmultitasking.
Howdoesitwork?
Thetranslationofalinearaddresstoaphysicaladdressisdoneinmultiplesteps:
1. TheprocessorusetheregistryCR3toknowthephysicaladdressofthepagesdirectory.
2. Thefirst10bitsofthelinearaddressrepresentanoffset(between0and1023),pointingtoanentryinthepagesdirectory.Thisentrycontainsthephysicaladdressofapagestable.
3. thenext10bitsofthelinearaddressrepresentanoffset,pointingtoanentryinthepagestable.Thisentryispointingtoa4kopage.
4. Thelast12bitsofthelinearaddressrepresentanoffset(between0and4095),whichindicatesthepositioninthe4kopage.
Theory:physicalandvirtualmemory
32
Formatforpagestableanddirectory
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=4k
Theory:physicalandvirtualmemory
33
Apagestablecanaddress1024*4k=4MbApagesdirectorycanaddress1024(10244k)=4Gb
Howtoenablepagination?
Toenablepagination,wejustneedtosetbit31oftheCR0registryto1:
asm("mov%%cr0,%%eax;\
or%1,%%eax;\
mov%%eax,%%cr0"\
::"i"(0x80000000));
Butbefore,weneedtoinitializeourpagesdirectorywithatleastonepagestable.
IdentityMapping
Withtheidentitymappingmodel,thepagewillapplyonlytothekernelasthefirst4MBofvirtualmemorycoincidewiththefirst4MBofphysicalmemory:
Thismodelissimple:thefirstvirtualmemorypagecoincidetothefirstpageinphysicalmemory,thesecondpagecoincidetothesecondpageonphysicalmemoryandsoon...
Theory:physicalandvirtualmemory
34
Memorymanagement:physicalandvirtualThekernelknowsthesizeofthephysicalmemoryavailablethankstoGRUB.
Inourimplementation,thefirst8megabytesofphysicalmemorywillbereservedforusebythekernelandwillcontain:
ThekernelGDT,IDTetTSSKernelStackSomespacereservedtohardware(videomemory,...)Pagedirectoryandpagestableforthekernel
Therestofthephysicalmemoryisfreelyavailabletothekernelandapplications.
VirtualMemoryMapping
Theaddressspacebetweenthebeginningofmemoryand0x40000000addressisthekernelspace,whilethespacebetweentheaddress0x40000000andtheendofthememorycorrespondstouserspace:
Memorymanagement:physicalandvirtual
35
Thekernelspaceinvirtualmemory,whichisusing1Gbofvirtualmemory,iscommontoalltasks(kernelanduser).
Thisisimplementedbypointingthefirst256entriesofthetaskpagedirectorytothekernelpagedirectory(Invmm.cc):
/*
*KernelSpace.v_addr<USER_OFFSETareaddressedbythekernelpagestable
*/
for(i=0;i<256;i++)
pdir[i]=pd0[i];
Memorymanagement:physicalandvirtual
36