亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫(kù)
最近訪問板塊 發(fā)新帖
樓主: mik
打印 上一主題 下一主題

【x86 & x64 沉思錄】(6.7 更新) [復(fù)制鏈接]

論壇徽章:
0
21 [報(bào)告]
發(fā)表于 2009-04-06 22:46 |只看該作者
話題 7、    x86 & x64 提供的保護(hù)措施


  x64(x86)提供了 segmentation 與 paging 級(jí)的保護(hù)措施,x86 提供完整的 segmentation 保護(hù)措施,而 x64 提供了有限的 segmentation 保護(hù)措施。

  對(duì)于 segmentation 級(jí)的保護(hù)來(lái)說,processor 定義了 4 個(gè)權(quán)限級(jí)別,為:0 ~ 3 級(jí),0 為最高級(jí)別,3 為最低級(jí)別。0 級(jí)能訪問所有的 processor 資源,如:所有指令、I/O的訪問權(quán)限,一些系統(tǒng)的管理資源等。3 級(jí)只能有限度的訪問 processor 資源。
  對(duì)于 paging 級(jí)的保護(hù)來(lái)說,processor 實(shí)際上只使用了 2 個(gè)權(quán)限級(jí)別,為:supervisor 與 user 級(jí)別。3 級(jí)就是 user 權(quán)限,而 0 ~ 2 都劃分為 supervisor 權(quán)限。在相應(yīng)的 page table entry 結(jié)構(gòu)中只有 1 位表示 supervisor /user 權(quán)限。




7.1、  segmentation 級(jí)保護(hù)措施

  在 segmentation 階段控制訪問權(quán)限時(shí),定義了 3 個(gè)權(quán)限級(jí)別類型:RPL、CPL 以及 DPL。

RPL(Requestor Privilege Level):在使用 selector 時(shí)定義的權(quán)限級(jí)別類型,表示:你將使用什么樣的權(quán)限去訪問數(shù)據(jù)。用哪個(gè)級(jí)別的權(quán)限取決于你。在主動(dòng)發(fā)起訪問時(shí),使用 0 級(jí)還是 3 級(jí)去訪問完全取決于你。

CPL(Current Privilege Level):表示當(dāng)前 processor 運(yùn)行于哪個(gè)權(quán)限級(jí)別。在 Intel 的文檔里表明:CPL 存儲(chǔ)在 CS 寄存器 selector 里的 RPL(實(shí)質(zhì)上是 DPL) 域里,當(dāng)發(fā)生權(quán)限的改變時(shí)會(huì)更新 CS selector 里的 RPL(實(shí)質(zhì)上是 DPL) 域。沒錯(cuò),的確可以這樣理解!
  在 AMD 的文檔沒有明確表明 CPL 就是 CS selector 里的 DPL,只是描述了 CPL 會(huì)存儲(chǔ)在 processor 內(nèi)部的寄存器里,但是理解為:CPL 就是 CS selector 里的 DPL 是不會(huì)錯(cuò)誤。這里有一個(gè)很重要線索就是:在 REST# 后,CS selector 的 DPL 是 0,表明實(shí)模式的 CPL 是 0 級(jí)。

DPL(Descriptor Privilege Level):在 descriptor 里定義了 DPL,表明訪問的目標(biāo)將需要什么級(jí)別的權(quán)限。這代表一個(gè)訪問門坎,你需要的權(quán)限至少要不能小于這個(gè)設(shè)定的門坎。
  DPL 設(shè)為 2 級(jí),那么你最少需要的 RPL 為 2 以及 CPL 為 2。


  RPL、CPL 以及 DPL 構(gòu)造了 x86 & x64 segmentation 階段的控制訪問的權(quán)限檢查機(jī)制。在絕大部分情況下,需要 RPL <= DPL && CPL <= DPL 的訪問權(quán)限去訪問 segment descriptor 或 gate descriptor。


  processor 除了對(duì)訪問權(quán)限的檢查外,還要圍繞 selector、descriptor table、TSS segment 以及 descriptor 等系統(tǒng)數(shù)據(jù)結(jié)構(gòu)進(jìn)行相關(guān)的檢查,如:Null-selector、limit、type 等,以確保當(dāng)前的執(zhí)行環(huán)境是正確有效的。





:wink:

[ 本帖最后由 mik 于 2009-4-7 11:42 編輯 ]

論壇徽章:
0
22 [報(bào)告]
發(fā)表于 2009-04-07 12:20 |只看該作者
7.1.1、 date segment 的訪問控制

  這里的 data segment 訪問控制針使用對(duì) DS、ES、FS 以及 GS selector register 進(jìn)行訪問,不包括 SS selector register,stack segment 訪問與一般的 data segment 有些差別。

  以 DS 為例代表所有的 data segment 訪問(ES、FS 以及 GS),需要的權(quán)限條件是:RPL(DS.RPL) <= DPL 且 CPL(CS.DPL) <= DPL。
  這里的 RPL 就是 DS.RPL,即:使用 ds selector 進(jìn)行 data segment 訪問時(shí),使用什么級(jí)別的 RPL。CPL 就是當(dāng)前 CS selector 里的 DPL,代表 processor 處于什么權(quán)限級(jí)別。
  DPL 就是目標(biāo) data segment descriptor 所定義的訪問 data segment 所需要的權(quán)限。


用簡(jiǎn)單的式子表達(dá)為:

if (RPL <= DPL && CPL <= DPL) {
  /* 通過權(quán)限檢查,允許訪問,加載 descriptor 進(jìn)入 DS */

} else {
  /* 拒絕訪問,產(chǎn)生 #GP 異常 */
  goto do_#GP
}


  式子中的比較表示在數(shù)字上大小關(guān)系。數(shù)字越大權(quán)限越低,所以 RPL <= DPL 是判斷 RPL 權(quán)限級(jí)別是否大于 DPL 的權(quán)限級(jí)別
  RPL 和 CPL 的權(quán)限極別必須大于或等于 DPL 的權(quán)限級(jí)別。


  在得到允許訪問后的后續(xù)處理:目標(biāo) data segment descriptor 將加到了 DS 寄存器中。在不改變?cè)L問的目標(biāo) data segment 的情況下,processor 不會(huì)對(duì) DS 進(jìn)行更新,也就是:不會(huì)得新加載 data segment descriptor。
  DS 寄存器保持著內(nèi)部的 descriptor 記錄不變,直至到更新 DS 寄存器。因此,對(duì)同一個(gè) data segment 的訪問僅需要加載 1 次 descriptor 。這種情況對(duì)于所有的 selector registers 都是一樣的。


情景提示:
  當(dāng) processor 發(fā)現(xiàn) descriptor 的 P 標(biāo)志位為 0 時(shí),將引發(fā) #NP 異常。所以系統(tǒng)必須要設(shè)定 descriptor 的 P 標(biāo)志位為 1,processor 不會(huì)對(duì) P 標(biāo)志位進(jìn)行更改。
  系統(tǒng)將 P 標(biāo)志置為 0,有時(shí)會(huì)特定的用途,大多數(shù)OS 會(huì)在軟件中會(huì)引發(fā)類似 segment falut 錯(cuò)誤提示,指示程序員犯了某些錯(cuò)誤。





7.1.1.1、 x64 的 long mode 下 data segment 的訪問

  在 long mode 下的兼容模式與 x86 下的 data segment 的控制訪問檢查并無(wú)兩樣。但是在 64 bit 模式下就完全改變了。


在 64 bit 模式下:
  processor 不對(duì) data segment 的訪問進(jìn)行任何的權(quán)限檢查!data segment descriptor 中僅有 P 標(biāo)志位是有效的,其它都無(wú)效。也就是 DS、ES、FS、GS 以及 SS 中除 P 屬性有效外,其它都是無(wú)效的。
  所有基于 DS、ES 以及 SS 的 data segment 的 base 是無(wú)效的,被強(qiáng)制為 0。而基于 FS 與 GS 的 data segment 的 base 是有效的,故 FS 與 GS 的 data segment 可以定位在 64 位地址空間的任何位置。



所以:
  1、所有的 data segment 的訪問都不進(jìn)行權(quán)限檢查,包括 DS、ES、FS、GS 以及 SS。這是因?yàn),?64bit 下所有的 data segment descriptor 的 DPL 屬性是無(wú)效的。
 。、所有的 data segment 的訪問也不進(jìn)行 type、limit 檢查,這些屬性都是無(wú)效的。64 bit 下,所有的 data segment 都是 Readable/writable 屬性。所有的 data segment 都具有 64 位地址空間。
 。场⑺械牡刂 limit 檢查都變?yōu)?canonical-address 的地址形式檢查。




:wink:

[ 本帖最后由 mik 于 2009-4-7 14:52 編輯 ]

論壇徽章:
0
23 [報(bào)告]
發(fā)表于 2009-04-07 14:14 |只看該作者
7.1.2、  stack segment 的訪問控制


  基于 stack 的訪問都將引發(fā) stack 訪問控制檢查,對(duì)于 stack 的訪問控制比一般的 data segment 訪問檢查要嚴(yán)格。


用簡(jiǎn)單的式子表達(dá)為:

if (RPL == DPL && CPL = DPL) {
  /* 通過檢查,允許訪問,加載 descriptor 進(jìn)入 SS */

} else {
  /* 拒絕訪問,引發(fā) #GP 異常 */
  goto do_#GP
}


  訪問 stack 僅限于同級(jí)訪問,RPL、CPL 以及 DPL 三者必須相等。即使 0 級(jí)代碼也不能訪問 3 級(jí)的 stack。

So:
  TSS segment 中為每個(gè)權(quán)限級(jí)為了相應(yīng)的 stack pointer,就是基于這個(gè)原因,TSS 中設(shè)了 0 ~ 2 級(jí)的 stack pointer,但不包括 3 級(jí)的 stack pointer,3 級(jí)的 stack pointer 要么存放在 SS & RSP 中,要么存放在 0 ~ 2 級(jí)的 stack 中。
  若發(fā)生代碼從 3 級(jí)切換到 0 級(jí)時(shí),切換 stack 時(shí),3 級(jí)的 stack pointer 被切換出來(lái),存放在 0 級(jí)的 stack 中,而 0 級(jí)的 stack pointer 被加到 SS & RSP 中。0 級(jí)代碼返回 3 級(jí)代碼時(shí),3 級(jí) stack pointer 被重新加載到 SS & RSP 中。


通過檢查的后續(xù)處理:
  和 DS 一樣,stack segment descriptor 會(huì)被加載到 SS 寄存器中,SS.RPL 就代表當(dāng)前運(yùn)行 stack 的權(quán)限級(jí)別。當(dāng)發(fā)生 stack 切換時(shí),SS.RPL 會(huì)被更新為新的權(quán)限級(jí)別。
  



7.1.2.1、 x64 下的 long mode 的 stack segment 訪問
  
  long mode 的兼容模式與 x86 原有的模式一致。 64 bit 模式下的 stack segment 訪問與前述的 data segment 訪問一樣,processor 不會(huì)對(duì) stack segment 的訪問進(jìn)行權(quán)限檢查。




:wink:

[ 本帖最后由 mik 于 2009-4-7 18:05 編輯 ]

論壇徽章:
0
24 [報(bào)告]
發(fā)表于 2009-04-07 18:04 |只看該作者
題外話:關(guān)于數(shù)據(jù)的訪問倒底是使用哪個(gè) selector registers 進(jìn)行引用?


  對(duì)于 code segment 的訪問,都知道是使用 CS selector register 。對(duì)于 data segment 的訪問,則是根據(jù)指令或內(nèi)存的尋址方式來(lái)決定使用哪個(gè) selector registers。




1、對(duì)于串操作指令來(lái)說,在缺省的情況下:源串引用 DS 作為參考對(duì)象,目標(biāo)串引用 ES 作為參考對(duì)象。

  loadsd       /* 從源串 ds:[esi] 中 load 到 eax 中 */
  stosd        /*  從 eax 中 store 到目標(biāo)串 es:[edi] 中 */
  movsd        /* 從源串 ds:[esi] 復(fù)制到目標(biāo)串 es:[edi] 中 */
---------------------------------------------------------------- 
  stosd 缺省使用 ES selector register 來(lái)引用數(shù)據(jù),而 movsd 更是同時(shí)使用了 DS 和 ES selector register 來(lái)引用數(shù)據(jù)。




2、強(qiáng)制性改變?nèi)笔〉膮⒖紝?duì)象。

  loadsd dword ptr fs:[esi]             /* 這里強(qiáng)制性地將缺省引用對(duì)象 ES 改變?yōu)?FS,這將使用 FS 作為參考對(duì)象 */
      mov eax, dword ptr es:[eax]       /* 強(qiáng)制使用 ES 作為 參考對(duì)象 */
-------------------------------------------------------------
  以上是使用修改指令前綴方法,強(qiáng)制使用指令的 selector registers 作為引用對(duì)象進(jìn)行數(shù)據(jù)訪問。對(duì)于 mov eax, dword ptr es:[eax] 這條指令來(lái)說:它的內(nèi)存操作數(shù) [eax] 缺省是使用 DS 來(lái)訪問 data segment 的。es:[eax] 則改為使用 ES 來(lái)訪問 data segment。
  將 DS 強(qiáng)制改為 ES 后,當(dāng)然是使用 ES selector 來(lái)讀取 data segment descriptor。若 ES = DS 則與缺省情況并無(wú)兩樣,若 ES <> DS 則 ES selector 要先通過權(quán)限檢查,加載到 ES register 再作訪問。




3、push / pop 指令使用 SS 作為參考對(duì)象。  
  
  push / pop 之類的指令是固定使用 SS selector register 作為引用對(duì)象的。push 指令使用 SS 作為目標(biāo)操作數(shù)的引用對(duì)象。而 pop 指令使用 SS 作為源操作數(shù)的引用對(duì)象,這兩個(gè)是無(wú)法改變的。
  push 指令的源操作數(shù)和 pop 指令的目標(biāo)操作數(shù),它們的引用對(duì)象則是可改的。




4、 基于 ebp 和 esp 的尋址方式使用 SS 作為參考對(duì)象
  
  mov eax, dword ptr [ebp+0x0c]     
-------------------------------------------
  ebp 作為 stack frame base pointer 它缺省是使用 SS 來(lái)訪問數(shù)據(jù)的。
  實(shí)際上等于顯式的: mov eax, dword ptr ss:[ebp+0x0c],當(dāng)然這個(gè)情況下,可以強(qiáng)制改為 DS,如:mov eax, dword ptr ds:[ebp+0x0c],這樣改為使用 DS 訪問數(shù)據(jù)。


又如:
  mov eax, dword ptr [esp]
------------------------------------------------
  esp 作為 stack pointer 它缺省是使用 SS 來(lái)訪問數(shù)據(jù)的。
  實(shí)際上等于顯式的: mov eax, dword ptr ss:[esp],同樣也可以強(qiáng)制改為:mov eax dword ptr ds:[esp] 這樣就使用 DS 來(lái)訪問數(shù)據(jù)。



5、除了上述幾種情況下,其它都是使用 DS 作為缺省的參考對(duì)象
  
  大多數(shù)情況下,指令的內(nèi)存操作數(shù)都是使用 DS 作為缺省對(duì)象來(lái)訪問數(shù)據(jù)。

典型的如:
  mov eax, [eax]
  mov eax, [ebx]
  mov byte ptr [esi], 0x0c
等等... ...




6、 在 64 bit 模式下的 selector registers 使用

  在 64 bit 模式下 DS、ES、SS 是無(wú)效的,因此,已經(jīng)沒有該使用哪個(gè) selector register 來(lái)訪問數(shù)據(jù)這種考慮了。所有的 data segment 的 base 都是 0。

無(wú)論是:
  mov rax, qword ptr [rsp]
  mov rax, qword ptr [rbp]
還是:
  movsq
  mov rax, qword ptr [rax]
--------------------------------------------------
  它們的結(jié)果都是在基于 0 的 data segment 上進(jìn)行數(shù)據(jù)訪問。都是在一個(gè)平垣的內(nèi)存塊上進(jìn)行操作。所以,無(wú)所謂 selector 是什么?segment register 的值是什么?data segment descriptor 是什么?


  但是,F(xiàn)S 和 GS 還是有效的。程序中依舊可以使用 FS 和 GS 訪問附加的 data segment。這些附加的 data segment 的 base 值可以不為 0,也就是 FS 和 GS 依舊可以構(gòu)造有限的 segmentation 功能。

論壇徽章:
0
25 [報(bào)告]
發(fā)表于 2009-04-07 23:45 |只看該作者
題外話:x64 的 canonical-address 地址形式


  canonical-address 地址形式就是:64 位的 linear address(virtual address)中的從 MSB(Most Significant Bit)最高有效位到第 63 位全是 0 或 1 的這種形式。

看下面的地址:
  FFFF8010_bc001000:這是符合 canonical-address 的地址,MSB 是 bit47,值為 1。 而 bit63 ~ bit48 全是 1

  00007c80_b8102040:這是符合 canonical-address 的地址,MSB 是 bit47,值為 0。 而 bit63 ~ bit48 全是 0


  實(shí)質(zhì)上,canonical-address 地址中 bit63 ~ bit48 是符號(hào)擴(kuò)展位。bit47 是 64 位地址中最高能表示的位(MSB)。



1、canonical-address 地址形式產(chǎn)生的前提

  當(dāng)前的 x64 體系中,64 位的 virtual address(linear address)僅實(shí)現(xiàn)了 48 位 virtual address,剩下的高 16 位僅僅是作為符號(hào)擴(kuò)展,組成最終的 64 位 virtual address。
  高 16 位是符號(hào)位加上 48 位真正的 virtual address 組成 64 位 virtual address,這種地址形式就是 x64 體系中的 canonical-address 地址形式。
  之所以這樣做,是考慮到以后實(shí)現(xiàn) 52、56、60 以及真正的 64 位 virtual address 時(shí),這種地址形式(48 位+16 符號(hào)位)無(wú)需做出任何的改變就可以平滑的兼容(52、56、60 或 64 位 virtual address)。
  即:48 位 + 16 符號(hào)位這個(gè)實(shí)現(xiàn),既可以看成是 52 位 + 12 位符號(hào),也可看成是 56 位 + 8 位符號(hào)位,或看成是 60 位 + 4 位符號(hào)位,或看成全 64 位 virtual address。這對(duì)于軟件層面來(lái)看完全沒有任何影響。
  實(shí)現(xiàn)的不同只是 processor 才能感受到,processor 在做 virtual address 轉(zhuǎn)到為 physical address 才有影響。



2、非 canonical-address 地址形式檢查

  processor 會(huì)對(duì)軟件上的非 canonical-address 地址形式進(jìn)行檢查,程序中使用了非 canonical-address 會(huì)產(chǎn)生 #GP 異常。

象下面這種情形:
   mov rax, qword ptr [0x11223344557788]
--------------------------------------------------------------
  這里 [0x1122334455667788] 明顯不是 canonical-address 地址,processor 檢查到將會(huì)產(chǎn)生 #GP 異常。

  又如:64 bit 下 processor 不會(huì)對(duì) descriptor 的 limit 進(jìn)行任何檢查,但會(huì)以 canonical-address 地址檢查為代替。若 gate descriptor 中的 offset 值是非 canonical-address 地址,processor 檢測(cè)到就會(huì)產(chǎn)生 #GP 異常。等等諸如之類的檢查。

論壇徽章:
0
26 [報(bào)告]
發(fā)表于 2009-04-07 23:58 |只看該作者
7.1.3、 目標(biāo) code segment 的訪問控制

  當(dāng)程序中使用指令 call / jmp,以及通過 int 引發(fā)中斷例程的執(zhí)行,這將都是對(duì)目標(biāo)的 code segment 進(jìn)行訪問,當(dāng)通過權(quán)限的檢查后程序?qū)?huì)跳轉(zhuǎn)到目標(biāo)的 code segment 進(jìn)行執(zhí)行。
  在 code segment 的訪問過程中涉及到權(quán)限級(jí)別的改變,stack 的改變等問題。


訪問目標(biāo) code segment 的幾種情形:

1、call / jmp offset 
  在段內(nèi)直接 call / jmp,不改變目標(biāo) code segment

2、call / jmp code_selector:offset 
  直接 call / jmp 目標(biāo) code segment

3、call / jmp callgate_selector:offset 
  使用 call gate 的 call / jmp 形式

4、call / jmp tss_selector:offset  
  使用 TSS gate 的直接 call / jmp 形式,使用了 TSS 任務(wù)切換機(jī)制

5、call / jmp taskgate_selector:offset
  通過 task gate 使用 TSS 任務(wù)切換機(jī)制

6、int n
  使用 int 指令調(diào)用 interrupt / trp 例程,或者 task gate 提供的任務(wù)切換機(jī)制

7、使用 syscall / sysret、 sysenter / sysexit 指令快速調(diào)用系統(tǒng)服務(wù)例程

8、ret 或 iret
  利用 ret 以及 iret 指令構(gòu)造另一種訪問 code segment 的途徑。


  以上是程序中主動(dòng)發(fā)起訪問目標(biāo) code segment 大多數(shù)方法,在繼續(xù)執(zhí)行之前,processor 會(huì)進(jìn)行一系統(tǒng)的檢查,包括相關(guān)的權(quán)限檢查、type 檢查、limit 檢查等,通過了檢查后,加載到 cs:rip 后繼續(xù)執(zhí)行。



.

[ 本帖最后由 mik 于 2009-4-8 12:16 編輯 ]

論壇徽章:
0
27 [報(bào)告]
發(fā)表于 2009-04-08 15:33 |只看該作者
7.1.3.1、 call/jmp offset  段內(nèi)調(diào)用

段內(nèi)的調(diào)用/跳轉(zhuǎn)的特性:

  不改變 CS,也就是不用重新加載 code segment descriptor,當(dāng)然也就不需要進(jìn)行權(quán)限的檢查,但是這里需要做 segment 的 limit 檢查,檢測(cè)到越 segment limit 會(huì)產(chǎn)生 #GP 異常。
  由于在段時(shí)調(diào)用,所以使用 call offset 時(shí),processor 僅會(huì)做 push eip 的處理,不會(huì)做 push cs。在執(zhí)行 ret 返回時(shí),processor 同樣會(huì)做 limit 檢查。


對(duì)于 call 來(lái)說,offset 值可以是:

1、16 位的 displacement 值、32 位的 displacement 值,這個(gè) displacement 是個(gè)符號(hào)數(shù)。
  在 64 bit 模式下,rip = rip + disp32,這個(gè) disp32 值會(huì)符號(hào)擴(kuò)展為 64 位再加上 rip。

2、16 位、32 位的內(nèi)存操作數(shù),這個(gè)內(nèi)存操作數(shù)支持 x86 的所有內(nèi)存尋址模式。在 64 bit 模式下支持 64 位的內(nèi)存操作數(shù)。
  16 位、32 位的寄存器操作數(shù)。在 64 bit 模式下支持 64 位的寄存器操作數(shù)。

3、64 bit 模式下還支持新增的 rip-relative (RIP 相對(duì)尋址)尋址模式。

論壇徽章:
0
28 [報(bào)告]
發(fā)表于 2009-04-08 17:20 |只看該作者
7.1.3.2、 使用 call/jmp 直接調(diào)用/跳轉(zhuǎn)目標(biāo) code segment

直接調(diào)用/跳轉(zhuǎn)的形式是:
  call / jmp selector:offset

  這里的 selector 是 code segment selector 直接使用 selector 來(lái)索引 code segment,這將引發(fā) CS 的改變,code segment descriptor 最終會(huì)被加載到 CS 寄存器里。
  在 code segment descriptor 加載到 CS 之前,processor 會(huì)進(jìn)行一系列的檢查,包括權(quán)限檢查、type 檢查、limit 檢查等,在通過檢查后,processor 才加載 descriptor 到 CS,緊接著 eip = CS.base + offset,最后跳轉(zhuǎn)到 cs:eip 執(zhí)行。



以下面的指令為例:

(1)  call  0x20:0x00040000
(2)  jmp 0x20:0x00040000

0x20 是目標(biāo) code segment selector ,看看 processor 如何處理。




1、索引 code segment descriptor

  selector:0x20 的 RPL = 00,TI = 0,SI = 4
  processor 在 GDT 以 SI = 4 索引查找 descriptor,當(dāng)查找到 descriptor,processor 將判斷這個(gè) descriptor 的 types 是什么,再做進(jìn)一步的處理。


這個(gè)查找 descriptor 的過程表述如下:

RPL = 00;
TI = 0;
SI = 4;


if (TI == 0)
  DT = GDT;                 /* 在 GDT 表 */
else
  DT = LDT;                 /* 在 LDT 表 */


temp_descriptor = DT.base + SI * 8;               /* 獲取 descriptor */


switch (temp_descriptor.type)
{
case CODE_DESC:                    /* 是個(gè) code segment descriptor */
  goto do_code_desc;

case CALL_GATE:                    /* 是個(gè) call gate descriptor */
  goto do_call_gate;     

case TSS_DESC:                     /* 是個(gè) TSS descriptor */
  goto do_tss_desc;            

case TASK_GATE:                   /* 是個(gè) task gate descriptor */
  goto do_task_gate;

default:                           /* 若不是上述幾種類型,則產(chǎn)生 #GP 異常 */
  goto do_#GP_exception;               
};


  processor 在判斷 descriptor 后作進(jìn)一步處理,這里假設(shè) descriptor 是 code segment descriptor,下一步是 processor 將作權(quán)限的檢查,檢查程序是否有權(quán)限訪問目標(biāo) code segment。

  在上述獲取 descriptor 之前,processor 還會(huì)對(duì) GDT 的 limit 作檢測(cè),若發(fā)現(xiàn) GDT.base + SI * 8 > GDT.limit 同樣會(huì)引發(fā) #GP 異常。這種情況也就是說:索引值越界了。




2、權(quán)限 check

  processor 用當(dāng)前的權(quán)限與目標(biāo) code segment descriptor 作的權(quán)限 check。當(dāng)前的權(quán)限就是 RPL & CPL。在這直接調(diào)用/跳轉(zhuǎn)目標(biāo) code segment 的 check 中 conforming 與 nonconforming 類型的 descriptor 有著很大的區(qū)別。



這個(gè)權(quán)限的 check 表述如下:

DPL = temp_descriptor.DPL;

if (temp_descriptor.C == 0) {    /* code segment 是 non-conforming 類型 */

  if (CPL == DPL) {

    if (RPL <= DPL) {
      goto do_next;            /* 通過檢查,允許訪問 */  

    } else
      goto do_#GP_exception;

  } else
    goto do_#GP_exception;     /* 產(chǎn)生 #GP 異常 */


     
} else {                        /* code segment 是 conforming 類型 */

  if (CPL >= DPL) {
    goto do_next;            /* 通過檢查,允許訪問 */  
  } else
    goto do_#GP_exception;           /* 產(chǎn)生 #GP 異常 */
}


當(dāng) code segment 是 non-conforming 類型時(shí),需要 CPL == DPL && RPL <= DPL 才能通過。
當(dāng) code segment 是 conforming 類型時(shí),僅需要 CPL >= DPL 就能通過了。

當(dāng) code segment 是 conforming 類型時(shí),CPL >= DPL,表示當(dāng)前的代碼可以向高權(quán)限級(jí)別跳轉(zhuǎn)。這里無(wú)需判斷 RPL 權(quán)限。
  假設(shè)當(dāng)前運(yùn)行在 3 級(jí)代碼上,通過 call / jmp 到 conforming 類型的 0 級(jí)別代碼時(shí),當(dāng)前的 CPL 依然是 3 級(jí)。因?yàn)樵谥苯?call/jmp 目標(biāo) code segment 這種調(diào)用方式上,是不會(huì)改變當(dāng)前的運(yùn)行級(jí)別。


情景提示:
  在直接 call/jmp 目標(biāo) code segment 方式上,CPL 是不會(huì)改變的。既使由低權(quán)限代碼調(diào)用高權(quán)限的 conforming 類型的代碼,CPL 也不會(huì)改變。
  在由低權(quán)限直接 call/jmp 高權(quán)限的代碼僅限于 conforming 類型的 code segment。


  conforming 類型的 code segment 允許低權(quán)限的代碼向高權(quán)限的這類代碼調(diào)用/跳轉(zhuǎn),而 non-conforming 則不允許直接調(diào)用/跳轉(zhuǎn)。直接 call/jmp 目標(biāo) code segment 不改變 CPL,基于這個(gè)原因 non-conforming 類型的 code segment 必須要 CPL == DPL。
  若要向高權(quán)限的 non-conforming 類型 code segment 調(diào)用/跳轉(zhuǎn)時(shí),必須通過 call gate 進(jìn)行 call / jmp。




3、加載 descriptor

  通過上述權(quán)限檢查后,processor 會(huì)將目標(biāo)的 selector 加載到 CS 寄存器中,而 descriptor 也會(huì)加載到 CS 寄存中。



加載 descriptor 過程表述為:

selector = selector | (SI << 3) | (TI << 2) | CS.selector.DPL;

CS.selector = selector;                          /* 加載 selector */

CS.base = temp_descriptor.base;                     /* 加載 base 進(jìn)入 CS*/
CS.limit = temp_descriptor.limit;                  /* 加載 limit 進(jìn)入 CS */
CS.attribute = temp_descriptor.attribute;         /* 加載 attribute 進(jìn)入 CS */


selector = selector | (SI << 3) | (TI << 2) | CS.selector.DPL; 
-----------------------------------------------------------
  在這一步里,使用目標(biāo) code segment selector 的 SI 更新 CS.selector.SI,使用 TI 更新 CS.selector.TI。
  但是這里不更新 CS.selector.DPL,因?yàn)?CPL 不會(huì)改變。

  CS 內(nèi)的信息(selector & descriptor)會(huì)保持下去,直至下一次重新加載 descriptor 到 CS 為止。所以,在同一 code segment 內(nèi)的 call/jmp 是不會(huì)做權(quán)限檢查等等。




4、執(zhí)行目標(biāo) code segment

  processor 會(huì)加載 CS.base + offset 進(jìn)入 eip ,然后執(zhí)行 CS: eip 處的代碼。這個(gè) offset 就是 call/jmp 指令的 eip 值,也就是上述的 0x00040000 值。



push old_cs;
push old_eip;

eip = CS.base + offset;         /* 加載 eip */

(void (*)()) &eip;                      /* 執(zhí)行 cs: eip */



  由于這里不會(huì)改變 CPL,所以也無(wú)需做檢測(cè)是否需要 stack 切換的工作。






7.1.3.2.1、 long mode 的 64 bit 模式下的直接 call / jmp

  在 64 bit 模式下不支持 call/jmp selector:offset 這種指令形式,在 64 bit 模式下,這種形式將引發(fā) #UD 異常。

  
在 64 bit 模式下僅支持:

  call far ptr [target_code]

  jmp far ptr [target_code]
---------------------------------------
  僅支持目標(biāo)是內(nèi)存操作數(shù)的指令形式。當(dāng)然這個(gè)內(nèi)存操作數(shù)可以是任一種內(nèi)存尋址模式。
如:
  call far ptr [rax+rcx*8+0xc]
  call far ptr [rip+0x80140]

  指令從 [target_code] 中取出 32 位的 offset 和 16 位 selector。 32 位的 offset 被零擴(kuò)展至 64 位再加上 rip。


情景提示:
  Intel 明確說明: call far ptr [target_code],在 [target_code] 中可以直接讀取 64 位的 offset 值和 16 位 selector 值。當(dāng)編譯機(jī)器碼為:48 ff /3 時(shí)可以支持 64 位 offset 值 + 16 位 selector。
  AMD 則明確說明:當(dāng) operands 為 64 位時(shí),讀取的僅是 32 位的 offset 值 + 16 位的 selector,32 位的 offset 將零擴(kuò)展至 64 位的 offset。



情景提示:
  Intel 說的是在指令編碼中使用 REX.W 將 operands 擴(kuò)展為 64 位,則讀取的是 64 位 offset。AMD 的文檔中沒有說明當(dāng)使用 REX.W 將 operands 擴(kuò)展為 64 位時(shí) call far 指令將會(huì)讀取多少?
  但是,在調(diào)試器 x64 版的 windbg 里實(shí)驗(yàn)表明:使用 REX.W 確實(shí)可以將 call far 指令擴(kuò)展為讀取 64 位 offset + 16 位的 selector 。




processor 的處理過程:
  
1、索引 code segment descriptor 的方法和 x86 的一致。但和 x86 下不同的是:
  (1)、64 bit 下不存在 task gate
  (2)、若使用 selector 查找到的 descriptor 是 TSS descriptor 將產(chǎn)生 #GP 異常。
  (3)、64 bit 下不進(jìn)行 limit 的 check。
  (4)、64 bit 下 processor 將檢測(cè) code segment descriptor 的 L = 1 &&  D = 0,表明目標(biāo)代碼是 64 位代碼,若 L = 0 或者 D = 1 則產(chǎn)生 #GP  異常





2、權(quán)限的 check  

64 bit 的權(quán)限 check 和 x86 的一致,即:

if (non-conforming == 1) {   /* 是 non-conforming 類型 */

  if ( CPL == DPL && RPL <= DPL)
    /* 通過,允許訪問 */
  else
    /* 失敗,拒絕訪問,產(chǎn)生 #GP 異常 */


}  else {        /* 是 conforming 類型 */

  if (CPL >= DPL)
    /* 通過,允許訪問 */
  else
    /* 失敗,拒絕訪問,產(chǎn)生 #GP 異常 */
}




3、加載 descriptor 進(jìn)入 CS

  由于 64 bit 模式下,code segment descriptor 中僅 L、D、DPL、C 及 P 屬性有效,其它都無(wú)效的,這一步意義不大。CS.base 和 CS.limit 都是無(wú)效的。base 被強(qiáng)制為 0,limit 是固定的 64 位空間。
  代替的是進(jìn)行 canonical-address 地址檢查。

  此時(shí),CPL 也不會(huì)改變,即:CS.selector.DPL 不會(huì)被更新。所以也不會(huì)引發(fā) stack 切換。



4、執(zhí)行 code segment

  接下來(lái) 64 位的 offset 值被加到了 rip 寄存器中,然后執(zhí)行 rip 處的指令。







:)

[ 本帖最后由 mik 于 2009-4-8 21:09 編輯 ]

論壇徽章:
0
29 [報(bào)告]
發(fā)表于 2009-04-09 01:48 |只看該作者
7.1.3.3、 通過 call gate 進(jìn)訪問目標(biāo) code segment

  直接 call / jmp 目標(biāo) code segment 不能改變當(dāng)前的 CPL,若要 call / jmp 高權(quán)限的 code segment 必須使用 call gate,在 x86 下還要可以 call / jmp TSS descriptor 或者 call / jmp task gate,但在 64 bit 模式下 TSS 任務(wù)切換機(jī)制不被支持。



同樣以下面的指令為例:

(1)  call  0x20:0x00040000
(2)  jmp 0x20:0x00040000

--------------------------------
  這里的 0x20 是 call gate selector,0x0004000 是 offset ,看看 processor 怎樣處理。



1、索引 call gate descriptor 及 目標(biāo) code segment descriptor

  (1)第一步先找到 call gate descriptor,索引查找 call gate descriptor 的方法與 7.1.3.2 節(jié)中的 “索引 code segment descriptor “ 是一樣的。

 。ǎ玻┑诙皆俑鶕(jù)找到的 call gate descriptor,使用同樣的方法用 descriptor 里的 selector 再找到目標(biāo) code segment descriptor。


兩個(gè)過程表述如下:

call_gate_descriptor = get_descriptor(0x20);     /* 用 selector 0x20 先找到 call gate */

selector = call_gate_descriptor.selector;         /* 使用 call gate 中的 selector */

temp_descriptor = get_descriptor(selector);       /* 再找到 code segment descriptor */


  查找 call gate descriptor 與 code segment descriptor 的方法是一樣的。根據(jù)得到的 selector 找到相應(yīng)的 descriptor 結(jié)構(gòu)。




2、權(quán)限的 check

  processor 檢查權(quán)限,既要檢查是否有權(quán)限訪問 call gate,還要檢查是否有權(quán)限訪問 code segment。


check 過程表述如下:

DPLg = call_gate_descriptor.DPL;              /* call gate 的 DPL */
DPLs = temp_descriptor.DPL;                   /* code segment descriptor 的 DPL */


if (RPL <= DPLg && CPL <= DPLg) {                /* 檢查是否有權(quán)限訪問 call gate */
  /* pass */

  if (temp_descriptor.C == 0) {         /* 目標(biāo) code segment 是 non-conforming 類型 */
    if (Opcode == JMP)          /* 假如使用 jmp 指令 */
      if (CPL == DPLs) {
                        /* 通過,允許訪問 */
      } else {
        goto do_#GP_exception;         /* 失敗,拒絕訪問,#GP 異常 */
      }
  }

  if (CPL >= DPLs) {           /* 檢查是否有權(quán)限訪問 code segment */
                                  /* 通過,允許訪問 */
  } else {
    goto do_#GP_exception;        /* 失敗,拒絕訪問,#GP 異常 */
  }


} else {

  goto do_#GP_exception;                 /* 失敗,拒絕訪問 #GP 異常產(chǎn)生 */
  
}



  代碼中,DPLg 代表 call gate descriptor 的 DPL,DPLs 代表目標(biāo) code segment descriptor 的 DPL。


檢查通過的條件是:

  (1)(RPL <= DPLg)  &&  (CPL <= DPLg) 表示有權(quán)訪問 call gate。
并且:
 。ǎ玻〤PL >= DPLs  表示有權(quán)訪問 code segment,表示:只允許低權(quán)限向高權(quán)限轉(zhuǎn)移,或者平級(jí)轉(zhuǎn)移。不允許高權(quán)限向低權(quán)限轉(zhuǎn)移。

-------------------------------------------------------
在第(2)步的條件里:
  假如使用 call 指令:則無(wú)論是 conforming 類型還是 non-conforming 類型的 code segment,都可以成功通過。
  假如使用 jmp 指令:目標(biāo)是 conforming 類型 code segment 可以通過。但是目標(biāo)是 non-conforming 類型的 code segment 的情況下,必須:CPL == DPLs(CPL 必須等于 code segment descriptor 的 DPL)才能通過。

  這是基于 jmp 指令訪問 non-conforming 類型的代碼不改變 CPL 的原因。       


所以,這兩個(gè)條件是:
 。ǎ保㏑PL <= DPLg && CPL <= DPLg
并且:
 。ǎ玻〤PL >= DPLs (call/jmp conforming 類型或者 call non-conforming 類型)
    或:
     CPL == DPLs。╦mp non-conforming 類型)



  call gate 用來(lái)是建立一個(gè)保護(hù)的系統(tǒng)例程機(jī)制,目的是由低權(quán)限的代碼調(diào)用高權(quán)限的系統(tǒng)例程。所以:CPL >= DPLs,當(dāng)前的 CPL 權(quán)限要低于 DPL 權(quán)限。
  conforming 類型 code segment 的目的是可以由低權(quán)限向高權(quán)限代碼轉(zhuǎn)移。non-conforming 類型則要求嚴(yán)格按照規(guī)定的權(quán)限進(jìn)行。




3、加載 descriptor 進(jìn)入 CS

  同樣,通過權(quán)限 check 后,processor 會(huì)加載 selector 和 descriptor 進(jìn)入 CS 寄存器。但是,在一步里 processor 的額外工作是判斷是否進(jìn)行 CPL 改變。


  假設(shè)當(dāng)前代碼是 3 級(jí),目標(biāo)代碼是 0 級(jí),則發(fā)生權(quán)限的改變,CPL 改變也導(dǎo)致 3 級(jí)的 stack 切換到 0 級(jí)的 stack。

加載 descriptor 的表述如下:

CS.selector = temp_descriptor.selector;       /* 加載目標(biāo) code segment 的 selector */

CS.selector.DPL =  temp_descriptor.DPL;       /* 更新 CPL */

CS.base = temp_descriptor.base;
CS.limit = temp_descriptor.limit;
CS.attribute = temp_descriptor.attribute;


  CS.selector.DPL = temp_descriptor.DPL;
  由于權(quán)限的改變,CPL 需要更新,因此目標(biāo) code segment 的 DPL 將被更新至 CS.selector 的 DPL(或者說 RPL)中。




4、 stack 的切換

  由于 CPL 的改變,導(dǎo)致 stack pointer 也要進(jìn)行切換。新的 stack pointer 在 TSS 中相應(yīng)權(quán)限級(jí)別的 stack pointer 中獲取。

  接上所述,stack 將由 3 級(jí)切換至 0 級(jí)。


stack 的切換表述如下:

DPL = temp_descriptor.DPL;

old_ss = SS;
old_esp = esp;

SS = TSS.stack_pointer[DPL].SS;                    /* 加載 SS */
esp = TSS.stack_pointer[DPL].esp;                /* 加載 esp */


push(old_cs);
push(old_esp);


if (call_gate_descriptor.count) {
  copy_parameter_from_old(call_gate_descriptor.count, old_ss, old_esp);
}


push(old_cs);
push(old_eip);




stack 切換主要做以下 5 個(gè)工作:

(1)用 code segment descriptor 的 DPL 來(lái)索引相應(yīng)的級(jí)別 stack pointer(0 級(jí))
(2)將索引找到的 stack pointer(0 級(jí)) 加載到 SS 和 ESP 寄存器,當(dāng)前變?yōu)?0 級(jí)的 stack pointer。
(3)將原來(lái)的 stack pointer(0 級(jí)) 保存到新的 stack 中。
(4)如果 call gate 中的 count 不為 0 時(shí),表示需要傳遞參數(shù)。
(5)保存原來(lái)的 CS 和 EIP

----------------------------------------------
  上面代碼中的紅色部分是判斷 call gate 中是否使用了 count 域來(lái)傳遞參數(shù)。復(fù)制多少個(gè)字節(jié)?復(fù)制 count * sizeof(esp) 個(gè)字節(jié)。參數(shù)會(huì)被復(fù)制到新的 stack 中,也就是 0 級(jí)的 stack 中,以供例程使用。

  在將 SS selector 加載到 SS 寄存器時(shí),processor 同樣要做權(quán)限的檢查。CPL 已經(jīng)更新為 0,SS selector.RPL == 0 && stack segment descriptor.DPL == 0,所以條件:CPL == DPL && RPL == DPL 是成立的,新的 SS selector 加載到 SS 寄存器是成功的。

  SS selector 加載到 SS ,processor 會(huì)自動(dòng)加載 stack segment descriptor 到 SS 寄存器,SS.selector.RPL 就是當(dāng)前的 stack 的運(yùn)行級(jí)別,也就是 0 級(jí)。
  舊的 SS selector(3 級(jí)) 被保存在 0 級(jí)的 stack 中,在例程返回時(shí),會(huì)重新加載 old_SS 到 SS 寄存器,實(shí)現(xiàn)切換回原來(lái)的 stack pointer。



5、執(zhí)行系統(tǒng)例程 code segment

  成功加載 CS 和 SS 后,EIP 將由 call gate 中的 offset 加載。


執(zhí)行例程表述為:

eip = call_gate_descriptor.offset;              /* 加載 eip */

(void (*)()) &eip;                                /* 執(zhí)行例程 */



  由于例程的入口地址在 call gate 中指定,所以指令中的 offset 是被忽略的。

指令:
  call 0x20:0x00040000
------------------------------------
  指令中的 offset 值 0x0004000 將被 processor 忽略。真正的 offset 在 call gate 中指出。但是從指令格式上必須給出 offset 值,即:cs:eip 這個(gè)形式對(duì)于 call far 指令來(lái)說是必須的。










7.1.3.3.1、 long mode 下的 call gate


指令:
  call 0x20:0x00040000

---------------------------------------
  當(dāng)前 processor 運(yùn)行在 long mode 的 compatibility 模式下,這條指僅是有效的。若在 long mode 的 64 bit 模式下,有條指令是無(wú)效的,產(chǎn)生 #UD 異常。

在 64 bit 模式下:

指令:  call far ptr [mem32/mem64]
--------------------------------------
  這種形式的 far call 才被支持。memory 操作數(shù)可以是 32 位 offset + 16 位 selector 或者 64 位 offset + 16 位 selector



所以最終的指令形式是:

(1) call far ptr 0x20:0x00040000    或  call far ptr [call_gate64]        /* compatibility 模式 */

(2) call far ptr [call_gate64]                                                       /* 64 bit 模式 */




情景提示:
  long mode 下僅允許 64 位的 gate 存在。無(wú)論是 compatibility 模式還是 64 bit 模式,都不允許 32 位的 gate 存在。


  因此 long mode 下 call gate 是 64 位的 call gate(共 16 個(gè)字節(jié)),offset 被擴(kuò)展為 64 位。processor 會(huì)對(duì) gate 中的 selector 指向的 code segment 進(jìn)行檢查。64 位的 call gate 指向的 code segment 必須是 64 位 code segment,即:L = 1 并且 D = 0。
  processor 若發(fā)現(xiàn) L = 0 或者 D = 1 將會(huì)產(chǎn)生 #GP 異常。

情景提示:
  由于 long mode 的 gate 是 64 位的,當(dāng)在 compatibility 模式下的 32 位代碼執(zhí)行調(diào)用 call-gate 執(zhí)行系統(tǒng)服務(wù)例程,或由中斷指令 INT n 陷入中斷服務(wù)例程時(shí),執(zhí)行的是 64 bit 的系統(tǒng)服務(wù)例程(64 位的 OS 組件)。





  因此,0x20 是一個(gè) 64 位 call gate 的 selector。


1、獲取 call gate 和 code segment。

  processor 對(duì) call gate 的索引查找以及 code segment 的索引查找和 x86 下是一樣的。見:上述第 1 步。



2、processor 對(duì) call gate 和 code segment 的檢查

  在索引到 call gate 后,processor 會(huì)對(duì) call gate 首先進(jìn)行檢查,包括:

(1)檢查 call gate 的高半部分的 types 是否為 0000,不為 0 則產(chǎn)生 #GP 異常。
(2)檢查 call gate 中的 selector 指向的 code segment 是否為 L = 0 并且 D = 0,表明目標(biāo) code segment 是 64 bit 的。否則產(chǎn)生 #GP 異常。



3、權(quán)限的 check

  與 x86 下的 call gate 檢查機(jī)制一樣。

即:
(1)RPL <= DPLg && CPL <= DPLg 。ㄔL問 gate 的權(quán)限)
(2)CPL >= DPLs     (call/jmp conforming 類型或者 call non-conforming 類型)
  或:CPL == DPLs   。╦mp non-conforming 類型)



同樣:CPL >= DPLs  表明由低權(quán)限調(diào)用高權(quán)限代碼
   CPL == DPLs  表明不能改變 CPL,這個(gè)情況是由 jmp non-conforming 時(shí)產(chǎn)生。



4、加載 code segment descriptor

  同樣,目標(biāo) code segment 的 selector 和 descriptor 將被加載到 CS 寄存器中。

情景提示:  
  在 64 bit 模式下僅 CS.L、CS.D、CS.DPL、CS.C 以及 CS.P 是有效的,其它屬性和域都是無(wú)效的。

  可是,即使 processor 當(dāng)前處于 compatibility 模式下,在使用 gate 的情況下,加載到 CS 的結(jié)果和 64 bit 模式下是完全一樣的。因?yàn)椋涸?long mode 下 gate 是 64 位的,所使用的目標(biāo) code segment 也是 64 位的。


  因此,當(dāng) CS 加載完后:CS.L = 1、CS.D = 0。
即:
  此時(shí) processor 由 compatibility 模式切換到 64 bit 模式
  當(dāng)系統(tǒng)服務(wù)例程執(zhí)行完畢返回時(shí),processor 會(huì)由 64 bit 切換回到 compatibility 模式,直至最軟件退出返回 OS,最終 processor 再次切換回到 64 bit 模式。


code segment descriptor 在 long mode 下的意義是:

(1)建立一個(gè) segmentation 保護(hù)機(jī)制。
(2)控制目標(biāo) code segment 是 compatibility 模式還是 64 bit 模式。



同樣,若發(fā)生權(quán)限的改變,CPL 需要更新,stack 也需要切換。假設(shè)當(dāng)前的代碼為 3 級(jí)調(diào)用 0 級(jí)的代碼:

(1)CS.selector.DPL = temp_descriptor.DPL  (使用目標(biāo) code segment 的 DPL 更新 CPL)
(2)接下著進(jìn)行 stack 的切換




5、stack 切換

  經(jīng)由 call-gate 轉(zhuǎn)到服務(wù)例程,此時(shí) processor 必定處于 64 bit 模式下。發(fā)生權(quán)限的改變時(shí),processor 從 TSS 里取出相應(yīng)級(jí)別的 stack pointe,即:RSP。此時(shí)的 TSS 是 64 位的 TSS


這個(gè)過程表述如下:

DPL = temp_descriptor.DPL;         

old_ss = ss;
old_rsp = rsp;

ss = NULL;                                  /* NULL selector 被加載到 ss */
ss.selector.RPL = DPL;                     /* 更新當(dāng)前的 stack 的級(jí)別 */
rsp = TSS.stack_pointer[DPL];              /* 索引到相應(yīng)的 rsp,加載到 rsp 中 */

push64(old_ss);
push64(old_rsp);

push64(old_cs);
push64(old_rip);



在這里的 stack 切換中注意:

(1)SS 被加載為 NULL selector(0x00)。
(2)SS.selector.RPL 需要被更新為 CPL,指示當(dāng)前的 stack 級(jí)別。

------------------------------------------------------------

在由 compatibility 模式切換到 64 bit 模式的情況下:

(1)原來(lái)的 SS 和 32 位的 ESP 被擴(kuò)展為 64 位壓入 stack 中。
(2)同樣,原來(lái)的 CS 和 32 的 EIP 被擴(kuò)展為 64 位壓入 stack 中。
(3)返回后,SS 和 32 的 ESP 被加載回 SS 和 ESP,RSP 的高半部分被拋棄。
(4)同樣,CS 和 32 的 EIP 被加載回 CS 和 EIP,RIP 的高半部分被拋棄。





6、執(zhí)行系統(tǒng)服務(wù)例程

  processor 從 call gate 處獲取 offset 值,加載到 RIP 中,從而執(zhí)行 RIP 處的代碼。










:)

[ 本帖最后由 mik 于 2009-4-10 20:05 編輯 ]

論壇徽章:
0
30 [報(bào)告]
發(fā)表于 2009-04-12 00:44 |只看該作者
題外話: long mode 模式下 system descriptor 與 gate descriptor 的疑惑


1、 32 位的 system descriptor 與 64 位的 system descriptor

(1)compatibility 模式下,LDT / TSS descriptor 還是原來(lái)的 32 位的 descriptor,與原來(lái) x86 的 LDT / TSS 意義一致。
(2)64 bit 模式下,LDT / TSS descriptor 擴(kuò)展為 64 位的 descriptor。 descriptor 的 type 被相應(yīng)改烴。由原來(lái)的 32 bit-LDT 改為 64 bit-LDT,available/busy 32bit-TSS 改變 available/busy 64bit-TSS。

  一個(gè)全新 64 位 OS 在開啟和激活 long mode 后 轉(zhuǎn)入 64 bit 模式,在 64 bit 模式下必須使用 LTR 指令來(lái)加載 available 64-TSS 來(lái)建立一個(gè) 64 位的 TSS 環(huán)境。available 64-TSS 加載后,被 processor 置為 busy 64-TSS 且不會(huì)再置回為 avalilable 64-TSS。
  所以,無(wú)論是在 compatibility 模式還是在 64 bit 模式,使用的都是 64 位 TSS,但是在 compatibility 模式下 TSS 還是被認(rèn)為是 32 位的 TSS,其行為如同 x86 下,若在 compatibility 下使用 LTR 指令只能加載 32 位的 TSS。64 bit 下使用 LTR 指令加載的是 64 位的 TSS。在 OS 初始化時(shí),實(shí)際上的 TSS 環(huán)境是 64 位 TSS。
  因此,在 compatibility 下實(shí)際上還是使用的是 64 位的 TSS 環(huán)境。

  long mode 下不支持 TSS 的任務(wù)切換機(jī)制,因此,將不支持使用 TSS selector 來(lái) far call 進(jìn)行任務(wù)切換機(jī)制。

(3)不能使用 TSS 的任務(wù)切換機(jī)制,導(dǎo)致了不能加載 LDT 。而 LLDT 指令在 OS 初始化時(shí)進(jìn)行,加載的是 64 位的 LDT。


  
2、64 位的 gate(call-gate、interrupt-gate 及 trap gate)

  long mode 下不支持 task gate。在 long mode 模式下,無(wú)論是 compatibility 模式還是 64 bit 模式,所有的 gate 都是 64 位的。gate 指向的 code segment 必須是 64 位的 code segment(L = 1 & D = 0)。
  在 compatibility 模式下使用 call-gate 來(lái) far call 到 code segment 或者 INT n 調(diào)用系統(tǒng)例程。processor 將由 compatibility 模式切換到 64 bit 模式。
  在不同權(quán)限的 code segment 間執(zhí)行并改變 CPL 只能通過 gate (call、interrupt/trap)達(dá)到,因此 64 位的 OS 核心必定運(yùn)行在 64 bit 模式下。




:wink:

[ 本帖最后由 mik 于 2009-4-12 00:56 編輯 ]
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP