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