ARM核学习(五)异常处理


参考资料

ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition

  • B1.8 Exception handling

ARM® Cortex™-A Series Programmer’s Guide Version: 4.0

异常介绍

什么是异常

异常是处理器核在执行程序指令的过程中突然遇到了异常的事情,这些事件包括硬件中断、指令执行错误、用户程序请求服务、内存访问异常、取指令异常等,几乎每种处理器都支持特定的异常处理,中断也是异常的一种

ARM的异常源

image-20241125214514494

FIQ较IRQ快的原因

image-20241125214951043

ARM核异常处理过程

image-20241125215607973

处理器工作模式切换

异常向量表

image-20241125215846276

image-20241125215810206

[!NOTE]

ARM核怎么知道异常向量表放在内存哪个地方呢?

  1. Cortex-A架构版本之前,是由协处理器(Coprocessor 15CP15C1寄存器来决定的,且只能存放在 0xFFFF00000x00000000两者中的一个。
  2. 从Cortex-A架构版本开始,由协处理器 CP15c12寄存器来决定,且不限制起始地址

Table B1-3 The vector tables

image-20241125222433967

https://developer.arm.com/documentation/den0013/d/Exception-Handling/Exception-priorities/Exception-mode-summary?lang=en

Cortex-A处理器异常向量表基地址指定

image-20241125223320833

https://developer.arm.com/documentation/den0013/d/Exception-Handling/Exception-priorities?lang=en

The first column in Table 11.1 gives the vector offset within the vector table associated with the particular type of exception. This is a table of instructions that the ARM core jumps to when an exception is raised. These instructions are located in a specific place in memory. The default vector base address is 0x00000000, but most ARM cores permit the vector base address to be moved to 0xFFFF0000 (or HIVECS). All Cortex-A series processors permit this, and it is the default address selected by the Linux kernel. Cores that implement the Security Extensions can additionally set the vector base address, separately for Secure and Non-secure states, using the CP15 Vector Base Address registers.

https://developer.arm.com/documentation/den0013/d/Exception-Handling/Exception-priorities/The-Vector-table?lang=en

CP15 c1

https://documentation-service.arm.com/static/602cf701083323480d479d18?token=

image-20241125225458216

Table 4-52 SCTLR bit assignments (continued)

image-20241125225701958

CP15 c12

ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition

image-20241125230347930

[!NOTE]

其中高位[31:5]是向量表的基地址,低位[4:0]是异常向量的偏移量(offset)

SVC/SWI异常处理

image-20241126170039244

在操作系统中的运用

image-20241126170125829

案例代码

.global _start

_start:
    b reset
    b undefined_instruction
    b software_interrupt
    b prefetch_abort
    b data_abort
    b not_used
    b irq_interrupt
    b fiq_interrupt 
    
undefined_instruction:.word undefined_instruction
prefetch_abort:.word prefetch_abort
data_abort:.word data_abort
not_used:.word not_used
irq_interrupt:.word irq_interrupt
fiq_interrupt:.word fiq_interrupt

reset:
    mov r0,#1
    mov r1,#2
    mov r2,#3
    
    swi #8      @will save CPSR to SPSR and set CPSR(T,M), save PC to LR
    
    mov r3,#4
    mov r4,#5
	
software_interrupt:
    ldr sp,=0x4000fff0
    stmfd sp!,{r0-r12,lr}
    
    ldr r0,[lr,#-4]
    mov r1,#0xff
    bic r0,r0,r1,lsl #24
    
    ldmfd sp!,{r0-r12,pc}
    
stop:
	b stop				
	

定义异常向量表

这里我们将异常向量表放在代码段起始地址:

_start:
    b reset
    b undefined_instruction
    b software_interrupt
    b prefetch_abort
    b data_abort
    b not_used
    b irq_interrupt
    b fiq_interrupt 

异常向量表的指令顺序和地址偏移需要遵循规范,当发生异常跳转时处理器会按照规范从异常向量表找到对应的指令来执行

image-20241126174514822

程序入口reset

第一条指令跳转到 reset

reset:
    mov r0,#1
    mov r1,#2
    mov r2,#3
    
    swi #8      @will save CPSR to SPSR and set CPSR(T,M), save PC to LR
    
    mov r3,#4
    mov r4,#5

其中我们在 swi软中断指令前后增加了一些寄存器操作来验证一些事情:

  • 中断返回后,中断前的寄存器应该保持原样
  • 中断返回后,应该接着下一条指令继续执行

中断跳转——ARM核自动做的事情

image-20241126175353623
  • 将当前CPSR保存到异常状态下的SPSR中
  • 对CPSR进行如下设置
    • 进入ARM状态(对应CPSR的T位)
    • 设置工作模式为相应的异常模式(M位)
    • 禁止中断(设置I位和F位为1)
  • 将当前PC的值保存到LR,以便异常处理完后能够跳转回中断发生的地方继续执行
  • 将当前PC设置到异常向量表的相应位置(由于我们执行的是 swi软中断,因此会根据异常向量表规范,将PC指向向量表的基地址偏移 0x08的位置)

[!NOTE]

由于使用的是ARM9E-S的核,因此没有手动指定向量表基地址时,默认为 0x00000000

中断跳转——从向量表到中断服务程序

_start:
    b reset
    b undefined_instruction
    b software_interrupt
    b prefetch_abort
    b data_abort
    b not_used
    b irq_interrupt
    b fiq_interrupt 

根据向量表基地址及偏移量 0x08能够找到我们编写的 b software_interrupt,接着跳转到对应的中断服务程序:

software_interrupt:
ldr sp,=0x4000fff0
stmfd sp!,{r0-r12,lr}

ldr r0,[lr,#-4]
mov r1,#0xff
bic r0,r0,r1,lsl #24

ldmfd sp!,{r0-r12,pc}^
  • 设置SP,将R0-12、LR压栈保护;

  • 将LR偏移 -4的地址中的内容给到R0,由于LR是由ARM核中断跳转时设置的,因此我们回头看看中断跳转的地方:

    image-20241126181400176

  • 接着将 0xff送入R1,并将R1左移24位后作为掩码将R0中的高8位清零,得到中断号 #8

    image-20241126181736106

  • ldmfd sp!,{r0-r12,pc},准备返回,出栈复原R0-R12,将LR(指向 mov r3,#4)送到PC,中断返回

[!IMPORTANT]

这里需要注意的是,ldmfd sp!,{r0-r12,pc}^中的 ^,会将SPSR暂存的值写回CPSR

总结

异常产生时,ARM核自动做的事情

image-20241126190902985

异常生成之前,我们需要做的事情

image-20241126192006040

异常返回

image-20241126192130322

[!NOTE]

S后缀表明在原有指令基础上,同时将SPSR恢复到C

The End


文章作者: 安文
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 安文 !
  目录