文章目录
- 处理流程
- 示例
- 代码实现
- SWI
- 未定义指令
- 附录源码
处理流程
SWI 和未定义指令异常中断是由当前执行的指令自身产生的,当 SWI 和未定义指令异常中断产生时,程序计数器的 PC 的值还未更新,它指向当前指令后面第 2 条指令(对于 ARM 指令来说,它指向当前指令地址加 8 个字节的位置;对于 Thumb 指令来说,它指向当前指令地址加 4 个字节的位置)。
当 SWI 和未定义指令异常中断发生时,处理器将 PC-4 保存到异常模式下的 LR_mode 寄存器中。这时 LR_mode 中的值即为PC-4 ,即指向当前指令的第一条指令。因此返回操作直接将 LR_mode(指向当前指令的下一条指令) 的值赋给 PC 即可,可以通过下面的指令实现:
MOVS PC, LR // 注意 S ,指定了 S 意味着同时将SPSR 拷贝到 CPSR
该指令将寄存器 LR 中的值,复制到程序计数器 PC 中,实现程序返回,同时将 SPSR_mode 寄存器内容复制到当前程序计数器 CPSR 中。
当异常中断处理程序中使用了数据栈时,可以通过下面的指令在异常中断处理程序时保存被中断程序的执行现场,在退出异常中断处理程序时恢复被中断程序的执行现场。异常中断处理程序中使用的数据栈由用户提供。
stmdb sp!, {r0-r12, lr} // 保存现场 r0-r12 - reg_list
// user code
ldmia sp!, {r0-r12, pc}^ // 恢复现场 r0-r12 - reg_list
在上述指令中,reg_list 是异常中断处理程序中使用的寄存器列表。标识符 ^ 指示将 SPSR_mode 寄存器内容复制到 CPSR 寄存器中,该指令只能在特权模式下使用。
示例
- 假设在 instruction+0 指令执行完成后发生 swi/undef 异常
- 此时 PC 值 未更新还是指向 instruction+2
- 进入 swi/undef 异常后,LR_mode = PC -4,所以此时 LR 指向 instruction+1
- swi/undef 异常处理完成之后,程序需要返回到 instruction+1 处执行,所以将此时的 LR_mode 寄存器的值赋给 PC 即可
代码实现
SWI
异常处理程序
.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"
触发 swi
swi_code:
swi 0x123 /* 执行此命令, 触发SWI异常, 进入0x8执行 */
C 语言中将软中断号打印出来
void printSWIVal(unsigned int *pSWI)
{
swi_flag = 1;
printf("SWI val = 0x%x\r\n", *pSWI & ~0xff000000);
}
未定义指令
异常处理程序
.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"
触发未定义指令异常
und_code:
nop /* for debug */
.word 0xeeadc0de /* undefine instruction */
nop /* for debug */
.word 0xFFFFFFFF /* undefine instruction */
nop /* for debug */
ldr r0, =main
bx r0
C 语言中将异常字符串打印出来
void printException(unsigned int cpsr, char *str)
{
excep_flag = 1;
printf("Exception! cpsr is 0x%x\r\n", cpsr);
}
附录源码
.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