BIOS 是如何访问在内存还没有的时候检测并初始化内存的

Posted on May 8, 2011

首先必须明确的一点是, BIOS 运行初期,CPU 其实是不能访问内存的。

BIOS 所在的 FLASH 是那种可以被 CPU 直接寻址的 FLASH 芯片。被都固定在 0x4FFFF (记不清具体地址了) 地址上了。类似 ARM 使用的 NOR FLASH。 uboot 就在 NOR FLASH上。

然后,BIOS 初始化代码开始通过寄存器和北桥芯片沟通。 因为 BIOS 就是版卡厂商制作的,这不成问题。使用固化的 IO 地址就可以了。

成功和北桥联系上后, BIOS 继续前进,利用北桥提供的寄存器操控SMBUS, SMBUS 是一种 I2C 总线,每个 SMBUS 设备都有一个固定的地址。SDRAM 的 EEPROM 就可 以通过 SMBUS 访问! 而 SDRAM 通常在 0x50 ~ 0x53 这4个地址上,对于主板上 的4个内存插槽。不过,主板厂商也可以改变这些地址。反正 BIOS 是自己出的, 可以固化这些地址,无所谓。

读取 SDRAM eeprom 里超过 17 个参数后。 BIOS 就可以使用这些参数设置好北桥 的寄存器了。而在这之前, BIOS 严重依赖 CPU 的寄存器做这些操作,不能使用 任何内存! 北桥用来控制内存的寄存器地址,又是一个每个版卡都不一样的地方 了。无所谓, BIOS 自己出,爱怎么用就怎么用。

搞定内存之后, BIOS 终于可以使用内存啦! BIOS 将内存的前 4k 作为临时 stack 开始进入了第二步初始化,这个使用有了内存,就可以使用函数了。 前面,因为无法使用内存,无法进行复杂的运算,要小心编写汇编指令,全部使用 寄存器进行操作,所以没法进行复杂的运算。现在有 RAM 可以用了,自然就有内 存了。

BIOS 通过检查可用的内存值,将自己使用的临时 stack 移步到 > 640K 的地 方。 开始继续下面的工作。

下面, BIOS 该开始检测 CPU 了。主要的工作是比较 CPU 的微代码版本是否和 BIOS 里带的对应CPU的代码一致。否则更新 CPU 的微代码。所以刷新 BIOS 支持 新 CPU 的道理就在这里! ;)

再然后?那要看你是不是打开了 BIOS shadow 就会选择是不是做这一步了。 注意,这个时候 BIOS 是在一片能被 CPU 直接寻址的 FLASH 里运行的。但是 FLASH毕竟比 SDRAM 慢. 所以, BIOS 会通过北桥重新映射 BIOS FLASH 的地址到内存空间的顶端,原有的 映射没有断开!这个时候由于 A20 gate 没有打开,高 12 位被忽略,导致 CPU 寻址仍然在 640k ~ 1M,也就是 BIOS 现在在的地址,所以 BIOS 继续运行。 这个时候 BIOS 切换到 32 位寻址,注意,是 32位寻址,但是还是 16 位 模式。 想知道详细情况的,可以自己网上 google . 因为先前已经映射好了 BIOS 到高位 内存,所以 BIOS 继续运行。 然后 BIOS 将原先自己所在的 6040k~1M 地址区间 通过操作北桥,断开映射!是的,没错,断开了! 这个时候,这个区间其实就是 RAM 而不再是 BIOS ROM 了,这个很重要!接着打开 A20。 接下来就是很平常的 memcpy 了,将自己从高位内存拷贝回 640k ~1M 区间的地 位内存。 然后关闭 A20. 关闭 A20 后, CPU 寻址重新回到低位内存。 BIOS 完 成了将自己拷贝到同一个地址的工作. 但是 BIOS 将运行在 SDRAM 中,而不是 FLASH 里。

然后干嘛呢? 自然是继续检测基本硬件啦。基本硬件一般都已经固定使用某个 IO 端口了。 BIOS 就是发送一个无害的请求看是否有返回就可以知道硬件在不在。

对于显卡和网卡这类设备, BIOS 并不包括这些设备的初始化代码, BIOS 通过调 用这些设备的 BIOS 来实现。

对于任何找到的 PCI 设备,BIOS 都是直接调用对应 BIOS 设备的 BIOS 来初始化 它的 。 这也是硬盘还原卡截获 init 13 中断的道理。

而这些设备的 BIOS,自然初始化工作就是配置设备的寄存器地址了。

BIOS 搞定的 PCI 设备,会形成一个表格,叫 PCI CONFIG ,保存到 PCI 控制器 里 。可以通过 IO 端口 0xCFC-0xCFF 访问。

对于集成设备,这些设备的 BIOS 其实都被包含在 system BIOS 里,但是逻辑上 并不是一个整体。如果用一些 BIOS 工具,还是可以分离各个设备的 BIOS 的。

没用 BIOS 将设备配置好,并写入 PCI config , Linux 内核就会当这个设备不 存在 … 囧。

还有,各个 PCI 设备除了几个基本寄存器,别的都是通过 MMIO 映射到内存地址 里进行的,而不是使用 IO 端口地址。BIOS 要负责分配好,使他们不会重叠。

PCI 设备的地址从 0xFFFFFFFF- sizeof (BIOS) 开始向下分配,SDRAM 的地址从 0x00000000 开始向上分配。这些都是通过操作北桥的寄存器实现的。

自然,他们会在中间形成一个洞。不过,那是在你 SDRAM 内存比较少的情况下 …..

如果你的 SDRAM 内存比较多,恭喜你,你又遇到问题了,你的内存将被吞掉一部 分 …. 被 PCI 设备覆盖掉了一部分高位内存… 对 CPU 而言,整个内存地址都 充满了设备咯,没有空洞。

其实 Linux 认为的 640k~1MB 空洞也是不存在的。如果后续不使用 BIOS , 可 以放心的当 SDRAM 来用,因为就是 SDRAM . 自然,如果你没有开启 BIOS shadow 就另当别论了。

我之所以接触 Linux , 也是我本来计划自己写系统,我很讨厌 640K~1M 的内存 空洞,想知道怎么解决,就去看别的 OS 是怎么实现的,结果就发现了 Linux , 然后开始看 ….

不过后来比较囧的是, Linux 确实没有利用这个区间的 RAM … 真 TMD 有点浪 费啊! 现代的 BIOS 都默认 shadow 了,甚至没有地方给你配置了。 如果 Linux 不使用 BIOS , 不知道留这个洞在这里干嘛. 诶。 其实我 google 了 lkml , 是有人这么提出过这个问题。不过那个 Grek H 什么 的,就是貌似的Linux二号or三号人物,回的邮件里说,虽然多数系统可以回收这 个空洞,不过在个别系统上报告问题了。所以就默认不回收了。 如果你要回收,这里有 patch … use at your own risk ….

呵呵。

扯远了。洗洗睡吧。明天估计开机会看到一堆回复了。

Comments