15 IMX6ULL裸机开发:程序示例(未定义指令异常和SVC异常)

创建时间:2022/1/20 22:11
更新时间:2022/1/21 14:34
作者:gi51wa2j
标签:100ask_IMX6ULL_v11, bingo, 操作, 正文

一、实战_未定义指令异常

undefined.zip
要想深入理解异常处理,需要写程序来验证。 本节课程故意执行一条SVC指令,让它触发异常。

参考资料:ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf

1.1 A7的异常向量表

从向量表可以看出,A7支持哪些异常:未定义指令、软中断(SVC)、预取指令中止、数据中止、IRQ、FIQ。
_start:     b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt                       /*cortex的A7中称为SVC*/ ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq

1.2 什么是未定义指令

    未定义指令,即使"还没有定义的指令",也就是CPU不认识的指令。很多时候,我们故意在代码里插入一些伪造的指令,故意让CPU执行到它时触发错误。这在调试时很有用,比如想打断点:怎么实现呢?
    有很多种方法:硬件监视点(watch point,数量有限)、软件断点(数量无限)。
    软件断点就是使用未定义指令来实现的,比如想让程序执行到某个地址A时停下来,可以这样做:

本节教程并不打算制作调试器,这里只是讲述一下未定义指令的作用,使用它来深入理解异常处理流程。

1.4 编程过程

1.4.1 在汇编代码里插入未定义指令

在代码中插入:
.word  0xffffffff

1.4.2 设置异常向量表基地址

MRC p15, 0, <Rt>, c12, c0, 0 ; Read VBAR into Rt MCR p15, 0, <Rt>, c12, c0, 0 ; Write Rt to VBAR

1.4.3 设置未定义的栈

本次实验代码建立在前几节代码的重定位基础上,这里设置异常部分程序使用的栈空间

1.4.4 保存现场

这里可以重温一下上一节笔记中的2.3小节
14 IMX6ULL裸机开发:异常处理(基础概念)
stmdb sp!, {R0-R3,R12,LR}

1.4.5 处理异常

这里我们新建一个C文件,里面写一些调试信息,然后使用该命令跳转去执行
bl do_undefined_c

1.4.6 恢复现场

ldmia sp!, {R0-R3,R12,PC}^

1.4.7 总结

//#define STACK_BASE (0xc0000000 + 0x100000)  // stm32mp157 #define STACK_BASE (0x80000000 + 0x100000)  // imx6ull #define STACK_SIZE (2048) .text .global  _start _start:     b reset ldr pc, =do_undefined .word 0  // ldr pc, _software_interrupt .word 0  // ldr pc, _prefetch_abort .word 0  // ldr pc, _data_abort .word 0  // ldr pc, _not_used .word 0  // ldr pc, _irq .word 0  // ldr pc, _fiq reset: /* 设置sp */ /* 对于STM32MP157设置链接地址为0xC0200000, 对于IMX6ULL设为0x80200000 */ ldr sp, =STACK_BASE adr r0, _start bl SystemInit bl uart_init /* 设置异常向量表基地址 : VBAR */ ldr r0, =_start mcr p15, 0, r0, c12, c0, 0 .word 0xffffffff /* 调用main函数 */ //bl main ldr pc, =main do_undefined: /* 设置SP_und */ ldr sp, =STACK_BASE - STACK_SIZE /* 保存现场 */ stmdb sp!, {R0-R3,R12,LR} /* 调用处理函数 */ bl do_undefined_c /* 恢复现场 */ ldmia sp!, {R0-R3,R12,PC}^

二、实战_SVC异常

02_svc.zip

2.1 什么是SVC异常

     简单地说就是执行SVC这条汇编指令时就会触发这个异常,CPU就会跳转过执行SVC异常向量的代码。

     supervisor call(SVC)通常在用户模式下使用,这使得用户模式的代码能够访问OS功能。例如,如果用户代码想要访问系统的特权部分(例如执行文件I / O),则通常将使用SVC指令执行此操作。在Linux中对文件的open/read/write等APP层的系统函数,它的本质都是执行SVC指令,从而进入Linux内核中预设的SVC异常处理函数,在内核里操作文件。

可以使用寄存器或者操作码中某个字段将参数传递给SVC处理程序。

发生异常时,异常处理程序可能必须确定内核是处于ARM还是Thumb状态。

特别是SVC处理程序,可能必须读取指令集状态。这是通过检查SPSR T位完成的。该位设置为Thumb状态,清除为ARM状态。

     ARM和Thumb指令集都具有SVC指令。从Thumb状态调用SVC时,必须考虑以下因素:

•指令地址位于LR-2,而不是LR-4;

•指令本身是16位的,因此需要半字加载;

• SVC编号为8位而不是ARM状态下的24位。

注意:ARM9等比较老的芯片里,这个异常是SWI异常,对应的指令是SWI。


本节课程不讲解SVC在内核中的使用,我们只是看看如何处理SVC触发的异常。