注:本程序?yàn)樵瓌?chuàng),若發(fā)現(xiàn)bug,萬望指出,若有問題,歡迎交流,轉(zhuǎn)載請(qǐng)指明出處。若能有助于一二訪客,幸甚。
以下為結(jié)果截圖,顯示的LOGO為小篆字體的歡迎 baby os 加載完成...幾個(gè)字。
保護(hù)模式
參考資料:
《Intel 64 and IA-32 Architectures Software Developer's Manual》
《Orange's 一個(gè)操作系統(tǒng)的實(shí)現(xiàn)'》
《X86/X64 體系探測(cè)及編程》
《30天自制操作系統(tǒng)》
《Linux 內(nèi)核完全剖析》
0.概述
Intel IA 32下,CUP有兩種工作模式:實(shí)模式和保護(hù)模式。打開PC,開始時(shí)CPU工作在實(shí)模式下,即此前幾篇東西寫的代碼都是在實(shí)模式下的。
實(shí)模式下有16位的寄存器、16位的數(shù)據(jù)總線、及20位的地址總線,1MB的尋址能力。物理地址的計(jì)算方法:
物理地址(Physical Address) = 段值(Segment)* 16 + 偏移值(Offset) 其中段和偏移都是16位的。
從80386開始,Intel的CPU 進(jìn)入32位時(shí)代,80386有32位地址總線,尋址能力達(dá)到4GB.
保護(hù)模式保護(hù)處理器的某些資源不能被隨意訪問,如處理器的硬件資源和系統(tǒng)的軟件資源,如CR0等控制寄存器,GDT、IDT等系統(tǒng)級(jí)的數(shù)據(jù)結(jié)構(gòu),OS kernel的代碼和數(shù)據(jù)等。
x86的segmentation和paging即分段和分頁機(jī)制是實(shí)施保護(hù)措施的手段。分段和分頁實(shí)行了不同的內(nèi)存管理模式和訪問控制。
1.權(quán)限和環(huán)境
4個(gè)權(quán)限級(jí)別:0~3,0為最高級(jí)別。
3種權(quán)限類型:CPL、DPL、RPL:
1)CPL(current privilege level):當(dāng)前的權(quán)限級(jí)別,指示當(dāng)前代碼在哪個(gè)權(quán)限級(jí)別,CPL的值存放在CS寄存器Selector域的RPL。(另外,SS寄存器的Selector的RPL總等于CPL)。
2)DPL(Descriptor Privilege Level):DPL存放在描述符Descriptor(包括段描述符Segment Descriptor和門描述符Gate Descriptor)里的DPL域,它指示訪問這些segment所需要的權(quán)限級(jí)別
3)RPL(Requested Privilege Level):存放在訪問者所使用的選擇子Selector的Bit0和Bit1,指示發(fā)起訪問的訪問者使用什么樣的權(quán)限對(duì)目標(biāo)進(jìn)行訪問。
若CPL > DPL表示當(dāng)前運(yùn)行的代碼的權(quán)限級(jí)別不足,不能對(duì)segment或gate進(jìn)行訪問。
從實(shí)模式進(jìn)入保護(hù)模式,段式管理機(jī)制必須建立,分頁機(jī)制是可選的,當(dāng)分頁機(jī)制關(guān)閉時(shí),從段式內(nèi)存管理中得到的線性地址(linear address)就是物理地址。
2.段式管理所使用的資源
硬件資源:
1)CR0、CR4
2)GDTR、LDTR(可選)、IDTR、TR
3)段選擇子寄存器:ES、CS、SS、DS、FS、GS寄存器
數(shù)據(jù)結(jié)構(gòu):
1)GDT、LDT(可選)、IDT
2)TSS
3)段描述符(Segment Descriptor):系統(tǒng)(System)段描述符、代碼(Code)/數(shù)據(jù)(Data)段描述符
4)門描述符(Gate Descriptor):包括調(diào)用門(Call-gate),中斷/陷阱門(Interrupt/Trap-gate)和任務(wù)門(Task-gate)
5)選擇子(Selector):存放在段寄存器里。
分段機(jī)制的內(nèi)存管理職責(zé):從邏輯地址(Logic address)轉(zhuǎn)換為處理器的線性地址(Linear address).
3.分頁機(jī)制使用的資源:
1)控制寄存器:CR0、CR2、CR3、CR4
2)IA32_EFER
頁轉(zhuǎn)換表:
1)PDPT(Page Directory Pointer Table)
2)PDT(Page Directory Table)
3)PT(Page Table)
分頁機(jī)制內(nèi)存管理職責(zé):從處理器的線性地址(即virtual address)映射到物理地址。
read/write的內(nèi)存設(shè)備RAM(DRAM)、read-only的內(nèi)存設(shè)備ROM(EPROM),及memory mapped I/O設(shè)備都可以映射到物理地址空間上。
典型的ROM設(shè)備映射到物理地址空間的高端和低端,Video和IGD設(shè)備的buffer映射到A0000H到BFFFFH的物理地址空間,PCIe等設(shè)備映射到物理地址空間的E0000000位置上,I/O APIC 設(shè)備映射到FEC00000以上的位置,等等。
經(jīng)過頁式轉(zhuǎn)換形成的物理地址,可以映射到DRAM或外部存儲(chǔ)設(shè)備Disk上。
4.段式內(nèi)存管理
兩方面的管理:
1)內(nèi)存管理:為地址的轉(zhuǎn)換提供基礎(chǔ)
Linear Address = base + offset
與實(shí)模式下的原理是一致的,實(shí)模式下段的base = selector << 4; 保護(hù)模式下,base從segment descriptor里加載而來。
2)保護(hù)措施:對(duì)訪問行為的控制
對(duì)段的limit、type、privilege檢查
5.段式管理的數(shù)據(jù)結(jié)構(gòu)
1)段選擇子(Segment Selector)
RPL:bit0~bit1,請(qǐng)求訪問者所使用的權(quán)限級(jí)別
TI:Table Indicator,描述符表索引位,bit2, TI=0表示GDT,TI=1表示LDT。
Index:Descriptor Index,它是Descriptor在GDT/LDT中的序號(hào)。bit3~bit15,13位,范圍0~8191,即可尋址8192個(gè)descriptor。
2)描述符表(Descriptor Table)
Segment Selector用于在Descriptor Table中查找descriptor。
描述符表由描述符表寄存器進(jìn)行定位,對(duì)應(yīng)GDT,LDT,IDT有GDTR,LDTR,IDTR。在IA32中,這三個(gè)寄存器都是48位,包括低16位為Limit和髙32位為Base,加載描述符表方法為lgdt, lldt, lidt。
其中Limit用于檢查Selector是否超出GDT的limit,如同數(shù)組的長度一樣,判斷數(shù)組是否越界。
3)段描述符(Segment Descriptor)
段描述符要么存放在描述符表里,要么被加載到段寄存器里。被加載到段寄存器后,它所描述的段變成了active狀態(tài)。
描述符有兩大類:段描述符和門描述符。
6.切換到保護(hù)模式
Intel推薦的步驟:
1)關(guān)中斷,包括可屏蔽中斷和不可屏蔽中斷
2)使用lgdt加載GDTR
3)置cr0 的PE位,切換到保護(hù)模式
4)使用far jmp/call,提供一個(gè)同級(jí)權(quán)限的CS Selector更新CS寄存器
5)若需要使用LDT,用lldt加載LDTR
6)使用ltr加載TR
7)更新SS、DS寄存器
8)使用lidt加載IDTR
9)開中斷
程序源碼:
boot.s:
[cpp] view plaincopyprint?
#--------------------------------------------------------------??#?文件:boot.s??#?描述:1.清屏??#???????2.設(shè)置顯示模式為0x103(800*600,256色)??#???????3.讀取軟盤,將內(nèi)核加載到內(nèi)存??#???????4.將內(nèi)核第一個(gè)扇區(qū)(load.s)移動(dòng)到內(nèi)存0x0000位置??#???????5.將引導(dǎo)扇區(qū)中的GDT及新顯示模式的一些參數(shù)移動(dòng)到指定位置??#???????6.開啟A20總線,置位cr0寄存器的PE位,進(jìn)入保護(hù)模式??#?時(shí)間:2012-12-29?21:47:12??#?作者:guzhoudiaoke@126.com??#--------------------------------------------------------------????.include?"include/kernel.inc"????.section?.text??.global?_start??.code16????_start:??????jmp?????main????#---------------------------------------------------------------??#?清屏:??#???設(shè)置屏幕背景色,調(diào)色板的索引0指代的顏色為背景色??#???先不考慮效率,只考慮可讀性,故ah,al分開賦值??#---------------------------------------------------------------??clear_screen:???????????????#?清屏函數(shù)??????movb????$0x06,??%ah?????#?功能號(hào)0x06??????movb????$0,?????%al?????#?上卷全部行,即清屏??????movb????$0,?????%ch?????#?左上角行??????movb????$0,?????%ch?????#?左上角列????????movb????$24,????%dh?????#?右下角行??????movb????$79,????%dl?????#?右下角列??????movb????$0x07,??%bh?????#?空白區(qū)域?qū)傩??????int?????$0x10????????????ret????#--------------------------------------------------------------------??#?設(shè)置顯示模式:??#???1.檢查VBE是否存在,即顯卡是否支持VESA?BIOS?EXTENSION??#???2.檢查VBE版本,是否為2.0以上??#???3.檢查要設(shè)置的mode的一些參數(shù),看是否符合要求??#???4.設(shè)置顯示模式為VBE?0x103(800*600,256色)??#???5.記錄新顯示模式的一些參數(shù)??#???6.若上面檢查或設(shè)置失敗,則設(shè)置顯示模式為VGA?0x13(320*200,256色)??#--------------------------------------------------------------------??set_video_mode:??????movw????$0x800,?????????????%ax??????movw????%ax,????????????????%es??????movw????%ax,????????????????%ds??????xorw????%di,????????????????%di??check_vbe:??????movb????$0x4f,??????????????%ah?????????#?表示使用VBE標(biāo)準(zhǔn)??????movb????$0x00,??????????????%al?????????#?功能號(hào)??????int?????$0x10??????cmp?????$0x004f,????????????%ax?????????#?若有VBE,AX應(yīng)該為0x004f??????jne?????set_mode_vga_0x13??????movw????0x04(%di),??????????%ax??????cmp?????$0x0200,????????????%ax?????????#?若VBE版本不是2.0以上??????jb??????set_mode_vga_0x13??check_vbe_mode:?????????????????????????????#?檢查MODE_VBE_0x13的參數(shù)??????movw????$VIDEO_MODE_0x103,??%cx??????movb????$0x4f,??????????????%ah?????????#?表明VBE標(biāo)準(zhǔn)??????movb????$0x01,??????????????%al?????????#?子功能號(hào)??????int?????$0x10??????cmpb????$0x00,??????????????%ah?????????#?是否調(diào)用成功??????jne?????set_mode_vga_0x13??????cmpb????$0x4f,??????????????%al?????????#?是否支持該模式??????jne?????set_mode_vga_0x13??????cmpb????$8,?????????????????0x19(%di)???#?顏色是否占8bit??????jne?????set_mode_vga_0x13??????cmpb????$4,?????????????????0x1b(%di)???#?顏色的指定方法為4(調(diào)色板方式)??????jne?????set_mode_vga_0x13??????movw????(%di),??????????????%ax??????andw????$0x0080,????????????%ax??????jz??????set_mode_vga_0x13???????????????#?AX第bit7是否為1(線性幀緩存是否有效)??set_mode_vbe:???????????????????????????????#?下面設(shè)置模式??????movw????$VIDEO_MODE_0x103,??%bx??????addw????$0x4000,????????????%bx?????????#?BX第14個(gè)比特表示是否使用大的線性緩存區(qū)??????movb????$0x4f,??????????????%ah?????????#?表示使用VBE標(biāo)準(zhǔn)??????movb????$0x02,??????????????%al?????????#?功能號(hào),表示設(shè)置模式??????int?????$0x10??save_video_mode_info:???????????????????????#?記錄切換到的模式的一些參數(shù)信息??????movw????$VIDEO_MODE_0x103,??video_mode??????movw????0x12(%di),??????????%ax??????movw????%ax,????????????????screen_x??????movw????0x14(%di),??????????%ax??????movw????%ax,????????????????screen_y??????movl????0x28(%di),??????????%eax??????movl????%eax,???????????????video_ram??????movw????$1,?????????????????%ax????????ret??set_mode_vga_0x13:??????????????????????????#?若不支持VBE則設(shè)置為VGA?0x13?mode??????movb????$0,?????????????????%ah?????????#?功能號(hào)0x0??????movb????$VIDEO_MODE_0x13,???%al?????????#?顯示模式??????int?????$0x10??????movw????$0x13,??????????????video_mode??????movw????$320,???????????????screen_x??????movw????$200,???????????????screen_y??????movl????$0xb8000,???????????video_ram????????????ret????#----------------------------------------------------------------??#?讀取軟盤一個(gè)扇區(qū):??#???使用BIOS?INT?0x13中斷讀軟盤,使用前需要設(shè)置ES:BX作為緩沖區(qū)??#???AX為相對(duì)扇區(qū)號(hào),基于相對(duì)扇區(qū)號(hào),為學(xué)習(xí)軟盤的知識(shí),使用了由??#???相對(duì)扇區(qū)號(hào)來讀軟盤的方式,也可以直接設(shè)置讀取扇區(qū)數(shù)而讀連續(xù)的??#???多個(gè)扇區(qū)。但好像有不能跨越磁道、不能超過64KB等限制,要小心。??#???柱面號(hào)、磁頭號(hào)、扇區(qū)號(hào)計(jì)算公式如下:??#???柱面號(hào)CH?=?N?/?36,令x?=?N?%?36??#???磁頭號(hào)DH?=?x?/?18,扇區(qū)號(hào)CL?=?x?%?18?+?1(因?yàn)閺?開始,故加1)??#-----------------------------------------------------------------??read_a_sect:??????movb????$36,????%dl??????divb????%dl??????movb????%al,????%ch?????#?柱面號(hào)=N?/?36,?假設(shè)x?=?N?%?36??????movb????%ah,????%al?????#?AL?=?N?%?36??????xorb????%ah,????%ah?????#?AH?=?0,?則AX?=?AL?=?N?%?36??????movb????$18,????%dl??????divb????%dl??????movb????%al,????%dh?????#?磁頭號(hào)DH?=?x?/?18??????movb????%ah,????%cl?????#?CL?=?x?%?18??????incb????%cl?????????????#?扇區(qū)號(hào)CL?=?x?%?18?+?1????????movb????$0x00,??%dl?????#?驅(qū)動(dòng)器號(hào)DL?=?0,表示第一個(gè)軟盤即floppya??????movb????$0x02,??%ah?????#?功能號(hào)0x02表示讀軟盤??????movb????$0x01,??%al?????#?讀取一個(gè)扇區(qū)數(shù)????re_read:????????????????????#?若調(diào)用失敗(可能是軟盤忙損壞等)則重新調(diào)用??????int?????$0x13??????jc??????re_read?????????#?若進(jìn)位位(CF)被置位,表示調(diào)用失敗????????????ret????#-------------------------------------------------------------------??#?讀取內(nèi)核到內(nèi)存??#???該函數(shù)讀取baby?OS?的內(nèi)核到內(nèi)存,第一個(gè)扇區(qū)為引導(dǎo)扇區(qū),需要讀取??#???的是從第二個(gè)扇區(qū)(相對(duì)扇區(qū)號(hào)1)開始的KERNEL_SECT_NUM個(gè)扇區(qū)??#???ES:BX為緩沖區(qū),為讀取內(nèi)核的臨時(shí)位置0x10000??#-------------------------------------------------------------------??read_kernel:??????movw????$0x1000,????????????%ax???????????movw????%ax,????????????????%es?????#?ES:BX?為緩沖區(qū)地址??????xorw????%bx,????????????????%bx??????movw????$0x00,??????????????%si?????#?已經(jīng)讀取的扇區(qū)數(shù)??????movw????$0x01,??????????????%di?????#?相對(duì)扇區(qū)號(hào)??1:????????movw????%di,????????????????%ax?????#?將相對(duì)扇區(qū)號(hào)傳給AX作為參數(shù)??????call????read_a_sect????????incw????%si??????incw????%di??????addw????$512,???????????????%bx??????cmpw????$KERNEL_SECT_NUM,???%si??????jne?????1b????????ret????#--------------------------------------------------------------------??#?移動(dòng)內(nèi)核第一個(gè)扇區(qū):??#???內(nèi)核從軟盤讀取到內(nèi)存的一個(gè)臨時(shí)位置,現(xiàn)在將第一個(gè)扇區(qū)移動(dòng)到內(nèi)存??#???0x0000處,第一個(gè)扇區(qū)即load.s,它將會(huì)把內(nèi)核剩余部分移動(dòng)到它的后面,??#???之所以分兩次移動(dòng),是因?yàn)槿魞?nèi)核較大,一次移動(dòng)會(huì)覆蓋0x7c00處的代碼,??#???即引導(dǎo)扇區(qū)的代碼,導(dǎo)致運(yùn)行出錯(cuò)。??#--------------------------------------------------------------------??move_first_sect_of_kernel:??????cli?????????????????????????????????#?指明SI,DI遞增??????movw????$0x1000,????????????%ax??????movw????%ax,????????????????%ds?????#?DS:SI?為源地址??????xorw????%si,????????????????%si??????movw????$0x00,??????????????%ax??????movw????%ax,????????????????%es?????#?ES:DI?為目標(biāo)地址??????xorw????%di,????????????????%di??????movw????$512?>>?2,????????????%cx?????#?移動(dòng)512/4?次??????rep?????movsl???????????????????????#?每次移動(dòng)4個(gè)byte????????ret??????#--------------------------------------------------------------------??#?移動(dòng)GDT及新顯示模式的參數(shù)信息到指定位置??#???該函數(shù)把GDT及參數(shù)信息移動(dòng)到指定的位置,以便于以后使用??#--------------------------------------------------------------------??move_gdt_and_video_info:??????xorw????%ax,????????????????????????%ax??????movw????%ax,????????????????????????%ds?????#?DS:SI?為源地址??????leaw????gdt,????????????????????????%si??????movw????$GDT_ADDR?>>?4,???????????????%ax?????#?由要保存的地址來計(jì)算段基址??????movw????%ax,????????????????????????%es?????#?ES:DI?為目的地址??????xorw????%di,????????????????????????%di??????movw????$GDT_SIZE+VIDEO_INFO_SIZE,??%cx?????#?移動(dòng)的雙字個(gè)數(shù)??????rep?????movsb????????ret????#--------------------------------------------------------------------??#?開啟保護(hù)模式:?????#???1.關(guān)中斷??#???2.加載GDT??#???3.開啟A20總線,置cr0的PE位,切換到保護(hù)模式??#???4.far?jmp/call,用一個(gè)CS?Selector?更新CS?寄存器,開始執(zhí)行新代碼??#--------------------------------------------------------------------??enter_protected_mode:??????cli?????????????????????????????????#?關(guān)中斷??????lgdt????gdt_ptr?????????????????????#?加載GDT????enable_a20:???????inb?????$0x64,??????????%al?????????#?從端口0x64讀取數(shù)據(jù)??????testb???$0x02,??????????%al?????????#?測(cè)試讀取數(shù)據(jù)第二個(gè)bit??????jnz?????enable_a20??????????????????#?忙等待????????movb????$0xdf,??????????%al??????outb????%al,????????????$0x64???????#?將0xdf寫入端口0x60????????movl????%cr0,???????????%eax????????#?讀取cr0寄存器??????orl?????$0x01,??????????%eax????????#?置位最后以為即PE位??????movl????%eax,???????????%cr0????????#?寫cr0寄存器????????ljmp????$CODE_SELECTOR,?$0x00???????#?跳轉(zhuǎn)到代碼段,即load.s處開始執(zhí)行????????????ret?????#--------------------------------------------------------------------??#?開始執(zhí)行后,會(huì)跳轉(zhuǎn)到此處開始執(zhí)行??#--------------------------------------------------------------------?????main:??????movw????%cx,????????%ax??????movw????%ax,????????%ds??????movw????%ax,????????%es??????movw????%ax,????????%ss??????movw????$0x1000,????%sp????????call????clear_screen????????????????#?清屏??????call????set_video_mode??????????????#?設(shè)置顯示模式??????call????read_kernel?????????????????#?從軟盤讀取內(nèi)核??????call????move_first_sect_of_kernel???#?將內(nèi)核第一個(gè)扇區(qū)load.s移動(dòng)到0x0000??????call????move_gdt_and_video_info?????#?將GDT和顯示模式信息保存起來??????call????enter_protected_mode????????#?進(jìn)入包含模式????1:??????jmp?????1b????gdt:??????.quad???0x0000000000000000??????????#?空描述符??????.quad???0x00cf9a000000ffff??????????#?代碼段描述符??????.quad???0x00cf92000000ffff??????????#?數(shù)據(jù)段描述符??????.quad???000000000000000000??????????#?留待以后使用??????.quad???000000000000000000??????????#?留待以后使用??video_mode:?????????????????????????????#?顯示模式??????.short??0??screen_x:???????????????????????????????#?水平分辨率??????.short??0???screen_y:???????????????????????????????#?垂直分辨率??????.short??0?????video_ram:??????????????????????????????#?video_ram地址??????.long???0??gdt_ptr:????????????????????????????????#?用與lgdt?加載GDT??????.word???screen_x?-?gdt?-?1??????????#?GDT段限長??????.long???GDT_ADDR????????????????????#?GDT基地址????????.org????0x1fe,??0x90????????????????#?用nop?指令填充??????.word???0xaa55??????????????????????#?引導(dǎo)扇區(qū)標(biāo)志?? load.s:
[cpp] view plaincopyprint?
#*************************************************************************??#???>?File:??????load.s??#???>?Desc:??????1.設(shè)置新的數(shù)據(jù)段等??#???????????????2.將內(nèi)核剩余部分移動(dòng)到load.s后面??#???????????????3.顯示babyos?加載成功的Logo??#???>?Author:????孤舟釣客??#???>?Mail:??????guzhoudiaoke@126.com???#???>?Time:??????2012年12月30日?星期日?22時(shí)23分55秒??#*************************************************************************????.include?"include/kernel.inc"????.section?.text??.global?_start????.org????0????_start:??????movl????$DATA_SELECTOR,?????????%eax??????movw????%ax,????????????????????%ds??????movw????%ax,????????????????????%es??????movw????%ax,????????????????????%fs??????movw????%ax,????????????????????%gs??????movw????%ax,????????????????????%ss??????movl????$STACK_BOTTOM,??????????%esp????load_lefted_kernel:??????cld??????movl????$0x10200,???????????????%esi??????movl????$0x200,?????????????????%edi??????movl????$(KERNEL_SECT_NUM-1)<<7,%ecx??????rep?????movsl????show_logo:??????movl????$0xe0000000,????????????%edi??????addl????$272?+?800*10,??????????%edi??????movl????$0x400,?????????????????%esi????????movl????$128,???????????????????%ebx??????movl????$1,?????????????????????%eax??1:????????movl????$256,???????????????????%ecx??set_line_mem:?????????cmpb????$255,???????????????????(%esi)??????je??????2f??????movb????%al,????????????????????(%edi)??2:??????inc?????%esi??????inc?????%edi??????loop????set_line_mem????????????addl????$800-256,???????????????%edi??????decl????%ebx??????jnz?????1b????3:????????jmp?????3b????????.org????512,????0x90?????? baby os 暫時(shí)使用下面的簡單logo:
o(∩∩)o...哈哈,這個(gè)logo 使用小篆字體,還是很有中國特色的呦~
總結(jié)
以上是生活随笔為你收集整理的跳转到保护模式并显示一个LOGO的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。