ARM-SWI and return of undefined instruction exception handler (7)

processing flow

SWI and undefined instruction exception interrupts are generated by the currently executing instruction itself. When SWI and undefined instruction exception interrupts are generated, the PC value of the program counter has not been updated, and it points to the second instruction behind the current instruction (for ARM For instructions, it points to the location of the current instruction address plus 8 bytes; for Thumb instructions, it points to the location of the current instruction address plus 4 bytes).
When SWI and undefined instruction exceptions occur, the processor saves PC-4 to the LR_mode register in exception mode. At this time, the value in LR_mode is PC-4, which points to the first instruction of the current instruction. Therefore, the return operation can directly assign the value of LR_mode (pointing to the next instruction of the current instruction) to the PC, which can be realized by the following instructions:

MOVS PC, LR   // 注意 S ,指定了 S 意味着同时将SPSR 拷贝到 CPSR

This instruction copies the value in the register LR to the program counter PC to realize the program return, and at the same time copies the contents of the SPSR_mode register to the current program counter CPSR.
When the data stack is used in the abnormal interrupt processing program, the execution site of the interrupted program can be saved during the abnormal interrupt processing program through the following instructions, and the execution site of the interrupted program can be restored when the abnormal interrupt processing program is exited. The data stack used in the exception handler is provided by the user.

stmdb sp!, {
    
    r0-r12, lr}   // 保存现场 r0-r12 - reg_list
// user code
ldmia sp!, {
    
    r0-r12, pc}^  // 恢复现场 r0-r12 - reg_list

In the above instruction, reg_list is the list of registers used in the exception handler. The identifier ^ indicates that the content of the SPSR_mode register is copied to the CPSR register, and this instruction can only be used in privileged mode.

example

insert image description here

  • Suppose a swi/undef exception occurs after the instruction+0 instruction is executed
  • At this time, the PC value is not updated and still points to instruction+2
  • After entering the swi/undef exception, LR_mode = PC -4, so at this time LR points to instruction+1
  • After the swi/undef exception handling is completed, the program needs to return to instruction+1 for execution, so just assign the value of the LR_mode register at this time to the PC

Code

SWI

exception handler

    .align 2
    .arm
    .weak SVC_Handler
    .type SVC_Handler, %function
SVC_Handler:
    /* 执行到这里之前:
    * 1. lr_svc 保存有被中断模式中的下一条即将执行的指令的地址
    * 2. SPSR_svc 保存有被中断模式的 CPSR
    * 3. CPSR 中的 M4-M0 被设置为 10011, 进入到 svc 模式
    * 4. 跳到 0x08 的地方执行程序
    */

    /* 保存现场 */
    /* 在 swi 异常处理函数中有可能会修改 r0-r12, 所以先保存 */
    /* lr 是异常处理完后的返回地址, 也要保存 */
    stmdb sp!, {
    
    r0-r12, lr}

.if 0
    mov r4, lr /* LR 由硬件自动保存软中断指令下一条指令的地址 */

    /* 处理swi异常 */
    mrs r0, cpsr
    ldr r1, =swi_string
    bl printException

    sub r0, r4, #4 /* LR 为硬件自动保存 SWI xxx 指令的下一条指令地址,LR – 4 就是 SWI 指令地址,由 SWI 指令编码知识可知,SWI 指令低 24 位保存有软中断号 */
    bl printSWIVal
.else
    ldr r4,[lr, #-4] /* LR 由硬件自动保存软中断指令下一条指令的地址,指令地址存放的是指令的操作码,将操作码取出,低 24 位置,即为软中断号 */
    bic r0, r4, #0xff000000 /* LR 为硬件自动保存 SWI xxx 指令的下一条指令地址,LR – 4 就是 SWI 指令地址,由 SWI 指令编码知识可知,SWI 指令低 24 位保存有软中断号 */
    push {
    
    r0}

    /* 处理swi异常 */
    mrs r0, cpsr
    ldr r1, =swi_string
    bl printException

    pop {
    
    r0}

    bl printSWIVal
.endif

    /* 恢复现场 */
    ldmia sp!, {
    
    r0-r12, pc}^  /* ^ 会把 spsr 的值恢复到 cpsr 里 */

swi_string:
    .string "swi exception"

trigger swi

swi_code:
    swi 0x123  /* 执行此命令, 触发SWI异常, 进入0x8执行 */

Print the soft interrupt number in C language

void printSWIVal(unsigned int *pSWI)
{
    
    
    swi_flag = 1;
    printf("SWI val = 0x%x\r\n", *pSWI & ~0xff000000);
}

undefined instruction

exception handler

    .align 2
    .arm
    .weak Undefined_Handler
    .type Undefined_Handler, %function
Undefined_Handler:
    /* 执行到这里之前:
    * 1. lr_und 保存有被中断模式中的下一条即将执行的指令的地址
    * 2. SPSR_und 保存有被中断模式的CPSR
    * 3. CPSR 中的 M4-M0 被设置为 11011, 进入到 und 模式
    * 4. 跳到 0x4 的地方执行程序
    */

    /* 在 und 异常处理函数中有可能会修改 r0-r12, 所以先保存 */
    /* lr 是异常处理完后的返回地址, 也要保存 */
    stmdb sp!, {
    
    r0-r12, lr}

    /* 保存现场 */
    /* 处理 und 异常 */
    mrs r0, cpsr
    ldr r1, =und_string
    bl printException

    /* 恢复现场 */
    ldmia sp!, {
    
    r0-r12, pc}^  /* ^ 会把 spsr 的值恢复到 cpsr 里 */

und_string:
    .string "undefined instruction exception"

Raises an undefined instruction exception

und_code:
    nop              /* for debug */
    .word 0xeeadc0de /* undefine instruction */
    nop              /* for debug */
    .word 0xFFFFFFFF /* undefine instruction */
    nop              /* for debug */

    ldr   r0, =main
    bx    r0

Print out the exception string in C language

void printException(unsigned int cpsr, char *str)
{
    
    
    excep_flag = 1;
    printf("Exception! cpsr is 0x%x\r\n", cpsr);
}

Appendix source code

.equ Mode_USR,        0x10
.equ Mode_FIQ,        0x11
.equ Mode_IRQ,        0x12
.equ Mode_SVC,        0x13
.equ Mode_MON,        0x16
.equ Mode_ABT,        0x17
.equ Mode_HYP,        0x1A
.equ Mode_UND,        0x1B
.equ Mode_SYS,        0x1F

.equ Stack_size,      0x400
.equ Stack_Start,     0x80200000

.equ Mode_USR_Stack,  Stack_Start + Stack_size
.equ Mode_FIQ_Stack,  Mode_USR_Stack + Stack_size
.equ Mode_IRQ_Stack,  Mode_FIQ_Stack + Stack_size
.equ Mode_SVC_Stack,  Mode_IRQ_Stack + Stack_size
.equ Mode_MON_Stack,  Mode_SVC_Stack + Stack_size
.equ Mode_ABT_Stack,  Mode_MON_Stack + Stack_size
.equ Mode_HYP_Stack,  Mode_ABT_Stack + Stack_size
.equ Mode_UND_Stack,  Mode_HYP_Stack + Stack_size
.equ Mode_SYS_Stack,  Mode_UND_Stack + Stack_size

    /* 定义一个 .isr_vector 段,链接脚本中定义相应的段来存放该段的数据 */
    .section .isr_vector, "a"
    .arm
    .align 2
    .globl __isr_vector
__isr_vector:
    ldr     pc, =Reset_Handler           /* Reset                  */
    ldr     pc, =Undefined_Handler       /* Undefined instructions */
    ldr     pc, =SVC_Handler             /* Supervisor Call        */
    ldr     pc, =PrefAbort_Handler       /* Prefetch abort         */
    ldr     pc, =DataAbort_Handler       /* Data abort             */
    .word   0                            /* RESERVED               */
    ldr     pc, =IRQ_Handler             /* IRQ interrupt          */
    ldr     pc, =FIQ_Handler             /* FIQ interrupt          */

    /* 余下的指令的放在 .text 中,所以用 .text 指定段 */
/* Reset Handler */
    .text
    .arm
    .align 2
    .globl   Reset_Handler
    .weak    Reset_Handler
    .type    Reset_Handler, %function
Reset_Handler:

    cpsid i /* 关闭全局中断 */

    /* 关闭I,DCache和MMU
     * 采取读-改-写的方式。
     */
    mrc p15, 0, r0, c1, c0, 0     /* 读取 CP15 的 C1 寄存器到 R0 中 */
    bic r0,  r0, #(0x1 << 12)     /* 清除 C1 寄存器的  bit12 位(I位),关闭 I Cache */
    bic r0,  r0, #(0x1 <<  2)     /* 清除 C1 寄存器的 bit2 (C位),关闭 D Cache */
    bic r0,  r0, #0x2             /* 清除 C1 寄存器的 bit1 (A位),关闭对齐 */
    bic r0,  r0, #(0x1 << 11)     /* 清除 C1 寄存器的 bit11 (Z位),关闭分支预测 */
    bic r0,  r0, #0x1             /* 清除 C1 寄存器的 bit0 (M位),关闭 MMU */
    mcr p15, 0, r0, c1, c0, 0     /* 将 r0 寄存器中的值写入到 CP15 的 C1 寄存器中 */

#if 0
    /* 汇编版本设置中断向量表偏移 */
    ldr r0, =0x80000000

    dsb
    isb
    mcr p15, 0, r0, c12, c0, 0
    dsb
    isb
#endif

    /* 模式切换并设置 sp 地址 */
    cps     #Mode_FIQ
    ldr     sp, =Mode_FIQ_Stack

    cps     #Mode_IRQ
    ldr     sp, =Mode_IRQ_Stack

    cps     #Mode_SVC
    ldr     sp, =Mode_SVC_Stack

    cps     #Mode_MON
    ldr     sp, =Mode_MON_Stack

    cps     #Mode_ABT
    ldr     sp, =Mode_ABT_Stack

    cps     #Mode_HYP
    ldr     sp, =Mode_HYP_Stack

    cps     #Mode_UND
    ldr     sp, =Mode_UND_Stack

    /* sys mode and user have common sp register */
    cps     #Mode_SYS
    ldr     sp, =Mode_SYS_Stack

    cpsie i /* 打开全局中断 */

    /* clear bss */
    ldr r1, =__bss_start__
    ldr r2, =__bss_end__
    mov    r0, #0
bss_loop:
    cmp     r1, r2
    itt     lt
    strlt   r0, [r1], #4
    blt    bss_loop


    /* 使能IRQ中断 */
    mrs r0, cpsr        /* 读取cpsr寄存器值到r0中 */
    bic r0, r0, #0x80   /* 将r0寄存器中bit7清零,也就是CPSR中的I位清零,表示允许IRQ中断 */
    msr cpsr, r0        /* 将r0重新写入到cpsr中 */

swi_code:
    swi 0x123  /* 执行此命令, 触发SWI异常, 进入0x8执行 */

und_code:
    nop              /* for debug */
    .word 0xeeadc0de /* undefine instruction */
    nop              /* for debug */
    .word 0xFFFFFFFF /* undefine instruction */
    nop              /* for debug */

    ldr   r0, =main
    bx    r0


    .align 2
    .arm
    .weak Undefined_Handler
    .type Undefined_Handler, %function
Undefined_Handler:
    /* 执行到这里之前:
    * 1. lr_und 保存有被中断模式中的下一条即将执行的指令的地址
    * 2. SPSR_und 保存有被中断模式的CPSR
    * 3. CPSR 中的 M4-M0 被设置为 11011, 进入到 und 模式
    * 4. 跳到 0x4 的地方执行程序
    */

    /* 在 und 异常处理函数中有可能会修改 r0-r12, 所以先保存 */
    /* lr 是异常处理完后的返回地址, 也要保存 */
    stmdb sp!, {
    
    r0-r12, lr}

    /* 保存现场 */
    /* 处理 und 异常 */
    mrs r0, cpsr
    ldr r1, =und_string
    bl printException

    /* 恢复现场 */
    ldmia sp!, {
    
    r0-r12, pc}^  /* ^ 会把 spsr 的值恢复到 cpsr 里 */

und_string:
    .string "undefined instruction exception"

    .align 2
    .arm
    .weak SVC_Handler
    .type SVC_Handler, %function
SVC_Handler:
    /* 执行到这里之前:
    * 1. lr_svc 保存有被中断模式中的下一条即将执行的指令的地址
    * 2. SPSR_svc 保存有被中断模式的 CPSR
    * 3. CPSR 中的 M4-M0 被设置为 10011, 进入到 svc 模式
    * 4. 跳到 0x08 的地方执行程序
    */

    /* 保存现场 */
    /* 在 swi 异常处理函数中有可能会修改 r0-r12, 所以先保存 */
    /* lr 是异常处理完后的返回地址, 也要保存 */
    stmdb sp!, {
    
    r0-r12, lr}

.if 0
    mov r4, lr /* LR 由硬件自动保存软中断指令下一条指令的地址 */

    /* 处理swi异常 */
    mrs r0, cpsr
    ldr r1, =swi_string
    bl printException

    sub r0, r4, #4 /* LR 为硬件自动保存 SWI xxx 指令的下一条指令地址,LR – 4 就是 SWI 指令地址,由 SWI 指令编码知识可知,SWI 指令低 24 位保存有软中断号 */
    bl printSWIVal
.else
    ldr r4,[lr, #-4] /* LR 由硬件自动保存软中断指令下一条指令的地址,指令地址存放的是指令的操作码,将操作码取出,低 24 位置,即为软中断号 */
    bic r0, r4, #0xff000000 /* LR 为硬件自动保存 SWI xxx 指令的下一条指令地址,LR – 4 就是 SWI 指令地址,由 SWI 指令编码知识可知,SWI 指令低 24 位保存有软中断号 */
    push {
    
    r0}

    /* 处理swi异常 */
    mrs r0, cpsr
    ldr r1, =swi_string
    bl printException

    pop {
    
    r0}

    bl printSWIVal
.endif

    /* 恢复现场 */
    ldmia sp!, {
    
    r0-r12, pc}^  /* ^ 会把 spsr 的值恢复到 cpsr 里 */

swi_string:
    .string "swi exception"


    .align 2
    .arm
    .weak PrefAbort_Handler
    .type PrefAbort_Handler, %function
PrefAbort_Handler:
    ldr r0, =PrefAbort_Handler
    bx r0

    .align 2
    .arm
    .weak DataAbort_Handler
    .type DataAbort_Handler, %function
DataAbort_Handler:
    ldr r0, =DataAbort_Handler
    bx r0

    .align 2
    .arm
    .weak IRQ_Handler
    .type IRQ_Handler, %function

IRQ_Handler:
    push {
    
    lr}                   /* 保存lr地址 */
    push {
    
    r0-r3, r12}           /* 保存r0-r3,r12寄存器 */

    mrs r0, spsr                /* 读取spsr寄存器 */
    push {
    
    r0}                   /* 保存spsr寄存器 */

.if 0 /* it always return 0, i don't know why */
    mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中
                                * 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49
                                * Cortex-A7 Technical ReferenceManua.pdf P68 P138
                                */
.else
    ldr r1, =0x00a00000
.endif
    add r1, r1, #0X2000         /* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */
    ldr r0, [r1, #0XC]          /* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,
                                    * GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据
                                    * 这个中断号来绝对调用哪个中断服务函数
                                    */
    push {
    
    r0, r1}               /* 保存r0,r1 */

    cps #0x13                   /* 进入SVC模式,允许其他中断再次进去 */

    push {
    
    lr}                   /* 保存SVC模式的lr寄存器 */
    ldr r2, =system_irqhandler	/* 加载C语言中断处理函数到r2寄存器中*/
    blx r2                      /* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */

    pop {
    
    lr}                    /* 执行完C语言中断服务函数,lr出栈 */
    cps #0x12                   /* 进入IRQ模式 */
    pop {
    
    r0, r1}
    str r0, [r1, #0X10]         /* 中断执行完成,写EOIR */

    pop {
    
    r0}
    msr spsr_cxsf, r0           /* 恢复spsr */

    pop {
    
    r0-r3, r12}            /* r0-r3,r12出栈 */
    pop {
    
    lr}                    /* lr出栈 */
    subs pc, lr, #4             /* 将lr-4赋给pc */


    .align 2
    .arm
    .weak FIQ_Handler
    .type FIQ_Handler, %function
FIQ_Handler:
    ldr r0, =FIQ_Handler
    bx r0


    .end

Guess you like

Origin blog.csdn.net/tyustli/article/details/131463041