• 2004-06-01

    引导中的模拟

    Tag:向下走

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/202366.html

    Linux的引导代码(Linux源码目录下的arch/i386/boot/setup.S)中有这样一段:
    # NOTE: For high loaded big kernels we need a
    #       jmpi    0x100000,__KERNEL_CS
    #
    #       but we yet haven't reloaded the CS register, so the default size
    #       of the target offset still is 16 bit.
    #       However, using an operand prefix (0x66), the CPU will properly
    #       take our 48 bit far pointer. (INTeL 80386 Programmer's Reference
    #       Manual, Mixing 16-bit and 32-bit code, page 16-6)                                                                                             
            .byte 0x66, 0xea                       # prefix + jmpi-opcode
    code32:.long   0x1000                         # will be set to 0x100000
                                                   # for big kernels
            .word   __KERNEL_CS

    它出现在刚刚进入保护模式之后,从注释可以看出,这是一段模拟代码,模拟的是
        jmpi    0x100000,__KERNEL_CS

    认可手法容易,认可动机难。
    顺藤摸瓜,根据注释,我找到了《INTEL 80386 Programmer's Reference Manual》。第16章讲的是如何将16位代码同32位代码混和起来。我从16.2节我得到了一些有用的信息:
    80386有这样两个指令前缀,正是它们的存在,才使得在一个段中混和32位和16位操作成为了可能。它们是:
    operand-size前缀 66H
    address-size前缀 67H
    这两个前缀可以反转缺省的大小,就是16位变成32位,32位变成16位,从而为混合创造条件。看一下手册中提到的这个MOV mem, reg(这是常见INTEL汇编格式,而前面Linux用的是AT&T汇编格式)的例子:
    在32位段内:
    1 通常是从32位寄存器中移动32位到内存的32位有效地址中
    2 如果使用operand-size前缀,就是从16位寄存器中移动16位到内存的32位有效地址中
    3 如果使用address-size前缀,就是从32位寄存器中移动32位到内存的16位有效地址中
    4 如果同时使用address-size前缀和operand-size前缀,就是从16位寄存器中移动16位到内存的16位有效地址中
    在16位段内:
    1 通常是从16位寄存器中移动16位到内存的32位有效地址中
    2 如果使用operand-size前缀,就是从32位寄存器中移动32位到内存的16位有效地址中
    3 如果使用address-size前缀,就是从16位寄存器中移动16位到内存的32位有效地址中
    4 如果同时使用address-size前缀和operand-size前缀,就是从32位寄存器中移动32位到内存的32位有效地址中

    好像一段绕口令,回头来看前面的问题。
    虽然经过一系列的努力,代码已经进入到保护模式之中,但刚刚打下的天下哪是那样太平,CS就是一个实模式残留下来的余孽,因为刚刚进入到保护模式社会,还没来得及对它进行保护模式改造。当大家已经进入保护模式的康庄大道,它还默守着实模式的陈规。既然已经进入了保护模式社会,把还处于实模式社会的CS请进来就成了当务之急,毕竟在Intel的世界中,CS还是个举足轻重的家伙。
    我们需要的是32位的地址模式,而CS只能给我们16位。这时,混和16位与32位的方法就有了用武之地。
    一个简单的跳转就可以重新加载CS的内容,套用上面证据,为16位的代码加上66H,16位就成了32位,于是CS得到了浴火重生的机会。
    直接把jmp指令写在程序里,还在16位下的CS只能带给CPU得到的只能是一个16位的操作,于是就有了这种模拟的写法。

    分享到:

    历史上的今天:

    六一校园行 2011-06-01
    引用地址: