34、使用 sysenter/sysexit 指令快速切換
通過 GDT / IDT 表索引查找 target code-segment descriptor 顯得不夠迅速。
* 原因1:經(jīng)過多重的索引查找
* 原因2:經(jīng)過相關(guān)的權(quán)限檢查
因此 Intel 推出了 sysenter 指令來快速陷入 0 層,同時免去了相關(guān)的權(quán)限檢查環(huán)節(jié)。sysexit 指令快速返回。
1、sysenter/sysexit 的使用前提
* sysenter/sysexit 必須在 protected 模式下執(zhí)行。在 real 模式下使用將產(chǎn)生 #GP 異常。
* sysenter/sysexit 必須執(zhí)行在 flat 內(nèi)存模式,即:CS.base = 0 & SS.base = 0
* 在 AMD 的 processor 中,sysenter/sysexit 在 long mode 是無效的,在 long mode 下使用將產(chǎn)生 #UD 異常。
在 Intel 的 processor 中,sysenter/sysexit 在 long mode 是有效的。
2、sysenter 的使用環(huán)境
前面說過,sysenter/sysexit 須使用在 flat 內(nèi)存模式下。
雖然,在相應的 MSR 寄存器中提供了 CS 和 SS selector,但實際上這個 CS 和 SS selector 將被忽略。而以硬性的 code segment descriptor 和 stack segment descriptor 來代替。processor 自動負責這個具體工作的實施。
processor 會重設 code / stack segment descriptor:
* CS.base = 0 & SS.base = 0
* CS.limit = FFFFF & SS.limit = FFFFF (4G 的 limit,實際等于 CS.limit = FFFFFFFF)
* CS.G = 1 & SS.G = 1
* CS.DPL = 0 & SS.DPL = 0 (強制為 0 級權(quán)限)
* CS.type = 1011。╡xecute/read & accessed)
* SS.type = 0011 (read/write & accessed)
* CS.P = 1 & SS.P = 1
無論提供的 code/stack segment descriptor 是怎樣,processor 將硬性按上面的重設 CS 與 SS 環(huán)境。
3、sysenter 執(zhí)行環(huán)境的獲取
processor 會按規(guī)則去獲取執(zhí)行環(huán)境,包括:CS 和 SS selector、eip 和 esp(代碼入口和棧頂入口)。
* MSR_SYSENTER_CS[15:0]
獲取 CS selector。這個 MSR 的 bit15 ~ bit0 提供了 CS selector,而 bit64 ~ bit16 須為 0。MSR_SYSENTER_CS[15:0] 將載入 CS 中。
* MSR_SYSENTER_EIP[31:0]
提供 target code segment 的入口 EIP 值,MSR_SYSENTER_EIP[31:0] 將載入 EIP 中。
* MSR_SYSENTER_ESP[31:0]
提供 target stack segment 的入口 ESP 值,MSR_SYSENTER_ESP[31:0] 將載入 ESP 中。
* SS selector 的獲取
沒有直接 MSR 寄存器去提供 SS selector,但是,Intel 將視提供的 CS descriptor 的下一個 descriptor 為 SS descriptor。
也就是說:由 MSR_SYSENTER_CS[15:0] 提供的 CS selector 的下一個就是 SS selctor。
XXXXXXXXXXXXX 0 00
-----------------
|
+---------------> CS_selector.SI + 1(加上8)
由上面可見,CS seelctor 的下一個 selector 就是 SS selector。
其結(jié)果就是:SS selector = MSR_SYSENTER_CS + 8
---------------------------------------------------------
前面說過,MSR 雖然提供了 CS 和 SS selector,但最終會被 processor 重設。
4、 sysenter 執(zhí)行環(huán)境的設置
由于 CS & SS selector、EIP 以及 ESP 在相應的 MSR 寄存器中獲取,所以使用前須由指令 WRMSR 指令來設定相應的值。
* MSR_SYSENTER_CS 的地址在 174H
* MSR_SYSENTER_ESP 的地址在 175H
* MSR_SYSENTER_EIP 的地址在 176H
實例:
xor edx, edx /* MSR_SYSENTER_EIP[63:32] 為 0 */
mov eax, 0x80100400 /* eip 為 0x80100400 */
mov ecx, 0x176 /* MSR_SYSENTER_EIP 地址 */
wrmsr /* 寫 MSR */
-------------------------------------
上面這段代碼將設置 MSR_SYSENTER_EIP[31:0] 值為 0x80100400
5、 sysexit 執(zhí)行環(huán)境的獲取
當 sysenter 指令執(zhí)行到的 0 級代碼使用 sysexit 返回時,sysexit 的執(zhí)行,processor 同樣按規(guī)則去獲取返回執(zhí)行環(huán)境。
* 返回 CS selector 的獲取
MSR_SYSENTER_CS[15:0] 提供的 CS slector 的下第二個 selector 為返回的 CS selector。
XXXXXXXXXXXXX 0 00
-----------------
|
+---------------> CS_selector.SI + 2(加上16)
MSR_SYSENTER_CS[15:0] 的下第二個 selector 就是 CS selector。
其結(jié)果就是:返回 CS selector = MSR_SYSENTER_CS + 16
* 返回 SS selector 的獲取
同樣根據(jù)規(guī)則 MSR_SYSENTER_CS[15:0] 提供的 selector 的下第三個 selector 就是返回的 SS selector。
XXXXXXXXXXXXX 0 00
-----------------
|
+---------------> CS_selector.SI + 3(加上24)
MSR_SYSENTER_CS[15:0] 接下來的第 3 個 selector 就是返回的 SS selector。
其結(jié)果就是:返回的 SS selector = MSR_SYSENTER_CS+24
* 返回的 EIP 和 ESP
EIP 和 ESP 簡單得多:
EIP 放在 EDX 寄存器中。
ESP 放在 ECX 寄存器中。
6、 sysexit 的使用環(huán)境
和 sysenter 指令一樣, sysexit 指令的使用環(huán)境 CS 和 SS 同樣會被 processor 重新設定,雖然給出了返回的 CS 和 SS。
除了 DPL 外,sysexit 和 sysenter 的環(huán)境是一樣的。
* CS.DPL = 3(CS.CPL = 3) & SS.DPL =3
-------------------------------------------------------------
區(qū)別就是:sysenter 目標代碼必須是 0 級,sysexit 目標代碼必須是 3 級。
7、 long mode 下的 sysenter/sysexit 指令
僅在 Intel 的 processor 下才有效,AMD 的 processor 在 long mode 下使用 syscall/sysret 來代替。
在 long mode 下,MSR_SYSENTER_ESP[63:32] 和 MSR_SYSENTER_EIP[63:32] 是有效的,擴展成 64 位,變成了 MSR_SYSENTER_RSP 和 MSR_SYSENTER_RIP。
* sysenter 進入的 target code segment descriptor 的 L 必須是 1,D = 0
即:目標的 CS.L = 1 & CS.D = 0 表示目標為 64 位代碼。
* MSR_SYSENTER_RSP 和 MSR_SYSENTER_RIP 的值必須的 canonical address 形式。
8、sysenter 的執(zhí)行
if (CR0.PE == 0) /* non-proected mode */
goto do_GP_exception;
if (MSR_SYSENTER_CS[15:2] == 0) /* NULL-selector */
goto do_GP_exception;
eflags.VM = 0;
eflags.IF = 0;
eflags.RF = 0;
if (EFER.LMA == 0) /* x86-legacy mode */
goto x86_sysenter;
else
goto x64_sysenter; /* long mode */
x86_sysenter:
CS.selector = MSR_SYSENTER_CS; /* load CS selector with MSR_SYSENTER_CS[15:0] */
CS.selector.RPL = 0;
CS.base = 0x00000000;
CS.base = 0xFFFFFFFF;
CS.type = 0x0B; /* type = 1011 */
/* non-conforming, excecute/readable, accessed */
CS.D = 1; /* 32 bit code segment */
CS.P = 1;
CS.G = 1;
CS.S = 1; /* non-system descriptor */
SS.selector = MSR_SYSENTER_CS + 8; /* next-selector with MSR_SYSENTER_CS + 8 */
SS.selector.RPL = 0;
SS.base = 0x00000000;
SS.limit = 0xFFFFFFFF;
SS.type = 0x03; /* type = 0011 */
/* expand-up, write/read, accessed */
SS.D = 1; /* 32 bit stack pointer */
SS.P = 1;
SS.G = 1;
SS.S = 1;
esp = MSR_SYSENTER_ESP;
eip = MSR_SYSENTER_EIP;
x64_sysenter:
CS.selector = MSR_SYSENTER_CS; /* load CS selector with MSR_SYSENTER_CS[15:0] */
CS.selector.RPL = 0;
CS.base = 0x00000000_00000000;
CS.base = 0xFFFFFFFF_FFFFFFFF;
CS.type = 0x0B; /* type = 1011 */
/* non-conforming, excecute/readable, accessed */
CS.L = 1;
CS.D = 0; /* 64 bit code segment */
CS.P = 1;
CS.G = 1;
CS.S = 1; /* non-system descriptor */
SS.selector = MSR_SYSENTER_CS + 8; /* next-selector with MSR_SYSENTER_CS + 8 */
SS.selector.RPL = 0;
SS.base = 0x00000000_00000000;
SS.limit = 0xFFFFFFFF_FFFFFFFF;
SS.type = 0x03; /* type = 0011 */
/* expand-up, write/read, accessed */
SS.D = 1; /* 64 bit stack pointer */
SS.P = 1;
SS.G = 1;
SS.S = 1;
rsp = MSR_SYSENTER_RSP;
rip = MSR_SYSENTER_RIP;
---------------------------------------------------------------------------------------------
processor 設置必要的執(zhí)行環(huán)境:CS 和 SS 都是 0 級的 flat 內(nèi)存模式。
9、sysexit 的執(zhí)行
if (CR0.PE == 0) /* non-proected mode */
goto do_GP_exception;
if (MSR_SYSENTER_CS[15:2] == 0) /* NULL-selector */
goto do_GP_exception;
if (CS.RPL != 0) /* CPL != 0 */
goto do_GP_exception;
if (EFER.LMA == 0) /* x86-legacy mode */
goto x86_sysexit;
else
goto x64_sysexit; /* long mode */
x86_sysexit:
CS.selector = MSR_SYSENTER_CS + 16; /* MSR_SYSENTER_CS[15:0] + 16 */
CS.selector.RPL = 3;
CS.base = 0x00000000;
CS.base = 0xFFFFFFFF;
CS.type = 0x0B; /* type = 1011 */
/* non-conforming, excecute/readable, accessed */
CS.D = 1; /* 32 bit code segment */
CS.P = 1;
CS.G = 1;
CS.S = 1; /* non-system descriptor */
SS.selector = MSR_SYSENTER_CS + 24;
SS.selector.RPL = 3;
SS.base = 0x00000000;
SS.limit = 0xFFFFFFFF;
SS.type = 0x03; /* type = 0011 */
/* expand-up, write/read, accessed */
SS.D = 1; /* 32 bit stack pointer */
SS.P = 1;
SS.G = 1;
SS.S = 1;
esp = ecx;
eip = edx;
x64_sysexit:
if (opcode[0] == REX.W) /* return to 64 bit */
goto 64bit_sysexit;
else
goto 32bit_sysexit;
64bit_sysexit:
CS.selector = MSR_SYSENTER_CS + 32; /* MSR_SYSENTER_CS[15:0] + 32 */
CS.selector.RPL = 3;
CS.base = 0x00000000_00000000;
CS.base = 0xFFFFFFFF_FFFFFFFF;
CS.type = 0x0B; /* type = 1011 */
/* non-conforming, excecute/readable, accessed */
CS.L = 1;
CS.D = 0; /* 64 bit code segment */
CS.P = 1;
CS.G = 1;
CS.S = 1; /* non-system descriptor */
SS.selector = MSR_SYSENTER_CS + 40; /* next-selector with MSR_SYSENTER_CS + 40 */
SS.selector.RPL = 0;
SS.base = 0x00000000_00000000;
SS.limit = 0xFFFFFFFF_FFFFFFFF;
SS.type = 0x03; /* type = 0011 */
/* expand-up, write/read, accessed */
SS.D = 1; /* 64 bit stack pointer */
SS.P = 1;
SS.G = 1;
SS.S = 1;
rsp = rcx;
rip = rdx;
32bit_sysexit:
CS.L = 0
goto x86_sysexit;
:wink:
[ 本帖最后由 mik 于 2009-6-7 00:31 編輯 ] |