2. x86计算机体系结构基础:CPU模式、内存地址空间与I/O机制

本章是BIOS/UEFI固件开发的基石。x86处理器从加电复位到操作系统启动,会经历多种CPU模式切换;内存地址空间的布局决定了固件、操作系统和硬件资源的映射关系;I/O端口与MMIO则是固件与外围设备通信的两大核心通道。理解这三部分,才能正确编写初始化代码、配置芯片组和加载操作系统。

2.1 CPU模式:实模式、保护模式与长模式

x86处理器自Intel 8086起,经历了16位、32位到64位的演进,形成了三种主要的CPU工作模式。BIOS/UEFI固件在启动过程中必须依次穿越这些模式。

模式 地址宽度 最大物理内存 段/页机制 典型用途
实模式 20位 1 MB 段基址:偏移,无页 BIOS早期初始化、MBR/VBR引导
保护模式 32位 4 GB 段描述符 + 可选分页 BIOS后期、传统OS内核、UEFI DXE阶段
长模式 64位 ≥ 64 TB 段基址强制0 + 4级/5级分页 UEFI 64位运行时、现代OS内核

2.1.1 实模式(Real Mode)

处理器复位后默认进入实模式。该模式下CPU如同一个高速的8086:

  • 地址计算:物理地址 = 段寄存器 × 16 + 偏移量,最大寻址1 MB(0x00000 ~ 0xFFFFF)。
  • 段寄存器:CS、DS、ES、SS、FS、GS,每个段最大64 KB。
  • 中断向量表(IVT):位于物理地址0x00000 ~ 0x003FF,共256个中断向量,每个4字节。
  • BIOS数据区(BDA):位于0x00400 ~ 0x004FF,存放系统硬件参数。
BIOS开发要点:实模式下没有特权级保护,任何代码都可以访问全部1 MB空间。BIOS的POST(上电自检)和早期硬件初始化都在此模式完成。切换到保护模式前,必须关闭中断、加载全局描述符表(GDT)。

2.1.2 保护模式(Protected Mode)

保护模式引入段描述符和分页机制,提供4 GB线性地址空间和硬件级保护:

  • 段描述符:每个段由8字节描述符定义,包含基址、限长、类型、特权级(DPL)等属性。
  • 全局描述符表(GDT):存放所有段描述符,由GDTR寄存器指向。
  • 分页:可选启用,将线性地址转换为物理地址,支持4 KB/2 MB/4 MB页面。
  • 特权级:Ring 0(最高,内核/固件)~ Ring 3(最低,应用程序)。
常见陷阱:从实模式切换到保护模式时,必须立即用远跳转(far jump)刷新CS段选择器,否则后续指令会因段缓存不一致而崩溃。另外,保护模式下IVT不再使用,需改用中断描述符表(IDT)。

2.1.3 长模式(Long Mode)

长模式是x86-64架构的原生64位模式,也是UEFI固件运行的主要模式:

  • 地址空间:线性地址48位(当前实现),物理地址最高52位(取决于CPU)。
  • 段基址强制为0:CS、DS、ES、SS的基址被忽略,分页成为唯一地址转换机制。
  • 4级/5级分页:PML4 → PDPT → PD → PT(4级),或增加PML5(5级)。
  • 兼容性子模式:长模式下可运行32位代码(CS.L=0, CS.D=1),用于执行传统Option ROM。
UEFI实践:UEFI固件在SEC阶段仍使用实模式或16位保护模式,进入PEI阶段后切换到32位保护模式,DXE阶段则进入64位长模式。Runtime Services必须在长模式下运行,以支持64位操作系统。
x86 CPU模式切换流程 x86 CPU模式切换流程(BIOS/UEFI启动路径) CPU复位 → 实模式 加载GDT → 保护模式(32位) 启用分页 + PAE → 长模式准备 切换CS → 长模式(64位) 关键步骤 1. 关闭中断(CLI) 2. 加载GDT/IDT 3. 设置CR0.PE=1 4. 远跳转刷新CS

2.2 内存地址空间布局

x86处理器的物理地址空间并非全部映射为DRAM。芯片组和固件将地址空间划分为多个区域,用于映射内存、MMIO、固件ROM、ACPI表等。理解这个布局是BIOS开发的核心技能。

2.2.1 传统1 MB以下布局(实模式可见)

地址范围 大小 用途
0x00000000 ~ 0x000003FF 1 KB 中断向量表(IVT)
0x00000400 ~ 0x000004FF 256 B BIOS数据区(BDA)
0x00000500 ~ 0x00007BFF 约30 KB 常规内存(可用)
0x00007C00 ~ 0x00007DFF 512 B MBR/VBR引导扇区加载地址
0x0000E000 ~ 0x0000FFFF 8 KB BIOS扩展数据区(EBDA)
0x000A0000 ~ 0x000BFFFF 128 KB VGA显存(MMIO)
0x000C0000 ~ 0x000C7FFF 32 KB VGA BIOS Option ROM
0x000E0000 ~ 0x000FFFFF 128 KB 系统BIOS ROM(影子内存)

2.2.2 4 GB以下布局(保护模式视角)

在32位保护模式下,物理地址空间0 ~ 4 GB的典型划分如下:

  • 0x00000000 ~ 0x0009FFFF:常规内存(640 KB),DOS兼容区域。
  • 0x000A0000 ~ 0x000FFFFF:传统ISA/PCI MMIO区域(384 KB),包括VGA、Option ROM、BIOS ROM。
  • 0x00100000 ~ 0x0FFFFFFF:扩展内存(约256 MB - 1 MB),可被操作系统使用。
  • 0x10000000 ~ 0xFEBFFFFF:PCI/PCIe MMIO、APIC、HPET等设备映射。
  • 0xFEC00000 ~ 0xFFFFFFFF:系统保留区域(LAPIC、IOAPIC、SPI闪存、ACPI等)。
BIOS开发要点:UEFI固件使用EFI内存映射表(Memory Map)来描述物理地址空间。在DXE阶段,内存管理器会分配可用内存、保留MMIO区域、标记ACPI NVS等。固件必须确保不将MMIO区域当作普通内存使用。

2.2.3 64位长模式地址空间

长模式下线性地址空间分为用户空间(0x0000000000000000 ~ 0x00007FFFFFFFFFFF)和内核空间(0xFFFF800000000000 ~ 0xFFFFFFFFFFFFFFFF)。物理地址空间则取决于芯片组:

  • 4 GB以下:与传统布局兼容。
  • 4 GB ~ 64 GB:扩展内存(需要PAE或长模式分页)。
  • 64 GB以上:PCIe MMIO、内存映射设备等(具体由芯片组寄存器配置)。
x86物理地址空间布局 x86物理地址空间布局(4 GB以下) 常规内存 640 KB 传统MMIO 384 KB 扩展内存(DRAM) PCI/PCIe MMIO 系统保留 0x00000000 0x000A0000 0x00100000 0x10000000 0xFEC00000 0xFFFFFFFF 注意:实际布局由芯片组内存控制器配置,不同平台可能调整各区域边界

2.3 I/O端口与MMIO

x86架构提供两种与外围设备通信的方式:I/O端口(Port I/O)和内存映射I/O(MMIO)。BIOS固件在初始化过程中大量使用这两种机制。

2.3.1 I/O端口(Port I/O)

I/O端口是x86特有的独立地址空间,使用专用指令访问:

  • 地址空间:16位地址,共65,536个端口(0x0000 ~ 0xFFFF)。
  • 访问指令:IN(读)、OUT(写)、INS/OUTS(串操作)。
  • 典型端口:
    • 0x0060 ~ 0x0064:键盘控制器(8042/EC)
    • 0x00A0 ~ 0x00A1:从片PIC(8259A)
    • 0x00CF8 ~ 0x00CFC:PCI配置空间访问(CF8=地址,CFC=数据)
    • 0x0080:POST码输出(用于调试)
; 示例:通过I/O端口读取键盘状态
IN  AL, 0x64        ; 读取键盘控制器状态寄存器
TEST AL, 0x01       ; 检查输出缓冲区是否满
JZ   no_key         ; 如果为空,跳转
IN   AL, 0x60       ; 读取键盘扫描码

2.3.2 内存映射I/O(MMIO)

MMIO将设备寄存器映射到物理地址空间,CPU使用普通的MOV指令访问:

  • 优点:无需特殊指令,可配合缓存策略(UC/WC/WT/WB)使用。
  • 缺点:占用物理地址空间,需要页表正确配置MTRR/PAT。
  • 典型设备:
    • VGA帧缓冲:0x000A0000 ~ 0x000BFFFF
    • PCIe配置空间:通过MMIO方式(ECAM)访问,基址由芯片组寄存器设定
    • LAPIC:0xFEE00000 ~ 0xFEEFFFFF
    • IOAPIC:0xFEC00000 ~ 0xFECFFFFF
    • SPI闪存控制器:通常在0xFED00000附近
性能对比:I/O端口访问(IN/OUT)通常比MMIO慢,因为I/O指令是序列化指令,会等待所有之前的内存操作完成。MMIO则可以利用写合并(Write Combining)优化,但必须注意设备寄存器的访问顺序和副作用。

2.3.3 PCI配置空间访问

PCI/PCIe设备的配置空间是BIOS初始化的核心。x86提供两种访问方式:

方式 访问方法 地址范围 适用场景
I/O端口(CF8/CFC) 写地址到0xCF8,读写数据到0xCFC Bus 0~255, Dev 0~31, Func 0~7 传统PCI,兼容性好
MMIO(ECAM) 直接内存访问,基址由MCFG表指定 Bus 0~255, Dev 0~31, Func 0~7 PCIe,速度快,支持扩展配置空间
// C语言示例:通过CF8/CFC读取PCI配置空间
uint32_t pci_read_config(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset) {
    uint32_t address = (1UL << 31) | (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xFC);
    outl(0xCF8, address);
    return inl(0xCFC);
}
注意事项:访问PCI配置空间时,必须确保设备存在(通过Vendor ID检查),否则可能返回全F或导致系统挂起。在UEFI中,应使用Protocol(如EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL)而非直接I/O操作,以保证跨平台兼容性。

2.4 本章小结

本章覆盖了x86计算机体系结构的三个核心基础:

  • CPU模式:实模式(1 MB寻址,无保护)→ 保护模式(4 GB,段页保护)→ 长模式(64位,分页强制)。BIOS/UEFI启动路径必须依次穿越这些模式。
  • 内存地址空间:物理地址空间被划分为常规内存、传统MMIO、扩展内存、PCI MMIO和系统保留区。固件必须正确管理这些区域,避免冲突。
  • I/O机制:I/O端口(IN/OUT指令)和MMIO(内存访问)是设备通信的两种方式。PCI配置空间访问是BIOS初始化的关键操作。