Update mailbox to support 'short' (28-byte) and 'long' (4k) messages.
MatchaApp has a trivial message-handling loop that just edits messages and sends them back.
Added support for async wait in MatchaApp, implemented in mailbox_client.rs
I also ran "cargo fmt", which shuffled a few "use" lines.
MailboxHAL is now static_init'd by MatchaPlatform and components/capsules can hold a static ref to it.
Mailbox IRQs now go through a trivial ISR trait.
Platform-specific mailbox constants moved to matcha/config/src/lib.rs
Change-Id: I8860a76b4b8e3c712206dff61bfb1faee156a657
diff --git a/app/src/mailbox_client.rs b/app/src/mailbox_client.rs
new file mode 100644
index 0000000..ca21744
--- /dev/null
+++ b/app/src/mailbox_client.rs
@@ -0,0 +1,69 @@
+use core::cell::Cell;
+use core::ptr;
+use libtock::futures;
+use libtock::syscalls;
+use matcha_config::*;
+
+//------------------------------------------------------------------------------
+
+pub struct MailboxClient {}
+
+impl MailboxClient {
+ pub fn init() {
+ let _ = syscalls::command(CAPSULE_MAILBOX, CMD_MAILBOX_INIT, 0, 0);
+ }
+
+ pub async fn wait_message_async(buffer: &mut [u8]) -> usize {
+ // To coordinate with async/await we need a "global-ish" flag that can
+ // have multiple mutable references. The Tock workaround is to put those
+ // kind of things in 'Cells'.
+ let message_size: Cell<usize> = Cell::new(0);
+
+ // The mailbox capsule will notify us using this callback,
+ unsafe extern "C" fn subscribe_callback(_ok: usize, _len: usize, _: usize, data: usize) {
+ // which just sets the message flag to true.
+ let message_size = &*(data as *const Cell<usize>);
+ message_size.set(_len)
+ }
+
+ // We give Tock a reference to our message and our callback+flag
+ unsafe {
+ syscalls::raw::allow(
+ CAPSULE_MAILBOX,
+ CMD_MAILBOX_RECV,
+ buffer.as_ptr() as *mut u8,
+ buffer.len(),
+ );
+ syscalls::raw::subscribe(
+ CAPSULE_MAILBOX,
+ CMD_MAILBOX_RECV,
+ subscribe_callback as *const _,
+ &message_size as *const _ as usize,
+ );
+ }
+
+ // and asynchronously wait until the callback sets the flag.
+ futures::wait_until(|| (message_size.get() > 0)).await;
+
+ // Then we clear Tock's references to our message and callback.
+ unsafe {
+ syscalls::raw::subscribe(CAPSULE_MAILBOX, 0, ptr::null(), 0);
+ syscalls::raw::allow(CAPSULE_MAILBOX, 0, ptr::null_mut(), 0);
+ }
+
+ return message_size.get();
+ }
+
+ pub fn send_message_sync(size: usize, buffer: &[u8]) {
+ unsafe {
+ syscalls::raw::allow(
+ CAPSULE_MAILBOX,
+ CMD_MAILBOX_SEND,
+ buffer.as_ptr() as *mut u8,
+ buffer.len(),
+ );
+ let _ = syscalls::command(CAPSULE_MAILBOX, CMD_MAILBOX_SEND, size, 0);
+ syscalls::raw::allow(CAPSULE_MAILBOX, CMD_MAILBOX_SEND, ptr::null_mut(), 0);
+ }
+ }
+}
diff --git a/app/src/main.rs b/app/src/main.rs
index f06d653..dcb381b 100644
--- a/app/src/main.rs
+++ b/app/src/main.rs
@@ -1,22 +1,58 @@
#![no_std]
+mod dprintf;
+mod mailbox_client;
+
+use crate::mailbox_client::MailboxClient;
use libtock::result::TockResult;
use libtock::syscalls;
use matcha_config::*;
-mod dprintf;
-
libtock_core::stack_size! {0x1000}
+// Global static message buffer. The mailbox driver will copy incoming messages
+// here when they arrive.
+static mut MESSAGE_BUF: [u8; 4096] = [0; 4096];
+
//------------------------------------------------------------------------------
#[libtock::main]
async fn main() -> TockResult<()> {
- dprintf!("sw/matcha/app/src/main.rs::main()\n");
+ dprintf!("SEC: sw/matcha/app/src/main.rs::main()\n");
- dprintf!("Booting sel4 from TockOS app!\n");
+ dprintf!("SEC: Booting sel4 from TockOS app!\n");
let _result = syscalls::command(CAPSULE_ELFLOADER, CMD_ELFLOADER_BOOT_SEL4, 0, 0);
- dprintf!("Booting sel4 from TockOS app done!\n");
+ dprintf!("SEC: Booting sel4 from TockOS app done!\n");
- Ok(())
+ MailboxClient::init();
+
+ unsafe {
+ loop {
+ dprintf!("SEC: Waiting for request\n");
+ let message_size = MailboxClient::wait_message_async(&mut MESSAGE_BUF).await;
+ dprintf!("SEC: Request arrived, {} bytes\n", message_size);
+
+ let dst: *mut u32 = core::mem::transmute(MESSAGE_BUF.as_ptr());
+
+ // We don't actually have any message handling set up, so we just
+ // tweak the first and last dword of the message so the sender can
+ // tell we were able to modify it.
+ let last = (message_size / 4) - 1;
+ dprintf!("request[0] = {:X}\n", dst.offset(0).read());
+ dprintf!(
+ "request[{}] = {:X}\n",
+ last,
+ dst.offset(last as isize).read()
+ );
+
+ dst.offset(0).write(0x12345678);
+ dst.offset(last as isize).write(0x87654321);
+
+ dprintf!("SEC: Sending response\n");
+ MailboxClient::send_message_sync(message_size, &MESSAGE_BUF);
+
+ dprintf!("SEC: Response sent\n");
+ }
+ }
+ // Unreachable
}
diff --git a/capsules/src/elfloader_capsule.rs b/capsules/src/elfloader_capsule.rs
index 393fec5..5cd6780 100644
--- a/capsules/src/elfloader_capsule.rs
+++ b/capsules/src/elfloader_capsule.rs
@@ -2,8 +2,21 @@
use kernel::{AppId, AppSlice, Callback, Driver, ReturnCode, Shared};
use matcha_hal::dprintf;
+use matcha_hal::mailbox_hal::MailboxHAL;
-pub struct ElfLoaderCapsule {}
+pub struct ElfLoaderCapsule {
+ mailbox_hal: Option<&'static dyn MailboxHAL>,
+}
+
+impl ElfLoaderCapsule {
+ pub fn new() -> Self {
+ Self { mailbox_hal: None }
+ }
+
+ pub fn set_mailbox(&mut self, mailbox: &'static dyn MailboxHAL) {
+ self.mailbox_hal = Some(mailbox);
+ }
+}
impl Driver for ElfLoaderCapsule {
fn subscribe(&self, _: usize, _: Option<Callback>, _: AppId) -> ReturnCode {
@@ -14,7 +27,7 @@
dprintf!("ElfLoaderCapsule::command()\n");
if minor_num == matcha_config::CMD_ELFLOADER_BOOT_SEL4 {
- matcha_utils::load_sel4();
+ self.mailbox_hal.map(|mb| matcha_utils::load_sel4(mb));
return ReturnCode::SUCCESS;
}
diff --git a/capsules/src/mailbox_capsule.rs b/capsules/src/mailbox_capsule.rs
index 1b93b0d..e097131 100644
--- a/capsules/src/mailbox_capsule.rs
+++ b/capsules/src/mailbox_capsule.rs
@@ -1,29 +1,207 @@
-//! Stub Shodan mailbox driver capsule.
-
+use core::cell::Cell;
use kernel::{AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
+use matcha_hal::dprintf;
+use matcha_hal::mailbox_hal::MailboxHAL;
-#[derive(Default)]
+use matcha_config::*;
+
+//------------------------------------------------------------------------------
+
pub struct AppData {
- pub buffer: Option<AppSlice<Shared, u8>>,
+ pub send_callback: Option<Callback>,
+ pub recv_callback: Option<Callback>,
+ pub send_buffer: Option<AppSlice<Shared, u8>>,
+ pub recv_buffer: Option<AppSlice<Shared, u8>>,
}
+impl Default for AppData {
+ fn default() -> AppData {
+ AppData {
+ send_callback: None,
+ recv_callback: None,
+ send_buffer: None,
+ recv_buffer: None,
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// MailboxCapsule is a global static singleton - its members have to be
+// pseudo-const.
+
pub struct MailboxCapsule {
pub app_data_grant: Grant<AppData>,
+ pub mailbox_hal: Cell<Option<&'static dyn MailboxHAL>>,
+ pub current_app: Cell<Option<AppId>>,
}
+//----------------------------------------
+
+impl MailboxCapsule {
+ pub fn new(app_data_grant: Grant<AppData>) -> Self {
+ dprintf!("MailboxCapsule::new()\n");
+
+ return MailboxCapsule {
+ app_data_grant: app_data_grant,
+ mailbox_hal: Cell::new(None),
+ current_app: Cell::new(None),
+ };
+ }
+
+ pub fn set_mailbox(&self, mailbox: &'static dyn MailboxHAL) {
+ self.mailbox_hal.set(Some(mailbox));
+ }
+
+ pub fn handle_command(
+ &self,
+ app_id: AppId,
+ app_data: &mut AppData,
+ cmd_num: usize,
+ arg2: usize,
+ _arg3: usize,
+ ) -> ReturnCode {
+ match cmd_num {
+ CMD_MAILBOX_INIT => {
+ self.current_app.set(Some(app_id));
+ ReturnCode::SUCCESS
+ }
+ CMD_MAILBOX_SEND => {
+ let message_len = arg2;
+ match &app_data.send_buffer {
+ Some(buffer) => {
+ self.mailbox_hal.get().map(|mb| {
+ let _ = mb.send_message_slice_sync(message_len, buffer);
+ });
+ ReturnCode::SUCCESS
+ }
+ None => ReturnCode::FAIL,
+ }
+ }
+ CMD_MAILBOX_RECV => {
+ // Nothing to do here yet?
+ ReturnCode::SUCCESS
+ }
+ _ => {
+ dprintf!("MailboxCapsule::handle_command({:?}, ????)\n", app_id);
+ ReturnCode::EINVAL
+ }
+ }
+ }
+
+ pub fn handle_subscribe(
+ &self,
+ _app_id: AppId,
+ app_data: &mut AppData,
+ minor_num: usize,
+ callback: Option<Callback>,
+ ) -> ReturnCode {
+ match minor_num {
+ CMD_MAILBOX_SEND => {
+ app_data.send_callback = callback;
+ ReturnCode::SUCCESS
+ }
+ CMD_MAILBOX_RECV => {
+ app_data.recv_callback = callback;
+ ReturnCode::SUCCESS
+ }
+ _ => ReturnCode::EINVAL,
+ }
+ }
+
+ pub fn handle_allow(
+ &self,
+ _app_id: AppId,
+ app_data: &mut AppData,
+ minor_num: usize,
+ slice: Option<AppSlice<Shared, u8>>,
+ ) -> ReturnCode {
+ match minor_num {
+ CMD_MAILBOX_SEND => {
+ app_data.send_buffer = slice;
+ ReturnCode::SUCCESS
+ }
+ CMD_MAILBOX_RECV => {
+ app_data.recv_buffer = slice;
+ ReturnCode::SUCCESS
+ }
+ _ => ReturnCode::EINVAL,
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Our "driver" trait impl just unwraps the app data grant and delegates to
+// MailboxCapsule.
+
impl Driver for MailboxCapsule {
- fn subscribe(&self, _: usize, _: Option<Callback>, _: AppId) -> ReturnCode {
- ReturnCode::EINVAL
+ fn subscribe(&self, minor_num: usize, callback: Option<Callback>, app_id: AppId) -> ReturnCode {
+ self.app_data_grant
+ .enter(app_id, |app_data, _| {
+ self.handle_subscribe(app_id, app_data, minor_num, callback)
+ })
+ .unwrap_or_else(|err| err.into())
}
- fn command(&self, _minor_num: usize, _r2: usize, _r3: usize, _app_id: AppId) -> ReturnCode {
- return ReturnCode::EINVAL;
+ fn command(&self, cmd_num: usize, r2: usize, r3: usize, app_id: AppId) -> ReturnCode {
+ self.app_data_grant
+ .enter(app_id, |app_data, _| {
+ self.handle_command(app_id, app_data, cmd_num, r2, r3)
+ })
+ .unwrap_or_else(|err| err.into())
}
- fn allow(&self, app_id: AppId, _: usize, slice: Option<AppSlice<Shared, u8>>) -> ReturnCode {
- let _ = self.app_data_grant.enter(app_id, |app_data, _| {
- app_data.buffer = slice;
+ fn allow(
+ &self,
+ app_id: AppId,
+ minor_num: usize,
+ slice: Option<AppSlice<Shared, u8>>,
+ ) -> ReturnCode {
+ self.app_data_grant
+ .enter(app_id, |app_data, _| {
+ self.handle_allow(app_id, app_data, minor_num, slice)
+ })
+ .unwrap_or_else(|err| err.into())
+ }
+}
+
+//------------------------------------------------------------------------------
+
+impl matcha_hal::mailbox_hal::MailboxISR for MailboxCapsule {
+ fn on_wtirq(&self) {
+ // Nothing to do here at the moment, sends are synchronous and the
+ // polarity of the ISR is backwards.
+ }
+
+ // When a message arrives, we copy it to the waiting app's buffer (if there
+ // is one) and then fire the app's callback. Async waiting is handled in
+ // app/src/mailbox_client.rs
+ fn on_rtirq(&self) {
+ // Unwrap all our optionals. There's probably a better way to do this.
+ self.current_app.get().map(|app_id| {
+ self.app_data_grant.enter(app_id, |app_data, _| {
+ app_data.recv_buffer.as_ref().map(|buffer| {
+ self.mailbox_hal.get().map(|mailbox| {
+ // Copy the message to the app's buffer and then
+ // schedule the app callback.
+ match mailbox.get_message_slice_sync(&buffer) {
+ Ok(len) => {
+ app_data.recv_callback.map(|mut callback| {
+ callback.schedule(1, len, 0);
+ });
+ }
+ Err(len) => {
+ app_data.recv_callback.map(|mut callback| {
+ callback.schedule(0, len, 0);
+ });
+ }
+ }
+ });
+ });
+ })
});
- return ReturnCode::SUCCESS;
+ }
+
+ fn on_eirq(&self) {
+ // We should probably do something here eventually.
}
}
diff --git a/config/src/lib.rs b/config/src/lib.rs
index 155222f..f7a74cc 100644
--- a/config/src/lib.rs
+++ b/config/src/lib.rs
@@ -15,8 +15,21 @@
pub const CAPSULE_ELFLOADER: usize = 0x50004;
pub const CAPSULE_MAILBOX: usize = 0x50005;
+pub const CMD_MAILBOX_INIT: usize = 1;
+pub const CMD_MAILBOX_SEND: usize = 2;
+pub const CMD_MAILBOX_RECV: usize = 3;
+pub const CMD_MAILBOX_SEND_PAGE: usize = 4;
+pub const CMD_MAILBOX_RECV_PAGE: usize = 5;
+
pub const CMD_ELFLOADER_BOOT_SEL4: usize = 10;
pub const CMD_DPRINTF_PRINT: usize = 0;
pub const IRQ_UART0_TX_WATERMARK: u32 = 1; // kTopMatchaPlicIrqIdUart0TxWatermark @ top_matcha.h
pub const IRQ_UART0_RX_PARITY_ERR: u32 = 8; // kTopMatchaPlicIrqIdUart0RxParityErr @ top_matcha.h
+
+pub const PLIC_BASE: u32 = 0x48000000; // TOP_MATCHA_RV_PLIC_BASE_ADDR
+
+pub const MAILBOX_BASE: u32 = 0x40800000; // TOP_MATCHA_MAILBOX_SEC_BASE_ADDR
+pub const MAILBOX_WTIRQ: u32 = 181; // kTopMatchaPlicIrqIdMailboxSecWtirq
+pub const MAILBOX_RTIRQ: u32 = 182; // kTopMatchaPlicIrqIdMailboxSecRtirq
+pub const MAILBOX_EIRQ: u32 = 183; // kTopMatchaPlicIrqIdMailboxSecEirq
\ No newline at end of file
diff --git a/hal/src/mailbox_hal.rs b/hal/src/mailbox_hal.rs
index cff310d..f28a76f 100644
--- a/hal/src/mailbox_hal.rs
+++ b/hal/src/mailbox_hal.rs
@@ -4,22 +4,28 @@
// used in this file.
#![allow(dead_code)]
+use core::cell::Cell;
use core::mem::transmute;
+use core::slice;
use kernel::common::registers::{register_structs, ReadOnly, ReadWrite, WriteOnly};
use kernel::common::StaticRef;
+use kernel::{AppSlice, Shared};
use crate::*;
-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
+//------------------------------------------------------------------------------
+
+// The high bit of the message header is used to distinguish between "inline"
+// messages that fit in the mailbox and "long" messages that contain the
+// physical address of a memory page containing the message.
+const HEADER_FLAG_LONG_MESSAGE: u32 = 0x80000000;
const MAILBOX_SIZE_DWORDS: u32 = 8;
const INTR_STATE_BIT_WTIRQ: u32 = 0b001;
const INTR_STATE_BIT_RTIRQ: u32 = 0b010;
const INTR_STATE_BIT_EIRQ: u32 = 0b100;
+const INTR_STATE_MASK: u32 = 0b111;
const INTR_ENABLE_BIT_WTIRQ: u32 = 0b001;
const INTR_ENABLE_BIT_RTIRQ: u32 = 0b010;
@@ -30,6 +36,8 @@
const STATUS_BIT_WFIFOL: u32 = 0b0100;
const STATUS_BIT_RFIFOL: u32 = 0b1000;
+//------------------------------------------------------------------------------
+
register_structs! {
pub MailboxRegisters {
(0x000 => reg_intr_state: ReadWrite<u32>),
@@ -42,96 +50,232 @@
(0x01C => reg_wirqt: ReadWrite<u32>),
(0x020 => reg_rirqt: ReadWrite<u32>),
(0x024 => reg_ctrl: WriteOnly<u32>),
- (0x028 => @END),
+ (0x028 => reg_fifor: ReadOnly<u32>),
+ (0x02C => reg_fifow: ReadOnly<u32>),
+ (0x030 => @END),
}
}
-pub struct MailboxHAL {
+//------------------------------------------------------------------------------
+
+pub trait MailboxISR {
+ fn on_wtirq(&self);
+ fn on_rtirq(&self);
+ fn on_eirq(&self);
+}
+
+//------------------------------------------------------------------------------
+
+pub trait MailboxHAL {
+ fn get_message_sync(&self, buf: &mut [u8]) -> Result<usize, usize>;
+ fn send_message_sync(&self, message_size: usize, buf: &[u8]) -> Result<usize, usize>;
+ fn get_message_slice_sync(&self, slice: &AppSlice<Shared, u8>) -> Result<usize, usize>;
+ fn send_message_slice_sync(
+ &self,
+ message_len: usize,
+ slice: &AppSlice<Shared, u8>,
+ ) -> Result<usize, usize>;
+}
+
+//------------------------------------------------------------------------------
+
+pub struct MailboxImpl {
regs: StaticRef<MailboxRegisters>,
- plic_wtirq: u32,
- plic_rtirq: u32,
- plic_eirq: u32,
+
+ // PLIC line for write interrupt
+ wtirq: u32,
+
+ // PLIC line for read interrupt
+ rtirq: u32,
+
+ // PLIC line for error interrupt
+ eirq: u32,
+
+ // Client interrupt callback
+ client_isr: Cell<Option<&'static dyn MailboxISR>>,
+
+ // Physical address of the memory page used to send the last message, if it
+ // was a long message.
+ message_page: Cell<u32>,
}
-pub const MAILBOX: MailboxHAL = MailboxHAL {
- 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.
+impl MailboxImpl {
+ pub fn new(mailbox_base_addr: u32, wtirq: u32, rtirq: u32, eirq: u32) -> Self {
+ let inst = Self {
+ regs: unsafe { StaticRef::new(mailbox_base_addr as *mut MailboxRegisters) },
+ wtirq: wtirq,
+ rtirq: rtirq,
+ eirq: eirq,
+ client_isr: Cell::new(None),
+ message_page: Cell::new(0),
+ };
-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();
- bits |= 1 << (bit_index & 31);
- buf.offset(bit_index >> 5).write_volatile(bits);
-}
+ // Clear any old interrupts and enable receive and error interrupts.
+ inst.regs.reg_wirqt.set(0);
+ inst.regs.reg_rirqt.set(0);
+ inst.regs.reg_intr_state.set(INTR_STATE_MASK); // W1C
+ inst.regs
+ .reg_intr_enable
+ .set(INTR_ENABLE_BIT_RTIRQ | INTR_ENABLE_BIT_EIRQ);
+ unsafe {
+ MailboxImpl::set_bit(plic_hal::PLIC_EN0, inst.rtirq as isize);
+ MailboxImpl::set_bit(plic_hal::PLIC_EN0, inst.eirq as isize);
+ }
-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);
-}
+ return inst;
+ }
-impl MailboxHAL {
- // 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() {
- // 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]);
+ pub fn set_client_isr(&self, client: &'static dyn MailboxISR) {
+ self.client_isr.set(Some(client));
+ }
+
+ //--------------------------------------------------------------------------
+ // 32-bit-word bitfield helpers for the PLIC's irq enable lines.
+
+ 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();
+ bits |= 1 << (bit_index & 31);
+ buf.offset(bit_index >> 5).write_volatile(bits);
+ }
+
+ 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);
+ }
+
+ fn enqueue_u32(&self, x: u32) {
+ while (self.regs.reg_status.get() & STATUS_BIT_FULL) == STATUS_BIT_FULL {}
+ self.regs.reg_mboxw.set(x);
+ }
+
+ fn dequeue_u32(&self) -> u32 {
+ while (self.regs.reg_status.get() & STATUS_BIT_EMPTY) == STATUS_BIT_EMPTY {}
+ return self.regs.reg_mboxr.get();
+ }
+
+ pub fn drain_read_fifo(&self) {
+ while (self.regs.reg_status.get() & STATUS_BIT_EMPTY) == 0 {
+ let _ = self.regs.reg_mboxr.get();
}
}
- // Synchronously wait on a message to arrive. Blocks the caller if no
- // message is in the inbox.
+ pub fn dump(&self) {
+ dprintf!("reg_intr_state {:x}\n", self.regs.reg_intr_state.get());
+ dprintf!("reg_intr_enable {:x}\n", self.regs.reg_intr_enable.get());
+ dprintf!("reg_intr_test {:x}\n", self.regs.reg_intr_test.get());
+ dprintf!("reg_status {:x}\n", self.regs.reg_status.get());
+ dprintf!("reg_error {:x}\n", self.regs.reg_error.get());
+ dprintf!("reg_wirqt {:x}\n", self.regs.reg_wirqt.get());
+ dprintf!("reg_rirqt {:x}\n", self.regs.reg_rirqt.get());
+ dprintf!("reg_fifor {:x}\n", self.regs.reg_fifor.get());
+ dprintf!("reg_fifow {:x}\n", self.regs.reg_fifow.get());
+ }
+}
- 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_hal::PLIC_EN0, self.plic_rtirq as isize);
- }
+impl MailboxHAL for MailboxImpl {
+ fn get_message_sync(&self, buf: &mut [u8]) -> Result<usize, usize> {
+ let header = self.dequeue_u32();
+ let message_size = header & !HEADER_FLAG_LONG_MESSAGE;
- // Wait for the read interrupt to fire.
- while (self.regs.reg_intr_state.get() & INTR_STATE_BIT_RTIRQ) == 0 {
+ if (header & HEADER_FLAG_LONG_MESSAGE) != 0 {
+ // Long message, copy from message page to result buffer.
+ let message_page = self.dequeue_u32();
+ self.message_page.set(message_page);
unsafe {
- asm!("wfi");
+ let src: *const u8 = transmute(message_page);
+ let dst: *mut u8 = transmute(buf.as_ptr());
+ core::ptr::copy_nonoverlapping(src, dst, message_size as usize);
+ }
+ } else {
+ // Short message, copy from mailbox to result buffer.
+ unsafe {
+ let dst: *mut u32 = transmute(buf.as_ptr());
+ let message_dwords = message_size / 4;
+ for i in 0..message_dwords {
+ dst.offset(i as isize).write(self.dequeue_u32());
+ }
}
}
- // 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.
+ return Ok(message_size as usize);
+ }
- // Turn the interrupt and IRQ line back off.
+ fn send_message_sync(&self, message_size: usize, buf: &[u8]) -> Result<usize, usize> {
unsafe {
- clear_bit(plic_hal::PLIC_EN0, self.plic_rtirq as isize);
- }
- self.regs.reg_intr_enable.set(0);
+ let message_page = self.message_page.get();
+ if message_page != 0 {
+ // Long message, reply using the same physical page as the request.
+ let src: *const u8 = transmute(buf.as_ptr());
+ let dst: *mut u8 = transmute(message_page);
+ core::ptr::copy_nonoverlapping(src, dst, message_size);
- // Ack it in the mailbox so it doesn't re-fire later.
+ self.enqueue_u32(0x80000000 | message_size as u32);
+ self.enqueue_u32(message_page);
+
+ // Response sent, clear cached page address
+ self.message_page.set(0);
+ } else {
+ // Short message, respond via the mailbox.
+ let src: *const u32 = transmute(buf.as_ptr());
+ let message_dwords = message_size / 4;
+ self.enqueue_u32(message_size as u32);
+ for i in 0..message_dwords {
+ self.enqueue_u32(src.offset(i as isize).read());
+ }
+ }
+ }
+
+ return Ok(message_size);
+ }
+
+ fn get_message_slice_sync(&self, slice: &AppSlice<Shared, u8>) -> Result<usize, usize> {
+ unsafe {
+ let buf: &mut [u8] = slice::from_raw_parts_mut(slice.ptr() as *mut u8, slice.len());
+ return self.get_message_sync(buf);
+ }
+ }
+
+ fn send_message_slice_sync(
+ &self,
+ message_len: usize,
+ slice: &AppSlice<Shared, u8>,
+ ) -> Result<usize, usize> {
+ unsafe {
+ let buf: &[u8] = slice::from_raw_parts(slice.ptr() as *const u8, slice.len());
+ return self.send_message_sync(message_len, buf);
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// The interrupt handler in platform/src/chip.rs will call these handlers, which
+// in turn call the capsule-level handlers and then clear the interrupt.
+
+impl MailboxISR for MailboxImpl {
+ fn on_wtirq(&self) {
+ self.client_isr.get().map(|c| c.on_wtirq());
+ self.regs.reg_intr_state.set(INTR_STATE_BIT_WTIRQ);
+ }
+
+ fn on_rtirq(&self) {
+ self.client_isr.get().map(|c| c.on_rtirq());
+ if (self.regs.reg_status.get() & STATUS_BIT_EMPTY) == 0 {
+ self.drain_read_fifo();
+ }
self.regs.reg_intr_state.set(INTR_STATE_BIT_RTIRQ);
}
- // 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();
- }
- message[i] = self.regs.reg_mboxr.get();
- }
+ fn on_eirq(&self) {
+ dprintf!("MailboxImpl::on_eirq fired - this is probably bad\n");
+ self.client_isr.get().map(|c| c.on_eirq());
+ self.regs.reg_intr_state.set(INTR_STATE_BIT_EIRQ);
}
}
+
+//------------------------------------------------------------------------------
diff --git a/platform/src/chip.rs b/platform/src/chip.rs
index f339bbf..96d1b4a 100644
--- a/platform/src/chip.rs
+++ b/platform/src/chip.rs
@@ -13,6 +13,7 @@
use crate::uart;
use matcha_hal::plic_hal;
use matcha_config::*;
+use matcha_hal::mailbox_hal::MailboxISR;
PMPConfigMacro!(4);
@@ -25,6 +26,7 @@
userspace_kernel_boundary: SysCall,
pmp: PMP,
scheduler_timer: kernel::VirtualSchedulerTimer<A>,
+ mailbox_isr: Cell<Option<&'static dyn MailboxISR>>,
}
impl<A: 'static + Alarm<'static>> Matcha<A> {
@@ -33,9 +35,14 @@
userspace_kernel_boundary: SysCall::new(),
pmp: PMP::new(),
scheduler_timer: kernel::VirtualSchedulerTimer::new(alarm),
+ mailbox_isr: Cell::new(None),
}
}
+ pub fn set_mailbox_isr(&self, mailbox_isr : &'static dyn MailboxISR) {
+ self.mailbox_isr.set(Some(mailbox_isr));
+ }
+
pub unsafe fn enable_plic_interrupts(&self) {
plic_hal::disable_all();
plic_hal::clear_all_pending();
@@ -46,6 +53,9 @@
while let Some(interrupt) = plic_hal::next_pending() {
match interrupt {
IRQ_UART0_TX_WATERMARK..=IRQ_UART0_RX_PARITY_ERR => uart::UART0.handle_interrupt(),
+ MAILBOX_WTIRQ => {self.mailbox_isr.get().map(|mb| mb.on_wtirq());},
+ MAILBOX_RTIRQ => {self.mailbox_isr.get().map(|mb| mb.on_rtirq());},
+ MAILBOX_EIRQ => {self.mailbox_isr.get().map(|mb| mb.on_eirq());},
_ => debug!("Pidx {}", interrupt),
}
plic_hal::complete(interrupt);
diff --git a/platform/src/main.rs b/platform/src/main.rs
index 9a811b2..00935f9 100644
--- a/platform/src/main.rs
+++ b/platform/src/main.rs
@@ -19,6 +19,7 @@
use matcha_capsules::dprintf_capsule::DprintfCapsule;
use matcha_capsules::elfloader_capsule::ElfLoaderCapsule;
use matcha_capsules::storage_capsule::StorageCapsule;
+use matcha_capsules::mailbox_capsule::MailboxCapsule;
use matcha_config::*;
use matcha_hal::dprintf;
use rv32i::csr;
@@ -70,6 +71,7 @@
dprintf_capsule: &'static DprintfCapsule,
storage_capsule: &'static StorageCapsule,
elfloader_capsule: &'static ElfLoaderCapsule,
+ mailbox_capsule: &'static MailboxCapsule,
lldb_capsule: &'static capsules::low_level_debug::LowLevelDebug<
'static,
capsules::virtual_uart::UartDevice<'static>,
@@ -88,6 +90,7 @@
CAPSULE_DPRINTF => f(Some(self.dprintf_capsule)),
CAPSULE_STORAGE => f(Some(self.storage_capsule)),
CAPSULE_ELFLOADER => f(Some(self.elfloader_capsule)),
+ CAPSULE_MAILBOX => f(Some(self.mailbox_capsule)),
CAPSULE_LLDB => f(Some(self.lldb_capsule)),
_ => f(None),
}
@@ -197,17 +200,46 @@
)
);
+ // Construct a mailbox that points to the platform-specific base address and
+ // PLIC lines
+ let mailbox_hal : &'static mut matcha_hal::mailbox_hal::MailboxImpl = static_init!(
+ matcha_hal::mailbox_hal::MailboxImpl,
+ matcha_hal::mailbox_hal::MailboxImpl::new(
+ MAILBOX_BASE,
+ MAILBOX_WTIRQ,
+ MAILBOX_RTIRQ,
+ MAILBOX_EIRQ,
+ )
+ );
+
let elfloader_capsule = static_init!(
matcha_capsules::elfloader_capsule::ElfLoaderCapsule,
- matcha_capsules::elfloader_capsule::ElfLoaderCapsule {}
+ matcha_capsules::elfloader_capsule::ElfLoaderCapsule::new()
);
+ let mailbox_capsule = static_init!(
+ matcha_capsules::mailbox_capsule::MailboxCapsule,
+ matcha_capsules::mailbox_capsule::MailboxCapsule::new(
+ board_kernel.create_grant(&memory_allocation_cap),
+ )
+ );
+
+ // Mailbox interrupts are delegated to the capsule, except for the
+ // bottom-level bit twiddling.
+ mailbox_hal.set_client_isr(mailbox_capsule);
+
+ // Our other capsules and our "chip" hold references to the mailbox.
+ mailbox_capsule.set_mailbox(mailbox_hal);
+ elfloader_capsule.set_mailbox(mailbox_hal);
+ chip.set_mailbox_isr(mailbox_hal);
+
let platform = MatchaPlatform {
console_capsule: console_capsule,
alarm_capsule: alarm_capsule,
dprintf_capsule: dprintf_capsule,
storage_capsule: storage_capsule,
elfloader_capsule: elfloader_capsule,
+ mailbox_capsule: mailbox_capsule,
lldb_capsule: lldb_capsule,
};
diff --git a/utils/src/lib.rs b/utils/src/lib.rs
index 03c45c6..37b2cd8 100644
--- a/utils/src/lib.rs
+++ b/utils/src/lib.rs
@@ -1,7 +1,7 @@
#![no_std]
-use core::mem::transmute;
use matcha_hal::dprintf;
+use matcha_hal::mailbox_hal::MailboxHAL;
pub mod elf_loader;
pub mod tar_loader;
@@ -13,7 +13,7 @@
}
// TODO(aappleby): Break this down into smaller pieces and move into app/main
-pub fn load_sel4() {
+pub fn load_sel4(mailbox : &'static dyn MailboxHAL) {
unsafe {
// Look in our tar file for the ELFs we need to boot Shodan.
let sel4_elf = elf_loader::find_elf("kernel");
@@ -50,31 +50,15 @@
- (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),
+ let message : [u32; 4] = [
+ sel4_pend,
+ sel4_pend + round_up_to_page(elf_loader::elf_phys_max(capdl_segments) - elf_loader::elf_phys_min(capdl_segments)),
+ sel4_pend - elf_loader::elf_phys_min(capdl_segments),
+ (*capdl_elf).e_entry,
];
- matcha_hal::mailbox_hal::MAILBOX.send_message_sync(&message);
+ let _ = mailbox.send_message_sync(16, core::mem::transmute(message.as_ref()));
dprintf!("seL4 boot message posted\n");
SMC_CONTROL_BLOCK.write_volatile(entry_point);
-
- dprintf!("SMC started, waiting on ACK\n");
-
- let mut reply : [u32;1] = [0];
- matcha_hal::mailbox_hal::MAILBOX.recv_message_sync(&mut reply);
-
- dprintf!("ACK = 0x{:08X} received.\n", reply[0]);
-
- // This message is checked in shodan_boot.robot - keep in sync.
- dprintf!("load_sel4() completed successfully\n");
}
}