From f367943059c49cd60a742b629efacbb7f19ac2cd Mon Sep 17 00:00:00 2001 From: Anthony Rocha Date: Tue, 8 Oct 2024 10:20:46 -0700 Subject: [PATCH] Caliptra API Enhancements : - Add documentation showing how to use the SocManager trait. - Address feedback from the last review. - Update SocManager with new trait methods. --- api/src/lib.rs | 30 +- api/src/mailbox.rs | 25 +- api/src/soc_mgr.rs | 294 ++++++++++++ api/types/src/lib.rs | 2 +- hw-model/src/lib.rs | 441 +++++++++++++----- .../test_idevid_derivation.rs | 2 +- .../caliptra_integration_tests/smoke_test.rs | 2 +- 7 files changed, 676 insertions(+), 120 deletions(-) diff --git a/api/src/lib.rs b/api/src/lib.rs index 0bfed12d96..0075fd998e 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -13,6 +13,34 @@ pub use soc_mgr::SocManager; #[derive(Debug, Eq, PartialEq)] pub enum CaliptraApiError { - ReadBuffTooSmall, + UnableToSetPauser, + UnableToLockMailbox, + UnableToReadMailbox, BufferTooLargeForMailbox, + UnknownCommandStatus(u32), + MailboxTimeout, + MailboxCmdFailed(u32), + UnexpectedMailboxFsmStatus { + expected: u32, + actual: u32, + }, + MailboxRespInvalidFipsStatus(u32), + MailboxRespInvalidChecksum { + expected: u32, + actual: u32, + }, + MailboxRespTypeTooSmall, + MailboxReqTypeTooSmall, + MailboxNoResponseData, + MailboxUnexpectedResponseLen { + expected_min: u32, + expected_max: u32, + actual: u32, + }, + UploadFirmwareUnexpectedResponse, + UploadMeasurementResponseError, + ReadBuffTooSmall, + FusesAlreadyIniitalized, + FuseDoneNotSet, + StashMeasurementFailed, } diff --git a/api/src/mailbox.rs b/api/src/mailbox.rs index 46508b0d83..ed666ea4e0 100644 --- a/api/src/mailbox.rs +++ b/api/src/mailbox.rs @@ -1051,9 +1051,26 @@ pub struct AuthorizeAndStashResp { } impl Response for AuthorizeAndStashResp {} +/// Retrieves dlen bytes from the mailbox. +pub fn mbox_read_response( + mbox: mbox::RegisterBlock, + buf: &mut [u8], +) -> Result<&[u8], CaliptraApiError> { + let dlen_bytes = mbox.dlen().read() as usize; + + // Buffer must be big enough to store dlen bytes. + let buf = buf + .get_mut(..dlen_bytes) + .ok_or(CaliptraApiError::ReadBuffTooSmall)?; + + mbox_read_fifo(mbox, buf)?; + + Ok(buf) +} + pub fn mbox_read_fifo( mbox: mbox::RegisterBlock, - mut buf: &mut [u8], + buf: &mut [u8], ) -> core::result::Result<(), CaliptraApiError> { use zerocopy::Unalign; @@ -1065,9 +1082,9 @@ pub fn mbox_read_fifo( let dlen_bytes = mbox.dlen().read() as usize; - if dlen_bytes < buf.len() { - buf = &mut buf[..dlen_bytes]; - } + let buf = buf + .get_mut(..dlen_bytes) + .ok_or(CaliptraApiError::UnableToReadMailbox)?; let len_words = buf.len() / size_of::(); let (mut buf_words, suffix) = LayoutVerified::new_slice_unaligned_from_prefix(buf, len_words) diff --git a/api/src/soc_mgr.rs b/api/src/soc_mgr.rs index b5da6f70e4..2f20ab4f40 100644 --- a/api/src/soc_mgr.rs +++ b/api/src/soc_mgr.rs @@ -1,6 +1,63 @@ // Licensed under the Apache-2.0 license + +use crate::{ + calc_checksum, + mailbox::{ + mbox_read_response, mbox_write_fifo, MailboxReqHeader, MailboxRespHeader, Request, + Response, StashMeasurementReq, + }, + CaliptraApiError, +}; +use caliptra_api_types::Fuses; +use core::mem; use ureg::MmioMut; +use zerocopy::{AsBytes, FromBytes}; +/// Implementation of the `SocManager` trait for a `RealSocManager`. +/// +/// # Example +/// +/// ```rust +/// use caliptra_api::SocManager; +/// use ureg::RealMmioMut; +/// struct RealSocManager; +/// const CPTRA_SOC_IFC_ADDR: u32 = 0x3003_0000; +/// const CPTRA_SOC_IFC_TRNG_ADDR: u32 = 0x3003_0000; +/// const CPTRA_SOC_SHA512_ACC_ADDR: u32 = 0x3002_1000; +/// const CPTRA_SOC_MBOX_ADDR: u32 = 0x3002_0000; +/// const fn caliptra_address_remap(addr : u32) -> u32 { +/// addr +/// } +/// impl SocManager for RealSocManager { +/// /// Address of the mailbox, remapped for the SoC. +/// const SOC_MBOX_ADDR: u32 = caliptra_address_remap(CPTRA_SOC_MBOX_ADDR); +/// +/// /// Address of the SoC interface, remapped for the SoC. +/// const SOC_IFC_ADDR: u32 = caliptra_address_remap(CPTRA_SOC_IFC_ADDR); +/// +/// /// Address of the SoC TRNG interface, remapped for the SoC. +/// const SOC_IFC_TRNG_ADDR: u32 = caliptra_address_remap(CPTRA_SOC_IFC_TRNG_ADDR); +/// +/// /// Address of the SHA-512 accelerator, remapped for the SoC. +/// const SOC_SHA512_ACC_ADDR: u32 = caliptra_address_remap(CPTRA_SOC_SHA512_ACC_ADDR); +/// +/// /// Maximum number of wait cycles. +/// const MAX_WAIT_CYCLES: u32 = 400000; +/// +/// /// Type alias for mutable memory-mapped I/O. +/// type TMmio<'a> = RealMmioMut<'a>; +/// +/// /// Returns a mutable reference to the memory-mapped I/O. +/// fn mmio_mut(&mut self) -> Self::TMmio<'_> { +/// ureg::RealMmioMut::default() +/// } +/// +/// /// Provides a delay function to be invoked when polling mailbox status. +/// fn delay(&mut self) { +/// //real_soc_delay_fn(1); +/// } +/// } +/// ``` pub trait SocManager { const SOC_IFC_ADDR: u32; const SOC_MBOX_ADDR: u32; @@ -15,8 +72,94 @@ pub trait SocManager { fn mmio_mut(&mut self) -> Self::TMmio<'_>; + // Provide a time base for mailbox status polling loop. fn delay(&mut self); + /// Set up valid PAUSERs for mailbox access. + fn setup_mailbox_users(&mut self, apb_pausers: &[u32]) -> Result<(), CaliptraApiError> { + for (idx, apb_pauser) in apb_pausers.iter().enumerate() { + if self + .soc_ifc() + .cptra_mbox_pauser_lock() + .at(idx) + .read() + .lock() + { + return Err(CaliptraApiError::UnableToSetPauser); + } + + self.soc_ifc() + .cptra_mbox_valid_pauser() + .at(idx) + .write(|_| *apb_pauser); + self.soc_ifc() + .cptra_mbox_pauser_lock() + .at(idx) + .write(|w| w.lock(true)); + } + Ok(()) + } + + /// Initializes the fuse values and locks them in until the next reset. + /// + /// # Errors + /// + /// If the cptra_fuse_wr_done has already been written, or the + /// hardware prevents cptra_fuse_wr_done from being set. + fn init_fuses(&mut self, fuses: &Fuses) -> Result<(), CaliptraApiError> { + if !self.soc_ifc().cptra_reset_reason().read().warm_reset() + && self.soc_ifc().cptra_fuse_wr_done().read().done() + { + return Err(CaliptraApiError::FusesAlreadyIniitalized); + } + + self.soc_ifc().fuse_uds_seed().write(&fuses.uds_seed); + self.soc_ifc() + .fuse_field_entropy() + .write(&fuses.field_entropy); + self.soc_ifc() + .fuse_key_manifest_pk_hash() + .write(&fuses.key_manifest_pk_hash); + self.soc_ifc() + .fuse_key_manifest_pk_hash_mask() + .write(|w| w.mask(fuses.key_manifest_pk_hash_mask.into())); + self.soc_ifc() + .fuse_owner_pk_hash() + .write(&fuses.owner_pk_hash); + self.soc_ifc() + .fuse_fmc_key_manifest_svn() + .write(|_| fuses.fmc_key_manifest_svn); + self.soc_ifc().fuse_runtime_svn().write(&fuses.runtime_svn); + self.soc_ifc() + .fuse_anti_rollback_disable() + .write(|w| w.dis(fuses.anti_rollback_disable)); + self.soc_ifc() + .fuse_idevid_cert_attr() + .write(&fuses.idevid_cert_attr); + self.soc_ifc() + .fuse_idevid_manuf_hsm_id() + .write(&fuses.idevid_manuf_hsm_id); + self.soc_ifc() + .fuse_life_cycle() + .write(|w| w.life_cycle(fuses.life_cycle.into())); + self.soc_ifc() + .fuse_lms_verify() + .write(|w| w.lms_verify(fuses.lms_verify)); + self.soc_ifc() + .fuse_lms_revocation() + .write(|_| fuses.fuse_lms_revocation); + self.soc_ifc() + .fuse_soc_stepping_id() + .write(|w| w.soc_stepping_id(fuses.soc_stepping_id.into())); + + self.soc_ifc().cptra_fuse_wr_done().write(|w| w.done(true)); + + if !self.soc_ifc().cptra_fuse_wr_done().read().done() { + return Err(CaliptraApiError::FuseDoneNotSet); + } + Ok(()) + } + /// A register block that can be used to manipulate the soc_ifc peripheral /// over the simulated SoC->Caliptra APB bus. fn soc_ifc(&mut self) -> caliptra_registers::soc_ifc::RegisterBlock> { @@ -60,4 +203,155 @@ pub trait SocManager { ) } } + + /// 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(CaliptraApiError::MailboxCmdFailed) if the microcontroller + /// responded with an error, or other errors if there was a problem + /// communicating with the mailbox. + fn mailbox_exec<'r>( + &mut self, + cmd: u32, + buf: &[u8], + resp_data: &'r mut [u8], + ) -> core::result::Result, CaliptraApiError> { + self.start_mailbox_exec(cmd, buf)?; + self.finish_mailbox_exec(resp_data) + } + + /// Send a command to the mailbox but don't wait for the response + fn start_mailbox_exec( + &mut self, + cmd: u32, + buf: &[u8], + ) -> core::result::Result<(), CaliptraApiError> { + // Read a 0 to get the lock + if self.soc_mbox().lock().read().lock() { + return Err(CaliptraApiError::UnableToLockMailbox); + } + + // Mailbox lock value should read 1 now + // If not, the reads are likely being blocked by the PAUSER check or some other issue + if !(self.soc_mbox().lock().read().lock()) { + return Err(CaliptraApiError::UnableToReadMailbox); + } + + self.soc_mbox().cmd().write(|_| cmd); + mbox_write_fifo(&self.soc_mbox(), buf)?; + + // Ask the microcontroller to execute this command + self.soc_mbox().execute().write(|w| w.execute(true)); + + Ok(()) + } + + fn finish_mailbox_exec<'r>( + &mut self, + resp_data: &'r mut [u8], + ) -> core::result::Result, CaliptraApiError> { + // Wait for the microcontroller to finish executing + let mut timeout_cycles = Self::MAX_WAIT_CYCLES; // 100ms @400MHz + while self.soc_mbox().status().read().status().cmd_busy() { + self.delay(); + timeout_cycles -= 1; + if timeout_cycles == 0 { + return Err(CaliptraApiError::MailboxTimeout); + } + } + let status = self.soc_mbox().status().read().status(); + if status.cmd_failure() { + self.soc_mbox().execute().write(|w| w.execute(false)); + let soc_ifc = self.soc_ifc(); + return Err(CaliptraApiError::MailboxCmdFailed( + if soc_ifc.cptra_fw_error_fatal().read() != 0 { + soc_ifc.cptra_fw_error_fatal().read() + } else { + soc_ifc.cptra_fw_error_non_fatal().read() + }, + )); + } + if status.cmd_complete() { + self.soc_mbox().execute().write(|w| w.execute(false)); + return Ok(None); + } + if !status.data_ready() { + return Err(CaliptraApiError::UnknownCommandStatus(status as u32)); + } + + let res = mbox_read_response(self.soc_mbox(), resp_data); + + self.soc_mbox().execute().write(|w| w.execute(false)); + + let buf = res?; + + Ok(Some(buf)) + } + + /// 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_exec_req( + &mut self, + mut req: R, + resp_bytes: &mut [u8], + ) -> core::result::Result { + if mem::size_of::() < mem::size_of::() { + return Err(CaliptraApiError::MailboxReqTypeTooSmall); + } + if mem::size_of::() < mem::size_of::() { + return Err(CaliptraApiError::MailboxRespTypeTooSmall); + } + if R::Resp::MIN_SIZE < mem::size_of::() { + return Err(CaliptraApiError::MailboxRespTypeTooSmall); + } + let (header_bytes, payload_bytes) = req + .as_bytes_mut() + .split_at_mut(mem::size_of::()); + + let mut header = MailboxReqHeader::read_from(header_bytes as &[u8]).unwrap(); + header.chksum = calc_checksum(R::ID.into(), payload_bytes); + header_bytes.copy_from_slice(header.as_bytes()); + + let Some(data) = SocManager::mailbox_exec(self, R::ID.into(), req.as_bytes(), resp_bytes)? else { + return Err(CaliptraApiError::MailboxNoResponseData); + }; + + if data.len() < R::Resp::MIN_SIZE || data.len() > mem::size_of::() { + return Err(CaliptraApiError::MailboxUnexpectedResponseLen { + expected_min: R::Resp::MIN_SIZE as u32, + expected_max: mem::size_of::() as u32, + actual: data.len() as u32, + }); + } + + let mut response = R::Resp::new_zeroed(); + response.as_bytes_mut()[..data.len()].copy_from_slice(data); + + let response_header = MailboxRespHeader::read_from_prefix(data).unwrap(); + let actual_checksum = calc_checksum(0, &data[4..]); + if actual_checksum != response_header.chksum { + return Err(CaliptraApiError::MailboxRespInvalidChecksum { + expected: response_header.chksum, + actual: actual_checksum, + }); + } + if response_header.fips_status != MailboxRespHeader::FIPS_STATUS_APPROVED { + return Err(CaliptraApiError::MailboxRespInvalidFipsStatus( + response_header.fips_status, + )); + } + Ok(response) + } + + fn send_stash_measurement_req( + &mut self, + req: StashMeasurementReq, + response_packet: &mut [u8], + ) -> Result<(), CaliptraApiError> { + let resp = self.mailbox_exec_req(req, response_packet)?; + if resp.dpe_result == 0 { + return Ok(()); + } + Err(CaliptraApiError::StashMeasurementFailed) + } } diff --git a/api/types/src/lib.rs b/api/types/src/lib.rs index 10940e7bd9..37f4f05d71 100644 --- a/api/types/src/lib.rs +++ b/api/types/src/lib.rs @@ -152,7 +152,7 @@ impl TryFrom for U4 { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub struct Fuses { pub uds_seed: [u32; 12], pub field_entropy: [u32; 8], diff --git a/hw-model/src/lib.rs b/hw-model/src/lib.rs index 77f2a28f75..1487314d64 100644 --- a/hw-model/src/lib.rs +++ b/hw-model/src/lib.rs @@ -1,13 +1,11 @@ // Licensed under the Apache-2.0 license -use api::calc_checksum; -use api::mailbox::{MailboxReqHeader, MailboxRespHeader, Response}; use api::CaliptraApiError; use caliptra_api as api; use caliptra_api::SocManager; use caliptra_api_types as api_types; use caliptra_emu_bus::Bus; -use std::mem; +use core::panic; use std::path::PathBuf; use std::str::FromStr; use std::{ @@ -275,6 +273,7 @@ impl<'a> Default for BootParams<'a> { #[derive(Debug, Eq, PartialEq)] pub enum ModelError { MailboxCmdFailed(u32), + UnableToSetPauser, UnableToLockMailbox, BufferTooLargeForMailbox, UploadFirmwareUnexpectedResponse, @@ -307,13 +306,56 @@ pub enum ModelError { MailboxRespInvalidFipsStatus(u32), MailboxTimeout, ReadBufferTooSmall, + FuseDoneNotSet, + FusesAlreadyInitialized, + StashMeasurementFailed, } impl From for ModelError { fn from(error: CaliptraApiError) -> Self { match error { + CaliptraApiError::UnableToLockMailbox => ModelError::UnableToLockMailbox, + CaliptraApiError::UnableToReadMailbox => ModelError::UnableToReadMailbox, CaliptraApiError::BufferTooLargeForMailbox => ModelError::BufferTooLargeForMailbox, + CaliptraApiError::UnknownCommandStatus(code) => ModelError::UnknownCommandStatus(code), + CaliptraApiError::MailboxTimeout => ModelError::MailboxTimeout, + CaliptraApiError::MailboxCmdFailed(code) => ModelError::MailboxCmdFailed(code), + CaliptraApiError::UnexpectedMailboxFsmStatus { expected, actual } => { + ModelError::UnexpectedMailboxFsmStatus { expected, actual } + } + CaliptraApiError::MailboxRespInvalidFipsStatus(status) => { + ModelError::MailboxRespInvalidFipsStatus(status) + } + CaliptraApiError::MailboxRespInvalidChecksum { expected, actual } => { + ModelError::MailboxRespInvalidChecksum { expected, actual } + } + CaliptraApiError::MailboxRespTypeTooSmall => ModelError::MailboxRespTypeTooSmall, + CaliptraApiError::MailboxReqTypeTooSmall => ModelError::MailboxReqTypeTooSmall, + CaliptraApiError::MailboxNoResponseData => ModelError::MailboxNoResponseData, + CaliptraApiError::MailboxUnexpectedResponseLen { + expected_min, + expected_max, + actual, + } => ModelError::MailboxUnexpectedResponseLen { + expected_min, + expected_max, + actual, + }, + CaliptraApiError::UploadFirmwareUnexpectedResponse => { + ModelError::UploadFirmwareUnexpectedResponse + } + CaliptraApiError::UploadMeasurementResponseError => { + ModelError::UploadMeasurementResponseError + } caliptra_api::CaliptraApiError::ReadBuffTooSmall => ModelError::ReadBufferTooSmall, + caliptra_api::CaliptraApiError::FuseDoneNotSet => ModelError::FuseDoneNotSet, + caliptra_api::CaliptraApiError::FusesAlreadyIniitalized => { + ModelError::FusesAlreadyInitialized + } + caliptra_api::CaliptraApiError::StashMeasurementFailed => { + ModelError::StashMeasurementFailed + } + caliptra_api::CaliptraApiError::UnableToSetPauser => ModelError::UnableToSetPauser, } } } @@ -385,6 +427,20 @@ impl Display for ModelError { ModelError::ReadBufferTooSmall => { write!(f, "Cant read mailbox because read buffer too small") } + + ModelError::FuseDoneNotSet => { + write!(f, "Fuse Wr Done bit not set") + } + + ModelError::FusesAlreadyInitialized => { + write!(f, "Fuses already initialized") + } + ModelError::StashMeasurementFailed => { + write!(f, "Stash measurement request failed") + } + ModelError::UnableToSetPauser => { + write!(f, "Valid PAUSER locked") + } } } } @@ -488,7 +544,7 @@ pub trait HwModel: SocManager { where Self: Sized, { - self.init_fuses(&boot_params.fuses); + HwModel::init_fuses(self, &boot_params.fuses); self.soc_ifc() .cptra_dbg_manuf_service_reg() @@ -516,18 +572,9 @@ pub trait HwModel: SocManager { .write(|_| reg); } - { - for idx in 0..boot_params.valid_pauser.len() { - self.soc_ifc() - .cptra_mbox_valid_pauser() - .at(idx) - .write(|_| boot_params.valid_pauser[idx]); - self.soc_ifc() - .cptra_mbox_pauser_lock() - .at(idx) - .write(|w| w.lock(true)); - } - } + // Set up the PAUSER as valid for the mailbox (using index 0) + self.setup_mailbox_users(boot_params.valid_pauser.as_slice()) + .map_err(ModelError::from)?; writeln!(self.output().logger(), "writing to cptra_bootfsm_go")?; self.soc_ifc().cptra_bootfsm_go().write(|w| w.go(true)); @@ -562,7 +609,7 @@ pub trait HwModel: SocManager { fn warm_reset_flow(&mut self, fuses: &Fuses) { self.warm_reset(); - self.init_fuses(fuses); + HwModel::init_fuses(self, fuses); self.soc_ifc().cptra_bootfsm_go().write(|w| w.go(true)); } @@ -611,55 +658,13 @@ pub trait HwModel: SocManager { /// If the cptra_fuse_wr_done has already been written, or the /// hardware prevents cptra_fuse_wr_done from being set. fn init_fuses(&mut self, fuses: &Fuses) { - if !self.soc_ifc().cptra_reset_reason().read().warm_reset() { - assert!( - !self.soc_ifc().cptra_fuse_wr_done().read().done(), - "Fuses are already locked in place (according to cptra_fuse_wr_done)" + println!("Initializing fuses"); + if let Err(e) = caliptra_api::SocManager::init_fuses(self, fuses) { + panic!( + "{}", + format!("Fuse initializaton error: {}", ModelError::from(e)) ); } - println!("Initializing fuses: {:#x?}", fuses); - - self.soc_ifc().fuse_uds_seed().write(&fuses.uds_seed); - self.soc_ifc() - .fuse_field_entropy() - .write(&fuses.field_entropy); - self.soc_ifc() - .fuse_key_manifest_pk_hash() - .write(&fuses.key_manifest_pk_hash); - self.soc_ifc() - .fuse_key_manifest_pk_hash_mask() - .write(|w| w.mask(fuses.key_manifest_pk_hash_mask.into())); - self.soc_ifc() - .fuse_owner_pk_hash() - .write(&fuses.owner_pk_hash); - self.soc_ifc() - .fuse_fmc_key_manifest_svn() - .write(|_| fuses.fmc_key_manifest_svn); - self.soc_ifc().fuse_runtime_svn().write(&fuses.runtime_svn); - self.soc_ifc() - .fuse_anti_rollback_disable() - .write(|w| w.dis(fuses.anti_rollback_disable)); - self.soc_ifc() - .fuse_idevid_cert_attr() - .write(&fuses.idevid_cert_attr); - self.soc_ifc() - .fuse_idevid_manuf_hsm_id() - .write(&fuses.idevid_manuf_hsm_id); - self.soc_ifc() - .fuse_life_cycle() - .write(|w| w.life_cycle(fuses.life_cycle.into())); - self.soc_ifc() - .fuse_lms_verify() - .write(|w| w.lms_verify(fuses.lms_verify)); - self.soc_ifc() - .fuse_lms_revocation() - .write(|_| fuses.fuse_lms_revocation); - self.soc_ifc() - .fuse_soc_stepping_id() - .write(|w| w.soc_stepping_id(fuses.soc_stepping_id.into())); - - self.soc_ifc().cptra_fuse_wr_done().write(|w| w.done(true)); - assert!(self.soc_ifc().cptra_fuse_wr_done().read().done()); } fn step_until_exit_success(&mut self) -> std::io::Result<()> { @@ -813,56 +818,12 @@ pub trait HwModel: SocManager { /// response is validated. fn mailbox_execute_req( &mut self, - mut req: R, + req: R, ) -> std::result::Result { - if mem::size_of::() < mem::size_of::() { - return Err(ModelError::MailboxReqTypeTooSmall); - } - if mem::size_of::() < mem::size_of::() { - return Err(ModelError::MailboxRespTypeTooSmall); - } - if R::Resp::MIN_SIZE < mem::size_of::() { - return Err(ModelError::MailboxRespTypeTooSmall); - } - let (header_bytes, payload_bytes) = req - .as_bytes_mut() - .split_at_mut(mem::size_of::()); - - 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); - }; - if response_bytes.len() < R::Resp::MIN_SIZE - || response_bytes.len() > mem::size_of::() - { - return Err(ModelError::MailboxUnexpectedResponseLen { - expected_min: R::Resp::MIN_SIZE as u32, - expected_max: mem::size_of::() as u32, - actual: response_bytes.len() as u32, - }); - } - let mut response = R::Resp::new_zeroed(); - response.as_bytes_mut()[..response_bytes.len()].copy_from_slice(&response_bytes); - - 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) + + self.mailbox_exec_req(req, response.as_bytes_mut()) + .map_err(ModelError::from) } /// Executes `cmd` with request data `buf`. Returns `Ok(Some(_))` if @@ -1392,6 +1353,98 @@ mod tests { ); } + #[test] + /// Test SocManager maiLbox API. + fn test_negative_soc_mgr_mbox_users() { + let mut model = caliptra_hw_model::new_unbooted(InitParams { + rom: &gen_image_hi(), + ..Default::default() + }) + .unwrap(); + + model.soc_ifc().cptra_fuse_wr_done().write(|w| w.done(true)); + model.soc_ifc().cptra_bootfsm_go().write(|w| w.go(true)); + + // Set up the PAUSER as valid for the mailbox (using index 0) + model + .soc_ifc() + .cptra_mbox_valid_pauser() + .at(0) + .write(|_| 0x1); + model + .soc_ifc() + .cptra_mbox_pauser_lock() + .at(0) + .write(|w| w.lock(true)); + + assert_eq!( + model.setup_mailbox_users(&[1]), + Err(caliptra_api::CaliptraApiError::UnableToSetPauser) + ); + } + + #[test] + /// Test SocManager maiLbox API. + fn test_soc_mgr_mbox_api() { + use caliptra_api::CaliptraApiError; + let message: [u8; 10] = [0x90, 0x5e, 0x1f, 0xad, 0x8b, 0x60, 0xb0, 0xbf, 0x1c, 0x7e]; + + let rom = + caliptra_builder::build_firmware_rom(&firmware::hw_model_tests::MAILBOX_RESPONDER) + .unwrap(); + + let mut model = caliptra_hw_model::new( + InitParams { + rom: &rom, + ..Default::default() + }, + BootParams::default(), + ) + .unwrap(); + + // Send command that echoes the command and input message + let mut resp_data = [0u8; 128]; + + assert_eq!( + model.mailbox_exec(0x1000_0000, &message, &mut resp_data), + Ok(Some( + [[0x00, 0x00, 0x00, 0x10].as_slice(), &message] + .concat() + .as_bytes() + )), + ); + + // Send command that echoes the command and input message + let mut resp_data = [0u8; 128]; + assert_eq!( + model.mailbox_exec(0x1000_0000, &message[..8], resp_data.as_bytes_mut()), + Ok(Some( + [0x00, 0x00, 0x00, 0x10, 0x90, 0x5e, 0x1f, 0xad, 0x8b, 0x60, 0xb0, 0xbf].as_slice() + )), + ); + + // Send command that returns 7 bytes of output, and doesn't consume input + // Send command that echoes the command and input message + let mut resp_data = [0u8; 128]; + + assert_eq!( + model.mailbox_exec(0x1000_1000, &[42], resp_data.as_bytes_mut()), + Ok(Some([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd].as_slice())), + ); + + // Send command that returns success with no output + assert_eq!( + model.mailbox_exec(0x2000_0000, &[], resp_data.as_bytes_mut()), + Ok(None) + ); + + // Send command that returns failure + assert_eq!( + model.mailbox_exec(0x4000_0000, &message, resp_data.as_bytes_mut()), + Err(CaliptraApiError::MailboxCmdFailed(0)) + ); + } + #[test] pub fn test_mailbox_receive() { let rom = caliptra_builder::build_firmware_rom(&firmware::hw_model_tests::MAILBOX_SENDER) @@ -1521,6 +1574,170 @@ mod tests { } } + #[test] + pub fn test_soc_mgr_exec_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], + } + impl mailbox::Response for TestResp {} + + #[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( + InitParams { + rom: &rom, + ..Default::default() + }, + BootParams::default(), + ) + .unwrap(); + + // Success + set_response( + &mut model, + &[ + 0x2d, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, b'H', b'I', b'!', b'!', + ], + ); + + let mut packet = [0u8; 256]; + let resp = model + .mailbox_exec_req( + TestReq { + data: *b"Hi!!", + ..Default::default() + }, + &mut packet, + ) + .unwrap(); + model + .step_until_output_and_take("|dcfeffff48692121|") + .unwrap(); + assert_eq!( + resp, + TestResp { + hdr: MailboxRespHeader { + chksum: 0xffffff2d, + 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_exec_req( + TestReq { + data: *b"Hi!!", + ..Default::default() + }, + &mut packet, + ) + .map_err(ModelError::from); + assert_eq!( + resp, + Err(ModelError::MailboxUnexpectedResponseLen { + expected_min: 12, + expected_max: 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_exec_req( + TestReq { + data: *b"Hi!!", + ..Default::default() + }, + packet.as_bytes_mut(), + ) + .map_err(ModelError::from); + assert_eq!( + resp, + Err(ModelError::MailboxRespInvalidChecksum { + expected: 0xffffff2e, + actual: 0xffffff2d + }) + ); + + // 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 mut packet = [0u8; 12]; + let resp = model + .mailbox_exec_req( + TestReq { + data: *b"Hi!!", + ..Default::default() + }, + &mut packet, + ) + .map_err(ModelError::from); + assert_eq!(resp, Err(ModelError::MailboxRespInvalidFipsStatus(0x2001))); + + // Set no data in response + let resp = model + .mailbox_exec_req( + TestReqNoData { + data: *b"Hi!!", + ..Default::default() + }, + &mut packet, + ) + .map_err(ModelError::from); + assert_eq!(resp, Err(ModelError::MailboxNoResponseData)); + } + #[test] pub fn test_mailbox_execute_req() { const NO_DATA_CMD: u32 = 0x2000_0000; diff --git a/rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs b/rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs index 016ce8c568..cb77cceca2 100644 --- a/rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs +++ b/rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs @@ -89,7 +89,7 @@ fn test_generate_csr_stress() { for _ in 0..num_tests { let fuses = fuses_with_random_uds(); let (mut hw, image_bundle) = - helpers::build_hw_model_and_image_bundle(fuses, ImageOptions::default()); + helpers::build_hw_model_and_image_bundle(fuses.clone(), ImageOptions::default()); let csr_bytes = generate_csr(&mut hw, &image_bundle); diff --git a/test/tests/caliptra_integration_tests/smoke_test.rs b/test/tests/caliptra_integration_tests/smoke_test.rs index 0376c1dcd5..f34d256479 100644 --- a/test/tests/caliptra_integration_tests/smoke_test.rs +++ b/test/tests/caliptra_integration_tests/smoke_test.rs @@ -174,7 +174,7 @@ fn smoke_test() { ..Default::default() }, BootParams { - fuses, + fuses: fuses.clone(), fw_image: Some(&image.to_bytes().unwrap()), ..Default::default() },