diff --git a/Cargo.lock b/Cargo.lock index bc3c27a13c..d98299b20d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -480,6 +480,7 @@ dependencies = [ "caliptra-emu-types", "caliptra-hw-model-types", "caliptra-registers", + "const-random", "fips204", "lazy_static", "rand", @@ -816,6 +817,7 @@ dependencies = [ "caliptra-cfi-lib-git", "caliptra-cpu", "caliptra-drivers", + "caliptra-emu-bus", "caliptra-error", "caliptra-gen-linker-scripts", "caliptra-hw-model", @@ -1118,6 +1120,26 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + [[package]] name = "constant_time_eq" version = "0.3.0" @@ -1148,6 +1170,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + [[package]] name = "crypto" version = "0.1.0" @@ -2456,6 +2484,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinytemplate" version = "1.2.1" diff --git a/Cargo.toml b/Cargo.toml index e546243d74..ec23704b39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,6 +142,7 @@ cbindgen = { version = "0.24.0", default-features = false } cfg-if = "1.0.0" chrono = "0.4" clap = { version = "3.2.14", default-features = false, features = ["std"] } +const-random = "0.1.18" cms = "0.2.2" convert_case = "0.6.0" dpe = { path = "dpe/dpe", default-features = false, features = ["dpe_profile_p384_sha384"] } diff --git a/drivers/src/dma.rs b/drivers/src/dma.rs index d6b5d799c8..05d3b47fd0 100644 --- a/drivers/src/dma.rs +++ b/drivers/src/dma.rs @@ -28,7 +28,7 @@ use crate::cprintln; pub enum DmaReadTarget { Mbox, AhbFifo, - AxiWr(AxiAddr), + AxiWr(AxiAddr, bool), } #[derive(Debug, Clone, Copy)] @@ -155,7 +155,7 @@ impl Dma { dma.src_addr_l().write(|_| read_addr.lo); dma.src_addr_h().write(|_| read_addr.hi); - if let DmaReadTarget::AxiWr(target_addr) = read_transaction.target { + 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); } @@ -164,13 +164,17 @@ impl Dma { c.rd_route(|_| match read_transaction.target { DmaReadTarget::Mbox => RdRouteE::Mbox, DmaReadTarget::AhbFifo => RdRouteE::AhbFifo, - DmaReadTarget::AxiWr(_) => RdRouteE::AxiWr, + DmaReadTarget::AxiWr(_, _) => RdRouteE::AxiWr, }) .rd_fixed(read_transaction.fixed_addr) .wr_route(|_| match read_transaction.target { - DmaReadTarget::AxiWr(_) => WrRouteE::AxiRd, + DmaReadTarget::AxiWr(_, _) => WrRouteE::AxiRd, _ => WrRouteE::Disable, }) + .wr_fixed(match read_transaction.target { + DmaReadTarget::AxiWr(_, fixed) => fixed, + _ => false, + }) }); dma.byte_count().write(|_| read_transaction.length); @@ -463,15 +467,21 @@ impl<'a> MmioMut for &DmaMmio<'a> { // Wrapper around the DMA peripheral that provides access to the I3C recovery interface. pub struct DmaRecovery<'a> { base: AxiAddr, + mci_base: AxiAddr, dma: &'a Dma, } impl<'a> DmaRecovery<'a> { const RECOVERY_REGISTER_OFFSET: usize = 0x100; + const RECOVERY_DMA_BLOCK_SIZE_BYTES: u32 = 256; #[inline(always)] - pub fn new(base: AxiAddr, dma: &'a Dma) -> Self { - Self { base, dma } + pub fn new(base: AxiAddr, mci_base: AxiAddr, dma: &'a Dma) -> Self { + Self { + base, + mci_base, + dma, + } } /// Returns a register block that can be used to read @@ -537,6 +547,28 @@ impl<'a> DmaRecovery<'a> { Ok(()) } + fn transfer_payload_to_axi( + &self, + read_addr: AxiAddr, + payload_len_bytes: u32, + write_addr: AxiAddr, + fixed_addr: bool, + block_size: u32, + ) -> CaliptraResult<()> { + self.dma.flush(); + + let read_transaction = DmaReadTransaction { + read_addr, + fixed_addr, + length: payload_len_bytes, + target: DmaReadTarget::AxiWr(write_addr, false), + }; + 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, @@ -559,21 +591,70 @@ impl<'a> DmaRecovery<'a> { } // Downloads an image from the recovery interface to the mailbox SRAM. - pub fn download_image_to_mbox(&self, fw_image_index: u32) -> CaliptraResult { + pub fn download_image_to_mbox( + &self, + fw_image_index: u32, + caliptra_fw: bool, + ) -> CaliptraResult { const INDIRECT_FIFO_DATA_OFFSET: u32 = 0x68; - const RECOVERY_DMA_BLOCK_SIZE_BYTES: u32 = 256; + let image_size_bytes = self.download_image(fw_image_index, caliptra_fw)?; + // 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, + Self::RECOVERY_DMA_BLOCK_SIZE_BYTES, + )?; + self.set_device_status_recovery_pending()?; + Ok(image_size_bytes) + } + + // Downloads an image from the recovery interface to the MCU SRAM. + pub fn download_image_to_mcu( + &self, + fw_image_index: u32, + caliptra_fw: bool, + ) -> CaliptraResult { + const INDIRECT_FIFO_DATA_OFFSET: u32 = 0x68; + let image_size_bytes = self.download_image(fw_image_index, caliptra_fw)?; + let addr = self.base + INDIRECT_FIFO_DATA_OFFSET; + let mcu_offset = 0x20_0000u64; + cprintln!("[dma-recovery] Uploading image to MCU SRAM"); + self.transfer_payload_to_axi( + addr, + image_size_bytes, + self.mci_base + mcu_offset, + true, + Self::RECOVERY_DMA_BLOCK_SIZE_BYTES, + )?; + self.set_device_status_recovery_pending()?; + Ok(image_size_bytes) + } + pub fn set_device_status_recovery_pending(&self) -> CaliptraResult<()> { 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)). + // Set DEVICE_STATUS:Byte0 to 0x4 ('Recovery pending'). recovery .device_status_0() - .modify(|val| (val & 0xFF00FF00) | (0x12 << 16) | 0x03); + .modify(|val| (val & !0xff) | 0x04); + }) + } + + // Downloads an image from the recovery interface to the mailbox SRAM. + pub fn download_image(&self, fw_image_index: u32, caliptra_fw: bool) -> CaliptraResult { + cprintln!( + "[dma-recovery] Requesting recovery image {}", + fw_image_index + ); + + 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 RECOVERY_STATUS:Byte0 Bit[3:0] to 0x1 ('Awaiting recovery image') & // Byte0 Bit[7:4] to 0 (Recovery image index). @@ -583,30 +664,53 @@ impl<'a> DmaRecovery<'a> { // Set Byte0 Bit[7:4] to recovery image index (recovery_status_val & 0xFFFFFF00) | (fw_image_index << 4) | 0x1 }); + + if caliptra_fw { + // the first image is our own firmware, so we needd to set up to receive it + // 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); + } else { + // if this is our own firmware, then we must now be running the recovery image, + // which is necessary to load further images + // Set DEVICE_STATUS:Byte0 to 0x5 ('Running recovery image'). + recovery + .device_status_0() + .modify(|val| (val & !0xff) | 0x05); + } })?; // Loop on the 'payload_available' signal for the recovery image details to be available. - cprintln!("[fwproc] Waiting for payload available signal..."); + cprintln!("[dma-recovery] 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(); + // set RESET signal to indirect control to load the nexxt image + recovery.indirect_fifo_ctrl_0().modify(|val| val | (1 << 8)); + // [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_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); - let image_size_dwords = indirect_fifo_ctrl_val1; + let image_size_dwords = + (indirect_fifo_ctrl_val0 & 0xFFFF0000) | (indirect_fifo_ctrl_val1 & 0xFFFF); 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) } + + // TODO: move to separate MCI struct and use autogenerated registers + pub fn set_mci_flow_status(&self, status: u32) -> CaliptraResult<()> { + let mmio = &DmaMmio::new(self.mci_base, self.dma); + // Safety: 0x24 is the offset for the MCI flow status register + unsafe { mmio.write_volatile(0x24 as *mut u32, status) }; + mmio.check_error(()) + } } diff --git a/drivers/src/soc_ifc.rs b/drivers/src/soc_ifc.rs index e3e7d50ed6..48e2eedd01 100644 --- a/drivers/src/soc_ifc.rs +++ b/drivers/src/soc_ifc.rs @@ -534,6 +534,12 @@ impl SocIfc { let high = self.soc_ifc.regs().ss_recovery_ifc_base_addr_h().read(); (high as u64) << 32 | low as u64 } + + pub fn mci_base_addr(&self) -> u64 { + let low = self.soc_ifc.regs().ss_mci_base_addr_l().read(); + let high = self.soc_ifc.regs().ss_mci_base_addr_h().read(); + (high as u64) << 32 | low as u64 + } } bitflags::bitflags! { diff --git a/fmc/build.sh b/fmc/build.sh index 0feabf5fd8..0519c37b2b 100755 --- a/fmc/build.sh +++ b/fmc/build.sh @@ -1,6 +1,5 @@ -# Licensed under the Apache-2.0 license - #!/bin/bash +# Licensed under the Apache-2.0 license cd "$(dirname "${BASH_SOURCE[0]}")" @@ -9,4 +8,5 @@ cargo build \ --target riscv32imc-unknown-none-elf \ --profile=firmware \ --no-default-features \ + --features riscv \ --bin=caliptra-fmc diff --git a/hw-model/src/bus_logger.rs b/hw-model/src/bus_logger.rs index 8cdcd4770a..7dbdcbc084 100644 --- a/hw-model/src/bus_logger.rs +++ b/hw-model/src/bus_logger.rs @@ -130,4 +130,13 @@ impl Bus for BusLogger { fn update_reset(&mut self) { self.bus.update_reset(); } + fn incoming_event(&mut self, event: Rc) { + self.bus.incoming_event(event); + } + fn register_outgoing_events( + &mut self, + sender: std::sync::mpsc::Sender, + ) { + self.bus.register_outgoing_events(sender); + } } diff --git a/hw-model/src/lib.rs b/hw-model/src/lib.rs index 3b2b99fc98..4dfb2f7a83 100644 --- a/hw-model/src/lib.rs +++ b/hw-model/src/lib.rs @@ -4,10 +4,11 @@ use api::CaliptraApiError; use caliptra_api as api; use caliptra_api::SocManager; use caliptra_api_types as api_types; -use caliptra_emu_bus::Bus; +use caliptra_emu_bus::{Bus, Event}; use core::panic; use std::path::PathBuf; use std::str::FromStr; +use std::sync::mpsc; use std::{ error::Error, fmt::Display, @@ -274,6 +275,10 @@ pub struct BootParams<'a> { pub initial_adaptp_thresh_reg: Option, pub valid_axi_user: Vec, pub wdt_timeout_cycles: u64, + // SoC manifest passed via the recovery interface + pub soc_manifest: Option<&'a [u8]>, + // MCU firmware image passed via the recovery interface + pub mcu_fw_image: Option<&'a [u8]>, } impl<'a> Default for BootParams<'a> { @@ -286,6 +291,8 @@ impl<'a> Default for BootParams<'a> { initial_adaptp_thresh_reg: Default::default(), valid_axi_user: vec![0, 1, 2, 3, 4], wdt_timeout_cycles: EXPECTED_CALIPTRA_BOOT_TIME_IN_CYCLES, + soc_manifest: Default::default(), + mcu_fw_image: Default::default(), } } } @@ -632,7 +639,11 @@ pub trait HwModel: SocManager { if active_mode { "active" } else { "passive" } )?; if active_mode { - self.upload_firmware_rri(fw_image)?; + self.upload_firmware_rri( + fw_image, + boot_params.soc_manifest, + boot_params.mcu_fw_image, + )?; } else { self.upload_firmware(fw_image)?; } @@ -1036,11 +1047,21 @@ pub trait HwModel: SocManager { } /// HW-model function to place the image in rri - fn put_firmware_in_rri(&mut self, firmware: &[u8]) -> Result<(), ModelError>; + fn put_firmware_in_rri( + &mut self, + firmware: &[u8], + soc_manifest: Option<&[u8]>, + mcu_firmware: Option<&[u8]>, + ) -> Result<(), ModelError>; /// Upload fw image to RRI. - fn upload_firmware_rri(&mut self, firmware: &[u8]) -> Result<(), ModelError> { - self.put_firmware_in_rri(firmware)?; + fn upload_firmware_rri( + &mut self, + firmware: &[u8], + soc_manifest: Option<&[u8]>, + mcu_firmware: Option<&[u8]>, + ) -> Result<(), ModelError> { + self.put_firmware_in_rri(firmware, soc_manifest, mcu_firmware)?; let response = self.mailbox_execute(RI_DOWNLOAD_FIRMWARE_OPCODE, &[])?; if response.is_some() { return Err(ModelError::UploadFirmwareUnexpectedResponse); @@ -1048,6 +1069,10 @@ pub trait HwModel: SocManager { Ok(()) } + fn events_from_caliptra(&mut self) -> Vec; + + fn events_to_caliptra(&mut self) -> mpsc::Sender; + fn wait_for_mailbox_receive(&mut self) -> Result, ModelError> where Self: Sized, diff --git a/hw-model/src/model_emulated.rs b/hw-model/src/model_emulated.rs index 1d90303275..f366b612c0 100644 --- a/hw-model/src/model_emulated.rs +++ b/hw-model/src/model_emulated.rs @@ -7,8 +7,10 @@ use std::hash::Hasher; use std::io::Write; use std::path::PathBuf; use std::rc::Rc; +use std::sync::mpsc; use caliptra_emu_bus::Clock; +use caliptra_emu_bus::Event; #[cfg(feature = "coverage")] use caliptra_emu_cpu::CoverageBitmaps; use caliptra_emu_cpu::{Cpu, InstrTracer}; @@ -67,6 +69,10 @@ pub struct ModelEmulated { _rom_image_tag: u64, iccm_image_tag: Option, trng_mode: TrngMode, + + events_to_caliptra: mpsc::Sender, + events_from_caliptra: mpsc::Receiver, + collected_events_from_caliptra: Vec, } #[cfg(feature = "coverage")] @@ -194,12 +200,13 @@ impl HwModel for ModelEmulated { dccm_dest.copy_from_slice(params.dccm); } let soc_to_caliptra_bus = root_bus.soc_to_caliptra_bus(); - let cpu = { + let (events_to_caliptra, events_from_caliptra, cpu) = { let mut cpu = Cpu::new(BusLogger::new(root_bus), clock); if let Some(stack_info) = params.stack_info { cpu.with_stack_info(stack_info); } - cpu + let (events_to_caliptra, events_from_caliptra) = cpu.register_events(); + (events_to_caliptra, events_from_caliptra, cpu) }; let mut hasher = DefaultHasher::new(); @@ -217,6 +224,9 @@ impl HwModel for ModelEmulated { _rom_image_tag: image_tag, iccm_image_tag: None, trng_mode, + events_to_caliptra, + events_from_caliptra, + collected_events_from_caliptra: vec![], }; // Turn tracing on if the trace path was set m.tracing_hint(true); @@ -243,6 +253,8 @@ impl HwModel for ModelEmulated { if self.cpu_enabled.get() { self.cpu.step(self.trace_fn.as_deref_mut()); } + let events = self.events_from_caliptra.try_iter().collect::>(); + self.collected_events_from_caliptra.extend(events); } fn output(&mut self) -> &mut Output { @@ -305,8 +317,41 @@ impl HwModel for ModelEmulated { } // [TODO][CAP2] Should it be statically provisioned? - fn put_firmware_in_rri(&mut self, firmware: &[u8]) -> Result<(), ModelError> { - self.cpu.bus.bus.dma.axi.recovery.cms_data = Some(Rc::new(firmware.to_vec())); + fn put_firmware_in_rri( + &mut self, + firmware: &[u8], + soc_manifest: Option<&[u8]>, + mcu_firmware: Option<&[u8]>, + ) -> Result<(), ModelError> { + self.cpu.bus.bus.dma.axi.recovery.cms_data = vec![firmware.to_vec()]; + if let Some(soc_manifest) = soc_manifest { + self.cpu + .bus + .bus + .dma + .axi + .recovery + .cms_data + .push(soc_manifest.to_vec()); + if let Some(mcu_fw) = mcu_firmware { + self.cpu + .bus + .bus + .dma + .axi + .recovery + .cms_data + .push(mcu_fw.to_vec()); + } + } Ok(()) } + + fn events_from_caliptra(&mut self) -> Vec { + self.collected_events_from_caliptra.drain(..).collect() + } + + fn events_to_caliptra(&mut self) -> mpsc::Sender { + self.events_to_caliptra.clone() + } } diff --git a/hw-model/src/model_fpga_realtime.rs b/hw-model/src/model_fpga_realtime.rs index d6eecf8a43..2566869357 100644 --- a/hw-model/src/model_fpga_realtime.rs +++ b/hw-model/src/model_fpga_realtime.rs @@ -2,6 +2,7 @@ use std::io::{BufRead, BufReader, Write}; use std::marker::PhantomData; +use std::mpsc; use std::process::{Child, Command, Stdio}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -9,7 +10,7 @@ use std::thread; use std::{env, str::FromStr}; use bitfield::bitfield; -use caliptra_emu_bus::{Bus, BusError, BusMmio}; +use caliptra_emu_bus::{Bus, BusError, BusMmio, Event}; use caliptra_emu_types::{RvAddr, RvData, RvSize}; use libc; use nix; @@ -533,6 +534,14 @@ impl HwModel for ModelFpgaRealtime { fn put_firmware_in_rri(&mut self, firmware: &[u8]) -> Result<(), ModelError> { todo!() } + + fn events_from_caliptra(&mut self) -> Vec { + todo!() + } + + fn events_to_caliptra(&mut self) -> mpsc::Sender { + todo!() + } } impl ModelFpgaRealtime { diff --git a/hw-model/src/model_verilated.rs b/hw-model/src/model_verilated.rs index 2890b31e4f..2a993899bd 100644 --- a/hw-model/src/model_verilated.rs +++ b/hw-model/src/model_verilated.rs @@ -6,12 +6,14 @@ use crate::EtrngResponse; use crate::{HwModel, SocManager, TrngMode}; use caliptra_emu_bus::Bus; use caliptra_emu_bus::BusMmio; +use caliptra_emu_bus::Event; use caliptra_emu_types::{RvAddr, RvData, RvSize}; use caliptra_hw_model_types::ErrorInjectionMode; use caliptra_verilated::{AhbTxnType, CaliptraVerilated}; use std::cell::{Cell, RefCell}; use std::ffi::OsStr; use std::io::Write; +use std::mpsc; use std::path::{Path, PathBuf}; use std::rc::Rc; @@ -444,4 +446,12 @@ impl ModelVerilated { fn put_firmware_in_rri(&mut self, firmware: &[u8]) -> Result<(), ModelError> { todo!() } + + fn events_from_caliptra(&mut self) -> Vec { + todo!() + } + + fn events_to_caliptra(&mut self) -> mpsc::Sender { + todo!() + } } diff --git a/rom/dev/Makefile b/rom/dev/Makefile index d6a345dbf5..ecdf099b6f 100644 --- a/rom/dev/Makefile +++ b/rom/dev/Makefile @@ -39,6 +39,17 @@ build-emu: --no-default-features \ --features "emu no-cfi" +build-fmc: + cargo \ + "--config=$(EXTRA_CARGO_CONFIG)" \ + build \ + --profile firmware \ + --manifest-path ../../fmc/Cargo.toml \ + --target=riscv32imc-unknown-none-elf \ + --no-default-features \ + --features emu,riscv \ + --bin caliptra-fmc + build-test-fmc: cargo \ "--config=$(EXTRA_CARGO_CONFIG)" \ @@ -49,6 +60,17 @@ build-test-fmc: --no-default-features \ --features emu +build-rt: + cargo \ + "--config=$(EXTRA_CARGO_CONFIG)" \ + build \ + --profile firmware \ + --manifest-path ../../runtime/Cargo.toml \ + --target=riscv32imc-unknown-none-elf \ + --no-default-features \ + --features emu,riscv \ + --bin caliptra-runtime + build-test-rt: cargo \ "--config=$(EXTRA_CARGO_CONFIG)" \ @@ -63,7 +85,7 @@ gen-certs: $(shell bash $(CURRENT_DIR)/tools/scripts/gen_test_certs.sh $(TARGET_DIR)) $(shell cp $(CURRENT_DIR)/tools/keys.toml $(TARGET_DIR)) -build-fw-image: gen-certs build-test-fmc build-test-rt +build-fw-test-image: gen-certs build-test-fmc build-test-rt cargo \ "--config=$(EXTRA_CARGO_CONFIG)" \ run \ @@ -84,6 +106,27 @@ build-fw-image: gen-certs build-test-fmc build-test-rt --out $(TARGET_DIR)/caliptra-rom-test-fw \ --print-hashes +build-fw-image: gen-certs build-fmc build-rt + cargo \ + "--config=$(EXTRA_CARGO_CONFIG)" \ + run \ + --manifest-path ../../image/app/Cargo.toml \ + -- \ + create \ + --pqc-key-type $(PQC_KEY_TYPE) \ + --key-config $(TARGET_DIR)/keys.toml \ + --ecc-pk-idx 1 \ + --pqc-pk-idx 0 \ + --fmc $(TARGET_DIR)/caliptra-fmc \ + --fmc-version 0 \ + --fmc-rev $(GIT_REV) \ + --rt $(TARGET_DIR)/caliptra-runtime \ + --rt-version 0 \ + --rt-rev $(GIT_REV) \ + --fw-svn 0 \ + --out $(TARGET_DIR)/caliptra-fw \ + --print-hashes + bloat: build cargo \ "--config=$(EXTRA_CARGO_CONFIG)" \ @@ -116,7 +159,7 @@ build-rom: --rom-with-log $(TARGET_DIR)/caliptra-rom.bin \ --fw /dev/null -run: build-emu build-fw-image build-rom +run: build-emu build-fw-test-image build-rom cargo \ "--config=$(EXTRA_CARGO_CONFIG)" \ run \ @@ -142,7 +185,7 @@ run-active: build-emu build-fw-image build-rom --device-lifecycle unprovisioned \ --active-mode \ -run-update: build-emu build-fw-image build-rom +run-update: build-emu build-fw-test-image build-rom cargo \ "--config=$(EXTRA_CARGO_CONFIG)" \ run \ @@ -183,4 +226,3 @@ hexdump: build-rom clean: cargo clean - diff --git a/rom/dev/src/flow/cold_reset/fw_processor.rs b/rom/dev/src/flow/cold_reset/fw_processor.rs index 7f8f77674a..e52665055a 100644 --- a/rom/dev/src/flow/cold_reset/fw_processor.rs +++ b/rom/dev/src/flow/cold_reset/fw_processor.rs @@ -860,8 +860,9 @@ impl FirmwareProcessor { soc_ifc: &mut SocIfc, ) -> CaliptraResult { let rri_base_addr = soc_ifc.recovery_interface_base_addr().into(); + let mci_base_addr = soc_ifc.mci_base_addr().into(); const FW_IMAGE_INDEX: u32 = 0x0; - let dma_recovery = DmaRecovery::new(rri_base_addr, dma); - dma_recovery.download_image_to_mbox(FW_IMAGE_INDEX) + let dma_recovery = DmaRecovery::new(rri_base_addr, mci_base_addr, dma); + dma_recovery.download_image_to_mbox(FW_IMAGE_INDEX, true) } } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 09123eb00a..7f13b148fd 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -44,6 +44,7 @@ cfg-if.workspace = true [dev-dependencies] caliptra-api.workspace = true caliptra-builder.workspace = true +caliptra-emu-bus.workspace = true caliptra-hw-model.workspace = true caliptra-image-elf.workspace = true caliptra-image-fake-keys.workspace = true diff --git a/runtime/build.sh b/runtime/build.sh index 4d38168cfd..a931d1b897 100755 --- a/runtime/build.sh +++ b/runtime/build.sh @@ -1,6 +1,5 @@ -# Licensed under the Apache-2.0 license - #!/bin/bash +# Licensed under the Apache-2.0 license cd "$(dirname "${BASH_SOURCE[0]}")" @@ -9,4 +8,5 @@ cargo build \ --target riscv32imc-unknown-none-elf \ --profile=firmware \ --no-default-features \ + --features riscv \ --bin=caliptra-runtime diff --git a/runtime/src/recovery_flow.rs b/runtime/src/recovery_flow.rs index bbf29c54f5..2715210731 100644 --- a/runtime/src/recovery_flow.rs +++ b/runtime/src/recovery_flow.rs @@ -38,11 +38,14 @@ impl RecoveryFlow { const MCU_FIRMWARE_INDEX: u32 = 2; let dma = &drivers.dma; - let dma_recovery = - DmaRecovery::new(drivers.soc_ifc.recovery_interface_base_addr().into(), dma); + let dma_recovery = DmaRecovery::new( + drivers.soc_ifc.recovery_interface_base_addr().into(), + drivers.soc_ifc.mci_base_addr().into(), + dma, + ); // // download SoC manifest - let _soc_size_bytes = dma_recovery.download_image_to_mbox(SOC_MANIFEST_INDEX)?; + let _soc_size_bytes = dma_recovery.download_image_to_mbox(SOC_MANIFEST_INDEX, false)?; let Ok((manifest, _)) = AuthorizationManifest::read_from_prefix(drivers.mbox.raw_mailbox_contents()) else { return Err(CaliptraError::IMAGE_VERIFIER_ERR_MANIFEST_SIZE_MISMATCH); }; @@ -55,11 +58,15 @@ impl RecoveryFlow { .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)?; + let _mcu_size_bytes = dma_recovery.download_image_to_mcu(MCU_FIRMWARE_INDEX, false)?; // [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 + + // notify MCU that it can boot + // TODO: get the correct value for this + dma_recovery.set_mci_flow_status(123); Ok(()) } } diff --git a/runtime/tests/runtime_integration_tests/common.rs b/runtime/tests/runtime_integration_tests/common.rs index b540ba1eee..130576f513 100644 --- a/runtime/tests/runtime_integration_tests/common.rs +++ b/runtime/tests/runtime_integration_tests/common.rs @@ -62,6 +62,10 @@ pub struct RuntimeTestArgs<'a> { pub test_image_options: Option, pub init_params: Option>, pub test_mfg_flags: Option, + // SoC manifest passed via the recovery interface + pub soc_manifest: Option<&'a [u8]>, + // MCU firmware image passed via the recovery interface + pub mcu_fw_image: Option<&'a [u8]>, } pub fn run_rt_test_lms(args: RuntimeTestArgs) -> DefaultHwModel { @@ -128,6 +132,8 @@ pub fn run_rt_test_lms(args: RuntimeTestArgs) -> DefaultHwModel { ..Default::default() }, initial_dbg_manuf_service_reg: boot_flags, + soc_manifest: args.soc_manifest, + mcu_fw_image: args.mcu_fw_image, ..Default::default() }, ) @@ -205,6 +211,8 @@ pub fn run_rt_test_pqc( ..Default::default() }, initial_dbg_manuf_service_reg: boot_flags, + soc_manifest: args.soc_manifest, + mcu_fw_image: args.mcu_fw_image, ..Default::default() }, ) diff --git a/runtime/tests/runtime_integration_tests/main.rs b/runtime/tests/runtime_integration_tests/main.rs index 417946cda3..591d3aa336 100644 --- a/runtime/tests/runtime_integration_tests/main.rs +++ b/runtime/tests/runtime_integration_tests/main.rs @@ -17,6 +17,7 @@ mod test_panic_missing; mod test_pauser_privilege_levels; mod test_pcr; mod test_populate_idev; +mod test_recovery_flow; mod test_set_auth_manifest; mod test_stash_measurement; mod test_tagging; diff --git a/runtime/tests/runtime_integration_tests/test_recovery_flow.rs b/runtime/tests/runtime_integration_tests/test_recovery_flow.rs new file mode 100644 index 0000000000..158d1d3831 --- /dev/null +++ b/runtime/tests/runtime_integration_tests/test_recovery_flow.rs @@ -0,0 +1,36 @@ +// Licensed under the Apache-2.0 license +use crate::common::{run_rt_test, RuntimeTestArgs}; +#[cfg(all(not(feature = "verilator"), not(feature = "fpga_realtime")))] +use caliptra_emu_bus::{Device, EventData}; +use caliptra_hw_model::{HwModel, InitParams}; + +const RT_READY_FOR_COMMANDS: u32 = 0x600; + +#[cfg(all(not(feature = "verilator"), not(feature = "fpga_realtime")))] +#[test] +fn test_loads_mcu_fw() { + // Test that the recovery flow runs and loads MCU's firmware + let soc_manifest = vec![0x12u8; 128]; + let mcu_fw = vec![0x34u8; 128]; + let mut args = RuntimeTestArgs::default(); + let rom = caliptra_builder::rom_for_fw_integration_tests().unwrap(); + args.init_params = Some(InitParams { + rom: &rom, + active_mode: true, + ..Default::default() + }); + args.soc_manifest = Some(&soc_manifest); + args.mcu_fw_image = Some(&mcu_fw); + let mut model = run_rt_test(args); + model.step_until_boot_status(RT_READY_FOR_COMMANDS, true); + // check that we got an MCU write + let events = model.events_from_caliptra(); + let mut found = false; + for event in events { + if event.dest == Device::MCU && matches!(event.event, EventData::MemoryWrite { .. }) { + found = true; + break; + } + } + assert!(found); +} diff --git a/sw-emulator/app/src/main.rs b/sw-emulator/app/src/main.rs index 36d32df6c5..d6e6fb08ca 100644 --- a/sw-emulator/app/src/main.rs +++ b/sw-emulator/app/src/main.rs @@ -328,7 +328,7 @@ fn main() -> io::Result<()> { 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); + root_bus.dma.axi.recovery.cms_data = vec![current_fw_buf.as_ref().clone()]; } let soc_ifc = unsafe { diff --git a/sw-emulator/lib/types/src/bus.rs b/sw-emulator/lib/bus/src/bus.rs similarity index 76% rename from sw-emulator/lib/types/src/bus.rs rename to sw-emulator/lib/bus/src/bus.rs index 2b7019ceba..ac64450d1d 100644 --- a/sw-emulator/lib/types/src/bus.rs +++ b/sw-emulator/lib/bus/src/bus.rs @@ -12,7 +12,22 @@ Abstract: --*/ -use crate::{RvAddr, RvData, RvSize}; +use crate::Event; + +use caliptra_emu_types::{RvAddr, RvData, RvSize}; +use std::{rc::Rc, sync::mpsc}; + +pub trait EventListener { + fn incoming_event(&mut self, _event: Rc) { + // By default, do nothing + } +} + +pub trait EventSender { + fn register_outgoing_events(&mut self, _sender: mpsc::Sender) { + // By default, do nothing + } +} #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum BusError { @@ -74,4 +89,12 @@ pub trait Bus { fn update_reset(&mut self) { // By default, do nothing } + + fn incoming_event(&mut self, _event: Rc) { + // By default, do nothing + } + + fn register_outgoing_events(&mut self, _sender: mpsc::Sender) { + // By default, do nothing + } } diff --git a/sw-emulator/lib/bus/src/event.rs b/sw-emulator/lib/bus/src/event.rs new file mode 100644 index 0000000000..7ed0246bc9 --- /dev/null +++ b/sw-emulator/lib/bus/src/event.rs @@ -0,0 +1,91 @@ +// Licensed under the Apache-2.0 license. + +#[derive(Clone, Debug, PartialEq)] +pub struct Event { + pub src: Device, + pub dest: Device, + pub event: EventData, +} + +impl Event { + pub fn new(src: Device, dest: Device, event: EventData) -> Self { + Self { src, dest, event } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Device { + CaliptraCore, + MCU, + BMC, + External(&'static str), +} + +#[derive(Clone, Debug, PartialEq)] +pub enum EventData { + WireRequest { + name: &'static str, + }, + WireValue { + name: &'static str, + value: u32, + }, + MemoryRead { + start_addr: u32, + len: u32, + }, + MemoryReadResponse { + start_addr: u32, + data: Vec, + }, + MemoryWrite { + start_addr: u32, + data: Vec, + }, + RecoveryBlockReadRequest { + source_addr: u8, + target_addr: u8, + command_code: RecoveryCommandCode, + }, + RecoveryBlockReadResponse { + source_addr: u8, + target_addr: u8, + command_code: RecoveryCommandCode, + payload: Vec, + }, + RecoveryBlockWrite { + source_addr: u8, + target_addr: u8, + command_code: RecoveryCommandCode, + payload: Vec, + }, + RecoveryImageAvailable { + image_id: u8, + image: Vec, + }, + I3CBlock { + source_addr: u8, + dest_addr: u8, + ibi: u8, + descriptor: [u8; 8], + data: Vec, + }, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum RecoveryCommandCode { + ProtCap, + DeviceId, + DeviceStatus, + DeviceReset, + RecoveryCtrl, + RecoveryStatus, + HwStatus, + IndirectCtrl, + IndirectStatus, + IndirectData, + Vendor, + IndirectFifoCtrl, + IndirectFifoStatus, + IndirectFifoData, +} diff --git a/sw-emulator/lib/bus/src/lib.rs b/sw-emulator/lib/bus/src/lib.rs index c54d9e5dd8..927ab5ccf6 100644 --- a/sw-emulator/lib/bus/src/lib.rs +++ b/sw-emulator/lib/bus/src/lib.rs @@ -11,8 +11,10 @@ Abstract: File contains exports for for Caliptra Emulator Bus library. --*/ +mod bus; mod clock; mod dynamic_bus; +mod event; mod mem; mod mmio; mod ram; @@ -21,8 +23,10 @@ mod register_array; mod rom; pub mod testing; +pub use crate::bus::{Bus, BusError}; pub use crate::clock::{ActionHandle, Clock, Timer, TimerAction}; pub use crate::dynamic_bus::DynamicBus; +pub use crate::event::{Device, Event, EventData, RecoveryCommandCode}; pub use crate::mmio::BusMmio; pub use crate::ram::Ram; pub use crate::register::{ @@ -31,4 +35,3 @@ pub use crate::register::{ }; pub use crate::register_array::{ReadWriteRegisterArray, RegisterArray}; pub use crate::rom::Rom; -pub use caliptra_emu_types::bus::{Bus, BusError}; diff --git a/sw-emulator/lib/cpu/src/cpu.rs b/sw-emulator/lib/cpu/src/cpu.rs index b4aae462c2..64732e3c8d 100644 --- a/sw-emulator/lib/cpu/src/cpu.rs +++ b/sw-emulator/lib/cpu/src/cpu.rs @@ -17,8 +17,10 @@ use crate::instr::Instr; use crate::types::{RvInstr, RvMEIHAP, RvMStatus, RvMemAccessType, RvMsecCfg, RvPrivMode}; use crate::xreg_file::{XReg, XRegFile}; use bit_vec::BitVec; -use caliptra_emu_bus::{Bus, BusError, Clock, TimerAction}; +use caliptra_emu_bus::{Bus, BusError, Clock, Event, EventData, TimerAction}; use caliptra_emu_types::{RvAddr, RvData, RvException, RvSize}; +use std::rc::Rc; +use std::sync::mpsc; pub type InstrTracer<'a> = dyn FnMut(u32, RvInstr) + 'a; @@ -283,6 +285,11 @@ pub struct Cpu { pub code_coverage: CodeCoverage, stack_info: Option, + + // incoming communication with other components + incoming_events: Option>, + // events sent to other components + outgoing_events: Option>, } impl Drop for Cpu { @@ -336,6 +343,8 @@ impl Cpu { // isn't supposed to know anything about the caliptra memory map) code_coverage: CodeCoverage::new(ROM_SIZE, ICCM_SIZE), stack_info: None, + incoming_events: None, + outgoing_events: None, } } @@ -652,10 +661,14 @@ impl Cpu { return StepAction::Continue; } - match self.exec_instr(instr_tracer) { + let action = match self.exec_instr(instr_tracer) { Ok(result) => result, Err(exception) => self.handle_exception(exception), - } + }; + + // handle incoming events at this point, if there are any + self.handle_incoming_events(); + action } /// Handle synchronous exception @@ -896,6 +909,36 @@ impl Cpu { Ok(()) } + + // returns a sender (that sends events to this CPU) + // and a receiver (that receives events that this CPU sends) + pub fn register_events(&mut self) -> (mpsc::Sender, mpsc::Receiver) { + let (tx, rx) = mpsc::channel(); + let (tx2, rx2) = mpsc::channel(); + + self.incoming_events = Some(rx); + // let the bus be able to send events + self.bus.register_outgoing_events(tx2.clone()); + self.outgoing_events = Some(tx2); + (tx, rx2) + } + + fn handle_incoming_events(&mut self) { + if let Some(incoming_events) = &self.incoming_events { + for event in incoming_events.try_iter() { + match event.event { + EventData::MemoryRead { .. } => { + panic!("Caliptra core does not support memory read requests"); + } + EventData::MemoryWrite { .. } => { + panic!("Caliptra core does not support memory write requests"); + } + _ => {} + } + self.bus.incoming_event(Rc::new(event)); + } + } + } } #[cfg(test)] diff --git a/sw-emulator/lib/derive/src/bus.rs b/sw-emulator/lib/derive/src/bus.rs index 25ad9281a0..e9a542c812 100644 --- a/sw-emulator/lib/derive/src/bus.rs +++ b/sw-emulator/lib/derive/src/bus.rs @@ -12,11 +12,10 @@ Abstract: fields of a struct. --*/ -use std::collections::HashMap; use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; - use quote::{format_ident, quote}; +use std::collections::HashMap; use crate::util::literal::{self, hex_literal_u32}; use crate::util::sort::sorted_by_key; @@ -28,9 +27,11 @@ use crate::util::token_iter::{ pub fn derive_bus(input: TokenStream) -> TokenStream { let mut iter = input.into_iter(); let struct_attrs = skip_to_struct_with_attributes(&mut iter); - 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 poll_fn = get_fn(&struct_attrs, "poll_fn"); + let warm_reset_fn = get_fn(&struct_attrs, "warm_reset_fn"); + let update_reset_fn = get_fn(&struct_attrs, "update_reset_fn"); + let incoming_event_fn = get_fn(&struct_attrs, "incoming_event_fn"); + let register_outgoing_events_fn = get_fn(&struct_attrs, "register_outgoing_events_fn"); 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()); @@ -68,6 +69,22 @@ pub fn derive_bus(input: TokenStream) -> TokenStream { } else { quote! {} }; + let self_incoming_event_tokens = if let Some(incoming_event_fn) = &incoming_event_fn { + let incoming_event_fn = Ident::new(incoming_event_fn, Span::call_site()); + quote! { + Self::#incoming_event_fn(self, event); + } + } else { + quote! {} + }; + let self_register_outgoing_events_tokens = + if let Some(register_outgoing_events_fn) = ®ister_outgoing_events_fn { + let register_outgoing_events_fn = + Ident::new(register_outgoing_events_fn, Span::call_site()); + quote! { Self::#register_outgoing_events_fn(self, sender); } + } else { + quote! {} + }; let field_idents: Vec<_> = peripheral_fields .iter() @@ -98,48 +115,23 @@ pub fn derive_bus(input: TokenStream) -> TokenStream { #(self.#field_idents.update_reset();)* #self_update_reset_tokens } - - } - } -} - -fn get_poll_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 == "poll_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()); - } - } + fn incoming_event(&mut self, event: std::rc::Rc) { + #(self.#field_idents.incoming_event(event.clone());)* + #self_incoming_event_tokens } - } - } - None -} - -fn get_warm_reset_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 == "warm_reset_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()); - } - } + fn register_outgoing_events(&mut self, sender: std::sync::mpsc::Sender) { + #(self.#field_idents.register_outgoing_events(sender.clone());)* + #self_register_outgoing_events_tokens } } } - None } -fn get_update_reset_fn(struct_attrs: &[Group]) -> Option { +fn get_fn(struct_attrs: &[Group], name: &str) -> Option { for attr in struct_attrs { let mut iter = attr.stream().into_iter(); if let Some(TokenTree::Ident(ident)) = iter.next() { - if ident == "update_reset_fn" { + if ident == name { if let Some(TokenTree::Group(group)) = iter.next() { if let Some(TokenTree::Ident(ident)) = group.stream().into_iter().next() { return Some(ident.to_string()); @@ -795,6 +787,28 @@ mod tests { self.i2c2.update_reset(); self.spi0.update_reset(); } + fn incoming_event(&mut self, event: std::rc::Rc) { + self.rom.incoming_event(event.clone()); + self.sram.incoming_event(event.clone()); + self.dram.incoming_event(event.clone()); + self.uart0.incoming_event(event.clone()); + self.uart1.incoming_event(event.clone()); + self.i2c0.incoming_event(event.clone()); + self.i2c1.incoming_event(event.clone()); + self.i2c2.incoming_event(event.clone()); + self.spi0.incoming_event(event.clone()); + } + fn register_outgoing_events(&mut self, sender: std::sync::mpsc::Sender) { + self.rom.register_outgoing_events(sender.clone()); + self.sram.register_outgoing_events(sender.clone()); + self.dram.register_outgoing_events(sender.clone()); + self.uart0.register_outgoing_events(sender.clone()); + self.uart1.register_outgoing_events(sender.clone()); + self.i2c0.register_outgoing_events(sender.clone()); + self.i2c1.register_outgoing_events(sender.clone()); + self.i2c2.register_outgoing_events(sender.clone()); + self.spi0.register_outgoing_events(sender.clone()); + } } }.to_string() ); @@ -821,6 +835,10 @@ mod tests { } fn update_reset(&mut self) { } + fn incoming_event(&mut self, event: std::rc::Rc) { + } + fn register_outgoing_events(&mut self, sender: std::sync::mpsc::Sender) { + } } }.to_string() ); diff --git a/sw-emulator/lib/derive/src/lib.rs b/sw-emulator/lib/derive/src/lib.rs index 685b64818c..cbff427818 100644 --- a/sw-emulator/lib/derive/src/lib.rs +++ b/sw-emulator/lib/derive/src/lib.rs @@ -23,8 +23,10 @@ use proc_macro::TokenStream; poll_fn, warm_reset_fn, update_reset_fn, + incoming_event_fn, + register_outgoing_events_fn, register, - register_array + register_array, ) )] pub fn derive_bus(input: TokenStream) -> TokenStream { diff --git a/sw-emulator/lib/periph/Cargo.toml b/sw-emulator/lib/periph/Cargo.toml index e003a3cdb1..19edb15c74 100644 --- a/sw-emulator/lib/periph/Cargo.toml +++ b/sw-emulator/lib/periph/Cargo.toml @@ -19,6 +19,7 @@ caliptra-emu-derive.workspace = true caliptra-emu-types.workspace = true caliptra-hw-model-types.workspace = true caliptra-registers.workspace = true +const-random.workspace = true fips204.workspace = true lazy_static.workspace = true rand.workspace = true diff --git a/sw-emulator/lib/periph/src/dma.rs b/sw-emulator/lib/periph/src/dma.rs index 288d7d94cf..179c60bdea 100644 --- a/sw-emulator/lib/periph/src/dma.rs +++ b/sw-emulator/lib/periph/src/dma.rs @@ -14,13 +14,15 @@ File contains DMA peripheral implementation. use crate::{MailboxRam, SocRegistersInternal}; use caliptra_emu_bus::{ - ActionHandle, Bus, BusError, Clock, ReadOnlyRegister, ReadWriteRegister, Timer, + ActionHandle, Bus, BusError, Clock, Event, ReadOnlyRegister, ReadWriteRegister, Timer, WriteOnlyRegister, }; use caliptra_emu_derive::Bus; use caliptra_emu_types::{RvAddr, RvData, RvSize}; use std::borrow::BorrowMut; use std::collections::VecDeque; +use std::rc::Rc; +use std::sync::mpsc; use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::register_bitfields; @@ -28,7 +30,7 @@ pub mod axi_root_bus; use axi_root_bus::{AxiAddr, AxiRootBus}; pub mod mci; pub mod otp_fc; -mod recovery; +pub mod recovery; const RECOVERY_STATUS_OFFSET: u64 = 0x40; const AWATING_RECOVERY_IMAGE: u32 = 0x1; @@ -89,6 +91,8 @@ register_bitfields! [ #[derive(Bus)] #[poll_fn(poll)] +#[incoming_event_fn(incoming_event)] +#[register_outgoing_events_fn(register_outgoing_events)] pub struct Dma { /// ID #[register(offset = 0x0000_0000)] @@ -160,6 +164,11 @@ pub struct Dma { /// Mailbox mailbox: MailboxRam, + + // Ongoing DMA operations + pending_axi_to_axi: Option, + pending_axi_to_fifo: bool, + pending_axi_to_mailbox: bool, } struct ReadXfer { @@ -210,6 +219,9 @@ impl Dma { fifo: VecDeque::with_capacity(Self::FIFO_SIZE), axi: AxiRootBus::new(soc_reg, prod_dbg_unlock_keypairs), mailbox, + pending_axi_to_axi: None, + pending_axi_to_fifo: false, + pending_axi_to_mailbox: false, } } @@ -302,53 +314,103 @@ impl Dma { } } - fn axi_to_mailbox(&mut self) { + // Returns true if this completed immediately. + fn axi_to_mailbox(&mut self) -> bool { let xfer = self.read_xfer(); - let mbox_ram = self.mailbox.borrow_mut(); - 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(); + // check if we have to do the read async + if self.axi.must_schedule(xfer.src) { + self.pending_axi_to_mailbox = true; + self.timer.schedule_poll_in(1); + self.axi.schedule_read(xfer.src, xfer.len as u32).unwrap(); + return false; + } + + let block = self.read_axi_block(xfer); + self.write_mailbox(&block); + true + } + + fn write_mailbox(&mut self, block: &[u8]) { + let mbox_ram = self.mailbox.borrow_mut(); + for i in (0..block.len()).step_by(Self::AXI_DATA_WIDTH) { + let data = u32::from_le_bytes(block[i..i + Self::AXI_DATA_WIDTH].try_into().unwrap()); mbox_ram .write(Self::AXI_DATA_WIDTH.into(), i as RvAddr, data as RvData) .unwrap(); } } - fn axi_to_fifo(&mut self) { + // Returns true if this completed immediately. + fn axi_to_fifo(&mut self) -> bool { 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 }; + // check if we have to do the read async + if self.axi.must_schedule(xfer.src) { + self.pending_axi_to_fifo = true; + self.timer.schedule_poll_in(1); + self.axi.schedule_read(xfer.src, xfer.len as u32).unwrap(); + return false; + } + + let block = self.read_axi_block(xfer); + self.write_fifo_block(&block); + true + } + + fn write_fifo_block(&mut self, block: &[u8]) { + for i in (0..block.len()).step_by(Self::AXI_DATA_WIDTH) { 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; } - 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)); + self.fifo.extend(&block[i..i + Self::AXI_DATA_WIDTH]); } } - fn axi_to_axi(&mut self) { + // Returns true if this completed immediately. + fn axi_to_axi(&mut self) -> bool { let read_xfer = self.read_xfer(); let write_xfer = self.write_xfer(); + // check if we have to do the read async + if self.axi.must_schedule(read_xfer.src) { + self.pending_axi_to_axi = Some(write_xfer); + self.timer.schedule_poll_in(1); + self.axi + .schedule_read(read_xfer.src, read_xfer.len as u32) + .unwrap(); + return false; + } + let data = self.read_axi_block(read_xfer); + self.write_axi_block(&data, write_xfer); + true + } + + fn read_axi_block(&mut self, read_xfer: ReadXfer) -> Vec { + let mut block = vec![]; 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(); + block.extend(data.to_le_bytes()); + } + block + } + + fn write_axi_block(&mut self, block: &[u8], write_xfer: WriteXfer) { + for i in (0..write_xfer.len).step_by(Self::AXI_DATA_WIDTH) { + let dest = write_xfer.dest + if write_xfer.fixed { 0 } else { i as AxiAddr }; + let data = u32::from_le_bytes(block[i..i + Self::AXI_DATA_WIDTH].try_into().unwrap()); self.axi .write(Self::AXI_DATA_WIDTH.into(), dest, data) .unwrap(); } } - fn mailbox_to_axi(&mut self) { + // Returns true if this completed immediately. + fn mailbox_to_axi(&mut self) -> bool { let xfer = self.write_xfer(); let mbox_ram = self.mailbox.borrow_mut(); @@ -361,9 +423,11 @@ impl Dma { .write(Self::AXI_DATA_WIDTH.into(), addr, data) .unwrap(); } + true } - fn fifo_to_axi(&mut self) { + // Returns true if this completed immediately. + fn fifo_to_axi(&mut self) -> bool { 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 }; @@ -377,7 +441,7 @@ impl Dma { None => { self.status0.reg.write(Status0::ERROR::SET); // TODO set interrupt bits - return; + return true; } } } @@ -387,10 +451,8 @@ impl Dma { .write(Self::AXI_DATA_WIDTH.into(), addr, data) .unwrap(); - // Check if FW is inficating that it is ready to receive the recovery image. - if addr - == axi_root_bus::AxiRootBus::RECOVERY_REGISTER_INTERFACE_OFFSET - + RECOVERY_STATUS_OFFSET + // Check if FW is indicating that it is ready to receive the recovery image. + if ((addr & RECOVERY_STATUS_OFFSET) == RECOVERY_STATUS_OFFSET) && ((data & AWATING_RECOVERY_IMAGE) == AWATING_RECOVERY_IMAGE) { self.status0.reg.modify(Status0::PAYLOAD_AVAILABLE::CLEAR); @@ -399,13 +461,14 @@ impl Dma { Some(self.timer.schedule_poll_in(PAYLOAD_AVAILABLE_OP_TICKS)); } } + true } 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) { + let complete = match (read_target, write_origin) { (Control::READ_ROUTE::Value::MAILBOX, Control::WRITE_ROUTE::Value::DISABLE) => { self.axi_to_mailbox() } @@ -422,8 +485,14 @@ impl Dma { self.fifo_to_axi() } (_, _) => panic!("Invalid read/write DMA combination"), + }; + + if complete { + self.set_status_complete(); } + } + fn set_status_complete(&mut self) { self.status0 .reg .modify(Status0::BUSY::CLEAR + Status0::DMA_FSM_PRESENT_STATE::DONE); @@ -436,6 +505,34 @@ impl Dma { if self.timer.fired(&mut self.op_payload_available_action) { self.status0.reg.modify(Status0::PAYLOAD_AVAILABLE::SET); } + if let Some(dma_data) = self.axi.dma_result.take() { + if let Some(write_xfer) = self.pending_axi_to_axi.take() { + self.write_axi_block(&dma_data, write_xfer); + self.set_status_complete(); + } else if self.pending_axi_to_fifo { + self.write_fifo_block(&dma_data); + self.set_status_complete(); + self.pending_axi_to_fifo = false; + } else if self.pending_axi_to_mailbox { + self.write_mailbox(&dma_data); + self.set_status_complete(); + self.pending_axi_to_mailbox = false; + } + } else if self.pending_axi_to_axi.is_some() + || self.pending_axi_to_fifo + || self.pending_axi_to_mailbox + { + // check again next cycle + self.timer.schedule_poll_in(1); + } + } + + fn incoming_event(&mut self, event: Rc) { + self.axi.incoming_event(event); + } + + fn register_outgoing_events(&mut self, sender: mpsc::Sender) { + self.axi.register_outgoing_events(sender); } } diff --git a/sw-emulator/lib/periph/src/dma/axi_root_bus.rs b/sw-emulator/lib/periph/src/dma/axi_root_bus.rs index d3a25de21f..0da34fd9e7 100644 --- a/sw-emulator/lib/periph/src/dma/axi_root_bus.rs +++ b/sw-emulator/lib/periph/src/dma/axi_root_bus.rs @@ -12,24 +12,28 @@ Abstract: --*/ +use crate::dma::otp_fc::FuseController; +use crate::dma::recovery::RecoveryRegisterInterface; +use crate::SocRegistersInternal; use caliptra_emu_bus::{ - Bus, BusError, BusError::LoadAccessFault, BusError::StoreAccessFault, Register, + Bus, + BusError::{self, LoadAccessFault, StoreAccessFault}, + Device, Event, EventData, Register, }; use caliptra_emu_types::{RvAddr, RvData, RvSize}; +use const_random::const_random; +use std::{rc::Rc, sync::mpsc}; pub type AxiAddr = u64; -use crate::dma::otp_fc::FuseController; -use crate::dma::recovery::RecoveryRegisterInterface; -use crate::SocRegistersInternal; - use super::mci::Mci; pub struct AxiRootBus { pub reg: u32, pub recovery: RecoveryRegisterInterface, - + event_sender: Option>, + pub dma_result: Option>, pub otp_fc: FuseController, pub mci: Mci, @@ -37,14 +41,18 @@ pub struct AxiRootBus { 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 const OTC_FC_OFFSET: AxiAddr = 0xf_00001000; - pub const OTC_FC_END: AxiAddr = 0xf_00001fff; - - pub const SS_MCI_OFFSET: AxiAddr = 0x10_00000000; - pub const SS_MCI_END: AxiAddr = 0x10_00000fff; + // ROM and Runtime code should not depend on the exact values of these. + pub const RECOVERY_REGISTER_INTERFACE_OFFSET: AxiAddr = + const_random!(u64) & 0xffffffff_00000000; + pub const RECOVERY_REGISTER_INTERFACE_END: AxiAddr = + Self::RECOVERY_REGISTER_INTERFACE_OFFSET + 0xff; + pub const SS_MCI_OFFSET: AxiAddr = const_random!(u64) & 0xffffffff_00000000; + pub const SS_MCI_END: AxiAddr = Self::SS_MCI_OFFSET + 0xfff; + pub const MCU_SRAM_OFFSET: AxiAddr = Self::SS_MCI_OFFSET + 0x20_0000; + pub const MCU_SRAM_END: AxiAddr = Self::MCU_SRAM_OFFSET + 2 * 1024 * 1024 - 1; // the aperture size is 2 MB even though the underlying SRAM may be smaller + + pub const OTC_FC_OFFSET: AxiAddr = (const_random!(u64) & 0xffffffff_00000000) + 0x1000; + pub const OTC_FC_END: AxiAddr = Self::OTC_FC_OFFSET + 0xfff; pub fn new( soc_reg: SocRegistersInternal, @@ -55,6 +63,37 @@ impl AxiRootBus { recovery: RecoveryRegisterInterface::new(), otp_fc: FuseController::new(soc_reg), mci: Mci::new(prod_dbg_unlock_keypairs), + event_sender: None, + dma_result: None, + } + } + + pub fn must_schedule(&mut self, addr: AxiAddr) -> bool { + matches!(addr, Self::MCU_SRAM_OFFSET..=Self::MCU_SRAM_END) + } + + pub fn schedule_read(&mut self, addr: AxiAddr, len: u32) -> Result<(), BusError> { + if self.dma_result.is_some() { + println!("Cannot schedule read if previous DMA result has not been consumed"); + return Err(BusError::LoadAccessFault); + } + match addr { + Self::MCU_SRAM_OFFSET..=Self::MCU_SRAM_END => { + if let Some(sender) = self.event_sender.as_mut() { + sender + .send(Event::new( + Device::CaliptraCore, + Device::MCU, + EventData::MemoryRead { + start_addr: addr as u32, + len, + }, + )) + .unwrap(); + } + Ok(()) + } + _ => Err(BusError::LoadAccessFault), } } @@ -81,22 +120,54 @@ impl AxiRootBus { 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::TEST_REG_OFFSET => 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); + Bus::write(&mut self.recovery, size, addr, val) + } + Self::MCU_SRAM_OFFSET..=Self::MCU_SRAM_END => { + if let Some(sender) = self.event_sender.as_mut() { + sender + .send(Event::new( + Device::CaliptraCore, + Device::MCU, + EventData::MemoryWrite { + start_addr: (addr - Self::MCU_SRAM_OFFSET) as u32, + data: val.to_le_bytes().to_vec(), + }, + )) + .unwrap(); + } + Ok(()) } Self::OTC_FC_OFFSET..=Self::OTC_FC_END => { let addr = (addr - Self::OTC_FC_OFFSET) as RvAddr; - return Bus::write(&mut self.otp_fc, size, addr, val); + Bus::write(&mut self.otp_fc, size, addr, val) } Self::SS_MCI_OFFSET..=Self::SS_MCI_END => { - let addr = (addr - Self::RECOVERY_REGISTER_INTERFACE_OFFSET) as RvAddr; - return Bus::write(&mut self.mci, size, addr, val); + let addr = (addr - Self::SS_MCI_OFFSET) as RvAddr; + Bus::write(&mut self.mci, size, addr, val) + } + _ => Err(StoreAccessFault), + } + } + + pub fn incoming_event(&mut self, event: Rc) { + self.recovery.incoming_event(event.clone()); + if let EventData::MemoryReadResponse { + start_addr: _, + data, + } = &event.event + { + // we only access read responses from the MCU + if event.src == Device::MCU { + self.dma_result = Some(data.clone()); } - _ => {} } + } - Err(StoreAccessFault) + pub fn register_outgoing_events(&mut self, sender: mpsc::Sender) { + self.event_sender = Some(sender.clone()); + self.recovery.register_outgoing_events(sender); } } diff --git a/sw-emulator/lib/periph/src/dma/mci.rs b/sw-emulator/lib/periph/src/dma/mci.rs index 9c2f71d241..69ed9fd225 100644 --- a/sw-emulator/lib/periph/src/dma/mci.rs +++ b/sw-emulator/lib/periph/src/dma/mci.rs @@ -21,6 +21,8 @@ const SS_MANUF_DBG_UNLOCK_NUMBER_OF_FUSES: usize = 4; #[derive(Bus)] pub struct Mci { + #[register(offset = 0x24)] + flow_status: u32, #[register_array(offset = 0xa00)] fuses: [u32; SS_MANUF_DBG_UNLOCK_FUSE_SIZE / size_of::() * SS_MANUF_DBG_UNLOCK_NUMBER_OF_FUSES], @@ -32,6 +34,7 @@ impl Mci { pub fn new(key_pairs: Vec<(&[u8; 96], &[u8; 2592])>) -> Self { Self { + flow_status: 0, fuses: { let mut fuses = [0; SS_MANUF_DBG_UNLOCK_FUSE_SIZE / size_of::() * SS_MANUF_DBG_UNLOCK_NUMBER_OF_FUSES]; diff --git a/sw-emulator/lib/periph/src/dma/recovery.rs b/sw-emulator/lib/periph/src/dma/recovery.rs index 4dcc4e1b75..91e3582287 100644 --- a/sw-emulator/lib/periph/src/dma/recovery.rs +++ b/sw-emulator/lib/periph/src/dma/recovery.rs @@ -12,11 +12,13 @@ Abstract: --*/ -use caliptra_emu_bus::{BusError, ReadOnlyRegister, ReadWriteRegister}; +use caliptra_emu_bus::{ + BusError, Event, EventData, ReadOnlyRegister, ReadWriteRegister, RecoveryCommandCode, +}; use caliptra_emu_derive::Bus; use caliptra_emu_types::{RvData, RvSize}; -use std::mem; use std::rc::Rc; +use std::sync::mpsc; use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::register_bitfields; @@ -24,7 +26,7 @@ register_bitfields! [ u32, /// Recovery Control - RecoveryControl [ + pub RecoveryControl [ CMS OFFSET(0) NUMBITS(8) [], IMAGE_SELECTION OFFSET(8) NUMBITS(8) [ NoOperation = 0, @@ -39,23 +41,24 @@ register_bitfields! [ ], /// Recovery Status - RecoveryStatus [ - DEVICE_RECOVERY OFFSET(0) NUMBITS(8) [ + pub RecoveryStatus [ + DEVICE_RECOVERY OFFSET(0) NUMBITS(4) [ NotInRecovery = 0x0, AwaitingRecoveryImage = 0x1, BootingRecoveryImage = 0x2, - RecoverySuccesfull = 0x3, + RecoverySuccessful = 0x3, RecoveryFailed = 0xc, AuthenticationError = 0xd, ErrorEnteringRecovery = 0xe, InvalidComponentAddressSpace = 0xf, // 0x10-ff Reserved ], + RECOVERY_IMAGE_INDEX OFFSET(4) NUMBITS(4) [], VENDOR_SPECIFIC OFFSET(8) NUMBITS(8) [], ], /// HW Status - HwStatus [ + pub HwStatus [ HW_STATUS OFFSET(0) NUMBITS(8) [ TemperatureCritial = 0x0, HardwareSoftError = 0x1, @@ -73,13 +76,17 @@ register_bitfields! [ ], /// Indirect FIFO Control - IndirectCtrl [ + pub IndirectCtrl0 [ CMS OFFSET(0) NUMBITS(8), RESET OFFSET(8) NUMBITS(1), + LEN23 OFFSET(16) NUMBITS(16), + ], + pub IndirectCtrl1 [ + LEN01 OFFSET(0) NUMBITS(16), ], /// Indirect FIFO Status - IndirectStatus [ + pub IndirectStatus [ FIFO_EMPTY OFFSET(0) NUMBITS(1) [], FIFO_FULL OFFSET(1) NUMBITS(1) [], REGION_TYPE OFFSET(8) NUMBITS(3) [ @@ -94,6 +101,8 @@ register_bitfields! [ /// Recovery register interface #[derive(Bus)] +#[incoming_event_fn(incoming_event)] +#[register_outgoing_events_fn(register_outgoing_events)] pub struct RecoveryRegisterInterface { // Capability registers #[register(offset = 0x0)] @@ -139,9 +148,9 @@ pub struct RecoveryRegisterInterface { // 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, + pub indirect_fifo_ctrl_0: ReadWriteRegister, + #[register(offset = 0x4c)] + pub indirect_fifo_ctrl_1: ReadOnlyRegister, #[register(offset = 0x50)] pub indirect_fifo_status_0: ReadOnlyRegister, #[register(offset = 0x54)] @@ -157,7 +166,8 @@ pub struct RecoveryRegisterInterface { #[register(offset = 0x68, read_fn = indirect_fifo_data_read)] pub indirect_fifo_data: ReadWriteRegister, - pub cms_data: Option>>, // TODO Multiple images? + pub cms_data: Vec>, + pub event_sender: Option>, } impl RecoveryRegisterInterface { @@ -195,7 +205,7 @@ impl RecoveryRegisterInterface { // Indirect FIFO registers indirect_fifo_ctrl_0: ReadWriteRegister::new(0), - indirect_fifo_ctrl_1: ReadWriteRegister::new(0), + indirect_fifo_ctrl_1: ReadOnlyRegister::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), @@ -204,7 +214,8 @@ impl RecoveryRegisterInterface { indirect_fifo_status_5: ReadWriteRegister::new(0), indirect_fifo_data: ReadWriteRegister::new(0), - cms_data: None, + cms_data: vec![], + event_sender: None, } } @@ -212,15 +223,17 @@ impl RecoveryRegisterInterface { 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, + if self.cms_data.is_empty() { + println!("No image set in RRI"); + return Ok(0xffff_ffff); + } + let image_index = ((self.recovery_status.reg.get() >> 4) & 0xf) as usize; + let Some(image) = self.cms_data.get(image_index) else { + println!("Recovery image index out of bounds"); + return Ok(0xffff_ffff); }; - let cms = self.indirect_fifo_ctrl_0.reg.read(IndirectCtrl::CMS); + let cms = self.indirect_fifo_ctrl_0.reg.read(IndirectCtrl0::CMS); if cms != 0 { println!("CMS {cms} not supported"); return Ok(0xffff_ffff); @@ -254,16 +267,29 @@ impl RecoveryRegisterInterface { 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); + let load: ReadWriteRegister = ReadWriteRegister::new(val); + if load.reg.is_set(IndirectCtrl0::RESET) { + let image_index = ((self.recovery_status.reg.get() >> 4) & 0xf) as usize; + if let Some(image) = self.cms_data.get(image_index) { + let cms = load.reg.read(IndirectCtrl0::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 + let len_dwords = image.len() as u32 / 4; + self.indirect_fifo_ctrl_0 + .reg + .modify(IndirectCtrl0::CMS.val(cms)); + self.indirect_fifo_ctrl_0 + .reg + .modify(IndirectCtrl0::RESET::CLEAR); + self.indirect_fifo_ctrl_0 + .reg + .modify(IndirectCtrl0::LEN23.val(len_dwords >> 16)); + self.indirect_fifo_ctrl_1 + .reg + .modify(IndirectCtrl1::LEN01.val(len_dwords & 0xffff)); self.indirect_fifo_status_0 .reg .set(IndirectStatus::REGION_TYPE::CodeSpaceRecovery.value); @@ -274,7 +300,11 @@ impl RecoveryRegisterInterface { .reg .modify(IndirectStatus::FIFO_EMPTY::CLEAR + IndirectStatus::FIFO_FULL::CLEAR); } else { - println!("No Image in RRI"); + println!( + "No Image in RRI ({} >= {})", + image_index, + self.cms_data.len() + ); self.indirect_fifo_status_0 .reg .set(IndirectStatus::REGION_TYPE::UnsupportedRegion.value); @@ -283,18 +313,101 @@ impl RecoveryRegisterInterface { Ok(()) } - pub fn indirect_fifo_ctrl_1_read(&mut self, size: RvSize) -> Result { - if size != RvSize::Word { - Err(BusError::LoadAccessFault)? - } + pub fn register_outgoing_events(&mut self, sender: mpsc::Sender) { + self.event_sender = Some(sender); + } - match &self.cms_data { - Some(d) => Ok((d.as_ref().len() / mem::size_of::()) as u32), - None => Ok(0), + pub fn incoming_event(&mut self, event: Rc) { + let sender = self + .event_sender + .as_ref() + .expect("Incoming event but we have no sender registered"); + match &event.event { + EventData::RecoveryImageAvailable { image_id, image } => { + let idx = *image_id as usize; + // ensure we have space for the image + if idx >= self.cms_data.len() { + self.cms_data + .extend(std::iter::repeat(vec![]).take(idx - self.cms_data.len() + 1)); + } + while idx >= self.cms_data.len() { + self.cms_data.push(vec![]); + } + // replace any existing image + self.cms_data[idx].clear(); + self.cms_data[idx].extend_from_slice(image); + } + EventData::RecoveryBlockReadRequest { + source_addr, + target_addr, + command_code, + } => { + let resp: Option> = match command_code { + RecoveryCommandCode::ProtCap => to_payload( + &[ + self.prot_cap_0.reg.get(), + self.prot_cap_1.reg.get(), + self.prot_cap_2.reg.get(), + self.prot_cap_3.reg.get(), + ], + 15, + ), + RecoveryCommandCode::DeviceId => to_payload( + &[ + self.device_id_0.reg.get(), + self.device_id_1.reg.get(), + self.device_id_2.reg.get(), + self.device_id_3.reg.get(), + self.device_id_4.reg.get(), + self.device_id_5.reg.get(), + self.device_id_6.reg.get(), + ], + 24, + ), + RecoveryCommandCode::DeviceStatus => to_payload( + &[ + self.device_status_0.reg.get(), + self.device_status_1.reg.get(), + ], + 7, + ), + RecoveryCommandCode::RecoveryStatus => { + to_payload(&[self.recovery_status.reg.get()], 2) + } + RecoveryCommandCode::RecoveryCtrl => { + to_payload(&[self.recovery_ctrl.reg.get()], 3) + } + _ => None, + }; + if let Some(resp) = resp { + sender + .send(Event { + src: event.dest, + dest: event.src, + event: EventData::RecoveryBlockReadResponse { + source_addr: *target_addr, + target_addr: *source_addr, + command_code: *command_code, + payload: resp, + }, + }) + .expect("Could not send event"); + } + } + _ => {} } } } +fn to_payload(data: &[u32], len: usize) -> Option> { + Some( + data.iter() + .flat_map(|x| x.to_le_bytes().to_vec()) + .take(len) + .collect(), + ) +} + impl Default for RecoveryRegisterInterface { fn default() -> Self { Self::new() @@ -308,24 +421,26 @@ mod tests { use super::*; - const INDIRECT_FIFO_CTRL: RvAddr = 0x48; + const INDIRECT_FIFO_CTRL0: RvAddr = 0x48; const INDIRECT_FIFO_RESET: RvData = 0x100; - const INDIRECT_FIFO_IMAGE_SIZE: RvAddr = 0x4c; + const INDIRECT_FIFO_CTRL1: 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 = vec![0xab; 512]; let image_len = image.len(); let mut rri = RecoveryRegisterInterface::new(); - rri.cms_data = Some(image.clone()); + rri.cms_data = vec![image.clone()]; // Reset - rri.write(RvSize::Word, INDIRECT_FIFO_CTRL, INDIRECT_FIFO_RESET) + rri.write(RvSize::Word, INDIRECT_FIFO_CTRL0, INDIRECT_FIFO_RESET) .unwrap(); - let image_size = rri.read(RvSize::Word, INDIRECT_FIFO_IMAGE_SIZE).unwrap(); + let a = rri.read(RvSize::Word, INDIRECT_FIFO_CTRL0).unwrap(); + let b = rri.read(RvSize::Word, INDIRECT_FIFO_CTRL1).unwrap(); + let image_size = (a & 0xffff_0000) | (b & 0xffff); assert_eq!(image_len, image_size as usize * 4); let mut read_image = Vec::new(); @@ -334,6 +449,6 @@ mod tests { let bytes = dword_read.to_le_bytes(); read_image.extend_from_slice(&bytes); } - assert_eq!(read_image, *image); + assert_eq!(read_image, image); } } diff --git a/sw-emulator/lib/periph/src/lib.rs b/sw-emulator/lib/periph/src/lib.rs index 7a50fd4979..fecb032387 100644 --- a/sw-emulator/lib/periph/src/lib.rs +++ b/sw-emulator/lib/periph/src/lib.rs @@ -16,7 +16,7 @@ extern crate arrayref; mod asym_ecc384; mod csrng; -mod dma; +pub mod dma; mod doe; mod emu_ctrl; mod hash_sha256; diff --git a/sw-emulator/lib/periph/src/root_bus.rs b/sw-emulator/lib/periph/src/root_bus.rs index 536747eaea..0daf9105ca 100644 --- a/sw-emulator/lib/periph/src/root_bus.rs +++ b/sw-emulator/lib/periph/src/root_bus.rs @@ -22,11 +22,11 @@ use crate::{ MailboxInternal, MailboxRam, Sha512Accelerator, SocRegistersInternal, Uart, }; use caliptra_api_types::{DbgManufServiceRegReq, SecurityState}; -use caliptra_emu_bus::{Clock, Ram, Rom}; +use caliptra_emu_bus::{Bus, Clock, Event, Ram, Rom}; use caliptra_emu_cpu::{Pic, PicMmioRegisters}; use caliptra_emu_derive::Bus; use caliptra_hw_model_types::{EtrngResponse, RandomEtrngResponses, RandomNibbles}; -use std::path::PathBuf; +use std::{path::PathBuf, rc::Rc, sync::mpsc}; use tock_registers::registers::InMemoryRegister; /// Default Deobfuscation engine key @@ -254,6 +254,8 @@ impl Default for CaliptraRootBusArgs<'_> { } #[derive(Bus)] +#[incoming_event_fn(incoming_event)] +#[register_outgoing_events_fn(register_outgoing_events)] pub struct CaliptraRootBus { #[peripheral(offset = 0x0000_0000, mask = 0x0fff_ffff)] pub rom: Rom, @@ -372,6 +374,52 @@ impl CaliptraRootBus { soc_ifc: self.soc_reg.external_regs(), } } + + fn incoming_event(&mut self, event: Rc) { + self.rom.incoming_event(event.clone()); + self.doe.incoming_event(event.clone()); + self.doe.incoming_event(event.clone()); + self.ecc384.incoming_event(event.clone()); + self.hmac.incoming_event(event.clone()); + self.key_vault.incoming_event(event.clone()); + self.sha512.incoming_event(event.clone()); + self.sha256.incoming_event(event.clone()); + self.ml_dsa87.incoming_event(event.clone()); + self.iccm.incoming_event(event.clone()); + self.dccm.incoming_event(event.clone()); + self.uart.incoming_event(event.clone()); + self.ctrl.incoming_event(event.clone()); + self.soc_reg.incoming_event(event.clone()); + self.mailbox_sram.incoming_event(event.clone()); + self.mailbox.incoming_event(event.clone()); + self.sha512_acc.incoming_event(event.clone()); + self.dma.incoming_event(event.clone()); + self.csrng.incoming_event(event.clone()); + self.pic_regs.incoming_event(event); + } + + fn register_outgoing_events(&mut self, sender: mpsc::Sender) { + self.rom.register_outgoing_events(sender.clone()); + self.doe.register_outgoing_events(sender.clone()); + self.doe.register_outgoing_events(sender.clone()); + self.ecc384.register_outgoing_events(sender.clone()); + self.hmac.register_outgoing_events(sender.clone()); + self.key_vault.register_outgoing_events(sender.clone()); + self.sha512.register_outgoing_events(sender.clone()); + self.sha256.register_outgoing_events(sender.clone()); + self.ml_dsa87.register_outgoing_events(sender.clone()); + self.iccm.register_outgoing_events(sender.clone()); + self.dccm.register_outgoing_events(sender.clone()); + self.uart.register_outgoing_events(sender.clone()); + self.ctrl.register_outgoing_events(sender.clone()); + self.soc_reg.register_outgoing_events(sender.clone()); + self.mailbox_sram.register_outgoing_events(sender.clone()); + self.mailbox.register_outgoing_events(sender.clone()); + self.sha512_acc.register_outgoing_events(sender.clone()); + self.dma.register_outgoing_events(sender.clone()); + self.csrng.register_outgoing_events(sender.clone()); + self.pic_regs.register_outgoing_events(sender); + } } #[derive(Bus)] diff --git a/sw-emulator/lib/types/src/lib.rs b/sw-emulator/lib/types/src/lib.rs index 6b608907c3..fe9644ac80 100644 --- a/sw-emulator/lib/types/src/lib.rs +++ b/sw-emulator/lib/types/src/lib.rs @@ -13,7 +13,6 @@ Abstract: --*/ #![cfg_attr(not(test), no_std)] -pub mod bus; mod exception; mod macros;