ARM 异常级别切换机制详解
一、异常级别层级图
二、切换方式总结表
| 方向 | 切换机制 | 触发方式 | 典型场景 |
|---|
| 低→高 | 异常进入 | 同步异常/异步异常 | 系统调用、中断、陷阱 |
| 高→低 | 异常返回 | ERET指令 | 返回用户态、返回Guest |
三、从低到高的切换方式
🔼 核心机制:异常进入
EL0 ──► EL1 : SVC指令 / 中断 / 数据异常 EL1 ──► EL2 : HVC指令 / 虚拟化陷阱 EL2 ──► EL3 : SMC指令 / 安全监控调用
具体触发方式:
| 异常类型 | 触发指令/事件 | 说明 |
|---|
| 同步异常 | SVC #imm | EL0→EL1 系统调用 |
HVC #imm | EL1→EL2 虚拟化管理调用 |
SMC #imm | EL2→EL3 安全监控调用 |
| 异步异常 | IRQ/FIQ | 外部中断触发 |
| SError | 系统错误(如总线错误) |
| 异常陷阱 | 配置陷阱 | 如访问敏感寄存器自动陷入更高EL |
切换时的硬件自动操作:
// 异常进入时硬件自动完成: SP_ELx ← 当前SP // 保存栈指针 ELR_ELx ← 返回地址 // 保存PC SPSR_ELx ← 当前PSTATE // 保存处理器状态 PSTATE.EL ← 目标EL // 切换到目标异常级别 PC ← VBAR_ELx + offset // 跳转到异常向量
四、从高到低的切换方式
🔽 核心机制:异常返回(ERET)
EL3 ──► EL2 : ERET 指令 EL2 ──► EL1 : ERET 指令 EL1 ──► EL0 : ERET 指令
ERET 指令的作用:
// ERET 执行时硬件自动完成: PC ← ELR_ELx // 恢复返回地址 PSTATE ← SPSR_ELx // 恢复处理器状态(包含目标EL) SP ← SP_ELx // 恢复栈指针(如果使用banked SP)
⚠️关键点:目标EL由SPSR_ELx.M[3:0]决定,可实现降级切换。
五、相互调用机制
调用关系图:
六、调用规则详解
❌ 高级别能否调用低级别代码?
不能直接调用!必须通过ERET返回。
| 尝试方式 | 结果 | 原因 |
|---|
直接跳转BR/BLR | ❌ 失败 | 权限检查失败 |
| 直接调用函数 | ❌ 失败 | EL不能主动降低 |
使用ERET | ✅ 成功 | 唯一合法方式 |
原因:处理器不允许主动降低特权级,防止恶意代码提权后降级执行恶意操作。
✅ 低级别能否调用高级别代码?
可以!通过异常/特殊指令。
| 调用方式 | 指令 | 场景 |
|---|
| 系统调用 | SVC #0 | EL0→EL1(如 read/write) |
| 虚拟化调用 | HVC #0 | EL1→EL2(如VM管理) |
| 安全调用 | SMC #0 | EL1/EL2→EL3(如TrustZone) |
| 中断触发 | 外部IRQ | 任意EL→更高EL |
七、完整切换流程示例
示例:用户态系统调用
// ===== EL0 (用户态) ===== mov x0, #1 // 参数 mov x1, #buffer // 参数 svc #0 // 触发系统调用,进入EL1 // ===== EL1 (内核态) ===== // 异常向量入口 kernel_handler: stp x29, x30, [sp, #-16]! // 保存寄存器 bl sys_write // 执行系统调用 ldp x29, x30, [sp], #16 // 恢复寄存器 eret // 返回EL0 // ===== 回到 EL0 ===== // 继续执行svc后面的指令
八、权限控制总结
核心原则:
| 原则 | 说明 |
|---|
| 只能提权,不能主动降权 | 防止权限滥用 |
| 降权必须通过ERET | 确保状态完整恢复 |
| 异常是唯一入口 | 所有特权切换都经过异常处理 |
| 硬件自动保存状态 | ELR/SPSR保证可恢复 |
九、实际应用场景
| 场景 | 切换路径 | 触发方式 |
|---|
| 应用调用内核 | EL0→EL1 | SVC |
| 内核调度用户进程 | EL1→EL0 | ERET |
| VM陷入Hypervisor | EL1→EL2 | 陷阱/HVC |
| 进入安全世界 | EL1→EL3 | SMC |
| 中断处理 | 任意EL→EL1 | IRQ |
这种设计确保了安全性和可控性,是现代处理器特权级管理的核心机制。