Vamos a escribir un controlador de UART
La máquina “virt” de QEMU tiene una UART [PL011]https://developer.arm.com/documentation/ddi0183/g), así que vamos a escribir un controlador para ella.
const FLAG_REGISTER_OFFSET: usize = 0x18;
const FR_BUSY: u8 = 1 << 3;
const FR_TXFF: u8 = 1 << 5;
/// Controlador mínimo para un UART PL011.
#[derive(Debug)]
pub struct Uart {
base_address: *mut u8,
}
impl Uart {
/// Construye una instancia nueva del controlador de UART para un dispositivo PL011 en la
/// dirección base proporcionada.
///
/// # Seguridad
///
/// La dirección base debe apuntar a los 8 registros de control MMIO de un
/// dispositivo PL011, que debe asignarse al espacio de direcciones del proceso
/// como memoria del dispositivo y no tener ningún otro alias.
pub unsafe fn new(base_address: *mut u8) -> Self {
Self { base_address }
}
/// Escribe un solo byte en el UART.
pub fn write_byte(&self, byte: u8) {
// Espera hasta que haya espacio en el búfer de TX.
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 {
// Escribe en el búfer de TX.
self.base_address.write_volatile(byte);
}
// Espera hasta que el UART esté libre.
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() }
}
}
- Ten en cuenta que
Uart::newno es seguro, mientras que los otros métodos sí lo son. Esto se debe a que mientras que el llamador deUart::newasegure que se cumplan sus requisitos de seguridad (es decir, que solo haya una instancia del controlador para una UART determinada y que nada más asigne alias a su espacio de direcciones), siempre es más seguro llamar awrite_bytemás adelante, ya que podemos asumir\ las condiciones previas necesarias. - Podríamos haberlo hecho al revés (haciendo que
newfuese seguro ywrite_byteno seguro), pero\sería mucho menos cómodo de usar, ya que cada lugar que llamase awrite_bytetendría que pensar en la seguridad - Este es un patrón común para escribir envoltorios seguros de código inseguro: mover la carga de la prueba de seguridad de un gran número de lugares a otro más pequeño.