diff --git a/builder/src/firmware.rs b/builder/src/firmware.rs index 1b665c0e9e..95265f42a8 100644 --- a/builder/src/firmware.rs +++ b/builder/src/firmware.rs @@ -159,11 +159,6 @@ pub mod driver_tests { features: &["emu"], }; - pub const DMA: FwId = FwId { - bin_name: "axi_dma_tests", - ..BASE_FWID - }; - pub const DOE: FwId = FwId { bin_name: "doe", ..BASE_FWID @@ -414,7 +409,6 @@ pub const REGISTERED_FW: &[&FwId] = &[ &hw_model_tests::TEST_DCCM_DOUBLE_BIT_ECC, &hw_model_tests::TEST_UNITIALIZED_READ, &hw_model_tests::TEST_PCR_EXTEND, - &driver_tests::DMA, &driver_tests::DOE, &driver_tests::ECC384, &driver_tests::ECC384_SIGN_VALIDATION_FAILURE, diff --git a/drivers/src/dma.rs b/drivers/src/dma.rs index 6c15e8ef28..01a0026930 100644 --- a/drivers/src/dma.rs +++ b/drivers/src/dma.rs @@ -17,6 +17,7 @@ use caliptra_registers::axi_dma::{ enums::{RdRouteE, WrRouteE}, AxiDmaReg, }; +use core::{ops::Add, ptr::read_volatile}; use zerocopy::AsBytes; pub enum DmaReadTarget { @@ -39,6 +40,22 @@ impl From for AxiAddr { } } } +impl From for u64 { + fn from(addr: AxiAddr) -> Self { + (addr.hi as u64) << 32 | (addr.lo as u64) + } +} + +impl Add for AxiAddr { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + let self_u64: u64 = self.into(); + let rhs_u64: u64 = rhs.into(); + let sum = self_u64 + rhs_u64; + sum.into() + } +} pub struct DmaReadTransaction { pub read_addr: AxiAddr, @@ -161,16 +178,21 @@ impl Dma { let status = dma.status0().read(); if read_data.len() > status.fifo_depth() as usize { - return Err(CaliptraError::DRIVER_DMA_FIFO_UNDERRUN); + Err(CaliptraError::DRIVER_DMA_FIFO_UNDERRUN)?; } - read_data.chunks_mut(4).for_each(|word| { - let ptr = dma.read_data().ptr as *mut u8; - // Reg only exports u32 writes but we need finer grained access - unsafe { - ptr.copy_to_nonoverlapping(word.as_mut_ptr(), word.len()); - } - }); + // Only multiple of 4 bytes are allowed + if read_data.len() % core::mem::size_of::() != 0 { + Err(CaliptraError::DRIVER_DMA_FIFO_INVALID_SIZE)?; + } + + let read_data_ptr = dma.read_data().ptr as *const u8; + + // 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()); + } Ok(()) } @@ -182,16 +204,19 @@ impl Dma { let current_fifo_depth = dma.status0().read().fifo_depth(); if write_data.len() as u32 > max_fifo_depth - current_fifo_depth { - return Err(CaliptraError::DRIVER_DMA_FIFO_OVERRUN); + Err(CaliptraError::DRIVER_DMA_FIFO_OVERRUN)?; } - 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()); - } - }); + // Only multiple of 4 bytes are allowed + if write_data.len() % core::mem::size_of::() != 0 { + Err(CaliptraError::DRIVER_DMA_FIFO_INVALID_SIZE)?; + } + + // 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) }; + } Ok(()) } @@ -230,20 +255,34 @@ impl Dma { /// * `CaliptraResult` - Read value or error code pub fn read_dword(&mut self, read_addr: AxiAddr) -> CaliptraResult { let mut read_val: u32 = 0; + self.read_buffer(read_addr, read_val.as_bytes_mut())?; + Ok(read_val) + } + /// Read an arbitrary length buffer to fifo and read back the fifo into the provided buffer + /// + /// # Arguments + /// + /// * `read_addr` - Address to read from + /// * `buffer` - Target location to read to + /// + /// # Returns + /// + /// * CaliptraResult<()> - Success or failure + pub fn read_buffer(&mut self, read_addr: AxiAddr, buffer: &mut [u8]) -> CaliptraResult<()> { self.flush(); let read_transaction = DmaReadTransaction { read_addr, fixed_addr: false, - length: core::mem::size_of::() as u32, + length: buffer.len() as u32, target: DmaReadTarget::AhbFifo, }; self.setup_dma_read(read_transaction); self.do_transaction()?; - self.dma_read_fifo(read_val.as_bytes_mut())?; - Ok(read_val) + self.dma_read_fifo(buffer)?; + Ok(()) } /// Write a 32-bit word to the specified address diff --git a/drivers/test-fw/Cargo.toml b/drivers/test-fw/Cargo.toml index 6110444368..09d3c91085 100644 --- a/drivers/test-fw/Cargo.toml +++ b/drivers/test-fw/Cargo.toml @@ -174,11 +174,6 @@ name = "trng_driver_responder" path = "src/bin/trng_driver_responder.rs" required-features = ["riscv"] -[[bin]] -name = "axi_dma_tests" -path = "src/bin/dma_tests.rs" -required-features = ["riscv"] - [[bin]] name = "ml_dsa87" path = "src/bin/mldsa87_tests.rs" diff --git a/drivers/test-fw/src/bin/dma_tests.rs b/drivers/test-fw/src/bin/dma_tests.rs deleted file mode 100644 index 912d3e173e..0000000000 --- a/drivers/test-fw/src/bin/dma_tests.rs +++ /dev/null @@ -1,83 +0,0 @@ -/*++ - -Licensed under the Apache-2.0 license. - -File Name: - - dma_tests.rs - -Abstract: - - File contains test cases for DMA driver API - ---*/ - -#![no_std] -#![no_main] - -use caliptra_drivers::{memory_layout, AxiAddr, Dma, Mailbox}; -use caliptra_registers::{axi_dma::AxiDmaReg, ecc::EccReg, mbox::MboxCsr}; -use caliptra_test_harness::test_suite; -use core::slice; - -// We test that reading from a periph into the fifo works by reading ECC name -fn test_dma_read_from_periph() { - let mut dma = unsafe { Dma::new(AxiDmaReg::new()) }; - - let ecc_regs = unsafe { EccReg::new() }; - let ecc_name = ecc_regs.regs().name().ptr(); - - let dword = dma.read_dword(AxiAddr::from(ecc_name as u64)).unwrap(); - assert_eq!(dword.to_ne_bytes(), [0x70, 0x63, 0x65, 0x73]); // secp -} - -// We test that reading from a periph into the fifo works by reading ECC name -fn test_dma_write_to_periph() { - let mut dma = unsafe { Dma::new(AxiDmaReg::new()) }; - - let ecc_regs = unsafe { EccReg::new() }; - let ecc_iv = ecc_regs.regs().iv().at(0).ptr; - - let data: u32 = 0xdead_beef; - - dma.write_dword(AxiAddr::from(ecc_iv as u64), data).unwrap(); - let dword = dma.read_dword(AxiAddr::from(ecc_iv as u64)).unwrap(); - assert_eq!(dword, data); -} - -fn test_read_rri_to_mailbox() { - let mut dma = unsafe { Dma::new(AxiDmaReg::new()) }; - - let test_image = [0xab; 512]; - let block_size = 256; - - // TODO use i3c generated regs - let rri_regs = 0x1003_806c; - - // Get mailbox lock - let mut mbox_driver = unsafe { Mailbox::new(MboxCsr::new()) }; - let mut txn = mbox_driver.try_start_send_txn().unwrap(); - txn.send_request(0xdead_beef, b"").unwrap(); - - dma.transfer_payload_to_mbox( - AxiAddr::from(rri_regs as u64), - test_image.len() as u32, - true, - block_size, - ) - .unwrap(); - - let mbox_fifo = unsafe { - slice::from_raw_parts( - memory_layout::MBOX_ORG as *const u8, - memory_layout::MBOX_SIZE as usize, - ) - }; - assert_eq!(mbox_fifo.get(..test_image.len()).unwrap(), test_image); -} - -test_suite! { - test_dma_read_from_periph, - test_dma_write_to_periph, - test_read_rri_to_mailbox, -} diff --git a/drivers/tests/drivers_integration_tests/main.rs b/drivers/tests/drivers_integration_tests/main.rs index 25b1c21680..6d0a5b6b1f 100644 --- a/drivers/tests/drivers_integration_tests/main.rs +++ b/drivers/tests/drivers_integration_tests/main.rs @@ -1132,23 +1132,3 @@ fn test_uart() { fn test_mailbox_txn_drop() { run_driver_test(&firmware::driver_tests::MBOX_SEND_TXN_DROP); } - -#[test] -fn test_dma() { - let rom = caliptra_builder::build_firmware_rom(&firmware::driver_tests::DMA).unwrap(); - let recovery_image = &[0xab; 512]; - - let init_params = InitParams { - rom: &rom, - ..default_init_params() - }; - - let boot_params = BootParams { - ..Default::default() - }; - - let mut model = caliptra_hw_model::new_unbooted(init_params).unwrap(); - model.put_firmware_in_rri(recovery_image).unwrap(); - model.boot(boot_params).unwrap(); - model.step_until_exit_success().unwrap(); -} diff --git a/error/src/lib.rs b/error/src/lib.rs index c940727e51..707e8b81a1 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -376,6 +376,7 @@ impl CaliptraError { pub const DRIVER_DMA_TRANSACTION_ERROR: CaliptraError = CaliptraError::new_const(0x0000f001); 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); /// Runtime Errors pub const RUNTIME_INTERNAL: CaliptraError = CaliptraError::new_const(0x000E0001); diff --git a/hw-model/c-binding/examples/api/caliptra_api.c b/hw-model/c-binding/examples/api/caliptra_api.c index cd0aa51af4..d45ed7c314 100644 --- a/hw-model/c-binding/examples/api/caliptra_api.c +++ b/hw-model/c-binding/examples/api/caliptra_api.c @@ -31,7 +31,7 @@ int caliptra_init_fuses(struct caliptra_model *model, struct caliptra_fuses *fus caliptra_fuse_array_write(model, GENERIC_AND_FUSE_REG_FUSE_IDEVID_MANUF_HSM_ID_0, fuses->idevid_manuf_hsm_id, CALIPTRA_ARRAY_SIZE(fuses->idevid_manuf_hsm_id)); // Write to Caliptra Fuse Done - caliptra_model_axi_write_u32(model, EXTERNAL_PERIPH_BASE + CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_CPTRA_FUSE_WR_DONE, 1); + caliptra_model_apb_write_u32(model, EXTERNAL_PERIPH_BASE + CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_CPTRA_FUSE_WR_DONE, 1); // It shouldn`t be longer ready for fuses if (caliptra_model_ready_for_fuses(model)) @@ -48,7 +48,7 @@ int caliptra_bootfsm_go(struct caliptra_model *model) } // Write BOOTFSM_GO Register - caliptra_model_axi_write_u32(model, EXTERNAL_PERIPH_BASE + CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_CPTRA_BOOTFSM_GO, 1); + caliptra_model_apb_write_u32(model, EXTERNAL_PERIPH_BASE + CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_CPTRA_BOOTFSM_GO, 1); return 0; } diff --git a/hw-model/c-binding/examples/api/caliptra_fuses.h b/hw-model/c-binding/examples/api/caliptra_fuses.h index deca4a51f2..4c0669c769 100644 --- a/hw-model/c-binding/examples/api/caliptra_fuses.h +++ b/hw-model/c-binding/examples/api/caliptra_fuses.h @@ -19,7 +19,7 @@ extern "C" { static inline void caliptra_fuse_write(caliptra_model *model, uint32_t offset, uint32_t data) { - caliptra_model_axi_write_u32(model, EXTERNAL_PERIPH_BASE + (offset + CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_BASE_ADDR), data); + caliptra_model_apb_write_u32(model, EXTERNAL_PERIPH_BASE + (offset + CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_BASE_ADDR), data); } static inline void caliptra_fuse_array_write(caliptra_model *model, uint32_t offset, uint32_t *data, size_t size) diff --git a/hw-model/c-binding/examples/api/caliptra_mbox.h b/hw-model/c-binding/examples/api/caliptra_mbox.h index f84672db4c..0cd0f6e9a5 100644 --- a/hw-model/c-binding/examples/api/caliptra_mbox.h +++ b/hw-model/c-binding/examples/api/caliptra_mbox.h @@ -13,13 +13,13 @@ extern "C" { static inline void caliptra_mbox_write(caliptra_model *model, uint32_t offset, uint32_t data) { - caliptra_model_axi_write_u32(model, EXTERNAL_PERIPH_BASE + (offset + CALIPTRA_TOP_REG_MBOX_CSR_BASE_ADDR), data); + caliptra_model_apb_write_u32(model, EXTERNAL_PERIPH_BASE + (offset + CALIPTRA_TOP_REG_MBOX_CSR_BASE_ADDR), data); } static inline uint32_t caliptra_mbox_read(caliptra_model *model, uint32_t offset) { uint32_t data; - caliptra_model_axi_read_u32(model, EXTERNAL_PERIPH_BASE + (offset + CALIPTRA_TOP_REG_MBOX_CSR_BASE_ADDR), &data); + caliptra_model_apb_read_u32(model, EXTERNAL_PERIPH_BASE + (offset + CALIPTRA_TOP_REG_MBOX_CSR_BASE_ADDR), &data); return data; } diff --git a/hw-model/c-binding/src/caliptra_model.rs b/hw-model/c-binding/src/caliptra_model.rs index 531df88c6f..1e08bb9d49 100644 --- a/hw-model/c-binding/src/caliptra_model.rs +++ b/hw-model/c-binding/src/caliptra_model.rs @@ -72,7 +72,7 @@ pub unsafe extern "C" fn caliptra_model_destroy(model: *mut caliptra_model) { /// # Safety #[no_mangle] -pub unsafe extern "C" fn caliptra_model_axi_read_u32( +pub unsafe extern "C" fn caliptra_model_apb_read_u32( model: *mut caliptra_model, addr: c_uint, data: *mut c_uint, @@ -80,7 +80,7 @@ pub unsafe extern "C" fn caliptra_model_axi_read_u32( // Parameter check assert!(!model.is_null() || !data.is_null()); *data = (*{ model as *mut DefaultHwModel }) - .axi_bus() + .apb_bus() .read(RvSize::Word, addr) .unwrap(); @@ -89,7 +89,7 @@ pub unsafe extern "C" fn caliptra_model_axi_read_u32( /// # Safety #[no_mangle] -pub unsafe extern "C" fn caliptra_model_axi_write_u32( +pub unsafe extern "C" fn caliptra_model_apb_write_u32( model: *mut caliptra_model, addr: c_uint, data: c_uint, @@ -97,7 +97,7 @@ pub unsafe extern "C" fn caliptra_model_axi_write_u32( // Parameter check assert!(!model.is_null()); (*{ model as *mut DefaultHwModel }) - .axi_bus() + .apb_bus() .write(RvSize::Word, addr, data) .unwrap(); diff --git a/hw-model/src/bus_logger.rs b/hw-model/src/bus_logger.rs index e38077da5d..8cdcd4770a 100644 --- a/hw-model/src/bus_logger.rs +++ b/hw-model/src/bus_logger.rs @@ -130,7 +130,4 @@ impl Bus for BusLogger { fn update_reset(&mut self) { self.bus.update_reset(); } - fn handle_dma(&mut self) { - self.bus.handle_dma(); - } } diff --git a/hw-model/src/lib.rs b/hw-model/src/lib.rs index 7467662b42..1a6456b248 100644 --- a/hw-model/src/lib.rs +++ b/hw-model/src/lib.rs @@ -634,11 +634,11 @@ pub trait HwModel: SocManager { self.soc_ifc().cptra_bootfsm_go().write(|w| w.go(true)); } - /// The AXI bus from the SoC to Caliptra + /// The APB bus from the SoC to Caliptra /// /// WARNING: Reading or writing to this bus may involve the Caliptra /// microcontroller executing a few instructions - fn axi_bus(&mut self) -> Self::TBus<'_>; + fn apb_bus(&mut self) -> Self::TBus<'_>; /// Step execution ahead one clock cycle. fn step(&mut self); @@ -667,7 +667,7 @@ pub trait HwModel: SocManager { /// Returns true if the microcontroller has signalled that it is ready for /// firmware to be written to the mailbox. For RTL implementations, this - /// should come via a caliptra_top wire rather than an AXI register. + /// should come via a caliptra_top wire rather than an APB register. fn ready_for_fw(&self) -> bool; /// Initializes the fuse values and locks them in until the next reset. This @@ -1153,21 +1153,21 @@ mod tests { .write(|w| w.lock(true)); assert_eq!( - model.axi_bus().read(RvSize::Word, MBOX_ADDR_LOCK).unwrap(), + model.apb_bus().read(RvSize::Word, MBOX_ADDR_LOCK).unwrap(), 0 ); assert_eq!( - model.axi_bus().read(RvSize::Word, MBOX_ADDR_LOCK).unwrap(), + model.apb_bus().read(RvSize::Word, MBOX_ADDR_LOCK).unwrap(), 1 ); model - .axi_bus() + .apb_bus() .write(RvSize::Word, MBOX_ADDR_CMD, 4242) .unwrap(); assert_eq!( - model.axi_bus().read(RvSize::Word, MBOX_ADDR_CMD).unwrap(), + model.apb_bus().read(RvSize::Word, MBOX_ADDR_CMD).unwrap(), 4242 ); } diff --git a/hw-model/src/model_emulated.rs b/hw-model/src/model_emulated.rs index f94c22e4f4..fa36fbf567 100644 --- a/hw-model/src/model_emulated.rs +++ b/hw-model/src/model_emulated.rs @@ -30,11 +30,11 @@ use crate::TrngMode; use caliptra_emu_bus::{Bus, BusMmio}; use caliptra_api::soc_mgr::SocManager; -pub struct EmulatedAxiBus<'a> { +pub struct EmulatedApbBus<'a> { model: &'a mut ModelEmulated, } -impl<'a> Bus for EmulatedAxiBus<'a> { +impl<'a> Bus for EmulatedApbBus<'a> { fn read(&mut self, size: RvSize, addr: RvAddr) -> Result { let result = self.model.soc_to_caliptra_bus.read(size, addr); self.model.cpu.bus.log_read("SoC", size, addr, result); @@ -109,14 +109,14 @@ fn hash_slice(slice: &[u8]) -> u64 { } impl SocManager for ModelEmulated { - type TMmio<'a> = BusMmio>; + type TMmio<'a> = BusMmio>; fn delay(&mut self) { self.step(); } fn mmio_mut(&mut self) -> Self::TMmio<'_> { - BusMmio::new(self.axi_bus()) + BusMmio::new(self.apb_bus()) } const SOC_IFC_ADDR: u32 = 0x3003_0000; @@ -128,7 +128,7 @@ impl SocManager for ModelEmulated { } impl HwModel for ModelEmulated { - type TBus<'a> = EmulatedAxiBus<'a>; + type TBus<'a> = EmulatedApbBus<'a>; fn new_unbooted(params: InitParams) -> Result> where @@ -228,8 +228,8 @@ impl HwModel for ModelEmulated { fn ready_for_fw(&self) -> bool { self.ready_for_fw.get() } - fn axi_bus(&mut self) -> Self::TBus<'_> { - EmulatedAxiBus { model: self } + fn apb_bus(&mut self) -> Self::TBus<'_> { + EmulatedApbBus { model: self } } fn step(&mut self) { @@ -297,8 +297,9 @@ impl HwModel for ModelEmulated { self.step(); } + // [TODO][CAP2] Should it be statically provisioned? fn put_firmware_in_rri(&mut self, firmware: &[u8]) -> Result<(), ModelError> { - self.cpu.bus.bus.recovery.cms_data = Some(Rc::new(firmware.to_vec())); + self.cpu.bus.bus.dma.axi.recovery.cms_data = Some(Rc::new(firmware.to_vec())); Ok(()) } } diff --git a/hw-model/src/model_fpga_realtime.rs b/hw-model/src/model_fpga_realtime.rs index 54e3b02df0..d6eecf8a43 100644 --- a/hw-model/src/model_fpga_realtime.rs +++ b/hw-model/src/model_fpga_realtime.rs @@ -334,7 +334,7 @@ impl SocManager for ModelFpgaRealtime { type TMmio<'a> = BusMmio>; fn mmio_mut(&mut self) -> Self::TMmio<'_> { - BusMmio::new(self.axi_bus()) + BusMmio::new(self.apb_bus()) } fn delay(&mut self) { @@ -344,7 +344,7 @@ impl SocManager for ModelFpgaRealtime { impl HwModel for ModelFpgaRealtime { type TBus<'a> = FpgaRealtimeBus<'a>; - fn axi_bus(&mut self) -> Self::TBus<'_> { + fn apb_bus(&mut self) -> Self::TBus<'_> { FpgaRealtimeBus { mmio: self.mmio, phantom: Default::default(), diff --git a/hw-model/src/model_verilated.rs b/hw-model/src/model_verilated.rs index e09e7c4ec5..2890b31e4f 100644 --- a/hw-model/src/model_verilated.rs +++ b/hw-model/src/model_verilated.rs @@ -22,15 +22,15 @@ const DEFAULT_AXI_PAUSER: u32 = 0x1; // How many clock cycles before emitting a TRNG nibble const TRNG_DELAY: u32 = 4; -pub struct VerilatedAxiBus<'a> { +pub struct VerilatedApbBus<'a> { model: &'a mut ModelVerilated, } -impl<'a> Bus for VerilatedAxiBus<'a> { +impl<'a> Bus for VerilatedApbBus<'a> { fn read(&mut self, size: RvSize, addr: RvAddr) -> Result { if addr & 0x3 != 0 { return Err(caliptra_emu_bus::BusError::LoadAddrMisaligned); } - let result = Ok(self.model.v.axi_read_u32(self.model.soc_axi_pauser, addr)); + let result = Ok(self.model.v.apb_read_u32(self.model.soc_axi_pauser, addr)); self.model .log .borrow_mut() @@ -53,7 +53,7 @@ impl<'a> Bus for VerilatedAxiBus<'a> { } self.model .v - .axi_write_u32(self.model.soc_axi_pauser, addr, val); + .apb_write_u32(self.model.soc_axi_pauser, addr, val); self.model .log .borrow_mut() @@ -113,10 +113,10 @@ fn ahb_txn_size(ty: AhbTxnType) -> RvSize { } } impl SocManager for ModelVerilated { - type TMmio<'a> = BusMmio>; + type TMmio<'a> = BusMmio>; fn mmio_mut(&mut self) -> Self::TMmio<'_> { - BusMmio::new(self.axi_bus()) + BusMmio::new(self.apb_bus()) } fn delay(&mut self) { @@ -132,7 +132,7 @@ impl SocManager for ModelVerilated { } impl HwModel for ModelVerilated { - type TBus<'a> = VerilatedAxiBus<'a>; + type TBus<'a> = VerilatedApbBus<'a>; fn new_unbooted(params: crate::InitParams) -> Result> where @@ -271,8 +271,8 @@ impl HwModel for ModelVerilated { self.trng_mode } - fn axi_bus(&mut self) -> Self::TBus<'_> { - VerilatedAxiBus { model: self } + fn apb_bus(&mut self) -> Self::TBus<'_> { + VerilatedApbBus { model: self } } fn step(&mut self) { diff --git a/libcaliptra/examples/hwmodel/interface.c b/libcaliptra/examples/hwmodel/interface.c index c233d25b84..14706c6ee0 100644 --- a/libcaliptra/examples/hwmodel/interface.c +++ b/libcaliptra/examples/hwmodel/interface.c @@ -71,7 +71,7 @@ int caliptra_write_u32(uint32_t address, uint32_t data) { struct caliptra_model *m = hwmod_get_or_init(); - int result = caliptra_model_axi_write_u32(m, address, (int)data); + int result = caliptra_model_apb_write_u32(m, address, (int)data); caliptra_model_step(m); @@ -90,7 +90,7 @@ int caliptra_write_u32(uint32_t address, uint32_t data) */ int caliptra_read_u32(uint32_t address, uint32_t *data) { - return caliptra_model_axi_read_u32(hwmod_get_or_init(), address, (uint*)data); + return caliptra_model_apb_read_u32(hwmod_get_or_init(), address, (uint*)data); } /** diff --git a/rom/dev/Makefile b/rom/dev/Makefile index 361dffc212..7bdda1bed3 100644 --- a/rom/dev/Makefile +++ b/rom/dev/Makefile @@ -128,6 +128,19 @@ run: build-emu build-fw-image build-rom --recovery-image-fw $(TARGET_DIR)/caliptra-rom-test-fw \ --device-lifecycle unprovisioned \ +run-active: build-emu build-fw-image build-rom + cargo \ + "--config=$(EXTRA_CARGO_CONFIG)" \ + run \ + -p caliptra-emu \ + -- \ + --req-idevid-csr \ + --idevid-key-id-algo sha1 \ + --rom $(TARGET_DIR)/caliptra-rom.bin \ + --recovery-image-fw $(TARGET_DIR)/caliptra-rom-test-fw \ + --device-lifecycle unprovisioned \ + --active-mode \ + run-update: build-emu build-fw-image build-rom cargo \ "--config=$(EXTRA_CARGO_CONFIG)" \ diff --git a/sw-emulator/app/src/main.rs b/sw-emulator/app/src/main.rs index 8c2a0aa23d..0f02020868 100644 --- a/sw-emulator/app/src/main.rs +++ b/sw-emulator/app/src/main.rs @@ -39,6 +39,9 @@ use tock_registers::register_bitfields; /// Firmware Load Command Opcode const FW_LOAD_CMD_OPCODE: u32 = 0x4657_4C44; +/// Recovery register interface download Command Opcode +const RI_DOWNLOAD_FIRMWARE: u32 = 0x5249_4644; + /// The number of CPU clock cycles it takes to write the firmware to the mailbox. const FW_WRITE_TICKS: u64 = 1000; @@ -162,6 +165,11 @@ fn main() -> io::Result<()> { .value_parser(value_parser!(u64)) .default_value(&(EXPECTED_CALIPTRA_BOOT_TIME_IN_CYCLES.to_string())) ) + .arg( + arg!(--"active-mode" ... "Active mode: get image update via recovery register interface") + .required(false) + .action(ArgAction::SetTrue) + ) .get_matches(); let args_rom = args.get_one::("rom").unwrap(); @@ -267,6 +275,11 @@ fn main() -> io::Result<()> { }, ); + let active_mode = args.get_flag("active-mode"); + + // Clippy seems wrong about this clone not being necessary + #[allow(clippy::redundant_clone)] + let firmware_buffer = current_fw_buf.clone(); let bus_args = CaliptraRootBusArgs { rom: rom_buffer, log_dir: args_log_dir.clone(), @@ -275,12 +288,20 @@ fn main() -> io::Result<()> { 0xFF => exit(0x00), _ => print!("{}", val as char), }), - ready_for_fw_cb: ReadyForFwCb::new(move |args| { - let firmware_buffer = current_fw_buf.clone(); - args.schedule_later(FW_WRITE_TICKS, move |mailbox: &mut MailboxInternal| { - upload_fw_to_mailbox(mailbox, firmware_buffer); - }); - }), + ready_for_fw_cb: if active_mode { + ReadyForFwCb::new(move |args| { + args.schedule_later(FW_WRITE_TICKS, move |mailbox: &mut MailboxInternal| { + issue_ri_download_fw_mbox_cmd(mailbox) + }) + }) + } else { + ReadyForFwCb::new(move |args| { + let firmware_buffer = firmware_buffer.clone(); + args.schedule_later(FW_WRITE_TICKS, move |mailbox: &mut MailboxInternal| { + upload_fw_to_mailbox(mailbox, firmware_buffer) + }); + }) + }, security_state, upload_update_fw: UploadUpdateFwCb::new(move |mailbox: &mut MailboxInternal| { upload_fw_to_mailbox(mailbox, update_fw_buf.clone()); @@ -294,10 +315,16 @@ fn main() -> io::Result<()> { download_idev_id_csr(mailbox, log_dir.clone(), cptra_dbg_manuf_service_reg); }, ), + active_mode, ..Default::default() }; - let root_bus = CaliptraRootBus::new(&clock, bus_args); + let mut root_bus = CaliptraRootBus::new(&clock, bus_args); + // Populate the RRI data + if active_mode { + root_bus.dma.axi.recovery.cms_data = Some(current_fw_buf); + } + let soc_ifc = unsafe { caliptra_registers::soc_ifc::RegisterBlock::new_with_mmio( 0x3003_0000 as *mut u32, @@ -457,6 +484,22 @@ fn upload_fw_to_mailbox(mailbox: &mut MailboxInternal, firmware_buffer: Rc, diff --git a/sw-emulator/lib/bus/src/clock.rs b/sw-emulator/lib/bus/src/clock.rs index 6017a3839b..c05664e47e 100644 --- a/sw-emulator/lib/bus/src/clock.rs +++ b/sw-emulator/lib/bus/src/clock.rs @@ -180,7 +180,6 @@ impl Clock { bus.update_reset(); break; } - TimerAction::DmaAction => bus.handle_dma(), TimerAction::Nmi { .. } | TimerAction::SetNmiVec { .. } | TimerAction::ExtInt { .. } @@ -251,7 +250,6 @@ pub enum TimerAction { SetExtIntVec { addr: u32 }, SetGlobalIntEn { en: bool }, SetExtIntEn { en: bool }, - DmaAction, Halt, } diff --git a/sw-emulator/lib/derive/src/bus.rs b/sw-emulator/lib/derive/src/bus.rs index a664d09cfe..25ad9281a0 100644 --- a/sw-emulator/lib/derive/src/bus.rs +++ b/sw-emulator/lib/derive/src/bus.rs @@ -31,7 +31,6 @@ pub fn derive_bus(input: TokenStream) -> TokenStream { let poll_fn = get_poll_fn(&struct_attrs); let warm_reset_fn = get_warm_reset_fn(&struct_attrs); let update_reset_fn = get_update_reset_fn(&struct_attrs); - let handle_dma_fn = get_handle_dma_fn(&struct_attrs); let struct_name = expect_ident(&mut iter); let struct_fields = skip_to_group(&mut iter, Delimiter::Brace); let peripheral_fields = parse_peripheral_fields(struct_fields.stream()); @@ -69,12 +68,6 @@ pub fn derive_bus(input: TokenStream) -> TokenStream { } else { quote! {} }; - let self_handle_dma_tokens = if let Some(handle_dma_fn) = &handle_dma_fn { - let handle_dma_fn = Ident::new(handle_dma_fn, Span::call_site()); - quote! { Self::#handle_dma_fn(self); } - } else { - quote! {} - }; let field_idents: Vec<_> = peripheral_fields .iter() @@ -105,10 +98,6 @@ pub fn derive_bus(input: TokenStream) -> TokenStream { #(self.#field_idents.update_reset();)* #self_update_reset_tokens } - fn handle_dma(&mut self) { - #(self.#field_idents.handle_dma();)* - #self_handle_dma_tokens - } } } @@ -162,22 +151,6 @@ fn get_update_reset_fn(struct_attrs: &[Group]) -> Option { None } -fn get_handle_dma_fn(struct_attrs: &[Group]) -> Option { - for attr in struct_attrs { - let mut iter = attr.stream().into_iter(); - if let Some(TokenTree::Ident(ident)) = iter.next() { - if ident == "handle_dma_fn" { - if let Some(TokenTree::Group(group)) = iter.next() { - if let Some(TokenTree::Ident(ident)) = group.stream().into_iter().next() { - return Some(ident.to_string()); - } - } - } - } - } - None -} - #[derive(Clone, Debug)] struct RegisterField { // If this is None, read_fn and write_fn will be Some @@ -822,17 +795,6 @@ mod tests { self.i2c2.update_reset(); self.spi0.update_reset(); } - fn handle_dma(&mut self) { - self.rom.handle_dma(); - self.sram.handle_dma(); - self.dram.handle_dma(); - self.uart0.handle_dma(); - self.uart1.handle_dma(); - self.i2c0.handle_dma(); - self.i2c1.handle_dma(); - self.i2c2.handle_dma(); - self.spi0.handle_dma(); - } } }.to_string() ); @@ -859,8 +821,6 @@ mod tests { } fn update_reset(&mut self) { } - fn handle_dma(&mut self) { - } } }.to_string() ); diff --git a/sw-emulator/lib/derive/src/lib.rs b/sw-emulator/lib/derive/src/lib.rs index 90fc609c17..685b64818c 100644 --- a/sw-emulator/lib/derive/src/lib.rs +++ b/sw-emulator/lib/derive/src/lib.rs @@ -23,7 +23,6 @@ use proc_macro::TokenStream; poll_fn, warm_reset_fn, update_reset_fn, - handle_dma_fn, register, register_array ) diff --git a/sw-emulator/lib/derive/tests/derive_bus_test.rs b/sw-emulator/lib/derive/tests/derive_bus_test.rs index ee6dd3c168..a2ea31ed56 100644 --- a/sw-emulator/lib/derive/tests/derive_bus_test.rs +++ b/sw-emulator/lib/derive/tests/derive_bus_test.rs @@ -23,7 +23,6 @@ impl From for RvData { #[derive(Bus)] #[poll_fn(poll)] -#[handle_dma_fn(handle_dma)] #[allow(clippy::manual_non_exhaustive)] struct MyBus { pub log: Log, @@ -162,10 +161,6 @@ impl MyBus { fn poll(&self) { write!(self.log.w(), "poll; ").unwrap(); } - - fn handle_dma(&self) { - write!(self.log.w(), "dma; ").unwrap(); - } } #[test] @@ -553,7 +548,4 @@ fn test_poll_and_dma() { Bus::poll(&mut bus); assert_eq!(bus.log.take(), "poll; "); assert_eq!(bus.fake.log.take(), "poll()\n"); - - Bus::handle_dma(&mut bus); - assert_eq!(bus.log.take(), "dma; "); } diff --git a/sw-emulator/lib/periph/src/dma.rs b/sw-emulator/lib/periph/src/dma.rs index 9e7d1620a2..bfb8de1696 100644 --- a/sw-emulator/lib/periph/src/dma.rs +++ b/sw-emulator/lib/periph/src/dma.rs @@ -12,48 +12,21 @@ File contains DMA peripheral implementation. --*/ +use crate::MailboxRam; use caliptra_emu_bus::{ - Bus, BusError, Clock, ReadOnlyRegister, ReadWriteRegister, Timer, TimerAction, + ActionHandle, Bus, BusError, Clock, ReadOnlyRegister, ReadWriteRegister, Timer, WriteOnlyRegister, }; use caliptra_emu_derive::Bus; use caliptra_emu_types::{RvAddr, RvData, RvSize}; -use std::cell::RefCell; +use std::borrow::BorrowMut; use std::collections::VecDeque; -use std::rc::Rc; use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::register_bitfields; -use crate::CaliptraRootBus; - -#[derive(Clone)] -pub struct Dma { - regs: Rc>, -} - -impl Dma { - pub fn new(clock: &Clock) -> Self { - Self { - regs: Rc::new(RefCell::new(DmaRegs::new(clock))), - } - } - - pub fn do_dma_handling(&mut self, root_bus: &mut CaliptraRootBus) { - self.regs.borrow_mut().do_dma_handling(root_bus) - } -} - -impl Bus for Dma { - /// Read data of specified size from given address - fn read(&mut self, size: RvSize, addr: RvAddr) -> Result { - self.regs.borrow_mut().read(size, addr) - } - - /// Write data of specified size to given address - fn write(&mut self, size: RvSize, addr: RvAddr, val: RvData) -> Result<(), BusError> { - self.regs.borrow_mut().write(size, addr, val) - } -} +pub mod axi_root_bus; +use axi_root_bus::{AxiAddr, AxiRootBus}; +mod recovery; register_bitfields! [ u32, @@ -78,7 +51,7 @@ register_bitfields! [ DISABLE = 0b00, MAILBOX = 0b01, AHB_FIFO = 0b10, - AXI_WR = 0b11, + AXI_RD = 0b11, ], WRITE_ADDR_FIXED OFFSET(28) NUMBITS(1) [], ], @@ -103,7 +76,8 @@ register_bitfields! [ ]; #[derive(Bus)] -pub struct DmaRegs { +#[poll_fn(poll)] +pub struct Dma { /// ID #[register(offset = 0x0000_0000)] name: ReadOnlyRegister, @@ -117,7 +91,7 @@ pub struct DmaRegs { control: ReadWriteRegister, /// Status 0 - #[register(offset = 0x0000_000c)] + #[register(offset = 0x0000_000c, read_fn = on_read_status0)] status0: ReadOnlyRegister, /// Status 1: Reports remaining byte count that must be sent to destination. @@ -160,21 +134,42 @@ pub struct DmaRegs { /// Timer timer: Timer, + /// Operation complete callback + op_complete_action: Option, + /// FIFO fifo: VecDeque, + + /// Axi Bus + pub axi: AxiRootBus, + + /// Mailbox + mailbox: MailboxRam, } -impl DmaRegs { - const NAME: u32 = 0x6776_8068; // CLPD +struct ReadXfer { + pub src: AxiAddr, + pub fixed: bool, + pub len: usize, +} - const RRI_BASE: u32 = 0x1003_8000; // TODO - const RRI_FIFO_OFFSET: u32 = 0x6c; +struct WriteXfer { + pub dest: AxiAddr, + pub fixed: bool, + pub len: usize, +} + +impl Dma { + const NAME: u32 = 0x6776_8068; // CLPD const FIFO_SIZE: usize = 0x1000; + // [TODO][CAP2] DMA transactions need to be a multiple of this + const AXI_DATA_WIDTH: usize = 4; + const DMA_CLOCKS_PER_WORD: u64 = 4; - pub fn new(clock: &Clock) -> Self { + pub fn new(clock: &Clock, mailbox: MailboxRam) -> Self { Self { name: ReadOnlyRegister::new(Self::NAME), capabilities: ReadOnlyRegister::new(Self::FIFO_SIZE as u32 - 1), // MAX FIFO DEPTH @@ -190,7 +185,10 @@ impl DmaRegs { write_data: WriteOnlyRegister::new(0), read_data: ReadOnlyRegister::new(0), timer: Timer::new(clock), + op_complete_action: None, fifo: VecDeque::with_capacity(Self::FIFO_SIZE), + axi: AxiRootBus::new(), + mailbox, } } @@ -217,9 +215,9 @@ impl DmaRegs { todo!(); } - self.timer.schedule_action_in( - Self::DMA_CLOCKS_PER_WORD * self.byte_count.reg.get() as u64, - TimerAction::DmaAction, + self.op_complete_action = Some( + self.timer + .schedule_poll_in(Self::DMA_CLOCKS_PER_WORD * self.byte_count.reg.get() as u64), ); self.status0 .reg @@ -229,6 +227,18 @@ impl DmaRegs { Ok(()) } + fn on_read_status0(&mut self, size: RvSize) -> Result { + if size != RvSize::Word { + Err(BusError::LoadAccessFault)? + } + + let status0 = ReadWriteRegister::new(self.status0.reg.get()); + status0 + .reg + .modify(Status0::FIFO_DEPTH.val(self.fifo.len() as u32)); + Ok(status0.reg.get()) + } + pub fn on_write_data(&mut self, size: RvSize, val: RvData) -> Result<(), BusError> { let bytes = &val.to_le_bytes(); bytes[..size as usize] @@ -253,133 +263,237 @@ impl DmaRegs { Ok(bytes) } - fn write_to_mailbox(&mut self, data: Vec, root_bus: &mut CaliptraRootBus) { - let mailbox = &mut root_bus.mailbox; - let mailbox_regs = &mut mailbox.mailbox_regs(); - let mut mailbox_regs = mailbox_regs.borrow_mut(); - // TODO does the CMD matter? - mailbox_regs.write_cmd(RvSize::Word, 0xdeadbeef).unwrap(); - mailbox_regs - .write_dlen(RvSize::Word, self.byte_count.reg.get()) - .unwrap(); + fn read_xfer(&self) -> ReadXfer { + assert!(self.byte_count.reg.get() % Self::AXI_DATA_WIDTH as u32 == 0); + ReadXfer { + src: ((self.src_addr_h.reg.get() as u64) << 32) | self.src_addr_l.reg.get() as u64, + fixed: self.control.reg.is_set(Control::READ_ADDR_FIXED), + len: self.byte_count.reg.get() as usize, + } + } + + fn write_xfer(&self) -> WriteXfer { + assert!(self.byte_count.reg.get() % Self::AXI_DATA_WIDTH as u32 == 0); + WriteXfer { + dest: ((self.dest_addr_h.reg.get() as u64) << 32) | self.dest_addr_l.reg.get() as u64, + fixed: self.control.reg.is_set(Control::WRITE_ADDR_FIXED), + len: self.byte_count.reg.get() as usize, + } + } - assert_eq!(data.len(), self.byte_count.reg.get() as usize); + fn axi_to_mailbox(&mut self) { + let xfer = self.read_xfer(); + let mbox_ram = self.mailbox.borrow_mut(); - data.chunks(RvSize::Word as usize).for_each(|c| { - mailbox_regs - .write_din(RvSize::Word, u32::from_le_bytes(c.try_into().unwrap())) - .unwrap() - }); + for i in (0..xfer.len).step_by(Self::AXI_DATA_WIDTH) { + let addr = xfer.src + if xfer.fixed { 0 } else { i as AxiAddr }; + let data = self.axi.read(Self::AXI_DATA_WIDTH.into(), addr).unwrap(); + mbox_ram + .write(Self::AXI_DATA_WIDTH.into(), i as RvAddr, data as RvData) + .unwrap(); + } } - pub fn do_dma_handling(&mut self, root_bus: &mut CaliptraRootBus) { - // DMA reads - let read_addr_fixed = self.control.reg.is_set(Control::READ_ADDR_FIXED); - let read_addr = self.src_addr_l.reg.get(); - assert_eq!(self.src_addr_h.reg.get(), 0); // 32bit - let read_data = - // Special case for putting stuff image in the mailbox from recovery register interface - if read_addr == Self::RRI_BASE + Self::RRI_FIFO_OFFSET && read_addr_fixed { - if let Some(data) = root_bus.recovery.cms_data.clone() { - (*data).clone() - } else { - vec![] - } - } else { - let range = read_addr..read_addr + self.byte_count.reg.get(); - range - .step_by(RvSize::Word as usize) - .flat_map(|offset| { - let read_offset = if read_addr_fixed { read_addr } else { offset }; - root_bus - .read(RvSize::Word, read_offset) - .unwrap() - .to_le_bytes() - }).collect() - }; - match self.control.reg.read_as_enum(Control::READ_ROUTE) { - Some(Control::READ_ROUTE::Value::MAILBOX) => { - self.write_to_mailbox(read_data, root_bus); + fn axi_to_fifo(&mut self) { + let xfer = self.read_xfer(); + + for i in (0..xfer.len).step_by(Self::AXI_DATA_WIDTH) { + let addr = xfer.src + if xfer.fixed { 0 } else { i as AxiAddr }; + let cur_fifo_depth = self.status0.reg.read(Status0::FIFO_DEPTH); + if cur_fifo_depth + 4 >= Self::FIFO_SIZE as u32 { + self.status0.reg.write(Status0::ERROR::SET); + // TODO set interrupt bits + return; } - Some(Control::READ_ROUTE::Value::AHB_FIFO) => { - if self.fifo.len() + read_data.len() > Self::FIFO_SIZE { - self.status0 - .reg - .write(Status0::DMA_FSM_PRESENT_STATE::ERROR); - read_data[..Self::FIFO_SIZE - self.fifo.len()] - .iter() - .for_each(|b| self.fifo.push_back(*b)); - } else { - read_data.iter().for_each(|b| self.fifo.push_back(*b)); + let data = self.axi.read(Self::AXI_DATA_WIDTH.into(), addr).unwrap(); + let data_bytes = data.to_le_bytes(); + data_bytes[..Self::AXI_DATA_WIDTH] + .iter() + .for_each(|b| self.fifo.push_back(*b)); + } + } + + fn axi_to_axi(&mut self) { + let read_xfer = self.read_xfer(); + let write_xfer = self.write_xfer(); + + for i in (0..read_xfer.len).step_by(Self::AXI_DATA_WIDTH) { + let src = read_xfer.src + if read_xfer.fixed { 0 } else { i as AxiAddr }; + let dest = write_xfer.dest + if write_xfer.fixed { 0 } else { i as AxiAddr }; + let data = self.axi.read(Self::AXI_DATA_WIDTH.into(), src).unwrap(); + self.axi + .write(Self::AXI_DATA_WIDTH.into(), dest, data) + .unwrap(); + } + } + + fn mailbox_to_axi(&mut self) { + let xfer = self.write_xfer(); + let mbox_ram = self.mailbox.borrow_mut(); + + for i in (0..xfer.len).step_by(Self::AXI_DATA_WIDTH) { + let addr = xfer.dest + if xfer.fixed { 0 } else { i as AxiAddr }; + let data = mbox_ram + .read(Self::AXI_DATA_WIDTH.into(), i as RvAddr) + .unwrap(); + self.axi + .write(Self::AXI_DATA_WIDTH.into(), addr, data) + .unwrap(); + } + } + + fn fifo_to_axi(&mut self) { + let xfer = self.write_xfer(); + for i in (0..xfer.len).step_by(Self::AXI_DATA_WIDTH) { + let addr = xfer.dest + if xfer.fixed { 0 } else { i as AxiAddr }; + let data = { + let mut bytes = [0u8; Self::AXI_DATA_WIDTH]; + for byte in bytes.iter_mut() { + match self.fifo.pop_front() { + Some(b) => { + *byte = b; + } + None => { + self.status0.reg.write(Status0::ERROR::SET); + // TODO set interrupt bits + return; + } + } } - self.status0 - .reg - .modify(Status0::FIFO_DEPTH.val(self.fifo.len() as u32)); - } - Some(Control::READ_ROUTE::Value::AXI_WR) => { - todo!() - } - _ => {} + u32::from_le_bytes(bytes) + }; + self.axi + .write(Self::AXI_DATA_WIDTH.into(), addr, data) + .unwrap(); } + } - // DMA writes - let write_addr_fixed = self.control.reg.is_set(Control::WRITE_ADDR_FIXED); - let write_addr = self.dest_addr_l.reg.get(); - assert_eq!(self.dest_addr_h.reg.get(), 0); // 32bit - match self.control.reg.read_as_enum(Control::WRITE_ROUTE) { - Some(Control::WRITE_ROUTE::Value::MAILBOX) => todo!(), - Some(Control::WRITE_ROUTE::Value::AHB_FIFO) => { - let to_send = self - .fifo - .drain(0..self.byte_count.reg.get() as usize) - .collect::>(); - - to_send.chunks(4).enumerate().for_each(|(i, b)| { - let word = u32::from_le_bytes(b.try_into().unwrap()); - let addr = if write_addr_fixed { - 0 - } else { - write_addr + 4 * i as u32 - }; - root_bus - .write(RvSize::Word, addr as RvAddr, word as RvData) - .unwrap(); - }); - } - Some(Control::WRITE_ROUTE::Value::AXI_WR) => todo!(), + fn op_complete(&mut self) { + let read_target = self.control.reg.read_as_enum(Control::READ_ROUTE).unwrap(); + let write_origin = self.control.reg.read_as_enum(Control::WRITE_ROUTE).unwrap(); - _ => {} + match (read_target, write_origin) { + (Control::READ_ROUTE::Value::MAILBOX, Control::WRITE_ROUTE::Value::DISABLE) => { + self.axi_to_mailbox() + } + (Control::READ_ROUTE::Value::AHB_FIFO, Control::WRITE_ROUTE::Value::DISABLE) => { + self.axi_to_fifo() + } + (Control::READ_ROUTE::Value::AXI_WR, Control::WRITE_ROUTE::Value::AXI_RD) => { + self.axi_to_axi() + } + (Control::READ_ROUTE::Value::DISABLE, Control::WRITE_ROUTE::Value::MAILBOX) => { + self.mailbox_to_axi() + } + (Control::READ_ROUTE::Value::DISABLE, Control::WRITE_ROUTE::Value::AHB_FIFO) => { + self.fifo_to_axi() + } + (_, _) => panic!("Invalid read/write DMA combination"), } self.status0 .reg .modify(Status0::BUSY::CLEAR + Status0::DMA_FSM_PRESENT_STATE::DONE); } + + fn poll(&mut self) { + if self.timer.fired(&mut self.op_complete_action) { + self.op_complete(); + } + } } #[cfg(test)] mod tests { + use tock_registers::registers::InMemoryRegister; + use super::*; - const NAME_OFFSET: u32 = 0; - const NAME_VAL: u32 = 0x6776_8068; - const CAPABILITIES_OFFSET: u32 = 4; + const AXI_TEST_OFFSET: AxiAddr = 0xaa00; - #[test] - fn test_name() { - let clock = Clock::new(); - let mut dma = Dma::new(&clock); + const CTRL_OFFSET: RvAddr = 0x8; + const STATUS0_OFFSET: RvAddr = 0xc; + const SRC_ADDR_L_OFFSET: RvAddr = 0x14; + const SRC_ADDR_H_OFFSET: RvAddr = 0x18; + const DST_ADDR_L_OFFSET: RvAddr = 0x1c; + const DST_ADDR_H_OFFSET: RvAddr = 0x20; + const BYTE_COUNT_OFFSET: RvAddr = 0x24; + const WRITE_DATA_OFFSET: RvAddr = 0x2c; + const READ_DATA_OFFSET: RvAddr = 0x30; - let name = dma.read(RvSize::Word, NAME_OFFSET).unwrap(); - assert_eq!(name, NAME_VAL); + fn dma_read_u32(dma: &mut Dma, clock: &Clock, addr: AxiAddr) -> u32 { + let ctrl = InMemoryRegister::::new(0); + ctrl.modify(Control::FLUSH::SET); + dma.write(RvSize::Word, CTRL_OFFSET, ctrl.get()).unwrap(); + + let ctrl = InMemoryRegister::::new(0); + ctrl.modify(Control::READ_ROUTE::AHB_FIFO); + dma.write(RvSize::Word, CTRL_OFFSET, ctrl.get()).unwrap(); + + dma.write(RvSize::Word, SRC_ADDR_L_OFFSET, addr as RvAddr) + .unwrap(); + dma.write(RvSize::Word, SRC_ADDR_H_OFFSET, (addr >> 32) as RvAddr) + .unwrap(); + + dma.write(RvSize::Word, BYTE_COUNT_OFFSET, Dma::AXI_DATA_WIDTH as u32) + .unwrap(); + + // Launch transaction + ctrl.modify(Control::GO::SET); + dma.write(RvSize::Word, CTRL_OFFSET, ctrl.get()).unwrap(); + + while { + let status0 = dma.read(RvSize::Word, STATUS0_OFFSET).unwrap(); + let status0 = InMemoryRegister::::new(status0); + status0.is_set(Status0::BUSY) + } { + clock.increment_and_process_timer_actions(1, dma); + } + + dma.read(RvSize::Word, READ_DATA_OFFSET).unwrap() + } + + fn dma_write_u32(dma: &mut Dma, clock: &Clock, addr: AxiAddr, data: RvData) { + let ctrl = InMemoryRegister::::new(0); + ctrl.modify(Control::FLUSH::SET); + dma.write(RvSize::Word, CTRL_OFFSET, ctrl.get()).unwrap(); + + dma.write(RvSize::Word, WRITE_DATA_OFFSET, data).unwrap(); + + dma.write(RvSize::Word, DST_ADDR_L_OFFSET, addr as RvAddr) + .unwrap(); + dma.write(RvSize::Word, DST_ADDR_H_OFFSET, (addr >> 32) as RvAddr) + .unwrap(); + + let ctrl = InMemoryRegister::::new(0); + ctrl.modify(Control::WRITE_ROUTE::AHB_FIFO); + dma.write(RvSize::Word, CTRL_OFFSET, ctrl.get()).unwrap(); + + dma.write(RvSize::Word, BYTE_COUNT_OFFSET, Dma::AXI_DATA_WIDTH as u32) + .unwrap(); + + // Launch transaction + ctrl.modify(Control::GO::SET); + dma.write(RvSize::Word, CTRL_OFFSET, ctrl.get()).unwrap(); + + while { + let status0 = dma.read(RvSize::Word, STATUS0_OFFSET).unwrap(); + let status0 = InMemoryRegister::::new(status0); + status0.is_set(Status0::BUSY) + } { + clock.increment_and_process_timer_actions(1, dma); + } } #[test] - fn test_capabilities() { + fn test_dma_fifo_read_write() { let clock = Clock::new(); - let mut dma = Dma::new(&clock); + let mbox_ram = MailboxRam::new(); + let mut dma = Dma::new(&clock, mbox_ram); - let capabilities = dma.read(RvSize::Word, CAPABILITIES_OFFSET).unwrap(); - assert_eq!(capabilities, 0xfff); + assert_eq!(dma_read_u32(&mut dma, &clock, AXI_TEST_OFFSET), 0xaabbccdd); // Initial test value + let test_value = 0xdeadbeef; + dma_write_u32(&mut dma, &clock, AXI_TEST_OFFSET, test_value); + assert_eq!(dma_read_u32(&mut dma, &clock, AXI_TEST_OFFSET), test_value); } } diff --git a/sw-emulator/lib/periph/src/dma/axi_root_bus.rs b/sw-emulator/lib/periph/src/dma/axi_root_bus.rs new file mode 100644 index 0000000000..ad82e3cc98 --- /dev/null +++ b/sw-emulator/lib/periph/src/dma/axi_root_bus.rs @@ -0,0 +1,73 @@ +/*++ + +Licensed under the Apache-2.0 license. + +File Name: + + axi_root_bus.rs + +Abstract: + + File contains the axi root bus peripheral. + +--*/ + +use caliptra_emu_bus::{ + Bus, BusError, BusError::LoadAccessFault, BusError::StoreAccessFault, Register, +}; +use caliptra_emu_types::{RvAddr, RvData, RvSize}; + +pub type AxiAddr = u64; + +use crate::dma::recovery::RecoveryRegisterInterface; + +pub struct AxiRootBus { + pub reg: u32, + + pub recovery: RecoveryRegisterInterface, +} + +impl Default for AxiRootBus { + fn default() -> Self { + Self::new() + } +} + +impl AxiRootBus { + const TEST_REG_OFFSET: AxiAddr = 0xaa00; + pub const RECOVERY_REGISTER_INTERFACE_OFFSET: AxiAddr = 0xf_00000000; + pub const RECOVERY_REGISTER_INTERFACE_END: AxiAddr = 0xf_000000ff; + + pub fn new() -> Self { + Self { + reg: 0xaabbccdd, + recovery: RecoveryRegisterInterface::new(), + } + } + + pub fn read(&mut self, size: RvSize, addr: AxiAddr) -> Result { + match addr { + Self::TEST_REG_OFFSET => return Register::read(&self.reg, size), + Self::RECOVERY_REGISTER_INTERFACE_OFFSET..=Self::RECOVERY_REGISTER_INTERFACE_END => { + let addr = (addr - Self::RECOVERY_REGISTER_INTERFACE_OFFSET) as RvAddr; + return Bus::read(&mut self.recovery, size, addr); + } + _ => {} + }; + + Err(LoadAccessFault) + } + + pub fn write(&mut self, size: RvSize, addr: AxiAddr, val: RvData) -> Result<(), BusError> { + match addr { + Self::TEST_REG_OFFSET => return Register::write(&mut self.reg, size, val), + Self::RECOVERY_REGISTER_INTERFACE_OFFSET..=Self::RECOVERY_REGISTER_INTERFACE_END => { + let addr = (addr - Self::RECOVERY_REGISTER_INTERFACE_OFFSET) as RvAddr; + return Bus::write(&mut self.recovery, size, addr, val); + } + _ => {} + } + + Err(StoreAccessFault) + } +} diff --git a/sw-emulator/lib/periph/src/dma/recovery.rs b/sw-emulator/lib/periph/src/dma/recovery.rs new file mode 100644 index 0000000000..da71b107de --- /dev/null +++ b/sw-emulator/lib/periph/src/dma/recovery.rs @@ -0,0 +1,333 @@ +/*++ + +Licensed under the Apache-2.0 license. + +File Name: + + recovery.rs + +Abstract: + + File contains the recovery register interface peripheral + +--*/ + +use caliptra_emu_bus::{BusError, ReadOnlyRegister, ReadWriteRegister}; +use caliptra_emu_derive::Bus; +use caliptra_emu_types::{RvData, RvSize}; +use std::mem; +use std::rc::Rc; +use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; +use tock_registers::register_bitfields; + +register_bitfields! [ + u32, + + /// Recovery Control + RecoveryControl [ + CMS OFFSET(0) NUMBITS(8) [], + IMAGE_SELECTION OFFSET(8) NUMBITS(8) [ + NoOperation = 0, + RecoveryFromCms = 1, + RecoveryStoredOnDevice = 2, + // Other bits are reserved + ], + ACTIVATE_RECOVERY_IMAGE OFFSET(16) NUMBITS(8) [ + DoNotActivate = 0, + Activate = 0xf, + ], + ], + + /// Recovery Status + RecoveryStatus [ + DEVICE_RECOVERY OFFSET(0) NUMBITS(8) [ + NotInRecovery = 0x0, + AwaitingRecoveryImage = 0x1, + BootingRecoveryImage = 0x2, + RecoverySuccesfull = 0x3, + RecoveryFailed = 0xc, + AuthenticationError = 0xd, + ErrorEnteringRecovery = 0xe, + InvalidComponentAddressSpace = 0xf, + // 0x10-ff Reserved + ], + VENDOR_SPECIFIC OFFSET(8) NUMBITS(8) [], + ], + + /// HW Status + HwStatus [ + HW_STATUS OFFSET(0) NUMBITS(8) [ + TemperatureCritial = 0x0, + HardwareSoftError = 0x1, + HardwareFatalError = 0x2, + ], + VENDOR_HW_STATUS OFFSET(8) NUMBITS(8) [], + // 0x00-0x7e: 0 to 126 C + // 0x7f: 127 C or higher + // 0x80: no temperature data, or data is older than 5 seconds + // 0x81: temperature sensor failure + // 0x82-0x83: reserved + // 0xc4: -60 C or lower + // 0xc5-0xff: -1 to -59 C (in twoʼs complement) + COMPOSITE_TEMP OFFSET(16) NUMBITS(8) [], + ], + + /// Indirect FIFO Control + IndirectCtrl [ + CMS OFFSET(0) NUMBITS(8), + RESET OFFSET(8) NUMBITS(1), + ], + + /// Indirect FIFO Status + IndirectStatus [ + FIFO_EMPTY OFFSET(0) NUMBITS(1) [], + FIFO_FULL OFFSET(1) NUMBITS(1) [], + REGION_TYPE OFFSET(8) NUMBITS(3) [ + CodeSpaceRecovery = 0, // Write Only + LogWithDebugFormat = 1, // Read Only + VendorDefinedRegionWriteOnly = 4, + VendorDefinedRegionReadOnly = 5, + UnsupportedRegion = 7, + ], + ], +]; + +/// Recovery register interface +#[derive(Bus)] +pub struct RecoveryRegisterInterface { + // Capability registers + #[register(offset = 0x0)] + pub extcap_header: ReadOnlyRegister, + #[register(offset = 0x4)] + pub prot_cap_0: ReadWriteRegister, + #[register(offset = 0x8)] + pub prot_cap_1: ReadWriteRegister, + #[register(offset = 0xc)] + pub prot_cap_2: ReadWriteRegister, + #[register(offset = 0x10)] + pub prot_cap_3: ReadWriteRegister, + + // Device ID registers + #[register(offset = 0x14)] + pub device_id_0: ReadWriteRegister, + #[register(offset = 0x18)] + pub device_id_1: ReadWriteRegister, + #[register(offset = 0x1c)] + pub device_id_2: ReadWriteRegister, + #[register(offset = 0x20)] + pub device_id_3: ReadWriteRegister, + #[register(offset = 0x24)] + pub device_id_4: ReadWriteRegister, + #[register(offset = 0x28)] + pub device_id_5: ReadWriteRegister, + #[register(offset = 0x2c)] + pub device_id_6: ReadWriteRegister, + + // Status and control registers + #[register(offset = 0x30)] + pub device_status_0: ReadWriteRegister, + #[register(offset = 0x34)] + pub device_status_1: ReadWriteRegister, + #[register(offset = 0x38)] + pub device_reset: ReadWriteRegister, + #[register(offset = 0x3c)] + pub recovery_ctrl: ReadWriteRegister, + #[register(offset = 0x40)] + pub recovery_status: ReadWriteRegister, + #[register(offset = 0x44)] + pub hw_status: ReadWriteRegister, + + // Indirect FIFO registers + #[register(offset = 0x48, write_fn = indirect_fifo_ctrl_0_write)] + pub indirect_fifo_ctrl_0: ReadWriteRegister, + #[register(offset = 0x4c, read_fn = indirect_fifo_ctrl_1_read)] + pub indirect_fifo_ctrl_1: ReadWriteRegister, + #[register(offset = 0x50)] + pub indirect_fifo_status_0: ReadOnlyRegister, + #[register(offset = 0x54)] + pub indirect_fifo_status_1: ReadOnlyRegister, // Write index + #[register(offset = 0x58)] + pub indirect_fifo_status_2: ReadOnlyRegister, // Read index + #[register(offset = 0x5c)] + pub indirect_fifo_status_3: ReadWriteRegister, // FIFO size + #[register(offset = 0x60)] + pub indirect_fifo_status_4: ReadWriteRegister, // Max transfer size + #[register(offset = 0x64)] + pub indirect_fifo_status_5: ReadWriteRegister, + #[register(offset = 0x68, read_fn = indirect_fifo_data_read)] + pub indirect_fifo_data: ReadWriteRegister, + + pub cms_data: Option>>, // TODO Multiple images? +} + +impl RecoveryRegisterInterface { + pub fn new() -> Self { + 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), + + // Device ID registers + device_id_0: ReadWriteRegister::new(0), + device_id_1: ReadWriteRegister::new(0), + device_id_2: ReadWriteRegister::new(0), + device_id_3: ReadWriteRegister::new(0), + device_id_4: ReadWriteRegister::new(0), + device_id_5: ReadWriteRegister::new(0), + device_id_6: ReadWriteRegister::new(0), + + // Status and control registers + device_status_0: ReadWriteRegister::new(0), + device_status_1: ReadWriteRegister::new(0), + device_reset: ReadWriteRegister::new(0), + recovery_ctrl: ReadWriteRegister::new(0), + recovery_status: ReadWriteRegister::new(0), + hw_status: ReadWriteRegister::new(0), + + // Indirect FIFO registers + indirect_fifo_ctrl_0: ReadWriteRegister::new(0), + indirect_fifo_ctrl_1: ReadWriteRegister::new(0), + indirect_fifo_status_0: ReadOnlyRegister::new(0x1), // EMPTY=1 + indirect_fifo_status_1: ReadOnlyRegister::new(0), + indirect_fifo_status_2: ReadOnlyRegister::new(0), + indirect_fifo_status_3: ReadWriteRegister::new(0), + indirect_fifo_status_4: ReadWriteRegister::new(0), + indirect_fifo_status_5: ReadWriteRegister::new(0), + indirect_fifo_data: ReadWriteRegister::new(0), + + cms_data: None, + } + } + + pub fn indirect_fifo_data_read(&mut self, size: RvSize) -> Result { + if size != RvSize::Word { + Err(BusError::LoadAccessFault)?; + } + let image = match &self.cms_data { + None => { + println!("No image set in RRI"); + return Ok(0xffff_ffff); + } + Some(x) => x, + }; + + let cms = self.indirect_fifo_ctrl_0.reg.read(IndirectCtrl::CMS); + if cms != 0 { + println!("CMS {cms} not supported"); + return Ok(0xffff_ffff); + } + + let read_index = self.indirect_fifo_status_2.reg.get(); + let address = read_index * 4; + let image_len = image.len().try_into().unwrap(); + if address >= image_len { + return Ok(0xffff_ffff); + }; + if address >= image_len - 4 { + self.indirect_fifo_status_0 + .reg + .modify(IndirectStatus::FIFO_EMPTY::SET); + } + + let address: usize = address.try_into().unwrap(); + let range = address..(address + 4); + let data = &image[range]; + self.indirect_fifo_status_2.reg.set(read_index + 1); + Ok(u32::from_le_bytes(data.try_into().unwrap())) + } + + pub fn indirect_fifo_ctrl_0_write( + &mut self, + size: RvSize, + val: RvData, + ) -> Result<(), BusError> { + // Writes have to be Word aligned + if size != RvSize::Word { + Err(BusError::StoreAccessFault)? + } + let load: ReadWriteRegister = ReadWriteRegister::new(val); + if load.reg.is_set(IndirectCtrl::RESET) { + if let Some(image) = &self.cms_data { + let cms = load.reg.read(IndirectCtrl::CMS); + if cms != 0 { + self.indirect_fifo_status_0 + .reg + .set(IndirectStatus::REGION_TYPE::UnsupportedRegion.value); + } else { + self.indirect_fifo_ctrl_1.reg.set(image.len() as u32 / 4); // DWORD + self.indirect_fifo_status_0 + .reg + .set(IndirectStatus::REGION_TYPE::CodeSpaceRecovery.value); + } + self.indirect_fifo_status_1.reg.set(0); + self.indirect_fifo_status_2.reg.set(0); + self.indirect_fifo_status_0 + .reg + .modify(IndirectStatus::FIFO_EMPTY::CLEAR + IndirectStatus::FIFO_FULL::CLEAR); + } else { + println!("No Image in RRI"); + self.indirect_fifo_status_0 + .reg + .set(IndirectStatus::REGION_TYPE::UnsupportedRegion.value); + } + } + Ok(()) + } + + pub fn indirect_fifo_ctrl_1_read(&mut self, size: RvSize) -> Result { + if size != RvSize::Word { + Err(BusError::LoadAccessFault)? + } + + match &self.cms_data { + Some(d) => Ok((d.as_ref().len() / mem::size_of::()) as u32), + None => Ok(0), + } + } +} + +impl Default for RecoveryRegisterInterface { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use caliptra_emu_bus::Bus; + use caliptra_emu_types::RvAddr; + + use super::*; + + const INDIRECT_FIFO_CTRL: RvAddr = 0x48; + const INDIRECT_FIFO_RESET: RvData = 0x100; + const INDIRECT_FIFO_IMAGE_SIZE: RvAddr = 0x4c; + const INDIRECT_FIFO_STATUS: RvAddr = 0x50; + const INDIRECT_FIFO_DATA: RvAddr = 0x68; + + #[test] + fn test_get_image() { + let image = Rc::new(vec![0xab; 512]); + let image_len = image.len(); + let mut rri = RecoveryRegisterInterface::new(); + rri.cms_data = Some(image.clone()); + + // Reset + rri.write(RvSize::Word, INDIRECT_FIFO_CTRL, INDIRECT_FIFO_RESET) + .unwrap(); + + let image_size = rri.read(RvSize::Word, INDIRECT_FIFO_IMAGE_SIZE).unwrap(); + assert_eq!(image_len, image_size as usize * 4); + + let mut read_image = Vec::new(); + while rri.read(RvSize::Word, INDIRECT_FIFO_STATUS).unwrap() & 1 == 0 { + let dword_read = rri.read(RvSize::Word, INDIRECT_FIFO_DATA).unwrap(); + let bytes = dword_read.to_le_bytes(); + read_image.extend_from_slice(&bytes); + } + assert_eq!(read_image, *image); + } +} diff --git a/sw-emulator/lib/periph/src/lib.rs b/sw-emulator/lib/periph/src/lib.rs index fe7a05ed1a..7a50fd4979 100644 --- a/sw-emulator/lib/periph/src/lib.rs +++ b/sw-emulator/lib/periph/src/lib.rs @@ -27,7 +27,6 @@ mod iccm; mod key_vault; mod mailbox; mod ml_dsa87; -mod recovery; mod root_bus; mod sha512_acc; pub mod soc_reg; diff --git a/sw-emulator/lib/periph/src/recovery.rs b/sw-emulator/lib/periph/src/recovery.rs deleted file mode 100644 index d8575eecca..0000000000 --- a/sw-emulator/lib/periph/src/recovery.rs +++ /dev/null @@ -1,425 +0,0 @@ -/*++ - -Licensed under the Apache-2.0 license. - -File Name: - - recovery.rs - -Abstract: - - File contains the recovery register interface peripheral - ---*/ - -use caliptra_emu_bus::{BusError, ReadOnlyRegister, ReadWriteRegister}; -use caliptra_emu_derive::Bus; -use caliptra_emu_types::{RvData, RvSize}; -use std::rc::Rc; -use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; -use tock_registers::register_bitfields; - -// The description for these field and values come from https://www.opencompute.org/documents/ocp-recovery-document-1p0-final-1-pdf - -const FIFO_SIZE_DWORD: usize = 64; - -register_bitfields! [ - u8, - - /// Device ID type - DidType [ - TYPE OFFSET(0) NUMBITS(8) [ - PCIVendor = 0, - IANA = 1, - UUID = 2, - PnPVendor = 3, - ACPIVendor = 4, - IANAEnterprise = 5, - NVMeMI = 0xff, - ], - ], -]; - -register_bitfields! [ - u32, - - /// Version and Capabilities - VersionCapabilities [ - MAJOR OFFSET(0) NUMBITS(8) [], - MINOR OFFSET(8) NUMBITS(8) [], - IDENTIFICATION OFFSET(16) NUMBITS(1) [], - FORCED_RECOVERY OFFSET(17) NUMBITS(1) [], - MGMT_RESET OFFSET(18) NUMBITS(1) [], - DEVICE_RESET OFFSET(19) NUMBITS(1) [], - DEVICE_STATUS OFFSET(20) NUMBITS(1) [], - RECOVERY_MEMORY_ACCESS OFFSET(21) NUMBITS(1) [], - LOCAL_C_IMAGE_SUPPORT OFFSET(22) NUMBITS(1) [], - PUSH_C_IMAGE_SUPPORT OFFSET(23) NUMBITS(1) [], - INTERFACE_ISOLATION OFFSET(24) NUMBITS(1) [], - HARDWARE_STATUS OFFSET(25) NUMBITS(1) [], - VENDORS_COMMAND OFFSET(26) NUMBITS(1) [], - ], - - /// Number of component memory spaces and timing magnitudes - CmsTimings [ - CMS OFFSET(0) NUMBITS(8) [], - MAX_RESPONSE_TIME OFFSET(8) NUMBITS(8) [], - HEARTBEAT_PERIOD OFFSET(16) NUMBITS(8) [], - ], - - /// Device Information - DeviceInfo [ - STATUS OFFSET(0) NUMBITS(8) [ - StatusPending = 0, - DeviceHealthy = 1, - DeviceError = 2, - RecoveryMode = 3, - RecoveryPending = 4, - RunningRecovery = 5, - BootFailure = 0xe, - FatalError = 0xf, - ], - ERROR OFFSET(8) NUMBITS(8) [ - NoProtocolError = 0, - UnsupportedCmd = 1, - UnsupportedParameter = 2, - LengthWriteError = 3, - CrcError = 4, - ], - RECOVERY_REASON OFFSET(16) NUMBITS(16) [ - NoBootFailureDetected = 0x0, - GenericHardwareError = 0x1, - GenericHardwareSoError = 0x2, - SelfTestFailure = 0x3, - CorruptedMissingCriticalData = 0x4, - MissingCorruptKeyManifest = 0x5, - AuthenticationFailureOnKeyManifest = 0x6, - AntiRollbackFailureOnKeyManifest = 0x7, - MissingCorruptBootLoaderFirmwareImage = 0x8, - AuthenticationFailureOnBootLoaderFirmwareImage = 0x9, - AntiRollbackFailureBootLoaderFirmwareImage = 0xA, - MissingCorruptMainManagementFirmwareImage = 0xB, - AuthenticationFailureMainManagementFirmwareImage = 0xC, - AntiRollbackFailureMainManagementFirmwareImage = 0xD, - MissingCorruptRecoveryFirmware = 0xE, - AuthenticationFailureRecoveryFirmware = 0xF, - AntiRollbackFailureOnRecoveryFirmware = 0x10, - ForcedRecovery = 0x11, - // 0x12 - 0x7F are reserved and not represented in the enum - // 0x80 - 0xFF are assumed to be vendor unique codes and not represented in the enum - ], - ], - - /// Reset control - ResetControl [ - DEVICE_RESET_CONTROL OFFSET(0) NUMBITS(8) [ - NoReset = 0, - ResetDevice = 1, - ResetManagement = 2, - // All other numbers are reserved modes - ], - FORCED_RESET OFFSET(8) NUMBITS(8) [ - NoForcedRecovery = 0, - // 1 - 0xe Reserved - RecoveryMode = 0xf, - // Rest is reserved - ], - INTERFACE_CONTROL OFFSET(16) NUMBITS(1) [], - ], - - /// Recovery Control - RecoveryControl [ - CMS OFFSET(0) NUMBITS(8) [], - IMAGE_SELECTION OFFSET(8) NUMBITS(8) [ - NoOperation = 0, - RecoveryFromCms = 1, - RecoveryStoredOnDevice = 2, - // Other bits are reserved - ], - ACTIVATE_RECOVERY_IMAGE OFFSET(16) NUMBITS(8) [ - DoNotActivate = 0, - Activate = 0xf, - ], - ], - - /// Recovery Status - RecoveryStatus [ - DEVICE_RECOVERY OFFSET(0) NUMBITS(8) [ - NotInRecovery = 0x0, - AwaitingRecoveryImage = 0x1, - BootingRecoveryImage = 0x2, - RecoverySuccesfull = 0x3, - RecoveryFailed = 0xc, - AuthenticationError = 0xd, - ErrorEnteringRecovery = 0xe, - InvalidComponentAddressSpace = 0xf, - // 0x10-ff Reserved - ], - VENDOR_SPECIFIC OFFSET(8) NUMBITS(8) [], - ], - - /// HW Status - HwStatus [ - HW_STATUS OFFSET(0) NUMBITS(8) [ - TemperatureCritial = 0x0, - HardwareSoftError = 0x1, - HardwareFatalError = 0x2, - ], - VENDOR_HW_STATUS OFFSET(8) NUMBITS(8) [], - // 0x00-0x7e: 0 to 126 C - // 0x7f: 127 C or higher - // 0x80: no temperature data, or data is older than 5 seconds - // 0x81: temperature sensor failure - // 0x82-0x83: reserved - // 0xc4: -60 C or lower - // 0xc5-0xff: -1 to -59 C (in twoʼs complement) - COMPOSITE_TEMP OFFSET(16) NUMBITS(8) [], - ], - - /// Indirect FIFO Control - IndirectCtrl [ - CMS OFFSET(0) NUMBITS(8), - RESET OFFSET(8) NUMBITS(1), - ], - - /// Indirect FIFO Status - IndirectStatus [ - FIFO_EMPTY OFFSET(0) NUMBITS(1) [], - FIFO_FULL OFFSET(1) NUMBITS(1) [], - REGION_TYPE OFFSET(8) NUMBITS(3) [ - CodeSpaceRecovery = 0, // Write Only - LogWithDebugFormat = 1, // Read Only - VendorDefinedRegionWriteOnly = 4, - VendorDefinedRegionReadOnly = 5, - UnsupportedRegion = 7, - ], - ], -]; - -/// Recovery register interface -#[derive(Bus)] -pub struct RecoveryRegisterInterface { - // #[register_array(offset = 0x00)] - // prot_cap_magic: [u8; 8], - #[register(offset = 0x08)] - prot_cap_version: ReadWriteRegister, - - #[register(offset = 0x0c)] - prot_cap_cms_timing: ReadOnlyRegister, - - // TODO will this work with 32bit memory access? - // #[register(offset = 0x10)] - // device_id_descriptor_type: ReadOnlyRegister, - - // #[register(offset = 0x11)] - // device_id_vendor_id_string_length: ReadOnlyRegister, - - // #[register_array(offset = 0x12)] - // device_id: [u8; 22], - #[register(offset = 0x28)] - device_info: ReadWriteRegister, - - // #[register(offset = 0x2c)] - // hearthbeat: ReadOnlyRegister, // TODO do we need hearthbeat? - - // #[register(offset = 0x2e)] - // vendor_status_length: ReadOnlyRegister, // TODO Not supported? - #[register(offset = 0x30)] - device_reset: ReadWriteRegister, - - #[register(offset = 0x34)] - recovery_control: ReadWriteRegister, - - #[register(offset = 0x38)] - recovery_status: ReadWriteRegister, - - #[register(offset = 0x3c)] - hw_status: ReadOnlyRegister, - - #[register(offset = 0x40, write_fn = indirect_fifo_ctrl_write)] - indirect_fifo_ctrl: ReadWriteRegister, - - #[register(offset = 0x44)] // TODO Aligned here but spec says otherwise? - indirect_fifo_image_size: ReadOnlyRegister, - - #[register(offset = 0x48)] - indirect_fifo_status: ReadOnlyRegister, - - #[register(offset = 0x4c)] - write_index: ReadOnlyRegister, - - #[register(offset = 0x50)] - read_index: ReadOnlyRegister, - - #[register(offset = 0x54)] - indirect_size: ReadOnlyRegister, - - #[register(offset = 0x58)] - max_transfer_window: ReadOnlyRegister, - - // TODO email said it's 24 sized - #[register(offset = 0x6c, read_fn = indirect_fifo_data_read)] - indirect_fifo_data: ReadOnlyRegister, - // indirect_fifo_data: ReadWriteRegisterArray, // TODO should be larger size but for FW with only read use just one dword - pub cms_data: Option>>, // TODO Multiple images? -} - -impl RecoveryRegisterInterface { - // const MAGIC: [u8; 8] = [0x4f, 0x43, 0x50, 0x20, 0x52, 0x45, 0x43, 0x56]; - const MAJOR_VERSION: u32 = 0x01; - const MINOR_VERSION: u32 = 0x00; - - pub fn new() -> Self { - Self { - // prot_cap_magic: Self::MAGIC, - prot_cap_version: ReadWriteRegister::new( - VersionCapabilities::MAJOR.val(Self::MAJOR_VERSION).value - | VersionCapabilities::MINOR.val(Self::MINOR_VERSION).value, - ), - prot_cap_cms_timing: ReadOnlyRegister::new( - CmsTimings::CMS.val(1).value // TODO - | CmsTimings::MAX_RESPONSE_TIME.val(0xff).value // TODO - | CmsTimings::HEARTBEAT_PERIOD.val(0).value, // TODO 0 means unsupported - ), - // device_id_descriptor_type: ReadOnlyRegister::new(DidType::TYPE::UUID.value), // TODO - // device_id_vendor_id_string_length: ReadOnlyRegister::new(0), // not supported - // device_id: [0; 22], - device_info: ReadWriteRegister::new( - DeviceInfo::STATUS::DeviceHealthy.value - | DeviceInfo::ERROR::NoProtocolError.value - | DeviceInfo::RECOVERY_REASON::NoBootFailureDetected.value, - ), - // hearthbeat: ReadOnlyRegister::new(0), - // vendor_status_length: ReadOnlyRegister::new(0), - device_reset: ReadWriteRegister::new(0), - recovery_control: ReadWriteRegister::new(0), - recovery_status: ReadWriteRegister::new(0), - hw_status: ReadOnlyRegister::new(0), // TODO - indirect_fifo_ctrl: ReadWriteRegister::new(0), - indirect_fifo_image_size: ReadOnlyRegister::new(0), - indirect_fifo_status: ReadOnlyRegister::new(0), - write_index: ReadOnlyRegister::new(0), - read_index: ReadOnlyRegister::new(0), - indirect_size: ReadOnlyRegister::new(FIFO_SIZE_DWORD.try_into().unwrap()), - max_transfer_window: ReadOnlyRegister::new(0), - indirect_fifo_data: ReadOnlyRegister::new(0), - cms_data: None, - } - } - - pub fn indirect_fifo_data_read(&mut self, size: RvSize) -> Result { - if size != RvSize::Word { - return Err(BusError::LoadAccessFault); - } - let image = match &self.cms_data { - None => { - println!("No image set in RRI"); - return Ok(0xffff_ffff); - } - Some(x) => x, - }; - - let cms = self.indirect_fifo_ctrl.reg.read(IndirectCtrl::CMS); - if cms != 0 { - println!("CMS {cms} not supported"); - return Ok(0xffff_ffff); - } - - let read_index = self.read_index.reg.get(); - let address = read_index * 4; - let image_len = image.len().try_into().unwrap(); - if address >= image_len { - return Ok(0xffff_ffff); - }; - if address >= image_len - 4 { - self.indirect_fifo_status - .reg - .modify(IndirectStatus::FIFO_EMPTY::SET); - } - - let address: usize = address.try_into().unwrap(); - let range = address..(address + 4); - let data = &image[range]; - self.read_index.reg.set(read_index + 1); - Ok(u32::from_le_bytes(data.try_into().unwrap())) - } - - pub fn indirect_fifo_ctrl_write(&mut self, size: RvSize, val: RvData) -> Result<(), BusError> { - // Writes have to be Word aligned - if size != RvSize::Word { - Err(BusError::StoreAccessFault)? - } - let load: ReadWriteRegister = ReadWriteRegister::new(val); - if load.reg.is_set(IndirectCtrl::RESET) { - if let Some(image) = &self.cms_data { - let cms = load.reg.read(IndirectCtrl::CMS); - if cms != 0 { - self.indirect_fifo_status - .reg - .set(IndirectStatus::REGION_TYPE::UnsupportedRegion.value); - } else { - self.indirect_fifo_image_size - .reg - .set(image.len() as u32 / 4); // DWORD - self.indirect_fifo_status - .reg - .set(IndirectStatus::REGION_TYPE::CodeSpaceRecovery.value); - } - self.write_index.reg.set(0); - self.read_index.reg.set(0); - self.indirect_fifo_status - .reg - .modify(IndirectStatus::FIFO_EMPTY::CLEAR + IndirectStatus::FIFO_FULL::CLEAR); - } else { - println!("No Image in RRI"); - self.indirect_fifo_status - .reg - .set(IndirectStatus::REGION_TYPE::UnsupportedRegion.value); - } - } - Ok(()) - } -} - -impl Default for RecoveryRegisterInterface { - fn default() -> Self { - Self::new() - } -} - -#[cfg(test)] -mod tests { - use caliptra_emu_bus::Bus; - use caliptra_emu_types::RvAddr; - - use super::*; - - const INDIRECT_FIFO_CTRL: RvAddr = 0x40; - const INDIRECT_FIFO_RESET: RvData = 0x100; - const INDIRECT_FIFO_IMAGE_SIZE: RvAddr = 0x44; - const INDIRECT_FIFO_STATUS: RvAddr = 0x48; - const INDIRECT_FIFO_DATA: RvAddr = 0x6c; - - #[test] - fn test_get_image() { - let image = Rc::new(vec![0xab; 512]); - let image_len = image.len(); - let mut rri = RecoveryRegisterInterface::new(); - rri.cms_data = Some(image.clone()); - - // Reset - rri.write(RvSize::Word, INDIRECT_FIFO_CTRL, INDIRECT_FIFO_RESET) - .unwrap(); - - let image_size = rri.read(RvSize::Word, INDIRECT_FIFO_IMAGE_SIZE).unwrap(); - assert_eq!(image_len, image_size as usize * 4); - - let mut read_image = Vec::new(); - while rri.read(RvSize::Word, INDIRECT_FIFO_STATUS).unwrap() & 1 == 0 { - let dword_read = rri.read(RvSize::Word, INDIRECT_FIFO_DATA).unwrap(); - let bytes = dword_read.to_le_bytes(); - read_image.extend_from_slice(&bytes); - } - assert_eq!(read_image, *image); - } -} diff --git a/sw-emulator/lib/periph/src/root_bus.rs b/sw-emulator/lib/periph/src/root_bus.rs index d4ca7706c0..39b07e907c 100644 --- a/sw-emulator/lib/periph/src/root_bus.rs +++ b/sw-emulator/lib/periph/src/root_bus.rs @@ -17,7 +17,6 @@ use crate::{ helpers::words_from_bytes_be, iccm::Iccm, ml_dsa87::Mldsa87, - recovery::RecoveryRegisterInterface, soc_reg::{DebugManufService, SocRegistersExternal}, AsymEcc384, Csrng, Doe, EmuCtrl, HashSha256, HashSha512, HmacSha, KeyVault, MailboxExternal, MailboxInternal, MailboxRam, Sha512Accelerator, SocRegistersInternal, Uart, @@ -227,6 +226,7 @@ pub struct CaliptraRootBusArgs { pub itrng_nibbles: Option>>, pub etrng_responses: Box>, + pub active_mode: bool, } impl Default for CaliptraRootBusArgs { fn default() -> Self { @@ -242,12 +242,12 @@ impl Default for CaliptraRootBusArgs { cptra_obf_key: words_from_bytes_be(&DEFAULT_DOE_KEY), itrng_nibbles: Some(Box::new(RandomNibbles::new_from_thread_rng())), etrng_responses: Box::new(RandomEtrngResponses::new_from_stdrng()), + active_mode: false, } } } #[derive(Bus)] -#[handle_dma_fn(handle_dma)] pub struct CaliptraRootBus { #[peripheral(offset = 0x0000_0000, mask = 0x0fff_ffff)] pub rom: Rom, @@ -273,10 +273,6 @@ pub struct CaliptraRootBus { #[peripheral(offset = 0x1003_0000, mask = 0x0000_ffff)] pub ml_dsa87: Mldsa87, - // We set I3C at 0x1004_0000 and EC is at 0x100 offset - #[peripheral(offset = 0x1004_0100, mask = 0x0000_7fff)] // TODO - pub recovery: RecoveryRegisterInterface, - #[peripheral(offset = 0x4000_0000, mask = 0x0fff_ffff)] pub iccm: Iccm, @@ -330,6 +326,7 @@ impl CaliptraRootBus { // This is necessary to match the behavior of the RTL. key_vault.clear_keys_with_debug_values(false); } + let dma = Dma::new(clock, mailbox_ram.clone()); let sha512 = HashSha512::new(clock, key_vault.clone()); @@ -342,7 +339,6 @@ impl CaliptraRootBus { sha512, sha256: HashSha256::new(clock), ml_dsa87: Mldsa87::new(clock, key_vault.clone()), - recovery: RecoveryRegisterInterface::new(), iccm, dccm: Ram::new(vec![0; Self::DCCM_SIZE]), uart: Uart::new(), @@ -351,7 +347,7 @@ impl CaliptraRootBus { mailbox_sram: mailbox_ram.clone(), mailbox, sha512_acc: Sha512Accelerator::new(clock, mailbox_ram), - dma: Dma::new(clock), + dma, csrng: Csrng::new(itrng_nibbles.unwrap()), pic_regs: pic.mmio_regs(clock), } @@ -364,11 +360,6 @@ impl CaliptraRootBus { soc_ifc: self.soc_reg.external_regs(), } } - - fn handle_dma(&mut self) { - let mut dma = self.dma.clone(); - dma.do_dma_handling(self) - } } #[derive(Bus)] diff --git a/sw-emulator/lib/periph/src/soc_reg.rs b/sw-emulator/lib/periph/src/soc_reg.rs index 6a3e878ad8..bfee0fa2dd 100644 --- a/sw-emulator/lib/periph/src/soc_reg.rs +++ b/sw-emulator/lib/periph/src/soc_reg.rs @@ -702,6 +702,12 @@ struct SocRegistersImpl { #[register_array(offset = 0x34c)] fuse_manuf_dbg_unlock_token: [u32; FUSE_MANUF_DBG_UNLOCK_TOKEN_SIZE / 4], + #[register(offset = 0x510)] + ss_recovery_ifc_base_addr_l: ReadOnlyRegister, + + #[register(offset = 0x514)] + ss_recovery_ifc_base_addr_h: ReadOnlyRegister, + #[register(offset = 0x520)] ss_uds_seed_base_addr_l: ReadOnlyRegister, @@ -836,6 +842,8 @@ impl SocRegistersImpl { /// The number of CPU clock cycles it takes to read the IDEVID CSR from the mailbox. const IDEVID_CSR_READ_TICKS: u64 = 100; + const CALIPTRA_HW_CONFIG_ACTIVE_MODE: u32 = 1 << 5; + pub fn new( clock: &Clock, mailbox: MailboxInternal, @@ -846,6 +854,8 @@ impl SocRegistersImpl { let flow_status = InMemoryRegister::::new(0); flow_status.write(FlowStatus::READY_FOR_FUSES.val(1)); + let rri_offset = crate::dma::axi_root_bus::AxiRootBus::RECOVERY_REGISTER_INTERFACE_OFFSET; + let regs = Self { cptra_hw_error_fatal: ReadWriteRegister::new(0), cptra_hw_error_non_fatal: ReadWriteRegister::new(0), @@ -874,7 +884,11 @@ impl SocRegistersImpl { cptra_generic_output_wires: Default::default(), cptra_hw_rev_id: ReadOnlyRegister::new(0x11), // TODO 2.0 cptra_fw_rev_id: Default::default(), - cptra_hw_config: ReadWriteRegister::new(0), // [TODO][CAP2] Program this + cptra_hw_config: ReadWriteRegister::new(if args.active_mode { + Self::CALIPTRA_HW_CONFIG_ACTIVE_MODE + } else { + 0 + }), cptra_wdt_timer1_en: ReadWriteRegister::new(0), cptra_wdt_timer1_ctrl: ReadWriteRegister::new(0), cptra_wdt_timer1_timeout_period: [0xffff_ffff; 2], @@ -906,6 +920,8 @@ impl SocRegistersImpl { fuse_mldsa_revocation: Default::default(), fuse_soc_stepping_id: ReadWriteRegister::new(0), fuse_manuf_dbg_unlock_token: [0; 4], + ss_recovery_ifc_base_addr_l: ReadOnlyRegister::new(rri_offset as u32), + ss_recovery_ifc_base_addr_h: ReadOnlyRegister::new((rri_offset >> 32) as u32), internal_obf_key: args.cptra_obf_key, internal_iccm_lock: ReadWriteRegister::new(0), internal_fw_update_reset: ReadWriteRegister::new(0), diff --git a/sw-emulator/lib/types/src/bus.rs b/sw-emulator/lib/types/src/bus.rs index c845bc1410..2b7019ceba 100644 --- a/sw-emulator/lib/types/src/bus.rs +++ b/sw-emulator/lib/types/src/bus.rs @@ -74,8 +74,4 @@ pub trait Bus { fn update_reset(&mut self) { // By default, do nothing } - - fn handle_dma(&mut self) { - // By default, do nothing - } } diff --git a/test/dpe_verification/transport.go b/test/dpe_verification/transport.go index fe618bb326..f096c4299e 100644 --- a/test/dpe_verification/transport.go +++ b/test/dpe_verification/transport.go @@ -68,7 +68,7 @@ var CALIPTRA_C_MODEL *C.struct_caliptra_model //export caliptra_write_u32 func caliptra_write_u32(address C.uint32_t, data C.uint32_t) C.int { - result := C.caliptra_model_axi_write_u32(CALIPTRA_C_MODEL, address, data) + result := C.caliptra_model_apb_write_u32(CALIPTRA_C_MODEL, address, data) C.caliptra_model_step(CALIPTRA_C_MODEL) @@ -77,7 +77,7 @@ func caliptra_write_u32(address C.uint32_t, data C.uint32_t) C.int { //export caliptra_read_u32 func caliptra_read_u32(address C.uint32_t, data *C.uint32_t) C.int { - return C.caliptra_model_axi_read_u32(CALIPTRA_C_MODEL, address, data) + return C.caliptra_model_apb_read_u32(CALIPTRA_C_MODEL, address, data) } //export caliptra_wait diff --git a/test/tests/fips_test_suite/security_parameters.rs b/test/tests/fips_test_suite/security_parameters.rs index aad950e27e..795a2649e2 100755 --- a/test/tests/fips_test_suite/security_parameters.rs +++ b/test/tests/fips_test_suite/security_parameters.rs @@ -27,11 +27,11 @@ fn prove_jtag_inaccessible(_hw: &mut T) { fn attempt_csp_fuse_read(hw: &mut T) { let uds_ptr = hw.soc_ifc().fuse_uds_seed().at(0).ptr; let uds_read_val = - unsafe { caliptra_hw_model::BusMmio::new(hw.axi_bus()).read_volatile(uds_ptr) }; + unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).read_volatile(uds_ptr) }; let field_entropy_ptr = hw.soc_ifc().fuse_field_entropy().at(0).ptr; let field_entropy_read_val = - unsafe { caliptra_hw_model::BusMmio::new(hw.axi_bus()).read_volatile(field_entropy_ptr) }; + unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).read_volatile(field_entropy_ptr) }; // TODO: Add exception for SW emulator (does not model locked registers at the MMIO level) assert_eq!(uds_read_val, INACCESSIBLE_READ_VALUE); @@ -45,7 +45,7 @@ fn attempt_psp_fuse_modify(hw: &mut T) { // Try to write a new value let owner_pk_hash_ptr = hw.soc_ifc().fuse_key_manifest_pk_hash().at(0).ptr; unsafe { - caliptra_hw_model::BusMmio::new(hw.axi_bus()).write_volatile(owner_pk_hash_ptr, 0xaaaaaaaa) + caliptra_hw_model::BusMmio::new(hw.apb_bus()).write_volatile(owner_pk_hash_ptr, 0xaaaaaaaa) }; let owner_pk_hash_read_val = hw.soc_ifc().fuse_key_manifest_pk_hash().at(0).read(); @@ -62,17 +62,17 @@ fn attempt_keyvault_access(hw: &mut T) { // This is not visible to the SoC, but shared modules (mailbox, SHA engine, etc.) use a 1:1 // address mapping between the SoC and Caliptra let kv_key_ctrl_val = - unsafe { caliptra_hw_model::BusMmio::new(hw.axi_bus()).read_volatile(kv_key_ctrl_ptr) }; + unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).read_volatile(kv_key_ctrl_ptr) }; assert_eq!(kv_key_ctrl_val, INACCESSIBLE_READ_VALUE); // Attempt to write unsafe { - caliptra_hw_model::BusMmio::new(hw.axi_bus()).write_volatile(kv_key_ctrl_ptr, 0xffffffff) + caliptra_hw_model::BusMmio::new(hw.apb_bus()).write_volatile(kv_key_ctrl_ptr, 0xffffffff) }; // Read again let kv_key_ctrl_val = - unsafe { caliptra_hw_model::BusMmio::new(hw.axi_bus()).read_volatile(kv_key_ctrl_ptr) }; + unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).read_volatile(kv_key_ctrl_ptr) }; assert_eq!(kv_key_ctrl_val, INACCESSIBLE_READ_VALUE); } @@ -83,14 +83,14 @@ fn attempt_caliptra_dccm_access(hw: &mut T) { // Attempt to read DCCM module from the SoC side // This is not visible to the SoC, but shared modules (mailbox, SHA engine, etc.) use a 1:1 // address mapping between the SoC and Caliptra - let dccm_val = unsafe { caliptra_hw_model::BusMmio::new(hw.axi_bus()).read_volatile(dccm_ptr) }; + let dccm_val = unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).read_volatile(dccm_ptr) }; assert_eq!(dccm_val, INACCESSIBLE_READ_VALUE); // Attempt to write - unsafe { caliptra_hw_model::BusMmio::new(hw.axi_bus()).write_volatile(dccm_ptr, 0xffffffff) }; + unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).write_volatile(dccm_ptr, 0xffffffff) }; // Read again - let dccm_val = unsafe { caliptra_hw_model::BusMmio::new(hw.axi_bus()).read_volatile(dccm_ptr) }; + let dccm_val = unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).read_volatile(dccm_ptr) }; assert_eq!(dccm_val, INACCESSIBLE_READ_VALUE); }