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