CM3 / Chromium-os: MemManage Fault Case Analysis

背景概念

  • CM3堆栈指针SP

    主堆栈指针(MSP), 或写作SP_main, 这是缺省堆栈指针,用于OS内核,异常中断处理程序以及所有需要特权访问的应用程序代码。

    进程堆栈指针(PSP), 或写作SP_process, 用于常规代码/task执行。

  • CM3异常/中断响应序列

    异常/中断发生时,CM3会自动执行流程:入栈 -> 取向量 -> 更新寄存器

   入栈8个寄存器值压入栈

    

压入哪个堆栈 MSP/PSP的选择:

    如果当响应异常时,当前代码正在使用PSP, 则压入PSP, 即使用线程堆栈;

    否则压入MSP, 使用主堆栈。

取向量: 从向量表里找到正确的异常向量, 然后在服务程序入口取指;

更新寄存器 在执行异常/终端处理程序之前,会更新一系列寄存器:

  1. SP:  用MSP作为堆栈指针;
  2. PSR: IPSR位段(PSR的最低部分)会被更新为异常编号;
  3. PC: 向量取出完毕后,PC指向处理程序入口;
  4. LR: 将被更新成一种特殊的值”EXC_RETURN”, 用于异常返回。

     

    

异常/中断返回时,CM3处理流程

     

    根据EXC_RETURN的值,从不同的SP里恢复那8个寄存器。

    EXC_RETURN=0xFFFF_FFF1时,从MSP里恢复,

    EXC_RETURN=0xFFFF_FFFD时,从PSP里恢复。

 

  • CM3 MemManage Fault介绍

    MemMagae Fault

    MemManage Fault也称作“存储器管理fault”,是CM3编号为4的异常。

    触发的因素

  1. 访问了MPU设置区域范围之外的地址
  2. 往只读region写数据
  3. 用户级下访问了只允许在特权级下访问的地址

    相关寄存器

    为了查找问题,NVIC有一个”存储器管理fault状态寄存器(MFSR)”, 它表示导致MemManage fault的原因;

    如果是数据访问违例(DACCVIOL)或是取指访问违例(IACCVIOL),则违例指令的地址已经被压入栈中;

    如果MMARVALID被置位,则还能进一步查出引发fault时访问的地址,读取NVIC”存储 器管理地址寄存器(MMAR)”.

      


问题现象

    调试AP(Cortex-A53/A73) idle的时候,由于AP的power on/off在SCP(Cortex-M3)里完成,

    发现A73 idle,会导致SCP出现MemManage Fault异常。

[17:01:22]=== HANDLER EXCEPTION: 04 ====== xPSR: 61000077 ===

[17:01:22]r0 :10009308 r1 :10008408 r2 :004da495 r3 :00000000

[17:01:22]r4 :00000000 r5 :00000000 r6 :00000000 r7 :00000000

[17:01:22]r8 :00000000 r9 :00000000 r10:00000000 r11:00000000

[17:01:22]r12:0000000a sp :100083e0 lr :fffffffd pc :4685480a

[17:01:22]Instruction access violation

[17:01:22]mmfs = 1, shcsr = 70001, hfsr = 0, dfsr = 0

[17:01:22]

[17:01:22]=========== Process Stack Contents ===========

[17:01:22]100085d8: 00000000 00000000 03b00100 ff80023c

[17:01:22]100085e8: 00000000 10000c1f 10000c20 01000000

[17:01:22]100085f8: 00000000 100009fd deadd00d deadd00d

[17:01:22]10008608: deadd00d deadd00d deadd00d deadd00d


问题分析

 

分析的方向是找到触发异常的地址在哪里。

从异常打印上来看, MMFS寄存器值1,表示是取指令违例,但无法用MMAR寄存器来查看出问题的地址;

从异常log来看, 进程堆栈内容是正常的,PC值是错误的,但这个PC值不在芯片的正常区域,无法从PC值判断出问题的触发点,参考价值不是太大;

修改代码,在MemManage Fault异常处理的入口停止,然后连DS5进去查看:


通过DS5查看LR/SP寄存器:


LR值是0xFFFF_FFF1, 根据EXC_RETURN的定义,可以得知触发异常的地方并不在进程里,而在异常/中断处理/特权代码执行区域,根据执行场景来看,当时主要是在做AP core的power on/off, 初步判断是在执行AP power on/off的中断处理程序中触发了异常。

到底是power off还是power on出的问题,需要进一步区分, 根据LR的值,说明出现异常的context被压入MSP堆栈里,查看MSP堆栈内容,可以发现其中xPSR的值是0x61000077, 0x77是中断号,查找对应的中断,发现这是触发CM3执行AP power on的中断号,问题进一步锁定在power on流程里;

重新回到LR的值,仍是EXC_RETURN, 说明问题出在刚刚进入中断处理程序,还没有任何函数调用来更改LR的值,所以开始从异常向量表中断入口查起,最终发现在异常向量表入口处,中断处理程序入口没有定义好, 下图if条件是false了。

修改CONFIG_IRQ_COUNT, 正确定义0x77号中断的处理程序入口,问题解决。


后记

在分析问题过程中,发现异常的panic report log打印, lr: 0xffff_fffd, 而实际用DS5连进去,可以看到lr: 0xFFFF_FFF1, 这里出现了不一致,如果没有连DS5, log打印会让分析问题的方向出现偏差。

跟踪代码发现,在打印lr的时候,使用的是pdata->cm.frame里的lr值,而不是pdata->cm.regs里的值, 代码并没有把pdata->cm.regs的lr数据copy到pdata->cm.frame, 修改打印代码, 使用pdata->cm.regs来打印lr, 即可获得正确的lr值。

// 打印lr: panic_data_print(...)

if (pdata->flags & PANIC_DATA_FLAG_FRAME_VALID)

sregs = pdata->cm.frame;

......

print_reg(14, sregs, 5);

 

// 而pdata->cm.frame copy的寄存器只有8个 report_panic(...)

for (i = 0; i < 8; i++)

pdata->cm.frame[i] = sregs[i];

pdata->flags |= PANIC_DATA_FLAG_FRAME_VALID;

 

// lr打印修改成:

print_reg(14, lregs, 5); 






  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值