Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hw-model: High-level API for making mailbox requests. #1070

Merged
merged 3 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 51 additions & 0 deletions api/src/mailbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ impl From<CommandId> for u32 {
}
}

/// A trait implemented by request types. Describes the associated command ID
/// and response type.
pub trait Request: AsBytes + FromBytes {
const ID: CommandId;
type Resp: FromBytes;
}

// Contains all the possible mailbox response structs
#[cfg_attr(test, derive(PartialEq, Debug, Eq))]
#[allow(clippy::large_enum_variant)]
Expand Down Expand Up @@ -292,6 +299,16 @@ pub struct GetIdevInfoResp {
pub idev_pub_y: [u8; 48],
}

#[repr(C)]
#[derive(Default, Debug, AsBytes, FromBytes, PartialEq, Eq)]
pub struct TestOnlyGetLdevCertReq {
header: MailboxReqHeader,
}
impl Request for TestOnlyGetLdevCertReq {
const ID: CommandId = CommandId::TEST_ONLY_GET_LDEV_CERT;
type Resp = GetLdevCertResp;
}

// GET_LDEV_CERT
// No command-specific input args
#[repr(C)]
Expand All @@ -303,6 +320,10 @@ pub struct GetLdevCertResp {
}
impl GetLdevCertResp {
pub const DATA_MAX_SIZE: usize = 1024;

pub fn data(&self) -> Option<&[u8]> {
self.data.get(..self.data_size as usize)
}
}

// ECDSA384_SIGNATURE_VERIFY
Expand All @@ -315,6 +336,10 @@ pub struct EcdsaVerifyReq {
pub signature_r: [u8; 48],
pub signature_s: [u8; 48],
}
impl Request for EcdsaVerifyReq {
const ID: CommandId = CommandId::ECDSA384_VERIFY;
type Resp = MailboxRespHeader;
}
// No command-specific output args

// TEST_ONLY_HMAC384_SIGNATURE_VERIFY
Expand All @@ -327,6 +352,10 @@ pub struct HmacVerifyReq {
pub len: u32,
pub msg: [u8; 256],
}
impl Request for HmacVerifyReq {
const ID: CommandId = CommandId::TEST_ONLY_HMAC384_VERIFY;
type Resp = MailboxRespHeader;
}
// No command-specific output args

// STASH_MEASUREMENT
Expand All @@ -350,6 +379,10 @@ impl Default for StashMeasurementReq {
}
}
}
impl Request for StashMeasurementReq {
const ID: CommandId = CommandId::STASH_MEASUREMENT;
type Resp = StashMeasurementResp;
}

#[repr(C)]
#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)]
Expand Down Expand Up @@ -384,6 +417,10 @@ impl Default for InvokeDpeReq {
}
}
}
impl Request for InvokeDpeReq {
const ID: CommandId = CommandId::INVOKE_DPE;
type Resp = InvokeDpeResp;
}

#[repr(C)]
#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)]
Expand Down Expand Up @@ -417,6 +454,16 @@ impl Default for InvokeDpeResp {
}
}

#[repr(C)]
#[derive(Debug, Default, AsBytes, FromBytes, PartialEq, Eq)]
pub struct TestOnlyGetFmcAliasCertReq {
header: MailboxReqHeader,
}
impl Request for TestOnlyGetFmcAliasCertReq {
const ID: CommandId = CommandId::TEST_ONLY_GET_FMC_ALIAS_CERT;
type Resp = GetLdevCertResp;
}

// TEST_ONLY_GET_FMC_ALIAS_CERT
// No command-specific input args
#[repr(C)]
Expand All @@ -428,6 +475,10 @@ pub struct TestGetFmcAliasCertResp {
}
impl TestGetFmcAliasCertResp {
pub const DATA_MAX_SIZE: usize = 1024;

pub fn data(&self) -> Option<&[u8]> {
self.data.get(..self.data_size as usize)
}
}

// FIPS_SELF_TEST
Expand Down
228 changes: 227 additions & 1 deletion hw-model/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Licensed under the Apache-2.0 license

use std::mem;
use std::path::PathBuf;
use std::str::FromStr;
use std::{
Expand All @@ -8,6 +9,8 @@ use std::{
io::{stdout, ErrorKind, Write},
};

use api::calc_checksum;
use api::mailbox::{MailboxReqHeader, MailboxRespHeader};
use caliptra_api as api;
use caliptra_emu_bus::Bus;
use caliptra_hw_model_types::{
Expand Down Expand Up @@ -237,6 +240,12 @@ pub enum ModelError {
UnableToLockSha512Acc,
UploadMeasurementResponseError,
UnableToReadMailbox,
MailboxNoResponseData,
MailboxReqTypeTooSmall,
MailboxRespTypeTooSmall,
MailboxUnexpectedResponseLen { expected: u32, actual: u32 },
MailboxRespInvalidChecksum { expected: i32, actual: i32 },
MailboxRespInvalidFipsStatus(u32),
}
impl Error for ModelError {}
impl Display for ModelError {
Expand Down Expand Up @@ -268,6 +277,33 @@ impl Display for ModelError {
write!(f, "Error in response after uploading measurement")
}
ModelError::UnableToReadMailbox => write!(f, "Unable to read mailbox regs"),
ModelError::MailboxNoResponseData => {
write!(f, "Expected response data but none was found")
}
ModelError::MailboxReqTypeTooSmall => {
write!(f, "Mailbox request type too small to contain header")
}
ModelError::MailboxRespTypeTooSmall => {
write!(f, "Mailbox response type too small to contain header")
}
ModelError::MailboxUnexpectedResponseLen { expected, actual } => {
write!(
f,
"Expected mailbox response lenth of {expected}, was {actual}"
)
}
ModelError::MailboxRespInvalidChecksum { expected, actual } => {
write!(
f,
"Mailbox response had invalid checksum: expected {expected}, was {actual}"
)
}
ModelError::MailboxRespInvalidFipsStatus(status) => {
write!(
f,
"Mailbox response had non-success FIPS status: 0x{status:x}"
)
}
}
}
}
Expand Down Expand Up @@ -732,6 +768,56 @@ pub trait HwModel {

fn set_apb_pauser(&mut self, pauser: u32);

/// Executes a typed request and (if success), returns the typed response.
/// The checksum field of the request is calculated, and the checksum of the
/// response is validated.
fn mailbox_execute_req<R: api::mailbox::Request>(
&mut self,
mut req: R,
) -> std::result::Result<R::Resp, ModelError> {
if mem::size_of::<R>() < mem::size_of::<MailboxReqHeader>() {
return Err(ModelError::MailboxReqTypeTooSmall);
}
if mem::size_of::<R::Resp>() < mem::size_of::<MailboxRespHeader>() {
return Err(ModelError::MailboxRespTypeTooSmall);
}
let (header_bytes, payload_bytes) = req
.as_bytes_mut()
.split_at_mut(mem::size_of::<MailboxReqHeader>());

let mut header = MailboxReqHeader::read_from(header_bytes as &[u8]).unwrap();
header.chksum = api::calc_checksum(R::ID.into(), payload_bytes);
header_bytes.copy_from_slice(header.as_bytes());

let Some(response_bytes) = self.mailbox_execute(R::ID.into(), req.as_bytes())? else {
return Err(ModelError::MailboxNoResponseData);
};
let response = match R::Resp::read_from(response_bytes.as_slice()) {
Some(response) => response,
None => {
return Err(ModelError::MailboxUnexpectedResponseLen {
expected: mem::size_of::<R::Resp>() as u32,
actual: response_bytes.len() as u32,
})
}
};
let response_header =
MailboxRespHeader::read_from_prefix(response_bytes.as_slice()).unwrap();
let actual_checksum = calc_checksum(0, &response_bytes[4..]);
if actual_checksum != response_header.chksum {
return Err(ModelError::MailboxRespInvalidChecksum {
expected: response_header.chksum,
actual: actual_checksum,
});
}
if response_header.fips_status != MailboxRespHeader::FIPS_STATUS_APPROVED {
return Err(ModelError::MailboxRespInvalidFipsStatus(
response_header.fips_status,
));
}
Ok(response)
}

/// Executes `cmd` with request data `buf`. Returns `Ok(Some(_))` if
/// the uC responded with data, `Ok(None)` if the uC indicated success
/// without data, Err(ModelError::MailboxCmdFailed) if the microcontroller
Expand Down Expand Up @@ -962,11 +1048,15 @@ pub trait HwModel {

#[cfg(test)]
mod tests {
use crate::{mmio::Rv32GenMmio, BootParams, HwModel, InitParams, ModelError, ShaAccMode};
use crate::{
mmio::Rv32GenMmio, BootParams, DefaultHwModel, HwModel, InitParams, ModelError, ShaAccMode,
};
use caliptra_api::mailbox::{self, CommandId, MailboxReqHeader, MailboxRespHeader};
use caliptra_builder::firmware;
use caliptra_emu_bus::Bus;
use caliptra_emu_types::RvSize;
use caliptra_registers::{mbox::enums::MboxStatusE, soc_ifc};
use zerocopy::{AsBytes, FromBytes};

use crate as caliptra_hw_model;

Expand Down Expand Up @@ -1377,4 +1467,140 @@ mod tests {
);
}
}

#[test]
pub fn test_mailbox_execute_req() {
const NO_DATA_CMD: u32 = 0x2000_0000;
const SET_RESPONSE_CMD: u32 = 0x3000_0000;
const GET_RESPONSE_CMD: u32 = 0x3000_0001;

#[repr(C)]
#[derive(AsBytes, FromBytes, Default)]
struct TestReq {
hdr: MailboxReqHeader,
data: [u8; 4],
}
impl mailbox::Request for TestReq {
const ID: CommandId = CommandId(GET_RESPONSE_CMD);
type Resp = TestResp;
}
#[repr(C)]
#[derive(AsBytes, Debug, FromBytes, PartialEq, Eq)]
struct TestResp {
hdr: MailboxRespHeader,
data: [u8; 4],
}

#[repr(C)]
#[derive(AsBytes, FromBytes, Default)]
struct TestReqNoData {
hdr: MailboxReqHeader,
data: [u8; 4],
}
impl mailbox::Request for TestReqNoData {
const ID: CommandId = CommandId(NO_DATA_CMD);
type Resp = TestResp;
}

fn set_response(model: &mut DefaultHwModel, data: &[u8]) {
model.mailbox_execute(SET_RESPONSE_CMD, data).unwrap();
}

let rom =
caliptra_builder::build_firmware_rom(&firmware::hw_model_tests::MAILBOX_RESPONDER)
.unwrap();
let mut model = caliptra_hw_model::new(BootParams {
init_params: InitParams {
rom: &rom,
..Default::default()
},
..Default::default()
})
.unwrap();

// Success
set_response(
&mut model,
&[
0x2d, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, b'H', b'I', b'!', b'!',
],
);
let resp = model
.mailbox_execute_req(TestReq {
data: *b"Hi!!",
..Default::default()
})
.unwrap();
model
.step_until_output_and_take("|dcfeffff48692121|")
.unwrap();
assert_eq!(
resp,
TestResp {
hdr: MailboxRespHeader {
chksum: -211,
fips_status: 0
},
data: *b"HI!!",
},
);

// Set wrong length in response
set_response(
&mut model,
&[
0x2d, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, b'H', b'I', b'!',
],
);
let resp = model.mailbox_execute_req(TestReq {
data: *b"Hi!!",
..Default::default()
});
assert_eq!(
resp,
Err(ModelError::MailboxUnexpectedResponseLen {
expected: 12,
actual: 11
})
);

// Set bad checksum in response
set_response(
&mut model,
&[
0x2e, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, b'H', b'I', b'!', b'!',
],
);
let resp = model.mailbox_execute_req(TestReq {
data: *b"Hi!!",
..Default::default()
});
assert_eq!(
resp,
Err(ModelError::MailboxRespInvalidChecksum {
expected: -210,
actual: -211
})
);

// Set bad FIPS status in response
set_response(
&mut model,
&[
0x0c, 0xff, 0xff, 0xff, 0x01, 0x20, 0x00, 0x00, b'H', b'I', b'!', b'!',
],
);
let resp = model.mailbox_execute_req(TestReq {
data: *b"Hi!!",
..Default::default()
});
assert_eq!(resp, Err(ModelError::MailboxRespInvalidFipsStatus(0x2001)));

// Set no data in response
let resp = model.mailbox_execute_req(TestReqNoData {
data: *b"Hi!!",
..Default::default()
});
assert_eq!(resp, Err(ModelError::MailboxNoResponseData));
}
}
Loading
Loading