3. Local APIC 架构:寄存器布局、版本寄存器与 LVT 条目

Local APIC(本地高级可编程中断控制器)是每个 CPU 核心内部的中断控制单元,负责接收来自 I/O APIC、IPI(处理器间中断)以及本地源(如 LINT0/LINT1、性能监控计数器等)的中断信号,并将其递交给 CPU 核心。理解 Local APIC 的内部寄存器布局、版本寄存器以及 LVT(Local Vector Table)条目,是配置中断路由的基础。

3.1 Local APIC 内部寄存器布局

Local APIC 的寄存器映射到物理地址空间中的一个 4KB 页面,基地址默认为 0xFEE00000(可通过 MSR 0x1B 修改)。所有寄存器均为 32 位或 64 位宽,访问时必须使用 16 字节对齐的地址。下表列出了关键寄存器的偏移量与功能:

偏移量 寄存器名称 功能描述
0x000 APIC ID 寄存器 标识当前 Local APIC(即 CPU 核心)的唯一 ID
0x020 APIC 版本寄存器 指示 Local APIC 版本、最大 LVT 条目数
0x030 任务优先级寄存器 (TPR) 设置当前 CPU 接受中断的优先级阈值
0x080 仲裁优先级寄存器 (APR) 反映当前正在处理的中断优先级
0x0D0 处理器优先级寄存器 (PPR) 结合 TPR 和 APR 计算出的实际优先级
0x0E0 EOI 寄存器 中断处理结束时写入,通知 Local APIC 中断完成
0x0F0 远程读取寄存器 用于其他 CPU 读取本 Local APIC 的寄存器
0x100–0x170 LVT 条目 (0–7) 配置本地中断源(定时器、LINT0、LINT1、错误等)
0x180–0x1E0 LVT 条目 (8–15) 扩展 LVT 条目(如性能计数器、温度传感器等)
0x200–0x2E0 ICR 寄存器(中断命令寄存器) 发送 IPI(处理器间中断)
0x300–0x3E0 定时器寄存器(初始计数、当前计数、分频配置) 配置 Local APIC 定时器
关键提示: 在 x86 启动早期(实模式或保护模式初期),必须确保 Local APIC 的基地址映射正确且未被禁用(MSR 0x1B 的 bit 11 为 0 表示启用)。如果 Local APIC 被禁用,所有中断将无法到达 CPU 核心。

下图展示了 Local APIC 内部寄存器在 4KB 页面中的布局结构:

Local APIC 寄存器布局结构图 Local APIC 寄存器布局(基址 0xFEE00000) 4KB 页面 (0x000 – 0xFFF) 0x000 APIC ID 0x020 版本寄存器 0x030 TPR 0x080 APR 0x0E0 EOI 0x100–0x170 LVT 条目 0–7 (定时器、LINT0/1、错误等) 0x180–0x1E0 扩展 LVT 条目 0x200–0x2E0 ICR 0x300–0x3E0 定时器寄存器 (初始/当前计数、分频) 说明 所有寄存器 16 字节对齐 32/64 位宽 MMIO 访问

3.2 版本寄存器

版本寄存器位于偏移量 0x020,是一个 32 位只读寄存器,提供 Local APIC 的硬件版本信息和最大 LVT 条目数量。其位字段定义如下:

位域 宽度 名称 描述
31:24 8 位 保留 应忽略
23:16 8 位 Max LVT Entry 最大 LVT 条目索引(0–15),指示 LVT 表中可用的条目数量
15:8 8 位 保留 应忽略
7:0 8 位 版本 ID Local APIC 版本号:
0x11 = 集成 APIC(P6 及之后)
0x14 = x2APIC 模式
版本 ID 解读: 在启动固件中,读取版本寄存器是判断 Local APIC 是否支持扩展 LVT 条目(如性能计数器、温度传感器)的关键步骤。如果版本 ID 为 0x11,则最大 LVT 条目通常为 5 或 6;如果为 0x14,则最大 LVT 条目可达 15。

读取版本寄存器的示例代码(C 语言风格,MMIO 访问):

// 假设 Local APIC 基地址已映射到虚拟地址 apic_base
volatile uint32_t *apic_version_reg = (volatile uint32_t *)(apic_base + 0x020);
uint32_t version_value = *apic_version_reg;

uint8_t version_id = version_value & 0xFF;
uint8_t max_lvt_entry = (version_value >> 16) & 0xFF;

printf("Local APIC Version ID: 0x%02X\n", version_id);
printf("Max LVT Entry Index: %d\n", max_lvt_entry);

3.3 LVT 条目

LVT(Local Vector Table)是 Local APIC 中一组用于配置本地中断源的寄存器。每个 LVT 条目是一个 32 位寄存器,控制一个特定的中断源(如 APIC 定时器、LINT0、LINT1、错误中断等)。LVT 条目的通用格式如下:

位域 宽度 名称 描述
31:18 14 位 保留 应写 0
17 1 位 Delivery Status 0 = 空闲,1 = 正在发送(只读)
16 1 位 Interrupt Input Pin Polarity 0 = 高电平有效,1 = 低电平有效(仅 LINT0/LINT1)
15 1 位 Remote IRR 0 = 未接收,1 = 已接收等待 EOI(只读,仅 LINT0/LINT1)
14 1 位 Trigger Mode 0 = 边沿触发,1 = 电平触发(仅 LINT0/LINT1)
13 1 位 Mask 1 = 屏蔽该中断源
12 1 位 Timer Mode 0 = 一次性,1 = 周期性(仅定时器 LVT)
11:8 4 位 Delivery Mode 0000 = Fixed
0010 = SMI
0100 = NMI
0111 = ExtINT
7:0 8 位 Vector 中断向量号(0–255),必须 ≥ 16(0x10)
注意: 对于 LINT0 和 LINT1 引脚,如果系统使用 I/O APIC 作为外部中断源,通常将 LINT0 配置为 ExtINT 模式(Delivery Mode = 0111),将 LINT1 配置为 NMI 模式(Delivery Mode = 0100)。错误 LVT 条目用于捕获 Local APIC 内部错误(如校验错误),建议在启动时启用并分配一个有效向量。

下表列出了标准 LVT 条目的偏移量和对应的中断源:

LVT 索引 偏移量 中断源 典型用途
0 0x100 APIC 定时器 本地定时中断
1 0x110 LINT0 外部中断(通常连接 8259A PIC)
2 0x120 LINT1 NMI 中断
3 0x130 错误中断 Local APIC 内部错误报告
4 0x140 性能监控计数器 性能监控中断(可选)
5 0x150 温度传感器 温度阈值中断(可选)
6–15 0x160–0x1E0 扩展 LVT 处理器特定用途

配置 LVT 条目的示例代码(以 APIC 定时器为例):

// 配置 APIC 定时器 LVT 条目(偏移 0x100)
volatile uint32_t *lvt_timer = (volatile uint32_t *)(apic_base + 0x100);
uint32_t lvt_value = 0;

// 设置向量号 0x30(48),Fixed 模式,非屏蔽,一次性
lvt_value |= (0x30 & 0xFF);          // Vector = 0x30
lvt_value |= (0x0 & 0x700);          // Delivery Mode = Fixed (0000)
lvt_value &= ~(1 << 13);             // Mask = 0 (启用)
lvt_value &= ~(1 << 12);             // Timer Mode = 0 (一次性)

*lvt_timer = lvt_value;

// 设置定时器初始计数(偏移 0x380)
volatile uint32_t *timer_init_count = (volatile uint32_t *)(apic_base + 0x380);
*timer_init_count = 1000000;         // 初始计数值

// 设置分频配置(偏移 0x3E0),分频系数 16
volatile uint32_t *timer_divide = (volatile uint32_t *)(apic_base + 0x3E0);
*timer_divide = 0x3;                 // 分频 16 (0x3 = 16)

下图展示了 LVT 条目与中断源之间的映射关系:

LVT 条目与中断源映射 LVT 条目与中断源映射 APIC 定时器 LINT0 (外部中断) LINT1 (NMI) 错误中断 扩展 LVT (可选) LVT 0 (0x100) LVT 1 (0x110) LVT 2 (0x120) LVT 3 (0x130) LVT 4–15 (0x140–0x1E0) CPU 核心 中断递送 (通过 INTR/NMI 引脚 或 x2APIC 总线)

3.4 LVT 配置注意事项

  • 向量号范围: 所有 LVT 条目中的 Vector 字段必须 ≥ 16(0x10),因为 0–15 被 Intel 保留用于异常和 NMI。使用非法向量号会导致中断被忽略或系统异常。
  • 屏蔽与启用: 在初始化阶段,建议先屏蔽所有 LVT 条目(设置 Mask=1),逐个配置完成后,再清除 Mask 位启用。这可以防止未初始化的中断源意外触发。
  • Delivery Mode 选择: 对于 LINT0,如果系统使用 I/O APIC 作为主中断控制器,应配置为 ExtINT 模式(0x7),以兼容 8259A PIC 的级联。对于 LINT1,通常配置为 NMI 模式(0x4),用于接收硬件 NMI 信号。
  • 定时器模式: APIC 定时器支持一次性(One-shot)和周期性(Periodic)模式。在启动阶段,通常使用一次性模式进行延迟测量,进入操作系统后再切换为周期性模式用于时间片调度。
固件实践: 在 UEFI 或 BIOS 启动固件中,Local APIC 的初始化通常在进入保护模式或长模式后立即执行。建议按以下顺序操作:
1. 读取版本寄存器,确认 Local APIC 存在且版本正确。
2. 屏蔽所有 LVT 条目。
3. 配置 LINT0 为 ExtINT,LINT1 为 NMI,错误 LVT 分配有效向量。
4. 设置 TPR 为 0(允许所有优先级的中断)。
5. 写入 EOI 寄存器清除任何残留中断状态。