long mode 模式下的中断服务例程

来源:互联网 发布:网络不安全事件 编辑:程序博客网 时间:2024/06/04 19:27

转自:点击打开链接


在 long mode 下,gate 是 16 字节的,并取消了对 task gate 的支持。即 IDT 的 entry 是 16 字节的,所以:gate = IDTR.base + vector * 16。

  在 long mode 下,code segment descriptor 的 L、D、C 以及 DPL 有效,其它域无效。由于 base 强制为 0,中断服务例程的入口地址就是 gate.offset,这个 offset 是 64 位值。code segment descriptor 的 L = 1 && D = 0,代表 code segment 是 64 位代码。



1、索引 gate 和 code segment descriptor

gate = IDTR.base + vector * 16;

temp_descriptor = get_descriptor(gate.selector, GDT /* LDT */);


同样,以 gate.selector 可以找到 code segment descriptor。



2、gate descriptor 和 code segment descriptor 的检查

  processor 将对 gate 和 code segment descriptor 进行检查

if (gate.type == INTERRUPT_GATE || gate.type == TRAP_GATE) {

} else {
  /* #GP 异常 */
}  

if (IS_CANONICAL(gate.offset)) {                /* is canonical-address ? */

} else {
  /* #GP */
}

if (temp_descriptor.L == 1 && temp_descriptor.D == 0) {            /* 64 bit */

} else {
  /* #GP 异常 */
}


  processor 对 gate 额外检查 offset 是否是 canonical-address 地址形式。processor 还将对 code segment descriptor 的 L 和 D 属性进行检查,是否为 64 位代码。



3、权限 check

  DPLg 是 gate.DPL,DPLs 是 code segment descripotr.DPL
if (CPL <= DPLg && D >= DPLs) {
  /* 通过,允许访问 */
} else {
  /* 失败, #GP 异常 */
}





4、stack 切换
  
  在 long mode 下的 interrupt/trap gate 增加了 IST 域,代表 1 ~ 7 stack pointer ID 值。允许在 interrupt/trap 提供自定义的 stack pointer,分别为:IST1 ~ IST7。


old_SS = SS;
old_RSP = RSP;

if (gate.IST)
{
  SS = NULL;
  RSP = TSS.IST[gate.IST].RSP;         /* Intrrupt Stack Table */

} else {

  SS = NULL:
  RSP = TSS.stack[temp_descriptor.DPL].RSP;       /* stack pointer */
}

RSP |= 0xFFFFFFFF_FFFFFFF0;                /* 调整 stack 为 16 字节对齐 */

push64(old_SS);
push64(old_RSP);

push64(RFlags);

push64(CS);
push64(RIP);

if (ERROR_CODE)
{
  push64(error_code);
}


(1)如果,interrupt/trap gate 的 IST 不为 0 无论是否有权限的改变,都将发生 stack 切换,以便使用 gate 提供的 IST 指针,若 gate.IST 为 0 的话,将沿有以前的的 stack 切换模式:在权限改变时发生 stack 切换。

(2)long mode 下的 stack 以 16 字节对齐,processor 额外将 RSP 调整为 16 字节对齐。

(3)SS 将被加载为 NULL selector,即:0x0000(selector),在 long mode 下的 stack 切换中 SS 被加载为 NULL-selector 是不会产生 #GP 异常的。

(4)旧的 SS & RSP、Eflags、CS & RIP 将被入栈,SS、CS 在 8 字节边界上,多余补 0 。Rflags 的高 32 位补 0 入栈。

(5)同样若是 exception 例程则入栈 error code

---------------------------------------------------------------------------------
  在 long mode 下的 TSS segment 是 64 位的,其中有 6 个 RSP 指针域,构成一个 Interrupt Stack Table 表,分别为:IST1 ~ IST7,这 6 个 RSP 由 interrupt / trap gate descriptor 中的 IST 域来索引获取。
  gate 中的 IST 域值为 001 ~ 111,相应地指出在 TSS 中的 IST 值。当 gate.IST 为 0 时,表示在 gate 中不提供对 IST 的索引,还是按原来的获取 stack pointer 方式。




5、Rflags 寄存器的处理

Rflags.NT = 0;
Rflags.RF = 0;
Rflags.TF = 0;

if (gate == INTERRUPT_GATE) {
  Rflags.IF = 0;
} else if (gate == TRAP_GATE) {

}

同样,processor 需将 NT、TF、RF 清为 0,在 interuupt gate 下 IF 清 0, trap-gate 下不修改 IF 标志。long mode 下不支持 virtual-8086 mode,对 VM 标志不修改。




6、加载 code segment descriptor

  和 x86 下一样,code segment selector 和 descriptor 被加载到 CS 中,但是仅 CS.L、CS.D、CS.P、CS.C 和 CS.DPL 有效,其它域无效,CS.base 被强制为 0,CS.limit 被强制为 FFFFFFFF_FFFFFFFF
  CS.RPL 被更新为 code segment descriptor 的 DPL,即为当前的 CPL。  


7、执行中断服务例程
  
  由于 CS.base 被强制为 0,因此 gate.offset 就是实际的中断服务例程入口地址。

  RIP = gate.offset 然后执行 CS:RIP 处理的中断服务例程。



8、中断服务例程的返回

  若返回到 64 bit 模式时,processor 处理将和 x86 的情形一样。但是在 64 bit 的中断服务例程里需使用 iretq 指令。

情景提示:
  由于 iret 指令在 64 bit 模式下 default operand-size 是 32 位的,这不同于 ret 指令。ret 指令的 default operand-size 是 64 位。所以,中断返回时需使用 iretq 指令,即:使用指令前缀 REX.W(48) 将 iret 调整为 iretq



7.1.3.7.1、 compatibility 模式与 64 bit 模式之间的切换

  由于 long mode 下有 compatiblity 与 64 bit 两种子模式,那么在 x64 版本的 64 位 OS 里就有可能存在 compatibility 与 64 bit 模式之间的切换情况发生。
  64 位 OS 核心运行在 64 bit 模式下,当系统加载的是原 x86 下的 32 位用户程序,processor 将从 64 bit 切换到 compatiblity 模式。32 位软件返回 OS 时,从 compatibility 模式切换回 64 bit 模式。
  当原 x86 程序中使用 int 陷入系统服务例程时,processor 从 compatibility 模式切换到 64 bit 模式,从 3 级切换到 0 级代码。

原创粉丝点击