Comprehensive Rust 课程实战:AArch64 裸机异常向量表与 Rust 异常处理实现

发布时间:2026/11/10 9:11:42

Comprehensive Rust 课程实战:AArch64 裸机异常向量表与 Rust 异常处理实现 Comprehensive Rust 课程实战AArch64 裸机异常向量表与 Rust 异常处理实现【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust导读在 Comprehensive Rust 课程Google Android 团队使用的 Rust 教学项目的 Application Processors应用处理器专题中exceptions.md讲解了一个核心裸机知识点如何在 AArch64 架构上为 EL1 异常级别搭建 16 条目的异常向量表并用汇编保存易失寄存器后进入 Rust 代码执行处理逻辑。本文以该文档为骨架结合仓库中src/bare-metal/aps/examples/的完整源码exceptions.rs、exceptions.S、entry.S等系统讲解 AArch64 异常模型、汇编宏的寄存器保存/恢复布局、向量表内存对齐规则、启动时代码设置VBAR_EL1的过程以及aarch64-rtcrate 提供的 trait 抽象方案帮助读者完整掌握在无操作系统环境下用 Rust 汇编处理异常的实战方法。一、背景为什么 AArch64 裸机程序需要自建异常向量表在微控制器如 Arm Cortex-M 系列中异常处理通常由厂商提供的启动文件或中断向量表机制直接承接。而应用处理器Application Processor场景下——课程使用 QEMU 的 aarch64virt开发板采用-machine virt -cpu max参数模拟——我们像编写操作系统一样直接面对硬件异常向量表必须由我们自己放在正确的地址并把处理逻辑接进 Rust。AArch64 架构定义了一张包含16 个条目的异常向量表结构为4 种异常类型 × 4 种来源状态来源状态 \ 异常类型Synchronous同步异常IRQFIQSError当前 EL使用 SP0✓✓✓✓当前 EL使用 SPxSP_ELx✓✓✓✓低 ELAArch64 状态✓✓✓✓低 ELAArch32 状态✓✓✓✓其中Synchronous同步异常包括未定义指令、系统调用SVC/HVC/SMC、数据中止、指令中止、栈对齐错误等IRQ / FIQ中断请求与快速中断请求属于异步异常SError系统错误属于异步异常。向量表条目之间以0x80128 字节为步长对齐这正是exceptions.S中连续出现的.balign 0x80指令的作用。整个向量表本身还必须 2KB0x800对齐对应汇编中的.balign 0x800。课程文档明确指出本次下午练习的所有示例都运行在EL1Exception Level 1即内核/OS 所在特权级。详见 src/bare-metal/aps/examples/src/exceptions.S。二、Rust 侧8 个extern C处理函数异常向量表用汇编编写但真正的处理逻辑放在 Rust 中。仓库中的 src/bare-metal/aps/examples/src/exceptions.rs 定义了与 16 个向量表条目一一对应的 8 个处理函数每条汇编入口跳转到同一个 Rust 函数故 16 条目只需 8 个函数use log::error; use smccc::Hvc; use smccc::psci::system_off; // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern C fn sync_current(_elr: u64, _spsr: u64) { error!(sync_current); system_off::Hvc().unwrap(); } #[unsafe(no_mangle)] extern C fn irq_current(_elr: u64, _spsr: u64) { error!(irq_current); system_off::Hvc().unwrap(); } // ...fiq_current、serror_current、sync_lower、irq_lower、fiq_lower、serror_lower 同构关键设计要点#[unsafe(no_mangle)]extern C汇编通过bl带链接分支直接调用这些函数因此必须使用 C ABI 并保留符号原名保证汇编侧符号解析成功。源码注释中每条都标注了// SAFETY: There is no other global function of this name.即安全前提是仓库内不存在同名的其他全局函数no_mangle不会导致符号冲突。两个形参_elr: u64与_spsr: u64汇编侧在调用前会把elr_el1异常返回地址寄存器和spsr_el1保存的程序状态寄存器读入x0、x1恰好作为 C ABI 下函数的第一、第二个参数。这样 Rust 处理函数可以直接拿到异常发生时在哪条指令、处于什么 PSTATE为后续恢复/诊断提供依据。处理策略记录日志并关机每个处理函数都调用log::error!输出异常类型名随后调用system_off::Hvc().unwrap()通过 PSCI 固件接口以 HVC 调用方式关闭系统。课程文档说明这样做的原因示例中不预期任何异常真正发生一旦发生只记录并停机便于在 QEMU 串口上看到是哪一类异常被触发。三、汇编侧保存易失寄存器并转入 Rust汇编实现位于 src/bare-metal/aps/examples/src/exceptions.S核心思路是用两条汇编宏分别完成保存易失寄存器到栈与从栈恢复中间插入一次对 Rust 处理函数的调用。3.1save_volatile_to_stack14 条指令的现场保存.macro save_volatile_to_stack /* Reserve stack space and save registers x0-x18, x29 x30. */ stp x0, x1, [sp, #-(8 * 24)]! stp x2, x3, [sp, #8 * 2] stp x4, x5, [sp, #8 * 4] stp x6, x7, [sp, #8 * 6] stp x8, x9, [sp, #8 * 8] stp x10, x11, [sp, #8 * 10] stp x12, x13, [sp, #8 * 12] stp x14, x15, [sp, #8 * 14] stp x16, x17, [sp, #8 * 16] str x18, [sp, #8 * 18] stp x29, x30, [sp, #8 * 20] /* Save elr_el1 spsr_el1... */ mrs x0, elr_el1 mrs x1, spsr_el1 stp x0, x1, [sp, #8 * 22] .endm要点一次性压栈 24 个 8 字节槽位共 192 字节覆盖x0–x18、x29帧指针、x30链接寄存器以及elr_el1、spsr_el1同时保存elr_el1/spsr_el1的目的见源码注释是支持嵌套异常发生时仍能正确回溯unwind宏末尾把elr_el1、spsr_el1分别装入x0、x1正好满足 Rust 处理函数(elr, spsr)的参数约定。3.2restore_volatile_from_stack对称恢复.macro restore_volatile_from_stack ldp x2, x3, [sp, #8 * 2] /* ... 依次恢复 x2-x18, x29, x30 ... */ ldp x0, x1, [sp, #8 * 22] msr elr_el1, x0 msr spsr_el1, x1 ldp x0, x1, [sp], #8 * 24 .endm恢复时先还原通用寄存器再把栈上保存的elr_el1、spsr_el1写回系统寄存器用x0/x1作临时寄存器最后恢复x0/x1并释放 192 字节栈空间。3.3current_exception组装完整处理流程.macro current_exception handler:req save_volatile_to_stack bl \handler restore_volatile_from_stack eret .endm保存现场 → 调用 Rust 处理函数 → 恢复现场 →eret返回被中断的执行流。值得注意的工程细节源码注释有明确计算保存现场 跳转共 15 条指令恢复 返回共 15 条指令整个 handler 恰好 30 条指令能塞进 128 字节32 条指令的向量表条目配额内——这正是把保存/恢复提取为宏而不是逐条展开的根本原因。3.4 16 条目向量表.section .text.vector_table_el1, ax .global vector_table_el1 .balign 0x800 vector_table_el1: sync_cur_sp0: current_exception sync_current .balign 0x80 irq_cur_sp0: current_exception irq_current /* ... 依序排列到 serr_lower_32 ... */16 个条目的排布严格遵循 AArch64 规范顺序当前 ELSP0四类 → 当前 ELSPx四类 → 低 EL AArch64 四类 → 低 EL AArch32 四类。课程文档同时说明了两处刻意简化不区分 SP0 与 SPx当前 EL 的两组异常共用一个处理函数不区分 AArch32 与 AArch64 低 EL 异常。四、启动流程把向量表地址写进VBAR_EL1有了向量表还不够——CPU 必须知道它在哪里。这一职责由启动汇编 src/bare-metal/aps/examples/src/entry.S 承担在跳转 Rustmain之前完成/* Prepare the stack. */ adr_l x30, boot_stack_end mov sp, x30 /* Set up exception vector. */ adr x30, vector_table_el1 msr vbar_el1, x30 /* Call into Rust code. */ bl mainVBAR_EL1Vector Base Address Register持有异常向量表基地址entry.S注释总结了整个入口的职责链配置 MMU 恒等映射并开启缓存 → 开启浮点 → 清零 bss → 准备栈 → 设置异常向量 → 跳转 Rustmain。同时entry.S明确提到入口会保留x0–x3给 Rust 入口点因为它们可能携带引导参数这正是main_rt.rs中fn main(x0: u64, x1: u64, x2: u64, x3: u64)四个参数的来源。五、把异常处理看作线程Send/Sync的考量课程文档给出一条非常实用的心智模型可以把异常处理函数与主执行上下文近似地视为不同的线程。因此想在两者之间共享的数据其类型必须满足相应的Send/Sync约束与线程间共享数据完全同理例如某值只Send而不Sync可移动所有权但不可多线程同时引用那么在异常处理函数与程序其他部分之间共享它时就需要用Mutex之类的互斥原语包裹后放入static静态变量中。这一节对应课程 Send 和 Sync 专题的内容在裸机环境下同样是编写安全并发/中断代码的基本功。特别要注意Send/Sync是unsafe trait除非数据确实满足共享条件否则不能随意实现。六、进阶抽象用aarch64-rtcrate 免除手写汇编手写 16 条目向量表属于从零做起的教学路线。课程还提供了另一条生产级路线aarch64-rtcrate 的 trait 抽象对应文档 src/bare-metal/aps/aarch64-rt/exceptions.md 与示例 src/bare-metal/aps/examples/src/exceptions_rt.rsuse aarch64_rt::{ExceptionHandlers, RegisterStateRef, exception_handlers}; struct Handlers; impl ExceptionHandlers for Handlers { extern C fn sync_current(_state: RegisterStateRef) { error!(sync_current); system_off::Hvc().unwrap(); } extern C fn irq_current(_state: RegisterStateRef) { error!(irq_current); system_off::Hvc().unwrap(); } // ... } exception_handlers!(Handlers);核心机制结合 src/bare-metal/aps/aarch64-rt/exceptions.md 的说明ExceptionHandlerstrait每个方法都有默认实现默认直接 panic因此不期望发生的异常可以干脆不重写对应方法代码更精简exception_handlers!宏编译期生成global_asm!块内含与手写版exceptions.S同构的异常向量表自动把汇编侧的栈帧接到 Rust 方法上RegisterStateRef包装异常发生时汇编保存寄存器栈帧的引用可用于从低 EL 的 SMC/HVC 调用中提取参数或在处理函数返回时更新将被恢复的寄存器值——为编写虚拟化/固件场景如 SMC/HVC 转发提供了便捷入口。对应的可运行程序是 src/bare-metal/aps/examples/src/main_rt.rs#![no_main] #![no_std]通过initial_pagetable!建立恒等页表0x900_0000处 PL011 UART 设备内存、普通内存与 256 GiB 处的设备内存entry!(main)注入启动入口main中通过Uart打印x0–x3引导参数后system_off。该二进制对应Cargo.toml中的rtbin 目标。七、构建与运行QEMU 上验证异常处理示例工程的构建与运行命令集中在 src/bare-metal/aps/examples/Makefilert.bin: build cargo objcopy --bin rt -- -O binary $ qemu_rt: rt.bin qemu-system-aarch64 -machine virt -cpu max -serial mon:stdio -display none -kernel $ -s流程为cargo build编译工程为 workspace依赖见 src/bare-metal/aps/examples/Cargo.toml含aarch64-paging、aarch64-rt、smccc、log等→cargo objcopy --bin rt -- -O binary生成裸二进制 → QEMU 以virt机器、maxCPU、串口直连终端的方式加载-kernel。运行后可在串口观察到main(0x0, 0x0, ...)之类的输出若程序运行中触发了异常则会看到对应处理函数打出的sync_current/irq_current等日志并触发关机——这就是验证向量表工作与否的直接手段。八、总结从课程文档 src/bare-metal/aps/exceptions.md 出发本文完整还原了 AArch64 EL1 裸机异常处理的四条主线异常模型16 条目向量表 4 种异常类型 × 4 种来源状态条目 128 字节对齐、整表 2KB 对齐汇编现场保护用save_volatile_to_stack/restore_volatile_from_stack两条宏在 30 条指令内完成保存、调用 Rust、恢复、eret的完整闭环并传递elr_el1、spsr_el1给 Rust 侧启动接线entry.S在进入 Rust 前把向量表地址写入VBAR_EL1两种落地方式手写汇编 extern C函数或aarch64-rt的 trait 宏抽象无论哪种都要遵循Send/Sync视角看待异常处理函数即隐式线程的共享数据纪律。这套实现既是 Comprehensive Rust 课程应用处理器专题的教学核心也是一份可以直接参考的裸机中断/异常处理工程范式。【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/11/8 23:00:57

流程定时启动全解析:触发原理、配置要点与运维实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/9 2:38:29

STM32三大隐性陷阱:时钟树、外设状态、开发环境脆弱性

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/9 15:54:14

CANN/GE模型查询信息创建接口

aclmdlBundleCreateQueryInfo 产品支持情况 产品 是否支持 Atlas A3 训练系列产品/Atlas A3 推理系列产品√ Atlas A2 训练系列产品/Atlas A2 推理系列产品√ 功能说明 创建aclmdlBundleQueryInfo类型的数据,表示模型描述信息。 如需销毁aclmdlBundleQueryInfo…

2026/11/10 9:07:35

电工证办理锦州怎么走官方报名入口?工地太忙根本没时间复习考试

电工证办理锦州怎么走官方报名入口?工地太忙根本没时间复习考试 工地太忙根本没时间复习考试,这几乎是每个电工从业者心里最真实的声音。尤其是像锦州这样的地区,工地项目多、工期紧,电工证办理锦州的流程、考试内容、政策更新,常常让人摸不着头脑。但别…

2026/11/10 9:07:35

湛江遂溪电工证代审内部消息:应届生必须知道的电工证报考真相

湛江遂溪电工证代审内部消息:应届生必须知道的电工证报考真相 你还在为找不到电工相关工作发愁?湛江遂溪电工证代审成了应届生找工作绕不开的门槛。别再等了,这可能是你最后的机会。今天就告诉你,为什么这个证在广东沿海地区特别吃香,以及你到底该怎么做…

2026/11/10 9:07:35

检测维修电工证官方回应实操考试怕挂科?别慌,这样准备稳过

检测维修电工证官方回应实操考试怕挂科?别慌,这样准备稳过 实操考试心里没底怕挂科?这几乎是每个准备考检测维修电工证的学员的共同焦虑。尤其是来自平顶山的中小型施工企业负责人,既要面对一线操作的高标准,又要应对频繁的复审和继续教育,稍有不慎就可…

2026/11/10 9:07:35

低学历也能考高级电工证的镗床图片?真实经历告诉你怎么破

低学历也能考高级电工证的镗床图片?真实经历告诉你怎么破 你是不是也担心自己学历不高,连高级电工证都报不了?我身边有个老电工,初中毕业,靠着这证在洛阳涧西区的施工队里干了七八年,现在工资都翻了三倍。今天我就从一个过来人的角度,把【高级电工证的…

2026/11/10 9:07:35

最新电工证培训内部消息:证书过期了不知道怎么复审延期?

最新电工证培训内部消息:证书过期了不知道怎么复审延期? 证书过期了不知道怎么复审延期?这个问题困扰了太多一线电工,尤其是像我这样从其他行业转岗过来的从业者。很多电工朋友因为不了解流程,导致证书过期,不仅影响工作,还可能面临法律责任。其实,…

2026/11/10 9:02:35

色弱电工证吗?别被中介骗了,这些费用和报考细节你得知道

色弱电工证吗?别被中介骗了,这些费用和报考细节你得知道 你是不是也像我一样,拿着电工证的报名资料,心里还在打鼓: 色弱电工证吗 ?报名流程复杂、费用模糊、怕被坑,这些问题像块大石头压得人喘不过气。别急,这篇文章就是给你支招,从 多少钱…

2026/11/10 8:12:38

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/10 0:01:28

学历不高怕报不上名?考电工证报名费+网上查询全攻略来了

学历不高怕报不上名?考电工证报名费+网上查询全攻略来了 你是不是也担心自己学历不高,考电工证报名费太贵,根本报不上名?别急,今天就来给你讲讲,怎么用最少的钱,把证拿到手,还能在工地上多挣点钱。 电工证报名费多少钱?网上查询是关键…

2026/11/10 0:01:29

绍兴考电工证培训机构怎么选?2026河南最新政策别错过

绍兴考电工证培训机构怎么选?2026河南最新政策别错过 怕考不过白交培训费?你不是一个人。2026河南最新政策出台,对电工证考试、报名、培训、复审都有明确规定。选择绍兴考电工证培训机构,别再踩坑,下面一步步教你避坑。…

2026/11/9 6:30:27

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/7 14:50:12

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑