免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3070 | 回复: 1
打印 上一主题 下一主题

x86处理器中的特权级检查 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-02-20 11:12 |只看该作者 |倒序浏览

该部分内容需要仔细阅读Intel或者AMD的相关文档,Intel文档名为Intel 64 and IA-32 Architectures Software Developer's Manual, AMD的文档名是:AMD64 Architecture Programmer's Manual,这些文档在intel和amd的官方网站上都可以下到,据我了解intel甚至提供纸质的文档供开发者使用(需要自己提交申请)。 这些文档基本上围绕x86的系统编程展开,包括x86的指令集,一般情况下大家很少会自己去写一个操作系统,那么大部分情况下阅读这些文档是为了理解Linux内核,所有关于x86系统编程方面的文档都不会超出上面提到的这两篇。个人的建议是IA32看Intel的文档,64位的看AMD64文档。

关于x86 64位的历史这里也简单提一下,当年AMD提出64位概念时,是要兼容以前的32位x86处理器的,而当时intel正忙于安腾处理器,这是个全新的架构,也就是现在说的IA64(Intel Architecture 64),和x86处理器完全不兼容。就技术本身而言,很难说安腾是个失败的产品,但是最终还是市场说了算,因为x86 32位多年历史的积累,上面运行着无数的应用程序,而这些程序将无法在安腾(Itanium)处理器上运行,导致了IA64无法在市场上获得成功, intel曾经有team专门在二进制级别上将x86 32的指令翻译成IA64指令,希望在市场全面转向IA64时作为一个兼容x86的临时解决方案。而AMD64处理器因为兼容32位而最终被市场所接受。历史上AMD的股价在05年底06年初时曾达到40多美元,那段时间对intel来说是比较郁闷的,直到后来core架构的出现。客观地说AMD在其发展历史上曾有过很多不错的技术与创新,可惜差不多都昙花一现,其中原因很多,我想外部因素主要应该有以下两方面,一方面是因为其对手Intel在x86领域深厚的技术沉淀与积累,另一方面是Intel具有强大的资金资源与市场影响力。很多时候AMD弄出来的东西Intel会明确表态不予支持,那么这个东西基本上就不会有多少市场(MSFT OS都不支持了,Application会去支持吗?!),搞得AMD不但必须得推倒自己的东西,而且还要反过来支持intel的,因为无论技术如何,最终还是由市场说了算。现在我们提x86 64位,比较中立的说法是x86 64,这估计也是intel乐于见到的,因为在x86 64位上,实际是AMD首先实现的,所以AMD将64位x86称之为AMD64. 有点扯远了...

下面回到本帖的主题,在x86处理器上如何进行代码特权级的检查。我将一些比较容易引起混淆的概念整理如下:

1.  对于数据段访问的权限检查:
    在处理器把要访问的数据段选择子加载到相应的段寄存器(DS, ES, SS, FS, GS)之前,处理器会先做个权限检查。它会比较CPL,要加载的数据段选择子的RPL以及选择子所选择的数据段描述符中的DPL.只有当下列条件满足时,才可以访问相应的数 据段:
   DPL >= CPL && DPL >= RPL. 否则就产生一个通用保护异常,这种情况下段寄存器并不会被加载。

2. 堆栈段作为一种特殊的数据段,其权限检查比较特别:只有当CPL=RPL=DPL时,堆栈段才允许被访问。

3. 对于代码间的转移,有几种不同的情况:
  a)Far形式的JMP, CALL, RET指令后跟的目标代码段的选择子(该selector中的低2位代表RPL,当前CS寄存器的低2位代表CPL)
     这里又分两种情况,非一致代码段和一致代码段:
    Nonconforming Code Segment --这种情况要求CPL一定要等于DPL,其次RPL<=DPL.如果成功转移到了目标代码段,CPL保持不变,即跳转前后无特权级变化。
    Conforming Code Segment --这种情况要求CPL >= DPL,RPL则无需检查。转移后特权级也不变化。因为转移前后CPL没有变化,所以也没有堆栈切换发生。
    绝大多数代码段都是非一致代码段,这种情况下(Far JMP, CALL, RET CS_Selector)不会引起特权级变化。
    一致代码段还是非一致代码段由目标代码段所属描述符的Type字段决定。

  b)如果想在不同特权级代码间穿越,则必须使用门这种机制。x86处理器提供四种门:
   调用门 陷进门 中断门 任务门
   任务门用来做任务切换,陷阱门和中断门是一种特殊的调用门,用来呼叫陷阱和中断处理函数。

   调用门 -- 通过调用门除了可以在不同特权级代码间转换外,还可以在16-bit和32-bit的代码间实现穿越。调用们描述符只存在于GDT或者
   LDT,不在IDT中。用Far CALL/JMP gate_selector, offset就可以通过调用门实现代码的穿越。指令中的offset虽然需要,但是
   处理器不会使用,应为被调用的代码段入口点将由调用们描述符中的offset来替换。
   在权限检查方面,CALL指令与JMP指令稍有区别。但是它们都会检查以下四个部分:CPL, RPL, 调用门的DPL和目标代码段描述符的
   DPL.

   CALL和JMP要想访问调用门,首先必须确保both CPL and RPL <= DPL of the call gate. 其次对于非一致的目标代码段的描述符
   中的DPL(简写为DDPL, Destination DPL),对于CALL指令要求:DDPL <= CPL,对于JMP指令,要求DDPL=CPL.所以,对于非一致目标
   代码段而言,CALL指令可以切换到更高的优先级,而JMP则不可以。一旦CALL指令成功切换到更高优先级代码段,CPL=DDPL并伴有
   堆栈的切换。

原帖链接:http://www.embexperts.com/forum. ... &extra=page%3D1

论坛徽章:
0
2 [报告]
发表于 2012-02-20 11:14 |只看该作者
接下来在1楼的基础上讨论一下Linux内核中关于系统调用与外部硬件中断的特权级检查
x86下的系统调用和中断处理最终都是通过中断门描述符表IDT,系统调用以int $0x80的形式出现。现在来看看系统调用是如何将处理器的特权级从ring 3转到ring 0。

系统调用在IDT中的初始化发生在trap_init函数中:
<arch/x86/kernel/traps.c>
void __init trap_init(void){
    ...
    #ifdef CONFIG_X86_32
        set_system_trap_gate(SYSCALL_VECTOR, &system_call);
        set_bit(SYSCALL_VECTOR, used_vectors);
    #endif
    ...
}

<arch/x86/include/asm/desc.h>
static inline void set_system_trap_gate(unsigned int n, void *addr)
{
        BUG_ON((unsigned)n > 0xFF);
        _set_gate(n, GATE_TRAP, addr, 0x3, 0, __KERNEL_CS);
}

set_system_trap_gate函数用来对IDT中第0x80项所对应的描述符做初始化:类型为TRAP(所以Linux下的系统调用在x86处理器中属于trap类型, trap属于x86异常中的一种,在trap发生时,处理器会将引起trap的下条指令的cs, eip保存到内核栈中),addr所对应的实参为systgem_call,加上最后的一个要转移的代码段选择子__KERNEL_CS,所以很清楚当int $0x80指令执行时,代码将试图向__KERNEL_CS: system_call处转移。_set_gate函数中的ist在x86 32下用不着,只出现在64位上。最后一个与特权级密切相关的是0x3,它是0x80所对应IDT中描述符的DPL, int n指令在保护模式下需要进行特权检查(可以参考intel volume 2A中的int指令微码逻辑),在用户空间发起系统调用时CPL=3, 在内核空间发起系统调用时CPL=0,都满足CPL <= DPL的要求,所以代码将跳转至__KERNEL_CS: system_call,因为__KERNEL_CS中的RPL=0,所以系统调用使得转移后的代码运行在ring 0上。

关于外部中断的IDT描述符,在Linux内核中由set_intr_gate负责设定,其中DPL指定为0,这意味着如果在用户空间使用int n指令,因为CPL>DPL,所以会有通用保护异常发生,这是为了防止在用户空间有恶意程序用int指令来模拟硬件中断。但是硬件中断发生时,被中断的程序或者运行在用户态(CPL=3),或者运行在内核态(CPL=0),对于这种外部中断的发生,处理器不会对其做特权级检查,所以外部硬件中断发生时,总是会转向__KERNEL_CS:general_intr_handler, 其中general_intr_handler是中断处理函数。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP