编写 UART 驱动程序

QEMU “虚拟机”具有 PL011 UART,现在为其编写驱动程序。

  1. const FLAG_REGISTER_OFFSET: usize = 0x18;
  2. const FR_BUSY: u8 = 1 << 3;
  3. const FR_TXFF: u8 = 1 << 5;
  4. /// Minimal driver for a PL011 UART.
  5. #[derive(Debug)]
  6. pub struct Uart {
  7. base_address: *mut u8,
  8. }
  9. impl Uart {
  10. /// Constructs a new instance of the UART driver for a PL011 device at the
  11. /// given base address.
  12. ///
  13. /// # Safety
  14. ///
  15. /// The given base address must point to the 8 MMIO control registers of a
  16. /// PL011 device, which must be mapped into the address space of the process
  17. /// as device memory and not have any other aliases.
  18. pub unsafe fn new(base_address: *mut u8) -> Self {
  19. Self { base_address }
  20. }
  21. /// Writes a single byte to the UART.
  22. pub fn write_byte(&self, byte: u8) {
  23. // Wait until there is room in the TX buffer.
  24. while self.read_flag_register() & FR_TXFF != 0 {}
  25. // SAFETY: We know that the base address points to the control
  26. // registers of a PL011 device which is appropriately mapped.
  27. unsafe {
  28. // Write to the TX buffer.
  29. self.base_address.write_volatile(byte);
  30. }
  31. // Wait until the UART is no longer busy.
  32. while self.read_flag_register() & FR_BUSY != 0 {}
  33. }
  34. fn read_flag_register(&self) -> u8 {
  35. // SAFETY: We know that the base address points to the control
  36. // registers of a PL011 device which is appropriately mapped.
  37. unsafe { self.base_address.add(FLAG_REGISTER_OFFSET).read_volatile() }
  38. }
  39. }
  • 请注意,使用 Uart::new 方法不安全,而其他方法则安全。原因在于,只要 Uart::new 的调用方保证满足其安全要求(即所指定的 UART 只有一个驱动程序实例,且没有其他内容与其地址空间存在重叠),那么后续调用 write_byte 始终是安全的,因为我们假定需要满足的前提条件。
  • 我们也可以采用相反的方式(即确保 new 安全,但 write_byte 不安全),不过这样会很不方便,因为每当调用 write_byte 时都需要推断是否安全。
  • 这是安全地封装不安全代码时常见的策略:即在少数调用代码的地方进行安全验证,而不是在很多地方进行。