Давайте напишемо драйвер 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
, мало б міркувати про безпеку - Це загальний шаблон для написання безпечних оболонок небезпечного коду: перенесення тягаря доведення правильності з великої кількості місць на меншу кількість місць.