編寫 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 {}
// Safe because 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 {
// Safe because 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並不安全,其他方法則是安全的。這是因為只要Uart::new的呼叫端保證能滿足安全規定,也就是特定 UART 只有一個驅動程式例項,沒有其他項目定義其位址空間的別名,那麼稍後呼叫write_byte一律是安全的,因為我們可以假設必要的先決條件。 - 我們可以反過來操作,也就是讓
new安全,而write_byte不安全,但這樣的使用便利度低許多,因為每個呼叫write_byte的位置都需要分析安全性 - 這是為不安全程式碼撰寫安全包裝函式的常見模式:將證明安全性的負擔從大量位置移到少量位置。