Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Console

Console is a trait for the console and defined in awkernel_lib/src/console.rs as follows.

#![allow(unused)]
fn main() {
pub trait Console: Write + Send {
    /// Enable the serial port.
    fn enable(&mut self);

    /// Disable the serial port.
    fn disable(&mut self);

    /// Enable the reception interrupt.
    fn enable_recv_interrupt(&mut self);

    /// Disable the reception interrupt.
    fn disable_recv_interrupt(&mut self);

    /// Acknowledge to the reception interrupt.
    fn acknowledge_recv_interrupt(&mut self);

    /// Get IRQ#.
    fn irq_id(&self) -> u16;

    /// Read a byte.
    fn get(&mut self) -> Option<u8>;

    /// Write a byte.
    fn put(&mut self, data: u8);
}
}

There are several functions regarding the Console trait in awkernel_lib/src/console.rs.

functiondescription
fn register_unsafe_puts(console: unsafe fn(&str))Register the unsafe puts function.
unsafe fn unsafe_puts(data: &str)Write a string.
unsafe fn unsafe_print_hex_u32(num: u32)Write a hexadecimal number.
unsafe fn unsafe_print_hex_u64(num: u64)Write a hexadecimal number.
unsafe fn unsafe_print_hex_u96(num: u128)Write a hexadecimal number.
unsafe fn unsafe_print_hex_u128(num: u128)Write a hexadecimal number.
fn register_console(console: Box<dyn Console>)Register the console.
fn enable()Enable the console.
fn disable()Disable the console.
fn enable_recv_interrupt()Enable the reception interrupt.
fn disable_recv_interrupt()Disable the reception interrupt.
fn acknowledge_recv_interrupt()Acknowledge to the reception interrupt.
fn irq_id()Get IRQ#.
fn get()Read a byte.
fn put(data: u8)Write a byte.
fn print(data: &str)Write a string.

When booting, an unsafe console should be registered by calling the register_unsafe_puts function. After that, the unsafe_puts and unsafe_print_hex functions can be used to print messages. Note that these functions are unsafe because they may cause data races.

After enabling mutual exclusion, a safe console should be registered by calling the register_console function. Then, the print, get, and put functions can be used to print messages.

Implementation

x86_64

x86_64 should equip UART 16550 serial ports, and Awkerenel uses the serial port to output messages as a console. UART 16550's device driver is implemented in awkernel_drivers/src/uart/uart_16550 whose original source code is from uart_16550. The Uart structure defined in kernel/src/arch/x86_64/console.rs implements the Console trait as follows.

#![allow(unused)]
fn main() {
pub struct Uart {
    port: uart_16550::SerialPort,
    enabled: bool,
}

impl Console for Uart {
    fn enable(&mut self) {
        self.enabled = true;
    }

    fn disable(&mut self) {
        self.enabled = false;
    }

    fn enable_recv_interrupt(&mut self) {
        self.port.enable_interrupt();
    }

    fn disable_recv_interrupt(&mut self) {
        self.port.disable_interrupt();
    }

    fn acknowledge_recv_interrupt(&mut self) {
        // nothing to do
    }

    fn irq_id(&self) -> u16 {
        36 // COM1
    }

    fn get(&mut self) -> Option<u8> {
        if self.enabled {
            self.port.try_receive()
        } else {
            None
        }
    }

    fn put(&mut self, data: u8) {
        if self.enabled {
            self.port.send(data);
        }
    }
}
}

AArch64

AArch64 should equip PL011 UART serial ports, and Awkerenel uses the serial port to output messages as a console. PL011 UART's device driver is implemented in awkernel_drivers/src/uart/pl011.rs, and it implements the Console trait as follows.

#![allow(unused)]
fn main() {
impl Console for PL011 {
    fn enable(&mut self) {
        use registers::CR;
        registers::UART0_CR.write(CR::EN | CR::RXE | CR::TXE, self.base_addr); // enable, Rx, Tx
    }

    fn disable(&mut self) {
        registers::UART0_CR.write(registers::CR::empty(), self.base_addr);
    }

    fn enable_recv_interrupt(&mut self) {
        registers::UART0_IMSC.setbits(IMSC_RXIM, self.base_addr);
    }

    fn disable_recv_interrupt(&mut self) {
        registers::UART0_IMSC.clrbits(IMSC_RXIM, self.base_addr);
    }

    fn acknowledge_recv_interrupt(&mut self) {
        registers::UART0_ICR.write(registers::ICR::RXIC, self.base_addr);
    }

    fn irq_id(&self) -> u16 {
        self.irq
    }

    fn get(&mut self) -> Option<u8> {
        if registers::UART0_FR.read(self.base_addr) & 0x10 != 0 {
            None
        } else {
            Some(registers::UART0_DR.read(self.base_addr) as u8)
        }
    }

    fn put(&mut self, data: u8) {
        // wait until we can send
        unsafe { asm!("nop;") };
        while registers::UART0_FR.read(self.base_addr) & 0x20 != 0 {
            core::hint::spin_loop();
        }

        // write the character to the buffer
        registers::UART0_DR.write(data as u32, self.base_addr);
    }
}
}