4. I/O APIC 架构:寄存器基址与 IOREDTBL 重定向表项

I/O APIC 是 x86 系统中负责收集外部硬件中断(如 PCI 设备、定时器、键盘等)并将其以消息形式发送给 Local APIC 的关键组件。本章节深入剖析 I/O APIC 的寄存器基址映射机制以及核心数据结构——I/O 重定向表项(IOREDTBL)。

4.1 I/O APIC 寄存器基址与访问方式

I/O APIC 通过一组 内存映射寄存器(Memory-Mapped I/O, MMIO) 与 CPU 通信。系统软件通过读写这些寄存器来配置中断路由、屏蔽中断以及查询状态。

I/O APIC 的寄存器基址由系统芯片组(如 Intel PCH 或 AMD FCH)在复位后默认分配,通常位于 0xFEC00000(对于第一个 I/O APIC)。每个寄存器占用 4 字节对齐的地址空间,但为了兼容旧版 APIC 的索引/数据访问模型,I/O APIC 实际只暴露两个关键的 MMIO 窗口:索引寄存器(Index Register) 和 数据寄存器(Data Register)。

关键概念: I/O APIC 的寄存器不是直接通过独立地址访问的,而是通过“索引-数据”对进行间接访问。软件先向索引寄存器写入目标寄存器的编号,然后通过数据寄存器读取或写入该寄存器的内容。
I/O APIC 核心 MMIO 寄存器窗口
偏移地址(基址 +) 寄存器名称 宽度 描述
0x00 IOREGSEL(索引寄存器) 32 位 写入要访问的寄存器编号(8 位有效,高 24 位保留)
0x10 IOWIN(数据寄存器) 32 位 对 IOREGSEL 指定的寄存器进行读写操作
0x20 EOI(中断结束寄存器) 32 位 写入 0 表示当前中断处理结束(仅用于 Level Trigger 模式)
编程提示: 访问 I/O APIC 寄存器时,必须确保 CPU 的 MTRR(Memory Type Range Registers)将 0xFEC00000 区域设置为 Uncacheable (UC) 或 Write-Combining (WC),以避免因缓存一致性导致的中断丢失或配置错误。

以下是一个典型的 I/O APIC 寄存器访问代码示例(C 语言风格):

// 定义 I/O APIC 基址(假设为 0xFEC00000)
#define IOAPIC_BASE         0xFEC00000
#define IOAPIC_IOREGSEL     (IOAPIC_BASE + 0x00)
#define IOAPIC_IOWIN        (IOAPIC_BASE + 0x10)

// 读取 I/O APIC 寄存器的函数
uint32_t ioapic_read(uint8_t reg_index) {
    volatile uint32_t *regsel = (volatile uint32_t *)IOAPIC_IOREGSEL;
    volatile uint32_t *data   = (volatile uint32_t *)IOAPIC_IOWIN;
    *regsel = reg_index;          // 选择寄存器
    return *data;                 // 读取数据
}

// 写入 I/O APIC 寄存器的函数
void ioapic_write(uint8_t reg_index, uint32_t value) {
    volatile uint32_t *regsel = (volatile uint32_t *)IOAPIC_IOREGSEL;
    volatile uint32_t *data   = (volatile uint32_t *)IOAPIC_IOWIN;
    *regsel = reg_index;          // 选择寄存器
    *data   = value;              // 写入数据
}

4.2 I/O APIC 内部寄存器布局

除了上述 MMIO 窗口,I/O APIC 内部还包含一组通过索引访问的配置寄存器。最重要的两个是:

  • IOAPICID(索引 0x00): 标识 I/O APIC 的 ID,用于多 I/O APIC 系统中的仲裁。
  • IOAPICVER(索引 0x01): 版本寄存器,高 8 位表示最大重定向表项数量(Max Redirection Entry,实际数量为 Max Redir Entry + 1)。
  • IOAPICARB(索引 0x02): 仲裁优先级寄存器(仅用于兼容模式)。
  • IOREDTBL[0] ~ IOREDTBL[n](索引 0x10 ~ 0x10 + 2*n): 重定向表项,每个表项占用两个 32 位寄存器(低 32 位 + 高 32 位)。
注意: IOREDTBL 表项的索引是连续的。例如,对于第 0 个中断输入(INTIN0),其低 32 位寄存器索引为 0x10,高 32 位寄存器索引为 0x11;第 1 个中断输入(INTIN1)对应索引 0x12 和 0x13,以此类推。

4.3 IOREDTBL 重定向表项结构详解

IOREDTBL(I/O Redirection Table)是 I/O APIC 的核心数据结构。每个表项对应一个外部中断输入引脚(INTINx),定义了该中断如何被路由到目标 CPU 以及如何被触发。每个 IOREDTBL 表项共 64 位,分为低 32 位(Lower DWord)和高 32 位(Upper DWord)。

IOREDTBL 表项位字段定义(64 位)
位范围 字段名称 描述
63:56 Destination Field 目标 CPU 的 APIC ID(在物理模式下)或 Logical Destination(在逻辑模式下)
55:17 Reserved 保留位,必须写 0
16 Mask 中断屏蔽位:1 = 屏蔽该中断,0 = 允许中断
15 Trigger Mode 触发模式:0 = Edge(边沿触发),1 = Level(电平触发)
14 Remote IRR 远程中断请求寄存器(只读):1 = 正在服务该 Level 中断,0 = 空闲
13 Interrupt Input Pin Polarity 中断输入引脚极性:0 = 高电平有效/上升沿,1 = 低电平有效/下降沿
12 Delivery Status 投递状态(只读):0 = 空闲,1 = 正在发送中断消息
11 Destination Mode 目标模式:0 = Physical(物理),1 = Logical(逻辑)
10:8 Delivery Mode 投递模式:000 = Fixed,001 = Lowest Priority,010 = SMI,100 = NMI,101 = INIT,111 = ExtINT
7:0 Interrupt Vector 中断向量号(0 ~ 255),必须为 16 字节对齐(即低 4 位为 0)
配置要点: 对于大多数 PCI 设备中断,推荐配置为:Delivery Mode = Fixed (000),Trigger Mode = Level (1),Polarity = Active Low (1),Destination Mode = Physical (0),并指定一个唯一的 Vector 和 Destination Field。

4.4 IOREDTBL 中断路由流程示意图

下图展示了从外部设备产生中断信号到 I/O APIC 根据 IOREDTBL 配置生成中断消息的完整流程:

I/O APIC 中断路由流程 I/O APIC 中断路由流程(基于 IOREDTBL) 外部设备 (PCI 设备/定时器) INTINx 引脚 I/O APIC IOREDTBL 查找 (索引 = INTINx) IOREDTBL 表项 Vector / Dest / Mode 读取配置 中断消息 (APIC 总线消息) 生成消息 目标 CPU (Local APIC) 系统总线 说明: 1. 外部设备通过 INTINx 引脚向 I/O APIC 发送中断信号。 2. I/O APIC 根据引脚编号索引 IOREDTBL,读取 Vector、Delivery Mode、Destination 等字段。 3. 根据配置生成中断消息,通过系统总线发送到目标 CPU 的 Local APIC。

4.5 IOREDTBL 配置实例:路由 PCI 设备中断

假设我们需要将连接到 I/O APIC INTIN16 引脚的 PCI 设备中断路由到 CPU #0(APIC ID = 0),使用 Fixed 投递模式、Level 触发、Active Low 极性,中断向量号为 0x50。对应的 IOREDTBL 配置如下:

// 假设 I/O APIC 基址为 0xFEC00000
// INTIN16 对应的 IOREDTBL 索引:低 32 位 = 0x10 + 16*2 = 0x30,高 32 位 = 0x31

// 配置低 32 位(IOREDTBL[16].low)
// Bit 7:0   = Vector = 0x50
// Bit 10:8  = Delivery Mode = 000 (Fixed)
// Bit 11    = Destination Mode = 0 (Physical)
// Bit 13    = Polarity = 1 (Active Low)
// Bit 15    = Trigger Mode = 1 (Level)
// Bit 16    = Mask = 0 (Unmasked)
uint32_t low_dword = (0x50) |          // Vector
                     (0x0 << 8) |      // Delivery Mode = Fixed
                     (0x0 << 11) |     // Destination Mode = Physical
                     (0x1 << 13) |     // Polarity = Active Low
                     (0x1 << 15) |     // Trigger Mode = Level
                     (0x0 << 16);      // Mask = 0

// 配置高 32 位(IOREDTBL[16].high)
// Bit 63:56 = Destination Field = 0x00 (APIC ID 0)
uint32_t high_dword = (0x00 << 24);    // Destination = CPU #0

// 写入 I/O APIC
ioapic_write(0x30, low_dword);
ioapic_write(0x31, high_dword);
调试建议: 在配置 IOREDTBL 后,建议读取 IOAPICVER 寄存器确认最大重定向表项数量,确保配置的 INTIN 引脚号不超过硬件支持的范围。同时,检查 Delivery Status 位(Bit 12)以确保中断消息已成功投递。

4.6 多 I/O APIC 与中断路由扩展

现代 x86 平台可能包含多个 I/O APIC 实例(例如,PCH 内部集成一个,独立 PCIe 桥片可能包含另一个)。每个 I/O APIC 拥有独立的 MMIO 基址和 IOREDTBL 表。系统软件通过 ACPI 表(如 MADT)枚举所有 I/O APIC 及其基址。

多 I/O APIC 系统典型布局
I/O APIC 实例 典型 MMIO 基址 中断输入范围 用途
I/O APIC #0 0xFEC00000 INTIN0 ~ INTIN23 PCH 内部设备、传统 ISA 中断
I/O APIC #1 0xFEC01000 INTIN0 ~ INTIN15 PCIe 桥片扩展中断
I/O APIC #2 0xFEC02000 INTIN0 ~ INTIN7 专用设备(如 IOMMU)

在配置多 I/O APIC 系统时,必须确保每个中断源的 IOREDTBL 表项中的 Destination Field 指向正确的目标 CPU,并且 Vector 在整个系统中唯一,以避免中断冲突。