diff --git a/drivers/src/soc_ifc.rs b/drivers/src/soc_ifc.rs index a6a022b402..24e641fbb4 100644 --- a/drivers/src/soc_ifc.rs +++ b/drivers/src/soc_ifc.rs @@ -365,6 +365,59 @@ 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() + } + + pub fn uds_fuse_row_granularity_64(&self) -> bool { + let config_val: u32 = self.soc_ifc.regs().cptra_hw_config().read().into(); + ((config_val >> 6) & 1) != 0 + } + + pub fn fuse_controller_base_addr(&self) -> u64 { + let low = self.soc_ifc.regs().ss_otp_fc_base_addr_l().read(); + let high = self.soc_ifc.regs().ss_otp_fc_base_addr_h().read(); + (high as u64) << 32 | low as u64 + } } bitflags::bitflags! { diff --git a/error/src/lib.rs b/error/src/lib.rs index 0f73e1851f..3da2d78b3f 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -610,6 +610,13 @@ 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); + pub const ROM_UDS_PROG_INVALID_SEED_LENGTH: CaliptraError = + CaliptraError::new_const(0x01045002); + /// 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 5428b8f7d0..868238d630 100644 --- a/rom/dev/README.md +++ b/rom/dev/README.md @@ -242,24 +242,25 @@ The following flows are conducted when the ROM is operating in the manufacturing 3. ROM then retrieves the UDS granularity from the `CPTRA_HW_CONFIG` register Bit6 to learn if the fuse row is accessible with 32-bit or 64-bit granularity. 4. ROM then performs the following steps until all the 512 bits of the UDS seed are programmed: - 1. The ROM verifies the idle state of the DAI by reading the `STATUS` register of the Fuse Controller, located at offset 0x10 from the Fuse Controller's base address. + 1. The ROM verifies the idle state of the DAI by reading the `STATUS` register `DAI_IDLE` bit (Bit 18) of the Fuse Controller, located at offset 0x10 from the Fuse Controller's base address. 2. If the granularity is 32-bit, the ROM writes the next word from the UDS seed to the `DIRECT_ACCESS_WDATA_0` register. If the granularity is 64-bit, the ROM writes the next two words to `the DIRECT_ACCESS_WDATA_0` and `DIRECT_ACCESS_WDATA_1` registers, located at offsets 0x44 and 0x48 respectively from the Fuse Controller's base address. 3. The ROM writes the lower 32 bits of the UDS Seed programming base address to the `DIRECT_ACCESS_ADDRESS` register at offset 0x40. 4. The ROM triggers the UDS seed write command by writing 0x2 to the `DIRECT_ACCESS_CMD` register at offset 0x3C. - 5. The ROM continuously polls the `STATUS` register until the DAI state returns to idle. - 6. [OPEN] Handle DAI error. - 7. The ROM increments the `DIRECT_ACCESS_ADDRESS` register by 4 for 32-bit granularity or 8 for 64-bit granularity and repeats the process for the remaining words of the UDS seed. + 5. [OPEN] Handle DAI error. + 6. The ROM increments the `DIRECT_ACCESS_ADDRESS` register by 4 for 32-bit granularity or 8 for 64-bit granularity and repeats the process for the remaining words of the UDS seed. -5. After completing the write operation, ROM triggers the partition digest operation performing the following steps: +5. The ROM continuously polls the Fuse Controller's `STATUS` register until the DAI state returns to idle. + +6. After completing the write operation, ROM triggers the partition digest operation performing the following steps: 1. The ROM writes the lower 32 bits of the UDS Seed programming base address to the `DIRECT_ACCESS_ADDRESS` register. 2. The ROM triggers the digest calculation command by writing 0x4 to the `DIRECT_ACCESS_CMD` register. 3. The ROM continuously polls the Fuse Controller's `STATUS` register until the DAI state returns to idle. -6. 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. +7. 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. -7. ROM then resets the `UDS_PROGRAM_IN_PROGRESS` bit in the `SS_DBG_MANUF_SERVICE_REG_RSP` register to indicate completion of the programming. +8. ROM then resets the `UDS_PROGRAM_IN_PROGRESS` bit in the `SS_DBG_MANUF_SERVICE_REG_RSP` register to indicate completion of the programming. -8. 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. +9. 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. #### 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..04c698cb06 --- /dev/null +++ b/rom/dev/src/flow/uds_programming.rs @@ -0,0 +1,180 @@ +/*++ + +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::{AxiAddr, CaliptraError, CaliptraResult, Lifecycle}; + +const STATUS_REG_OFFSET: u64 = 0x10; +const DIRECT_ACCESS_WDATA_0_REG_OFFSET: u64 = 0x44; +const DIRECT_ACCESS_WDATA_1_REG_OFFSET: u64 = 0x48; +const DIRECT_ACCESS_ADDRESS_REG_OFFSET: u64 = 0x40; +const DIRECT_ACCESS_CMD_REG_OFFSET: u64 = 0x3C; +const DAI_IDLE_BIT: u32 = 1 << 18; +const DIRECT_ACCESS_CMD_WRITE: u32 = 0x2; +const DIRECT_ACCESS_CMD_DIGEST: u32 = 0x4; + +/// 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."); + 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."); + 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]); + + let uds_fuse_row_granularity_64: bool = env.soc_ifc.uds_fuse_row_granularity_64(); + let fuse_controller_base_addr = env.soc_ifc.fuse_controller_base_addr(); + let status_reg_addr = fuse_controller_base_addr + STATUS_REG_OFFSET; + let direct_access_wdata_0_reg_addr = + fuse_controller_base_addr + DIRECT_ACCESS_WDATA_0_REG_OFFSET; + let direct_access_wdata_1_reg_addr = + fuse_controller_base_addr + DIRECT_ACCESS_WDATA_1_REG_OFFSET; + let mut uds_seed_dest_address = env.soc_ifc.uds_seed_dest_base_addr_low(); + let direct_access_address_reg_addr = + fuse_controller_base_addr + DIRECT_ACCESS_ADDRESS_REG_OFFSET; + let direct_access_cmd_reg_addr = + fuse_controller_base_addr + DIRECT_ACCESS_CMD_REG_OFFSET; + let mut seed_index = 0; + + while seed_index < seed.len() { + // Poll the STATUS register until the DAI state returns to idle. + while { + let status_value = env.dma.read_dword(AxiAddr::from(status_reg_addr))?; + (status_value & DAI_IDLE_BIT) == 0 + } { + // [TODO][CAP2] Handle errors. + } + + // Write the UDS seed to the DIRECT_ACCESS_WDATA_0 register + // and the DIRECT_ACCESS_WDATA_1 register (for 64-bit granularity). + let wdata_0 = seed[seed_index]; + env.dma + .write_dword(AxiAddr::from(direct_access_wdata_0_reg_addr), wdata_0)?; + if uds_fuse_row_granularity_64 { + if seed_index + 1 >= seed.len() { + Err(CaliptraError::ROM_UDS_PROG_INVALID_SEED_LENGTH)?; + } + // 64-bit granularity + let wdata_1 = seed[seed_index + 1]; + env.dma + .write_dword(AxiAddr::from(direct_access_wdata_1_reg_addr), wdata_1)?; + seed_index += 2; + } else { + // 32-bit granularity + seed_index += 1; + } + + // Write the lower 32 bits of the UDS Seed programming destination address to the DIRECT_ACCESS_ADDRESS register. + env.dma.write_dword( + AxiAddr::from(direct_access_address_reg_addr), + uds_seed_dest_address, + )?; + + // Trigger the UDS seed write command + env.dma.write_dword( + AxiAddr::from(direct_access_cmd_reg_addr), + DIRECT_ACCESS_CMD_WRITE, + )?; + + // Increment the DIRECT_ACCESS_ADDRESS register + if uds_fuse_row_granularity_64 { + uds_seed_dest_address += 8; + } else { + uds_seed_dest_address += 4; + } + } // End of UDS seed write loop. + + // Trigger the partition digest operation + // Poll the STATUS register until the DAI state returns to idle. + while { + let status_value = env.dma.read_dword(AxiAddr::from(status_reg_addr))?; + (status_value & DAI_IDLE_BIT) == 0 + } { + // [TODO][CAP2] Handle errors. + } + + // Write the lower 32 bits of the UDS Seed programming base address to the DIRECT_ACCESS_ADDRESS register. + cprintln!("[uds] Triggering the partition digest operation"); + env.dma.write_dword( + AxiAddr::from(direct_access_address_reg_addr), + env.soc_ifc.uds_seed_dest_base_addr_low(), + )?; + + // Trigger the digest calculation command + env.dma.write_dword( + AxiAddr::from(direct_access_cmd_reg_addr), + DIRECT_ACCESS_CMD_DIGEST, + )?; + + // Poll the STATUS register until the DAI state returns to idle + while { + let status_value = env.dma.read_dword(AxiAddr::from(status_reg_addr))?; + (status_value & DAI_IDLE_BIT) == 0 + } { + // [TODO][CAP2] Handle errors. + } + + 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 9b7ee46e29..fa121603a6 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 8750ede1a5..36df10406d 100644 --- a/rom/dev/src/rom_env.rs +++ b/rom/dev/src/rom_env.rs @@ -17,14 +17,14 @@ Abstract: use crate::fht::FhtDataStore; use caliptra_drivers::{ - DeobfuscationEngine, Ecc384, Hmac, KeyVault, Lms, Mailbox, Mldsa87, PcrBank, + 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, 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, 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 @@ -76,6 +76,9 @@ pub struct RomEnv { /// Mldsa87 Engine pub mldsa87: Mldsa87, + + /// Dma engine + pub dma: Dma, } impl RomEnv { @@ -104,6 +107,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 }