资讯动态

MADT与GSI:ACPI中断控制器拓扑及全局系统中断映射深度解析

发布时间:2026/10/21 2:59:57 来源:尧图企业网站定制
系统描述表看多了你会发现真正让中断子系统转起来的核心其实就两张表一张描述中断控制器的拓扑一张描述中断信号的映射规则。今天要拆的就是前者——MADTMultiple APIC Description Table以及它牵扯出的全局系统中断GSI概念。MADT的作用简单说就是把CPU核心、本地APIC、I/O APIC以及它们之间的中断路由关系一次性交代清楚操作系统内核拿到这张表才知道往哪里写中断控制器寄存器、如何给设备分配中断号、怎么处理NMI。做固件开发的人要会解析它写内核中断子系统的人要依赖它连调试中断问题的驱动工程师也绕不开它。这篇文章适合三类人正在阅读ACPI规范但被Entry结构绕晕的初学者需要在自己的模拟器或内核里实现APIC初始化的人以及已经在实机上被中断路由问题折磨过的开发者。我会把MADT的每个字节掰开讲结合全局系统中断的映射原理把从设备触发到CPU处理的整条链路捋清楚最后再附上实机调试中常见的坑和排查套路。1. 为什么需要MADT从8259A到xAPIC/x2APIC的中断拓扑1.1 中断体系演变从固定IRQ到灵活路由早期PC使用8259A可编程中断控制器只有16条IRQ线IRQ0固定给定时器、IRQ1给键盘设备少、中断数量少路由关系基本是硬编码。到了多核CPU时代每个核心都有自己的本地APICLocal APIC外部中断不再直接连CPU引脚而是先送进I/O APIC再由I/O APIC根据重定向表项把中断消息以总线消息形式送给某个CPU核心的本地APIC。这样就引出了核心问题操作系统怎么知道系统里有多少个I/O APIC它们的基地址在哪每个I/O APIC的管脚对应什么中断号哪些中断需要特殊处理比如覆盖默认ISA映射ACPI规范给出的答案就是MADT。这张表把所有中断控制器的存在性和拓扑关系静态描述出来BIOS固件在启动时构建它操作系统在引导阶段扫描它。值得注意的是MADT不携带任何“设备中断到GSI”的动态路由表——那是另一组表如DSDT里的中断资源描述做的事MADT只负责定义“GSI空间有多宽、每个控制器管哪些GSI区间”。1.2 MADT在ACPI描述表家族中的定位ACPI系统描述表分成几类RSDT/XSDT是根表下面挂着FADT、MADT、DSDT、SSDT、SRAT等。MADT的签名是“APIC”属于静态数据表它在引导早期就会被内核读取。为什么要有独立的一张表而不是全部塞进DSDT因为DSDT是AML字节码解析成本高而MADT是纯二进制结构体数组内核可以在不做AML解释器初始化前就快速遍历。这也解释了为什么MADT的格式必须非常严格嵌套少、长度固定、靠Type字段区分子结构。我见过一些固件把MADT表放在XSDT里但表内地址还写成32位也见过实际内存里的表校验和与RSDT里勾算不一致导致内核拒用的情况。所以读取MADT之前第一件事永远是验证校验和再验证长度字段最后才允许解析Entry。1.3 全局系统中断GSI的准确含义全局系统中断是一个逻辑中断编号空间由ACPI定义用于统一描述所有中断源的中断号。GSI编号从0开始通常由各I/O APIC的管脚输入映射而成如果你有两个I/O APIC第一个覆盖GSI 0~23第二个覆盖GSI 24~47那么一个设备的GSI就是它在某个I/O APIC上的管脚号加上该控制器的基数。操作系统最终要把这个GSI映射到IRQ号Linux叫IRQWindows叫IRQL关联向量并通过I/O APIC的重定向表项配置触发模式和极性。注意区分三个概念设备中断引脚比如PCI INTx#、GSI、以及CPU本地中断向量。PCI设备通过中断路由表ACPI _PRT映射到GSIGSI到I/O APIC重定向表项是MADT的I/O APIC结构确定的而最终CPU看到的vector由本地APIC的LVT与I/O APIC的Redirection Entry共同决定。这条链路上任何一环错了中断就静默丢失或者风暴式触发。2. MADT表结构逐字段拆解2.1 表头与Flags字段含义与常见误读MADT的表头是标准ACPI表头长度36字节紧随其后是“Local Interrupt Controller Address”字段也就是本地APIC基地址。这里有个老坑如果系统支持x2APIC模式这个字段可能被忽略实际基地址由MSR读取所以内核不能死信这个字段。接着是Flags最低位PCAT_COMPAT指示系统是否兼容传统8259A PIC模式。PCAT_COMPAT标志的坑在于很多操作系统会依据这个标志决定初始阶段是否保留8259A的掩码设置但如果固件错误地清掉这个位操作系统可能直接切到APIC模式导致旧设备中断失效。谨记这个标志描述的是“当前平台是否还维持PIC兼容”不是“系统是否支持APIC”。所有支持APIC的系统都应该把此位置1除非确定完全不需要PIC。2.2 Entry类型与结构从Local APIC到Processor Local x2APIC表体是连续存放的变长记录每条记录前两个字节是Type和Length然后根据类型解释剩余数据。常见Entry类型如下Type名称长度字节关键字段0Processor Local APIC8ACPI Processor ID、APIC ID、Flags1I/O APIC8I/O APIC ID、地址、全局中断基址2Interrupt Source Override10Bus、Source IRQ、Global System Interrupt、Flags3NMI Source8Flags、Global System Interrupt4Local APIC NMI6Processor ID、Flags、LINT引脚5Local APIC Address Override1264位物理地址6Processor Local x2APIC16x2APIC ID、Flags7Local x2APIC NMI8Processor ID、Flags、LINT引脚Processor Local APIC结构的Flags位0表示该处理器可用位1表示在线。需要注意如果位0为0但位1为1这在规范里是非法的实际固件偶尔会出现这种组合稳妥做法是只要位0为0就跳过该处理器。I/O APIC结构里的全局中断基址Global Interrupt Base非常关键第一个I/O APIC的基址通常是0第二个可能是24。这个基址决定了该I/O APIC管脚对应的GSI编号范围。同一系统里不同I/O APIC的中断基址不应该重叠否则GSI冲突。Interrupt Source Override结构用于覆盖ISA IRQ到GSI的默认映射。默认规则是IRQn映射到GSIn0~15但现实中很多平台把IRQ0映射到GSI2因为定时器的8254输出被重新路由。如果MADT里没有Override记录系统只能沿用默认映射这会导致定时器中断根本摸不到正确的GSI进而系统时钟停摆。2.3 遍历Entry的正确姿势与长度校验MADT的表头Length字段给出整个表字节数。从表头结束地址开始不断读取Type和Length按Length跳过直到指针到达表末尾。任何Entry的Length小于给出的最小长度都是非法的不要试图“人性化”修复直接按损坏表处理。以下是遍历伪代码void parse_madt(struct acpi_table_madt *madt) { char *ptr (char *)madt sizeof(*madt); char *end (char *)madt madt-header.length; while (ptr end) { struct madt_entry_header *hdr (struct madt_entry_header *)ptr; if (ptr hdr-length end) break; // 越界报错 switch (hdr-type) { case 0: /* Processor Local APIC */ break; case 1: /* I/O APIC */ break; case 2: /* Interrupt Source Override */ break; // ... } ptr hdr-length; } }遍历时一定要用表头Length限制边界不能依赖某个Entry自带的指针。曾经有固件在MADT末尾塞了全零填充字节如果代码看到Type0且Length0就死循环。规范里没有这种填充遇到Length0或Length最小合法值都应该视为Fatal Error而不是尝试跳过。3. GSI映射关系与中断重映射实操3.1 从设备中断到GSI再到CPU中断向量要理解GSI首先看一条完整的PCI设备中断链路。以PCIe设备为例设备发出INTx消息或者使用MSI/MSI-X当使用INTx时PCIe根复合体把它转换为对应的中断请求信号接入某个I/O APIC的输入管脚。这个管脚号加上I/O APIC的全局中断基址得到GSI号。操作系统为设备查询ACPI的_PRT方法得到该设备槽位对应的GSI号以及触发模式、极性然后配置对应I/O APIC的重定向表项。重定向表项里写有中断向量号和目标CPU当设备触发中断时I/O APIC通过APIC总线发送一条中断消息本地APIC解析消息并投递给CPU的向量处理程序。MADT在中间环节提供了“I/O APIC对应哪个GSI范围”的信息。如果缺少I/O APIC Entry内核就不知道如何访问这个控制器设备中断自然无法送达。如果I/O APIC的基址写错比如把24写成了23那所有高位GSI都会整体偏移表现为几个设备的中断互相串扰或者根本收不到。3.2 Interrupt Source OverrideISA中断的经典修正场景ISA设备的中断在传统上固定为IRQ0~15但现代芯片组不一定把IRQ0接到I/O APIC的Pin 0上。例如很多平台把ISA IRQ0连接到I/O APIC的Pin 2这时就需要一个Interrupt Source Override Entry声明Bus0、SourceIRQ0、GlobalSystemInterrupt2。操作系统看到这条记录就会把ISA IRQ0映射到GSI2而不是默认的GSI0。另一个高频场景是IRQ1键盘控制器覆盖到GSI1或GSI9IRQ8RTC覆盖到GSI8都是芯片组的物理接线决定的。调试时如果发现PS/2键盘中断失灵优先检查MADT里有没有IRQ1的Override。有些固件为了省事不写任何Override默认映射恰好能用但一旦遇到物理接线不同的硬件就翻车。所以操作系统不能完全信任默认映射必须以Override为准没有Override时才回退到IRQnGSIn。Flags字段在Override里描述的是IRQ极性高电平/低电平/上升沿/下降沿和触发模式/边沿/。常见值是0x0ISA默认边沿触发、高电平极性和0x3PCI默认电平触发、低电平极性。如果固件把电平极性写反设备一中断就变成持续拉低导致中断风暴。我曾经遇到一块板子串口中断每秒钟触发几千次排查到最后就是Override的极性位写反了I/O APIC配置成低电平有效而设备实际是高点平有效这下算是把“中断风暴”四个字烙在脑子里了。3.3 多处理器与NMILocal APIC NMI与Local x2APIC NMIMADT里还有两类特殊中断描述NMI Source和Local APIC NMI。NMI Source描述的是外部NMI源连接到哪个I/O APIC的GSI通常用于性能监控或看门狗。Local APIC NMI则描述每个处理器使用哪个LINT引脚接收NMI以及NMI的触发模式、极性、传递模式。在SMP系统中每个CPU的本地APIC有多个LVT表项其中包括LINT0和LINT1。MADT允许为不同的处理器指定不同的LINT引脚作为NMI入口。某些固件给Processor 0配置LINT0给Processor 1配置LINT1如果操作系统只假定所有处理器都用同一个引脚就会导致部分核心收不到NMI。解析NMI Entry时要特别注意标志位里是否存在“LINT Pin”字段如果值为0xFF表示未知或可忽略那就别做强制配置。x2APIC模式下对应Entry类型为7Local x2APIC NMI其Processor ID是x2APIC ID32位。旧代码经常只解析类型4忘了类型7导致在x2APIC模式下NMI配置缺失。建议解析器统一把类型4和类型7的处理逻辑收敛到同一个内部数据结构便于上层统一消费。4. 常见问题排查与内核实现借鉴4.1 表损坏、Entry数量不一致的排查步骤MADT解析失败通常表现为内核启动时中断控制器初始化失败、或者设备中断全都不工作。按以下顺序排查用内存转储查看MADT表头验证Signature是否为“APIC”Length是否合理范围至少36字节。手工算一遍校验和从表头第一个字节到Length-1所有字节累加后取低8位应为0。如果校验失败可能是固件更新出了问题或内存被破坏。提取所有Entry统计I/O APIC的数量对照硬件手册确认数量是否一致。我曾经遇到一个开发板声称有3个I/O APIC但MADT里只有2条导致第三个控制器的中断无法路由。检查是否有Entry越过Length边界。如果某个Type的Length写错比如I/O APIC Entry长度写成12解析器会读乱后续所有Entry这类问题一般伴随启动时APIC相关log出现大量垃圾值。4.2 常见BIOS固件错误速查表症状典型根因处理建议定时器中断不工作缺少IRQ0到GSI2的Override内核需强制建立ISA IRQ0映射表或添加quirk所有SCI中断无法唤醒SCI GSI未在Override中声明或极性错误建议固件改用ACPI事件模型保证SCI路由只有偶数编号CPU能收到中断Local APIC Entry里奇数CPU APIC ID缺失或Flags非法应核对每个处理器Entry确保APIC ID唯一串口中断风暴Override的极性/触发模式与设备实际不符通过I/O APIC寄存器读出当前配置对证拔出PCIe设备后系统hang热插拔中断GSI被错误共享检查MADT是否存在重复GSI映射最隐蔽的一个问题是“GPEGeneral Purpose Event中断的GSI号没有Override”。ACPI定义了SCISystem Control Interrupt专门用于ACPI事件这个SCI本身也是GSI空间里的一个号但它不是固定IRQ通常通过FADT里的SCI中断号指定而MADT里应有对应I/O APIC和GSI分配。如果固件没写正确系统可能连电源按钮事件都响应不了。排查ACPI事件问题时要先把SCI链路从FADT、MADT两张表对上。4.3 操作系统消费MADT的典型流程一个成熟的操作系统内核在启动时大致做这些事定位RSDT/XSDT遍历找到MADT。先解析表头记录本地APIC地址或等x2APIC启用后读MSR。遍历所有Entry构建三张内部映射表CPU集合、I/O APIC集合、GSI Override集合。如果支持x2APIC再遍历类型6和7用x2APIC ID补充/覆盖之前的APIC信息。为每个I/O APIC分配irq域根据Global Interrupt Base建立irq域映射。中断源信息与设备驱动请求结合转化设备request_irq的IRQ号到具体GSI和向量配置。这个过程其实就是一个“静态拓扑解析”到“动态资源注册”的过程。解析MADT不直接产生中断号但它为后续的中断域层次结构提供基础。内核实现时要特别注意MADT的全局中断基址和I/O APIC Entry的顺序关系。有些固件并不是按GSI升序排列I/O APIC的所以不能想当然认为“第0条Entry就是Base0的APIC”必须显式读字段。4.4 调试MADT与GSI映射的实战技巧调试这类问题我建议准备三种工具能够读取原始内存表的调试器、I/O APIC寄存器直接读写工具、以及产生已知中断的测试设备。我的常规操作是先手动从物理内存导出MADT二进制再用脚本按规范解析和人眼比对然后直接读I/O APIC的Redirection Table寄存器看内核写入的值是否和设备属性吻合最后用某个可开关中断的测试设备比如一个按键GPIO观察中断计数。一个实用技巧在计算IRQ问题时写一个小脚本输入目标设备的中断资源描述_PRT或_AERT输出期望的GSI、触发模式、极性再去比对MADT Override表和I/O APIC配置。这条“链路对账”能在五分钟内定位故障层。如果配置正确但中断还是不触发则继续检查设备是否配置了MSI很多系统默认优先MSI绕过了I/O APIC路径此时MADT作用不直接。还有个小提醒解析MADT时不要默认“第一个I/O APIC负责所有低端GSI”有的平台在低端GSI里插入了一个独立的I/O APIC只服务几个特殊中断。正确做法是把所有I/O APIC的GSI区间求并集再通过中断域的查找接口精确定位。一些内核引入“irqdomain”概念后这类问题才被系统性解决。5. 从MADT到全局中断空间的扩展思考MADT描述的是“中断控制器拓扑的静态快照”它和运行时中断分配如MSI的关系需要分清MSI设备不通过I/O APIC管脚但它仍需要分配到某个GSI范围或独立的MSI向量域MADT中的I/O APIC基址范围可以帮助内核保留一段GSI空间用于MSI分配。我见过某些老内核在做MSI分配时优先占用未被I/O APIC覆盖的GSI空洞结果导致和后续热插拔的PCI设备中断冲突。合理做法是让MSI域和I/O APIC域共享一套GSI分配器保证全局唯一。另外MADT里的Local APIC Address字段在x2APIC模式下的处理也值得单独提一句。芯片组支持x2APIC后本地APIC通过MSR访问不再直接映射到内存地址MADT表里的32位基地址只作为内存映射模式的备用值。操作系统不要根据这个地址去ioremap而应该读取IA32_APIC_BASE_MSR判断是否已启用x2APIC。很多解析器实现为了兼容性保留两种模式但优先级应当是以MSR为主MADT字段为辅。最后关于表里的Local APIC ID与ACPI Processor ID之间的关系这是固件开发者容易混淆的一点。ACPI Processor ID是AML/处理器对象如处理器设备对象中使用的编号而APIC ID是硬件中断控制器看到的物理ID两者通过MADT条目绑定。操作系统需要将ACPI定义的处理器对象比如OSPM处理器热插拔关联到具体APIC ID所以这个绑定关系影响热插拔场景。如果MADT里同一个ACPI Processor ID出现两次内核必须告警。6. 写在实操之后的一点总结把MADT和GSI彻底搞明白之后再看那些“中断怎么配都不对”的问题基本都有了下手点。我个人的习惯是新平台Bringup时第一件事就是先dump MADT把它里的I/O APIC数量、GSI基址、所有Override条目列成清单和芯片组手册一张张对。这一步虽然枯燥但能省掉后面三天调试时间。很多开发者喜欢直接看DSDT里的中断资源但那是动态映射出错时原因往往在更底层的MADT上。所以我的建议是先把MADT这张“硬件地图”核对完成再往上查ACPI方法最后才怀疑驱动代码。中断调的多了就会发现大多数玄学问题其实都是表里某个bit的极性写错了或者某个GSI区间重叠了。希望这篇拆解能让你下次看到“APIC Error”日志时不再一头雾水。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑