对ARM异常(Exceptions)的理解

kelly的头像
kelly 发布于:周四, 09/08/2016 - 14:57 ,关键词:

ARM支持7种异常。问题时发生了异常后ARM是如何响应的呢?下面一起来学习一下:

所有的系统引导程序前面中会有一段类似的代码,如下:

从中我们可以看出,ARM支持7种异常。问题时发生了异常后ARM是如何响应的呢?第一个复位异常很好理解,它放在0x0的位置,一上电就执行它,而且我们的程序总是从复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。那么怎么会执行到后面几个异常处理函数呢?

看看书后,明白了ARM对异常的响应过程,于是就能够回答以前的这个疑问。

当一个异常出现以后,ARM会自动执行以下几个步骤:

(1)把下一条指令的地址放到连接寄存器LR(通常是R14),这样就能够在处理异常返回时从正确的位置继续执行。

(2)将相应的CPSR(当前程序状态寄存器)复制到SPSR(备份的程序状态寄存器)中。从异常退出的时候,就可以由SPSR来恢复CPSR。

(3)根据异常类型,强制设置CPSR的运行模式位。

(4)强制PC(程序计数器)从相关异常向量地址取出下一条指令执行,从而跳转到相应的异常处理程序中。

至于这些异常类型各代表什么,我也没有深究。因为平常就关心reset了,也没有必要弄清楚。

ARM规定了异常向量的地址:

这样理解这段代码就非常简单了。碰到异常时,PC会被强制设置为对应的异常向量,从而跳转到相应的处理程序,然后再返回到主程序继续执行。

这些引导程序的中断向量,是仅供引导程序自己使用的,一旦引导程序引导Linux内核完毕后,会使用自己的中断向量。

嗬嗬,这又有问题了。比如,ARM发生中断(irq)的时候,总是会跑到0x18上执行啊。那Linux内核又怎么能使用自己的中断向量呢?原因在于Linux内核采用页式存储管理。开通MMU的页面映射以后,CPU所发出的地址就是虚拟地址而不是物理地址。就Linux内核而言,虚拟地址0x18经过映射以后的物理地址就是0xc000 0018。所以Linux把中断向量放到0xc000 0018就可以了。

另外,说一下MMU。说句实话,还不是很明白这个MMU机理。参加Intel培训的时候,李眈说了MMU的两个主要作用:

(1)安全性:规定访问权限

(2)提供地址空间:把不连续的空间转换成连续的。

第2点是不是实现页式存储的意思?

补充一下:

.globl _start ;系统复位位置

_start: b reset ;各个异常向量对应的跳转代码

ldr pc, _undefined_instruction ;未定义的指令异常

……

_undefined_instruction :

.word undefined_instruction

也许有人会有疑问,同样是跳转指令,为什么第一句用的是 b reset;

而后面的几个都是用ldr?

为了理解这个问题,我们以未定义的指令异常为例。

当发生了这个异常后,CPU总是跳转到0x4,这个地址是虚拟地址,它映射到哪个物理地址

取决于具体的映射。

ldr pc, _undefined_instruction

相对寻址,跳转到标号_undefined_instruction,然而真正的跳转地址其实是_undefined_instruction的内容——undefined_instruction。那句.word的相当于:

_undefined_instruction dw undefined_instruction (详见毕设笔记3)。

这个地址undefined_instruction到底有多远就难说了,也许和标号_undefined_instruction在同一个页面,也许在很远的地方。不过除了reset,其他的异常是MMU开始工作之后才可能发生的,因此undefined_instruction 的地址也经过了MMU的映射。

在刚加电的时候,CPU从0x0开始执行,MMU还没有开始工作,此时的虚拟地址和物理地址相同;另一方面,重启在MMU开始工作后也有可能发生,如果reset也用ldr就有问题了,因为这时候虚拟地址和物理地址完全不同。

因此,之所以reset用b,就是因为reset在MMU建立前后都有可能发生,而其他的异常只有在MMU建立之后才会发生。用b reset,reset子程序与reset向量在同一页面,这样就不会有问题(b是相对跳转的)。如果二者相距太远,那么编译器会报错的。

来源:互联网(版权归原著作者所有)

围观 528