RISC-V 64 启动卡死排查技术调研:PMP、DDR 物理地址、虚拟地址与 AIA/APLIC 机制
| 字段 | 内容 |
|---|---|
| 文档类型 | 技术调研报告 |
| 版本 | v1.0 |
| 状态 | 公开资料调研(未实测) |
| 密级 | 公开资料 |
| 作者 | Qoder |
| 生成日期 | 2026-10-09 |
| 研究截点 | 2026-10-09 |
| 目标读者 | 正在排查 RISC-V 64 平台"内核启动卡死"的固件与内核工程师;负责 SoC 地址空间与中断控制器配置的 bringup 工程师 |
| 调研问题 | PMP 如何授权物理内存访问;DDR 物理地址与内核加载地址从何而来;satp 与 Sv39/Sv48 虚拟地址如何配置;AIA/APLIC/IMSIC 如何工作;这些机制中哪一环配置错误会造成"内核启动卡死",以及如何逐层定位 |
| 覆盖范围 | RISC-V 特权架构规范(机器级 PMP、监管级 satp/Sv39/Sv48、M 态到 S 态交接)、AIA v1.0 规范(APLIC 与 IMSIC)、OpenSBI 与 Linux 内核对上述机制的实现、QEMU virt 参考平台地址图与调试手段 |
| 排除范围 | 具体 SoC 的 DDR 控制器寄存器级配置手册、性能与功耗实测、与其他体系结构的全景对比、芯片内部微架构实现 |
版本历史
| 版本 | 日期 | 变更说明 |
|---|---|---|
| v1.0 | 2026-10-09 | 首次发布:PMP 授权模型、DDR 物理地址与加载约定、Sv39/Sv48 虚拟地址机制、AIA/APLIC/IMSIC 中断投递路径,以及"启动卡死"症状漏斗与逐层验证步骤 |
术语与缩略语
| 术语/缩略语 | 含义 | 本文口径 |
|---|---|---|
| PMP | Physical Memory Protection,RISC-V 机器级物理内存保护 | 以 pmpcfg/pmpaddr 表项为 S 态与 U 态划定可访问物理区间 |
| TOR / NA4 / NAPOT | PMP 三种地址匹配模式:顶部地址匹配 / 4 字节自然对齐匹配 / 自然对齐区间匹配 | NAPOT 最常用,最小区间粒度 8 字节 |
| MMIO | 内存映射 I/O:UART、CLINT、APLIC、IMSIC 等以物理地址访问的设备寄存器 | 必须同时落在页表映射与 PMP 授权内 |
| DDR / DRAM | 主内存;控制器初始化完成后占据固定的物理区间 | 本报告用"物理区间(base + size)"描述其坐标 |
| satp | 监管级地址翻译与保护 CSR,MODE 字段决定 Bare/Sv39/Sv48 | 写入生效后需 sfence.vma 清理 TLB 残留 |
| Sv39 / Sv48 | 39 位 / 48 位虚拟地址的三级与四级页表翻译方案 | RISC-V 64 主流选择,按 WARL 语义裁定 |
| VA / PA | 虚拟地址 / 物理地址 | 完整判定链:VA —页表→ PA —PMP→ 物理访问 |
| PTE | 页表项(叶节点或非叶节点) | 含 V/R/W/X/U/G/A/D 位与 PPN 字段 |
| AIA | RISC-V Advanced Interrupt Architecture v1.0 | 含 Smaia/Ssaia 间接 CSR 面、APLIC、IMSIC |
| APLIC | Advanced Platform-Level Interrupt Controller,平台级中断控制器 | 负责"源控制":direct 与 MSI 两种投递模式 |
| IMSIC | Incoming MSI Controller,每 hart 的中断文件集合 | 每 hart 含 M 文件与 S 文件,各 4KB 内存映射 |
| MSI | Message Signaled Interrupt,以写内存完成中断投递 | MSI 编号即"中断身份" |
| FDT / DTB | 平坦设备树及其二进制形式 | 由 a1 寄存器传给内核;/memory 与中断节点均由其描述 |
| SBI / OpenSBI | Supervisor Binary Interface 与其参考实现 | M 态固件为 S 态提供服务并执行 PMP 配置 |
| earlycon | 内核早期控制台,在正式串口驱动加载前即可输出 | 排查"早期静默卡死"的首选输出通道 |
| scause / stval / sepc | S 态 trap 原因 / 伴随值 / 触发指令地址 | 12/13/15 分别为取指/读/写 page fault |
可靠性标记
| 标记 | 含义 | 使用规则 |
|---|---|---|
| 【C】 | 已被一手或多源证据支持的结论 | 必须附来源编号 |
| 【C*】 | 条件成立时的结论 | 写明条件与边界,并附来源编号 |
| 【S】 | 情景判断 | 说明触发信号与来源 |
| 【P】 | 建议或优先级 | 关联验收条件 |
| 【?】 | 待验证的不确定性 | 写明验证路径 |
1. 核心判断(TL;DR)
- 【C】RISC-V 64 的每次访存都经过同一条判定漏斗:虚拟地址先经页表翻译为物理地址,物理地址再经 PMP 授权才到达总线;启动早期 MMU 尚未开启时 PMP 是唯一权限闸门——因此"地址空间没配好"导致的卡死可以精确分为"页表层(VA)"与"物理层(PA/PMP)"两类,用串口有无输出即可初判方向。[S01][S02][S03]
- 【C】DDR 物理区间、内核加载地址与设备树 /memory 节点三者必须一致:S 态侧最早只认设备树给出的物理内存描述,PMP 侧必须覆盖内核镜像、FDT 与 initrd 所在全部物理区间;任一环节缺失或错位,症状都是内核早期静默挂死、串口无任何输出。[S08][S16]
- 【C*】虚拟地址层最隐蔽的失败模式是 WARL 语义被硬件静默忽略:平台不支持 satp.MODE 的某档(如写 Sv48 而硬件只支持 Sv39)时写入被丢弃、satp 保持原值,内核以为已开启分页而实际仍在裸机取指;此外 VA 必须符号扩展(bit63:39 等于 bit38),不规范的 VA 直接被判 fault。条件:对所有 RISC-V 64 实现成立。[S02][S09]
- 【C】AIA 把"中断源控制"与"每 hart 投递"拆成 APLIC 与 IMSIC 两级:有线中断经 APLIC(direct 或 MSI 模式)投递为对目标 hart 中断文件的 MSI 写,经 eidelivery/eithreshold 过滤后立起 mip 位;目标 hart、eiid、MSI 目标地址、eidelivery 任一配置错误都表现为"中断永不抵达",典型症状是定时器停摆、卡在等待根文件系统;一个易踩的坑是带 IMSIC 的 hart 上 direct 模式免直连支持则等效于关闭使能。[S04][S05][S06][S12]
- 【P】排查应按症状漏斗(见图 3)自上而下推进:先以"串口是否有输出"分流到物理地址/PMP 层或 MMIO/中断层,再以 JTAG/QEMU 读取 satp、mstatus、pmpcfg、mcause、scause 与对照映射表完成每层验收;每一步都给出可复现的读取或对照动作,而不是猜测。[S12][S13][S17]
2. 问题、范围与方法
本报告的触发问题是:一颗 RISC-V 64 CPU 在启动内核阶段卡死,怀疑地址空间配置错误,需要把"PMP、DDR 物理地址、虚拟地址、AIA/APLIC"这一组机制整理成可用于定位的完整知识。因此本报告的目标不是写一本体系结构教程,而是围绕"卡死"这一症状,把从复位到内核早期启动过程中所有与地址相关的机制按"谁在什么时候配置了什么、配错后表现出什么"的线索组织起来。
方法上采用三线交叉:其一,规范线(RISC-V 特权架构规范与 AIA 规范),用于确立机制的语义与名称;其二,实现线(OpenSBI 与 Linux 内核),用于确立真实系统里各机制的配置时序与默认值;其三,参考平台线(QEMU virt 与设备树绑定),用于给出可复现的地址图与观测点。所有机制结论都以规范或源码为准;观测与排查建议若属工程经验,均以【P】标记。研究限制与未核验项在第 7 节集中说明。
阅读路径建议:只关心排查的读者可先读第 1 节与第 4.4 节,再按图 1 的管线回查第 4.1 至 4.3 节中对应的机制小节;关心机制细节的读者按顺序阅读第 3、4 节。
3. 技术基线
先给出一幅完整的地址全景。RISC-V 64 平台从上电到内核运行的地址相关机制分四层:第一层是物理地址本身——DDR 控制器初始化后,主内存在物理地址空间中占据一段固定区间(例如从某个固定 base 起),设备寄存器(UART、CLINT、PLIC、APLIC、IMSIC、PCIe ECAM 等)占据另外的 MMIO 区间,这组坐标由 SoC 设计固定,软件不能移动,只能"发现并声明";声明的主要渠道是设备树:/memory 节点给出内核可管理的主内存区间,各外设节点给出 MMIO 区间。[S02][S16]
第二层是权限——PMP 是 RISC-V 机器级物理内存保护机制,它不翻译地址,只裁定"发出访问的特权级能不能碰这段物理区间"。PMP 由一组表项构成(规范允许实现 0、16 或 64 个条目、且从 0 号起连续实现;RV64 下仅偶数编号的 pmpcfg0/2/…/14 物理存在,每个配置字节装 8 个条目的权限,奇数编号非法;OpenSBI 以"向 pmpaddr0 写全 1 再回读"的方式探测允许的地址位与粒度,并在启动时打印 PMP Count / Granularity / Address Bits 三项供对照),每个表项包含 8 位配置(R/W/X/A/L)与一个地址寄存器(pmpaddr 存物理地址 bit55:2,即 PA>>2 编码);地址匹配有 OFF/TOR/NA4/NAPOT 四种模式,其中 NAPOT 支持以 8 字节为最小粒度的幂次区间,TOR(顶部地址)模式则以前一表项的地址为起点。M 态固件(如 OpenSBI)在把控制权交给 S 态之前完成 PMP 编程:默认策略是"固件区间(M 态 RWX,排在最前)+ 其余全部地址空间(S/U 态 RWX)"两个 region——PMP 表项按索引隐式优先,固件区间对 S/U 态回收访问、其余空间对 S/U 完全放行;平台可通过设备树 opensbi-domains 声明更细的隔离域。编号更小的表项优先匹配,锁定位(L)一旦置位连 M 态自身也不能改写——这使 PMP 成为从复位到内核早期之间真正的"地址空间总闸"。[S01][S08][S10]
第三层是翻译——satp CSR 的 MODE 字段决定监管态地址翻译方案:Bare(不翻译)、Sv39(三级页表、39 位 VA、512GiB 空间)、Sv48(四级页表)。Sv39/Sv48 均为 4KiB 基本页、8 字节 PTE,PTE 携带 V/R/W/X/U/G/A/D 权限与状态位。satp 是 WARL 寄存器:写入硬件不支持的模式时整个写操作无效果(ASID 与根页表 PPN 也不更新),软件读回发现的仍是旧值——这个"静默拒绝"是很多"分页没生效"故障的根源。切换 satp 或修改页表后必须执行 sfence.vma 清理 TLB(但 MODE、ASID 与 SUM/MXR 位本身的变更立即生效,无需围栏);此外所有 VA 必须满足符号扩展约定(Sv39 下 bit63:39 复制 bit38,Sv48 下 bit63:48 复制 bit47),否则按 page fault 处理。[S02][S09]
第四层是中断的"地址化"——AIA 规范用 APLIC 与 IMSIC 取代经典 PLIC 的集中式投递:APLIC 负责收集有线中断源、按 sourcecfg 配置为 direct 或 MSI 模式(direct 直接驱动目标 hart 的 mip.SEIP/MEIP,MSI 则按每 hart 的 MSI 目标地址把中断转发给 IMSIC;注意 direct 模式对带 IMSIC 的 hart 有前提:除非该 hart 的 eidelivery 支持直连值 0x40000000,否则 DM=0 与 IE=0 等效);IMSIC 为每个 hart 提供一对 4KiB 的中断文件(M 文件与 S 文件),任何设备或 APLIC 只要向目标中断文件的 MSI 写入口(如 seteipnum_le)写入约定的数据——即中断身份编号(有效范围 1 至 2047,0 无效)——即可投递一条 MSI,文件内以身份编号为索引由 eidelivery、eithreshold、eip/eie 与 topei 完成过滤、使能与认领。S 态内核通过 mideleg 委托与 Ssaia 间接 CSR 面操作 S 文件(Ssaia 即 Smaia 去掉机器级 CSR 后的监督者级扩展;APLIC/IOMMU 属非 ISA 组件不属于二者);设备树中 aplic 与 imsic 节点成对出现,与内核 irqchip 驱动一一对应。[S04][S05][S06][S07]
把四层串起来就是图 1 所示的管线:物理坐标(DDR 与 MMIO)→ PMP 授权 → M→S 交接(a0/a1/satp=0)→ 早期页表(satp + sfence.vma)→ 访存漏斗(VA—页表→PA—PMP→总线)。图 1 也标出了三类经典死法,本文第 4 节按此顺序展开机制与排查。[S01][S02][S08]
主张—证据矩阵
| 主张 | 证据 | 等级 | 备注 |
|---|---|---|---|
| PMP 以 pmpcfg/pmpaddr 表项为 S/U 态划定物理访问权限,匹配模式含 OFF/TOR/NA4/NAPOT | 特权规范机器级 PMP 章 [S01];OpenSBI hart PMP 配置实现 [S10] | C | NAPOT 最小粒度 8 字节 |
| 不支持的 satp.MODE 写入被忽略(WARL),切换后需 sfence.vma | 特权规范监管级 satp 章 [S02];内核早期页表与 TLB 序列 [S12] | C | 静默拒绝是常见误判来源 |
| S 态最早只认设备树给出的物理内存描述,/memory 缺失或错误即"没有内存" | 内核 RISC-V 引导文档 [S16];本文 4.1 节 | C | 与 PMP 覆盖共同构成物理层 |
| 每 hart 一对 IMSIC 中断文件,MSI 编号即中断身份;eidelivery/eithreshold 过滤投递 | AIA 规范 IMSIC 章 [S06];Linux IMSIC 状态管理源码 [S22] | C | 与 APLIC 成对使用 |
| APLIC 的 target/eiid 配置错误或 MSI 目标地址指错 IMSIC 文件时中断不达,表现为定时器停摆 | AIA 规范 APLIC 章 [S05];Linux APLIC 驱动 [S20][S21] | C | 症状为卡在等根文件系统 |
| QEMU virt 可切换 aia=none/aplic/aplic-imsic 以对照复现 AIA 配置问题 | QEMU virt 文档 [S12] | S | 用于建立复现基线 |
能力成熟度对比矩阵
| 机制 | 规范成熟度 | 软件支持(OpenSBI/Linux) | 平台普及度 | 排查可观测性 |
|---|---|---|---|---|
| PMP | 特权规范长期机制,语义稳定 [S01] | 完全支持,交接前必配 [S10] | 全部 RISC-V 平台 | 高:JTAG 可直接读 pmpcfg/pmpaddr |
| Sv39 | 长期机制,RISC-V 64 事实默认 [S02][S09] | 完全支持(内核默认页表配置)[S12] | 全部 RISC-V 64 平台 | 高:satp/scause/stval 直接可读 |
| Sv48 | 规范支持,WARL 可选项 [S02] | 支持(以硬件声明为准)[S12] | 视平台实现而定 | 中:需先读回 satp 确认 MODE 被接受 |
| AIA(APLIC direct) | v1.0 规范冻结 [S04][S05] | Linux 与 OpenSBI 均有驱动,direct 场景在扩展中 [S20][S22][S18] | 新平台逐步采用,与 PLIC 并存 | 中:/proc/interrupts 与 DT 对照 |
| AIA(APLIC MSI + IMSIC) | v1.0 规范冻结 [S04][S06] | Linux 驱动完整(APLIC MSI 与 IMSIC 配套)[S21][S22][S23] | 新平台为主流选择 | 中:需同时核对 aplic/imsic 两节点与驱动 |
图 1:从复位到内核运行的地址与权限管线,逐级为 DDR 物理地址与设备树、PMP 授权、M→S 交接、早期页表与访存判定漏斗,右侧为三个调试观测锚点。来源:RISC-V 特权架构规范 [S01][S02][S03]与 OpenSBI/Linux 实现 [S08][S10][S12] 归纳;数据口径:机制语义以规范为准,配置时序以参考平台文档口径为准。
见图 1,三个观测锚点对应三类典型卡死位置:串口无输出时用 JTAG 读 pmpcfg/pmpaddr 核验授权;有固件输出但内核无输出时读 satp/mstatus/mcause 核验交接与分页;中断类卡死则从"最早输出"倒推第一个未覆盖区间。
4. 关键机制深挖
4.1 DDR 物理地址、PMP 授权与 M→S 交接
接口与实现依据:物理层的三方约定由规范与实现共同固定。其一,M 态到 S 态的交接协议:内核文档明确期望"a0 含 hartid、a1 含设备树地址、satp=0(MMU 必须关闭)"(上游固件侧文档给出同形的 a0/a1 约定),且 Image 须按 PMD 边界(RV64 为 2MiB)对齐放置,不对齐时由 EFI stub 负责物理重定位;文档同时要求固件把常驻内存与受 PMP 保护的区间在设备树中标注,否则它们会被内核直接映射 [S11][S16]。其二,OpenSBI 作为参考实现,在跳转 S 态前执行 hart 级 PMP 配置(旧版本中的 sbi_hart_pmp_configure 例程;当前 master 已重构为 lib/sbi/sbi_hart_pmp.c 中的旧式与 Smepmp 两条路径):表项由 domain 的 memregion 数组逐条生成、剩余表项显式关闭,并以 sfence.vma/hfence.gvma 同步;PMP 被安排在固件初始化末尾执行——因为启用 Smepmp 后 M 态对 S/U 空间的访问会被收回,配置失败则直接 hang [S08][S10]。其三,Linux 侧最早的物理内存观感来自设备树:内核在极早期从 /chosen 与 /memory 节点填充 memblock(没有 /memory 节点即无可用 RAM),镜像头与加载约定则要求内核位于 DDR 区间内并按 PMD 对齐 [S16][S19]。
失效边界:这一层的故障大都是"静默"的:S 态访问未被 PMP 覆盖的 DDR 或 MMIO 时,访问被拒绝并 trap 进入 M 态;若 M 态未安装可恢复的处理路径(正常也不应安装),trap 将循环停留在 M 态,表现为 CPU 停转、串口再无输出。按规范语义,M 模式访存无匹配表项则放行、S/U 模式在平台至少实现一个表项时"无匹配即失败"——这正是"固件一切正常、一进 S 态就死"现象的分界原理 [S01]。OpenSBI 在配置期还能给出两类可观测信号:某 region 不在可编程范围时打印"Can not configure pmp for domain … is not in range."并跳过该 region(失败 region 不占用表项),表项耗尽时报"error: insufficient PMP entries" [S08][S10]。此外 PMP 表项数量有限(实现为 0/16/64 项)、NAPOT 区间有对齐要求(最小 8 字节、地址字段右移 2 位编码)、锁定项(L=1)到复位前不可再改;若 DDR 区间、内核加载地址与 /memory 三者错位(内核被放在 PMP 未覆盖区间,或 /memory 少写一段),症状同样是无输出挂死或即刻 trap [S01][S16]。
验证条件:物理层的验收动作是"读寄存器 + 对照地图",观测面有三处。固件侧:OpenSBI 的启动打印给出 PMP Count / Granularity / Address Bits 与 domain region dump(每个区间的地址范围与 M:/S/U: 权限),排查时应确认 boot prints 未被关闭。硬件侧:用 JTAG/OpenOCD(或 QEMU 的 gdbstub)读 pmpcfg0-14 与 pmpaddr0-63 确认覆盖区间与权限,读 mstatus/mepc/mcause 确认是否发生过 trap 及 trap 时的特权级。内核侧:以 memblock=debug 启动内核,打印 memory/reserved 区间,与设备树 /memory 及 PMP 覆盖逐段对照。通过条件为:内核镜像、FDT、initrd 与全部所需 MMIO 均落在"PMP 覆盖 ∩ 页表映射"内 [S12][S13][S16]。
成本与权衡:把 PMP 配成"全放行"能最快排除物理层嫌疑,但 bringup 后期必须收回最小授权,否则隔离形同虚设;反之过窄的授权在调试期会反复以"随机权限异常"的形式消耗时间。工程上的平衡姿势是:开发阶段先全放行验证功能,再逐项收紧为"内核区间 + FDT/initrd + 外设白名单",每收一项做一次启动回归 [S01][S10][S17]。
4.2 satp、Sv39/Sv48 与早期页表
接口与实现依据:satp 的格式由特权规范固定:MODE 字段(0=Bare、8=Sv39、9=Sv48、10=Sv57)与根页表物理页号(PPN,物理地址除以 4KiB)。Sv39 为三级页表:39 位 VA 拆成三个 9 位 VPN 与 12 位页内偏移,27 位 VPN 翻译为 44 位 PPN;Sv48 增加一级。PTE 8 字节,携带 V/R/W/X/U/G/A/D 位,其中"可写页必须同时可读"(W=1 而 R=0 为保留组合),非叶 PTE 的 D/A/U 位保留。规范对 VA 有硬性要求:Sv39 下 bit63:39 必须全等于 bit38(Sv48 下为 bit63:48 等于 bit47),否则触发 page fault;页表本身必须位于具备硬件页表写权限与 RsrvEventual PMA 属性的内存;sfence.vma 用于在页表变更后同步 TLB [S02]。实现侧分两个阶段:setup_vm 在 early_pg_dir 建立临时内核映射(此时内核可能仍运行在物理加载地址上),setup_vm_final 在 swapper_pg_dir 建立最终映射并创建直接映射;由于设备树在两个阶段都要可访问,内核用 fixmap 区间承载 DTB [S16]。内核虚拟地址布局把上半区(canonical 高地址)从高到低划分为 kernel(顶部 2GiB)、modules/BPF、KASAN、直接映射、vmalloc/ioremap、vmemmap、PCI I/O 与 fixmap——canonical 规则的原文表述是"bits 63–48 必须全等于 bit 47,否则触发 page fault",这解释了内核为什么固定住在上半区 [S09]。此外翻译与权限两层在此交汇:页表遍历本身的物理访问(按 S 特权级)同样受 PMP 检查,修改 PMP 后 M 态软件必须以 SFENCE.VMA(rs1=rs2=x0)同步 PMP 与地址翻译缓存 [S01][S02]。
失效边界:本层典型失败有三类。第一类是 MODE 被忽略:平台只实现 Sv39 而软件按 Sv48 写 satp,按规范"整个写操作无效果",内核以为已开启分页而实际仍在裸机语义下取指,卡死在错误地址;排查特征是读回 satp 的 MODE 与软件期望不符,且此时 ASID 与 PPN 同样未被更新 [S02]。第二类是映射缺口:时钟源、串口或中断控制器等关键 MMIO 未进入早期页表,内核在"该有输出"的位置静默停顿;这类故障通常发生在早期日志之后,因为日志本身用的映射恰好是完整的 [S12]。第三类是 TLB 残留:修改页表或复用 ASID 后忘记 sfence.vma,旧映射残余导致随机错误;规范同时明确 MODE/ASID 与 SUM/MXR 的变更立即生效、无需围栏,唯独页表内容变更需要 [S02][S09]。
验证条件:用 scause/stval/sepc 三件套精确定位:scause=12/13/15(取指/读/写 page fault)后,stval 给出触发 fault 的 VA、sepc 给出指令位置,两者交叉即可判断是"VA 不规范"(stval 不是合法符号扩展地址)还是"映射缺失"(合法地址但页表未覆盖);页表遍历中读 PTE 若违反 PMA/PMP 会以对应类型的 access-fault 报出,可借此区分"页表层"与"物理层"两类问题 [S02]。内核侧可开启 CONFIG_DEBUG_VM 获得启动时的"Virtual kernel memory layout"打印(fixmap/pci io/vmemmap/vmalloc/modules/lowmem/kernel 各段区间),与预期布局对照 [S24]。QEMU 场景下可在 gdbstub 读 satp 与走查页表;真实硅片用 OpenOCD 读同样寄存器。通过条件为:读回 satp.MODE 与预期一致,且 stval 指向的每一条 VA 在页表中均可走通 [S02][S12]。
成本与权衡:Sv39 三级页表结构简单、覆盖 512GiB,对绝大多数嵌入式与桌面 SoC 足够;Sv48 扩展了地址空间但增加一级页表访问(TLB miss 成本更高),且并非所有实现都支持——选择时应先向平台确认支持的 MODE 集合并回归读回验证。调试期临时以 Bare 模式启动可快速排除翻译层嫌疑,但会失去内存保护,只适合作为一次性对照实验。[S02][S09][S17]
4.3 AIA/APLIC/IMSIC:中断的地址化投递
接口与实现依据:AIA v1.0 定义了两级结构。APLIC 是中断源控制面:中断域按树形层级组织(根域恒为 M 级,源编号跨域一致,未下放给子域的源在子域中"如同未实现"),每个有线中断源按 sourcecfg 配置(0=Inactive、1=Detached、4/5=边沿升降、6/7=电平高低,第 10 位 D 决定是否下放子域);投递模式分 direct 与 MSI:direct 模式下 M 级域直拉 mip.MEIP、S 级域直拉 mip/sip.SEIP,target 寄存器装"Hart Index + IPRIO"——但前提是该 hart 没有 IMSIC、或其对应中断文件的 eidelivery 支持直连值 0x40000000,否则"DM=0 与 IE=0 等效",配了 direct 也不来中断;MSI 模式下 APLIC 按每 hart 的 MSI 目标地址(根域的 mmsiaddrcfg/smsiaddrcfg 系列寄存器或硬连线)把中断转发为 MSI 写,target 寄存器装"Hart Index + Guest Index + EIID",EIID 即投递的 MSI 数据、成为目标 hart 处该中断的 minor identity;genmsi 可在 MSI 模式下自发一条测试 MSI(direct 模式下只读零)[S05]。IMSIC 是每 hart 的投递面:M 文件与 S 文件各占一个自然对齐的 4KiB 页,MSI 写入口(典型为 seteipnum_le)写入的 32 位数据即中断身份编号(有效范围 1 至 2047,0 无效),成为该 hart 外部中断的 minor identity;中断文件内 eip/eie 是按身份编号索引的挂起位与使能位数组,eidelivery 是 WARL 寄存器(0=关闭、1=开启、0x40000000=允许 PLIC/APLIC 直连,若支持则复位初值即为该值),eithreshold 是 WLRL 门限——非零值 P 时身份编号 ≥P 的中断不参与信号(身份号越小优先级越高),topei 读出当前"pending 且 enabled 且过阈值"的最高优先级中断身份(CSR 写回即认领清除)[S06]。S 文件的中断信号表现为 mip/sip.SEIP,M 文件表现为 mip.MEIP;S 态经 mideleg 委托后由 Ssaia 的间接 CSR 面(siselect/sireg 与 stopi 等)操作 S 文件 [S07]。Linux 侧由 aplic(含 direct 与 msi 两个实现文件)与 imsic 两组 irqchip 驱动配套:APLIC 绑定要求 interrupts-extended(direct 模式目标 hart 列表)与 msi-parent(MSI 模式目标 IMSIC)至少其一,两者同时存在表示硬件双模式、由驱动按 msi-parent 是否存在二选一;IMSIC 绑定以 riscv,num-ids 声明中断身份数量(63–2047),MSI 目标地址中编码 group/hart/guest 索引 [S14][S15][S20][S21][S22][S23]。
失效边界:中断投递链上任一环节错配都会造成"中断永不抵达",而且远比地址 fault 更隐蔽——CPU 不 trap、也不 fault,只是永远等不到事件。已证实的失效模式包括:target 把外部中断送到了错误 hart 或错误 EIID,导致认领错源或无人认领;MSI 目标地址(mmsiaddrcfg 类配置或硬连线值)指向了非目标 hart 的 IMSIC 文件;eidelivery 未打开或 eithreshold 设得过高把中断挡在门外;以及在带 IMSIC 的系统上误选 direct 模式——除非 eidelivery 支持 0x40000000,该配置等效于关闭外部中断使能 [S05][S06][S21]。此外还有一类地址层耦合:IMSIC/APLIC 寄存器区本身是 MMIO,若未同时进入页表映射与 PMP 授权,第一次访问就会 fault 或 trap——这与 4.1/4.2 节的问题在症状上会合流 [S12][S22]。
验证条件:首选 /proc/interrupts 与设备树对照法——外部中断计数持续为零说明投递链断在来源或 APLIC 侧,内核日志中 irqchip 初始化失败信息则指向 DT 与驱动不匹配。QEMU virt 可用 aia=aplic-imsic 与 aia=aplic 对照复现两种模式(默认 aia=none 走 PLIC;其参考地址图中 APLIC_M=0xC000000、APLIC_S=0xD000000、IMSIC_M=0x24000000、IMSIC_S=0x28000000、UART0=0x10000000、CLINT=0x2000000、DRAM 起始 0x80000000,可作为迁移样板对照目标平台地址图);genmsi 自发中断可用于不依赖外设地验证 APLIC→IMSIC→hart 整链 [S05][S12]。通过条件为:目标外设触发一次后对应 irq 计数递增且无 spurious 风暴;真实平台还可借助时钟源作为低频参考(若平台用 Sstc 则时钟中断不经 APLIC,需分辨)[S06][S23]。
成本与权衡:AIA 的复杂度换来多 hart 与虚拟化的可扩展性(每 hart 独立文件、MSI 天然支持定向投递),相比 PLIC 的集中仲裁更适合现代多核 SoC;代价是配置面更宽(两个节点、两组驱动、更多寄存器)与更差的"自解释性"——出问题时静默而无日志。工程建议:bringup 期先以 QEMU 对照建立"已知可用配置",在真实平台逐项替换(DT 节点、target、MSI 目标地址)而不要一次全改 [S12][S18]。
图 2:AIA 两级结构——有线中断源经 APLIC(direct 模式直拉 SEIP;MSI 模式按域翻译为 MSI 写)投递到每 hart 一对的 IMSIC 中断文件,经 eidelivery/eithreshold/eip/eie 与 topei 后立起 S 态可见的中断位。来源:AIA v1.0 规范 [S04][S05][S06][S07]与 Linux irqchip 实现 [S21][S22][S23] 归纳;数据口径:寄存器与投递语义以 AIA v1.0 规范为准。
见图 2,排查时沿投递链逆流而上:先确认 hart 侧(eidelivery 与 mip.SEIP 是否立起),再查 APLIC 域(target/eiid/MSI 目标地址),最后回到中断源的 DT 描述;每一段都可独立观测,避免"换整份配置再试"的低效循环。
4.4 启动卡死排查路径:症状漏斗
接口与实现依据:排查路径的设计依据是"输出能力递减律"——越早的卡死可用观测面越少,因此用"最早输出"把问题分段的效率最高。串口是上电后第一个可用的输出设备,它在 pmb 之外的 MMIO 区间上:串口能输出说明 DDR/PMP 至少让固件跑起来了,串口不能输出则说明故障在固件到达串口之前的物理层 [S12][S13]。该漏斗与内核文档给出的早期启动顺序(复位→固件→内核早期→驱动→init)逐段对应,每段给出相应的寄存器读取与配置对照动作 [S16][S17];图 3 将其整理为三列矩阵(现象、首要怀疑、验证动作与判据)[S13]。
失效边界:漏斗法的边界在于"复合故障"与"假通过":当物理层与中断层同时配错时,修好第一层后症状会平移(从无输出变为卡在等 rootfs),排查者容易误判为"改坏了";假通过则多见于用了与目标平台不一致的对照配置(例如拿 QEMU 的 DT 对照真实硅片),验证动作必须始终以"当前平台读回值"为准 [S09][S12]。另外,涉及多 hart 时卡死可能是"别的 hart 在等本 hart"的锁步问题而非地址问题,需先回到单 hart 复现以隔离变量 [S03][S17]。
验证条件:每层的验收判据是"可观测状态与期望一致":无输出段读 pmpcfg/pmpaddr 与 PC 判据(PC 是否在取指环);固件后无输出段读 satp/mstatus/mcause/mepc;早期日志后停住段用 earlycon + ignore_loglevel 捞取最后输出(earlycon 无参时由设备树 stdout-path 决定输出端口,RISC-V 平台还可用 earlycon=sbi 走 SBI 调试控制台),并以 memblock=debug 打印的 memory/reserved 区间核对物理布局;卡在等 rootfs 段看 /proc/interrupts 与 DT 中断节点。以上动作均可在 QEMU virt 复现(-s -S + gdbstub),通过条件为定位到一个具体寄存器值或映射缺口,而不是"重启后自愈" [S12][S13]。
成本与权衡:JTAG 方案提供最强观测力(可读任意 CSR 与内存)但需要硬件链路与调试器成本;纯串口方案零成本但观测面窄、只能看到"程序自己愿意打印的东西";QEMU 对照方案成本最低且可无限重放,但存在"虚拟机与真实硅片的地址图差异"这一固有隔阂。工程上推荐"QEMU 建立基线、硅片复现对照、JTAG 解决死锁"的三段式组合,把成本花在 JTAG 链路这一环上通常收益最高 [S12][S17]。
图 3:五档症状(串口无输出、固件后无内核输出、早期日志后停住、卡在等 rootfs、明确 panic)对应的首要怀疑与验证动作;越靠上的症状越指向物理地址与权限层,越靠下越偏向中断与设备映射。来源:基于本文第 4.1 至 4.3 节机制分析与内核/QEMU 文档口径整理的排查顺序【P】 [S09][S12][S13][S17];数据口径:排查顺序为工程判断,验证动作均给出可复现的读取或对照方式。
见图 3,这张矩阵在实际排查中建议按行使用:先按当前症状锁定所在行,执行该行的"验证动作",得到判据后要么定案、要么下行到下一档症状重新匹配——每一步都以寄存器读回或映射对照作为证据,而不是凭经验换配置。
5. 演进方向与情景
从 2026 年中的口径看,这组机制正在经历三个方向的演进,观察窗为 2026-2027 [S04][S08][S12]。情景一(AIA 新平台默认化):以 Aplic+IMSIC 为新平台中断标准的新一代 SoC(含 RISC-V 服务器级平台的最新一批设计)继续铺开,PLIC 仅作为兼容设备存在于老平台;触发信号是新平台设备树评审与内核默认配置向 irq-aplic-imsic 倾斜,反证信号是某主流平台仍以 PLIC 为唯一中断控制器发布 [S04][S21]。情景二(PLIC 长期共存):大量既有嵌入式平台保持 PLIC 不动,AIA 只在新设计出现,"如何在新平台上禁用 AIA 或兼容 PLIC"成为周期性咨询主题;触发信号是内核对该兼容路径的维护活跃度 [S12][S18]。情景三(调试与验证工具链升级):围绕地址与权限的"静态可审计"工具(DT 校验、PMP 区间自动检查、QEMU AIA 对照脚本)逐步成形,把"卡死"从手工排查转为配置期拦截;触发信号是相关校验工具进入发行版 CI [S12][S17]。
三个情景共同无关的确定性趋势是:地址与权限的配置面在变宽(PMP 表项、两级页表选择、AIA 双控制器),而可观测性不会自动跟上——因此"把配置写成可审计清单、把观测点做成可重放脚本"是穿越三个情景都正确的投入 [S10][S12][S17]。
6. 建议与验收
针对"排查内核启动卡死且怀疑地址空间"这一具体场景,给出四组建议,全部以本报告第 4 节机制为依据 [S01][S02][S08][S12][S16]。第一,先建立"地址地图":一页纸列出 DDR 物理区间、内核加载地址、FDT 与 initrd 位置、全部必需 MMIO 区间,以及四组归口(设备树声明值、PMP 覆盖值、页表映射值、驱动实际使用值)——排查的本质就是找到哪一组与其他组不一致。第二,把 PMP 排查做成对照实验:先在参考固件上以"全放行 + 保留调试串口"跑通,再逐项收紧,任何一项收紧后启动回归失败即定位到具体表项。第三,常备两个观测通道:earlycon + ignore_loglevel 的串口参数与 JTAG/QEMU gdbstub 的寄存器读取脚本;前者覆盖"有输出"的场景,后者覆盖"无输出"的死锁场景。第四,对中断相关卡死坚持"逆流验证":从 /proc/interrupts 的计数出发,经 IMSIC 的 eidelivery/eip,到 APLIC 的 target/eiid/MSI 目标地址,最后回到 DT 与驱动匹配,每段独立观测 [S05][S06][S12][S22]。
建议—验收矩阵
| 建议 | 优先级 | 责任对象 | 前置条件 | 验收指标/阈值 | 验收方法 | 证据 |
|---|---|---|---|---|---|---|
| 建立"地址地图"一页纸:DDR/加载地址/MMIO 四组归口对照 | P0 | 排查者 | 拿到目标平台 DT 与固件配置 | 四组值逐项对齐;不一致项全部标注负责人 | 输出对照表并交叉复核 | [S01][S08][S16] |
| PMP 对照实验:全放行→逐项收紧,每项一步启动回归 | P0 | 固件工程师 | 可重放的启动环境(硅片或 QEMU) | 收紧后仍可启动;失败即定位到表项 | 每步记录 pmpcfg/pmpaddr 与启动结果 | [S01][S10][S12] |
| 常备双观测通道:earlycon 参数 + JTAG/gdbstub 寄存器读取脚本 | P0 | 排查者 | 串口与调试链路可用 | 无输出死锁可在 30 分钟内读回 satp/mstatus/mcause/pmpcfg | 演练一次全流程 | [S12][S13] |
| 中断类卡死按"逆流验证"清单执行:/proc/interrupts→IMSIC→APLIC→DT | P1 | 内核工程师 | 系统能跑到过半启动 | 定位到唯一断点(目标 hart/eiid/MSI 目标地址/eidelivery) | 对照表逐段核验 | [S05][S06][S22] |
| 以 QEMU virt 建立 aia=aplic-imsic 基线配置用于回归对照 | P1 | 排查者 | QEMU 环境 | 切换 aia 取值后行为差异可解释、可重放 | 对照脚本与日志入库 | [S12][S21] |
| 收尾:把本次排查结论固化进 bringup 检查单(本工作区已有前序模板) | P2 | 排查者 | 故障定位完成 | 检查单新增条目并在下个平台复用一次 | 检查单评审 | [S18][S17] |
7. 不确定性与研究限制
本报告未在真实硅片上进行实验验证,所有机制语义以规范与源码为准,所有排查动作以文档与参考平台口径复述;具体 SoC 的 DDR 控制器配置、PMP 表项实际数量、支持的最高 satp 模式等均以各平台实现为准,可能出现与本报告基线不同的行为【?】。验证路径为:在目标平台上逐项读回 pmpcfg/pmpaddr 与 satp 并按本文第 4 节的判据核对。此外,本报告对 AIA 在具体国产平台上的普及状态未做逐平台核验,第 5 节的情景判断可能因平台路线图变化而失准【?】。可能推翻本报告部分排查顺序的观察是:某平台把 DDR 初始化推迟到 S 态固件之后(改变"物理层在前、翻译层在后"的分界),此时图 3 的漏斗需要从该平台的固件时序重新校准。
参考来源
| 编号 | 来源 | 支撑内容 |
|---|---|---|
| [S01] | riscv-isa-manual machine.adoc(PMP 与 PMP/分页交互章,main 分支 1.13 稿) | PMP 条目格式、匹配模式与粒度、锁定与优先级、PMP 对分页访问的检查 |
| [S02] | riscv-isa-manual supervisor.adoc(satp/Sv39/Sv48 章) | satp 格式与 WARL 语义、PTE 位、VA 规范化、sfence.vma、翻译后 PMP 检查 |
| [S03] | riscv-isa-manual machine.adoc(mstatus/mepc 与特权级切换章) | 特权级模型、mstatus.MPP/mepc 与 mret 交接语境 |
| [S04] | riscv/riscv-aia intro.adoc(AIA v1.0 引言) | AIA 组成与 Smaia/Ssaia 定义(APLIC/IOMMU 属非 ISA 组件) |
| [S05] | riscv/riscv-aia AdvPLIC.adoc(APLIC 章) | 中断域树、sourcecfg、target 双格式、direct/MSI 模式、genmsi |
| [S06] | riscv/riscv-aia IMSIC.adoc(IMSIC 章) | 中断文件与 4KiB 页、MSI 写格式、eidelivery/eithreshold/eip/eie/topei |
| [S07] | AIA v1.0 MSLevel 章(docs.riscv.org 渲染版) | M 态侧 CSR 与 SEIP 控制语境 |
| [S08] | riscv-software-src/opensbi(lib/sbi/sbi_hart_pmp.c 与旧版 lib/sbi/sbi_hart.c) | PMP 编程路径(旧式与 Smepmp)、探测、启动打印与错误信息 |
| [S09] | Linux vm-layout.rst(RISC-V 虚拟内存布局) | 上/下半区划分、SV39/SV48/SV57 布局表、canonical 规则引述 |
| [S10] | OpenSBI domain_support.md 与 platform_requirements.md | domain 与 PMP 关系、region 约束、平台 PMP CSR 要求 |
| [S11] | OpenSBI fw.md(a0=hartid、a1=FDT 约定与固件形态) | 上一级引导的寄存器约定(与内核侧约定同形佐证) |
| [S12] | QEMU virt.rst 与 riscv-aia.rst(参考平台) | 设备清单、aia=none/aplic/aplic-imsic、自定义 DTB 要求、KVM split |
| [S13] | Linux kernel-parameters.txt | earlycon(含 earlycon=sbi)、ignore_loglevel、memblock=debug |
| [S14] | riscv,aplic.yaml(APLIC 设备树绑定) | riscv,num-sources、interrupts-extended 与 msi-parent 二选一 |
| [S15] | riscv,imsics.yaml(IMSIC 设备树绑定) | riscv,num-ids、MSI 目标地址编码(group/hart/guest 索引) |
| [S16] | Linux boot.rst(RISC-V 内核启动需求) | a0/a1/satp=0 期望、PMD 对齐、reserved 标注、setup_vm 两阶段与 fixmap |
| [S17] | 内部前序报告《RISC-V 64 CPU Bringup》(本工作区) | 引导链时序与固件分工基线 |
| [S18] | 内部前序报告《RISC-V 中断控制器:PLIC 与 AIA/APLIC》(本工作区) | AIA/APLIC 规范与实现证据基线 |
| [S19] | 内部前序报告《Linux 内核入口到 init》(本工作区) | 内核早期启动路径与调试观测面 |
| [S20] | Linux irq-riscv-aplic-main.c | APLIC 主驱动:按 msi-parent 存在性选择 MSI/direct 初始化 |
| [S21] | Linux irq-riscv-aplic-msi.c 与 irq-riscv-aplic-direct.c | APLIC 两个投递模式的实现(电平重触发等) |
| [S22] | Linux irq-riscv-imsic-state.c | IMSIC 状态管理(EITHRESHOLD/EIDELIVERY 顺序) |
| [S23] | Linux irq-riscv-imsic-platform.c | IMSIC 平台层(MSI 载荷、亲和迁移与 IPI 路径) |
| [S24] | Linux arch/riscv/mm/init.c | print_vm_layout 布局打印与 misc_mem_init 中的 memblock_dump_all 调用 |
附录:研究方法
- 检索与核验顺序:RISC-V 特权架构规范(riscv-isa-manual,main 分支 Version 1.13 稿)与 AIA 规范(riscv/riscv-aia,v1.0,2025-03-12 修订)原文优先;OpenSBI 与 Linux 内核源码(master 分支)次之;QEMU 文档与设备树绑定再次之;本地前序报告(本工作区 bringup 与中断两篇)作为交叉基线复用。
- 证据等级与标记:【C】为规范或源码可直接支持;【C*】为带条件的结论;【S】为情景判断;【P】为工程判断;【?】为未核验缺口,均按"可靠性标记"节使用。
- 口径:机制名与寄存器名以规范为准;配置时序以参考平台(OpenSBI + QEMU virt)口径为准;不替未核验平台补全行为。
- 复现方式:图 1 至图 3 由
riscv64-boot-addr-figs.py生成,SVG 源文件与 PNG 预览随报告提供;第 4 节的验证动作均给出可在 QEMU virt(-s -S + gdbstub,aia=aplic-imsic)复现的观测方式。 - 写作环境:本报告在 QoderWork 图形客户端内完成(调研、制图、校验与渲染发布)。