我有一个关于 BIOS 是如何加载和执行的问题。我知道CPU从0xFFFF0开始执行BIOS,但是BIOS在那里是怎样的呢?
BIOS 是否硬连接到 CPU 上的那个地址,到了你无法覆盖它的地方,
或者它被复制到该内存地址,就好像它只是您可以读/写的常规内存一样。
如果是这样,如果没有现有程序,它如何复制到该地址?
上下文:我正在为自定义 CPU 编写模拟器,我想知道 BIOS 如何在内存中存储和加载。
我有一个关于 BIOS 是如何加载和执行的问题。我知道CPU从0xFFFF0开始执行BIOS,但是BIOS在那里是怎样的呢?
BIOS 是否硬连接到 CPU 上的那个地址,到了你无法覆盖它的地方,
或者它被复制到该内存地址,就好像它只是您可以读/写的常规内存一样。
如果是这样,如果没有现有程序,它如何复制到该地址?
上下文:我正在为自定义 CPU 编写模拟器,我想知道 BIOS 如何在内存中存储和加载。
具体细节当然取决于系统设计,但例如 IBM PC(具有更便宜但功能相似的 8088)具有8kb ROM 芯片。正如您所建议的,这是硬连线到较高内存地址的。因此,在复位时,CPU 会直接从 ROM 中读取数据。写入 ROM 存储空间显然不会影响 ROM 内容。
要回答有关仿真的问题,请在 0xFFFF0 处启动仿真 CPU 之前,将所需的 BIOS 加载到所需的地址(0x100000 减去 BIOS 的大小)。
背景:
8086(及兼容)从地址 0xFFFF0 开始执行。由此可见,必须已经有一条有意义的指令(通常是跳转到 BIOS 的开头)。BIOS 不是 CPU 的一部分,因为 BIOS 是特定于系统的。如果CPU中有BIOS,则CPU只能在该系统中使用。
因此,为 BIOS 提供指令超出了 CPU 的范围,系统必须在 CPU 进行任何初始化之前向 CPU 提供这些指令。
当时常见的解决方案是在 ROM 芯片中提供 BIOS。8086 的地址空间为 1MB 或 0x100000,因此 0xFFFF0 位于顶部下方。当CPU要读取内存时,它会将所需的地址放置在地址总线上,系统必须根据该地址的高位来决定如何反应该地址。要选择 ROM 芯片,您只需要一个解码器来检测顶部地址位全部设置为 1,因此带有几个输入的简单与门就可以了。需要多少位取决于 BIOS 的大小。8kB或0x2000的BIOS将从0x100000-0x2000=0xFE000开始,因此需要顶部7个地址线来解码0xFE。
访问 ROM 的速度相当慢,因此后来的系统决定将内容复制到 RAM,然后从 RAM 运行 BIOS。但这并不适用于早期系统。最初的 IBM PC 配备 16kB RAM,可扩展至 64kB。仅仅为了更快地执行 BIOS 而浪费 8kB RAM 是没有意义的。浪费实现这一点所需的电路也是没有意义的。由于无论如何你的模拟器都是从 RAM 运行的,所以实现类似的东西是没有意义的,除非你这样做是为了某些需要它的现有软件的利益。
简单来说,设备的 BIOS 或固件并不物理存储在处理器中。PCB上有一个专用芯片来存储它。该固件旨在通过与 PCB 上的所有主要组件“对话”来检查并启动所有必需的测试和流程。
固件从芯片启动并存入内存(RAM)以供主动使用。设备上任何其他组件上的关联地址仅仅是 BIOS 用于运行/启动启动期间所需执行的任何进程的地址。
由于您已经编辑并指定了更多信息,我认为以下现有帖子可能会帮助您更好地理解,因此我不必全部写出来:
https://cs.stackexchange.com/questions/63839/where-does-the-cpu-get-its-first-instructions-from
https://stackoverflow.com/questions/7804724/how-is-the-bios-rom-mapped-into-address-space-on-pc
在 8088/8086 系统中,这通常是通过物理映射/连接 A16-A19 线来实现的,以启用(通过 74138 芯片启用/选择线)连接到 ROM 芯片启用线的正确 74138 型解码器。板上的其他 74138 的芯片启用/选择线的接线方式不同,因此当 RAM 的地址位于(地址)总线上时,后者被启用。在那个时代,您还拥有多个物理 ROM 芯片(小于 64Kb),因此这就是使用解码器的原因:A13-A15 行通常由同一个 74138 解码,以从集合中选择 ROM 芯片。
下面是一个类似的完整存储系统的图表(源)(139 是同一封装中的两个 138)。我怀疑这更像是一个学术系统而不是真正的系统:它的下半部分使用 4x16Kb ROM 芯片(27128)图中的上半部分是SRAM(62256芯片)。第一台真正的 IBM PC 使用 DRAM 作为 RAM,这就是为什么这更像是学术性的而不是真实的。
我认为,它曾经被称为“影子 RAM”,直到386 时代才出现。这是如何完成的(在一个实现中):
其余的描述[对我来说]有些令人困惑,但本质上似乎那个时代的内存控制器在初始引导/复制模式下重定向了 ROM 地址写入 RAM。然后 BIOS 将控制器切换到“影子模式”,从该模式对这些地址的读取也从 RAM 进行(在复制完成后)。显然,在后一种/“影子”模式下,对 ROM 寻址的写入被禁用/忽略[在内存控制器级别],因此 BIOS 的 RAM 副本在此之后无法更改。但这可能相当依赖于内存控制器。
控制启动与稍后/“影子”模式的位位于 I/O 空间中的某个位置,例如在该特定控制器上,在进行自复制之后,BIOS 例程必须执行以下操作:
为清楚起见,“386”处理器的影子模式工作方式并没有什么特别之处。这一切都在[外部]内存控制器级别。只是据我所知,早期的IBM PC 中不存在该功能。
BIOS 被硬件映射到内存的顶部 8KB,处理器从那里读取它,并具有 ROM 访问所需的所有相关额外等待状态。
原始 8086 PC 没有足够的备用 RAM 来将 ROM 复制到其中(原始的 RAM 总共只有 16KB),即使有,也没有硬件将 RAM 重新映射到 ROM 占用的地址空间。
我依稀记得 BIOS 大约为 8KB(我拥有的一台 IBM PC,在装订手册背面附有 BIOS 列表(!))。
iMC 作为 PCI(e) 配置空间中的一组寄存器和/或 MSR 寄存器公开。复位后,控制标准孔(包括从物理地址 0xa0000 到 0xfffff)的寄存器(例如 SAD 寄存器)的值默认不回收任何内存事务。
当内核执行加载(例如 0xffff0)时,iMC 不会回收它。然后负载到达充当默认目的地的系统代理(类似于 PCI 桥中的减法解码)。如果内部 PCI(e) 回收此类负载,则会将其分派给它(例如传统 VGA 帧缓冲区)。在这种情况下,没有正常的 PCI(e) 会这样做,因此系统代理会沿着 DMI 链路发送内存负载。
下游负载到达 PCH,PCH 在复位时读取(可能是虚拟的)strapon 引脚以了解固件闪存 ROM 的位置(通常通过 SPI 总线或 LPC 总线),然后读取闪存描述符以找出固件闪存 ROM 的位置。闪存区域并默认将 BIOS 区域映射到 4GiB 限制的末尾(PCI 孔所在的位置)。此外,默认情况下,PCH 将刚好低于 4GiB 的区域别名为 0xe0000-0xfffff。
因此,加载到 0xffff0 的内容会被别名为 0xfffffff0,由 ROM 回收,并解码到适当的偏移量。
正如您所看到的,这都是默认的。
当你回到过去时,架构会发生变化,但原理保持不变。在最初的 8086 中,可能只有一个简单的共享总线,每个对数字电子知识了解甚少的人都知道,将设备映射到单个共享总线上的固定地址非常容易(这就是传统 IO 端口号的产生方式)。
值得注意的是,您对 x86 引导的理解与实际的现代事物相去甚远。不仅现代 x86(不支持 x86S)以 0xffff0000 的 CS 基数启动,而且自 Haswell 以来,CPU 在 INIT 上做的第一件事就是获取(通过 ucode)FIT 并执行 ACM(最终将返回到传统引导)。
此外,通常是 PCH 首先唤醒并断言正确的引脚/虚拟线来启动 CPU。负责此操作的 PCH 固件是 BUP(Bring UP),它位于 CPU 固件的同一闪存 ROM 中(只是位于不同的区域)。由于闪存 ROM 直接连接到 PCH,因此很容易想象如何访问它。
即使在 PCH 通电之前,BMC(服务器)、SuperIO 芯片(台式机)或嵌入式控制器(笔记本电脑)也已通电,这些通常是 ucontroller(即它们有内部 ROM)。实际上,在获取第一个 x86 指令之前,有很多可能的软件会运行。
最后,固件通常将 iMC/NB 配置为回收对范围 0xe0000-0xfffff 的写入,然后读取整个范围并将其复制到自身。最后,写入被路由回 PCH(默认情况下,PCH 不会通过 SPI/LCP 路由它们),而读取则被回收。这具有复制内存中固件的效果,即所谓的镜像。
它可以直接读取(某些对地址进行解码的电路可以根据实际地址范围确定选择从哪个内存中读取),也可以将其从 ROM 复制到 RAM(称为映射)。不需要任何程序来执行此操作,专用(且相当简单)的硬件电路可以简单地循环访问地址范围并在启动时进行复制,然后再让处理器启动。
您可能想在 YouTube 上观看爱好者(例如 Ben Eater、James Sharman、Jack Oatley)使用分立元件创建 8 位时代微处理器的系列文章中的一个或多个。这既有趣,又对您真正了解这些电路的实际工作原理非常有帮助。
附录:因为相当多的其他答案和评论似乎将 ROM 阴影视为一些复杂的过程,需要处理器的特定支持,并且对于 8086 时代的技术来说是不可想象的,所以这是一个非常简单的硬件演示,感谢 James Sharman(如前所述)上): https: //www.youtube.com/watch?v =yR9cw7QehCg
尽管我确实希望该视频永远不会从 YouTube 上消失,但为了与自包含答案的 SE 政策保持一致,快速描述一下:计数器迭代所涉及的实际地址范围,同步寻址 ROM 和 RAM。ROM 被连线以断言其数据,RAM 被连线以从总线读取数据。当计数器到达末尾时,其进位输出触发附加电路以完全禁用 ROM、启用 RAM 并启动处理器的实际复位过程。