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]);
}
}