Let’s write a UART driver
The QEMU ‘virt’ machine has a PL011 UART, so let’s write a driver for that.
const FLAG_REGISTER_OFFSET: usize = 0x18;const FR_BUSY: u8 = 1 << 3;const FR_TXFF: u8 = 1 << 5;/// Minimal driver for a PL011 UART.#[derive(Debug)]pub struct Uart {base_address: *mut u8,}impl Uart {/// Constructs a new instance of the UART driver for a PL011 device at the/// given base address.////// # Safety////// The given base address must point to the 8 MMIO control registers of a/// PL011 device, which must be mapped into the address space of the process/// as device memory and not have any other aliases.pub unsafe fn new(base_address: *mut u8) -> Self {Self { base_address }}/// Writes a single byte to the UART.pub fn write_byte(&self, byte: u8) {// Wait until there is room in the TX buffer.while self.read_flag_register() & FR_TXFF != 0 {}// SAFETY: We know that the base address points to the control// registers of a PL011 device which is appropriately mapped.unsafe {// Write to the TX buffer.self.base_address.write_volatile(byte);}// Wait until the UART is no longer busy.while self.read_flag_register() & FR_BUSY != 0 {}}fn read_flag_register(&self) -> u8 {// SAFETY: We know that the base address points to the control// registers of a PL011 device which is appropriately mapped.unsafe { self.base_address.add(FLAG_REGISTER_OFFSET).read_volatile() }}}
- Note that
Uart::newis unsafe while the other methods are safe. This is because as long as the caller ofUart::newguarantees that its safety requirements are met (i.e. that there is only ever one instance of the driver for a given UART, and nothing else aliasing its address space), then it is always safe to callwrite_bytelater because we can assume the necessary preconditions. - We could have done it the other way around (making
newsafe butwrite_byteunsafe), but that would be much less convenient to use as every place that callswrite_bytewould need to reason about the safety - This is a common pattern for writing safe wrappers of unsafe code: moving the burden of proof for soundness from a large number of places to a smaller number of places.