From bebe372ca7daec10405d4300ac8467b721823407 Mon Sep 17 00:00:00 2001 From: Vishal Mhatre Date: Sun, 15 Dec 2024 22:00:02 -0800 Subject: [PATCH] [feat] UDS Programming Flow This change adds the UDS programming flow to the ROM. --- drivers/src/dma.rs | 25 ++++++++ drivers/src/soc_ifc.rs | 42 +++++++++++++ error/src/lib.rs | 5 ++ rom/dev/README.md | 10 +-- rom/dev/src/flow/mod.rs | 2 + rom/dev/src/flow/uds_programming.rs | 87 +++++++++++++++++++++++++++ rom/dev/src/main.rs | 5 ++ rom/dev/src/rom_env.rs | 13 ++-- sw-emulator/lib/periph/src/soc_reg.rs | 55 ++++++++++++++++- 9 files changed, 233 insertions(+), 11 deletions(-) create mode 100644 rom/dev/src/flow/uds_programming.rs diff --git a/drivers/src/dma.rs b/drivers/src/dma.rs index a2b8262757..86dad51990 100644 --- a/drivers/src/dma.rs +++ b/drivers/src/dma.rs @@ -263,6 +263,31 @@ impl Dma { Ok(()) } + /// Write an arbitrary length buffer to the target address. + /// + /// # Arguments + /// + /// * `write_addr` - Target address to write to + /// * `buffer` - Buffer to write + /// + /// # Returns + /// + /// * CaliptraResult<()> - Success or failure + pub fn write_buffer(&mut self, write_addr: usize, buffer: &[u8]) -> CaliptraResult<()> { + self.flush(); + + let write_transaction = DmaWriteTransaction { + write_addr, + fixed_addr: false, + length: buffer.len() as u32, + origin: DmaWriteOrigin::AhbFifo, + }; + self.dma_write_fifo(buffer)?; + self.setup_dma_write(write_transaction); + self.do_transaction()?; + Ok(()) + } + /// Transfer payload to mailbox /// /// The mailbox lock needs to be acquired before this can be called diff --git a/drivers/src/soc_ifc.rs b/drivers/src/soc_ifc.rs index a6a022b402..d808cd2547 100644 --- a/drivers/src/soc_ifc.rs +++ b/drivers/src/soc_ifc.rs @@ -365,6 +365,48 @@ impl SocIfc { .notif_internal_intr_r() .write(|w| w.notif_cmd_avail_sts(true)); } + + pub fn uds_program_req(&self) -> bool { + self.soc_ifc + .regs() + .ss_dbg_manuf_service_reg_req() + .read() + .uds_program_req() + } + + pub fn set_uds_programming_flow_state(&mut self, in_progress: bool) { + self.soc_ifc + .regs_mut() + .ss_dbg_manuf_service_reg_rsp() + .write(|w| w.uds_program_in_progress(in_progress)); + } + + pub fn set_uds_programming_flow_status(&mut self, flow_succeeded: bool) { + self.soc_ifc + .regs_mut() + .ss_dbg_manuf_service_reg_rsp() + .write(|w| { + if flow_succeeded { + w.uds_program_success(true); + w.uds_program_fail(false) + } else { + w.uds_program_success(false); + w.uds_program_fail(true) + } + }); + } + + pub fn uds_seed_dest_base_addr_low(&self) -> u32 { + self.soc_ifc.regs().ss_uds_seed_base_addr_l().read() + } + + pub fn active_mode(&self) -> bool { + self.soc_ifc + .regs() + .cptra_hw_config() + .read() + .active_mode_en() + } } bitflags::bitflags! { diff --git a/error/src/lib.rs b/error/src/lib.rs index ff0e8804ea..79853ea694 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -608,6 +608,11 @@ impl CaliptraError { pub const ROM_CFI_PANIC_FAKE_TRNG_USED_WITH_DEBUG_LOCK: CaliptraError = CaliptraError::new_const(0x104005D); + /// ROM UDS Programming Errors + pub const ROM_UDS_PROG_ILLEGAL_LIFECYCLE_STATE: CaliptraError = + CaliptraError::new_const(0x01045000); + pub const ROM_UDS_PROG_IN_PASSIVE_MODE: CaliptraError = CaliptraError::new_const(0x01045001); + /// ROM Global Errors pub const ROM_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x01050001); pub const ROM_GLOBAL_EXCEPTION: CaliptraError = CaliptraError::new_const(0x01050002); diff --git a/rom/dev/README.md b/rom/dev/README.md index 7e5b85de1a..0cf2d4912e 100644 --- a/rom/dev/README.md +++ b/rom/dev/README.md @@ -235,13 +235,15 @@ The following flows are conducted exclusively when the ROM is operating in ACTIV The following flows are conducted when the ROM is operating in the manufacturing mode, indicated by a value of `DEVICE_MANUFACTURING` (0x1) in the `CPTRA_SECURITY_STATE` register `device_lifecycle` bits. #### UDS Provisioning -1. On reset, the ROM checks if the `UDS_PROGRAM_REQ` bit in the `CPTRA_DBG_MANUF_SERVICE_REQ_REG` register is set. If the bit is set, the ROM initiates the UDS seed programming flow. +1. On reset, the ROM checks if the `UDS_PROGRAM_REQ` bit in the `SS_DBG_MANUF_SERVICE_REG_REQ` register is set. If the bit is set, ROM initiates the UDS seed programming flow by setting the `UDS_PROGRAM_IN_PROGRESS` bit in the `SS_DBG_MANUF_SERVICE_REG_RSP` register. -2. In this procedure, the ROM retrieves a 512-bit value from the iTRNG and writes it to the address specified by the `UDS_SEED_OFFSET` register, utilizing DMA hardware assistance. +2. ROM then retrieves a 512-bit value from the iTRNG and writes it to the address specified by the `SS_UDS_SEED_BASE_ADDR_L` register, utilizing DMA hardware assistance. -3. Following the DMA operation, the ROM updates the `UDS_PROGRAM_REQ` bit in the `CPTRA_DBG_MANUF_SERVICE_RSP_REG` register to either `UDS_PROGRAM_SUCCESS` or `UDS_PROGRAM_FAIL`, indicating the outcome of the operation. +3. Following the DMA operation, ROM updates the `UDS_PROGRAM_SUCCESS` or the `UDS_PROGRAM_FAIL` bit in the `SS_DBG_MANUF_SERVICE_REG_RSP` register to indicate the outcome of the operation. -4. The manufacturing process then polls this bit and continues with the fuse burning flow as outlined by the fuse controller specifications and SOC-specific VR methodologies. +4. ROM then resets the `UDS_PROGRAM_IN_PROGRESS` bit in the `SS_DBG_MANUF_SERVICE_REG_RSP` register to indicate completion of the programming. + +5. The manufacturing process polls this bit and continues with the fuse burning flow as outlined by the fuse controller specifications and SOC-specific VR methodologies. #### Debug Unlock 1. On reset, the ROM checks if the `MANUF_DEBUG_UNLOCK_REQ` bit in the `CPTRA_DBG_MANUF_SERVICE_REQ_REG` register and the `DEBUG_INTENT_STRAP` register are set diff --git a/rom/dev/src/flow/mod.rs b/rom/dev/src/flow/mod.rs index cdce5a522c..0d0d41a8f5 100644 --- a/rom/dev/src/flow/mod.rs +++ b/rom/dev/src/flow/mod.rs @@ -15,10 +15,12 @@ Abstract: mod cold_reset; #[cfg(feature = "fake-rom")] mod fake; +pub(crate) mod uds_programming; mod update_reset; mod warm_reset; use crate::cprintln; +pub use crate::flow::uds_programming::UdsProgrammingFlow; use crate::{handle_fatal_error, rom_env::RomEnv}; #[cfg(not(feature = "no-cfi"))] use caliptra_cfi_derive::cfi_mod_fn; diff --git a/rom/dev/src/flow/uds_programming.rs b/rom/dev/src/flow/uds_programming.rs new file mode 100644 index 0000000000..043a9f2a9c --- /dev/null +++ b/rom/dev/src/flow/uds_programming.rs @@ -0,0 +1,87 @@ +/*++ + +Licensed under the Apache-2.0 license. + +File Name: + + uds_programming.rs + +Abstract: + + File contains the implementation of UDS programming flow. +--*/ + +use crate::rom_env::RomEnv; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_derive::cfi_impl_fn; +use caliptra_common::cprintln; +use caliptra_drivers::Lifecycle; +use caliptra_drivers::{CaliptraError, CaliptraResult}; +use zerocopy::AsBytes; + +/// UDS Programming Flow +pub struct UdsProgrammingFlow {} + +impl UdsProgrammingFlow { + #[inline(never)] + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + pub fn program_uds(env: &mut RomEnv) -> CaliptraResult<()> { + cprintln!("[uds] ++"); + + // Check if UDS programming is requested. + if !env.soc_ifc.uds_program_req() { + return Ok(()); + } + + cprintln!("[uds] ++"); + + // Check if ROM is running in Active mode. + if !env.soc_ifc.active_mode() { + cprintln!("[uds] ROM is not in Active mode."); + return Err(CaliptraError::ROM_UDS_PROG_IN_PASSIVE_MODE); + } + + // Check if ROM is in manufacturing mode. + if env.soc_ifc.lifecycle() != Lifecycle::Manufacturing { + cprintln!("[uds] ROM is not in manufacturing mode."); + return Err(CaliptraError::ROM_UDS_PROG_ILLEGAL_LIFECYCLE_STATE); + } + + // Update the UDS programming state. + env.soc_ifc + .set_uds_programming_flow_state(true /* in_progress */); + + let result = (|| { + // Generate a 512-bit random value.. + let mut seed = [0u32; 16]; + let seed1 = env.trng.generate()?; + let seed2 = env.trng.generate()?; + seed[..12].copy_from_slice(&seed1.0); + seed[12..16].copy_from_slice(&seed2.0[0..4]); + + // Write the seed to the UDS_SEED_OFFSET using DMA assist. + cprintln!("[uds] Writing seed to UDS_SEED_OFFSET"); + env.dma.write_buffer( + env.soc_ifc.uds_seed_dest_base_addr_low() as usize, + seed.as_bytes(), + )?; + Ok(()) + })(); + + // Set the UDS programming result. + env.soc_ifc.set_uds_programming_flow_status(result.is_ok()); + + // Update the UDS programming state. + env.soc_ifc + .set_uds_programming_flow_state(false /* in_progress */); + + cprintln!( + "[uds] UDS programming flow completed with status: {}", + if result.is_ok() { "SUCCESS" } else { "FAILURE" } + ); + + cprintln!("[uds] --"); + + result + } +} diff --git a/rom/dev/src/main.rs b/rom/dev/src/main.rs index 48853aa3d2..9fc9a0e525 100644 --- a/rom/dev/src/main.rs +++ b/rom/dev/src/main.rs @@ -96,6 +96,11 @@ pub extern "C" fn rom_entry() -> ! { }; cprintln!("[state] LifecycleState = {}", _lifecyle); + // UDS programming. + if let Err(err) = crate::flow::UdsProgrammingFlow::program_uds(&mut env) { + handle_fatal_error(err.into()); + } + if cfg!(feature = "fake-rom") && (env.soc_ifc.lifecycle() == caliptra_drivers::Lifecycle::Production) && !(env.soc_ifc.prod_en_in_fake_mode()) diff --git a/rom/dev/src/rom_env.rs b/rom/dev/src/rom_env.rs index f2c7bfb74b..17c39cabb3 100644 --- a/rom/dev/src/rom_env.rs +++ b/rom/dev/src/rom_env.rs @@ -17,14 +17,15 @@ Abstract: use crate::fht::FhtDataStore; use caliptra_drivers::{ - DataVault, DeobfuscationEngine, Ecc384, Hmac, KeyVault, Lms, Mailbox, Mldsa87, PcrBank, + DataVault, DeobfuscationEngine, Dma, Ecc384, Hmac, KeyVault, Lms, Mailbox, Mldsa87, PcrBank, PersistentDataAccessor, Sha1, Sha256, Sha2_512_384, Sha2_512_384Acc, SocIfc, Trng, }; use caliptra_error::CaliptraResult; use caliptra_registers::{ - csrng::CsrngReg, doe::DoeReg, dv::DvReg, ecc::EccReg, entropy_src::EntropySrcReg, - hmac::HmacReg, kv::KvReg, mbox::MboxCsr, mldsa::MldsaReg, pv::PvReg, sha256::Sha256Reg, - sha512::Sha512Reg, sha512_acc::Sha512AccCsr, soc_ifc::SocIfcReg, soc_ifc_trng::SocIfcTrngReg, + axi_dma::AxiDmaReg, csrng::CsrngReg, doe::DoeReg, dv::DvReg, ecc::EccReg, + entropy_src::EntropySrcReg, hmac::HmacReg, kv::KvReg, mbox::MboxCsr, mldsa::MldsaReg, + pv::PvReg, sha256::Sha256Reg, sha512::Sha512Reg, sha512_acc::Sha512AccCsr, soc_ifc::SocIfcReg, + soc_ifc_trng::SocIfcTrngReg, }; /// Rom Context @@ -79,6 +80,9 @@ pub struct RomEnv { /// Mldsa87 Engine pub mldsa87: Mldsa87, + + /// Dma engine + pub dma: Dma, } impl RomEnv { @@ -108,6 +112,7 @@ impl RomEnv { trng, persistent_data: PersistentDataAccessor::new(), mldsa87: Mldsa87::new(MldsaReg::new()), + dma: Dma::new(AxiDmaReg::new()), }) } } diff --git a/sw-emulator/lib/periph/src/soc_reg.rs b/sw-emulator/lib/periph/src/soc_reg.rs index 7b0bfed20c..6a3e878ad8 100644 --- a/sw-emulator/lib/periph/src/soc_reg.rs +++ b/sw-emulator/lib/periph/src/soc_reg.rs @@ -299,6 +299,37 @@ register_bitfields! [ NOTIF_GEN_IN_TOGGLE_TRIG OFFSET(5) NUMBITS(1) [], RSVD OFFSET(6) NUMBITS(26) [], ], + + /// SubSytem Debug Manufacturing Service Request Register + SsDbgManufServiceRegReq [ + MANUF_DBG_UNLOCK_REQ OFFSET(0) NUMBITS(1) [], + PROD_DBG_UNLOCK_REQ OFFSET(1) NUMBITS(1) [], + UDS_PROGRAM_REQ OFFSET(2) NUMBITS(1) [], + RSVD OFFSET(3) NUMBITS(29) [], + ], + + /// SubSytem Debug Manufacturing Service Response Register + SsDbgManufServiceRegRsp [ + MANUF_DBG_UNLOCK_SUCCESS OFFSET(0) NUMBITS(1) [], + MANUF_DBG_UNLOCK_FAIL OFFSET(1) NUMBITS(1) [], + MANUF_DBG_UNLOCK_IN_PROGRESS OFFSET(2) NUMBITS(1) [], + PROD_DBG_UNLOCK_SUCCESS OFFSET(3) NUMBITS(1) [], + PROD_DBG_UNLOCK_FAIL OFFSET(4) NUMBITS(1) [], + PROD_DBG_UNLOCK_IN_PROGRESS OFFSET(5) NUMBITS(1) [], + UDS_PROGRAM_SUCCESS OFFSET(6) NUMBITS(1) [], + UDS_PROGRAM_FAIL OFFSET(7) NUMBITS(1) [], + UDS_PROGRAM_IN_PROGRESS OFFSET(8) NUMBITS(1) [], + RSVD OFFSET(9) NUMBITS(23) [], + ], + + /// Hardware Configuration + HwConfig [ + ITRNG_EN OFFSET(0) NUMBITS(1) [], + RSVD_EN OFFSET(1) NUMBITS(3) [], + LMS_ACC_EN OFFSET(4) NUMBITS(1) [], + ACTIVE_MODE_en OFFSET(5) NUMBITS(1) [], + RSVD OFFSET(6) NUMBITS(26) [], + ], ]; /// SOC Register peripheral @@ -371,7 +402,9 @@ impl SocRegistersInternal { } pub fn set_hw_config(&mut self, val: CptraHwConfigReadVal) { - self.regs.borrow_mut().cptra_hw_config = val.into(); + self.regs.borrow_mut().cptra_hw_config = ReadWriteRegister { + reg: InMemoryRegister::::new(val.into()), + }; } pub fn external_regs(&self) -> SocRegistersExternal { @@ -572,7 +605,7 @@ struct SocRegistersImpl { cptra_fw_rev_id: [u32; 2], #[register(offset = 0x00e0, write_fn = write_disabled)] - cptra_hw_config: u32, + cptra_hw_config: ReadWriteRegister, #[register(offset = 0x00e4, write_fn = on_write_wdt_timer1_en)] cptra_wdt_timer1_en: ReadWriteRegister, @@ -669,6 +702,18 @@ struct SocRegistersImpl { #[register_array(offset = 0x34c)] fuse_manuf_dbg_unlock_token: [u32; FUSE_MANUF_DBG_UNLOCK_TOKEN_SIZE / 4], + #[register(offset = 0x520)] + ss_uds_seed_base_addr_l: ReadOnlyRegister, + + #[register(offset = 0x524)] + ss_uds_seed_base_addr_h: ReadOnlyRegister, + + #[register(offset = 0x5c0)] + ss_dbg_manuf_service_reg_req: ReadWriteRegister, + + #[register(offset = 0x5c4)] + ss_dbg_manuf_service_reg_rsp: ReadWriteRegister, + /// INTERNAL_OBF_KEY Register internal_obf_key: [u32; 8], @@ -829,7 +874,7 @@ 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: 0, + cptra_hw_config: ReadWriteRegister::new(0), // [TODO][CAP2] Program this cptra_wdt_timer1_en: ReadWriteRegister::new(0), cptra_wdt_timer1_ctrl: ReadWriteRegister::new(0), cptra_wdt_timer1_timeout_period: [0xffff_ffff; 2], @@ -896,6 +941,10 @@ impl SocRegistersImpl { etrng_responses: args.etrng_responses, pending_etrng_response: None, op_pending_etrng_response_action: None, + ss_dbg_manuf_service_reg_req: ReadWriteRegister::new(0), + ss_dbg_manuf_service_reg_rsp: ReadWriteRegister::new(0), + ss_uds_seed_base_addr_l: ReadOnlyRegister::new(0), // [TODO][CAP2] Program this + ss_uds_seed_base_addr_h: ReadOnlyRegister::new(0), // [TODO][CAP2] Program this }; regs }