diff --git a/FROZEN_IMAGES.sha384sum b/FROZEN_IMAGES.sha384sum index 5427bb1453..57892d3729 100644 --- a/FROZEN_IMAGES.sha384sum +++ b/FROZEN_IMAGES.sha384sum @@ -1,3 +1,3 @@ # WARNING: Do not update this file without the approval of the Caliptra TAC -2c7b77ae40e6c760a26fe37d1a3b45910435196f642ef76f1df96e49eec5cf710fb7159d89189a0b4eae47007ddc2b98 caliptra-rom-no-log.bin -0c518139f1cd88acbd06ff4e24b28823008347ce5186e84af54adabedcd00c52b3ad9b6a0e6aaf8514b4dfafed5b64cb caliptra-rom-with-log.bin +f9db2f9623da514dedb5b5a8b3ca31e509ad2baff0b8ffc4ea3f362ae0325d0e2cae126fe42c91ad2235621edc1f5d08 caliptra-rom-no-log.bin +4c39f5227a53d7adc996aaff7118893f23d5307e167ce36a222b00d31a425ef0279aa308cac3f15ed75779bc0d24d8cd caliptra-rom-with-log.bin diff --git a/cfi/lib/src/cfi.rs b/cfi/lib/src/cfi.rs index 8c977f9431..2eb85ec24e 100644 --- a/cfi/lib/src/cfi.rs +++ b/cfi/lib/src/cfi.rs @@ -57,6 +57,9 @@ pub enum CfiPanicInfo { /// Random number generator error TrngError, + /// An enum match statement finds an unexpected value. + UnexpectedMatchBranch, + /// Unknown error UnknownError, } diff --git a/cfi/lib/src/cfi_counter.rs b/cfi/lib/src/cfi_counter.rs index d9ad7d1c8e..aefec55a5a 100644 --- a/cfi/lib/src/cfi_counter.rs +++ b/cfi/lib/src/cfi_counter.rs @@ -82,9 +82,9 @@ pub enum CfiCounter {} impl CfiCounter { /// Reset counter - #[inline(never)] + #[inline(always)] pub fn reset(trng: &mut caliptra_drivers::Trng) { - prng().seed_from_trng(trng); + prng().mix_entropy_from_trng(trng); Self::reset_internal(); } diff --git a/cfi/lib/src/xoshiro.rs b/cfi/lib/src/xoshiro.rs index 74818b8978..4cca47e335 100644 --- a/cfi/lib/src/xoshiro.rs +++ b/cfi/lib/src/xoshiro.rs @@ -61,13 +61,13 @@ impl Xoshiro128 { &*(addr as *const Xoshiro128) } - pub fn seed_from_trng(&self, trng: &mut caliptra_drivers::Trng) { + pub fn mix_entropy_from_trng(&self, trng: &mut caliptra_drivers::Trng) { loop { if let Ok(entropy) = trng.generate() { - self.s0.set(entropy.0[0]); - self.s1.set(entropy.0[1]); - self.s2.set(entropy.0[2]); - self.s3.set(entropy.0[3]); + self.s0.set(self.s0.get() ^ entropy.0[0]); + self.s1.set(self.s1.get() ^ entropy.0[1]); + self.s2.set(self.s2.get() ^ entropy.0[2]); + self.s3.set(self.s3.get() ^ entropy.0[3]); } else { cfi_panic(CfiPanicInfo::TrngError) } diff --git a/drivers/src/memory_layout.rs b/drivers/src/memory_layout.rs index 2d070faf2d..ee2afade4b 100644 --- a/drivers/src/memory_layout.rs +++ b/drivers/src/memory_layout.rs @@ -25,13 +25,16 @@ pub const ROM_ORG: u32 = 0x00000000; pub const MBOX_ORG: u32 = 0x30000000; pub const ICCM_ORG: u32 = 0x40000000; pub const DCCM_ORG: u32 = 0x50000000; -// Region of ~ 1k bytes between DCCM_ORG and CFI_VAL_ORG is reserved for ROM's DATA_ORG +pub const CFI_XO_S0_ORG: u32 = 0x50000000; +pub const CFI_XO_S1_ORG: u32 = 0x50000004; +pub const CFI_XO_S2_ORG: u32 = 0x50000008; +pub const CFI_XO_S3_ORG: u32 = 0x5000000C; +pub const CFI_RESERVED0_ORG: u32 = 0x50000010; +pub const CFI_RESERVED1_ORG: u32 = 0x50000014; +pub const CFI_RESERVED2_ORG: u32 = 0x50000018; +pub const CFI_RESERVED3_ORG: u32 = 0x5000001C; pub const CFI_VAL_ORG: u32 = 0x500003E4; pub const CFI_MASK_ORG: u32 = 0x500003E8; -pub const CFI_XO_S0_ORG: u32 = 0x500003EC; -pub const CFI_XO_S1_ORG: u32 = 0x500003F0; -pub const CFI_XO_S2_ORG: u32 = 0x500003F4; -pub const CFI_XO_S3_ORG: u32 = 0x500003F8; pub const BOOT_STATUS_ORG: u32 = 0x500003FC; pub const MAN1_ORG: u32 = 0x50000400; pub const MAN2_ORG: u32 = 0x50001C00; diff --git a/drivers/src/soc_ifc.rs b/drivers/src/soc_ifc.rs index f026816659..9ce0266db4 100644 --- a/drivers/src/soc_ifc.rs +++ b/drivers/src/soc_ifc.rs @@ -144,6 +144,11 @@ impl SocIfc { ((val >> 31) & 1) != 0 } + #[inline(always)] + pub fn hw_config_internal_trng(&mut self) -> bool { + self.soc_ifc.regs().cptra_hw_config().read().i_trng_en() + } + /// Enable or disable WDT1 /// /// # Arguments diff --git a/drivers/src/trng.rs b/drivers/src/trng.rs index ad1b895600..de21a86533 100644 --- a/drivers/src/trng.rs +++ b/drivers/src/trng.rs @@ -1,15 +1,22 @@ // Licensed under the Apache-2.0 license -use caliptra_error::CaliptraResult; +use caliptra_error::{CaliptraError, CaliptraResult}; use caliptra_registers::{ csrng::CsrngReg, entropy_src::EntropySrcReg, soc_ifc::SocIfcReg, soc_ifc_trng::SocIfcTrngReg, }; use crate::{trng_ext::TrngExt, Array4x12, Csrng}; +#[repr(u32)] pub enum Trng { - Internal(Csrng), - External(TrngExt), + Internal(Csrng) = 0xb714a2b1, + External(TrngExt) = 0xf3702ce3, + + // Teach the compiler that "other" values are possible to encourage it not + // to get too crazy with optimizations. Match statements should handle `_` + // by jumping to the CFI handler. + Invalid0 = 0x0060f20f, + Invalid1 = 0x0a8dfe7a, } impl Trng { @@ -48,6 +55,14 @@ impl Trng { match self { Self::Internal(csrng) => Ok(csrng.generate12()?.into()), Self::External(trng_ext) => trng_ext.generate(), + _ => { + extern "C" { + fn cfi_panic_handler(code: u32) -> !; + } + unsafe { + cfi_panic_handler(CaliptraError::ROM_CFI_PANIC_UNEXPECTED_MATCH_BRANCH.into()) + } + } } } } diff --git a/drivers/test-fw/src/bin/trng_driver_responder.rs b/drivers/test-fw/src/bin/trng_driver_responder.rs index 5435f49bbd..bec1780097 100644 --- a/drivers/test-fw/src/bin/trng_driver_responder.rs +++ b/drivers/test-fw/src/bin/trng_driver_responder.rs @@ -21,6 +21,12 @@ pub fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } +#[no_mangle] +extern "C" fn cfi_panic_handler(code: u32) -> ! { + println!("CFI Panic code=0x{:08X}", code); + loop {} +} + #[no_mangle] extern "C" fn main() { let csrng_reg = unsafe { CsrngReg::new() }; diff --git a/error/src/lib.rs b/error/src/lib.rs index 357e366b01..32dc3ed8ed 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -440,6 +440,8 @@ impl CaliptraError { pub const ROM_CFI_PANIC_ASSERT_GE_FAILURE: CaliptraError = CaliptraError::new_const(0x1040059); pub const ROM_CFI_PANIC_ASSERT_LE_FAILURE: CaliptraError = CaliptraError::new_const(0x104005A); pub const ROM_CFI_PANIC_TRNG_FAILURE: CaliptraError = CaliptraError::new_const(0x104005B); + pub const ROM_CFI_PANIC_UNEXPECTED_MATCH_BRANCH: CaliptraError = + CaliptraError::new_const(0x104005C); /// ROM Global Errors pub const ROM_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x01050001); diff --git a/fmc/src/main.rs b/fmc/src/main.rs index 90daa113c5..24380fd98f 100644 --- a/fmc/src/main.rs +++ b/fmc/src/main.rs @@ -109,6 +109,14 @@ extern "C" fn nmi_handler(trap_record: &TrapRecord) { handle_fatal_error(error.into()); } + +#[no_mangle] +extern "C" fn cfi_panic_handler(code: u32) -> ! { + cprintln!("CFI Panic code=0x{:08X}", code); + + handle_fatal_error(code); +} + #[panic_handler] #[inline(never)] #[cfg(not(feature = "std"))] diff --git a/rom/dev/src/main.rs b/rom/dev/src/main.rs index 24beb8e28a..af8f31b848 100644 --- a/rom/dev/src/main.rs +++ b/rom/dev/src/main.rs @@ -16,13 +16,13 @@ Abstract: #![cfg_attr(feature = "fake-rom", allow(unused_imports))] use crate::{lock::lock_registers, print::HexBytes}; -use caliptra_cfi_lib::CfiCounter; +use caliptra_cfi_lib::{cfi_assert_eq, CfiCounter}; use caliptra_registers::soc_ifc::SocIfcReg; use core::hint::black_box; use caliptra_drivers::{ cprintln, report_fw_error_fatal, report_fw_error_non_fatal, CaliptraError, Ecc384, Hmac384, - KeyVault, Mailbox, ResetReason, Sha256, Sha384, Sha384Acc, ShaAccLockState, SocIfc, + KeyVault, Mailbox, ResetReason, Sha256, Sha384, Sha384Acc, ShaAccLockState, SocIfc, Trng, }; use caliptra_error::CaliptraResult; use caliptra_image_types::RomInfo; @@ -70,10 +70,30 @@ pub extern "C" fn rom_entry() -> ! { if !cfg!(feature = "no-cfi") { cprintln!("[state] CFI Enabled"); CfiCounter::reset(&mut env.trng); + CfiCounter::reset(&mut env.trng); + CfiCounter::reset(&mut env.trng); } else { cprintln!("[state] CFI Disabled"); } + // Check if TRNG is correctly sourced as per hw config. + cfi_assert_eq( + env.soc_ifc.hw_config_internal_trng(), + matches!(env.trng, Trng::Internal(_)), + ); + cfi_assert_eq( + !env.soc_ifc.hw_config_internal_trng(), + matches!(env.trng, Trng::External(_)), + ); + cfi_assert_eq( + env.soc_ifc.hw_config_internal_trng(), + matches!(env.trng, Trng::Internal(_)), + ); + cfi_assert_eq( + !env.soc_ifc.hw_config_internal_trng(), + matches!(env.trng, Trng::External(_)), + ); + let _lifecyle = match env.soc_ifc.lifecycle() { caliptra_drivers::Lifecycle::Unprovisioned => "Unprovisioned", caliptra_drivers::Lifecycle::Manufacturing => "Manufacturing", diff --git a/rom/dev/src/rom.ld b/rom/dev/src/rom.ld index d8a663b8f8..7c6e4c3d7a 100644 --- a/rom/dev/src/rom.ld +++ b/rom/dev/src/rom.ld @@ -20,6 +20,7 @@ ROM_ORG = 0x00000000; ICCM_ORG = 0x40000000; DCCM_ORG = 0x50000000; DATA_ORG = 0x50000000; +DCCM_POST_CFI_ENTROPY_ORG = 0x50000010; STACK_ORG = 0x5001C000; ESTACK_ORG = 0x5001F800; NSTACK_ORG = 0x5001FC00; @@ -33,6 +34,7 @@ ROM_RELAXATION_PADDING = 4k; ROM_SIZE = 48K; ICCM_SIZE = 128K; DCCM_SIZE = 128K; +DCCM_POST_CFI_ENTROPY_SIZE = 131040; /* DCCM_SIZE - (CFI Entropy State (8 Words)) */ DATA_SIZE = 996; STACK_SIZE = 14K; ESTACK_SIZE = 1K; diff --git a/rom/dev/src/start.S b/rom/dev/src/start.S index a18a9750d6..8d97a71b6b 100644 --- a/rom/dev/src/start.S +++ b/rom/dev/src/start.S @@ -104,13 +104,20 @@ _start: andi t1, t1, 0x3 bne t1, x0, post_ecc_init + // // Cold Boot + // + + // Zero ICCM la a0, ICCM_ORG // dest la a1, ICCM_SIZE // len call _zero_mem256 - la a0, DCCM_ORG // dest - la a1, DCCM_SIZE // len + // Zero out post-CFI Entropy region + // CFI region is used as an additional entropy source for CFI RNG, + // hence it is not cleared. + la a0, DCCM_POST_CFI_ENTROPY_ORG // dest + la a1, DCCM_POST_CFI_ENTROPY_SIZE // len call _zero_mem256 post_ecc_init: diff --git a/rom/dev/test-fw/asm_tests.rs b/rom/dev/test-fw/asm_tests.rs index 3e685c1d86..4fa1947c12 100644 --- a/rom/dev/test-fw/asm_tests.rs +++ b/rom/dev/test-fw/asm_tests.rs @@ -13,6 +13,7 @@ core::arch::global_asm!(include_str!("../src/start.S")); mod exception; use caliptra_drivers::cprintln; +use caliptra_drivers::memory_layout::*; use caliptra_drivers::ExitCtrl; #[no_mangle] @@ -67,17 +68,32 @@ unsafe fn is_zeroed(mut ptr: *const u32, mut size: usize) -> bool { extern "C" { fn _zero_mem256(dest: *mut u32, len: usize); fn _copy_mem32(dest: *mut u32, src: *const u32, len: usize); + fn _zero_mem32(dest: *mut u32, len: usize); + static mut DCCM_POST_CFI_ENTROPY_ORG: u8; + static mut DCCM_POST_CFI_ENTROPY_SIZE: u8; } #[no_mangle] pub extern "C" fn rom_entry() -> ! { const SIZEOF_U32: usize = core::mem::size_of::(); unsafe { + let dccm_post_cfi_entropy_org = (&mut DCCM_POST_CFI_ENTROPY_ORG as *mut u8) as usize; + let dccm_post_cfi_entropy_size = (&mut DCCM_POST_CFI_ENTROPY_SIZE as *mut u8) as usize; + // Test that memory is cleared at startup assert!(is_zeroed(0x4000_0000 as *const u32, 1024 * 128)); - // Check everything but the last 3k, which might contain non-zero stack bytes - assert!(is_zeroed(0x5000_0000 as *const u32, 1024 * (128 - 3))); + // Test if the DCCM region after the CFI entropy is cleared, except for the last 3k, which might contain non-zero stack bytes + assert!(is_zeroed( + dccm_post_cfi_entropy_org as *const u32, + dccm_post_cfi_entropy_size - (3 * 1024) + )); + + // Check if CFI entropy source is not cleared. + assert_ne!((CFI_XO_S0_ORG as *const u32).read_volatile(), 0); + assert_ne!((CFI_XO_S1_ORG as *const u32).read_volatile(), 0); + assert_ne!((CFI_XO_S2_ORG as *const u32).read_volatile(), 0); + assert_ne!((CFI_XO_S3_ORG as *const u32).read_volatile(), 0); // Test _zero_mem256 diff --git a/test-harness/src/lib.rs b/test-harness/src/lib.rs index 843ab318fa..505f9de627 100644 --- a/test-harness/src/lib.rs +++ b/test-harness/src/lib.rs @@ -108,6 +108,15 @@ macro_rules! test_suite { } } + #[no_mangle] + extern "C" fn cfi_panic_handler(code: u32) -> ! { + println!("CFI Panic code=0x{:08X}", code); + + caliptra_drivers::report_fw_error_fatal(0xdead2); + + caliptra_drivers::ExitCtrl::exit(u32::MAX) + } + #[no_mangle] pub extern "C" fn main() { $( diff --git a/test/tests/smoke_test.rs b/test/tests/smoke_test.rs index 8b78c0fe19..b19904a739 100644 --- a/test/tests/smoke_test.rs +++ b/test/tests/smoke_test.rs @@ -588,10 +588,11 @@ fn test_rt_wdt_timeout() { const RUNTIME_GLOBAL_WDT_EPIRED: u32 = 0x000E001F; let rom = caliptra_builder::build_firmware_rom(&firmware::ROM_WITH_UART).unwrap(); + // TODO: Don't hard-code these; maybe measure from a previous boot? let rt_wdt_timeout_cycles = if cfg!(any(feature = "verilator", feature = "fpga_realtime")) { 27_000_000 } else { - 2_700_000 + 2_720_000 }; let security_state = *caliptra_hw_model::SecurityState::default().set_debug_locked(true); @@ -604,7 +605,11 @@ fn test_rt_wdt_timeout() { let mut hw = run_test(None, None, Some(init_params)); - hw.step_until(|m| m.soc_ifc().cptra_fw_error_fatal().read() == RUNTIME_GLOBAL_WDT_EPIRED); + hw.step_until(|m| m.soc_ifc().cptra_fw_error_fatal().read() != 0); + assert_eq!( + hw.soc_ifc().cptra_fw_error_fatal().read(), + RUNTIME_GLOBAL_WDT_EPIRED + ) } #[test] @@ -629,5 +634,9 @@ fn test_fmc_wdt_timeout() { let mut hw = caliptra_test::run_test(None, None, Some(init_params)); - hw.step_until(|m| m.soc_ifc().cptra_fw_error_fatal().read() == FMC_GLOBAL_WDT_EPIRED); + hw.step_until(|m| m.soc_ifc().cptra_fw_error_fatal().read() != 0); + assert_eq!( + hw.soc_ifc().cptra_fw_error_fatal().read(), + FMC_GLOBAL_WDT_EPIRED + ); }