From 530de81e021bd0bcbc7ba1f1788bf9e4bbc91b41 Mon Sep 17 00:00:00 2001 From: Christopher Swenson Date: Thu, 23 Jan 2025 14:00:39 -0800 Subject: [PATCH 1/7] [runtime] Start recovery flow in the runtime This stubs out the recovery flow in the runtime. We also update the DMA peripheral so that it can be used as a `ureg::Mmio`, which will allow us to use the autogenerated I3C recovery register interface instead of hardcoding offsets. We also had to modify `ureg` slightly to remove the unused `impl Uint` for `u64` (because we don't don't support 64-bit AXI transfers) and add a `from_u32()` method for the (unused) `u8` and `u16` implementations. This builds on #1867 and rewrites it to use the updated DMA interface. --- Cargo.lock | 1 + auth-manifest/types/src/lib.rs | 15 + common/src/lib.rs | 2 +- drivers/src/dma.rs | 516 +++++++++++++++----- drivers/src/lib.rs | 3 +- rom/dev/src/flow/cold_reset/fw_processor.rs | 61 +-- runtime/Cargo.toml | 16 +- runtime/src/drivers.rs | 10 +- runtime/src/lib.rs | 1 + runtime/src/recovery_flow.rs | 67 +++ ureg/src/lib.rs | 18 +- 11 files changed, 504 insertions(+), 206 deletions(-) create mode 100644 runtime/src/recovery_flow.rs diff --git a/Cargo.lock b/Cargo.lock index 6c672e87be..da45f1f40b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -839,6 +839,7 @@ dependencies = [ "platform", "sha2", "ufmt", + "ureg", "wycheproof", "x509-parser", "zerocopy", diff --git a/auth-manifest/types/src/lib.rs b/auth-manifest/types/src/lib.rs index 5b4818cc91..cd1badb81f 100644 --- a/auth-manifest/types/src/lib.rs +++ b/auth-manifest/types/src/lib.rs @@ -24,6 +24,7 @@ use zeroize::Zeroize; pub const AUTH_MANIFEST_MARKER: u32 = 0x4154_4D4E; pub const AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT: usize = 127; +pub const AUTH_MANIFEST_PREAMBLE_SIZE: usize = 7168; bitflags::bitflags! { #[derive(Default, Copy, Clone, Debug)] @@ -187,3 +188,17 @@ pub struct AuthorizationManifest { pub image_metadata_col: AuthManifestImageMetadataCollection, } + +#[cfg(test)] +mod test { + use crate::{AuthManifestPreamble, AUTH_MANIFEST_PREAMBLE_SIZE}; + use zerocopy::AsBytes; + + #[test] + fn test_auth_preamble_size() { + assert_eq!( + AUTH_MANIFEST_PREAMBLE_SIZE, + AuthManifestPreamble::default().as_bytes().len() + ); + } +} diff --git a/common/src/lib.rs b/common/src/lib.rs index 7f3349f00f..11c3c4467e 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -41,7 +41,7 @@ pub use pcr::{PcrLogEntry, PcrLogEntryId, RT_FW_CURRENT_PCR, RT_FW_JOURNEY_PCR}; pub const FMC_ORG: u32 = 0x40000000; pub const FMC_SIZE: u32 = 21 * 1024; pub const RUNTIME_ORG: u32 = FMC_ORG + FMC_SIZE; -pub const RUNTIME_SIZE: u32 = 100 * 1024; +pub const RUNTIME_SIZE: u32 = 128 * 1024; pub use memory_layout::{DATA_ORG, FHT_ORG, FHT_SIZE, MAN1_ORG}; pub use wdt::{restart_wdt, start_wdt, stop_wdt, WdtTimeout}; diff --git a/drivers/src/dma.rs b/drivers/src/dma.rs index b8059f8e16..90fbf028a8 100644 --- a/drivers/src/dma.rs +++ b/drivers/src/dma.rs @@ -15,11 +15,16 @@ Abstract: use caliptra_error::{CaliptraError, CaliptraResult}; use caliptra_registers::axi_dma::{ enums::{RdRouteE, WrRouteE}, - AxiDmaReg, + AxiDmaReg, RegisterBlock, }; -use core::{ops::Add, ptr::read_volatile}; +use caliptra_registers::i3ccsr::RegisterBlock as I3CRegisterBlock; +use core::cell::Cell; +use core::ops::Add; +use ureg::{Mmio, MmioMut, RealMmioMut}; use zerocopy::AsBytes; +use crate::cprintln; + pub enum DmaReadTarget { Mbox, AhbFifo, @@ -32,6 +37,36 @@ pub struct AxiAddr { pub hi: u32, } +impl Add for AxiAddr { + type Output = Self; + + fn add(self, rhs: u32) -> Self { + AxiAddr::from(u64::from(self) + rhs as u64) + } +} + +impl Add for AxiAddr { + type Output = Self; + + fn add(self, rhs: u64) -> Self { + AxiAddr::from(u64::from(self) + rhs) + } +} + +impl Add for AxiAddr { + type Output = Self; + + fn add(self, rhs: usize) -> Self { + AxiAddr::from(u64::from(self) + rhs as u64) + } +} + +impl From for AxiAddr { + fn from(addr: u32) -> Self { + Self { lo: addr, hi: 0 } + } +} + impl From for AxiAddr { fn from(addr: u64) -> Self { Self { @@ -79,7 +114,7 @@ pub struct DmaWriteTransaction { /// Dma Widget pub struct Dma { - dma: AxiDmaReg, + dma: Cell>, } impl Dma { @@ -89,78 +124,96 @@ impl Dma { /// /// * `dma` - DMA register block pub fn new(dma: AxiDmaReg) -> Self { - Self { dma } + Self { + dma: Cell::new(Some(dma)), + } } - fn flush(&mut self) { - let dma = self.dma.regs_mut(); - - dma.ctrl().write(|c| c.flush(true)); - - // Wait till we're not busy and have no errors - // TODO this assumes the peripheral does not clear that status0 state - // in one cycle. Maybe it can be removed if the assumption proves false - - while { - let status0 = dma.status0().read(); - status0.busy() || status0.error() - } {} + pub fn with_dma( + &self, + f: impl FnOnce(RegisterBlock) -> T, + ) -> CaliptraResult { + if let Some(mut dma) = self.dma.take() { + let result = f(dma.regs_mut()); + self.dma.set(Some(dma)); + Ok(result) + } else { + Err(CaliptraError::RUNTIME_INTERNAL) // should never happen + } } - fn setup_dma_read(&mut self, read_transaction: DmaReadTransaction) { - let dma = self.dma.regs_mut(); + pub fn flush(&self) -> CaliptraResult<()> { + self.with_dma(|dma| { + dma.ctrl().write(|c| c.flush(true)); + // Wait till we're not busy and have no errors + // TODO this assumes the peripheral does not clear that status0 state + // in one cycle. Maybe it can be removed if the assumption proves false + + while { + let status0 = dma.status0().read(); + status0.busy() || status0.error() + } {} + }) + } - let read_addr = read_transaction.read_addr; - dma.src_addr_l().write(|_| read_addr.lo); - dma.src_addr_h().write(|_| read_addr.hi); + pub fn set_block_size(&self, block_size: u32) -> CaliptraResult<()> { + self.with_dma(|dma| dma.block_size().write(|f| f.size(block_size))) + } - if let DmaReadTarget::AxiWr(target_addr) = read_transaction.target { - dma.dst_addr_l().write(|_| target_addr.lo); - dma.dst_addr_h().write(|_| target_addr.hi); - } + pub fn setup_dma_read(&self, read_transaction: DmaReadTransaction) -> CaliptraResult<()> { + self.with_dma(|dma| { + let read_addr = read_transaction.read_addr; + dma.src_addr_l().write(|_| read_addr.lo); + dma.src_addr_h().write(|_| read_addr.hi); - dma.ctrl().modify(|c| { - c.rd_route(|_| match read_transaction.target { - DmaReadTarget::Mbox => RdRouteE::Mbox, - DmaReadTarget::AhbFifo => RdRouteE::AhbFifo, - DmaReadTarget::AxiWr(_) => RdRouteE::AxiWr, - }) - .rd_fixed(read_transaction.fixed_addr) - .wr_route(|_| match read_transaction.target { - DmaReadTarget::AxiWr(_) => WrRouteE::AxiRd, - _ => WrRouteE::Disable, - }) - }); + if let DmaReadTarget::AxiWr(target_addr) = read_transaction.target { + dma.dst_addr_l().write(|_| target_addr.lo); + dma.dst_addr_h().write(|_| target_addr.hi); + } - dma.byte_count().write(|_| read_transaction.length); + dma.ctrl().modify(|c| { + c.rd_route(|_| match read_transaction.target { + DmaReadTarget::Mbox => RdRouteE::Mbox, + DmaReadTarget::AhbFifo => RdRouteE::AhbFifo, + DmaReadTarget::AxiWr(_) => RdRouteE::AxiWr, + }) + .rd_fixed(read_transaction.fixed_addr) + .wr_route(|_| match read_transaction.target { + DmaReadTarget::AxiWr(_) => WrRouteE::AxiRd, + _ => WrRouteE::Disable, + }) + }); + + dma.byte_count().write(|_| read_transaction.length); + }) } - fn setup_dma_write(&mut self, write_transaction: DmaWriteTransaction) { - let dma = self.dma.regs_mut(); - - let write_addr = write_transaction.write_addr; - dma.dst_addr_l().write(|_| write_addr.lo); - dma.dst_addr_h().write(|_| write_addr.hi); + fn setup_dma_write(&self, write_transaction: DmaWriteTransaction) -> CaliptraResult<()> { + self.with_dma(|dma| { + let write_addr = write_transaction.write_addr; + dma.dst_addr_l().write(|_| write_addr.lo); + dma.dst_addr_h().write(|_| write_addr.hi); - if let DmaWriteOrigin::AxiRd(origin_addr) = write_transaction.origin { - dma.dst_addr_l().write(|_| origin_addr.lo); - dma.dst_addr_h().write(|_| origin_addr.hi); - } - - dma.ctrl().modify(|c| { - c.wr_route(|_| match write_transaction.origin { - DmaWriteOrigin::Mbox => WrRouteE::Mbox, - DmaWriteOrigin::AhbFifo => WrRouteE::AhbFifo, - DmaWriteOrigin::AxiRd(_) => WrRouteE::AxiRd, - }) - .wr_fixed(write_transaction.fixed_addr) - .rd_route(|_| match write_transaction.origin { - DmaWriteOrigin::AxiRd(_) => RdRouteE::AxiWr, - _ => RdRouteE::Disable, - }) - }); + if let DmaWriteOrigin::AxiRd(origin_addr) = write_transaction.origin { + dma.dst_addr_l().write(|_| origin_addr.lo); + dma.dst_addr_h().write(|_| origin_addr.hi); + } - dma.byte_count().write(|_| write_transaction.length); + dma.ctrl().modify(|c| { + c.wr_route(|_| match write_transaction.origin { + DmaWriteOrigin::Mbox => WrRouteE::Mbox, + DmaWriteOrigin::AhbFifo => WrRouteE::AhbFifo, + DmaWriteOrigin::AxiRd(_) => WrRouteE::AxiRd, + }) + .wr_fixed(write_transaction.fixed_addr) + .rd_route(|_| match write_transaction.origin { + DmaWriteOrigin::AxiRd(_) => RdRouteE::AxiWr, + _ => RdRouteE::Disable, + }) + }); + + dma.byte_count().write(|_| write_transaction.length); + }) } /// Read data from the DMA FIFO @@ -172,76 +225,77 @@ impl Dma { /// # Returns /// /// * `CaliptraResult<()>` - Success or error code - pub fn dma_read_fifo(&mut self, read_data: &mut [u8]) -> CaliptraResult<()> { - let dma = self.dma.regs_mut(); - - let status = dma.status0().read(); - - if read_data.len() > status.fifo_depth() as usize { - Err(CaliptraError::DRIVER_DMA_FIFO_UNDERRUN)?; - } - - // Only multiple of 4 bytes are allowed - if read_data.len() % core::mem::size_of::() != 0 { - Err(CaliptraError::DRIVER_DMA_FIFO_INVALID_SIZE)?; - } + pub fn dma_read_fifo(&self, read_data: &mut [u8]) -> CaliptraResult<()> { + self.with_dma(|dma| { + let status = dma.status0().read(); - let read_data_ptr = dma.read_data().ptr as *const u8; + if read_data.len() > status.fifo_depth() as usize { + return Err(CaliptraError::DRIVER_DMA_FIFO_UNDERRUN); + } - // Process all 4-byte chunks - for chunk in read_data.chunks_exact_mut(4) { - let value = unsafe { read_volatile(read_data_ptr as *const u32) }; - chunk.copy_from_slice(&value.to_le_bytes()); - } + // Only multiple of 4 bytes are allowed + if read_data.len() % core::mem::size_of::() != 0 { + return Err(CaliptraError::DRIVER_DMA_FIFO_INVALID_SIZE); + } - Ok(()) + // Process all 4-byte chunks + let ptr = dma.read_data().ptr as *mut u8; + read_data.chunks_mut(4).for_each(|word| { + // Reg only exports u32 writes but we need finer grained access + unsafe { + ptr.copy_to_nonoverlapping(word.as_mut_ptr(), word.len()); + } + }); + Ok(()) + })? } - fn dma_write_fifo(&mut self, write_data: &[u8]) -> CaliptraResult<()> { - let dma = self.dma.regs_mut(); - - let max_fifo_depth = dma.cap().read().fifo_max_depth(); - let current_fifo_depth = dma.status0().read().fifo_depth(); - - if write_data.len() as u32 > max_fifo_depth - current_fifo_depth { - Err(CaliptraError::DRIVER_DMA_FIFO_OVERRUN)?; - } + fn dma_write_fifo(&self, write_data: &[u8]) -> CaliptraResult<()> { + self.with_dma(|dma| { + let max_fifo_depth = dma.cap().read().fifo_max_depth(); + let current_fifo_depth = dma.status0().read().fifo_depth(); - // Only multiple of 4 bytes are allowed - if write_data.len() % core::mem::size_of::() != 0 { - Err(CaliptraError::DRIVER_DMA_FIFO_INVALID_SIZE)?; - } + if write_data.len() as u32 > max_fifo_depth - current_fifo_depth { + Err(CaliptraError::DRIVER_DMA_FIFO_OVERRUN)?; + } - // Process all 4-byte chunks - for chunk in write_data.chunks(4) { - let value = u32::from_le_bytes(chunk.try_into().unwrap()); - unsafe { (dma.write_data().ptr as *mut u32).write_volatile(value) }; - } + // Process all 4-byte chunks + if write_data.len() as u32 > max_fifo_depth - current_fifo_depth { + return Err(CaliptraError::DRIVER_DMA_FIFO_OVERRUN); + } - Ok(()) + write_data.chunks(4).for_each(|word| { + let ptr = dma.write_data().ptr as *mut u8; + // Reg only exports u32 writes but we need finer grained access + unsafe { + ptr.copy_from_nonoverlapping(word.as_ptr(), word.len()); + } + }); + Ok(()) + })? } - fn do_transaction(&mut self) -> CaliptraResult<()> { - let dma = self.dma.regs_mut(); - - let status0 = dma.status0().read(); - if status0.busy() { - Err(CaliptraError::DRIVER_DMA_TRANSACTION_ALREADY_BUSY)?; - } + pub fn do_transaction(&self) -> CaliptraResult<()> { + self.with_dma(|dma| { + let status0 = dma.status0().read(); + if status0.busy() { + Err(CaliptraError::DRIVER_DMA_TRANSACTION_ALREADY_BUSY)?; + } - if status0.error() { - Err(CaliptraError::DRIVER_DMA_TRANSACTION_ERROR)?; - } + if status0.error() { + Err(CaliptraError::DRIVER_DMA_TRANSACTION_ERROR)?; + } - dma.ctrl().modify(|c| c.go(true)); + dma.ctrl().modify(|c| c.go(true)); - while dma.status0().read().busy() { - if dma.status0().read().error() { - Err(CaliptraError::DRIVER_DMA_TRANSACTION_ERROR)?; + while dma.status0().read().busy() { + if dma.status0().read().error() { + Err(CaliptraError::DRIVER_DMA_TRANSACTION_ERROR)?; + } } - } - Ok(()) + Ok(()) + })? } /// Read a 32-bit word from the specified address @@ -253,7 +307,7 @@ impl Dma { /// # Returns /// /// * `CaliptraResult` - Read value or error code - pub fn read_dword(&mut self, read_addr: AxiAddr) -> CaliptraResult { + pub fn read_dword(&self, read_addr: AxiAddr) -> CaliptraResult { let mut read_val: u32 = 0; self.read_buffer(read_addr, read_val.as_bytes_mut())?; Ok(read_val) @@ -269,8 +323,8 @@ impl Dma { /// # Returns /// /// * CaliptraResult<()> - Success or failure - pub fn read_buffer(&mut self, read_addr: AxiAddr, buffer: &mut [u8]) -> CaliptraResult<()> { - self.flush(); + pub fn read_buffer(&self, read_addr: AxiAddr, buffer: &mut [u8]) -> CaliptraResult<()> { + self.flush()?; let read_transaction = DmaReadTransaction { read_addr, @@ -279,7 +333,7 @@ impl Dma { target: DmaReadTarget::AhbFifo, }; - self.setup_dma_read(read_transaction); + self.setup_dma_read(read_transaction)?; self.do_transaction()?; self.dma_read_fifo(buffer)?; Ok(()) @@ -295,8 +349,8 @@ impl Dma { /// # Returns /// /// * `CaliptraResult<()>` - Success or error code - pub fn write_dword(&mut self, write_addr: AxiAddr, write_val: u32) -> CaliptraResult<()> { - self.flush(); + pub fn write_dword(&self, write_addr: AxiAddr, write_val: u32) -> CaliptraResult<()> { + self.flush()?; let write_transaction = DmaWriteTransaction { write_addr, @@ -305,7 +359,7 @@ impl Dma { origin: DmaWriteOrigin::AhbFifo, }; self.dma_write_fifo(write_val.as_bytes())?; - self.setup_dma_write(write_transaction); + self.setup_dma_write(write_transaction)?; self.do_transaction()?; Ok(()) } @@ -325,13 +379,13 @@ impl Dma { /// /// * `CaliptraResult<()>` - Success or error code pub fn transfer_payload_to_mbox( - &mut self, + &self, read_addr: AxiAddr, payload_len_bytes: u32, fixed_addr: bool, block_size: u32, ) -> CaliptraResult<()> { - self.flush(); + self.flush()?; let read_transaction = DmaReadTransaction { read_addr, @@ -339,11 +393,8 @@ impl Dma { length: payload_len_bytes, target: DmaReadTarget::Mbox, }; - self.setup_dma_read(read_transaction); - self.dma - .regs_mut() - .block_size() - .write(|f| f.size(block_size)); + self.setup_dma_read(read_transaction)?; + self.set_block_size(block_size)?; self.do_transaction()?; Ok(()) } @@ -353,7 +404,204 @@ impl Dma { /// # Returns /// true if payload is available, false otherwise /// - pub fn payload_available(&self) -> bool { - self.dma.regs().status0().read().payload_available() + pub fn payload_available(&self) -> CaliptraResult { + self.with_dma(|dma| dma.status0().read().payload_available()) + } +} + +// Implementation of the Mmio and MmioMut traits that uses +// the DMA peripheral to implement the actual reads and writes. +pub struct DmaMmio<'a> { + base: AxiAddr, + dma: &'a Dma, + last_error: Cell>, +} + +impl<'a> DmaMmio<'a> { + pub fn new(base: AxiAddr, dma: &'a Dma) -> Self { + Self { + base, + dma, + last_error: Cell::new(None), + } + } + + pub fn check_error(&self, x: T) -> CaliptraResult { + match self.last_error.take() { + Some(err) => Err(err), + None => Ok(x), + } + } + + fn set_error(&self, err: Option) { + self.last_error.set(self.last_error.take().or(err)); + } +} + +impl<'a> Mmio for &DmaMmio<'a> { + unsafe fn read_volatile(&self, src: *const T) -> T { + let offset = src as usize; + let a = self.dma.read_dword(self.base + offset); + // we don't support u64 + self.set_error(a.err()); + T::from_u32(a.unwrap_or_default()) + } +} + +impl<'a> MmioMut for &DmaMmio<'a> { + unsafe fn write_volatile(&self, dst: *mut T, src: T) { + let offset = dst as usize; + // we don't support u64 + let result = self.dma.write_dword(self.base + offset, src.into()); + self.set_error(result.err()); + } +} + +// Wrapper around the DMA peripheral that provides access to the I3C recovery interface. +pub struct DmaRecovery<'a> { + base: AxiAddr, + dma: &'a Dma, +} + +impl<'a> DmaRecovery<'a> { + #[inline(always)] + pub fn new(base: AxiAddr, dma: &'a Dma) -> Self { + Self { base, dma } + } + + /// Returns a register block that can be used to read + /// registers from this peripheral, but cannot write. + #[inline(always)] + pub fn with_regs(&self, f: F) -> CaliptraResult + where + F: FnOnce(I3CRegisterBlock<&DmaMmio>) -> T, + { + let mmio = DmaMmio::new(self.base, self.dma); + // SAFETY: we aren't referencing memory directly + let regs = unsafe { + I3CRegisterBlock::new_with_mmio( + core::ptr::null_mut(), // we don't use this except for offset calculations, + &mmio, + ) + }; + let t = f(regs); + match mmio.last_error.take() { + Some(err) => Err(err), + None => Ok(t), + } + } + + /// Return a register block that can be used to read and + /// write this peripheral's registers. + #[inline(always)] + pub fn with_regs_mut(&self, f: F) -> CaliptraResult + where + F: FnOnce(I3CRegisterBlock<&DmaMmio>) -> T, + { + let mmio = DmaMmio::new(self.base, self.dma); + // SAFETY: we aren't referencing memory directly + let regs = unsafe { + I3CRegisterBlock::new_with_mmio( + core::ptr::null_mut(), // we don't use this except for offset calculations + &mmio, + ) + }; + let t = f(regs); + match mmio.last_error.take() { + Some(err) => Err(err), + None => Ok(t), + } + } + + fn transfer_payload_to_mbox( + &self, + read_addr: AxiAddr, + payload_len_bytes: u32, + fixed_addr: bool, + block_size: u32, + ) -> CaliptraResult<()> { + self.dma.flush()?; + + let read_transaction = DmaReadTransaction { + read_addr, + fixed_addr, + length: payload_len_bytes, + target: DmaReadTarget::Mbox, + }; + self.dma.setup_dma_read(read_transaction)?; + self.dma.set_block_size(block_size)?; + self.dma.do_transaction()?; + Ok(()) + } + + pub fn transfer_mailbox_to_axi( + &self, + payload_len_bytes: u32, + block_size: u32, + write_addr: AxiAddr, + fixed_addr: bool, + ) -> CaliptraResult<()> { + self.dma.flush()?; + + let write_transaction = DmaWriteTransaction { + write_addr, + fixed_addr, + length: payload_len_bytes, + origin: DmaWriteOrigin::Mbox, + }; + self.dma.setup_dma_write(write_transaction)?; + self.dma.set_block_size(block_size)?; + self.dma.do_transaction()?; + Ok(()) + } + + // Downloads an image from the recovery interface to the mailbox SRAM. + pub fn download_image_to_mbox(&self, fw_image_index: u32) -> CaliptraResult { + const INDIRECT_FIFO_DATA_OFFSET: u32 = 0x68; + const RECOVERY_DMA_BLOCK_SIZE_BYTES: u32 = 256; + + self.with_regs_mut(|regs_mut| { + let recovery = regs_mut.sec_fw_recovery_if(); + + // Set PROT_CAP:Byte11 Bit3 (i.e. DWORD2:Bit27) to 1 ('Flashless boot'). + recovery.prot_cap_0().modify(|val| val | (1 << 27)); + + // Set DEVICE_STATUS:Byte0 to 0x3 ('Recovery mode - ready to accept recovery image'). + // Set DEVICE_STATUS:Byte[2:3] to 0x12 ('Recovery Reason Codes' 0x12 = 0 Flashless/Streaming Boot (FSB)). + recovery + .device_status_0() + .modify(|val| (val & 0xFF00FF00) | (0x12 << 16) | 0x03); + + // Set RECOVERY_STATUS:Byte0 Bit[3:0] to 0x1 ('Awaiting recovery image') & + // Byte0 Bit[7:4] to 0 (Recovery image index). + // [TODO][CAP2] the spec says this register is read-only, but there is no other way to select an image index? + recovery.recovery_status().modify(|recovery_status_val| { + // Set Byte0 Bit[3:0] to 0x1 ('Awaiting recovery image') + // Set Byte0 Bit[7:4] to recovery image index + (recovery_status_val & 0xFFFFFF00) | (fw_image_index << 4) | 0x1 + }); + })?; + + // Loop on the 'payload_available' signal for the recovery image details to be available. + cprintln!("[fwproc] Waiting for payload available signal..."); + while !self.dma.payload_available()? {} + let image_size_bytes = self.with_regs_mut(|regs_mut| { + let recovery = regs_mut.sec_fw_recovery_if(); + + // [TODO][CAP2] we need to program CMS bits, currently they are not available in RDL. Using bytes[4:7] for now for size + + // Read the image size from INDIRECT_FIFO_CTRL:Byte[2:5]. Image size in DWORDs. + let indirect_fifo_ctrl_val0 = recovery.indirect_fifo_ctrl_0().read(); + let indirect_fifo_ctrl_val1 = recovery.indirect_fifo_ctrl_1().read(); + let image_size_dwords = ((indirect_fifo_ctrl_val0 >> 16) & 0xFFFF) + | ((indirect_fifo_ctrl_val1 & 0xFFFF) << 16); + + image_size_dwords * 4 + })?; + + // Transfer the image from the recovery interface to the mailbox SRAM. + let addr = self.base + INDIRECT_FIFO_DATA_OFFSET; + self.transfer_payload_to_mbox(addr, image_size_bytes, true, RECOVERY_DMA_BLOCK_SIZE_BYTES)?; + Ok(image_size_bytes) } } diff --git a/drivers/src/lib.rs b/drivers/src/lib.rs index bf28f5af92..9870ef4efe 100644 --- a/drivers/src/lib.rs +++ b/drivers/src/lib.rs @@ -61,7 +61,8 @@ pub use caliptra_error::{CaliptraError, CaliptraResult}; pub use csrng::{Csrng, HealthFailCounts as CsrngHealthFailCounts, Seed as CsrngSeed}; pub use data_vault::{ColdResetEntries, DataVault, WarmResetEntries}; pub use dma::{ - AxiAddr, Dma, DmaReadTarget, DmaReadTransaction, DmaWriteOrigin, DmaWriteTransaction, + AxiAddr, Dma, DmaReadTarget, DmaReadTransaction, DmaRecovery, DmaWriteOrigin, + DmaWriteTransaction, }; pub use doe::DeobfuscationEngine; pub use ecc384::{ diff --git a/rom/dev/src/flow/cold_reset/fw_processor.rs b/rom/dev/src/flow/cold_reset/fw_processor.rs index d5dd837419..c565c50104 100644 --- a/rom/dev/src/flow/cold_reset/fw_processor.rs +++ b/rom/dev/src/flow/cold_reset/fw_processor.rs @@ -32,6 +32,7 @@ use caliptra_common::{ }; use caliptra_drivers::{pcr_log::MeasurementLogEntry, *}; use caliptra_image_types::{FwVerificationPqcKeyType, ImageManifest, IMAGE_BYTE_SIZE}; +use caliptra_image_types::{ImageManifest, IMAGE_BYTE_SIZE}; use caliptra_image_verify::{ImageVerificationInfo, ImageVerificationLogInfo, ImageVerifier}; use caliptra_kat::KatsEnv; use caliptra_x509::{NotAfter, NotBefore}; @@ -823,63 +824,9 @@ impl FirmwareProcessor { dma: &mut Dma, soc_ifc: &mut SocIfc, ) -> CaliptraResult { - let rri_base_addr: u64 = soc_ifc.recovery_interface_base_addr(); - const PROT_CAP_2_OFFSET: u32 = 0xC; - const DEVICE_STATUS_0: u32 = 0x30; - const RECOVERY_STATUS_OFFSET: u32 = 0x40; + let rri_base_addr = soc_ifc.recovery_interface_base_addr().into(); const FW_IMAGE_INDEX: u32 = 0x0; - // const INDIRECT_FIFO_CTRL_0: u32 = 0x48; - const INDIRECT_FIFO_CTRL_1: u32 = 0x4C; - const INDIRECT_FIFO_DATA_OFFSET: u32 = 0x68; - const RECOVERY_DMA_BLOCK_SIZE_BYTES: u32 = 256; - - // Set PROT_CAP:Byte11 Bit3 (i.e. DWORD2:Bit27) to 1 ('Flashless boot'). - let addr = AxiAddr::from(rri_base_addr + PROT_CAP_2_OFFSET as u64); - let mut prot_cap_val = dma.read_dword(addr)?; - prot_cap_val |= 1 << 27; - dma.write_dword(addr, prot_cap_val)?; - - // Set DEVICE_STATUS:Byte0 to 0x3 ('Recovery mode - ready to accept recovery image'). - let addr = AxiAddr::from(rri_base_addr + DEVICE_STATUS_0 as u64); - let mut device_status_val = dma.read_dword(addr)?; - device_status_val = (device_status_val & 0xFFFFFF00) | 0x03; - - // Set DEVICE_STATUS:Byte[2:3] to 0x12 ('Recovery Reason Codes' 0x12 = 0 Flashless/Streaming Boot (FSB)). - device_status_val = (device_status_val & 0xFF00FFFF) | (0x12 << 16); - - dma.write_dword(addr, device_status_val)?; - - // Set RECOVERY_STATUS:Byte0 Bit[3:0] to 0x1 ('Awaiting recovery image') & - // Byte0 Bit[7:4] to 0 (Recovery image index). - let addr = AxiAddr::from(rri_base_addr + RECOVERY_STATUS_OFFSET as u64); - let mut recovery_status_val = dma.read_dword(addr)?; - - // Set Byte0 Bit[3:0] to 0x1 ('Awaiting recovery image') - recovery_status_val = (recovery_status_val & 0xFFFFFFF0) | 0x1; - - // Set Byte0 Bit[7:4] to FW_IMAGE_INDEX (Recovery image index) - recovery_status_val = (recovery_status_val & 0xFFFFFF0F) | (FW_IMAGE_INDEX << 4); - - dma.write_dword(addr, recovery_status_val)?; - - // Loop on the 'payload_available' signal for the recovery image details to be available. - cprintln!("[fwproc] Waiting for payload available signal..."); - while !dma.payload_available() {} - - // Read the image size from INDIRECT_FIFO_CTRL:Byte[2:5]. Image size in DWORDs. - // let addr0 = AxiAddr::from(rri_base_addr + INDIRECT_FIFO_CTRL_0 as u64); - // [TODO][CAP2] we need to program CMS bits, currently they are not available in RDL. Using bytes[4:7] for now for size - let addr1 = AxiAddr::from(rri_base_addr + INDIRECT_FIFO_CTRL_1 as u64); - // let indirect_fifo_ctrl_val0 = dma.read_dword(addr0)?; - let indirect_fifo_ctrl_val1 = dma.read_dword(addr1)?; - // let image_size_dwords = - // ((indirect_fifo_ctrl_val0 >> 16) & 0xFFFF) | ((indirect_fifo_ctrl_val1 & 0xFFFF) << 16); - let image_size_dwords = indirect_fifo_ctrl_val1; - - // Transfer the image from the recovery interface to the mailbox SRAM. - let image_size_bytes = image_size_dwords * 4; - let addr = AxiAddr::from(rri_base_addr + INDIRECT_FIFO_DATA_OFFSET as u64); - dma.transfer_payload_to_mbox(addr, image_size_bytes, true, RECOVERY_DMA_BLOCK_SIZE_BYTES)?; - Ok(image_size_bytes) + let dma_recovery = DmaRecovery::new(rri_base_addr, dma); + dma_recovery.download_image_to_mbox(FW_IMAGE_INDEX) } } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 0323f58eb4..09123eb00a 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -7,9 +7,14 @@ edition = "2021" build = "build.rs" [dependencies] -caliptra-cfi-lib-git = { workspace = true, default-features = false, features = ["cfi", "cfi-counter" ] } +caliptra-cfi-lib-git = { workspace = true, default-features = false, features = [ + "cfi", + "cfi-counter", +] } caliptra-cfi-derive-git.workspace = true -caliptra_common = { workspace = true, default-features = false, features = ["runtime"] } +caliptra_common = { workspace = true, default-features = false, features = [ + "runtime", +] } caliptra-cpu.workspace = true caliptra-drivers = { workspace = true, features = ["runtime"] } caliptra-error = { workspace = true, default-features = false } @@ -19,7 +24,7 @@ caliptra-kat.workspace = true caliptra-lms-types.workspace = true caliptra-registers.workspace = true caliptra-x509 = { workspace = true, default-features = false } -dpe = { workspace = true, features = ["arbitrary_max_handles"] } +dpe = { workspace = true, features = ["arbitrary_max_handles"] } crypto.workspace = true platform.workspace = true ufmt.workspace = true @@ -29,6 +34,7 @@ caliptra-image-verify = { workspace = true, default-features = false } zeroize.workspace = true bitflags.workspace = true memoffset.workspace = true +ureg.workspace = true [build-dependencies] caliptra_common = { workspace = true, default-features = false } @@ -61,7 +67,7 @@ riscv = ["caliptra-cpu/riscv"] std = ["ufmt/std", "caliptra_common/std"] slow_tests = [] verilator = ["caliptra-hw-model/verilator"] -fips_self_test=[] +fips_self_test = [] no-cfi = ["caliptra-image-verify/no-cfi", "caliptra-drivers/no-cfi"] fpga_realtime = ["caliptra-drivers/fpga_realtime"] -fips-test-hooks = ["caliptra-drivers/fips-test-hooks"] \ No newline at end of file +fips-test-hooks = ["caliptra-drivers/fips-test-hooks"] diff --git a/runtime/src/drivers.rs b/runtime/src/drivers.rs index 2c018319a6..5c3f398325 100644 --- a/runtime/src/drivers.rs +++ b/runtime/src/drivers.rs @@ -16,7 +16,7 @@ Abstract: #[cfg(feature = "fips_self_test")] pub use crate::fips::{fips_self_test_cmd, fips_self_test_cmd::SelfTestStatus}; - +use crate::recovery_flow::RecoveryFlow; use crate::{ dice, CptraDpeTypes, DisableAttestationCmd, DpeCrypto, DpePlatform, Mailbox, DPE_SUPPORT, MAX_CERT_CHAIN_SIZE, PL0_DPE_ACTIVE_CONTEXT_THRESHOLD, PL0_PAUSER_FLAG, @@ -24,9 +24,11 @@ use crate::{ }; use arrayvec::ArrayVec; +use caliptra_auth_man_types::AuthorizationManifest; use caliptra_cfi_derive_git::{cfi_impl_fn, cfi_mod_fn}; use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_assert_eq_12_words, cfi_launder}; use caliptra_common::mailbox_api::AddSubjectAltNameReq; +use caliptra_drivers::Dma; use caliptra_drivers::{ cprint, cprintln, hand_off::DataStore, pcr_log::RT_FW_JOURNEY_PCR, Array4x12, CaliptraError, CaliptraResult, DataVault, Ecc384, Ecc384PubKey, Hmac, KeyId, KeyVault, Lms, Mldsa87, PcrBank, @@ -34,6 +36,7 @@ use caliptra_drivers::{ Sha2_512_384Acc, SocIfc, Trng, }; use caliptra_image_types::ImageManifest; +use caliptra_registers::axi_dma::AxiDmaReg; use caliptra_registers::{ csrng::CsrngReg, dv::DvReg, @@ -117,6 +120,7 @@ pub struct Drivers { pub is_shutdown: bool, pub dmtf_device_info: Option>, + pub dma: Dma, } impl Drivers { @@ -155,6 +159,7 @@ impl Drivers { cert_chain: ArrayVec::new(), is_shutdown: false, dmtf_device_info: None, + dma: Dma::new(AxiDmaReg::new()), }) } @@ -171,6 +176,9 @@ impl Drivers { ResetReason::ColdReset => { cfi_assert_eq(self.soc_ifc.reset_reason(), ResetReason::ColdReset); Self::initialize_dpe(self)?; + if self.soc_ifc.active_mode() { + RecoveryFlow::recovery_flow(self)?; + } } ResetReason::UpdateReset => { cfi_assert_eq(self.soc_ifc.reset_reason(), ResetReason::UpdateReset); diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 58627a9999..023be559aa 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -29,6 +29,7 @@ pub mod info; mod invoke_dpe; mod pcr; mod populate_idev; +mod recovery_flow; mod set_auth_manifest; mod stash_measurement; mod subject_alt_name; diff --git a/runtime/src/recovery_flow.rs b/runtime/src/recovery_flow.rs new file mode 100644 index 0000000000..de11c80852 --- /dev/null +++ b/runtime/src/recovery_flow.rs @@ -0,0 +1,67 @@ +/*++ + +Licensed under the Apache-2.0 license. + +File Name: + + recovery_flow.rs + +Abstract: + + File contains the implementation of the recovery flow for MCU firmware and SoC manifest. + +--*/ + +use crate::Drivers; +use caliptra_auth_man_types::{ + AuthManifestImageMetadataCollection, AuthManifestPreamble, AuthorizationManifest, + AUTH_MANIFEST_PREAMBLE_SIZE, +}; +use caliptra_cfi_derive_git::{cfi_impl_fn, cfi_mod_fn}; +use caliptra_drivers::{AxiAddr, Dma, DmaReadTarget, DmaReadTransaction, DmaRecovery}; +use caliptra_kat::{CaliptraError, CaliptraResult}; +use caliptra_registers::i3ccsr::RegisterBlock; +use core::{ + any::TypeId, + cell::{Cell, RefCell}, +}; +use ureg::{Mmio, MmioMut, Uint, UintType}; +use zerocopy::{AsBytes, FromBytes}; + +pub enum RecoveryFlow {} + +impl RecoveryFlow { + /// Load the SoC Manifest and MCU firwmare + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + pub(crate) fn recovery_flow(drivers: &mut Drivers) -> CaliptraResult<()> { + const SOC_MANIFEST_INDEX: u32 = 1; + const MCU_FIRMWARE_INDEX: u32 = 2; + + let dma = &drivers.dma; + let dma_recovery = + DmaRecovery::new(drivers.soc_ifc.recovery_interface_base_addr().into(), dma); + + // // download SoC manifest + let _soc_size_bytes = dma_recovery.download_image_to_mbox(SOC_MANIFEST_INDEX)?; + let Some(preamble) = AuthManifestPreamble::read_from_prefix(drivers.mbox.raw_mailbox_contents()) else { + return Err(CaliptraError::IMAGE_VERIFIER_ERR_MANIFEST_SIZE_MISMATCH); + }; + let Some(image_metadata_collection) = AuthManifestImageMetadataCollection::read_from_prefix(&drivers.mbox.raw_mailbox_contents()[AUTH_MANIFEST_PREAMBLE_SIZE..]) else { + return Err(CaliptraError::IMAGE_VERIFIER_ERR_MANIFEST_SIZE_MISMATCH); + }; + // [TODO][CAP2]: authenticate SoC manifest using keys available through Caliptra Image + // TODO: switch to ref_from method when we upgrade zerocopy + drivers + .persistent_data + .get_mut() + .auth_manifest_image_metadata_col = image_metadata_collection; + // [TODO][CAP2]: capture measurement of Soc manifest? + // [TODO][CAP2]: this should be writing to MCU SRAM directly via AXI + let _mcu_size_bytes = dma_recovery.download_image_to_mbox(SOC_MANIFEST_INDEX)?; + // [TODO][CAP2]: instruct Caliptra HW to read MCU SRAM and generate the hash (using HW SHA accelerator and AXI mastering capabilities to do this) + // [TODO][CAP2]: use this hash and verify it against the hash in the SOC manifest + // [TODO][CAP2]: after verifying/authorizing the image and if it passes, it will set EXEC/GO bit into the register as specified in the previous command. This register write will also assert a Caliptra interface wire + // [TODO][CAP2]: set recovery flow is completed + Ok(()) + } +} diff --git a/ureg/src/lib.rs b/ureg/src/lib.rs index 45e0413fb9..cb8f0a98b0 100644 --- a/ureg/src/lib.rs +++ b/ureg/src/lib.rs @@ -19,11 +19,11 @@ pub enum UintType { U8, U16, U32, - U64, } -pub trait Uint: Clone + Copy + private::Sealed { +pub trait Uint: Clone + Copy + Into + private::Sealed { const TYPE: UintType; + fn from_u32(val: u32) -> Self; } mod private { @@ -32,24 +32,28 @@ mod private { impl Uint for u8 { const TYPE: UintType = UintType::U8; + fn from_u32(val: u32) -> Self { + (val & 0xff) as u8 + } } impl private::Sealed for u8 {} impl Uint for u16 { const TYPE: UintType = UintType::U16; + fn from_u32(val: u32) -> Self { + (val & 0xffff) as u16 + } } impl private::Sealed for u16 {} impl Uint for u32 { const TYPE: UintType = UintType::U32; + fn from_u32(val: u32) -> Self { + val + } } impl private::Sealed for u32 {} -impl Uint for u64 { - const TYPE: UintType = UintType::U64; -} -impl private::Sealed for u64 {} - /// The root trait for metadata describing a MMIO register. /// /// Implementors of this trait should also consider implementing From 76758de0ce7632cbb6308c6bbf426f5cefc90537 Mon Sep 17 00:00:00 2001 From: Christopher Swenson Date: Tue, 28 Jan 2025 09:52:40 -0800 Subject: [PATCH 2/7] Address feedback --- drivers/src/dma.rs | 2 +- error/src/lib.rs | 1 + runtime/src/recovery_flow.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/src/dma.rs b/drivers/src/dma.rs index 90fbf028a8..20ad258047 100644 --- a/drivers/src/dma.rs +++ b/drivers/src/dma.rs @@ -138,7 +138,7 @@ impl Dma { self.dma.set(Some(dma)); Ok(result) } else { - Err(CaliptraError::RUNTIME_INTERNAL) // should never happen + Err(CaliptraError::DRIVER_DMA_INTERNAL) // should never happen } } diff --git a/error/src/lib.rs b/error/src/lib.rs index 68c99fd738..a5a1f7d092 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -381,6 +381,7 @@ impl CaliptraError { pub const DRIVER_DMA_FIFO_UNDERRUN: CaliptraError = CaliptraError::new_const(0x0000f002); pub const DRIVER_DMA_FIFO_OVERRUN: CaliptraError = CaliptraError::new_const(0x0000f003); pub const DRIVER_DMA_FIFO_INVALID_SIZE: CaliptraError = CaliptraError::new_const(0x0000f004); + pub const DRIVER_DMA_INTERNAL: CaliptraError = CaliptraError::new_const(0x0000f004); /// Runtime Errors pub const RUNTIME_INTERNAL: CaliptraError = CaliptraError::new_const(0x000E0001); diff --git a/runtime/src/recovery_flow.rs b/runtime/src/recovery_flow.rs index de11c80852..041140c218 100644 --- a/runtime/src/recovery_flow.rs +++ b/runtime/src/recovery_flow.rs @@ -57,7 +57,7 @@ impl RecoveryFlow { .auth_manifest_image_metadata_col = image_metadata_collection; // [TODO][CAP2]: capture measurement of Soc manifest? // [TODO][CAP2]: this should be writing to MCU SRAM directly via AXI - let _mcu_size_bytes = dma_recovery.download_image_to_mbox(SOC_MANIFEST_INDEX)?; + let _mcu_size_bytes = dma_recovery.download_image_to_mbox(MCU_FIRMWARE_INDEX)?; // [TODO][CAP2]: instruct Caliptra HW to read MCU SRAM and generate the hash (using HW SHA accelerator and AXI mastering capabilities to do this) // [TODO][CAP2]: use this hash and verify it against the hash in the SOC manifest // [TODO][CAP2]: after verifying/authorizing the image and if it passes, it will set EXEC/GO bit into the register as specified in the previous command. This register write will also assert a Caliptra interface wire From 919ca05b8f5ca2856c34efb62a9e7f0a9350577a Mon Sep 17 00:00:00 2001 From: Christopher Swenson Date: Tue, 28 Jan 2025 14:56:56 -0800 Subject: [PATCH 3/7] Read the whole manifest in at once --- runtime/src/recovery_flow.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/runtime/src/recovery_flow.rs b/runtime/src/recovery_flow.rs index 041140c218..1274190541 100644 --- a/runtime/src/recovery_flow.rs +++ b/runtime/src/recovery_flow.rs @@ -43,10 +43,7 @@ impl RecoveryFlow { // // download SoC manifest let _soc_size_bytes = dma_recovery.download_image_to_mbox(SOC_MANIFEST_INDEX)?; - let Some(preamble) = AuthManifestPreamble::read_from_prefix(drivers.mbox.raw_mailbox_contents()) else { - return Err(CaliptraError::IMAGE_VERIFIER_ERR_MANIFEST_SIZE_MISMATCH); - }; - let Some(image_metadata_collection) = AuthManifestImageMetadataCollection::read_from_prefix(&drivers.mbox.raw_mailbox_contents()[AUTH_MANIFEST_PREAMBLE_SIZE..]) else { + let Some(manifest) = AuthorizationManifest::read_from_prefix(drivers.mbox.raw_mailbox_contents()) else { return Err(CaliptraError::IMAGE_VERIFIER_ERR_MANIFEST_SIZE_MISMATCH); }; // [TODO][CAP2]: authenticate SoC manifest using keys available through Caliptra Image @@ -54,7 +51,7 @@ impl RecoveryFlow { drivers .persistent_data .get_mut() - .auth_manifest_image_metadata_col = image_metadata_collection; + .auth_manifest_image_metadata_col = manifest.image_metadata_col; // [TODO][CAP2]: capture measurement of Soc manifest? // [TODO][CAP2]: this should be writing to MCU SRAM directly via AXI let _mcu_size_bytes = dma_recovery.download_image_to_mbox(MCU_FIRMWARE_INDEX)?; From de27ca7a22bc8dc7c1076758a84b4f157fb781fc Mon Sep 17 00:00:00 2001 From: Christopher Swenson Date: Thu, 30 Jan 2025 10:05:56 -0800 Subject: [PATCH 4/7] Add back U64; ensure that we only support 32-bit DMA with unreachable macro --- drivers/src/dma.rs | 21 ++++++++++++++++----- ureg/src/lib.rs | 11 ++++++++++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/src/dma.rs b/drivers/src/dma.rs index 20ad258047..9f595f4e90 100644 --- a/drivers/src/dma.rs +++ b/drivers/src/dma.rs @@ -439,21 +439,32 @@ impl<'a> DmaMmio<'a> { } impl<'a> Mmio for &DmaMmio<'a> { + #[inline(always)] unsafe fn read_volatile(&self, src: *const T) -> T { + // we only support 32-bit reads + if T::TYPE != ureg::UintType::U32 { + unreachable!(); + } let offset = src as usize; let a = self.dma.read_dword(self.base + offset); - // we don't support u64 self.set_error(a.err()); T::from_u32(a.unwrap_or_default()) } } impl<'a> MmioMut for &DmaMmio<'a> { + #[inline(always)] unsafe fn write_volatile(&self, dst: *mut T, src: T) { - let offset = dst as usize; - // we don't support u64 - let result = self.dma.write_dword(self.base + offset, src.into()); - self.set_error(result.err()); + // we only support 32-bit writes + if T::TYPE != ureg::UintType::U32 { + unreachable!(); + } + // this will always work because we only support u32 + if let Ok(src) = src.try_into() { + let offset = dst as usize; + let result = self.dma.write_dword(self.base + offset, src); + self.set_error(result.err()); + } } } diff --git a/ureg/src/lib.rs b/ureg/src/lib.rs index cb8f0a98b0..bd125ff078 100644 --- a/ureg/src/lib.rs +++ b/ureg/src/lib.rs @@ -19,9 +19,10 @@ pub enum UintType { U8, U16, U32, + U64, } -pub trait Uint: Clone + Copy + Into + private::Sealed { +pub trait Uint: Clone + Copy + TryInto + private::Sealed { const TYPE: UintType; fn from_u32(val: u32) -> Self; } @@ -54,6 +55,14 @@ impl Uint for u32 { } impl private::Sealed for u32 {} +impl Uint for u64 { + const TYPE: UintType = UintType::U64; + fn from_u32(val: u32) -> Self { + val as u64 + } +} +impl private::Sealed for u64 {} + /// The root trait for metadata describing a MMIO register. /// /// Implementors of this trait should also consider implementing From 020f2a3ba7797d8278144ae8bb6ff45063917963 Mon Sep 17 00:00:00 2001 From: Christopher Swenson Date: Thu, 30 Jan 2025 10:49:31 -0800 Subject: [PATCH 5/7] Eliminate from_u32 with TryFrom; add todo about set_manifest --- drivers/src/dma.rs | 3 ++- rom/dev/src/flow/cold_reset/fw_processor.rs | 1 - runtime/src/recovery_flow.rs | 1 + ureg/src/lib.rs | 15 +-------------- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/src/dma.rs b/drivers/src/dma.rs index 9f595f4e90..f2545224a0 100644 --- a/drivers/src/dma.rs +++ b/drivers/src/dma.rs @@ -448,7 +448,8 @@ impl<'a> Mmio for &DmaMmio<'a> { let offset = src as usize; let a = self.dma.read_dword(self.base + offset); self.set_error(a.err()); - T::from_u32(a.unwrap_or_default()) + // try_into() will always succeed since we only support u32 + a.unwrap_or_default().try_into().unwrap_or_default() } } diff --git a/rom/dev/src/flow/cold_reset/fw_processor.rs b/rom/dev/src/flow/cold_reset/fw_processor.rs index c565c50104..2ae9159b8f 100644 --- a/rom/dev/src/flow/cold_reset/fw_processor.rs +++ b/rom/dev/src/flow/cold_reset/fw_processor.rs @@ -32,7 +32,6 @@ use caliptra_common::{ }; use caliptra_drivers::{pcr_log::MeasurementLogEntry, *}; use caliptra_image_types::{FwVerificationPqcKeyType, ImageManifest, IMAGE_BYTE_SIZE}; -use caliptra_image_types::{ImageManifest, IMAGE_BYTE_SIZE}; use caliptra_image_verify::{ImageVerificationInfo, ImageVerificationLogInfo, ImageVerifier}; use caliptra_kat::KatsEnv; use caliptra_x509::{NotAfter, NotBefore}; diff --git a/runtime/src/recovery_flow.rs b/runtime/src/recovery_flow.rs index 1274190541..125e77cbc0 100644 --- a/runtime/src/recovery_flow.rs +++ b/runtime/src/recovery_flow.rs @@ -48,6 +48,7 @@ impl RecoveryFlow { }; // [TODO][CAP2]: authenticate SoC manifest using keys available through Caliptra Image // TODO: switch to ref_from method when we upgrade zerocopy + // [TODO][CAP2]: replace this copy with set_manifest drivers .persistent_data .get_mut() diff --git a/ureg/src/lib.rs b/ureg/src/lib.rs index bd125ff078..0fe2a6e901 100644 --- a/ureg/src/lib.rs +++ b/ureg/src/lib.rs @@ -22,9 +22,8 @@ pub enum UintType { U64, } -pub trait Uint: Clone + Copy + TryInto + private::Sealed { +pub trait Uint: Clone + Copy + Default + TryInto + TryFrom + private::Sealed { const TYPE: UintType; - fn from_u32(val: u32) -> Self; } mod private { @@ -33,33 +32,21 @@ mod private { impl Uint for u8 { const TYPE: UintType = UintType::U8; - fn from_u32(val: u32) -> Self { - (val & 0xff) as u8 - } } impl private::Sealed for u8 {} impl Uint for u16 { const TYPE: UintType = UintType::U16; - fn from_u32(val: u32) -> Self { - (val & 0xffff) as u16 - } } impl private::Sealed for u16 {} impl Uint for u32 { const TYPE: UintType = UintType::U32; - fn from_u32(val: u32) -> Self { - val - } } impl private::Sealed for u32 {} impl Uint for u64 { const TYPE: UintType = UintType::U64; - fn from_u32(val: u32) -> Self { - val as u64 - } } impl private::Sealed for u64 {} From de73c2fee4192208c7ea4fbc343eda544784895d Mon Sep 17 00:00:00 2001 From: Christopher Swenson Date: Mon, 3 Feb 2025 09:22:12 -0800 Subject: [PATCH 6/7] Offset dma recovery interface by -0x100 To account for the offset inside the I3C registers. Otherwise the AXI address is off by 0x100. Also sets the PROT_CAP registers to their required values in the emulator. --- drivers/src/dma.rs | 6 ++++-- sw-emulator/lib/periph/src/dma/recovery.rs | 14 ++++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/src/dma.rs b/drivers/src/dma.rs index f2545224a0..ac83611844 100644 --- a/drivers/src/dma.rs +++ b/drivers/src/dma.rs @@ -492,7 +492,8 @@ impl<'a> DmaRecovery<'a> { // SAFETY: we aren't referencing memory directly let regs = unsafe { I3CRegisterBlock::new_with_mmio( - core::ptr::null_mut(), // we don't use this except for offset calculations, + // substract the recovery offset since all recovery registers are relative to it + core::ptr::null_mut::().sub(0x100 / core::mem::size_of::()), &mmio, ) }; @@ -514,7 +515,8 @@ impl<'a> DmaRecovery<'a> { // SAFETY: we aren't referencing memory directly let regs = unsafe { I3CRegisterBlock::new_with_mmio( - core::ptr::null_mut(), // we don't use this except for offset calculations + // substract the recovery offset since all recovery registers are relative to it + core::ptr::null_mut::().sub(0x100 / core::mem::size_of::()), &mmio, ) }; diff --git a/sw-emulator/lib/periph/src/dma/recovery.rs b/sw-emulator/lib/periph/src/dma/recovery.rs index da71b107de..4dcc4e1b75 100644 --- a/sw-emulator/lib/periph/src/dma/recovery.rs +++ b/sw-emulator/lib/periph/src/dma/recovery.rs @@ -165,10 +165,16 @@ impl RecoveryRegisterInterface { Self { // Capability registers extcap_header: ReadOnlyRegister::new(0x0020C0), // CAP_LENGTH = 0x0020, CAP_ID = 0xC0 - prot_cap_0: ReadWriteRegister::new(0), - prot_cap_1: ReadWriteRegister::new(0), - prot_cap_2: ReadWriteRegister::new(0), - prot_cap_3: ReadWriteRegister::new(0), + prot_cap_0: ReadWriteRegister::new(0x2050_434f), // "OCP RECV" in ASCII + prot_cap_1: ReadWriteRegister::new(0x5643_4552), + // lower two bytes are version 1.1 + // required: device id = bit 0 + // required: device status = bit 4 + // recovery memory access / indirect ctrl = bit 5 + // c-image = bit 7 + // fifo cms support / indirect ctrl= bit 12 + prot_cap_2: ReadWriteRegister::new(0x10b1_0101), + prot_cap_3: ReadWriteRegister::new(0x0000_0017), // maximum response time of 128ms, no heartbeat // Device ID registers device_id_0: ReadWriteRegister::new(0), From 6d537f9ce753af8a07b16e045881ac79e0a669fd Mon Sep 17 00:00:00 2001 From: Christopher Swenson Date: Wed, 5 Feb 2025 09:13:45 -0800 Subject: [PATCH 7/7] Address feedback --- drivers/src/dma.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/src/dma.rs b/drivers/src/dma.rs index ac83611844..afd1073413 100644 --- a/drivers/src/dma.rs +++ b/drivers/src/dma.rs @@ -476,6 +476,8 @@ pub struct DmaRecovery<'a> { } impl<'a> DmaRecovery<'a> { + const RECOVERY_REGISTER_OFFSET: usize = 0x100; + #[inline(always)] pub fn new(base: AxiAddr, dma: &'a Dma) -> Self { Self { base, dma } @@ -493,7 +495,8 @@ impl<'a> DmaRecovery<'a> { let regs = unsafe { I3CRegisterBlock::new_with_mmio( // substract the recovery offset since all recovery registers are relative to it - core::ptr::null_mut::().sub(0x100 / core::mem::size_of::()), + core::ptr::null_mut::() + .sub(Self::RECOVERY_REGISTER_OFFSET / core::mem::size_of::()), &mmio, ) }; @@ -516,7 +519,8 @@ impl<'a> DmaRecovery<'a> { let regs = unsafe { I3CRegisterBlock::new_with_mmio( // substract the recovery offset since all recovery registers are relative to it - core::ptr::null_mut::().sub(0x100 / core::mem::size_of::()), + core::ptr::null_mut::() + .sub(Self::RECOVERY_REGISTER_OFFSET / core::mem::size_of::()), &mmio, ) };