Switch Tock's mailbox driver to use the RTL register map. The previous implementation was a placeholder, now that we have a design doc and register map for the hardware mailbox we can make the two match up. Additionally, clean up the driver's 'wait for mailbox event' handling. Change-Id: I5aa43a112cef9e810dc6028a5dba327c4c4924a1
diff --git a/hal/src/mailbox.rs b/hal/src/mailbox.rs index 53c996a..c3bcefb 100644 --- a/hal/src/mailbox.rs +++ b/hal/src/mailbox.rs
@@ -1,45 +1,66 @@ //! Matcha hardware mailbox. -//! TODO(aappleby): Rework this to match the RTL implementation. use core::mem::transmute; -use kernel::common::registers::{register_structs, ReadWrite}; +use kernel::common::registers::{register_structs, ReadOnly, ReadWrite, WriteOnly}; use kernel::common::StaticRef; -use crate::dprintf; use crate::*; +// TODO(aappleby): These constants are hardcoded pending the merge of the +// mailbox RTL. + pub const MAILBOX0_BASE: u32 = 0x400F0000; pub const MAILBOX1_BASE: u32 = 0x400F1000; -pub const MAILBOX_SIZE_DWORDS: usize = 8; +pub const MAILBOX_SIZE_DWORDS: u32 = 8; +pub const MAILBOX_STATUS_HIWATER: u32 = 0b0100; +pub const MAILBOX_STATUS_LOWATER: u32 = 0b1000; + +pub const MAILBOX0_HIWATER_IRQ: isize = 100; +pub const MAILBOX0_LOWATER_IRQ: isize = 101; + +pub const MAILBOX1_HIWATER_IRQ: isize = 102; +pub const MAILBOX1_LOWATER_IRQ: isize = 103; + +pub const IRQ_ERROR : u32 = 0b001; +pub const IRQ_HIWATER : u32 = 0b010; +pub const IRQ_LOWATER : u32 = 0b100; register_structs! { pub MailboxRegisters { - (0x000 => message: [ReadWrite<u32>; 8]), - (0x020 => irq_send: ReadWrite<u32>), - (0x024 => irq_recv: ReadWrite<u32>), + (0x000 => reg_istate: ReadWrite<u32>), + (0x004 => reg_ienable: ReadWrite<u32>), + (0x008 => reg_itest: ReadWrite<u32>), + (0x00C => reg_write: WriteOnly<u32>), + (0x010 => reg_read: ReadOnly<u32>), + (0x014 => reg_status: ReadOnly<u32>), + (0x018 => reg_error: ReadOnly<u32>), + (0x01C => reg_hiwater: ReadWrite<u32>), + (0x020 => reg_lowater: ReadWrite<u32>), + (0x024 => reg_flush: WriteOnly<u32>), (0x028 => @END), } } pub struct Mailbox { regs: StaticRef<MailboxRegisters>, - plic_irq_send: isize, - plic_irq_recv: isize, + plic_irq_hiwater: isize, + plic_irq_lowater: isize, } pub const MAILBOX0: Mailbox = Mailbox { regs: unsafe { StaticRef::new(MAILBOX0_BASE as *mut MailboxRegisters) }, - plic_irq_send: 100, - plic_irq_recv: 101, + plic_irq_hiwater: MAILBOX0_HIWATER_IRQ, + plic_irq_lowater: MAILBOX0_LOWATER_IRQ, }; pub const MAILBOX1: Mailbox = Mailbox { regs: unsafe { StaticRef::new(MAILBOX1_BASE as *mut MailboxRegisters) }, - plic_irq_send: 102, - plic_irq_recv: 103, + plic_irq_hiwater: MAILBOX1_HIWATER_IRQ, + plic_irq_lowater: MAILBOX1_LOWATER_IRQ, }; -// 32-bit-word bitfield helper for the PLIC's irq enable lines. +// 32-bit-word bitfield helpers for the PLIC's irq enable lines. + pub unsafe fn set_bit(base: u32, bit_index: isize) { let buf: *mut u32 = transmute(base); let mut bits = buf.offset(bit_index >> 5).read_volatile(); @@ -47,30 +68,50 @@ buf.offset(bit_index >> 5).write_volatile(bits); } -impl Mailbox { - pub unsafe fn get_message(&self, message: &mut [u32]) { - if message.len() > MAILBOX_SIZE_DWORDS { - dprintf!("get_message() - Bad message size {}\n", message.len()); - return; - } +pub unsafe fn clear_bit(base: u32, bit_index: isize) { + let buf: *mut u32 = transmute(base); + let mut bits = buf.offset(bit_index >> 5).read_volatile(); + bits &= !(1 << (bit_index & 31)); + buf.offset(bit_index >> 5).write_volatile(bits); +} - // Empty the mailbox - must be done with dword-sized writes +impl Mailbox { + pub unsafe fn send_message(&self, message: &[u32]) { + self.regs.reg_lowater.set(MAILBOX_SIZE_DWORDS - 1); + self.regs.reg_ienable.set(IRQ_LOWATER); for i in 0..message.len() { - message[i] = self.regs.message[i].get(); - self.regs.message[i].set(0); + self.wait_irq_lowater(); + self.regs.reg_write.set(message[i]); } + self.regs.reg_ienable.set(0); + self.regs.reg_lowater.set(0); } - pub unsafe fn set_message(&self, message: &[u32]) { - if message.len() > MAILBOX_SIZE_DWORDS { - dprintf!("set_message() - Bad message size {}\n", message.len()); - return; - } - - // Fill the mailbox - must be done with dword-sized writes + pub unsafe fn recv_message(&self, message: &mut [u32]) { + self.regs.reg_hiwater.set(1); + self.regs.reg_ienable.set(IRQ_HIWATER); for i in 0..message.len() { - self.regs.message[i].set(message[i]); + self.wait_irq_hiwater(); + message[i] = self.regs.reg_read.get(); } + self.regs.reg_ienable.set(0); + self.regs.reg_hiwater.set(0); + } + + pub unsafe fn wait_hiwater(&self, hiwater : u32) { + self.regs.reg_hiwater.set(hiwater); + self.regs.reg_ienable.set(IRQ_HIWATER); + self.wait_irq_hiwater(); + self.regs.reg_ienable.set(0); + self.regs.reg_hiwater.set(0); + } + + pub unsafe fn wait_lowater(&self, lowater : u32) { + self.regs.reg_lowater.set(lowater); + self.regs.reg_ienable.set(IRQ_LOWATER); + self.wait_irq_lowater(); + self.regs.reg_ienable.set(0); + self.regs.reg_lowater.set(0); } // This requires a small bit of explanation - if an interrupt were to happen @@ -80,64 +121,29 @@ // an interrupt has been triggered, 'wfi' will continue and the isr will // execute immediately after global interrupts are re-enabled. - unsafe fn wait_until_full(&self) { - while self.regs.irq_send.get() == 0 { + unsafe fn wait_irq_hiwater(&self) { + while (self.regs.reg_status.get() & MAILBOX_STATUS_HIWATER) == 0 { clear_mstatus_bits(MIE_BIT | SIE_BIT); - if self.regs.irq_send.get() == 0 { + set_bit(plic::PLIC_EN0, self.plic_irq_hiwater); + if (self.regs.reg_status.get() & MAILBOX_STATUS_HIWATER) == 0 { asm!("wfi"); } + clear_bit(plic::PLIC_EN0, self.plic_irq_hiwater); set_mstatus_bits(MIE_BIT | SIE_BIT); } + self.regs.reg_istate.set(IRQ_HIWATER); } - unsafe fn wait_until_empty(&self) { - while self.regs.irq_send.get() != 0 { + unsafe fn wait_irq_lowater(&self) { + while (self.regs.reg_status.get() & MAILBOX_STATUS_LOWATER) == 0 { clear_mstatus_bits(MIE_BIT | SIE_BIT); - if self.regs.irq_send.get() != 0 { + set_bit(plic::PLIC_EN0, self.plic_irq_lowater); + if (self.regs.reg_status.get() & MAILBOX_STATUS_LOWATER) == 0 { asm!("wfi"); } + clear_bit(plic::PLIC_EN0, self.plic_irq_lowater); set_mstatus_bits(MIE_BIT | SIE_BIT); } - } - - unsafe fn wait_until_recv(&self) { - while self.regs.irq_recv.get() == 0 { - clear_mstatus_bits(MIE_BIT | SIE_BIT); - if self.regs.irq_recv.get() == 0 { - asm!("wfi"); - } - set_mstatus_bits(MIE_BIT | SIE_BIT); - } - } - - pub unsafe fn send_message(&self, message: &[u32], wait_for_ack: bool) { - self.wait_until_empty(); - self.set_message(message); - self.regs.irq_send.set(1); - - if wait_for_ack { - // Enable the RECV irq line, then wait for RECV. The ISR will disable the - // irq line once it fires. - set_bit(plic::PLIC_EN0, self.plic_irq_recv); - self.wait_until_recv(); - self.regs.irq_recv.set(0); - } - } - - // TODO(aappleby): Refactor - pub unsafe fn recv_message(&self, message: &mut [u32], send_ack: bool) { - // Enable the SEND irq line and wait for new mail. The ISR will disable the - // irq line once it fires. - // set_bit(plic::PLIC_EN1, self.plic_irq_send); - self.wait_until_full(); - self.regs.irq_send.set(0); - - // Message has arrived. - self.get_message(message); - - if send_ack { - // Trigger RECV - self.regs.irq_recv.set(1); - } + self.regs.reg_istate.set(IRQ_LOWATER); } }
diff --git a/utils/src/lib.rs b/utils/src/lib.rs index a8cd612..546bbb9 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs
@@ -19,45 +19,58 @@ let sel4_elf = elf_loader::find_elf("kernel"); let capdl_elf = elf_loader::find_elf("capdl-loader"); - // If we found all of them, boot in Shodan mode. - if !sel4_elf.is_null() && !capdl_elf.is_null() { - dprintf!("Loading seL4 kernel elf\n"); - elf_loader::load_elf_segments(sel4_elf.as_ref().unwrap(), 0); - - let sel4_segments = elf_loader::elf_get_segments(sel4_elf); - let capdl_segments = elf_loader::elf_get_segments(capdl_elf); - - let sel4_pend = round_up_to_page(elf_loader::elf_phys_max(sel4_segments)); - - dprintf!("Loading capdl-loader to the page after seL4\n"); - elf_loader::load_elf_segments( - capdl_elf.as_ref().unwrap(), - sel4_pend - elf_loader::elf_phys_min(capdl_segments), - ); - - dprintf!("Starting management core\n"); - - let entry_point: u32 = (*sel4_elf).e_entry - - (elf_loader::elf_virt_min(sel4_segments) - - elf_loader::elf_phys_min(sel4_segments)); - - let message = [ - transmute(sel4_pend), - transmute( - sel4_pend - + round_up_to_page( - elf_loader::elf_phys_max(capdl_segments) - - elf_loader::elf_phys_min(capdl_segments), - ), - ), - transmute(sel4_pend - elf_loader::elf_phys_min(capdl_segments)), - transmute((*capdl_elf).e_entry), - ]; - matcha_hal::mailbox::MAILBOX1.set_message(&message); - - SMC_CONTROL_BLOCK.write_volatile(entry_point); - } else { - dprintf!("Missing sel4_elf or capdl_elf\n"); + if sel4_elf.is_null() { + dprintf!("Boot failure, seL4 kernel ELF missing.\n"); + return; } + + if capdl_elf.is_null() { + dprintf!("Boot failure seL4 CapDL ELF missing.\n"); + return; + } + + // If we found all of them, boot in Shodan mode. + dprintf!("Loading seL4 kernel elf\n"); + elf_loader::load_elf_segments(sel4_elf.as_ref().unwrap(), 0); + + let sel4_segments = elf_loader::elf_get_segments(sel4_elf); + let capdl_segments = elf_loader::elf_get_segments(capdl_elf); + + let sel4_pend = round_up_to_page(elf_loader::elf_phys_max(sel4_segments)); + + dprintf!("Loading capdl-loader to the page after seL4\n"); + elf_loader::load_elf_segments( + capdl_elf.as_ref().unwrap(), + sel4_pend - elf_loader::elf_phys_min(capdl_segments), + ); + + dprintf!("Starting management core\n"); + + let entry_point: u32 = (*sel4_elf).e_entry + - (elf_loader::elf_virt_min(sel4_segments) + - elf_loader::elf_phys_min(sel4_segments)); + + let message = [ + transmute(sel4_pend), + transmute( + sel4_pend + + round_up_to_page( + elf_loader::elf_phys_max(capdl_segments) + - elf_loader::elf_phys_min(capdl_segments), + ), + ), + transmute(sel4_pend - elf_loader::elf_phys_min(capdl_segments)), + transmute((*capdl_elf).e_entry), + ]; + matcha_hal::mailbox::MAILBOX1.send_message(&message); + dprintf!("seL4 boot message posted\n"); + + SMC_CONTROL_BLOCK.write_volatile(entry_point); + + dprintf!("SMC started, waiting on mailbox to clear\n"); + + matcha_hal::mailbox::MAILBOX1.wait_lowater(0); + + dprintf!("Mailbox cleared\n"); } }