Arm64体系结构简介

ARM简介

ARM版本 典型处理器 主要特性
v1 26位地址空间
v2 增加乘法、乘加法、支持协处理指令等
v3 地址空间扩展到32位,增加SPSP和CPSR等
v4 ARM7TDMI/ARM920T 增加Thumb指令等
v5 ARM926EJ-S 增加Jazelle和VFPv2等
v6 ARM11 MPCore 增加SIMD、TrustZone以及Thumb-2等
v7 Cortex-A8/Cortex-A9 增加NEON和VFPv3/v4扩展
v8 Cortex-A72 支持32位和64位指令集处理器体系结构
v9 Cortex-x2 支持可伸缩矢量扩展计算、机密计算体系结构
  • A系列:面向性能密集型系统的应用处理器内核。
  • R系列:面向实时应用的高性能内核。
  • M系列:面向各类嵌入式应用的微控制器内核。

Armv8体系结构

通用寄存器

AArch64状态下支持31个64位通用寄存器,分别是X0~X30,而AArch32状态支持16个32位通用寄存器。在AArch64状态下使用X表示64位寄存器,另外还可以使用W来表示32位的数据。X29(FP)是栈帧寄存器,指向栈的顶部(SP指向栈的当前位置),X30(LR)是链接寄存器,保持函数返回地址。

下图是AArch32状态下AArch64到AArch32状态的寄存器mapping。

处理器状态

Aarch64使用了PSTATE寄存器来表示当前处理器的状态(armv7使用的是CPSR),上图中I和F表示cpu本地中断的屏蔽位。SP为选择SP寄存器,在不同的异常等级下,SP寄存器是不一样的(见下小节),当运行在EL0是即为SP_EL0。

特殊寄存器

除了31个通用的寄存器外,还有上面的特殊寄存器。不同特权模式下的寄存器有所差别。
- XZR/WZR:两个零寄存器。WZR是32位零位寄存器,XZR是64位零位寄存器。
- PC:指向当前运行指令的下一条指令地址。
- SP:每个异常等级都有一个专门的SP寄存器SP_ELn。高等级的特权模式可以访问低等级的SP寄存器。
- SPSR:备份程序状态寄存器,运行一个异常程序是,处理器的一些状态信息会保存在备份程序状态寄存器里,如会将处理器的PSTATE寄存器的值暂时保存到SPSR里,当处理完成返回时,再把SPSR的值恢复到PSTATE寄存器。
- ELR:存放异常返回地址。

异常

异常类型

  • 中断
  • 中止
  • 复位
  • 系统调用

异常等级

Armv8支持aarch64和aarch32两种执行状态,aarch64是64位执行状态,运行A64指令集;aarch32是兼容armv7架构32位执行状态,运行A32的指令集。aarch64有4中异常等级。

  • EL0:用户特权,用于运行普通的用户程序。
  • EL1:系统特权,用于操作系统内核。如果使能虚拟化,则运行虚拟机操作系统内核。
  • EL2:运行虚拟化扩展的虚拟机监控器(hypervisor)。
  • EL3:运行安全os下的监控器(secure monitor)。

除了EL0以外,每个异常等级都有两个选择,选择自己等级或使用EL0等级,如果选用自己等级SP为SP_ELn(SP_ELnH),如果使用EL0那么SP为SP_EL0(SP_ELnt)。
ARMv8有4种以上等级,只有在获取异常或异常返回发生等级的变化。当处理器从较高等级切换到较低等级时,执行状态可以不变或者可以从aarch64切换到aarch32。当处理器从较低等级切换到较高等级时,执行状态可以不变或从aarch32切换到aarch64。

中断发生硬件处理过程

当事件触发异常发生时,处理器硬件会自动的执行以下动作。
1. 进入异常:
(1)将PC保存到ELR_ELx
(2)PSTATE保存到SPSR_ELx,会关闭到cpu本地的IRQ和FIQ中断,防止中断嵌套
1. 退出异常:
(1)将ELR_ELx的数据保存到PC
(2)将SPSR_ELx的数据保存到PSTATE,打开中断。

以上过程都是硬件自动完成。硬件处理完成后,就进入程序处理阶段,分为三个阶段保存上下文、中断处理、恢复上下文。

  • 上下文:进入中断后,需要将打断的应用当前的相关寄存器保存起来,一般是存储在当前任务内核堆栈里面,当中断退出时,再从堆栈中获取恢复。
  • 中断处理:包括确定中断源,将触发的中断源屏蔽,接着再处理中断服务程序。

异常向量表

异常发生时,处理器会跳转到相应位置执行异常相关指令。异常相关的处理指令通常存储在内存种,这个存储位置为异常向量,在ARMv8体系结构中,EL1、EL2、EL3都有一个异常向量表,每个异常向量表存储的基地址在VBAR_ELx寄存器中,EL0没有异常向量表,那是因为EL0的异常发生在用户态,当用户态发生发生异常时系统会跃迁到内核态EL1处理,所以EL0没有异常向量表,也可以认为EL0和EL1是共用EL1的异常向量表。

下图是一张异常向量表的示例,可以看到异常向量表分为4组,每组有4种类型。

异常向量表为什么分为4组?
(1)当系统的发生异常处于内核态,异常等级为ELx(EL1/EL2/EL3)时,可以选择使用SP_EL0或SP_ELx来作为栈指针,所以分为Current EL with SP0和Current EL with SPx两组。目前对于arm64的嵌入式设备来说,通常只有到EL1等级,EL2用于虚拟化,EL3用于安全基本,所以EL2和EL3基本没有用。
(2)当系统的发生在用户态(EL0),会下陷到内核变为EL1,但是会根据执行状态是aarch64或者aarch32来区分两组,Lower EL using AArch64或Lower EL using AArch32。

arch/arm64/kernel/entry.S

SYM_CODE_START(vectors)
    #内核态发生异常,异常等级为EL1,但是栈依旧使用的是SP_EL0
    kernel_ventry   1, t, 64, sync      // Synchronous EL1t
    kernel_ventry   1, t, 64, irq       // IRQ EL1t
    kernel_ventry   1, t, 64, fiq       // FIQ EL1h
    kernel_ventry   1, t, 64, error     // Error EL1t
    #内核态发生异常,异常等级为EL1,但是栈使用是SP_EL1
    kernel_ventry   1, h, 64, sync      // Synchronous EL1h
    kernel_ventry   1, h, 64, irq       // IRQ EL1h
    kernel_ventry   1, h, 64, fiq       // FIQ EL1h
    kernel_ventry   1, h, 64, error     // Error EL1h
    #用户态发生异常,异常等级为EL0(切到内核会转为EL1),到内核后执行状态是aarch64
    kernel_ventry   0, t, 64, sync      // Synchronous 64-bit EL0
    kernel_ventry   0, t, 64, irq       // IRQ 64-bit EL0
    kernel_ventry   0, t, 64, fiq       // FIQ 64-bit EL0
    kernel_ventry   0, t, 64, error     // Error 64-bit EL0
    #用户态发生异常,异常等级为EL0(切到内核会转为EL1),到内核后执行状态是aarch32
    kernel_ventry   0, t, 32, sync      // Synchronous 32-bit EL0
    kernel_ventry   0, t, 32, irq       // IRQ 32-bit EL0
    kernel_ventry   0, t, 32, fiq       // FIQ 32-bit EL0
    kernel_ventry   0, t, 32, error     // Error 32-bit EL0
SYM_CODE_END(vectors)