鸿蒙轻内核M核源码分析系列十八Fault异常处理

开发 前端
Fault异常处理模块与OpenHarmony LiteOS-M内核芯片架构相关,本文先简单介绍下Fault异常类型,向量表及其代码,异常处理C语言程序,然后详细分析下异常处理汇编函数实现代码。

[[430071]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

Fault异常处理模块与OpenHarmony LiteOS-M内核芯片架构相关,提供对HardFault、MemManage、BusFault、UsageFault等各种故障异常处理。有关Cortex-M芯片相关的知识不在本文讨论,请自行参考《Cortex™-M7 Devices Generic User Guide》等官方资料。本文先简单介绍下Fault异常类型,向量表及其代码,异常处理C语言程序,然后详细分析下异常处理汇编函数实现代码。

1、Fault Type异常类型

如下图中的Fault类型表格所示,Fault表示各种故障,Handler表示故障处理机制,Bit Name标记故障的寄存器的Bit位,Fault status register故障状态寄存器。该图摘自《Cortex™-M7 Devices Generic User Guide》。

鸿蒙轻内核M核源码分析系列十八 Fault异常处理-鸿蒙HarmonyOS技术社区
鸿蒙轻内核M核源码分析系列十八 Fault异常处理-鸿蒙HarmonyOS技术社区

2、Vector table向量表

向量表包含栈指针的复位值和开始地址,也叫异常向量。异常可以看作特殊的中断,异常编号Exception number, 中断请求号IRQ number,偏移值offset,向量Vector的对应关系如下图所示,本文主要关注NMI、HardFault、Memory management fault、Bus fault、Usage fault、SVCall等异常。

鸿蒙轻内核M核源码分析系列十八 Fault异常处理-鸿蒙HarmonyOS技术社区
[[430072]]

在中断初始化时,会初始化该异常向量表,代码位置kernel\arch\arm\cortex-m7\gcc\los_interrupt.c。⑴处的HalExcNMI,⑵处的HalExcHardFault,⑶处的HalExcMemFault,⑷处的HalExcBusFault,⑸处的HalExcUsageFault,⑹处的HalExcSvcCall这些中断异常处理函数定义在kernel\arch\arm\cortex-m7\gcc\los_exc.S。本文我们主要分析这些汇编函数的代码。

⑺处开始的这两行代码也比较重要,通过更改系统处理控制与状态寄存器(System Handler Control and State Register)的bit位来使能相应的异常,通过更改配置与控制寄存器(Configuration and Control Register)的bit位来使能除零异常。

LITE_OS_SEC_TEXT_INIT VOID HalHwiInit(VOID) 

#if (LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT == 1) 
    UINT32 index
    g_hwiForm[0] = 0;             /* [0] Top of Stack */ 
    g_hwiForm[1] = Reset_Handler; /* [1] reset */ 
    for (index = 2; index < OS_VECTOR_CNT; index++) { /* 2: The starting position of the interrupt */ 
        g_hwiForm[index] = (HWI_PROC_FUNC)HalHwiDefaultHandler; 
    } 
    /* Exception handler register */ 
⑴  g_hwiForm[NonMaskableInt_IRQn + OS_SYS_VECTOR_CNT]   = HalExcNMI; 
⑵  g_hwiForm[HARDFAULT_IRQN + OS_SYS_VECTOR_CNT]        = HalExcHardFault; 
⑶  g_hwiForm[MemoryManagement_IRQn + OS_SYS_VECTOR_CNT] = HalExcMemFault; 
⑷  g_hwiForm[BusFault_IRQn + OS_SYS_VECTOR_CNT]         = HalExcBusFault; 
⑸  g_hwiForm[UsageFault_IRQn + OS_SYS_VECTOR_CNT]       = HalExcUsageFault; 
⑹  g_hwiForm[SVCall_IRQn + OS_SYS_VECTOR_CNT]           = HalExcSvcCall; 
    g_hwiForm[PendSV_IRQn + OS_SYS_VECTOR_CNT]           = HalPendSV; 
    g_hwiForm[SysTick_IRQn + OS_SYS_VECTOR_CNT]          = SysTick_Handler; 
 
    /* Interrupt vector table location */ 
    SCB->VTOR = (UINT32)(UINTPTR)g_hwiForm; 
#endif 
#if (__CORTEX_M >= 0x03U) /* only for Cortex-M3 and above */ 
    NVIC_SetPriorityGrouping(OS_NVIC_AIRCR_PRIGROUP); 
#endif 
 
    /* Enable USGFAULT, BUSFAULT, MEMFAULT */ 
⑺  *(volatile UINT32 *)OS_NVIC_SHCSR |= (USGFAULT | BUSFAULT | MEMFAULT); 
    /* Enable DIV 0 and unaligned exception */ 
    *(volatile UINT32 *)OS_NVIC_CCR |= DIV0FAULT; 
 
    return

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

3、HalExcHandleEntry异常处理C程序入口

HalExcHandleEntry异常处理函数是汇编异常函数跳转到C语言程序的入口,定义在文件kernel\arch\arm\cortex-m7\gcc\los_interrupt.c,被kernel\arch\arm\cortex-m7\gcc\los_exc.S文件中的汇编函数调用。函数参数由汇编程序中的R0-R3寄存器传值进来,汇编程序中的寄存器和HalExcHandleEntry函数参数对应关系如下表所示:

下面我们分析下函数的源代码,⑴处的标签表示异常类型参数的高16位用于特色的标记,主要用于标记故障地址是否有效、是否故障发生在中断中,是否支持浮点等。⑵处增加中断计数和嵌套异常数目。⑶记录异常类型,⑷处如果记录了有效的故障地址,则获取故障地址。⑸处如果当前运行任务存在时,若标记了异常发生在中断,则记录中断号,并记录异常发生在中断内,否则记录任务编号,并记录异常发生在任务内。如果当前运行任务为空,则异常发生在初始化阶段。⑹处如果异常类型里包含支持浮点数的标记,则相应处理下。⑺处输出异常信息到控制台。

LITE_OS_SEC_TEXT_INIT VOID HalExcHandleEntry(UINT32 excType, UINT32 faultAddr, UINT32 pid, EXC_CONTEXT_S *excBufAddr) 

⑴  UINT16 tmpFlag = (excType >> 16) & OS_NULL_SHORT; /* 16: Get Exception Type */ 
⑵  g_intCount++; 
    g_excInfo.nestCnt++; 
 
⑶  g_excInfo.type = excType & OS_NULL_SHORT; 
 
⑷  if (tmpFlag & OS_EXC_FLAG_FAULTADDR_VALID) { 
        g_excInfo.faultAddr = faultAddr; 
    } else { 
        g_excInfo.faultAddr = OS_EXC_IMPRECISE_ACCESS_ADDR; 
    } 
⑸  if (g_losTask.runTask != NULL) { 
        if (tmpFlag & OS_EXC_FLAG_IN_HWI) { 
            g_excInfo.phase = OS_EXC_IN_HWI; 
            g_excInfo.thrdPid = pid; 
        } else { 
            g_excInfo.phase = OS_EXC_IN_TASK; 
            g_excInfo.thrdPid = g_losTask.runTask->taskID; 
        } 
    } else { 
        g_excInfo.phase = OS_EXC_IN_INIT; 
        g_excInfo.thrdPid = OS_NULL_INT; 
    } 
⑹  if (excType & OS_EXC_FLAG_NO_FLOAT) { 
        g_excInfo.context = (EXC_CONTEXT_S *)((CHAR *)excBufAddr - LOS_OFF_SET_OF(EXC_CONTEXT_S, uwR4)); 
    } else { 
        g_excInfo.context = excBufAddr; 
    } 
 
⑺  OsDoExcHook(EXC_INTERRUPT); 
    OsExcInfoDisplay(&g_excInfo); 
    HalSysExit(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.

4、Los_Exc异常处理汇编函数

上文介绍Vector table向量表时,已经提到了在文件kernel\arch\arm\cortex-m7\gcc\los_exc.S中定义的的异常处理函数,如下。当发生Fault故障异常时,会调度执行这些异常处理函数,本节会详细分析函数的源代码来掌握内核如何处理这些发生的异常。这6个函数处理过程类似,我们选择2个典型的函数进行分析。

.global  HalExcNMI 
.global  HalExcHardFault 
.global  HalExcMemFault 
.global  HalExcBusFault 
.global  HalExcUsageFault 
.global  HalExcSvcCall 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

4.1 HalExcNMI

当发生NMI(Non Maskable Interrupt,不可屏蔽中断)时,会触发运行HalExcNMI汇编函数,该函数的执行流程如下图。下文会结合该流程图来阅读函数代码。

鸿蒙轻内核M核源码分析系列十八 Fault异常处理-鸿蒙HarmonyOS技术社区

HalExcNMI函数代码如下,⑴处给R0寄存器赋值OS_EXC_CAUSE_NMI,该值等于16,对应文件kernel\arch\arm\cortex-m7\gcc\los_arch_interrupt.h中的异常类型宏定义OS_EXC_CAUSE_NMI,均为16。该值对应HalExcHandleEntry函数的第一个参数。⑵处设置故障地址,该值对应HalExcHandleEntry函数的第二个参数。⑶处跳转到函数osExcDispatch继续执行。

   .type HalExcNMI, %function 
    .global HalExcNMI 
HalExcNMI: 
    .fnstart 
    .cantunwind 
⑴  MOV  R0, #OS_EXC_CAUSE_NMI 
⑵  MOV  R1, #0 
⑶  B  osExcDispatch 
    .fnend 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

下面分析的一些函数比较通用,其他异常处理函数也都会调用。

4.1.1 osExcDispatch函数

osExcDispatch函数代码如下,⑴处加载Interrupt Active Bit Registers中断活跃位寄存器基地址。中断活跃位寄存器共有8个,NVIC_IABR0-NVIC_IABR7,每个寄存器包含32位,可以对应32个中断号,共支持256个中断。其中,IABR[0]的 bit位0~31 分别对应中断号031;`IABR[1]`的bit位031对应中断32~63;其他以此类推。⑵处设置循环计数,对应8个寄存器,后文会循环遍历8个寄存器查询是否存在活跃的中断。

  .type osExcDispatch, %function 
    .global osExcDispatch 
osExcDispatch: 
    .fnstart 
    .cantunwind 
⑴  LDR   R2, =OS_NVIC_ACT_BASE 
⑵  MOV   R12, #8                       // R12 is hwi check loop counter 
    .fnend 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

4.1.2 _hwiActiveCheck函数

执行完上述osExcDispatch函数代码后,会继续执行随后的函数_hwiActiveCheck的代码。⑴处读取活跃位寄存器的数值,然后执行⑵比较寄存器数值与0的大小,如果相等,说明该活跃位寄存器对应的中断均不活跃,然后跳转到_hwiActiveCheckNext。如果不等于0,则执行⑶,参数类型的高16位标记为中断。⑷处代码根据中断活跃位计算中断号,并赋值给寄存器R2,该值对应HalExcHandleEntry函数的第三个参数。具体计算方式为,首先反转活跃中断位寄存器数值R3,并保存到R2,然后计算高位0的数量。把计数值R12加1,然后左移5位(等于乘以32),然后加上R2,就是中断号。

    .type _hwiActiveCheck, %function 
    .global _hwiActiveCheck 
_hwiActiveCheck: 
    .fnstart 
    .cantunwind 
⑴  LDR   R3, [R2]                      // R3 store active hwi register when exc 
⑵  CMP   R3, #0 
    BEQ   _hwiActiveCheckNext 
 
    // exc occurred in IRQ 
⑶  ORR   R0, R0, #FLAG_HWI_ACTIVE 
⑷  RBIT  R2, R3 
    CLZ   R2, R2 
    AND   R12, R12, #1 
    ADD   R2, R2, R12, LSL #5               // calculate R2 (hwi number) as pid 
    .fnend 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

4.1.3 _ExcInMSP函数和_NoFloatInMsp函数

如果有活跃的中断,则继续执行后续的代码。处理中断时,使用的主栈处理函数_ExcInMSP。⑴处比较异常返回值和#0XFFFFFFED的大小,如果相等说明支持浮点计算则继续执行后续代码,如果不相等则不支持浮点计算,会跳转到函数_NoFloatInMsp函数。有关异常返回值的更多信息请参考《Cortex™-M7 Devices Generic User Guide》表格Table 2-15 Exception return behavior。

如果支持浮点计算时,执行⑵把栈指针加上104赋值给R3寄存器,然后压栈,该值对应HalExcHandleEntry函数的第四个参数。104的大小应该来源于结构体EXC_CONTEXT_S。⑶处把寄存器PRIMASK数值复制到R12寄存器,然后把R4-R12寄存器压栈。⑷处把浮点寄存器压栈,⑸处跳转到函数_handleEntry。

当不支持浮点计算时,执行函数_NoFloatInMsp。⑹处把栈指针加上32赋值给R3寄存器,然后压栈,该值对应HalExcHandleEntry函数的第四个参数。然后把R3压栈,把寄存器PRIMASK数值复制到R12,然后压栈R4-R12。和支持浮点时的差别就是,不需要压栈D8-D15寄存器。⑺处把参数类型高位上加上不支持浮点的标记,然后跳转到函数_handleEntry。

  .type _ExcInMSP, %function 
    .global _ExcInMSP 
_ExcInMSP: 
    .fnstart 
    .cantunwind 
⑴  CMP   LR, #0XFFFFFFED 
    BNE   _NoFloatInMsp 
⑵  ADD   R3, R13, #104 
    PUSH  {R3} 
⑶  MRS   R12, PRIMASK                  // store message-->exc: disable int? 
    PUSH {R4-R12}                       // store message-->exc: {R4-R12} 
#if ((defined(__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ 
     (defined(__FPU_USED) && (__FPU_USED == 1U))) 
⑷  VPUSH {D8-D15} 
#endif 
⑸  B     _handleEntry 
  .fnend 
 
    .type _NoFloatInMsp, %function 
    .global _NoFloatInMsp 
_NoFloatInMsp: 
    .fnstart 
    .cantunwind 
⑹  ADD   R3, R13, #32 
    PUSH  {R3} // save IRQ SP            // store message-->exc: MSP(R13) 
 
    MRS   R12, PRIMASK                  // store message-->exc: disable int? 
    PUSH {R4-R12}                       // store message-->exc: {R4-R12} 
⑺  ORR   R0, R0, #FLAG_NO_FLOAT 
    B     _handleEntry 
  .fnend 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.

4.1.4 _hwiActiveCheckNext函数

遍历中断活跃位寄存器时,如果前一个寄存器没有活跃的中断则执行函数_hwiActiveCheckNext判断下一个寄存器是否有活跃的中断。⑴处把活跃位寄存器地址偏移4字节,计数减1,如果还有其他活跃位寄存器,则跳转到函数_hwiActiveCheck继续判断。否则执行后续的代码,⑵处加载System Handler Control and State Register(缩写SHCSRS)系统处理控制与状态寄存器的地址,然后加载半字节数值。⑶处加载掩码0xC00,该数值二进制的第10、第11位为1。SHCSRS寄存器的第11位对应SysTick异常活跃位,第10位对应PendSV异常活跃位。⑷处R2、R3进行逻辑与计算,然后把结果与0进行比较,如果结果为0,说明没有发生ysTick异常或PendSV异常。如果结果为1,说明发生了异常,需要执行⑸跳转到函数_ExcInMSP继续执行,上文已分析该函数。⑹处获取全局变量g_taskScheduled的地址,然后获取其数值,与1进行比较。如果等于1,说明系统已经开始任务调度,会继续执行后续的代码。如果不为1,系统未调度,处于初始化阶段,需要跳转到函数_ExcInMSP继续执行。

如果系统开始了任务调度,此时使用进程栈PSP,执行⑺,判断系统是否支持浮点计算。如果支持则继续执行,否则跳转到函数_NoFloatInPsp。⑻处开始的代码和函数_NoFloatInPsp可以对比着阅读,前者需要压栈浮点寄存器,后者不需要。⑻处把栈指针复制到R2寄存器,然后把栈指针减去96。⑼处把PSP线程栈指针值赋值给R3寄存器,然后把R3加104赋值给寄存器R12,计算出来的值是任务栈指针,然后进行压栈。

⑽处复制PRIMASK寄存器数值到R12,然后把寄存器R4-R12压栈,接着压栈浮点寄存器D8-D15。⑾处从PSP栈指针开始把R4-R11、D8-D15出栈,然后从R13栈指针开始把D8-D15、R4-R11进行压栈。⑿处跳转到函数_handleEntry继续指向。

  .type _hwiActiveCheckNext, %function 
    .global _hwiActiveCheckNext 
_hwiActiveCheckNext: 
    .fnstart 
    .cantunwind 
⑴  ADD   R2, R2, #4                        // next NVIC ACT ADDR 
    SUBS  R12, R12, #1 
    BNE   _hwiActiveCheck 
 
    /*NMI interrupt exception*/ 
⑵  LDR   R2, =OS_NVIC_SHCSRS 
    LDRH  R2,[R2] 
⑶  LDR   R3,=OS_NVIC_SHCSR_MASK 
⑷  AND   R2, R2,R3 
    CMP   R2,#0 
⑸  BNE   _ExcInMSP 
    // exc occured in Task or Init or exc 
    // reserved for register info from task stack 
 
⑹  LDR  R2, =g_taskScheduled 
    LDR  R2, [R2] 
    TST  R2, #1                         // OS_FLG_BGD_ACTIVE 
    BEQ  _ExcInMSP                      // if exc occurred in Init then branch 
 
 
⑺  CMP   LR, #0xFFFFFFED               //auto push floating registers 
    BNE   _NoFloatInPsp 
 
    // exc occurred in Task 
⑻  MOV   R2,  R13 
    SUB   R13, #96                      // add 8 Bytes reg(for STMFD) 
 
⑼  MRS   R3,  PSP 
    ADD   R12, R3, #104 
    PUSH  {R12}                         // save task SP 
 
⑽  MRS   R12, PRIMASK 
    PUSH {R4-R12} 
    VPUSH {D8-D15} 
 
    // copy auto saved task register 
 
⑾  LDMFD R3!, {R4-R11}                  // R4-R11 store PSP reg(auto push when exc in task) 
    VLDMIA  R3!, {D8-D15} 
    VSTMDB  R2!, {D8-D15} 
    STMFD R2!, {R4-R11} 
⑿  B     _handleEntry 
  .fnend 
 
    .type _NoFloatInPsp, %function 
    .global _NoFloatInPsp 
_NoFloatInPsp: 
    .fnstart 
    .cantunwind 
    MOV   R2,  R13                      // no auto push floating registers 
    SUB   R13, #32                      // add 8 Bytes reg(for STMFD) 
 
    MRS   R3,  PSP 
    ADD   R12, R3, #32 
    PUSH  {R12}                         // save task SP 
 
    MRS   R12, PRIMASK 
    PUSH {R4-R12} 
 
    LDMFD R3, {R4-R11}                  // R4-R11 store PSP reg(auto push when exc in task) 
    STMFD R2!, {R4-R11} 
    ORR   R0, R0, #FLAG_NO_FLOAT 
  .fnend 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.

4.1.5 _handleEntry函数

继续分析函数_handleEntry。代码很简单,⑴把栈指针复制给R3,该值对应HalExcHandleEntry函数的第四个参数。⑵处关闭中断,关闭Fault异常,然后执行⑵跳转到C语言的函数HalExcHandleEntry。

_handleEntry: 
    .fnstart 
    .cantunwind 
⑴  MOV R3, R13                         // R13:the 4th param 
⑵  CPSID I 
    CPSID F 
    B  HalExcHandleEntry 
 
    NOP 
  .fnend 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

4.2 HalExcUsageFault

当发生使用异常UsageFault时,会触发运行HalExcUsageFault汇编函数,该函数的执行流程如下图。下文会结合该流程图来阅读函数代码。

鸿蒙轻内核M核源码分析系列十八 Fault异常处理-鸿蒙HarmonyOS技术社区

HalExcUsageFault函数代码如下,⑴处把可配置故障状态寄存器Configurable Fault Status Register(CFSR)的地址复制到R0寄存器,然后读取寄存器值到R0寄存器。⑵处把0x030F赋值给R1寄存器,然后左移16位。UsageFault Status Register使用故障状态寄存器的有效性如下,即0-3,8-9为有效位,0x030F的二进制对应这些有效位。⑶处进行逻辑与,这样就计算出实际的使用故障对应的bit位。⑷处把R12赋值为0,然后会继续执行后续的汇编代码osExcCommonBMU。

鸿蒙轻内核M核源码分析系列十八 Fault异常处理-鸿蒙HarmonyOS技术社区
[[430073]]
   .type HalExcUsageFault, %function 
    .global HalExcUsageFault 
HalExcUsageFault: 
    .fnstart 
    .cantunwind 
⑴  LDR  R0, =OS_NVIC_FSR 
    LDR  R0, [R0] 
 
⑵  MOVW  R1, #0x030F 
    LSL  R1, R1, #16 
⑶  AND  R0, R0, R1 
⑷  MOV  R12, #0 
 
    .fnend 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

4.2.1 g_uwExcTbl数组

在看osExcCommonBMU函数的代码之前需要了解下g_uwExcTbl数组,g_uwExcTbl数组定义在文件kernel\arch\arm\cortex-m7\gcc\los_interrupt.c,代码如下。

该数组包含32个元素,每个元素对应CFSR寄存器的一个bit位,元素数值在LiteOS-M中定义为异常类型。比如OS_EXC_UF_DIVBYZERO等于异常类型10,为除零异常。

UINT8 g_uwExcTbl[FAULT_STATUS_REG_BIT] = { 
    0, 0, 0, 0, 0, 0, OS_EXC_UF_DIVBYZERO, OS_EXC_UF_UNALIGNED, 
    0, 0, 0, 0, OS_EXC_UF_NOCP, OS_EXC_UF_INVPC, OS_EXC_UF_INVSTATE, OS_EXC_UF_UNDEFINSTR, 
    0, 0, 0, OS_EXC_BF_STKERR, OS_EXC_BF_UNSTKERR, OS_EXC_BF_IMPRECISERR, OS_EXC_BF_PRECISERR, OS_EXC_BF_IBUSERR, 
    0, 0, 0, OS_EXC_MF_MSTKERR, OS_EXC_MF_MUNSTKERR, 0, OS_EXC_MF_DACCVIOL, OS_EXC_MF_IACCVIOL 
}; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

4.2.2 osExcCommonBMU函数

现在来分析下汇编代码osExcCommonBMU。⑴处计算出R0数值的高位0的个数,加载数组全局变量g_uwExcTbl地址到R3寄存器,然后执行⑵计算是第几个数组元素,加载元素值到R0寄存器。⑶处R0与R12进行逻辑或运算,没有什么影响。R0对应HalExcHandleEntry函数的第一个参数。后续会继续执行osExcDispatch函数,前文已经分析过。

  .type osExcCommonBMU, %function 
    .global osExcCommonBMU 
osExcCommonBMU: 
    .fnstart 
    .cantunwind 
⑴  CLZ  R0, R0 
    LDR  R3, =g_uwExcTbl 
⑵  ADD  R3, R3, R0 
    LDRB R0, [R3] 
⑶  ORR  R0, R0, R12 
    .fnend 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

小结

本文介绍了Fault异常类型,向量表及其代码,异常处理C语言程序,异常处理汇编函数实现代码。

文章相关附件可以点击下面的原文链接前往下载

https://harmonyos.51cto.com/resource/1344

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-01-12 10:50:23

鸿蒙HarmonyOS应用

2022-01-10 15:31:44

鸿蒙HarmonyOS应用

2021-06-04 09:57:49

鸿蒙HarmonyOS应用

2021-05-17 09:28:59

鸿蒙HarmonyOS应用

2021-06-04 14:15:10

鸿蒙HarmonyOS应用

2021-05-08 15:14:50

鸿蒙HarmonyOS应用

2021-05-25 09:28:34

鸿蒙HarmonyOS应用

2021-05-31 20:30:55

鸿蒙HarmonyOS应用

2021-09-22 14:36:32

鸿蒙HarmonyOS应用

2021-07-06 09:45:03

鸿蒙HarmonyOS应用

2022-03-11 20:23:14

鸿蒙源码分析进程管理

2022-04-13 11:02:12

鸿蒙事件模块事件Event

2022-03-03 18:28:28

Harmony进程任务管理模块

2021-06-09 09:48:01

鸿蒙HarmonyOS应用

2021-05-21 09:25:11

鸿蒙HarmonyOS应用

2021-05-27 09:43:56

鸿蒙HarmonyOS应用

2021-05-11 09:54:55

鸿蒙HarmonyOS应用

2021-04-30 15:06:34

鸿蒙HarmonyOS应用

2021-05-10 15:05:56

鸿蒙HarmonyOS应用

2022-01-14 08:39:47

鸿蒙HarmonyOS应用
点赞
收藏

51CTO技术栈公众号