diff --git a/src/interrupt_handlers.rs b/src/interrupt_handlers.rs index a48b468..bc051fb 100644 --- a/src/interrupt_handlers.rs +++ b/src/interrupt_handlers.rs @@ -1,9 +1,14 @@ use core::arch::asm; +use alloc::vec::Vec; + use crate::{ get_current_el, interrupt_handlers::daif::unmask_irq, - peripherals::gpio::{read_gpio_event_detect_status, reset_gpio_event_detect_status}, + peripherals::{ + gpio::{read_gpio_event_detect_status, reset_gpio_event_detect_status}, + uart::clear_uart_interrupt_state, + }, println, read_address, write_address, }; @@ -14,6 +19,15 @@ const DISABLE_IRQ_BASE: u32 = INTERRUPT_BASE + 0x21C; const GPIO_PENDING_BIT_OFFSET: u64 = 0b1111 << 49; +struct InterruptHandlers { + source: IRQSource, + function: fn(), +} + +// TODO: replace with hashmap and check for better alternatives for option +static mut INTERRUPT_HANDLERS: Option> = None; + +#[derive(Clone)] #[repr(u32)] pub enum IRQSource { AuxInt = 29, @@ -59,11 +73,20 @@ unsafe extern "C" fn rust_irq_handler() { if pending_irqs & GPIO_PENDING_BIT_OFFSET != 0 { handle_gpio_interrupt(); + let source_el = get_exception_return_exception_level() >> 2; + println!("Source EL: {}", source_el); + println!("Current EL: {}", get_current_el()); + println!("Return register address: {:#x}", get_elr_el1()); + } + + if let Some(handler_vec) = unsafe { INTERRUPT_HANDLERS.as_ref() } { + for handler in handler_vec { + if (pending_irqs & (1 << (handler.source.clone() as u32))) != 0 { + (handler.function)(); + clear_interrupt_for_source(handler.source.clone()); + } + } } - let source_el = get_exception_return_exception_level() >> 2; - println!("Source EL: {}", source_el); - println!("Current EL: {}", get_current_el()); - println!("Return register address: {:#x}", get_elr_el1()); } #[no_mangle] @@ -107,6 +130,13 @@ unsafe extern "C" fn rust_synchronous_interrupt_imm_lower_aarch64() { set_return_to_kernel_main(); } +fn clear_interrupt_for_source(source: IRQSource) { + match source { + IRQSource::UartInt => clear_uart_interrupt_state(), + _ => {} + } +} + fn set_return_to_kernel_main() { unsafe { asm!("ldr x0, =kernel_main", "msr ELR_EL1, x0"); @@ -238,3 +268,13 @@ pub mod daif { unsafe { asm!("msr DAIFClr, #0x2", options(nomem, nostack)) } } } + +pub fn initialize_interrupt_handler() { + unsafe { INTERRUPT_HANDLERS = Some(Vec::new()) }; +} + +pub fn register_interrupt_handler(source: IRQSource, function: fn()) { + if let Some(handler_vec) = unsafe { INTERRUPT_HANDLERS.as_mut() } { + handler_vec.push(InterruptHandlers { source, function }); + } +} diff --git a/src/lib.rs b/src/lib.rs index ad704cb..f12cb31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,14 +6,13 @@ extern crate alloc; use alloc::boxed::Box; use core::{ arch::asm, - fmt, panic::PanicInfo, ptr::{read_volatile, write_volatile}, }; use heap::Heap; -use crate::logger::{DefaultLogger, Logger}; +use crate::{interrupt_handlers::initialize_interrupt_handler, logger::DefaultLogger}; static PERIPHERAL_BASE: u32 = 0x3F00_0000; @@ -74,4 +73,5 @@ pub fn get_current_el() -> u64 { pub fn initialize_kernel() { logger::set_logger(Box::new(DefaultLogger)); + initialize_interrupt_handler(); } diff --git a/src/logger.rs b/src/logger.rs index 22beeae..5ecd9c6 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -6,43 +6,40 @@ use crate::peripherals::uart; static mut LOGGER: Option> = None; -pub trait Logger: Write + Sync {} +pub trait Logger: Write + Sync { + fn flush(&mut self); +} pub struct DefaultLogger; -impl Logger for DefaultLogger {} +impl Logger for DefaultLogger { + fn flush(&mut self) {} +} impl Write for DefaultLogger { fn write_str(&mut self, s: &str) -> core::fmt::Result { - uart::write_str(s) + uart::Uart.write_str(s) } } #[macro_export] -macro_rules! print { +macro_rules! log { () => {}; ($($arg:tt)*) => { - $crate::logger::_print(format_args!($($arg)*)) + $crate::logger::log(format_args!($($arg)*)) }; } -pub fn _print(args: fmt::Arguments) { +pub fn log(args: fmt::Arguments) { unsafe { if let Some(logger) = LOGGER.as_mut() { - logger.write_fmt(args); + logger.write_str("\n").unwrap(); + logger.write_fmt(args).unwrap(); + logger.flush(); } } } -#[macro_export] -macro_rules! println { - () => {}; - ($($arg:tt)*) => { - $crate::print!($($arg)*); - $crate::print!("\r\n"); - }; -} - pub fn set_logger(logger: Box) { unsafe { LOGGER = Some(logger); diff --git a/src/main.rs b/src/main.rs index 9076bce..80475c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,21 +15,17 @@ use nova::{ framebuffer::{FrameBuffer, BLUE, GREEN, RED}, get_current_el, init_heap, interrupt_handlers::{daif, enable_irq_source, IRQSource}, - mailbox, + log, mailbox, peripherals::{ gpio::{ blink_gpio, gpio_pull_up, set_falling_edge_detect, set_gpio_function, GPIOFunction, SpecificGpio, }, - uart::{read_uart_data, uart_init}, + uart::uart_init, }, - print, println, + println, }; -use crate::uart_term::Terminal; - -mod uart_term; - global_asm!(include_str!("vector.S")); extern "C" { @@ -120,14 +116,13 @@ pub extern "C" fn el0() -> ! { fb.draw_function(cos, 100, 101, RED); loop { - read_uart_data(); let temp = mailbox::read_soc_temp([0]).unwrap(); - println!("{} °C", temp[1] / 1000); + log!("{} °C", temp[1] / 1000); blink_gpio(SpecificGpio::OnboardLed as u8, 500); let b = Box::new([1, 2, 3, 4]); - println!("{:?}", b); + log!("{:?}", b); } } diff --git a/src/peripherals/uart.rs b/src/peripherals/uart.rs index 51aa82c..dfcd141 100644 --- a/src/peripherals/uart.rs +++ b/src/peripherals/uart.rs @@ -19,13 +19,54 @@ const UART0_FBRD: u32 = 0x3F20_1028; const UART0_CR: u32 = 0x3F20_1030; const UART0_CR_UARTEN: u32 = 1 << 0; -const UART0_CR_LBE: u32 = 1 << 7; const UART0_CR_TXE: u32 = 1 << 8; const UART0_CR_RXE: u32 = 1 << 9; const UART0_LCRH: u32 = 0x3F20_102C; const UART0_LCRH_FEN: u32 = 1 << 4; +const UART0_IMSC: u32 = 0x3F20_1038; +const UART0_IMSC_RXIM: u32 = 1 << 4; + +const UART0_ICR: u32 = 0x3F20_1044; + +pub struct Uart; + +impl Write for Uart { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + for byte in s.bytes() { + while (unsafe { read_address(UART0_FR) } & UART0_FR_TXFF) != 0 { + unsafe { asm!("nop") } + } + unsafe { write_address(UART0_DR, byte as u32) }; + } + // wait till uart is not busy anymore + while ((unsafe { read_address(UART0_FR) } >> 3) & 0b1) != 0 {} + Ok(()) + } +} + +#[macro_export] +macro_rules! print { + () => {}; + ($($arg:tt)*) => { + $crate::peripherals::uart::_print(format_args!($($arg)*)) + }; +} + +#[macro_export] +macro_rules! println { + () => {}; + ($($arg:tt)*) => { + $crate::print!($($arg)*); + $crate::print!("\r\n"); + }; +} + +pub fn _print(args: fmt::Arguments) { + let _ = Uart.write_fmt(args); +} + /// Initialize UART peripheral pub fn uart_init() { let baud_div_times_64 = (UART_CLK * 4) / BAUD; @@ -41,6 +82,7 @@ pub fn uart_init() { write_address(UART0_FBRD, fbrd); } + uart_enable_rx_interrupt(); uart_set_lcrh(0b11, true); // Enable transmit, receive and uart @@ -76,6 +118,10 @@ fn uart_fifo_enable(enable: bool) { unsafe { write_address(UART0_LCRH, lcrh) }; } +fn uart_enable_rx_interrupt() { + unsafe { write_address(UART0_IMSC, UART0_IMSC_RXIM) }; +} + /// Set UART word length and set FIFO status fn uart_set_lcrh(wlen: u32, enable_fifo: bool) { let mut value = (wlen & 0b11) << 5; @@ -85,21 +131,12 @@ fn uart_set_lcrh(wlen: u32, enable_fifo: bool) { unsafe { write_address(UART0_LCRH, value) }; } -pub fn read_uart_data() { - println!( - "{:?}", - (unsafe { read_address(UART0_DR) } & 0xFF) as u8 as char - ); +pub fn read_uart_data() -> char { + (unsafe { read_address(UART0_DR) } & 0xFF) as u8 as char } -pub fn write_str(s: &str) -> core::fmt::Result { - for byte in s.bytes() { - while (unsafe { read_address(UART0_FR) } & UART0_FR_TXFF) != 0 { - unsafe { asm!("nop") } - } - unsafe { write_address(UART0_DR, byte as u32) }; +pub fn clear_uart_interrupt_state() { + unsafe { + write_address(UART0_ICR, 1 << 4); } - // wait till uart is not busy anymore - while ((unsafe { read_address(UART0_FR) } >> 3) & 0b1) != 0 {} - Ok(()) } diff --git a/src/terminal.rs b/src/terminal.rs new file mode 100644 index 0000000..83b22a7 --- /dev/null +++ b/src/terminal.rs @@ -0,0 +1,53 @@ +use core::fmt::Write; + +use alloc::string::String; +use nova::{ + interrupt_handlers::register_interrupt_handler, logger::Logger, + peripherals::uart::read_uart_data, print, println, +}; + +pub struct Terminal { + buffer: String, + input: String, +} + +impl Terminal { + pub fn new() -> Self { + Self { + buffer: String::new(), + input: String::new(), + } + } + + fn flush(&mut self) { + println!("{}", self.buffer); + print!("> {}", self.input); + self.buffer.clear(); + } +} + +impl Write for Terminal { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.buffer.push_str(s); + Ok(()) + } +} + +impl Logger for Terminal { + fn flush(&mut self) { + println!("{}", self.buffer); + print!("> {}", self.input); + self.buffer.clear(); + } +} + +fn terminal_uart_rx_interrupt_handler() { + print!("{}", read_uart_data()); +} + +pub fn register_terminal_interrupt_handler() { + register_interrupt_handler( + nova::interrupt_handlers::IRQSource::UartInt, + terminal_uart_rx_interrupt_handler, + ); +} diff --git a/src/uart_term.rs b/src/uart_term.rs deleted file mode 100644 index 39a3ac8..0000000 --- a/src/uart_term.rs +++ /dev/null @@ -1,17 +0,0 @@ -use core::fmt::Write; - -use nova::logger::Logger; - -/// Goals: -/// - I want to have a functional terminal over uart -/// - It shall continue to log - -pub struct Terminal {} - -impl Write for Terminal { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - Ok(()) - } -} - -impl Logger for Terminal {} diff --git a/tools/start_simulator.sh b/tools/start_simulator.sh index b0942b2..0955560 100755 --- a/tools/start_simulator.sh +++ b/tools/start_simulator.sh @@ -6,6 +6,6 @@ llvm-objcopy -O binary ../target/aarch64-unknown-none/release/nova ../target/aar qemu-system-aarch64 \ -M raspi3b \ -cpu cortex-a53 \ - -serial pty \ + -serial stdio \ -sd ../sd.img \ -kernel ../target/aarch64-unknown-none/release/kernel8.img