LV010-异常处理示例
一、SVC 异常
1. 软中断指令
前边其实有学习过 SWI 指令,我们可以通过这个指令,触发软中断,从而进入 SVC 模式,一般语法如下:
SWI immed_24immed_24 是一个 24 位立即数,比如 0x123 些。注意:我们使用“ swi”指令,实际上我们可以在 .dis 中看到它被翻译为 SVC 指令。在老的芯片比如 S3C2440 中我们使用 SWI 指令,在 IMX6ULL 中可以使用 SVC 代替它。
2. SVC 异常处理
当使用 SWI 软中断的时候,就会产生 SVC 异常,进入这个模式,对应的 CPSR 的 M[4:0]的值为 10011,也就是 0x13。关于 SVC 异常的汇编如下:
/* SVC中断 */
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}
mov r4, lr
/* 处理swi异常 */
mrs r0, cpsr
ldr r1, =swi_string
bl printException
sub r0, r4, #4
bl printSWIVal
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
swi_string:
.string "swi exception"
.align 42
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
在 SVC_Handler 里将 r0-r12 和 lr 保存在 SVC 模式的栈上,然后将 lr 的值移动到 R4,调用 printException 函数打印出当前的 CPSR 值,和产生异常的原因。将 R4 减去 4,赋值给 R0,这是 swi 指令所在的地址,然后调用 printSWIVal 函数打印出 swi 指令的参数。最后将 r0-r12 从栈上恢复, lr 从栈上弹出到 PC,并同时将 SPSR 恢复到 CPSR,从而返回去执行 swi 指令的下一条指令。
3. 设置堆栈
注意一定要设置堆栈,否则进入对应的异常后,堆栈可能会异常。
/* 进入SVC模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0X80200000 /* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */2
3
4
5
6
4. 完整实例
完整的实例及修改可以看这里:08_EXCEPTION/SWI · sumumm/imx6ull-bare-demo
5. 测试结果
我们可以在终端看到以下打印信息:
二、未定义异常
1. 怎么产生未定义异常?
前边已经学习过了,就是 CPU 或协处理器不认识这条指令,执行这样的指令时就会产生“未定义指令异常”。 我们可以这样产生一个未定义异常:
.word 0xeeadc0de /* undefine instruction */
.word 0xFFFFFFFF2
上边两条都可以产生未定义异常。
2. 未定义异常处理
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 42
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
在 Undefined_Handler 里将 r0-r12 和 lr 保存在 und 模式的栈上,然后调用 printException 打印当前的 CPSR 值,并打印一个字符串。最后将 r0-r12 从栈上恢复, lr 从栈上弹出到 PC,并同时将 SPSR 恢复到 CPSR,从而返回去执行出现未定义异常指令的下一条指令。
3. 设置堆栈
/* 进入undef模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x1B /* r0或上0x1B,表示使用undef模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0x80800000 /* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB */2
3
4
5
6
4. 完整实例
完整的实例和修改的地方可以看这里:08_EXCEPTION/UNDEF · sumumm/imx6ull-bare-demo
5. 测试结果
最终我们可以在终端看到以下打印信息: