MSI 与 IOAPIC LAPIC 如何协作,操作系统如何初始化和使用他们
1. 核心组件角色回顾
I/O APIC (I/O Advanced Programmable Interrupt Controller):
角色: 系统全局中断控制器。负责收集来自外部设备(如PCI设备、USB控制器等)的传统引脚中断(PIN-based interrupts),并将其转换为消息。
位置: 通常位于芯片组(Chipset)中。
特点: 有多个中断输入引脚(如24个),每个引脚可以连接一个中断源。它通过系统总线向Local APIC发送中断消息。
Local APIC (Local Advanced Programmable Interrupt Controller):
角色: 每个CPU核心私有的中断控制器。负责接收来自I/O APIC的中断消息、处理来自其他CPU核心的处理器间中断(IPI)、以及处理本地中断(如定时器中断、性能计数器中断)。
位置: 集成在每个CPU核心内部。
特点: 每个Local APIC都有一个唯一的ID(APIC ID)。它直接向所属的CPU核心交付中断。
MSI (Message Signaled Interrupts):
角色: 一种中断机制,允许设备绕过I/O APIC的物理引脚,直接通过向特定内存地址执行写入操作来向Local APIC发送中断。
本质: 一次特殊的内存写事务(PCI Memory Write transaction),其目标地址和写入数据具有特定格式,CPU和芯片组能识别出这不是普通的内存写入,而是一个中断信号。
2. 三者的协作关系
下图清晰地展示了传统引脚中断与MSI中断两条路径的并行协作流程,以及I/O APIC和Local APIC在其中的不同角色:
关键协作点:
MSI“模拟”了I/O APIC的行为:OS为MSI设备设置的 Message Address 实际上包含了目标Local APIC的地址信息,而 Message Data 则包含了中断向量等信息。这使得设备产生的内存写事务,看起来就像是I/O APIC发送出来的中断消息,因此可以直接被目标Local APIC接收和处理。
I/O APIC处理传统设备,MSI处理现代设备:两者是并行工作的。I/O APIC负责处理那些只有传统引脚中断的老设备,而支持MSI的新设备则直接“空降”到Local APIC。
Local APIC是共同的终点:无论中断来自I/O APIC转发,还是来自设备直接的MSI写入,最终都由目标CPU的Local APIC统一接收、管理和交付给CPU核心。Local APIC是中断的“集散中心”。
3. 操作系统的初始化和使用流程
阶段一:系统启动早期 - 探测和初始化控制器
ACPI表解析:
OS内核启动时,会解析ACPI表(特别是MADT表)。
从表中获知系统中存在几个I/O APIC、它们的物理地址、以及每个I/O APIC的GSI(Global System Interrupt)中断输入引脚范围。
获知系统中所有Local APIC的ID(即对应的CPU核心)。
映射I/O APIC地址:
OS将I/O APIC的物理地址映射到内核的虚拟地址空间,以便后续对其进行编程配置。
初始化Local APIC:
OS在每个CPU核心上执行初始化代码,设置其Local APIC。
启用Local APIC(设置SPIV寄存器)。
设置Local APIC的定时器、LVT(Local Vector Table)等。
阶段二:中断路由设置 - 为传统中断做准备
构建中断路由表:
OS解析ACPI表中的PCI中断路由信息,得知“哪个PCI设备的哪个引脚(INTA#)连接到哪个I/O APIC的哪个输入引脚”。
OS为每个可能的I/O APIC输入引脚(即每个GSI)分配一个内核管理的虚拟中断号(virq)。
配置I/O APIC重定向表(Redirection Table):
对于每个被使用的I/O APIC输入引脚,OS需要配置其对应的重定向表条目。
此时,OS可能还不知道具体哪个设备会用这个引脚,所以它先填充一个占位的中断向量(Vector)。
配置内容包括:
Vector: 一个临时或通用的向量号。
Delivery Mode: 通常为Fixed。
Destination Field: 指定中断发送到哪个或哪些CPU核心(物理目标或逻辑目标模式)。
阶段三:设备枚举和中断分配 - 动态分配
这是最关键的阶段,OS为每个设备选择最佳的中断方式。
PCI设备枚举:
OS发现一个PCI设备,读取其配置空间。
读取 Interrupt Pin 寄存器(例如,值为1表示使用INTA#)。
检查是否支持 MSI 或 MSI-X Capability。
优先尝试启用MSI/MSI-X:
如果设备支持MSI:
OS根据设备请求的中断数量(多个队列可能需要多个向量),分配等量的空闲CPU中断向量(Vector)。
OS为每个中断向量构造一对 Message Address 和 Message Data。
Message Address: 编码了目标CPU的Local APIC的地址(通常是所有Local APIC的基地址,如 0xFEE00000),并通过Destination ID字段指定具体核心。
Message Data: 低8位就是分配的中断向量号(Vector)。其他位 delivery mode等。
OS将这些值写入设备的MSI Capability寄存器。
OS禁用设备的传统引脚中断(设置配置空间中的命令寄存器位)。
至此,该设备的中断路径被设置为MSI,完全绕过I/O APIC。
回退到传统中断:
如果设备不支持MSI或MSI启用失败:
OS查询在阶段二构建的中断路由表,找到该设备引脚(INTA#)对应的I/O APIC引脚和GSI。
OS获取该GSI对应的内核 virq。
设备驱动程序调用 request_irq(virq, handler) 申请中断。
OS此时才会为这个特定的 virq 分配一个真正的中断向量(Vector),并更新之前在I/O APIC重定向表中的配置,将占位的Vector替换为这个真正的Vector。
同时,OS会配置Local APIC,确保它能接收这个向量对应的中断。
阶段四:运行时中断处理
MSI路径: 设备产生中断 -> 直接发起内存写事务 -> 目标CPU的Local APIC接收 -> 根据 Message Data 中的Vector触发中断 -> CPU执行对应的ISR。
传统路径: 设备拉高中断引脚 -> I/O APIC检测到电平变化 -> I/O APIC根据配置的重定向表,构造中断消息并发送 -> 目标Local APIC接收 -> 根据消息中的Vector触发中断 -> CPU执行对应的ISR。
总结
操作系统通过ACPI表了解硬件布局,初始化I/O APIC和Local APIC作为基础设施。对于每个设备,OS优先选择MSI路径,通过编程设备自身的MSI寄存器,使其能够直接与Local APIC“对话”。如果MSI不可用,则回退到使用I/O APIC作为中转站的传统路径。