2008/7/15 Taishan <[EMAIL PROTECTED]>: > 哪里有关于这方面的权威文档我不大清楚,这里把我知道的一些情况在这里 > 说一下吧。首先有两个事情需要先明确一下: > > 第一,关于保护模式于实模式。所谓实模式,就是CPU在与 Intel 8086 完全 > 兼容的方式下运行。当年 Intel 还没有发家时,其微型处理器产品的老大、老二 > 和老三(即4004、8008和8080)并没有出多大的彩儿,直到老四8086出生,其孪生 > 兄弟8088被当时的大佬IBM选中作为其第一代PC机产品的中央处理器使用,Intel才 > 逐渐走向辉煌。Intel在8086以后生产的微型处理器产品在8086的基础上进行了极 > 大的改进,增添了许多强劲的功能,但是这些功能有些无法在与8086兼容的方式下 > 产生作用,所以为了同时兼顾兼容与性能这两方面的需求,Intel为CPU设定了两种 > 运行状态,即保护模式和实模式。在保护模式下CPU的所有功能全开,以最大的性 > 能运行;而在实模式下,除了主频较快、增添了一些指令以及扩展的寄存器外,几 > 乎与8086的运行方式没有什么不同。程序可以通过改变CPU的状态寄存器EFLAG中的 > 一位来改变运行模式。 > 在系统刚刚启动的时候CPU是运行在实模式下的,直到操作系统启动,才改变 > 到保护模式下运行,所以BIOS中的程序都是在实模式下运行的。在这里需要特别注 > 意的是8086只有20根地址线(80286有24根,80386有32根,现在的64位CPU有64 > 根),即它的寻址范围是在 00000H 到 FFFFFH,20位的二进制数,正好是一个 > 兆,因此在实模式下,不管你安装了1M、1G还是1T内存,你最多只能用1M。 > > 第二,关于CPU实模式下"线性"地址表示与"段:偏移"地址表示的换算。保护 > 模式下的情况于实模式下有很大不同,这里只说一下关于实模式的。内存的地址是 > 为内存的最小存储单元(通常是一个字节)分派的一个唯一的编号。CPU通过地址 > 线将表示这个编号的电信号,放到地址总线上来选中某一个存储单元,再通过数据 > 线从数据总线访问这个被选中的存储单元,因此这个地址也称为"物理地址";同时 > 这些编号又是一些连续的整数,支持加减乘除这样的线性运算,所以也将这个编号 > 称为"线性地址"(注意,在保护模式下物理地址与线性地址是两个不同的概念,不 > 能混淆,但在实模式下物理地址与线性地址可以看作是一个概念)。在8086中寄存 > 器都是16位的,不足以表示一个20位的地址,因此Intel提出了一个用两个16位数 > 表示一个20位地址的方法,即"段:偏移"的表示方法。用于表示20位地址的两个16 > 位数一个被称为"段",一个被成为"偏移",将"段"乘以16再加上"偏移",就可以得 > 到一个20的数,作为地址使用。这里有一个问题,像FFFF:FFFF这样的地址,用上 > 面的方法换算的话会产生一个超过20二进制的数: > FFFFH * 10H + FFFFH = 10FFEFH > 这时CPU会自动的丢掉超过20位的部分,变成 0FFEFH,因此,如果你访问 > FFFF:FFFF这个地址的话,你会发现访问的是 0FFEFH 这个字节,而不是 > 10FFEFH,这个处理称之为wrap。 > > 这里说一些关于wrap的题外话,不感兴趣的可以跳过这一段。Intel在8086之 > 后推出的80286有24位地址线,因此Intel认为对超过20位的地址进行wrap处理没有 > 必要,所以80286及其以后的CPU都不再对超过20位的地址进行处理。这样就产生了 > 一个有趣的现象,一个程序可以在不改变到保护模式的情况下,通过 FFFF:0010 > 到 FFFF:FFFF 这些地址来访问1MB之上的64K内存(准确的说是64K-16个字节的内 > 存),当时就产生了很多利用这个技巧的软件。IBM考虑到兼容性的问题曾经希望 > Intel增加wrap的设计,不果后自己动手,在主板上用一个与门将A20地址线(第21 > 根地址线,从0开始编号)与一个控制信号连接到一起然后才将输出接到总线上。 > 这样如果需要wrap功能的话,将控制信号设为表示0的第电平,这样不管A20是0还 > 是1,最后输出的都是0,而不需要wrap的时候将控制信号设为高电平就行了。这个 > 控制信号的控制曾经被IBM放到键盘上,现在的PC机一般都通过软件来设置这个控 > 制信号,有些主板的CMOS设置程序中就有这个选项,有兴趣的人可以自己找找,一 > 般叫做Gate A20。当然,现在的操作系统都会自己设置A20的控制,在保护模式下 > 开启A20的wrap是灾难性的,除非你的操作系统和程序不使用第21位地址是1的内 > 存。 > > 回归主题,线性地址对于一个存储单元是唯一的,但是"段"表示法不是唯一 > 的,例如下面的几个地址都是FFFF0H这个地址的段表示: > FFFF:0000 FEDC:1230 FCDE:3210 F000:FFF0 F777:8880 > 通过计算可以得到一个线性地址对应的段表示方法有 2^12 = 4096 种。因此这里 > 尽量使用线性地址,避免歧义。 > > 下面说一下实模式下CPU可以访问的1M地址空间的使用情况。00000H 到 > 9FFFFH 这640K的地址空间分配给机器安装的RAM使用,也就是我们通常说的内存, > 这一段地址空间历史上被成为常规内存(Standard Memory);A0000H 到 FFFFFH 这 > 一段384K历史上被成为高端内存(High Memory),一般由三部分内容组成:Video > Ram区,通过对这段内存的读写控制显示器上的显示内容;扩展卡BIOS区,实际上 > 不只主板上有BIOS,在显卡、声卡、网卡、硬盘、光驱、键盘上一般也都有BIOS芯 > 片,其中有这些设备的规格信息和基本的中断处理访问程序,这些BIOS会被映射到 > 这一段地址区间;系统BIOS区,这个地址区段就是主板上BIOS芯片(即我们平常所 > 说一般意义上的BIOS)被映射到的区域。至于这几个区域的具体大小我记不大清楚 > 了,依稀记得系统BIOS区应该是这1M空间的最末尾的128K(即 E0000H 到 > FFFFFH),扩展卡BIOS区在系统BIOS区与Video Ram区之间,而Video Ram区 > 在扩展卡BIOS区与常规内存之间。 > 系统开机后,CS:IP被置为 FFFF:0000, 换算成线性地址就是 FFFF0H,即 > 1M地 > 址空间的最后16个字节处,打开用flashrom获取的BIOS镜像文件,找到最后的16个 > 字节,将前5个字节反汇编会发现是一个长跳转指令,这个指令跳转的目标才是真 > 正的加电自检(POST)程序。这末尾的十六个字节中的其他十一个字节一般是OEM > 的信息,内容任意你甚至可以写上你自己的名字。 > 我的系统读取的BIOS镜像长度为512K(00000H 到 7FFFFH),最后十六个字节 > (7FFF0 d到 7FFFF)的前五个字节是 ea 5b e0 00 f0,反汇编过来就是 > JMP F000:E05B > 即跳转到 FE05BH,这里就是真正的POST程序的位置。FE05BH相对于FFFFFH的偏移量 > 是 > FE05BH - FFFFFH = -1FA4 > 因此 JMP F000:E05B 的目标在镜像文件中的位置就是 > 7FFFFH -1FA4H = 7E05BH >
非常感谢,我估计你比大学里的老师讲得更详细:) 我按照你的方法已经找到我的BIOS的入口了,(7FFF0 d到 7FFFF)的前五个字节是 ea aa ff 00 f0,接下来我就可以开始跟踪机器码了:) 这么看来intel cpu manul的确有问题,它说的第一条指令应该是near jmp,看来是不对的,因为ea是far jmp. 那么bios也的确应该在1M内存以内,而不是FFFF0000-FFFFFFF0,我实在不明白怎么会有这么严重的bug.... -- Regards! Star Shanghai, China

