自制操作系統流程筆記(一)


1.第一節:最小的操作系統(引導扇區)

1.1環境准備

1.vmware虛擬機:

建立一個新的空白的虛擬機,命名為Tinix.添加軟盤,使用自制的映像文件

虛擬機設置:

  

2.nasm編譯器

3.notepad++

1.2制作映像文件

1.boot.asm文件

    org 07c00h
    mov ax,cs
    mov ds,ax
    mov es,ax
    call DispStr
    jmp $
DispStr:
    mov ax,BootMessage
    mov bp,ax
    mov cx,16
    mov ax,01301h
    mov bx,000ch
    mov dl,0
    int 10h
    ret
BootMessage: db "Hello, OS world!"
times 510-($-$$) db 0
dw 0xaa55

2.使用nasm編譯

cmd.exe中使用命令:

nasm boot.asm -o boot.bin

1.3 啟動系統

將boot.bin文件放如軟盤中,啟動Tinix系統:

至此完成自制操作系統的第一步(boot sector)引導扇區.

 

第二小節:保護模式

2.1 實模式到保護模式 GDT表

參考:https://blog.csdn.net/yihaolovem/article/details/23483927

備注:寄存器參考:https://www.cnblogs.com/joey-hua/p/5347257.html

         https://blog.csdn.net/jnu_simba/article/details/11712675

     段頁機制:https://www.cnblogs.com/chenwb89/p/operating_system_003.html

        https://blog.csdn.net/suppercoder/article/details/9422093

        https://blog.csdn.net/fallingu/article/details/75221276

        https://blog.csdn.net/bian1029/article/details/49124593(GDT表第一項為0)

 

; ==========================================
; pmtest1.asm
; 編譯方法:nasm pmtest1.asm -o pmtest1.com
; ==========================================

%include    "pm.inc"    ; 常量, 宏, 以及一些說明

org    0100h
    jmp    LABEL_BEGIN

[SECTION .gdt]
; GDT
;                                         段基址,      段界限     , 屬性
LABEL_GDT:        Descriptor           0,                0, 0             ; 空描述符
LABEL_DESC_CODE32:    Descriptor           0, SegCode32Len - 1, DA_C + DA_32    ; 非一致代碼段, 32
LABEL_DESC_VIDEO:    Descriptor     0B8000h,           0ffffh, DA_DRW        ; 顯存首地址
; GDT 結束

GdtLen        equ    $ - LABEL_GDT    ; GDT長度
GdtPtr        dw    GdtLen - 1    ; GDT界限
        dd    0        ; GDT基地址

; GDT 選擇子
SelectorCode32        equ    LABEL_DESC_CODE32    - LABEL_GDT
SelectorVideo        equ    LABEL_DESC_VIDEO    - LABEL_GDT
; END of [SECTION .gdt]

[SECTION .s16]
[BITS    16]
LABEL_BEGIN:
    mov    ax, cs
    mov    ds, ax
    mov    es, ax
    mov    ss, ax
    mov    sp, 0100h

    ; 初始化 32 位代碼段描述符
    xor    eax, eax
    mov    ax, cs
    shl    eax, 4
    add    eax, LABEL_SEG_CODE32
    mov    word [LABEL_DESC_CODE32 + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_CODE32 + 4], al
    mov    byte [LABEL_DESC_CODE32 + 7], ah

    ; 為加載 GDTR 作准備
    xor    eax, eax
    mov    ax, ds
    shl    eax, 4
    add    eax, LABEL_GDT        ; eax <- gdt 基地址
    mov    dword [GdtPtr + 2], eax    ; [GdtPtr + 2] <- gdt 基地址

    ; 加載 GDTR
    lgdt    [GdtPtr]

    ; 關中斷
    cli

    ; 打開地址線A20
    in    al, 92h
    or    al, 00000010b
    out    92h, al

    ; 准備切換到保護模式
    mov    eax, cr0
    or    eax, 1
    mov    cr0, eax

    ; 真正進入保護模式
    jmp    dword SelectorCode32:0    ; 執行這一句會把 SelectorCode32 裝入 cs, 並跳轉到 Code32Selector:0  處
; END of [SECTION .s16]


[SECTION .s32]; 32 位代碼段. 由實模式跳入.
[BITS    32]

LABEL_SEG_CODE32:
    mov    ax, SelectorVideo
    mov    gs, ax            ; 視頻段選擇子(目的)

    mov    edi, (80 * 10 + 0) * 2    ; 屏幕第 10 行, 第 0 列。
    mov    ah, 0Ch            ; 0000: 黑底    1100: 紅字
    mov    al, 'P'
    mov    [gs:edi], ax

    ; 到此停止
    jmp    $

SegCode32Len    equ    $ - LABEL_SEG_CODE32
; END of [SECTION .s32]

1、[SECTION .XXX]為何物?
SECTION和SEGMENT的作用相類似,就是代表“段”的意思。從整個程序來看,該程序分為3個模塊,分別是[SECTION .gdt]、[SECITON .s16]、[SECTION .s32]三部分。我們很容易就可以看出,其中的[SECTION .gdt]應該是數據段,其他的兩個是代碼段。通過[SECTION .XXX]將程序分成不同模塊,完成不同的功能,使得程序看起來清晰明了。

2. 段描述符宏定義和初始化段描述符

描述符宏定義:

; 宏 ------------------------------------------------------------------------------------------------------
;
; 描述符
; usage: Descriptor Base, Limit, Attr  定義三個變量,段基址,段界限,段屬性
;        Base:  dd  4個字節
;        Limit: dd (low 20 bits available)  4個字節
;        Attr:  dw (lower 4 bits of higher byte are always 0)  2個字節
%macro Descriptor 3          ;%macro是宏定義,Descriptor是宏名,3表示該宏有三個參數
    dw    %2 & 0FFFFh                ; 段界限1,段界限值0-15位 注:匯編語言中數據不能以字母開頭
    dw    %1 & 0FFFFh                ; 段基址1,段基地址0-15
    db    (%1 >> 16) & 0FFh            ; 段基址2,段基地址16-23位
    dw    ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)    ; 屬性1 + 段界限2 + 屬性2
    db    (%1 >> 24) & 0FFh            ; 段基址3,基地址24-31位
%endmacro ; 共 8 字節

Base是%1,Limit是%2,Attr是%3。

段描述符結構圖:

P,present位,1表示所描述的段存在(有效),為0表示所描述的段無效,使用該描述符會引起異常 
DPL,Descriptor privilege,描述符特權級別,說明所描述段的特權級別 
S,描述符類型位,1說明當前描述符為存儲段描述符,0為系統描述符或門描述符. 
TYPE: 
位0:A(accessed)位,表明描述符是否已被訪問;把選擇子裝入段寄存器時,該位被標記為1 
位3:E(EXECUTABLE?)位,0說明所描述段為數據段;1為可執行段(代碼段)
當為數據段時, 
   位1為W位,說明該數據段是否可寫(0只讀,1可寫) 
   位2為ED位,說明該段的擴展方向(0向高位擴展,1向低位擴展) 
當為可執行段是, 
   位1為R位,說明該執行段是否可讀(0只執行,1可讀) 
   位2為C位,0說明該段不是一致碼段(普通代碼段),1為一致碼段 
G為粒度位,0說明LIMIT粒度為字節,1為4K字節. 
D位: 
   1.在可執行段中,D為1,表示使用32位地址,32/8位操作數;為0表示使用16位地址,16/8位操作數 
   2.在由SS尋址的段描述符(堆棧段?)中,D為1表示隱含操作(如PUSH/POP)使用ESP為堆棧指針,
     為0使用SP(隱含操作:未明確定義段屬性類型USE16/USE32?66H,67H?) 
   3.在向低擴展的存儲段中,D為1,表示段的上限為4G;為0上限為64K

參考:https://images2015.cnblogs.com/blog/790694/201606/790694-20160619001102057-493760916.png

GDT的作用是用來提供段式存儲機制,這種機制是段寄存器和GDT中的描述符共同提供的。每個描述符在GDT中占8字節,也就是 2 個雙字,或者說是 64 位。圖中,下面是低32位,上面是高32位。

將Limit低16位賦值給描述符的BYTE0和BYTE1
將Base低16位賦值給描述符的BYTE2和BYTE3
將Base右移16位后的低8位(也就是原Base的第16—23位)賦值給描述符的BYTE4
將Limit右移8位之后的第8—11位和Attr的0—7和12—15位,組合起來存儲到描述符的BYTE5和BYTE6
將Base右移24位后的低8位(也就是原Base的24—32位)賦值給描述符的BYTE7

初始化段描述符:

    ; 初始化 32 位代碼段描述符
    xor    eax, eax
    mov    ax, cs
    shl    eax, 4       ;左移四位 = cs*16
    add    eax, LABEL_SEG_CODE32   ;有效地址 = 段值*16 + offset
    mov    word [LABEL_DESC_CODE32 + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_CODE32 + 4], al
    mov    byte [LABEL_DESC_CODE32 + 7], ah

為什么要初始化?你會發現這里只是修改了段描述符基地址,即LABEL_DESC_CODE32的BYTE2,BYTE4,BYTE7。是不是突然恍然大悟?因為在我們初始化該LABEL_DESC_CODE32描述符時,將其基地址初始化為0,所以我們要修改描述符的基地址為其實際的地址。這也是在前面介紹段描述符的時候,我提醒大家需要注意的地方,即描述符的基地址所占有的字節是BYTE2,BYTE4,BYTE7(共32bit)。

詳解:

mov ax,cs

shl eax,4

將cs的值(16bit 實模式下即為當前代碼段的基地址) 

左移4bit即得到當前代碼段的物理基地址

add eax,LABEL_SEG_CODE32

此時eax即為實模式下LABEL_SEG_CODE32的物理地址

然后再用此地址分為三部分去初始化LABEL_DESC_CODE32

3.加載GDTR

    ; 為加載 GDTR 作准備
    xor    eax, eax
    mov    ax, ds
    shl    eax, 4
    add    eax, LABEL_GDT        ; eax <- gdt 基地址
    mov    dword [GdtPtr + 2], eax    ; [GdtPtr + 2] <- gdt 基地址

    ; 加載 GDTR
    lgdt    [GdtPtr]

這個很好理解,我們就是對GdtPtr進行賦值,主要是初始化GDT的基地址。也就是將GDT的初始地址,賦值給GdtPtr的BYTE2,BYTE3,BYTE4,BYTE5。使GdtPtr的數據結構剛好符合GDTR,然后執行lgdt [GdtPtr],加載全局描述符表寄存器。將GDT的基地址和界限賦值給GDTR。

2.2 LDT 局部描述符表

LDT和GDT本質上都是段描述符表,具有相同的結構,區別僅在於全局和局部的不同。

參考:https://blog.csdn.net/wrx1721267632/article/details/52056910

  1. LDT在系統中可以存在多個
  2. LDT不是全局可見的,它們只對引用它們的任務可見,每個任務最多可以擁有一個LDT。
  3. 每一個LDT自身作為一個段存在,它們的段描述符被放在GDT中。

LDTR寄存器: 

IA-32為LDT的入口地址也提供了一個寄存器LDTR,因為在任何時刻只能有一個任務在運行,所以LDT寄存器全局也只需要有一個。如果一個任務擁有自身的LDT,那么當它需要引用自身的LDT時,它需要通過lldt指令將其LDT的段描述符裝入此寄存器。lldt指令與lgdt指令不同的時,lgdt指令的操作數是一個32-bit的內存地址,這個內存地址處存放的是一個32-bit GDT的入口地址,以及16-bit的GDT Limit。而lldt指令的操作數是一個16-bit的選擇子,這個選擇子主要內容是:被裝入的LDT的段描述符在GDT中的索引值。

至此,我們可以這樣理解GDT和LDT:GDT為一級描述符表,LDT為二級描述符表。如圖:

段選擇子:

引用GDT和LDT中的段描述符所描述的段,是通過一個16-bit的數據結構來實現的,這個數據結構叫做Segment Selector——段選擇子。它的高13位作為被引用的段描述符在GDT/LDT中的下標索引,bit 2用來指定被引用段描述符被放在GDT中還是到LDT中,bit 0和bit 1是RPL——請求特權等級,被用來做保護目的。如圖所示:

 

 

 

2.3 特權級

在IA32的分段機制中,特權級總共有四個特權級別,從高到低分別對應0,1,2,3。數字越小表示特權越大。

 

 作用:防止低特權級應用訪問高特權級數據,代碼。

特權級檢驗主要通過CPL,DPL,RPL來實現。

1.CPL

當前執行程序或任務的特權級。

被存儲在CS的第0位和SS的第一位上。

通常表示代碼段的特權級。

2.DPL

表示段或門的特權級。

存儲在段描述符或門描述符的DPL字段中。

3.RPL

段選擇子的第0位和第1位。

系統調用時使用RPL作為調用者的特權級。

補充:

參考:https://www.cnblogs.com/LittleHann/p/3850655.html

門描述符:

處理器對程序的執行主要時順序和跳轉兩種方式,跳轉也就是程序控制的轉移,可以通過指令jmp、call、ret、sysenter、

sysexit、int n、iret引起,也可以由中斷和異常機制引起。

在I386CPU中,除了"段描述符"(描述某種內存段)之外還有一種描述符叫做"門描述符"(描述控制轉移的入口點,也就是異常控制中斷的入口點),通過這種門可以實現特權級的轉變和任務的切換。門描述符主要由以下幾部分組成:

1. 選擇子
2. 偏移地址
3. DPL

分為4種類型:

  • 調用門
  • 中斷門
  • 陷阱門
  • 任務門
1. 調用門描述符
調用門一般用在特權級的切換,存在於GDT中或者LDT中。調用門的選擇子指向代碼段描述符,偏移地址對應代碼段中的偏移量。當jump和call指令的操作數是調用門的時候,就會跳轉到對應的代碼處,並發生特權級的變化,也就會發生
堆棧的切換

2. 任務門描述符
任務門一般用在任務的切換,可以存放在GDT、LDT或IDT中。任務門的選擇子指向GDT中的TSS選擇符,偏移地址沒有意義。當jmp和Call指令的操作數是任務門的時候,就會發生任務的切換。

3. 中斷門描述符
4. 陷阱門描述符 中斷門描述符、陷阱門描述符用來對中斷服務例程進行尋址,從原理上來理解,中斷例程的尋址本質上也是內存的尋址

 

 

保護模式初步總結:

  1. 描述符中段基址、段界限、段屬性對段的一種保護
  2. 門描述符中特權級之間的變換
  3. 涉及到特權級的每一步中,處理器都會對CPL、DPL、RPL等內容進行比較。

關於段描述符和門描述符參考:https://blog.csdn.net/barech/article/details/4401417

可以理解為,段描述符是對存儲訪問的管理控制,門描述符是對調用,特權級轉變的管理控制。2019-04-25

 保護模式總結更新:

 

  1. 在GDT、LDT、IDT中,每一個描述符都有自己的界限和屬性 -- 對描述符所描述對象的一種限定和保護
  2. 分頁機制中的PDE和PTE都含有R/W和U/S位 -- 頁級保護
  3. 頁式存儲使應用程序使用的是線性地址空間 -- 物理內存被保護起來
  4. 中斷不再像實模式下一樣使用 -- 特權檢驗,中斷調用被保護
  5. I/O指令不再隨便使用 -- 端口被保護起來
  6. 在程序運行過程中,如遇到不同特權級間的訪問,會對CPL、RPL、DPL、IOPL等內容進行檢驗,同時進行堆棧切換 -- 對不同層級的程序進行了保護。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM