arm64架构下的linux内核中断——学习笔记

资料来源:
https://github.com/carloscn/blog

异常等级(Exception level)

ARMv8包含4个异常等级,由privilege level整合而来:

  • EL0:普通用户的应用程序
  • EL1:典型的操作系统内核,这个等级被称为“特权等级”(privileged)
  • EL2:hypervisor
  • EL3:Low-level固件,例如安全监视器 Secure monitor,最高权限

ARMv8分为普通世界(normal world)和安全世界(secure world),两个世界通过硬件进行隔离,安全世界目的是保证数据和程序安全,例如指纹认证就在安全世界执行。secure monitor负责控制两个世界之间的相互访问。Hypervisor 是一种软件,它在物理服务器和操作系统之间创建一个抽象层,允许多个操作系统和应用程序共享一个物理服务器。Trusted OS,在EL1和Guest OS并行运行,提供一个runtime环境执行安全应用,如OPTEE是一个开源可信操作系统。
在这里插入图片描述
ARMv8定义了两个执行状态,aarch64和aarch32。这两个执行状态和异常等级没有概念上面的交织,也就是aarch64和aarch32都有相应的异常等级和特权模式。在aarch64执行状态时,使用A64指令,在aarch32执行状态,使用A32或者T32(Thumb)指令集。指令集版本分为四种:A64(固定32位长度指令集)、A32(固定32位长度指令集)、T32(可变32位长度指令集,即Thumb-2)、T16(Thumb)。本文主要关注aarch64架构,即64位。在AArch64中,已经没有User、SVC、ABT等处理器模式的概念,为了向下兼容,在AArch32里,就把这些处理器模式map到了4个Exception level。

ARM64处理器内部的中断分为两种,FIQ和IRQ,FIQ叫快速中断请求,IRQ就是普通的中断请求。FIQ的优先级高于IRQ。

在ARM64处理器中异常模式有以下规则:

  • 升权:向EL3方向变迁,视为升权。
  • 异常不能被带入比其低的异常等级。
  • 在EL0没有异常处理机制,异常处理必须在其更高的异常等级处理。
  • 异常会造成程序执行流改变。通常异常处理有一个向量表需要循序,而有特例可以不需要遵循异常向量表:
    • IRQ/FIQ
    • abort memory system
    • 未定义指令
    • 系统调用
    • traps EL1和EL2
  • 异常处理结束之后返回前一个异常等级需要执行ERET指令
  • ERET之后只能保留在本层或者是更低的层级。
  • 安全状态无法改变EL层级。

32位的应用程序可以运行在64位的上面,但这样的执行是有个条件的,也就是在执行32位程序的时候,必须让处理器处于AArch32的执行状态。为了实现这样的执行,处理器只能在更高的EL层级去执行32位的程序。首先,32位的应用程序在EL0产生一个SVC指令向supervisor call,接着会产生一个IRQ,这个IRQ就会切换到Aarch64内的EL1,等着程序运行完毕之后任务ERET到EL0。32/64混合的程序没办法在ARM64上运行,还有64位程序就没办法在32位系统上运行了。
在这里插入图片描述

ARMv8异常处理

在ARM的世界我们应该纠正一个概念,“中断”。通用的中断的定义就是可以阻止正常软件流程执行的中断。然而在ARM的术语里面,不能仅仅用中断来表示中断,我们需要更上一层的定义,异常(Exception)。异常包含狭义的中断,也包含条件变量(conditions)或者是系统事件(system event),这些行为都有专门的异常处理函数来处理。在ARCH64里面有三类异常,中断(interrupts),中止(aborts) 和复位(reset)。ARM架构使用GIC中断控制器控制中断,而x86使用APIC。

异常和中断类似都有个异常向量表。程序发生异常的时候,CPU会在这里插入图片描述
跳转到到更高层级的handler里面,查找异常向量表,找到本级异常的handler的地址,再跳转回去异常发生的层级。

中断(interrupts)

在AArch64体系架构中有两种类型的中断,IRQ和FIQ,FIQ的优先级高于IRQ。

  • 中断线(request line):这个是对处理器而言的,是中断控制器的输入。
  • 中断向量表(vector table):中断名单,操作系统实现,内部包含中断线对应的IRQ号。

对于触发中断,在CPU上有专门的动词术语,assert一个中断,take一个异常。中断线由外部外设assert,中断线的信号被接入到中断控制器,中断控制器根据中断优先级判断先发出哪些信号,再根据中断向量表查找到中断处理函数的地址。由于中断并非执行在给定时间内,因此中断属于异步异常

中止(aborts)

包含两个aborts,取指失败(Instruction Aborts)和访问数据失败(Data Aborts)。

在内存上,发生这种异常的场景可能有两种,在访问内存时,外部存储器返回一个错误,或者指定访问的地址没有关联到真实的内存中(MMU产生该错误,一个操作系统可以使用MMU abort动态的分配内存给应用程序)。

在指令上,aborts发生在取指还未执行的时候。data abort发生在存储和加载指令。

abort异常属于同步异常

复位(reset)

reset异常有着最高的异常等级。当异常发生的时候ARM处理器就会跳转到指令所在的位置。RVBAR_ELn包含跳转地址,n应该是该处理器的最高权限,armv8里面就应该为n=3。该异常不可屏蔽,不可禁止,并且尝尝执行在上电后的初始化的时候。

中断嵌套(中断抢占)

中断嵌套指中断系统正在执行一个中断服务L时,有另一个优先级更高的中断H触发,这时中断系统会暂时中止当前正在执行低优先级中断服务程序L,而去处理高级别中断H,待处理完毕,再返回被中断了的中断服务程序L继续执行。

ARM Core、GIC中断控制器、RTOS kernel支持中断嵌套,但Linux Kernel不支持。

GIC中断控制器的中断嵌套是可以配置的。分为Without preemption和With preemption两种模式:

  • 在Without preemption的情况下,高优先级的中断无法抢占正在active的中断,只能等active的中断执行完了、返回了,高优先级的中断才能发生"抢占";
  • With preemption,描述的其实就是:一个中断正在执行,然后另一个更高优先级的中断打断了它,这也就是正是我们所说的中断嵌套。

在Linux Kernel中,ICC_BPRn_EL1寄存器的配置,在GICv3架构中,ICC_BPRn_EL1用于设置二进制点,这影响中断的优先级分组和抢占行为:

/*
* Some firmwares hand over to the kernel with the BPR changed from
* its reset value(and with a value large enough to prevent
* any pre-emptive interrupts from working at all). Writing a zero
* to BPR restores is reset value.
*/
gic_write bpr1(0); // 抢占是开启的

针对每一个 INTID(中断号) 的priority的配置,如下所示,在gic初始化阶段,给每一个 INTID(中断号) 都配置成了一样的优先级。
在这里插入图片描述

异常处理相关寄存器

在ARMv8架构中,PSTATE(Program Status Register)是一个特殊的寄存器,用于保存当前的程序状态信息。异常涉及的是PSTATE DAIF的值,这部分是异常处理标志位。根据手册,如果异常发生,PSTATE信息会被存入到Saved program status register(SPSR_ELn),只有3个,SPSR_EL1, SPSR_EL2, SPSR_EL3,是三个不同的寄存器实体。

在这里插入图片描述

  • D: debug exceptions mask
  • A: SError interrupt process state mask,例如异步external abort
  • I:IRQ中断就处理状态掩码
  • F:FIQ中断处理状态掩码

当异常发生的时候,PSTATE寄存器的值(current EL, DAIF, NZCV etc)都会被复制到SPSR_ELn,返回的地址会被也会被存储到ELR_ELn中。每一个异常等级都有自己的栈寄存器SP_ELn,用于指示不同EL层级专用的栈。

在这里插入图片描述

  • Exception Syndrome Register (ESR_ELn):异常的具体原因。
  • Fault Address Register (FAR_ELn):提供发生同步指令错误、数据错误和对齐错误的虚拟地址。
  • Exception Link register (ELR_ELn):数据访问发生错误的指令的地址,这个寄存器通常在内存错误之后被写入,还有一种情况也会写入这个地址,就是访问非对齐的地址。

CurrentEL寄存器可以判断当前的异常等级。

同步和异步异常

异步和同步异常在ARM64处理器里面处理的方式不太一样,而且使用的寄存器还有寄存器的行为都是不一样的。对于同步的异常,回退地址总能包含发生异常的原因,然而对于异步的异常,回退地址可能会包含发生异常的原因。

异步的异常主要有三种:

  • IRQ中断
  • FIQ快速中断
  • SError系统错误,(最可能是异步数据错误Data Abort,从cache回写脏数据到外存)

同步的异常包含:

  • MMU的指令错误(执行标记为不可执行的内存)
  • MMU的数据错误(读取权限问题,对齐问题)
  • SP/PC对齐检查错误
  • 同步外部错误,读translation table
  • 无法识别的指令
  • 调试异常

EL0 -> EL1,SVC指令等。但EL0,是不允许直接call到EL2或者EL3的,必须通过EL0 call到EL1的os kernel,由kernel发送请求到EL2/3。
EL1 -> EL2,HVC指令call到hypervisor,或者通过SMC指令call到secure monitor。
EL2 -> EL3,SMC指令。

在这里插入图片描述
关于EL1 call进EL2/3的图,对于同步的异常而言,没什么好说的了,就是一级一级的call进去,但是对于异步的异常而言,就有点不一样了,我们EL1需要call EL2或者EL3的时候,需要配置HCR寄存器决定路由信息,同样的EL3也是需要路由信息的配置的。否则会产生新的异常。

异常向量表

采用基地址+offset的模式,ARMv8有三个表,VBAR_EL1, VBAR_EL2, VBAR_EL3。 高11位有效。以下是linux 6.10内核的entry.S文件中的异常向量表:

/*
 * Exception vectors.
 */
	.pushsection ".entry.text", "ax"

	.align	11
SYM_CODE_START(vectors)
	kernel_ventry	1, t, 64, sync		// Synchronous EL1t
	kernel_ventry	1, t, 64, irq		// IRQ EL1t
	kernel_ventry	1, t, 64, fiq		// FIQ EL1t
	kernel_ventry	1, t, 64, error		// Error EL1t

	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

	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

	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)

异常处理步骤

CPU需要保存一些异常相关的寄存器,切换到异常执行环境,接着跳转异常向量表:

  • S1: PSTATE保存到SPSR_ELx
  • S2: 返回地址保存到ELR_ELx
  • S3: PSTATE寄存器里面的DAIF域都设定为1(等同于关闭调试异常、SError,IRQ FIQ中断)
  • S4: 更新ESR_ELx寄存器(包含了同步异常发生的原因)
  • S5: SP执行SP_ELx
  • S6: 切换到对应的EL,接着跳转到异常向量表执行

操作系统需要找到异常,并执行异常处理程序:

  • 识别异常发生的类型
  • 跳转到合适的异常向量表(包含异常跳转函数)
  • 处理异常
  • 操作系统执行eret指令

在操作系统执行ERET指令时,CPU需要从ELR_ELx寄存器中恢复PC的指针;从SPSR_ELx寄存器恢复处理器状态。

  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值