use core::arch::asm; use alloc::vec::Vec; use crate::{ aarch64::{ mmu::{allocate_memory, physical_mapping::reserve_page}, registers::{ daif::{mask_all, unmask_irq}, read_elr_el1, read_esr_el1, read_exception_source_el, }, }, get_current_el, peripherals::{ gpio::{read_gpio_event_detect_status, reset_gpio_event_detect_status}, uart::clear_uart_interrupt_state, }, pi3::mailbox, println, read_address, write_address, }; const INTERRUPT_BASE: u32 = 0x3F00_B000; const IRQ_PENDING_BASE: u32 = INTERRUPT_BASE + 0x204; const ENABLE_IRQ_BASE: u32 = INTERRUPT_BASE + 0x210; const DISABLE_IRQ_BASE: u32 = INTERRUPT_BASE + 0x21C; const GPIO_PENDING_BIT_OFFSET: u64 = 0b1111 << 49; #[repr(C)] pub struct TrapFrame { pub x0: u64, pub x1: u64, pub x2: u64, pub x3: u64, pub x4: u64, pub x5: u64, pub x6: u64, pub x7: u64, pub x8: u64, pub x9: u64, pub x10: u64, pub x11: u64, pub x12: u64, pub x13: u64, pub x14: u64, pub x15: u64, pub x16: u64, pub x17: u64, pub x18: u64, pub x29: u64, pub x30: u64, } 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, I2cSpiSlvInt = 44, Pwa0 = 45, Pwa1 = 46, Smi = 48, GpioInt0 = 49, GpioInt1 = 50, GpioInt2 = 51, GpioInt3 = 52, I2cInt = 53, SpiInt = 54, PcmInt = 55, UartInt = 57, } /// Representation of the ESR_ELx registers /// /// Reference: D1.10.4 #[derive(Debug, Clone, Copy)] #[allow(dead_code)] struct EsrElX { ec: u32, il: u32, iss: u32, } impl From for EsrElX { fn from(value: u32) -> Self { Self { ec: value >> 26, il: (value >> 25) & 0b1, iss: value & 0x1FFFFFF, } } } #[no_mangle] unsafe extern "C" fn rust_irq_handler() { mask_all(); let pending_irqs = get_irq_pending_sources(); if pending_irqs & GPIO_PENDING_BIT_OFFSET != 0 { handle_gpio_interrupt(); let source_el = read_exception_source_el() >> 2; println!("Source EL: {}", source_el); println!("Current EL: {}", get_current_el()); println!("Return register address: {:#x}", read_esr_el1()); } if let Some(handler_vec) = unsafe { &*core::ptr::addr_of_mut!(INTERRUPT_HANDLERS) } { for handler in handler_vec { if (pending_irqs & (1 << (handler.source.clone() as u32))) != 0 { (handler.function)(); clear_interrupt_for_source(handler.source.clone()); } } } } #[no_mangle] unsafe extern "C" fn rust_synchronous_interrupt_no_el_change() { mask_all(); let source_el = read_exception_source_el() >> 2; println!("--------Sync Exception in EL{}--------", source_el); println!("No EL change"); println!("Current EL: {}", get_current_el()); println!("{:?}", EsrElX::from(read_esr_el1())); println!("Return register address: {:#x}", read_esr_el1()); println!("-------------------------------------"); } /// Synchronous Exception Handler /// /// Source is a lower Exception level, where the implemented level /// immediately lower than the target level is using /// AArch64. #[no_mangle] unsafe extern "C" fn rust_synchronous_interrupt_imm_lower_aarch64(frame: &mut TrapFrame) -> usize { mask_all(); let esr: EsrElX = EsrElX::from(read_esr_el1()); match esr.ec { 0b100100 => { println!("Cause: Data Abort from a lower Exception level"); log_sync_exception(); } 0b010101 => { println!("Cause: SVC instruction execution in AArch64"); return handle_svc(frame); } _ => { println!("Unknown Error Code: {:b}", esr.ec); } } println!("Returning to kernel main..."); set_return_to_kernel_main(); return 0; } fn handle_svc(frame: &mut TrapFrame) -> usize { match frame.x8 { 67 => { let response = mailbox::read_soc_temp([0]).unwrap(); response[0] as usize } _ => 0, } } fn log_sync_exception() { let source_el = read_exception_source_el() >> 2; println!("--------Sync Exception in EL{}--------", source_el); println!("Exception escalated to EL {}", get_current_el()); println!("Current EL: {}", get_current_el()); let esr: EsrElX = EsrElX::from(read_esr_el1()); println!("{:?}", esr); println!("Return address: {:#x}", read_elr_el1()); println!("-------------------------------------"); } fn clear_interrupt_for_source(source: IRQSource) { match source { IRQSource::UartInt => clear_uart_interrupt_state(), _ => { todo!() } } } fn set_return_to_kernel_main() { unsafe { asm!("ldr x0, =kernel_main", "msr ELR_EL1, x0"); asm!("mov x0, #(0b0101)", "msr SPSR_EL1, x0"); } } fn handle_gpio_interrupt() { println!("Interrupt"); for i in 0..=53u32 { let val = read_gpio_event_detect_status(i); if val { #[allow(clippy::single_match)] match i { 26 => { println!("Button Pressed"); } _ => {} } // Reset GPIO Interrupt handler by writing a 1 reset_gpio_event_detect_status(i); } } unmask_irq(); } /// Enables IRQ Source pub fn enable_irq_source(state: IRQSource) { let nr = state as u32; let register = ENABLE_IRQ_BASE + 4 * (nr / 32); let register_offset = nr % 32; let current = unsafe { read_address(register) }; let mask = 0b1 << register_offset; let new_val = current | mask; unsafe { write_address(register, new_val) }; } /// Disable IRQ Source pub fn disable_irq_source(state: IRQSource) { let nr = state as u32; let register = DISABLE_IRQ_BASE + 4 * (nr / 32); let register_offset = nr % 32; let current = unsafe { read_address(register) }; let mask = 0b1 << register_offset; let new_val = current | mask; unsafe { write_address(register, new_val) }; } /// Read current IRQ Source status pub fn read_irq_source_status(state: IRQSource) -> u32 { let nr = state as u32; let register = ENABLE_IRQ_BASE + 4 * (nr / 32); let register_offset = nr % 32; (unsafe { read_address(register) } >> register_offset) & 0b1 } /// Status if a IRQ Source is pending pub fn is_irq_source_pending(state: IRQSource) -> bool { let nr = state as u32; let register = IRQ_PENDING_BASE + 4 * (nr / 32); let register_offset = nr % 32; ((unsafe { read_address(register) } >> register_offset) & 0b1) != 0 } /// Status if a IRQ Source is pending pub fn get_irq_pending_sources() -> u64 { let mut pending = unsafe { read_address(IRQ_PENDING_BASE + 4) as u64 } << 32; pending |= unsafe { read_address(IRQ_PENDING_BASE) as u64 }; pending } #[inline(always)] 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 { &mut *core::ptr::addr_of_mut!(INTERRUPT_HANDLERS) } { handler_vec.push(InterruptHandlers { source, function }); } }