34、使用 sysenter/sysexit 指令快速切换
通过 GDT / IDT 表索引查找 target code-segment descriptor 显得不够迅速。
* 原因1:经过多重的索引查找
* 原因2:经过相关的权限检查
因此 Intel 推出了 sysenter 指令来快速陷入 0 层,同时免去了相关的权限检查环节。sysexit 指令快速返回。
1、sysenter/sysexit 的使用前提
* sysenter/sysexit 必须在 protected 模式下执行。在 real 模式下使用将产生 #GP 异常。
* sysenter/sysexit 必须执行在 flat 内存模式,即:CS.base = 0 & SS.base = 0
* 在 AMD 的 processor 中,sysenter/sysexit 在 long mode 是无效的,在 long mode 下使用将产生 #UD 异常。
在 Intel 的 processor 中,sysenter/sysexit 在 long mode 是有效的。
2、sysenter 的使用环境
前面说过,sysenter/sysexit 须使用在 flat 内存模式下。
虽然,在相应的 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 级权限)
* CS.type = 1011 (execute/read & accessed)
* SS.type = 0011 (read/write & accessed)
* CS.P = 1 & SS.P = 1
无论提供的 code/stack segment descriptor 是怎样,processor 将硬性按上面的重设 CS 与 SS 环境。
3、sysenter 执行环境的获取
processor 会按规则去获取执行环境,包括: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。
其结果就是:SS selector = MSR_SYSENTER_CS + 8
---------------------------------------------------------
前面说过,MSR 虽然提供了 CS 和 SS selector,但最终会被 processor 重设。
4、 sysenter 执行环境的设置
由于 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 执行环境的获取
当 sysenter 指令执行到的 0 级代码使用 sysexit 返回时,sysexit 的执行,processor 同样按规则去获取返回执行环境。
* 返回 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。
其结果就是:返回 CS selector = MSR_SYSENTER_CS + 16
* 返回 SS selector 的获取
同样根据规则 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。
其结果就是:返回的 SS selector = MSR_SYSENTER_CS+24
* 返回的 EIP 和 ESP
EIP 和 ESP 简单得多:
EIP 放在 EDX 寄存器中。
ESP 放在 ECX 寄存器中。
6、 sysexit 的使用环境
和 sysenter 指令一样, sysexit 指令的使用环境 CS 和 SS 同样会被 processor 重新设定,虽然给出了返回的 CS 和 SS。
除了 DPL 外,sysexit 和 sysenter 的环境是一样的。
* CS.DPL = 3(CS.CPL = 3) & SS.DPL =3
-------------------------------------------------------------
区别就是: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 的执行
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 设置必要的执行环境:CS 和 SS 都是 0 级的 flat 内存模式。
9、sysexit 的执行
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 编辑 ] |