LV04-07-中断与异常-02-异常处理实例

本文主要是中断与异常——Cortex A7异常处理实例的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows版本 windows11
Ubuntu版本 Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
Linux开发板 正点原子 i.MX6ULL Linux 阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
Win32DiskImager Win32DiskImager v1.0
点击查看本文参考资料
分类 网址 说明
官方网站 https://www.arm.com/ ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档
https://www.nxp.com.cn/ NXP官方网站
https://www.nxpic.org.cn/NXP 官方社区
https://u-boot.readthedocs.io/en/latest/u-boot官网
https://www.kernel.org/linux内核官网
点击查看相关文件下载
分类 网址 说明
NXP https://github.com/nxp-imx NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库
https://elixir.bootlin.com/linux/latest/source 在线阅读linux kernel源码
nxp-imx/linux-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga NXP linux内核仓库tags中的rel_imx_4.1.15_2.1.0_ga
nxp-imx/uboot-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga NXP u-boot仓库tags中的rel_imx_4.1.15_2.1.0_ga
I.MX6ULL i.MX 6ULL Applications Processors for Industrial Products I.MX6ULL 芯片手册(datasheet,可以在线查看)
i.MX 6ULL Applications ProcessorReference Manual I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网)
ARM Cortex-A7 MPCore Technical Reference Manual Cortex-A7 MPCore技术参考手册
ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition ARM架构参考手册ARMv7-A和ARMv7-R版
Arm Generic Interrupt Controller Architecture Specification- version 3 and version 4 Arm通用中断控制器架构规范-版本3和版本4
ARM Generic Interrupt Controller Architecture Specification - Version 2.0 Arm通用中断控制器架构规范-版本2.0
ARM Cortex-A Series Programmer's Guide for ARMv7-A Cortex-A系列ARMv7-A编程指南

一、SVC异常

1. 软中断指令

前边其实有学习过SWI指令,我们可以通过这个指令,触发软中断,从而进入SVC模式,一般语法如下:

1
SWI immed_24

immed_24是一个24位立即数,比如0x123些。注意:我们使用“ swi”指令,实际上我们可以在 .dis 中看到它被翻译为 SVC 指令。在老的芯片比如 S3C2440 中我们使用 SWI 指令,在IMX6ULL 中可以使用 SVC 代替它。

2. SVC异常处理

当使用SWI软中断的时候,就会产生SVC异常,进入这个模式,对应的CPSR的M[4:0]的值为10011,也就是0x13。关于SVC异常的汇编如下:

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
/* 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 4

在 SVC_Handler 里将 r0-r12 和 lr 保存在 SVC 模式的栈上,然后将 lr 的值移动到 R4,调用 printException 函数打印出当前的 CPSR 值,和产生异常的原因。将 R4 减去 4,赋值给 R0,这是 swi 指令所在的地址,然后调用printSWIVal 函数打印出 swi 指令的参数。最后将 r0-r12 从栈上恢复, lr 从栈上弹出到 PC,并同时将 SPSR 恢复到 CPSR,从而返回去执行 swi 指令的下一条指令。

3. 设置堆栈

注意一定要设置堆栈,否则进入对应的异常后,堆栈可能会异常。

1
2
3
4
5
6
/* 进入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 */

4. 完整实例

完整的实例及修改可以看这里:08_EXCEPTION/SWI · sumumm/imx6ull-bare-demo - 码云 - 开源中国 (gitee.com)

5. 测试结果

我们可以在终端看到以下打印信息:

image-20240118215617370

二、未定义异常

1. 怎么产生未定义异常?

前边已经学习过了,就是 CPU 或协处理器不认识这条指令,执行这样的指令时就会产生“未定义指令异常”。 我们可以这样产生一个未定义异常:

1
2
.word 0xeeadc0de  /* undefine instruction */
.word 0xFFFFFFFF

上边两条都可以产生未定义异常。

2. 未定义异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 4

在 Undefined_Handler 里将 r0-r12 和 lr 保存在 und 模式的栈上,然后调用 printException 打印当前的 CPSR 值,并打印一个字符串。最后将 r0-r12从栈上恢复, lr 从栈上弹出到 PC,并同时将 SPSR 恢复到 CPSR,从而返回去执行出现未定义异常指令的下一条指令。

3. 设置堆栈

1
2
3
4
5
6
/* 进入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 */

4. 完整实例

完整的实例和修改的地方可以看这里:08_EXCEPTION/UNDEF · sumumm/imx6ull-bare-demo - 码云 - 开源中国 (gitee.com)

5. 测试结果

最终我们可以在终端看到以下打印信息:

image-20240118221514998