为什么第一个 BIOS 指令位于 0xFFFFFFF0(RAM 的“顶部”)?

为什么第一个 BIOS 指令位于 0xFFFFFFF0(RAM 的“顶部”)?

我知道 BIOS 从 0xFFFFFFF0 加载其第一条指令,但为什么是这个特定地址?我有很多问题,希望您至少能帮助我解决其中的一些问题。

我的问题:

  • 为什么第一个 BIOS 指令位于 4 GB RAM 的“顶部”?
  • 如果我的计算机只有 1 GB 的 RAM,会发生什么情况?
  • 那么具有超过 4 GB RAM 的系统怎么样(例如,8 GB、16 GB 等)?
  • 为什么堆栈用某个值初始化(在本例中,是位于 0xFFFFFFF0 的值)?

我今天下午读到了这个内容,但我仍然不明白。

答案1

0xFFFFFFF0是 x86 兼容 CPU 在通电后开始执行指令的地方。这是 CPU 的一个硬连线、不可更改(没有额外硬件)方面,不同类型的 CPU 的行为不同。

为什么第一个 BIOS 指令位于 4 GB RAM 的“顶部”?

它位于 4 GB 的“顶部”地址空间- 开机时 BIOS 或UEFIROM 被设置为响应这些地址的读取。

我对此的看法是:

编程中的几乎所有事情都使用连续地址会更好。CPU 设计人员不知道系统构建者想要用 CPU 做什么,因此 CPU 要求将位于空间中间的地址用于各种目的不是一个好主意。最好将其“避开”在地址空间的顶部或底部。当然,请记住,这个决定是在 8086 刚推出时做出的,当时它没有内存管理单元

在 8086 中,中断向量位于内存位置 0 及以上。中断向量需要位于已知地址,并且希望位于 RAM 中以实现灵活性 - 但 CPU 设计人员不可能知道系统中将有多少 RAM。因此从 0 开始并逐步增加是合理的(因为 1978 年发明 8086 时没有系统拥有 4 GB 的 RAM - 因此期望 RAM 位于 0xFFFFFFF0 不是一个好主意),然后 ROM 必须位于上限。

当然,至少从 80286 开始,中断向量可以移动到 0 以外的其他起始位置,但现代 64 位 x86 CPU 仍然以 8086 模式启动,因此一切仍然以旧方式运行以实现兼容性(虽然在 2015 年仍然需要​​ x86 CPU 才能运行 DOS 听起来很荒谬)。

因此,由于中断向量从 0 开始并向上工作,ROM 必须从顶部开始并向下工作。

如果我的计算机只有 1 GB 的 RAM,会发生什么情况?

32 位 CPU 有 4,294,967,296 个地址,编号为 0 (0x00000000) 至 4294967295 (0xFFFFFFFF)。ROM 可以位于某些地址,RAM 可以位于其他地址。借助 CPU 的 MMU,甚至可以即时切换。RAM 不必位于所有地址。

由于只有 1 GB 的 RAM,某些地址在读取或写入时不会有任何响应。这可能导致访问此类地址时读取无效数据或系统死机。

那么具有超过 4 GB RAM 的系统怎么样(例如:8 GB、16 GB 等)?

简单来说:64 位 CPU 有更多地址(这是使其成为 64 位的原因之一 - 例如 0x0000000000000000 到 0xFFFFFFFFFFFFFFFF),因此额外的 RAM“适合”。假设 CPU 处于长模式. 直到那时,RAM 仍然存在,只是不可寻址。

为什么堆栈用某个值初始化(在本例中,是位于 0xFFFFFFF0 的值)?

我无法立即找到有关 x86 在开机时分配堆栈指针的任何信息,但是一旦初始化例程发现系统中有多少 RAM,它最终都必须由初始化例程重新分配。 (@Eric Towers 在下面的评论中报告说它在开机时设置为零。)

答案2

它并不位于 RAM 的顶部;它位于 ROM 中,其地址位于内存地址空间的顶部,以及扩展卡(如以太网控制器)上的任何内存。它在那里是为了不与 RAM 冲突,至少在您安装 4 GB 之前不会。具有 4 GB 或更多 RAM 的系统可以做两件事来解决冲突。廉价主板只是忽略与 ROM 位置冲突的 RAM 部分。不错的主板会重新映射该 RAM,使其看起来具有 4 GB 标记以上的地址。

我不确定你问的堆栈是什么。它肯定没有初始化为 ROM。当 CPU 重置时,它最初处于“实模式”,其行为与原始 8086 一样,使用 16 位分段寻址,仅允许其访问 1 MB 内存。BIOS 代码位于该 1 MB 的顶部。BIOS 在 RAM 中挑选某个位置来设置堆栈,并加载和执行第一个可启动驱动器的第一个扇区。一旦操作系统接管并设置自己的堆栈(每个任务/线程一个),就由操作系统切换到 32 位或 64 位模式。

答案3

首先,这实际上与 RAM 无关。我们谈论的是地址空间这里 - 即使您只有 16 MiB 内存,您仍然拥有 32 位 CPU 上的完整 32 位地址空间。

这已经回答了您的第一个问题,实际上 - 在设计时,现实世界的 PC 内存远没有达到完整的 4 GiB;它们的内存范围更多在 1-16 MiB 之间。地址空间在所有意图和目的上都是免费的。

现在,为什么是 0xFFFFFFF0?CPU 不知道 BIOS 有多少空间。有些 BIOS 可能只占用几千字节,而另一些 BIOS 可能占用整兆字节的内存 - 我甚至没有涉及各种可选 RAM。CPU 必须硬连线到某个地址才能启动 - 没有人可以配置 CPU。但这只是地址空间的映射 - 地址直接映射到 BIOS ROM 芯片中(是的,这意味着如果您有那么多 RAM,您此时无法访问完整的 4 GiB RAM - 但这并不是什么特别的事情,许多设备都需要自己的地址空间范围)。在 32 位 CPU 上,此地址为您提供完整的 16 个字节来执行最基本的初始化 - 这足以设置您的段,如果需要,还可以设置地址模式(请记住,x86 以 16 位实模式启动 - 地址空间不是平坦的)并跳转到真实的启动“程序”。此时,您根本不使用 RAM - 它只是映射的 ROM。事实上,此时 RAM 甚至还没有准备好使用 - 这是 BIOS POST 的工作之一!现在,您可能会想 - 16 位实模式如何访问地址 0xFFFFFFF0?当然,有段,所以您有 20 位地址空间,但这还不够好。好吧,这里有一个技巧 - 地址的 12 个高位被设置,直到您执行第一个长跳转,让您可以访问高地址空间(同时拒绝访问低于 0xFFF00000 的任何内容 - 直到您执行长跳转)。

在现代操作系统中,所有这些都是程序员(更不用说用户)无法看到的东西。您通常无法访问如此低级别的任何内容 - 有些内容已经无法挽救(您不能随意切换 CPU 模式),有些内容则完全由 OS 内核处理。

因此,MS DOS 上的老式编码提供了更好的视图。设备内存直接映射到地址空间的另一个典型示例是直接访问视频内存。例如,如果您想快速将文本写入显示器,则直接写入地址B800:0000(加上偏移量 - 在 80x25 文本模式下,(y * 80 + x) * 2如果我没记错的话,这意味着每个字符两个字节,逐行)。如果您想逐像素绘制,则使用图形模式和起始地址A000:0000(通常为 320x200,每像素 8 位)。做任何高性能的事情通常意味着深入研究设备手册,以弄清楚如何直接访问它们。

直到今天,它仍然存在 - 只是被隐藏了。在 Windows 上,您可以在设备管理器中看到映射到设备的内存地址 - 只需打开网卡之类的属性,转到资源选项卡 - 所有内存范围项都是从设备内存到主地址空间的映射。在 32 位上,您会看到大多数设备都映射到 2 GiB(后来是 3 GiB)标记之上 - 同样,这是为了尽量减少与用户可用内存的冲突,尽管这实际上不是虚拟内存的问题(应用程序不会接近真实, 硬件地址空间 - 它们有自己的虚拟化内存块,例如,可以映射到 RAM、ROM、设备或页面文件)。

至于堆栈,嗯,应该有助于理解默认情况下堆栈从顶部增长。因此,如果您执行push,新的堆栈指​​针将位于0xFFFFFEC- 换句话说,您不会尝试写入 BIOS 初始化地址 :) 这当然意味着 BIOS 初始化例程可以安全地使用堆栈,然后再将其重新映射到更有用的地方。在老式编程中,在分页成为事实上的默认设置之前,堆栈通常从 RAM 的末尾开始,当您开始覆盖应用程序内存时会发生“堆栈溢出”。内存保护改变了很多,但总的来说,它尽可能保持向后兼容性 - 请注意,即使是最现代的 x86-64 CPU 也可以仍然启动 MS DOS 5- 或者 Windows 如何仍然可以运行许多不知道分页的 DOS 应用程序。

答案4

重置后,8088/8086 兼容 CPU 会执行 0FFFF0 处的指令,该指令比 1 兆字节限制低 16 个字节。通常,此位置的 ROM(在 PC 实现中)将是 BIOS,因此在 BIOS ROM 的末尾,会跳转到 BIOS rom 的开头。

此处显示:起始向量和其后面的“日期”签名,IBM 5150 PC 8KB eprom dump bios 日期:1981 年 10 月 19 日

00001FEE  FF                db 0xff
00001FEF  FF                db 0xff
00001FF0  EA5BE000F0        jmp word 0xf000:0xe05b
00001FF5  3130              xor [bx+si],si
00001FF7  2F                das
00001FF8  3139              xor [bx+di],di

请注意,寻址是 8KB $2000 rom,它将起始地址(绝对远 JMP,到任何其他位置,在本例中是 8KB rom 本身,但不是该 rom 内的最低可能地址)放置在 $FFFF:$0 分段或 $FFFF0 线性。

至于兼容性:如果某些“未来”或当前的处理器“期望”它在地址前面有更多的 F,那没关系。为了与旧系统中的较新 CPU 兼容,额外的地址线保持未连接,因此数据总线上的数据完全相同。只要最低有效位保持为 FFFF0。

(在只有 1mb RAM 且 ROM 位于该 RAM 末端的系统中,没有其他任何东西,它会很高兴地“认为”它正在与更高的地址通信但却获得完全相同的数据,因为这些实现从未听说过高于 A19 的地址线)

请注意,世界不仅仅是“个人电脑”……IBM 个人电脑是一个“意外”,这些处理器从未专门为“个人电脑”设计,而且除了个人电脑之外,还用于更多领域(例如卫星、武器系统等)。32 位和 64 位保护模式通常不是理想的选择。(例如,虚拟 8086 模式作为选择较新版本(386+)的原因更有趣)。因此,“向后兼容性”远不止“它能否运行 dos”。

相关内容