mailbox_client: add rpc mechanism and storage manager support
Revise the mailbox protocol between the SMC and the SEC. The SMC
writes rpc requests to the mailbox FIFO and optionally attaches
the physical address of a page frame for returning bulk data.
RPC messages are encoded with serde+postcard. Message are defined for:
- GetBuiltins -> Vec(String) of builtin filenames
- FindFile(name) -> (fid, size_bytes) locate a file in SPI flash
- GetFilePage(fid, offset) -> <data in attached page>
- Test(count) + <page> -> <modified page>
The matcha app now has a heap and global allocator (the default heap
size looks to be 1KiB).
Bug: 294433731
Change-Id: I115560455b3022f70e5488a4fd79c969b5cb1792
diff --git a/app/Cargo.toml b/app/Cargo.toml
index a48e763..3d32cd1 100644
--- a/app/Cargo.toml
+++ b/app/Cargo.toml
@@ -6,5 +6,7 @@
[dependencies]
core2 = { version = "0.3", default-features = false }
libtock = { path = "../../libtock-rs" }
-libtock_core = { path = "../../libtock-rs/core" }
+libtock_core = { path = "../../libtock-rs/core", features = ["alloc"] }
matcha_config = { path = "../config" }
+postcard = { version = "0.7", default-features = false, features = ["alloc"] }
+serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
diff --git a/app/src/dprintf.rs b/app/src/dprintf.rs
index e4aa62e..87fea90 100644
--- a/app/src/dprintf.rs
+++ b/app/src/dprintf.rs
@@ -1,7 +1,7 @@
//! App-side dprintf macro that calls through to the debug_uart capsule.
-use core2::io::{Cursor, Write};
use core::fmt;
+use core2::io::{Cursor, Write};
use libtock::syscalls;
use matcha_config::*;
diff --git a/app/src/mailbox_client.rs b/app/src/mailbox_client.rs
index ca21744..3249218 100644
--- a/app/src/mailbox_client.rs
+++ b/app/src/mailbox_client.rs
@@ -1,8 +1,63 @@
+use crate::dprintf;
+
+extern crate alloc;
+use alloc::string::{String, ToString};
+use alloc::vec::Vec;
use core::cell::Cell;
+use core::mem::{size_of, transmute};
use core::ptr;
use libtock::futures;
use libtock::syscalls;
use matcha_config::*;
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Serialize, Deserialize)]
+pub enum SECRequest<'a> {
+ GetBuiltins, // Get package names -> Vec(String)
+ FindFile(&'a str), // Find file by name -> (/*fid*/ u32, /*size*/ u32)
+ GetFilePage(u32, u32), // Read data from fid at offset -> <attached page>
+
+ Test(/*count*/ u32), // Scribble on count words of supplied page
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct GetBuiltinsResponse {
+ pub names: Vec<String>,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct FindFileResponse {
+ pub fid: u32, // Unique file identifier
+ pub size_bytes: u32, // File size
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct GetFilePageResponse {
+ pub count_bytes: u32, // Read count (bytes)
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum SECRequestError {
+ Success = 0,
+ DeserializeFailed,
+ SerializeFailed,
+ UnknownError,
+ PageInvalid,
+ FileNotFound,
+ FileOffsetInvalid,
+ // Generic errors.
+ SendFailed,
+ RecvFailed,
+}
+impl From<SECRequestError> for Result<(), SECRequestError> {
+ fn from(err: SECRequestError) -> Result<(), SECRequestError> {
+ if err == SECRequestError::Success {
+ Ok(())
+ } else {
+ Err(err)
+ }
+ }
+}
//------------------------------------------------------------------------------
@@ -11,49 +66,55 @@
impl MailboxClient {
pub fn init() {
let _ = syscalls::command(CAPSULE_MAILBOX, CMD_MAILBOX_INIT, 0, 0);
+ let _ = syscalls::command(CAPSULE_STORAGE, CMD_STORAGE_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);
+ // Waits for an inbound message that it writes to |buffer|. The msg
+ // size (in bytes) is returned along with any associated page frame
+ pub async fn wait_message_async(buffer: &mut [u8]) -> (usize, Option<usize>) {
+ // Status, message size (bytes), attached page (or 0)
+ type WaitResult = (Option<bool>, usize, Option<usize>);
+ let message_result: Cell<WaitResult> = Cell::new((None, 0, None));
- // 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)
+ // Callback on recv complete.
+ unsafe extern "C" fn subscribe_callback(ok: usize, len: usize, page: usize, data: usize) {
+ let message_result = &*(data as *const Cell<WaitResult>);
+ message_result.set((
+ Some(ok != 0),
+ len,
+ if page == 0 { None } else { Some(page) },
+ ));
}
- // We give Tock a reference to our message and our callback+flag
+ // Give Tock references to our callback, result, +buffer to write;
+ // then wait until the callback sets the message status. Finally,
+ // clear references to our local state.
unsafe {
syscalls::raw::allow(
CAPSULE_MAILBOX,
CMD_MAILBOX_RECV,
- buffer.as_ptr() as *mut u8,
+ buffer.as_mut_ptr(),
buffer.len(),
);
syscalls::raw::subscribe(
CAPSULE_MAILBOX,
CMD_MAILBOX_RECV,
subscribe_callback as *const _,
- &message_size as *const _ as usize,
+ &message_result 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.
+ futures::wait_until(|| (message_result.get().0.is_some())).await;
unsafe {
syscalls::raw::subscribe(CAPSULE_MAILBOX, 0, ptr::null(), 0);
syscalls::raw::allow(CAPSULE_MAILBOX, 0, ptr::null_mut(), 0);
}
- return message_size.get();
+ let (_, msg_size, attached_page) = message_result.into_inner();
+ (msg_size, attached_page)
}
+ // Sends the message of |size| bytes that resides in |buffer|
+ // XXX why isn't buffer.len() used?
pub fn send_message_sync(size: usize, buffer: &[u8]) {
unsafe {
syscalls::raw::allow(
@@ -66,4 +127,234 @@
syscalls::raw::allow(CAPSULE_MAILBOX, CMD_MAILBOX_SEND, ptr::null_mut(), 0);
}
}
+
+ // Parse & dispatch the request in |request_slice| and construct the result
+ // in |reply_slice|. |opt_page| holds any page attached to the request.
+ pub async fn dispatch(
+ request_slice: &[u8],
+ opt_page: Option<usize>,
+ reply_slice: &mut [u8],
+ ) -> Result<usize, SECRequestError> {
+ match postcard::from_bytes::<SECRequest>(request_slice) {
+ Ok(request) => {
+// dprintf!("dispatch {:?} opt_page {:?}\r\n", &request, opt_page);
+ match request {
+ SECRequest::GetBuiltins => {
+ MailboxClient::get_builtins_async(reply_slice).await
+ }
+ SECRequest::FindFile(name) => {
+ MailboxClient::find_file_async(name, reply_slice).await
+ }
+ SECRequest::GetFilePage(fid, offset) => {
+ MailboxClient::get_file_page_async(fid, offset, opt_page, reply_slice).await
+ }
+ SECRequest::Test(count) => {
+ MailboxClient::test_sync(count as usize, opt_page, reply_slice)
+ }
+ }
+ }
+ Err(e) => {
+ dprintf!("Invalid request: {:?}\r\n", e);
+ Err(SECRequestError::DeserializeFailed)
+ }
+ }
+ }
+
+ // Returns the names of objects that can be looked up using FindFile.
+ async fn get_builtins_async(reply_buffer: &mut [u8]) -> Result<usize, SECRequestError> {
+ const NAME_SIZE: usize = 100; // Arbitrary but matches TarHeader.name
+ let mut name: [u8; NAME_SIZE] = [0u8; NAME_SIZE];
+ let mut names = Vec::new();
+
+ let mut fid: u32 = 0; // First file in SPI
+ loop {
+ // Status, name length, next fid
+ type GetNameResult = (Option<bool>, usize, usize);
+ let get_name_result: Cell<GetNameResult> = Cell::new((None, 0, 0));
+
+ unsafe extern "C" fn get_name_callback(ok: usize, name_len: usize, next_fid: usize, data: usize) {
+ let get_name_result = &*(data as *const Cell<GetNameResult>);
+ get_name_result.set((Some(ok != 0), name_len, next_fid));
+ }
+ unsafe {
+ syscalls::raw::allow(
+ CAPSULE_STORAGE,
+ CMD_STORAGE_GET_NAME,
+ name.as_mut_ptr() as _,
+ NAME_SIZE,
+ );
+ syscalls::raw::subscribe(
+ CAPSULE_STORAGE,
+ CMD_STORAGE_GET_NAME,
+ get_name_callback as *const _,
+ &get_name_result as *const _ as usize,
+ );
+ let _ = syscalls::command(
+ CAPSULE_STORAGE,
+ CMD_STORAGE_GET_NAME,
+ fid as usize,
+ 0,
+ );
+ }
+ futures::wait_until(|| (get_name_result.get().0.is_some())).await;
+ unsafe {
+ syscalls::raw::subscribe(CAPSULE_STORAGE, 0, ptr::null(), 0);
+ syscalls::raw::allow(CAPSULE_STORAGE, 0, ptr::null_mut(), 0);
+ }
+
+ let (status, name_len, next_fid) = get_name_result.into_inner();
+ if !status.unwrap() { break; }
+ if let Ok(str) = core::str::from_utf8(&name[..name_len]) {
+ // XXX filter non-builtins? (harder if capdl-loader cpio archive is expanded)
+ match str {
+ "kernel" |
+ "capdl-loader" |
+ "matcha-tock-bundle.bin" => {}
+ _ => names.push(str.to_string()),
+ }
+ }
+ fid = next_fid as u32;
+ }
+ postcard::to_slice(&GetBuiltinsResponse { names }, reply_buffer)
+ .or(Err(SECRequestError::SerializeFailed))
+ .map(|slice| slice.len())
+ }
+
+ // Lookup a file by name. A file identifier (the offset of the file's
+ // data in SPI flash) and the data size (in bytes) is returned.
+ // XXX could use GET_NAME
+ async fn find_file_async(
+ name: &str,
+ reply_buffer: &mut [u8],
+ ) -> Result<usize, SECRequestError> {
+ // Status, file identifier, file size (bytes)
+ type FindResult = (Option<bool>, usize, usize);
+ let find_result: Cell<FindResult> = Cell::new((None, 0, 0));
+
+ unsafe extern "C" fn find_callback(ok: usize, fid: usize, size_bytes: usize, data: usize) {
+ let find_result = &*(data as *const Cell<FindResult>);
+ find_result.set((Some(ok != 0), fid, size_bytes));
+ }
+ unsafe {
+ syscalls::raw::allow(
+ CAPSULE_STORAGE,
+ CMD_STORAGE_FIND_FILE,
+ name.as_ptr() as _, // XXX const
+ name.len(),
+ );
+ syscalls::raw::subscribe(
+ CAPSULE_STORAGE,
+ CMD_STORAGE_FIND_FILE,
+ find_callback as *const _,
+ &find_result as *const _ as usize,
+ );
+ let _ = syscalls::command(CAPSULE_STORAGE, CMD_STORAGE_FIND_FILE, 0, 0);
+ }
+ futures::wait_until(|| (find_result.get().0.is_some())).await;
+ unsafe {
+ syscalls::raw::subscribe(CAPSULE_STORAGE, 0, ptr::null(), 0);
+ syscalls::raw::allow(CAPSULE_STORAGE, 0, ptr::null_mut(), 0);
+ }
+
+ let (ok, fid, size_bytes) = find_result.into_inner();
+ if ok.unwrap() {
+ postcard::to_slice(
+ &FindFileResponse {
+ fid: fid as u32,
+ size_bytes: size_bytes as u32,
+ },
+ reply_buffer,
+ )
+ .or(Err(SECRequestError::SerializeFailed))
+ .map(|slice| slice.len())
+ } else {
+ Err(SECRequestError::FileNotFound)
+ }
+ }
+
+ // Get/read 4KiB (or less at EOF) from |fid| starting at |offset| from
+ // the start of the file. Data is returned in an associated page that
+ // must reside in SMC memory
+ async fn get_file_page_async(
+ fid: u32,
+ offset: u32,
+ opt_page: Option<usize>,
+ reply_buffer: &mut [u8],
+ ) -> Result<usize, SECRequestError> {
+ // Status, read count (bytes)
+ type ReadResult = (Option<bool>, usize);
+ let read_result: Cell<ReadResult> = Cell::new((None, 0));
+
+ unsafe extern "C" fn read_callback(
+ ok: usize,
+ _fid: usize,
+ count_bytes: usize,
+ data: usize,
+ ) {
+ let read_result = &*(data as *const Cell<ReadResult>);
+ read_result.set((Some(ok != 0), count_bytes));
+ }
+ unsafe {
+// XXX check returns
+ syscalls::raw::subscribe(
+ CAPSULE_STORAGE,
+ CMD_STORAGE_READ_PAGE,
+ read_callback as *const _,
+ &read_result as *const _ as usize,
+ );
+ // NB: cannot use allow to share opt_page because the address
+ // is in the SMC memory and not the app/process. Instead we
+ // pass an opaque value through command.
+ let _ = syscalls::command(
+ CAPSULE_STORAGE,
+ CMD_STORAGE_READ_PAGE,
+ (fid + offset) as usize,
+ opt_page.unwrap() as _,
+ );
+ }
+ futures::wait_until(|| (read_result.get().0.is_some())).await;
+ unsafe { syscalls::raw::subscribe(CAPSULE_STORAGE, 0, ptr::null(), 0); }
+
+ let (ok, count_bytes) = read_result.into_inner();
+ if ok.unwrap() {
+ postcard::to_slice(
+ &GetFilePageResponse {
+ count_bytes: count_bytes as u32,
+ },
+ reply_buffer,
+ )
+ .or(Err(SECRequestError::SerializeFailed))
+ .map(|slice| slice.len())
+ } else {
+ Err(SECRequestError::FileOffsetInvalid) // XXX guess
+ }
+ }
+
+ fn test_sync(
+ count: usize,
+ opt_page: Option<usize>,
+ _reply_buffer: &mut [u8],
+ ) -> Result<usize, SECRequestError> {
+ if let Some(page) = opt_page {
+ if count >= (4096 / size_of::<u32>()) {
+ return Err(SECRequestError::PageInvalid); // XXX
+ }
+ // XXX cannot address page; must push to capsule
+ unsafe {
+ dprintf!("page {:#x}", page);
+ let dst: *mut u32 = transmute(page);
+ // Tweak the first and last dword of the message.
+ dprintf!("request[0] = {:#x}\r\n", dst.add(0).read());
+ dst.add(0).write(0x12345678);
+ if count > 1 {
+ let last = count - 1;
+ dprintf!("request[{}] = {:#x}\r\n", last, dst.add(last).read());
+ dst.add(last).write(0x87654321);
+ }
+ }
+ Ok(0) // NB: modified page has result
+ } else {
+ Err(SECRequestError::PageInvalid)
+ }
+ }
}
diff --git a/app/src/main.rs b/app/src/main.rs
index 6916dda..874faa4 100644
--- a/app/src/main.rs
+++ b/app/src/main.rs
@@ -2,8 +2,8 @@
mod dprintf;
mod mailbox_client;
+use mailbox_client::MailboxClient;
-use crate::mailbox_client::MailboxClient;
use libtock::result::TockResult;
use libtock::syscalls;
use matcha_config::*;
@@ -26,30 +26,15 @@
unsafe {
loop {
- dprintf!("SEC: Waiting for request\r\n");
- let message_size = MailboxClient::wait_message_async(&mut MESSAGE_BUF).await;
- dprintf!("SEC: Request arrived, {} bytes\r\n", message_size);
+ let (request_size, opt_page) =
+ MailboxClient::wait_message_async(&mut MESSAGE_BUF).await;
- let dst: *mut u32 = core::mem::transmute(MESSAGE_BUF.as_ptr());
+ let (request_slice, reply_slice) = MESSAGE_BUF.split_at_mut(request_size);
+ let reply_size = MailboxClient::dispatch(request_slice, opt_page, reply_slice)
+ .await
+ .unwrap_or(0); // XXX send error code
- // 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}\r\n", dst.offset(0).read());
- dprintf!(
- "request[{}] = {:X}\r\n",
- last,
- dst.offset(last as isize).read()
- );
-
- dst.offset(0).write(0x12345678);
- dst.offset(last as isize).write(0x87654321);
-
- dprintf!("SEC: Sending response\r\n");
- MailboxClient::send_message_sync(message_size, &MESSAGE_BUF);
-
- dprintf!("SEC: Response sent\r\n");
+ MailboxClient::send_message_sync(reply_size, reply_slice);
}
}
// Unreachable