天天看點

自制作業系統Antz day05——深入了解保護模式與進入方法   Github位址:https://github.com/CasterWx  0 .為什麼要有保護模式1 .保護模式的寄存器擴充2 .保護模式的運作模式反轉3 .全局描述符表概述4 .全局描述符表GDT及選擇子5 .進入保護模式

  Antz系統更新位址: 

https://www.cnblogs.com/LexMoon/category/1262287.html

  Linux核心源碼分析位址:

https://www.cnblogs.com/LexMoon/category/1267413.html

  Github位址: https://github.com/CasterWx

  在前幾天的任務中,我們已經簡單實作了MBR,直接操作顯示器和硬碟操作來加載其他扇區的程式,我們這些任務都是為了進入保護模式做準備,雖然我們已經給出了jmp到保護模式的方法,但是我們還是需要了解保護模式下的一些特性,才能更好的實作我們作業系統的功能。

0 .為什麼要有保護模式

  以下是實模式的不足。

  1)作業系統和使用者程式屬于同一特權級。

  2)使用者程式所引用的位址都是指向真實的實體位址,也就是說邏輯位址等于實體位址。

  3)使用者程式可以自由修改段基址,可以通路任意記憶體。

  4)通路超過64KB的記憶體區域時要切換段基址。

  5)一次隻能運作一個程式,無法充分利用計算機資源。

  6)共20跟位址線,最大可用記憶體為1MB。

  1~3是安全缺陷,4~5是使用方面的缺陷,第6條簡直就是不能忍受的硬傷,1MB記憶體真的太束縛手腳了。

  後來為了解決這些問題,廠商開發處保護模式。這時,實體記憶體位址不能被程式直接通路,程式内部的位址(虛拟位址)需要被轉換為實體位址後再去通路,程式對此一無所知。而且位址的轉換時由處理器和作業系統共同協作完成的,處理器在硬體上提供位址轉換部件,作業系統提供轉換過程需要的頁表。

1 .保護模式的寄存器擴充

  計算機的發展必須遵守相容的特點,CPU發展到32位之後,位址總線和資料總線也發展到了32位,尋址空間更是達到了4GB。尋址空間大了,但尋址方式還是得相容老方法,就是“段基址:偏移位址”,如果還是16位的話,不能承受4GB尋址的重任,是以寄存器也得跟上。為了讓一個寄存器就可以尋址4GB空間,寄存器擴充到了32位。除了段寄存器,其他寄存器均擴充到了32位,因為段寄存器16位就夠用了。

自制作業系統Antz day05——深入了解保護模式與進入方法   Github位址:https://github.com/CasterWx  0 .為什麼要有保護模式1 .保護模式的寄存器擴充2 .保護模式的運作模式反轉3 .全局描述符表概述4 .全局描述符表GDT及選擇子5 .進入保護模式

  寄存器的低16位都是為了相容模式,高16位無法單獨使用,隻能在用32位寄存器時才可以用到。

  偏移位址還是和實模式下一樣,但段基址為了安全,在其内添加了限制資訊,這些限制資訊就是記憶體段的描述資訊,由于這些資訊在寄存器中放不下,是以用了一個專門的資料結構——全局描述符表。其中有表項,用來描述各個記憶體段的起始位址,大小,權限等資訊,每個表項大小是64位元組,因為全局描述符表很大,隻能放在記憶體中,由寄存器指向它。

  至此,段寄存器中再也不是段基址了,裡面儲存的叫做選擇子(selector) ,它是一個數,用來索引全局描述符表中的段描述符,把全局描述符表當作數組,選擇子就像是下标。

  段描述符是放在記憶體中的,通路記憶體對于CPU而言效率不高,而且段描述符的格式很奇怪,一個資料要分三個地方存,是以CPU把這些數組合并成一個完整資料也是需要花時間的。既然如此花費時間,在保護模式中,CPU為了提高效率,采取了對段寄存器的緩存技術,将段資訊用一個寄存器來存儲,這就是段描述符緩沖寄存器(對程式員不可見)。在獲得一個段描述符之後,以後通路相同段時,會直接通路該寄存器。

  下面是三種段描述符寄存器的結構:

  

自制作業系統Antz day05——深入了解保護模式與進入方法   Github位址:https://github.com/CasterWx  0 .為什麼要有保護模式1 .保護模式的寄存器擴充2 .保護模式的運作模式反轉3 .全局描述符表概述4 .全局描述符表GDT及選擇子5 .進入保護模式

    

2 .保護模式的運作模式反轉

  保護模式如何分辨16位和32位指令和操作數呢?

  彙編産生的機器碼機器并不能識别是運作在16位還是32位系統下,在編譯時可以通過[bits 16]和[bits 32]來确定編譯器将代碼編譯為多少位的機器碼。

  [bis]是僞指令,編譯并無具體機器碼,那麼在編譯之後機器如何識别呢?  這裡引入字首,在指令前加入字首指令重複字首rep,段跨越字首"段寄存器",還有操作數反轉字首0x66,尋址方式反轉字首0x67。

行号 指令 機器碼
1 [bits 16] 僞指令
2 mov ax,0x1234 B83412
3 mov eax,0x1234 66B834120000
4 [bits 32]
5 66B83412
6 B834120000

   如果32位的代碼被編譯為16位的代碼就會在機器碼前加入字首。即一種模式下要用另一中模式的操作數大小,需要在指令前加入指令字首0x66。

  以上是操作數大小的改變時的字首,如果是尋址方式改變,則添加字首0x67。

3 .全局描述符表概述

  上面我們已經提到過全局描述符表了,它可以當作一個數組,而段描述符就是這個數組的下标。其結構如下:

自制作業系統Antz day05——深入了解保護模式與進入方法   Github位址:https://github.com/CasterWx  0 .為什麼要有保護模式1 .保護模式的寄存器擴充2 .保護模式的運作模式反轉3 .全局描述符表概述4 .全局描述符表GDT及選擇子5 .進入保護模式

  段描述符是8位元組的,專門用來描述一個記憶體段,8位元組也就是64位,而且是連續的8個位元組。

  保護模式下位址總線是32位,段基址需要32位位址表示,段界限用20位表示,不過這個段界限隻是個機關量,它的機關要麼是位元組,要麼是4KB,這是感覺描述符的G位來确定的。最終段的邊界是此段界限值*機關,故段的大小要麼是2的20次方1MB要麼是2的32次方(4KB==2的12次方)4GB。

  這裡說的1MB和4GB隻是個範圍,并不是具體的邊界值。由于段界限隻是個偏移量,是從0開始算,是以實際的段界限邊界值等于(描述符中段界限+1)*(段界限的粒度大小:4KB或1) -1  。

  這個公式的意思就是表示有多少個4KB或1 。由于描述符中的段界限是從0起的,是以左邊第1個括号中要加個1,表示實際數量,由于位址是從0開始的,是以最後減1 。

  記憶體通路需要用到“段基址:偏移位址”,段界限其實就是用來限制段内偏移位址的,段内偏移位址必須位于段内,否則CPU會抛異常,“段界限*機關”就是限定偏移位址的最值的。

  仔細觀察上面段描述符,你會發現段界限屬性被分為了兩部分,32位的段基址屬性居然被分為了三份,這是為了相容性考慮的。

  段描述符的低32位分為了兩部分,前16位來存儲段的段界限的前0~15位,後16位存儲段基址的0~15位。

  主要屬性都是在段描述符的高32位,0~7位是段基址的16~23位,24~31位是段基址的24~31位,加上段描述符的低32位的0~15位,這下32位的基位址才算完整。

  8~11位的type屬性,四位,用來指定本描述符的類型。一個段描述符在CPU眼中分為兩類,要麼是系統段,要麼是資料段。這是感覺段描述符的S位決定的,它用來訓示是否是系統段,CPU眼中,硬體運作需要的都是系統,軟體需要的都是資料,S是0表示系統段,S是1是資料段。type字段是和S字段配合才能确定段描述符的确切類型,至有S确定了,type才有具體意義。

  再來看type字段,它用于表示記憶體段或門的子類型。

自制作業系統Antz day05——深入了解保護模式與進入方法   Github位址:https://github.com/CasterWx  0 .為什麼要有保護模式1 .保護模式的寄存器擴充2 .保護模式的運作模式反轉3 .全局描述符表概述4 .全局描述符表GDT及選擇子5 .進入保護模式

  這是type在S确定之後的意義,我們需要注意的是非系統段。

  段描述符的第13~14位就是DPL字段,即描述符特權符,這是保護模式提供的安全解決方案,将計算機世界分為不同等級。這兩位可以代表四種特權級,分别是0~4,數字越小特權越大。

  段描述符的第15位的P字段,即段是否存在,如果段存在于記憶體中,P為1,否則P為0。P是由CPU檢查的,如果為0,CPU将抛出異常。

  段描述符的第16~19位是段界限的第16~19位。這樣段界限就齊全了。

  段描述符的第20位是AVL,這位是相對使用者的,暫不用理會。

  段描述符的第21位是L,是用來檢查是否是64位代碼段,在我們32位CPU時,将其置0即可。

  段描述符的第22位是D/B字段,用來表示有效位址及操作數的大小。

  段描述符的第23位是G,用來指定段界限的大小,粒度。

  段描述符的第24~31位是段基址的24~31位,是段基址的最後8位。

4 .全局描述符表GDT及選擇子

  一個段描述符隻能用來定義一個記憶體段,代碼段要占用一個段描述符,資料段和棧段等,多個記憶體段也要各自占用一個段描述符,這些描述符會放在全局描述符表中,也就是GDT,GDT是公用的,它位于記憶體中,需要專門的寄存器指向。這個寄存器就是GDTR,一個48位寄存器。

自制作業系統Antz day05——深入了解保護模式與進入方法   Github位址:https://github.com/CasterWx  0 .為什麼要有保護模式1 .保護模式的寄存器擴充2 .保護模式的運作模式反轉3 .全局描述符表概述4 .全局描述符表GDT及選擇子5 .進入保護模式

  gdtr不能直接用mov gdtr,xxxx的方式初始化,而是有專門的指令,就是lgdt。

  lgdt指令格式是: lgdt 48位記憶體資料

  這48位記憶體資料分為兩部分,前16位是GDT以位元組為機關的界限值,後32位是GDT的其實位址,由于GDT的大小是16位,是以範圍是65536位元組,每個描述符大小是8位元組,是以一個GDT中有8192個段或門。

  在保護模式下,原本存在段寄存器的段基址,現在放在了段描述符中,而段寄存器中放入的是選擇子,就是一個索引,在描述符表中索引描述符的索引。

  段寄存器是16位的,是以選擇子也是16位的,在其低2位即0~1位,用來存儲RPL,即請求特權級,可以表示四種特權。高13位,即3~15是索引部分,2的13次方是8192,故可以索引8192個段,正好吻合GDT的8192個段。

  下圖是描述符表和記憶體段的關系,還有選擇子的結構:

 

自制作業系統Antz day05——深入了解保護模式與進入方法   Github位址:https://github.com/CasterWx  0 .為什麼要有保護模式1 .保護模式的寄存器擴充2 .保護模式的運作模式反轉3 .全局描述符表概述4 .全局描述符表GDT及選擇子5 .進入保護模式
自制作業系統Antz day05——深入了解保護模式與進入方法   Github位址:https://github.com/CasterWx  0 .為什麼要有保護模式1 .保護模式的寄存器擴充2 .保護模式的運作模式反轉3 .全局描述符表概述4 .全局描述符表GDT及選擇子5 .進入保護模式

5 .進入保護模式

  1. 打開A20位址線

    打開A20Gate的方式極其簡單,隻需要将0x92端口的第一個位置置1就好了。

1 in al,0x92
2 or al,0000_0010B
3 out 0x92,al      

  2.保護模式的開關,CR0寄存器的PE位

    這是進入保護模式的最後一步,CR0寄存器的PE位置1。

    

自制作業系統Antz day05——深入了解保護模式與進入方法   Github位址:https://github.com/CasterWx  0 .為什麼要有保護模式1 .保護模式的寄存器擴充2 .保護模式的運作模式反轉3 .全局描述符表概述4 .全局描述符表GDT及選擇子5 .進入保護模式
自制作業系統Antz day05——深入了解保護模式與進入方法   Github位址:https://github.com/CasterWx  0 .為什麼要有保護模式1 .保護模式的寄存器擴充2 .保護模式的運作模式反轉3 .全局描述符表概述4 .全局描述符表GDT及選擇子5 .進入保護模式

    PE為0表示在實模式下運作,PE為1表示在保護模式運作。

1 mov eax,cr0
2 or eax,0x00000001
3 mov cr0,eax      

    寫入完畢,接下來可以讓我們進入保護模式了!

boot.inc :

1 ;-------------     loader和kernel   ----------
 2 
 3 LOADER_BASE_ADDR equ 0x900 
 4 LOADER_START_SECTOR equ 0x2
 5 
 6 ;--------------   gdt描述符屬性  -------------
 7 DESC_G_4K   equ      1_00000000000000000000000b   
 8 DESC_D_32   equ       1_0000000000000000000000b
 9 DESC_L        equ        0_000000000000000000000b    ;  64位代碼标記,此處标記為0便可。
10 DESC_AVL    equ         0_00000000000000000000b    ;  cpu不用此位,暫置為0  
11 DESC_LIMIT_CODE2  equ 1111_0000000000000000b
12 DESC_LIMIT_DATA2  equ DESC_LIMIT_CODE2
13 DESC_LIMIT_VIDEO2  equ 0000_000000000000000b
14 DESC_P        equ          1_000000000000000b
15 DESC_DPL_0  equ           00_0000000000000b
16 DESC_DPL_1  equ           01_0000000000000b
17 DESC_DPL_2  equ           10_0000000000000b
18 DESC_DPL_3  equ           11_0000000000000b
19 DESC_S_CODE equ             1_000000000000b
20 DESC_S_DATA equ      DESC_S_CODE
21 DESC_S_sys  equ             0_000000000000b
22 DESC_TYPE_CODE  equ          1000_00000000b    ;x=1,c=0,r=0,a=0 代碼段是可執行的,非依從的,不可讀的,已通路位a清0.  
23 DESC_TYPE_DATA  equ          0010_00000000b    ;x=0,e=0,w=1,a=0 資料段是不可執行的,向上擴充的,可寫的,已通路位a清0.
24 
25 DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00
26 DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00
27 DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b
28 
29 ;--------------   選擇子屬性  ---------------
30 RPL0  equ   00b
31 RPL1  equ   01b
32 RPL2  equ   10b
33 RPL3  equ   11b
34 TI_GDT     equ   000b
35 TI_LDT     equ   100b      

loader.asm:

1    %include "boot.inc"
 2    section loader vstart=LOADER_BASE_ADDR
 3    LOADER_STACK_TOP equ LOADER_BASE_ADDR
 4    jmp loader_start                
 5    GDT_BASE:   dd    0x00000000 
 6            dd    0x00000000
 7 
 8    CODE_DESC:  dd    0x0000FFFF 
 9            dd    DESC_CODE_HIGH4
10 
11    DATA_STACK_DESC:  dd    0x0000FFFF
12              dd    DESC_DATA_HIGH4
13 
14    VIDEO_DESC: dd    0x80000007           ;limit=(0xbffff-0xb8000)/4k=0x7
15            dd    DESC_VIDEO_HIGH4  
16 
17    GDT_SIZE   equ   $ - GDT_BASE
18    GDT_LIMIT   equ   GDT_SIZE -    1 
19    times 60 dq 0
20    SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0         
21    SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0    
22    SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0     
23 
24    gdt_ptr  dw  GDT_LIMIT 
25         dd  GDT_BASE
26    loadermsg db '2 loader in real.'
27 
28    loader_start:
29 
30    mov     sp, LOADER_BASE_ADDR
31    mov     bp, loadermsg           
32    mov     cx, 17            
33    mov     ax, 0x1301        
34    mov     bx, 0x001f         
35    mov     dx, 0x1800        
36    int     0x10                    
37 
38     in al,0x92
39    or al,0000_0010B
40    out 0x92,al
41    lgdt [gdt_ptr]
42    mov eax, cr0
43    or eax, 0x00000001
44    mov cr0, eax
45 
46    ;jmp dword SELECTOR_CODE:p_mode_start
47    jmp  SELECTOR_CODE:p_mode_start     
48 
49 [bits 32]
50 p_mode_start:
51    mov ax, SELECTOR_DATA
52    mov ds, ax
53    mov es, ax
54    mov ss, ax
55    mov esp,LOADER_STACK_TOP
56    mov ax, SELECTOR_VIDEO
57    mov gs, ax
58 
59    mov byte [gs:160], 'P'
60 
61    jmp $      

Antz_mbr.asm:

1 %include "boot.inc"
 2 SECTION MBR vstart=0x7c00
 3    mov ax,cs
 4    mov ds,ax
 5    mov es,ax
 6    mov ss,ax
 7    mov fs,ax
 8    mov sp,0x7c00
 9    mov ax,0xb800
10    mov gs,ax
11 
12    mov     ax, 0600h
13    mov     bx, 0700h
14    mov     cx, 0                
15    mov     dx, 184fh        
16    int     10h                    
17 
18    mov byte [gs:0x00],'1'
19    mov byte [gs:0x01],0xA4
20 
21    mov byte [gs:0x02],' '
22    mov byte [gs:0x03],0xA4
23 
24    mov byte [gs:0x04],'M'
25    mov byte [gs:0x05],0xA4    
26 
27    mov byte [gs:0x06],'B'
28    mov byte [gs:0x07],0xA4
29 
30    mov byte [gs:0x08],'R'
31    mov byte [gs:0x09],0xA4
32 
33    mov eax,LOADER_START_SECTOR     
34    mov bx,LOADER_BASE_ADDR      
35    mov cx,4             
36    call rd_disk_m_16         
37 
38    jmp LOADER_BASE_ADDR
39 
40 rd_disk_m_16:
41 
42       mov esi,eax      ;備份eax
43       mov di,cx        
44 
45 
46       mov dx,0x1f2
47       mov al,cl
48       out dx,al           
49 
50       mov eax,esi       
51 
52       mov dx,0x1f3
53       out dx,al
54 
55 
56       mov cl,8
57       shr eax,cl
58       mov dx,0x1f4
59       out dx,al
60 
61       shr eax,cl
62       mov dx,0x1f5
63       out dx,al
64 
65       shr eax,cl
66       and al,0x0f      
67       or al,0xe0      
68       mov dx,0x1f6
69       out dx,al
70 
71       mov dx,0x1f7
72       mov al,0x20
73       out dx,al
74 
75   .not_ready:
76       nop
77       in al,dx
78       and al,0x88       
79       cmp al,0x08
80       jnz .not_ready     
81 
82       mov ax, di
83       mov dx, 256
84       mul dx
85       mov cx, ax       
86 
87       mov dx, 0x1f0
88   .go_on_read:
89       in ax,dx
90       mov [bx],ax
91       add bx,2
92       loop .go_on_read
93       ret
94 
95    times 510-($-$$) db 0
96    db 0x55,0xaa      

繼續閱讀