SYSRET
SYSRET指令与SYSCALL指令是一对。它从OS System-call 例程返回到三环的用户代码。从RCX中加载IRP,之后从R11中加载RLFAGS。如果是64位操作数的大小,SYSRET仍然保持在64位模式;否则,它进入兼容模式,只有低32位的寄存器会被加载。
SYSRET加载CS和SS选择子使用IA32_STAR[63:48] MSR。然而,CS与SS描述符缓存不会从段选择子指定的描述符加载。相反,这描述符缓冲加载使用固定的值。看Operaton节来查找更多细节。OS软件负责确保段选择子指定的段描述符加载进段选择子缓存。这SYSRET指令并不会对此负责。
SYSRET指令并不会修改栈指针(ESP或RSP)。由于某些原因,系统必须要切换用户栈,这OS可能加载用户栈指针(如何它在SYSCALL之后保存)在执行SYSRET之前;相同地,用户代码可能加载栈指针(如果它在SYSCALL调用之前保存)在SYSRET之后控制。
如果OS加载栈指针在执行SYSRET之前,它必须确保在恢复栈指针和成功执行SYSRET之间的中断和异常并不会使用用户栈来调用。可以使用如下方法来正确的执行:
- 外部中断 OS可以阻止外部中断通过清除EFLAGS.IF位,在加载用户栈之前。
- 不可屏蔽中断(NMIs)。这OS可以确保 NMI 处理例程调用正确的栈通过使用中断栈表(IST)机制,对于 gate2(NMI)在IDT表中。(see Section 6.14.5, “Interrupt
Stack Table,” in Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A). - General-protection 异常(#GP). 这SYSRET指令生成 #GP(0)如何这个RCX值是不合法的。这OS可以定位正确的地址通过下面一个或多个方法:
- 在执行SYSRET指令之前,确保RCX值是合法数。
- 使用Paing来确保SYSCALL指令从来不会保存一个不合法的值进入RCX。
- 使用IST机制对gate13(#GP)在IDT中。
指令顺序:SYSRET之后的指令可能被读取进内存,在这个指令执行完成之前,但是他们将不会得到执行知道SYSRET之前的指令完全得到执行。
个人解读感悟:如果之前搞懂了SYSCALL指令,则SYSRET指令并不难读。这里涉及一个Gate 与 IST 机制,之前不了结果,以及GP保护的细节,我们之后找时间给读一遍,这块很好理解的。
Operation
IF (CS.L ≠ 1 ) or (IA32_EFER.LMA ≠ 1) or (IA32_EFER.SCE ≠ 1)
(* Not in 64-Bit Mode or SYSCALL/SYSRET not enabled in IA32_EFER *)
THEN #UD; FI;
IF (CPL ≠ 0) THEN #GP(0); FI;
IF (operand size is 64-bit)
THEN (* Return to 64-Bit Mode *)
IF (RCX is not canonical) THEN #GP(0);
RIP ← RCX;
ELSE (* Return to Compatibility Mode *)
RIP ← ECX;
FI;
RFLAGS ← (R11 & 3C7FD7H) | 2; (* Clear RF, VM, reserved bits; set bit 1 *)
IF (operand size is 64-bit)
THEN CS.Selector ← IA32_STAR[63:48]+16;
ELSE CS.Selector ← IA32_STAR[63:48];
FI;
CS.Selector ← CS.Selector OR 3; (* RPL forced to 3 *)
(* Set rest of CS to a fixed value *)
CS.Base ← 0; (* Flat segment *)
CS.Limit ← FFFFFH; (* With 4-KByte granularity, implies a 4-GByte limit *)
CS.Type ← 11; (* Execute/read code, accessed *)
CS.S ← 1;
CS.DPL ← 3;
CS.P ← 1;
IF (operand size is 64-bit)
THEN (* Return to 64-Bit Mode *)
CS.L ← 1; (* 64-bit code segment *)
CS.D ← 0; (* Required if CS.L = 1 *)
ELSE (* Return to Compatibility Mode *)
CS.L ← 0; (* Compatibility mode *)
CS.D ← 1; (* 32-bit code segment *)
FI;
CS.G ← 1; (* 4-KByte granularity *)
CPL ← 3;
SS.Selector ← (IA32_STAR[63:48]+8) OR 3; (* RPL forced to 3 *)
(* Set rest of SS to a fixed value *)
SS.Base ← 0; (* Flat segment *)
SS.Limit ← FFFFFH; (* With 4-KByte granularity, implies a 4-GByte limit *)
SS.Type ← 3; (* Read/write data, accessed *)
SS.S ← 1;
SS.DPL ← 3;
SS.P ← 1;
SS.B ← 1; (* 32-bit stack segment*)
SS.G ← 1; (* 4-KByte granularity *)
Flags Affected
All.
Protected Mode Exceptions
#UD The SYSRET instruction is not recognized in protected mode.SYSRET—Return From Fast System Call
INSTRUCTION SET REFERENCE, M-U
4-672 Vol. 2B
Real-Address Mode Exceptions
#UD The SYSRET instruction is not recognized in real-address mode.
Virtual-8086 Mode Exceptions
#UD The SYSRET instruction is not recognized in virtual-8086 mode.
Compatibility Mode Exceptions
#UD The SYSRET instruction is not recognized in compatibility mode.
64-Bit Mode Exceptions
#UD If IA32_EFER.SCE = 0.
If the LOCK prefix is used.
#GP(0) If CPL ≠ 0.
If the return is to 64-bit mode and RCX contains a non-canonical address