ARM32 v7内核模块FIQ触发报Bad Mode内核崩溃问题求助
在ARM32 v7核心、Linux 3.14.x版本内核环境中,通过内核模块实现GPIO触发的FIQ handler,功能为每次触发FIQ时递增全局计数器static volatile int fiq_counter = 0;。已确认该GPIO引脚仅启用FIQ、未注册IRQ handler,模块加载成功且在/proc/interrupts中可见,但触发FIQ时内核触发Bad Mode崩溃。崩溃信息显示FIQ代码已被执行,现求助排查故障原因。
FIQ Handler汇编代码
asm( ".global wdog_fiq_handler_start\n" ".global wdog_fiq_handler_end\n" "wdog_fiq_handler_start:\n" /* Use FIQ-mode-specific register R8_fiq to manipulate the counter */ " ldr r9, [r8]\n" // Load the current value of fiq_counter into r9 (FIQ banked register) " add r9, r9, #1\n" // Increment the counter " str r9, [r8]\n" // Store the incremented value back into fiq_counter "bx lr\n" "wdog_fiq_handler_end:\n" );
注册代码
void *fiqhandler_start; unsigned int fiqhandler_length; extern unsigned char wdog_fiq_handler_end, wdog_fiq_handler_start; fiqhandler_start = &wdog_fiq_handler_start; fiqhandler_length = &wdog_fiq_handler_end - &wdog_fiq_handler_start; if (claim_fiq(&fh)) { pr_err("wdogfiq : couldn't claim FIQ.\n"); return; } set_fiq_handler(fiqhandler_start, fiqhandler_length); get_fiq_regs(®s); regs.ARM_r8 = (unsigned long)&fiq_counter; set_fiq_regs(®s); enable_fiq(88); /* hwirq 88 corresponding to GPIO pin */
崩溃信息
[ 4145.425070] Internal error: Oops - bad mode: 0 [#1] SMP ARM [ 4145.523357] Process swapper/0 (pid: 0, stack limit = 0x808c0240) [ 4145.719365] Code: e320f000 e320f000 e320f000 e320f000 (e5989000)
经确认e5989000对应FIQ代码中的ldr r9, [r8]指令。
可能的故障原因及排查方向
1. FIQ模式专属R8寄存器未正确初始化
Linux内核的get_fiq_regs()和set_fiq_regs()操作的struct pt_regs结构体中,ARM_r8对应的是用户/系统模式的通用R8寄存器,而非FIQ模式下的banked专属R8寄存器。你通过regs.ARM_r8 = (unsigned long)&fiq_counter设置的是通用R8,而FIQ handler中访问的是FIQ模式的banked R8,其值未被初始化,导致ldr r9, [r8]访问非法地址,触发Bad Mode。
修复建议:
直接在FIQ handler中加载fiq_counter的虚拟地址,无需依赖寄存器传递:
asm( ".global wdog_fiq_handler_start\n" ".global wdog_fiq_handler_end\n" "wdog_fiq_handler_start:\n" " ldr r8, =fiq_counter\n" // 直接加载全局变量的虚拟地址 " ldr r9, [r8]\n" " add r9, r9, #1\n" " str r9, [r8]\n" " bx lr\n" "wdog_fiq_handler_end:\n" );
同时确保fiq_counter全局可见:
static volatile int fiq_counter = 0; EXPORT_SYMBOL(fiq_counter);
2. 内存地址访问权限异常
如果fiq_counter的虚拟地址在FIQ模式下无法被正常访问(比如页表映射异常、内存权限配置错误),也会触发ldr指令的访问异常。可以通过以下方式验证:
- 打印
&fiq_counter的虚拟地址,确认该地址在内核空间的映射状态; - 尝试将
fiq_counter放置在已确认可访问的内存区域(比如内核静态内存区)。
3. FIQ硬件配置遗漏
部分ARMv7平台的GPIO FIQ除了内核层面的enable_fiq,还需要在GPIO控制器中配置触发方式(边沿/电平)、使能FIQ通道等硬件寄存器。即使/proc/interrupts中可见FIQ项,若硬件配置不全,可能导致异常进入时的状态错误,间接引发地址访问崩溃。
4. FIQ handler返回指令问题
虽然bx lr是ARM异常返回的标准指令,但需确认FIQ模式下的LR寄存器(LR_fiq)是否正确保存了返回地址。若异常入口时LR值异常,bx lr可能跳转到非法地址,但崩溃点在ldr指令,此可能性较低。
内容的提问来源于stack exchange,提问作者InsaneCoder

