紙上得來終覺淺,絕知此事還是得搗鼓搗鼓才行啊。理論與實踐還是要兩手抓兩手都要硬嘛。
從理論上來說,在ret指令執行前,堆疊中應該已經準備好了目標**段的cs、eip,以及ss和esp,另外還可能有引數。需要注意的是,這些既可以使處理器壓入棧的,也可以使由我們自己壓入棧的。ret指令執行前的堆疊可能如下圖示:
這樣,執行ret後就可以轉移到低特權級的**中 了。我們在前面寫過的**中新增可以新增乙個ring3的**段和乙個ring3的堆疊段,如下:
label_desc_code_ring3: descriptor 0,segcodering3len-1, da_c+da_32+da_dpl3
……label_desc_stack3: descriptor 0, topofstack3, da_drwa+da_32+da_dpl3
……label_desc_video: descriptor 0b8000h, 0ffffh, da_drw+da_dpl3
……selectorcodering3 equ label_desc_code_ring3 - label_gdt + sa_rpl3
……selectorstack3 equ label_desc_stack3 - label_gdt + sa_rpl3
……; 堆疊段ring3
[section .s3]
align 32
[bits 32]
label_stack3:
times 512 db 0
topofstack3 equ $ - label_stack3 - 1
; end of [section .s3]
……; codering3
[section .ring3]
align 32
[bits 32]
label_code_ring3:
mov ax, selectorvideo
mov gs, ax
mov edi, (80 * 14 + 0) * 2
mov ah, 0ch
mov al, '3'
mov [gs:edi], ax
jmp $
segcodering3len equ $ - label_code_ring3
; end of [section .ring3]
這段ring3的**一目了然,只是由於這段**執行在ring3,而在其中要寫視訊記憶體而訪問到video段,為了不會產生錯誤,我們把video段的dpl修改為了3。
另外,新段對應的描述符的屬性加上了da_dpl3,讓它的dpl變成了3,相應的選擇子的sa_rpl3將rpl也置為了3。初始化描述符的**與前面相似,就不再重複了。這樣,**段和堆疊段就準備好了,下一步就是為retf指令準備ss、esp、cs、eip這些暫存器的目標值了,具體操作如下:
push selectorstack3
push topofstack3
push selectorcodering3
push 0
retf
至此需要修改的就改完了,我們將程式編譯為test.com然後拷貝到pm.img中,可以執行程式看到如下結果(書本完整原始碼pmtest5a.asm):
看到最後紅色的3就表明我們已經由ring0跳轉到ring3了!歷史性的特權級間跳轉成功!
進入ring3之後就可以體驗一下呼叫門了,先將呼叫門的描述符和選擇子以及[section .ring3]的**稍作修改。
; 門 目標選擇子,偏移,dcount, 屬性
label_call_gate_test: gate selectorcodedest, 0, 0, da_386cgate+da_dpl3
……selectorcallgatetest equ label_call_gate_test - label_gdt + sa_rpl3
……; codering3
[section .ring3]
align 32
[bits 32]
label_code_ring3:
mov ax, selectorvideo
mov gs, ax
mov edi, (80 * 14 + 0) * 2
mov ah, 0ch
mov al, '3'
mov [gs:edi], ax
call selectorcallgatetest:0
jmp $
segcodering3len equ $ - label_code_ring3
; end of [section .ring3]
在進入死迴圈前,我們使用了呼叫門指令,修改相應的描述符和選擇子是為了滿足cpl和rpl都小於等於呼叫門dpl的條件。但是現在還不能執行,因為從低特權級到高特權級的轉移需要tss的幫助,下面我們就來準備乙個tss。
label_desc_tss: descriptor 0, tsslen-1, da_386tss
……selectortss equ label_desc_tss - label_gdt
……; tss
[section .tss]
align 32
[bits 32]
label_tss:
dd 0 ; back
dd topofstack ; 0 級堆疊
dd selectorstack ;
dd 0 ; 1 級堆疊
dd 0 ;
dd 0 ; 2 級堆疊
dd 0 ;
dd 0 ; cr3
dd 0 ; eip
dd 0 ; eflags
dd 0 ; eax
dd 0 ; ecx
dd 0 ; edx
dd 0 ; ebx
dd 0 ; esp
dd 0 ; ebp
dd 0 ; esi
dd 0 ; edi
dd 0 ; es
dd 0 ; cs
dd 0 ; ss
dd 0 ; ds
dd 0 ; fs
dd 0 ; gs
dd 0 ; ldt
dw 0 ; 除錯陷阱標誌
dw $ - label_tss + 2 ; i/o點陣圖基址
db 0ffh ; i/o點陣圖結束標誌
tsslen equ $ - label_tss
上面我們只初始化了0級堆疊,新增初始化tss描述符的**後,tss就算是準備好了,我們需要在特權級轉換之前載入它。
call dispreturn
mov ax, selectortss
ltr ax
push selectorstack3
push topofstack3
push selectorcodering3
push 0
retf
到目前為止,我們已經成功地實現了由高特權級到低特權級或者由低特權級到高特權級的雙向轉換,並且熟悉了呼叫門這種經典門描述符的使用。
為了可以讓程式順利地返回實模式,我們將呼叫區域性任務的**加入到呼叫門的目標**(section .sdest)。最後,程式將由這裡進入區域性任務,然後原路返回實模式。
[section .sdest]; 呼叫門目標段
[bits 32]
label_seg_code_dest:
mov ax, selectorvideo
mov edi, (80 * 12 + 0) * 2 ; 螢幕第 12 行, 第 0 列。
mov ah, 0ch ; 0000: 黑底 1100: 紅字
mov al, 'c'
mov [gs:edi], ax
; load ldt
mov ax, selectorldt
lldt ax
jmp selectorldtcodea:0 ; 跳入區域性任務,將列印字母 'l'。
;retf
segcodedestlen equ $ - label_seg_code_dest
; end of [section .sdest]
最後的執行結果如下圖(完整**pmtest5.asm):
總結一下過程:乙個是從高特權級到低特權級的跳轉如何確定是跳轉到哪一級呢?這個是由當時對應堆疊中的cs、eip等決定的。那從低特權級到高特權級又是如何確定的呢?根據目標**段的dpl(新的cpl)從tss中選擇應該切換至哪個ss和esp。
第二章 作業系統
考試題型分析 選擇題 考試時間 上午試題型別 選擇題分值 6 8分 分值佔比 舉例 包餃子。前驅圖表示出了有些任務是有先後順序的,有些任務是可以並行操作的,箭頭表示約束。首次適應法 最佳適應法 最差適應法 迴圈首次適應法 邏輯位址與實體地址轉換 那麼如何根據邏輯位址求實體地址呢?我們得首先知道那一部...
作業系統實踐之第二章(LDT的使用)
前面我們已經使用過gdt了,本節主要體驗一下ldt的使用。首先ldt也是描述符表,跟gdt的區別主要在於g global 和l local 上的不同。那麼我們如何在 中使用ldt呢?下面將給出示例。使用ldt的主要 框架如下 section gdt label desc ldt descriptor...
作業系統 第二章 作業系統基礎操作
計算機體系結構概述 計算機記憶體和硬碟布局 開機順序 背景中斷 異常和系統呼叫相比較 中斷和異常處理機制 系統呼叫概念 系統呼叫的實現 程式呼叫與系統呼叫的不同之處開銷 2 disk 存放os 3 bios 存放i o處理系統 4 bios 載入os到記憶體中。5 post 加電自檢 尋找顯示卡和執...