Accessing Hardware in Rust
摘要
Ferrous Systems 详述了在 Rust 中访问硬件的三种主要方式:I/O 端口、系统寄存器和内存映射 I/O,以及位字段操作的最佳实践。
核心内容
1. I/O 端口 (x86)
老式 x86 架构使用专用 IN/OUT 指令访问 I/O 空间。
use x86_64::instructions::port::Port;
unsafe {
let mut port = Port::new(0xf4);
port.write(exit_code as u32);
}
2. 系统寄存器 (Arm/RISC-V)
系统寄存器是处理器内部特殊功能的寄存器,需要特殊指令访问。
32-bit Arm (协处理器):
core::arch::asm!("mcr p15, 0, {r}, c0, c0, 5",
r = out(reg) value, options(nomem, nostack));
AArch64 (命名寄存器):
core::arch::asm!("mrs {r:w}, MPIDR_EL1",
r = out(reg) value, options(nomem, nostack));
RISC-V (CSR):
core::arch::asm!("csrrs {r}, 0xF14, x0",
r = out(reg) value, options(nomem, nostack));
3. 内存映射 I/O (MMIO)
现代系统将外设映射到内存地址空间。
重要提示: 永远不要对 MMIO 地址使用引用!Rust 编译器假设引用是可解引用的,会导致 LLVM 优化错误。
正确方式 - volatile 写入:
const UART0_TRANSMIT_FIFO: *mut u32 = 0xE020_5000 as *mut u32;
unsafe { UART0_TRANSMIT_FIFO.write_volatile(byte as u32); }
错误方式 - 引用 (Unsound!):
let uart_ref: &Uart = unsafe { &*(0xE020_5000 as *mut Uart) };
// 不要这样做!
4. 位字段操作
使用闭包 API 实现 Zero-Cost Abstraction:
impl Uart {
pub fn modify_ifls<F>(&mut self, f: F)
where F: FnOnce(&mut Uartifls) {
let mut value = self.read_ifls();
f(&mut value);
self.write_ifls(value);
}
}
关键工具
- svd2rust - 从 Arm SVD 文件生成 PAC
- tock-registers - Tock 操作系统的寄存器抽象
- safe-mmio + bitflags - 安全 MMIO 封装
- derive-mmio + bitbybit - 派生宏方案
评价
亮点:
- 实战经验总结
- 解释 volatile 和引用问题
- Zero-Cost Abstraction 示例
- 文档最佳实践
适用人群: 嵌入式 Rust 开发者、操作系统爱好者、硬件接口工程师