首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

深入理解 x86/x64 的中断体系(3)

深入理解 x86/x64 的中断体系(3)

2.4 IDT 表的 limit 检查  在 IDT 表中查找索引 gate descriptor 时,processor 也会对 IDT 表的 limit 进行检查,这个检查的逻辑是:
  
gate_descriptor = IDTR.base + vector * 8;                  /* get gate descriptor */
if ((gate_descriptor + sizeof(DESCRIPTOR) - 1) > (IDTR.base + IDTR.limit))
  {
        /* failure: #GP exception */
}
   
  我们看看下面这个图:
  
  当我们设:
  
  • IDTR.base = 0x10000
  • IDTR.limit = 0x1f
  那么 IDT 表的有效地址范围是:0x10000 - 0x1001f,也就是:IDTR.base + IDTR.limit 这表示:
  
  • vector 0:0x10000 - 0x10007
  • vector 1:0x10008 - 0x1000f
  • vector 2: 0x10010 - 0x10017
  • vector 3: 0x10018 - 0x1001f
  上面是这 4 个 vector 的有效范围,因此:当设 IDTR.limit = 0x1e 时,如果访问 vector 3 时(调用中断3)processor 检测到访问 IDT 越界而出错!
  因此:访问的 vector 地址在 IDTR.base 到 IDTR.base + IDTR.limit(含)之外,将会产生 #GP 异常。
  2.5 请求访问 interrupt handler 时的权限检查  访问权限的检查是 x86/x64 体系中保护措施中非常重要的一环,它控制着访问者是否有权限进行访问,在访问 interrupt handler 过程权限控制中涉及 3权限类别
  
  • CPL:当前 processor 所处的权限级别
  • DPLg:代表 DPL of gate,也就是 IDT 中 gate descriptor 所要求的访问权限级别
  • DPLs:代表 DPL of code segment,也就是 interrupt handler 的目标 code segment 所要求的访问权限级别
  CPL 权限级别代表着访问者的权限,也就是说当前正在执行代码的权限,要理解权限控制的逻辑,你需要明白下面两点:
  
  • 要调用 interrupt handler 那么首先你必须要有权限去访问 IDT 表中的 gate,这表示:CPL 的权限必须不低于 DPLg (gate 所要求的权限),这样你才有权限去访问 gate
  • interrupt handler 会在高权限级别里执行,也就是说 interrupt handler 会在特权级别里运行。这表示:interrupt handler 里的权限至少不低于访问者的权限
  在调用 interrupt handler 中并不使用 selector 来访问 gate 而是使用使用 vector 来访问 gate,因此中断权限控制中并不使用 RPL 权限类别,我们可以得知中断访问权限控制的要素:
  
  • CPL <= DPLg
  • CPL >= DPLs
  需同时满足上面的两个式子,在比较表达式中数字高的权限低,数字低的权限高!用 C 描述为:
  
DPLg = gate_descriptor.DPL;               /* DPL of gate */
DPLs = code_descriptor.DPL;               /* DPL of code segment */
if ((CPL <= DPLg) && (CPL >= CPLs))
  {
     /* pass */
} else {
     /* failure: #GP exception */
}
   
  2.5.1 gate 的权限设置  对于 gate 的权设置,我们应考虑 interrupt handler 调用上的两个原则:
  
  • interrupt handler 开放给用户调用
  • interrupt handler 在系统内部使用
  由这两个原则产生了 gate 权根设置的两个设计方案:
  
  • gate 的权限设置为 3 级:这样可以给用户代码有足够的权限去访问 gate
  • gate 的权限设置为 0 级:只允许内核代码访问,用户无权通过这个 gate 去访问 interrupt handler
  这是现代操作系统典型的 gate 权限设置思路,绝大部分的 gate 都设置为高权限,仅有小部分允许用户访问。很明显:系统服务例程的调用入口应该设置为 3 级,以供用户调用
  下面是很典型的设计:
  
  • #BP 异常:BreakPoint(断点)异常的 gate 应该设置为 3 级,使得用户程序能够使用断点调试程序。
  • 系统调用:系统调用是 OS 提供给用户访问 OS 服务的接口,因此 gate 必须设置为 3 级。
  系统调用在每个 OS 实现上可能是不同的,#BP 异常必定是 vector 3,因此对于 vector 3 所使用的 gate 必须使用 3 级权限。
  下面是在 windows 7 x64 操作系统上的 IDT 表的设置:
   
<bochs:2> info idt
Interrupt Descriptor Table (base=0xfffff80004fea080, limit=4095):
IDT[0x00]=64-Bit Interrupt Gate target=0x0010:fffff80003abac40, DPL=0
IDT[0x01]=64-Bit Interrupt Gate target=0x0010:fffff80003abad40, DPL=0
IDT[0x02]=64-Bit Interrupt Gate target=0x0010:fffff80003abaf00, DPL=0
  IDT[0x03]=64-Bit Interrupt Gate target=0x0010:fffff80003abb280, DPL=3
  IDT[0x04]=64-Bit Interrupt Gate target=0x0010:fffff80003abb380, DPL=3
IDT[0x05]=64-Bit Interrupt Gate target=0x0010:fffff80003abb480, DPL=0
... ...
IDT[0x29]=64-Bit Interrupt Gate target=0x0010:fffff80003bf2290, DPL=0
IDT[0x2a]=64-Bit Interrupt Gate target=0x0010:fffff80003bf22a0, DPL=0
IDT[0x2b]=64-Bit Interrupt Gate target=0x0010:fffff80003bf22b0, DPL=0
  IDT[0x2c]=64-Bit Interrupt Gate target=0x0010:fffff80003abca00, DPL=3
  IDT[0x2d]=64-Bit Interrupt Gate target=0x0010:fffff80003abcb00, DPL=3

  IDT[0x2e]=64-Bit Interrupt Gate target=0x0010:fffff80003bf22e0, DPL=0
IDT[0x2f]=64-Bit Interrupt Gate target=0x0010:fffff80003b09590, DPL=0
IDT[0x30]=64-Bit Interrupt Gate target=0x0010:fffff80003bf2300, DPL=0
      
    上面的粗体显示 interrupt gate 被设置为 3 级,在 windows 7 x64 下 vector 0x2c0x2d 被设置为系统调用接口。实际上这两个 vector 的入口虽然不同,但是代码是一样的。你可以通过 int 0x2cint 0x2d 请求系统调用。
    那么对于系统内部使用的 gate 我们应该保持与用户的隔离,绝大部分 interrupt handler 的 gate 权限都是设置为 0 级的。
    2.5.2 interrupt handler 的 code segment 权限设置    前面说过:interrupt handler 的执行权限应该至少不低于调用者的权限,意味着 interrupt handler 需要在高权限级别下运行。无论是系统提供给用户的系统服务例程还是系统内部使用的 interrupt handler 我们都应该将 interrupt handler 设置为 0 级别的运行权限(最高权限),这样才能保证 interrupt handler 能访问系统的全部资源。
    在权限检查方面,要求 DPLs 权限(interrupt handler 的执行权限)要高于或等于调用者的权限,也就是 CPL 权限,当数字上 DPLs 要小于等于 CPL(DPLs <= CPL)。
    2.6 使用 interrupt gate    使用 interrupt gate 来构造中断调用机制的,当 processor 进入 interrupt handler 执行前,processor 会将 eflags 值压入栈中保存并且会清 eflags.IF 标志位,这意味着进入中断后不允许响应 makeable 中断(可屏蔽中断)。它的逻辑 C 描述为:
          
*(--esp) = eflags;                           /* push eflags */
if (gate_descriptor.type == INTERRUPT_GATE)
          eflags.IF = 0;                       /* clear eflags.IF */
     
          interrupt handler 使用 iret 指令返回时,会将栈中 eflags 值出栈以恢复原来的 eflags 值。
          下面是 interrupt gate 的结构图:
          
          可以看到 interrupt gate 和 trap gate 的结构是完全一样的,除了以 type 来区分 gate 外,interrupt gate 的类型是:
          
  • 1110:32-bit interrupt gate
  • 0110:16-bit interrupt gate
          32 位的 offset 值提供了 interrupt handler 的入口偏移地址,这个偏移量是基于 code segment 的 base 值,selector 域提供了目标 code segment 的 selector,用来在 GDT LDT 进行查找 code segment descriptor。这些域的使用描述为:
            
if (gate_descriptor.selector.TI == 0)
        code_descriptor = GDTR.base + gate_descriptor.selector * 8;        /* GDT */
else
        code_descriptor = LDTR.base + gate_descriptor.selector * 8;        /* LDT */

interrupt_handler = code_descriptor.base + gate_descriptor.offset;         /* interrupt handler entry */
     
            注得注意的是:在 interrupt gate 和 trap gate 中的 selector 它的 RPL 是不起作用的,这个 selector.RPL 将被忽略
            在 OS 的实现中大部分的 interrupt handler 都是使用 interrupt gate 进行构建的。在 windows 7 x64 系统上全部都使用 interrupt gate 并没有使用 trap gate
继承事业,薪火相传
返回列表