飞腾CPU体系结构(三)

第三节 异常模型

3.1 异常的基本概念

由于内部或外部原因,CPU会中断当前指令流,在指定的地址上,开始执行新的指令流;整个过程分为三个阶段:

异常触发
权限级切换
异常响应

这个过程一般参考资料笼统地称之为中断。根据权限级模型,异常触发和异常返回是权限级切换的触发条件。

异常触发的权限级切换就可以完整描述为从源权限级到目标权限级的过渡过程:

异常触发时CPU所处的权限级为源权限级
在异常响应运行时CPU所处的为目标权限级

参考第二节权限级模型,源权限级可以等于但一定不能高于目标权限级,例如EL2可以响应EL0/1和EL2的异常;EL1可以响应EL0和EL1的异常。

我们首先要清楚哪些原因会导致在源权限级运行的CPU触发异常;根据触发异常的条件,异常通常分为同步异常和异步异常。

同步异常是指,异常触发与当前执行的指令有因果关系;
异步异常是指,异常触发与当前执行的指令无因果关系。

一般说来,源权限级和异常类型确定后,目标权限级也就确定了。某些CPU操作在源权限级下能不能触发异常,或者触发后的异常由哪个目标权限级来响应,这些也可以通过高权限级提前配置好。例如,权限级EL1下,可以通过配置系统控制寄存器SCTLR_EL1,来控制在EL0下某些高速缓冲指令,要么正常运行、要么触发进入EL1的同步异常。

3.2 同步异常

触发同步异常的常见原因有:

  1. CPU提供的专用自陷指令,用于软件主动触发同步异常;
  2. 缺页。在某个页面的虚拟地址上进行数据访问或指令读取时,该虚拟页面没有映射相应的物理页帧时,CPU会触发缺页异常;
  3. 对齐异常。某些RISC体系结构CPU有对齐要求,可能来自数据地址对齐要求,也可能来自指令地址对齐要求;
  4. 超出受限范围操作,即当CPU处于低权限级时,超出受限范围执行指令、访问寄存器或地址时,CPU也会触发相应的异常。

当同步异常触发后,飞腾CPU提供了三个异常类型寄存器ESR_ELx,其中x=1/2/3,用于进一步查明触发同步异常的原因。

3.3 系统调用指令

飞腾CPU提供了SVC、HVC和SMC三个专用的自陷指令。

  1. 在EL0权限级时,只能运行SVC指令触发异常进入EL1,不能运行HVC和SMC指令;
  2. HVC指令仅仅供非安全态EL1/EL2触发进入EL2的自陷异常。
  3. SVC指令的软件使用场景如下:Linux操作系统内核软件在权限级EL1上运行,应用软件和C库都在权限级EL0级上运行,C库使用SVC指令实现了系统调用的应用级C语言封装。
  4. HVC指令的软件使用场景如下:虚拟机管理工具软件,例如qemu,在权限级EL0上运行;该管理工具软件通过操作系统内核实现与虚拟机监控软件通信,例如,kvm内核模块实现了这两部分软件;最终实现虚拟机管理功能。

飞腾CPU体系结构(三)

SVC、HVC和SMC自陷指令

3.4 异步异常

有一类异步异常虽然和CPU执行指令无关,但是与CPU内部有关,例如CPU缓存块的奇偶校验错,片上网络传输故障等。这类异步异常我们称之为系统错误,ARMv8体系结构虽然定义了系统错误异常类型,但是还没有形成统一的规范。

我们经常提到一类异步异常就是由通用中断控制器GIC控制和触发的异常。例如当外部设备完成一次DMA数据传输或者DMA传输发生错误时,就会通过通用中断控制器GIC,最终触发CPU异常;在多核处理器中,某个CPU也会通过通用中断控制器GIC,最终触发另一个CPU异常。我们把这类异步异常又称为中断。

虽然GIC架构并不属于ARMv8体系结构定义的范畴,但ARMv8体系结构将这类的中断分为两类:

  1. 一类是常规IRQ中断;
  2. 另一类是快速FIQ中断。

当前Linux操作系统将GIC管理的所有中断全部定义为IRQ类型的中断。

3.5 通用中断控制器

目前GIC架构有4个版本,其中GICv3是目前服务器芯片的主流版本。通用中断控制器GIC主要包括分发器和CPU接口两部分。

全系统共享分发器,它会接收全系统的所有中断源,控制每个中断源的属性,并决定哪些中断应该路由给哪个CPU。分发器还会将中断源分为两个优先级组:普通IRQ和快速FIQ;这种优先级划分有一个基本的原则是:安全态EL3和EL1软件需要访问的外设,中断一般采用高优先级。
每个处理器独占私有的GIC CPU接口;CPU可以通过自己的GIC CPU接口,控制中断状态。软件可以通过该接口获取硬件中断号,中断有4种类型:SGI、PPI、SPI和LPI。

  1. SGI中断号0~15,这类中断是软件写分发器的软件生成中断寄存器GICD_SGIR产生的,通常用于实现处理器之间的中断IPI。
  2. PPI中断号16~31,这类中断是每个处理器的私有组件产生的,例如定时器中断,每个处理器都有各自私有定时器,每个私有定时器使用相同的硬件中断号,只能给自己的处理器发中断。
  3. SPI中断号32~1020,这类中断是所有处理器共享的外设中断,这类中断一般是边沿触发或电平触发的。
  4. LPI是基于消息的中断。

GIC架构并不属于ARMv8体系结构定义的范畴;因此GIC的控制寄存器不属于CPU系统寄存器范畴,属于外设寄存器范畴。访问GIC寄存器和访问外设寄存器的方式相同,首先要将GIC寄存器的物理地址映射到Linux内核虚拟地址,然后进行读写操作。

  1. 所有处理器可以访问分发器的控制器寄存器;
  2. 每个处理器使用相同的物理地址,只能访问自己私有的GIC CPU接口寄存器。

3.6 异常响应向量表

一旦异常触发,权限级发生切换后,CPU进入目标权限级。无论目标权限级是否升高,还是不变,CPU都从指定的地址开始执行;这个指定的起始地址称为异常响应入口点。目标权限级一般会有多个异常响应入口点;多个异常响应入口点的集合就称为异常响应向量表。处理器一般会有一个专门的寄存器来保存异常响应向量表起始地址。因此当CPU切换进入目标权限级之前,先判断异常类型、源权限级和目标权限级,并在异常响应向量表中找到正确的入口点,然后跳转到该入口点执行。
除了EL0权限级,其他的权限级都具有响应异常能力;因此针对权限级EL1/2/3,飞腾CPU都有一个专门的异常响应向量表起始地址寄存器VBAR_ELx,指向异常响应向量表的起始地址。如果CPU开启了地址翻译功能,即MMU使能,这个起始地址为虚拟地址。
因此,权限级EL1/2/3,在内存中都有自己的异常响应向量表,组织结构都相同,参见下表。根据源权限级和目标权限级的情况,异常响应向量表分为四组,每一组都包含了同步异常、IRQ中断、FIQ中断和系统错误四个入口点,每入口点之间的长度为128字节,因为每条指令占用4字节,所以一共可以容纳32条指令。
飞腾CPU体系结构(三)异常响应向量表

  1. 前两组是源权限级和目标权限级相同,虽然异常触发了权限级切换,但是权限级并没有发生变化;这两组的之间主要区别在堆栈寄存器的选择不同,参见第四节内容。
  2. 后两组是源权限级比目标权限级低;这两组的之间主要区别是源权限级的CPU位宽,源权限级64位CPU位宽AArch64为一组,32位CPU位宽AArch32为一组。

当源权限级比目标权限级低,且CPU位宽64位的源权限级触发异常时,CPU位宽64位的目标权限级才能响应,而CPU位宽32位的目标权限级不能响应。当源权限级比目标权限级低,且CPU位宽32位的源权限级触发异常时,CPU位宽64位或32位的目标权限级都能响应。