Make Tock's mailbox match the TLUL Mailbox spec & addressess. Match register names, make Tock only use the "SEC" endpoint of the mailbox, tweak how interrupts work due to the wonky "WTIRQ" interrupt that doesn't actually work for sleeping until a mailbox is drained (greater-than vs less-than issue in the spec). SEC will now busy-wait if the outbox is full, which should not usually be a problem. Change-Id: Ibc4c220c39cfc8395aa7948efdf6022a85738d51
diff --git a/hal/src/mailbox.rs b/hal/src/mailbox.rs index c3bcefb..d4acf4b 100644 --- a/hal/src/mailbox.rs +++ b/hal/src/mailbox.rs
@@ -1,62 +1,63 @@ //! Matcha hardware mailbox. +// We want constants & registers to stay together even if they're not currently +// used in this file. +#![allow(dead_code)] + use core::mem::transmute; use kernel::common::registers::{register_structs, ReadOnly, ReadWrite, WriteOnly}; use kernel::common::StaticRef; use crate::*; -// TODO(aappleby): These constants are hardcoded pending the merge of the -// mailbox RTL. +const MAILBOX_BASE: u32 = 0x40800000; // TOP_MATCHA_MAILBOX_SEC_BASE_ADDR +const MAILBOX_WTIRQ: u32 = 181; // kTopMatchaPlicIrqIdMailboxSecWtirq +const MAILBOX_RTIRQ: u32 = 182; // kTopMatchaPlicIrqIdMailboxSecRtirq +const MAILBOX_EIRQ: u32 = 183; // kTopMatchaPlicIrqIdMailboxSecEirq -pub const MAILBOX0_BASE: u32 = 0x400F0000; -pub const MAILBOX1_BASE: u32 = 0x400F1000; -pub const MAILBOX_SIZE_DWORDS: u32 = 8; -pub const MAILBOX_STATUS_HIWATER: u32 = 0b0100; -pub const MAILBOX_STATUS_LOWATER: u32 = 0b1000; +const MAILBOX_SIZE_DWORDS: u32 = 8; -pub const MAILBOX0_HIWATER_IRQ: isize = 100; -pub const MAILBOX0_LOWATER_IRQ: isize = 101; +const INTR_STATE_BIT_WTIRQ: u32 = 0b001; +const INTR_STATE_BIT_RTIRQ: u32 = 0b010; +const INTR_STATE_BIT_EIRQ: u32 = 0b100; -pub const MAILBOX1_HIWATER_IRQ: isize = 102; -pub const MAILBOX1_LOWATER_IRQ: isize = 103; +const INTR_ENABLE_BIT_WTIRQ: u32 = 0b001; +const INTR_ENABLE_BIT_RTIRQ: u32 = 0b010; +const INTR_ENABLE_BIT_EIRQ: u32 = 0b100; -pub const IRQ_ERROR : u32 = 0b001; -pub const IRQ_HIWATER : u32 = 0b010; -pub const IRQ_LOWATER : u32 = 0b100; +const STATUS_BIT_EMPTY: u32 = 0b0001; +const STATUS_BIT_FULL: u32 = 0b0010; +const STATUS_BIT_WFIFOL: u32 = 0b0100; +const STATUS_BIT_RFIFOL: u32 = 0b1000; register_structs! { - pub MailboxRegisters { - (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 MailboxRegisters { + (0x000 => reg_intr_state: ReadWrite<u32>), + (0x004 => reg_intr_enable: ReadWrite<u32>), + (0x008 => reg_intr_test: ReadWrite<u32>), + (0x00C => reg_mboxw: WriteOnly<u32>), + (0x010 => reg_mboxr: ReadOnly<u32>), + (0x014 => reg_status: ReadOnly<u32>), + (0x018 => reg_error: ReadOnly<u32>), + (0x01C => reg_wirqt: ReadWrite<u32>), + (0x020 => reg_rirqt: ReadWrite<u32>), + (0x024 => reg_ctrl: WriteOnly<u32>), + (0x028 => @END), + } } pub struct Mailbox { regs: StaticRef<MailboxRegisters>, - plic_irq_hiwater: isize, - plic_irq_lowater: isize, + plic_wtirq: u32, + plic_rtirq: u32, + plic_eirq: u32, } -pub const MAILBOX0: Mailbox = Mailbox { - regs: unsafe { StaticRef::new(MAILBOX0_BASE as *mut MailboxRegisters) }, - 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_hiwater: MAILBOX1_HIWATER_IRQ, - plic_irq_lowater: MAILBOX1_LOWATER_IRQ, +pub const MAILBOX: Mailbox = Mailbox { + regs: unsafe { StaticRef::new(MAILBOX_BASE as *mut MailboxRegisters) }, + plic_wtirq: MAILBOX_WTIRQ, + plic_rtirq: MAILBOX_RTIRQ, + plic_eirq: MAILBOX_EIRQ, }; // 32-bit-word bitfield helpers for the PLIC's irq enable lines. @@ -76,74 +77,61 @@ } 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); + // Synchronously send a message. Blocks the caller if there's no room in the + // outbox. + pub fn send_message_sync(&self, message: &[u32]) { for i in 0..message.len() { - self.wait_irq_lowater(); - self.regs.reg_write.set(message[i]); + // NOTE: The spec for the TLUL mailbox says that the write interrupt + // fires when the FIFO is _above_ the threshold, which is useless + // for us as we need to wait for it to fall _below_ a threshold. + // So, we have to busy-wait here, which is slightly annoying. + while (self.regs.reg_status.get() & STATUS_BIT_FULL) != 0 {} + self.regs.reg_mboxw.set(message[i]); } - self.regs.reg_ienable.set(0); - self.regs.reg_lowater.set(0); } - 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.wait_irq_hiwater(); - message[i] = self.regs.reg_read.get(); + // Synchronously wait on a message to arrive. Blocks the caller if no + // message is in the inbox. + + pub fn wait_recv_sync(&self) { + // Set the mailbox to fire the read interrupt when there's more than + // zero items in it and enable the corresponding IRQ line in the PLIC so + // the interrupt will make it through to the CPU. + self.regs.reg_rirqt.set(0); + self.regs.reg_intr_enable.set(INTR_ENABLE_BIT_RTIRQ); + + unsafe { + set_bit(plic::PLIC_EN0, self.plic_rtirq as isize); } - 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 - // between the 'if (*flag == 0)' and 'asm("wfi")' statements, we could end up - // waiting forever for an interrupt that had already arrived. To prevent that, - // we have to globally disable interrupts around those statements. Once - // an interrupt has been triggered, 'wfi' will continue and the isr will - // execute immediately after global interrupts are re-enabled. - - unsafe fn wait_irq_hiwater(&self) { - while (self.regs.reg_status.get() & MAILBOX_STATUS_HIWATER) == 0 { - clear_mstatus_bits(MIE_BIT | SIE_BIT); - set_bit(plic::PLIC_EN0, self.plic_irq_hiwater); - if (self.regs.reg_status.get() & MAILBOX_STATUS_HIWATER) == 0 { + // Wait for the read interrupt to fire. + while (self.regs.reg_intr_state.get() & INTR_STATE_BIT_RTIRQ) == 0 { + unsafe { 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); + // NOTE: We don't need to deal with the PLIC claim/complete registers here + // as we're disabling the interrupt immediately after it arrives and before + // any other bit of code has a chance to claim it. + + // Turn the interrupt and IRQ line back off. + unsafe { + clear_bit(plic::PLIC_EN0, self.plic_rtirq as isize); + } + self.regs.reg_intr_enable.set(0); + + // Ack it in the mailbox so it doesn't re-fire later. + self.regs.reg_intr_state.set(INTR_STATE_BIT_RTIRQ); } - unsafe fn wait_irq_lowater(&self) { - while (self.regs.reg_status.get() & MAILBOX_STATUS_LOWATER) == 0 { - clear_mstatus_bits(MIE_BIT | SIE_BIT); - set_bit(plic::PLIC_EN0, self.plic_irq_lowater); - if (self.regs.reg_status.get() & MAILBOX_STATUS_LOWATER) == 0 { - asm!("wfi"); + // Synchronously receive a message. Blocks the caller if no message is in + // the inbox. + pub unsafe fn recv_message_sync(&self, message: &mut [u32]) { + for i in 0..message.len() { + while (self.regs.reg_status.get() & STATUS_BIT_EMPTY) != 0 { + self.wait_recv_sync(); } - clear_bit(plic::PLIC_EN0, self.plic_irq_lowater); - set_mstatus_bits(MIE_BIT | SIE_BIT); + message[i] = self.regs.reg_mboxr.get(); } - self.regs.reg_istate.set(IRQ_LOWATER); } }
diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 546bbb9..d0b0179 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs
@@ -62,15 +62,16 @@ transmute(sel4_pend - elf_loader::elf_phys_min(capdl_segments)), transmute((*capdl_elf).e_entry), ]; - matcha_hal::mailbox::MAILBOX1.send_message(&message); + matcha_hal::mailbox::MAILBOX.send_message_sync(&message); dprintf!("seL4 boot message posted\n"); SMC_CONTROL_BLOCK.write_volatile(entry_point); - dprintf!("SMC started, waiting on mailbox to clear\n"); + dprintf!("SMC started, waiting on ACK\n"); - matcha_hal::mailbox::MAILBOX1.wait_lowater(0); + let mut reply : [u32;1] = [0]; + matcha_hal::mailbox::MAILBOX.recv_message_sync(&mut reply); - dprintf!("Mailbox cleared\n"); + dprintf!("ACK = 0x{:08X} received, SMC is starting.\n", reply[0]); } }