blob: 671542fa2d106a963f88cd5acb688e4f0a6ef732 [file] [log] [blame]
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> {
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
// NB: must be last to match up with stripped down rootserver driver
GetBuiltins, // Get package names -> Vec(String)
}
#[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)
}
}
}
//------------------------------------------------------------------------------
pub struct MailboxClient {}
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);
}
// 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));
// 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) },
));
}
// 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_mut_ptr(),
buffer.len(),
);
syscalls::raw::subscribe(
CAPSULE_MAILBOX,
CMD_MAILBOX_RECV,
subscribe_callback as *const _,
&message_result as *const _ as usize,
);
}
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);
}
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(
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);
}
}
// 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 maybe explicitly include instead of filtering out?
match str {
// Filter bootstrap artifacts.
"kernel" | "capdl-loader" | "matcha-tock-bundle.bin" => {}
_ => {
// Filter CAmkES component executables.
if !str.ends_with("_group_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)
}
}
}