Linux/PA-RISC 内核底层调试指南:实模式绝对地址、HPMC 与 PSW Q 位解析

Linux/PA-RISC 内核底层调试指南:实模式绝对地址、HPMC 与 PSW Q 位解析 Linux/PA-RISC 内核底层调试指南实模式绝对地址、HPMC 与 PSW Q 位解析【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux本文基于 Linux 内核源码树中的 Documentation/arch/parisc/debugging.rst 编写面向需要调试 linux/parisc 底层代码启动汇编、异常处理、实模式路径的内核开发者。文章完整覆盖原文档的三个核心主题——实模式下的绝对地址翻译、HPMCHigh Priority Machine Check高优先级机器检查的现场分析、以及 PSW Q 位清除导致的“中断位置丢失”问题并结合内核源码arch/parisc/include/asm/page.h、arch/parisc/kernel/hpmc.S、arch/parisc/kernel/traps.c、arch/parisc/include/asm/psw.h 等给出可验证的实现依据与实战排查思路。读完本文你将掌握如何把实模式崩溃现场的绝对地址换算为可在 System.map 中查到的虚拟地址、如何通过 System Responder/Requestor 地址判断 HPMC 成因、以及如何识别和定位 Q 位相关的“幽灵挂起”。背景为什么 parisc 需要一份“另类”的调试指南在绝大多数 Linux 架构上内核一旦启动完成就运行在虚拟地址分页模式下异常、oops 与调试器看到的世界是统一的。但 PA-RISC 有一个显著差异相当一部分汇编代码运行在实模式real mode下使用绝对地址physical address而非虚拟地址。这直接导致了三种在其它架构上很少遇到的调试难题地址体系不统一——崩溃现场的地址需要手工做“实模式 → 虚拟模式”的换算访问不存在的内存不会产生常规的内核 oops而是触发 HPMC某些关键临界区会清除 PSW 的 Q 位一旦在其中断发生CPU 不记录中断现场故障位置变得不可知。原文档正是针对这三点给出的实战提示。下文逐节展开并补充源码级的佐证。一、绝对地址实模式地址与虚拟地址的换算1.1 问题本质Linux/parisc 的大部分汇编代码运行在实模式此时 CPU 没有开启地址翻译代码访问的是物理绝对地址。而内核其余部分以及 System.map 中的符号都是按虚拟地址组织的。因此当实模式代码崩溃、你在寄存器或崩溃现场看到一个“看起来不对劲”的大地址时第一步通常是判断它是绝对地址还是虚拟地址并把绝对地址换算成可以查询 System.map 的虚拟地址。原文档给出的换算公式非常直接将绝对地址转换为虚拟地址在 System.map 中查询时加上__PAGE_OFFSET当前为0x10000000。即虚拟地址 绝对地址 __PAGE_OFFSET1.2 源码中的 __PAGE_OFFSET 定义__PAGE_OFFSET并非一个随手写的魔数它在 arch/parisc/include/asm/page.h 中有明确定义且随位宽不同取值不同#define __PAGE_OFFSET_DEFAULT (0x40000000) /* 1GB */ #define __PAGE_OFFSET_DEFAULT (0x10000000) /* 256MB */ #define __PAGE_OFFSET (0) /* bootloader uses physical addresses */ #define __PAGE_OFFSET __PAGE_OFFSET_DEFAULT #define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET)64 位内核W 位为 1 时默认__PAGE_OFFSET 0x400000001GB32 位内核默认__PAGE_OFFSET 0x10000000256MB——这正是原文档写作时32 位为主的取值在 bootloader 阶段内核尚未建立分页时__PAGE_OFFSET被定义为0即直接使用物理地址。因此在实际调试时先确认内核是 32 位还是 64 位构建再决定加0x10000000还是0x40000000。同文件中还给出了物理地址与虚拟地址互转的两个宏arch/parisc/include/asm/page.h#define PA(x) ((x)-__PAGE_OFFSET) #define VA(x) ((x)__PAGE_OFFSET)VA(x)绝对地址 → 虚拟地址即文档所述换算PA(x)虚拟地址 → 绝对地址。汇编代码中同样大量使用这一换算。例如 arch/parisc/include/asm/assembly.h 中的辅助宏通过ldil L%(-__PAGE_OFFSET)与ldil L%(__PAGE_OFFSET)把寄存器的值在物理/虚拟地址空间之间平移ldil L%(-__PAGE_OFFSET), \grphys ldil L%(__PAGE_OFFSET), \grvirt1.3 实战换算流程拿到崩溃现场/寄存器中的地址值A判断A是否落在 I/O 高位区间或明显小于__PAGE_OFFSET——若是A很可能是绝对地址物理地址计算V A __PAGE_OFFSET32 位加0x1000000064 位加0x40000000用V在System.map中查找最近邻符号定位到具体函数/数据反向验证若V查出的符号与当前执行上下文相符说明换算正确。二、HPMC实模式内存访问错误的“第一现场”2.1 什么是 HPMCHPMCHigh Priority Machine Check是 PA-RISC 架构中最高优先级的机器检查。原文档明确指出一个关键差异当实模式代码尝试访问不存在的内存时你得到的是一个 HPMC而不是内核 oops。也就是说常规的缺页/总线错误处理路径fault handler、oops 打印在实模式下根本不适用——CPU 直接进入 HPMC 处理流程。因此调试实模式代码的内存访问错误必须学会读 HPMC 现场。2.2 内核的 HPMC 处理链内核的 HPMC 入口位于 arch/parisc/kernel/hpmc.S。该文件头部注释说明了其职责hpmc.SThis HPMC handler retrieves the HPMC pim data, resets IO and returns to the default trap handler with code set to 1 (HPMC).处理流程大致如下均为实模式代码复用 TOC 栈作为 HPMC 栈#define hpmc_stack toc_stackhpmc.S并保留一块HPMC_IODC_BUF_SIZE (0x8000)的缓冲区给 IODC 过程hpmc.S定义hpmc_pim_data缓冲区HPMC_PIM_DATA_SIZE 896足以容纳架构 2.0 规定的全部处理器状态hpmc.S;进入os_hpmc后通过PDC_PIM调用固件获取 HPMC 的 PIMProcessor Internal Memory数据hpmc.S调用PDC_IODC复位 I/O 设备最终以 trap code1交给默认 trap 处理函数hpmc.S。随后arch/parisc/kernel/traps.c 中的parisc_terminate()会以 code 1 作为“最后手段”被调用traps.c其注释明确说明它处理“kernel space faults 和 HPMC”两类情况case 1: transfer_pim_to_trap_frame(regs); break;transfer_pim_to_trap_frame()traps.c把hpmc_pim_data中的 PIM 数据搬运到标准的struct pt_regs中供打印与调试使用。根据 CPU 类型不同它分别解析宽struct pdc_hpmc_pim_20或窄struct pdc_hpmc_pim_11两种 PIM 布局其中包含全部通用寄存器、浮点寄存器、空间寄存器以及关键的cr[17]IASQ、cr[18]IAOQ、cr[19]IIR、cr[20]ISR、cr[21]IOR、cr[22]IPSW——这些正是中断/机器检查发生时定位指令地址所需的信息。在 arch/parisc/kernel/traps.c 中可以看到 HPMC 到达默认处理器后打印的信息/* High-priority machine check (HPMC) */ pdc_chassis_send_status(PDC_CHASSIS_DIRECT_HPMC); parisc_terminate(High Priority Machine Check (HPMC), ...);2.3 读 HPMC 现场System Responder / Requestor原文档给出的核心排查技巧是寻找 System Responder 与 System Requestor 地址HPMC 现场/固件信息中会包含这两个值。System Requestor 地址发起访问的“请求者”。它应当匹配某一个处理器 HPAHost Physical Address位于 I/O 区间的高位地址。如果 Requestor 不在这个范围说明请求来源可疑需要检查是哪个部件/代码路径发起了这次访问。System Responder 地址响应请求的“应答者”也就是实模式代码真正尝试访问的地址——这是定位故障的关键值。原文档给出了一个非常实用的经验判据System Responder 的典型值是大于__PAGE_OFFSET0x10000000的地址这意味着一个虚拟地址在实模式代码访问它之前没有被转换成物理地址。换句话说如果System Responder __PAGE_OFFSET故障根因几乎可以锁定为“该被PA()转换的虚拟地址漏掉了转换”——某段实模式代码直接拿虚拟地址去访问内存了。此时排查方向非常明确沿着崩溃地址反查调用链找到实模式下未经PA(x)/绝对地址转换就 dereference 的指令。结合上文PA(x) x - __PAGE_OFFSET的宏定义这条判据在源码层面是自洽的凡是应当“虚拟 → 物理”的转换点汇编宏、__pa()一旦缺失实模式访问的就是一个高出__PAGE_OFFSET的地址必然触发 HPMC。三、Q 位丢失的中断现场与“幽灵挂起”3.1 Q 位是什么PSWProcessor Status Word中的 Q 位collect interruption state中断状态收集位控制 CPU 是否在发生中断/异常时更新中断处理器所需的现场寄存器。在 Documentation/arch/parisc/registers.rst 中有明确记载Q (collect interruption state) 1 (0 in code directly preceding an rfi)即在正常情况下 Q 位为 1只有紧邻rfiReturn From Interruption指令的代码才会把它清 0。Q 位在 arch/parisc/include/asm/psw.h 中定义为#define PSW_Q 0x00000008 /* Enable Interrupt State Collection */3.2 清除 Q 位为什么危险原文档对这一机制讲得很透彻当 Q 位被清除时CPU 不会更新中断处理器用来判断“机器是在哪里被中断”的那些寄存器——所以如果你在清除 Q 位的指令与重新设置 Q 位的 RFI 之间收到一个中断你就完全不知道它发生在哪里。运气好的话 IAOQ 会指向清除 Q 位的那条指令运气不好则指向任意位置。这里的关键在于IAOQInstruction Address Offset Queue是中断/异常发生时恢复执行的地址依据。当 Q 位为 0 时CPU 不保存这些状态中断返回rfi或现场分析时就失去了“故障发生在哪条指令”的锚点。在内核源码中确实存在这样的“quiet 区域”。arch/parisc/include/asm/psw.h 定义了#define PSW_SM_QUIET PSW_SM_RPSW_SM_QPSW_SM_PPSW_SM_DPSW_SM_I这是一个“安静模式”掩码在即将执行rfi的返回路径上内核会同时清除 R、Q、P、D、I 等位。典型的使用点如 arch/parisc/kernel/entry.S 中系统调用/中断的公共返回路径rsm PSW_SM_QUIET,%r0 /* prepare for rfi */ ... rfirsmReset System Mask清掉这些位之后rfi才真正执行——这个窗口正是文档所警告的“Q 位被清除”的临界区。任何在这个窗口内到达的异步中断都无法被精确归位。3.3 Q 位问题的典型症状与排查原文档给出的症状判据非常实用通常 Q 位问题会表现为无法解释的系统挂起unexplainable system hangs或代码跑出物理内存末尾running off the end of physical memory。为什么是这两种症状无法解释的挂起中断现场丢失后rfi恢复执行时 IAOQ 指向了错误地址CPU 跳到无意义/未映射的位置可能进入循环、死等或非法执行表现为“卡死”且无任何 oops 输出——因为现场本身已被破坏连打印崩溃信息的代码都到不了跑出物理内存末尾IAOQ 指向错误地址后指令预取/数据访问命中不存在的物理内存在实模式下再次触发 HPMC回到本文第二节形成“错误现场 → 无法恢复 → 再次异常”的雪崩。排查思路优先检查所有rfi/rfir之前对 PSW 的修改路径entry.S 中大量rfir分布在各个中断/异常返回点entry.S确认在rsm PSW_SM_QUIET与rfi之间没有插入可能被中断打断、且依赖现场保存的代码若怀疑 Q 位问题可在临界区前后用断点/单步确认 IAOQ 是否仍指向预期指令结合 HPMC 现场中的 IAOQPIM 数据cr[18]与iaoq_back交叉验证——若 IAOQ 指向的地址与任何已知代码都不相邻则高度怀疑 Q 位临界区被打断。四、调试工具箱速查场景现象关键动作依据实模式崩溃地址无法理解寄存器值疑似物理地址虚拟地址 绝对地址 __PAGE_OFFSET再查 System.mappage.h、assembly.h实模式访问非法内存HPMC 而非 oops读取 System Responder / RequestorResponder ≥__PAGE_OFFSET说明虚拟地址漏转换hpmc.S、traps.c无故挂起/跑飞无 oops、现场不可信检查rsm PSW_SM_QUIET与rfi之间的 Q 位窗口核对 IAOQ 指向psw.h、entry.S延伸阅读Documentation/arch/parisc/registers.rstPA-RISC 通用寄存器、控制寄存器CR17–CR22 中断参数、CR19 IIR、CR20 ISR、CR21 IOR、CR22 IPSW、空间寄存器与 PSW 各位的完整用法表是理解 HPMC PIM 数据与 Q 位语义的第一手资料Documentation/arch/parisc/features.rstparisc 架构内核特性列表Documentation/arch/parisc/index.rstPA-RISC 架构文档索引源码入口arch/parisc/kernel/hpmc.SHPMC 处理器、arch/parisc/kernel/traps.cHPMC 现场转换与终止处理、arch/parisc/kernel/entry.S含rfi/rfir返回路径与 Q 位临界区、arch/parisc/include/asm/psw.hPSW 位定义。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考