Давайте напишемо драйвер UART
Машина QEMU ‘virt’ має PL011 UART, тож давайте напишемо для нього драйвер.
const FLAG_REGISTER_OFFSET: usize = 0x18;
const FR_BUSY: u8 = 1 << 3;
const FR_TXFF: u8 = 1 << 5;
/// Мінімальний драйвер для PL011 UART.
#[derive(Debug)]
pub struct Uart {
base_address: *mut u8,
}
impl Uart {
/// Створює новий екземпляр драйвера UART для пристрою PL011
/// за заданою базовою адресою.
///
/// # Безпека
///
/// Задана базова адреса повинна вказувати на 8 керуючих регістрів MMIO пристрою
/// PL011, які повинні бути відображені в адресному просторі процесу
/// як пам'ять пристрою і не мати ніяких інших псевдонімів.
pub unsafe fn new(base_address: *mut u8) -> Self {
Self { base_address }
}
/// Записує один байт до UART.
pub fn write_byte(&self, byte: u8) {
// Чекаємо, поки не звільниться місце в буфері TX.
while self.read_flag_register() & FR_TXFF != 0 {}
// БЕЗПЕКА: ми знаємо, що базова адреса вказує на регістри
// керування пристрою PL011, які відповідним чином відображені.
unsafe {
// Записуємо в буфер TX.
self.base_address.write_volatile(byte);
}
// Чекаємо, поки UART більше не буде зайнято.
while self.read_flag_register() & FR_BUSY != 0 {}
}
fn read_flag_register(&self) -> u8 {
// БЕЗПЕКА: ми знаємо, що базова адреса вказує на регістри
// керування пристрою PL011, які відповідним чином відображені.
unsafe { self.base_address.add(FLAG_REGISTER_OFFSET).read_volatile() }
}
}
- Зауважте, що
Uart::newє небезпечним, тоді як інші методи є безпечними. Це пов’язано з тим, що доки викликачUart::newгарантує, що його вимоги безпеки дотримано (тобто, що існує лише один екземпляр драйвера для даного UART, і ніщо інше не змінює його адресний простір), доти безпечно викликатиwrite_byteпізніше, оскільки ми можемо припустити, що виконано необхідні передумови. - Ми могли б зробити це навпаки (зробити
newбезпечним, алеwrite_byteнебезпечним), але це було б набагато менш зручно використовувати, оскільки кожне місце, яке викликаєwrite_byte, мало б міркувати про безпеку - Це загальний шаблон для написання безпечних оболонок небезпечного коду: перенесення тягаря доведення правильності з великої кількості місць на меншу кількість місць.