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