How to Create Bootable USB Drives and SD Cards for Every Operating System
How to Create an Operating System
description
Transcript of 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