بیایید یک درایور UART بنویسیم

این ماشین QEMU 'virt' یک PL011 به‌عنوان UART دارد، پس بیایید یک درایور برای آن بنویسیم.

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() }
    }
}
  • توجه داشته باشید که Uart::new ناامن یا unsafe است در حالی که متد‌های دیگر ایمن هستند. این به‌خاطر این است که تا زمانی که تماس گیرنده Uart::new تضمین کند که الزامات ایمنی آن برآورده شده است (یعنی فقط یک نمونه از درایور برای یک UART مشخص وجود دارد و هیچ چیز دیگری نام مستعار فضای آدرس آن را ندارد)، پس همیشه می‌توان write_byte را بعداً فراخوانی کرد زیرا می‌توانیم پیش‌شرط‌های لازم را فرض کنیم.
  • ما می‌توانستیم این کار را به صورت دیگری انجام دهیم ( ساخت new را ایمن کنیم، اما write_byte را ناامن کنیم)، اما استفاده از آن بسیار راحت‌تر خواهد بود، زیرا هر مکانی که write_byte را صدا می‌زند باید در مورد ایمنی یا 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.