From 3f7dce3052d028e70efcd90aaa58314360b604ee Mon Sep 17 00:00:00 2001 From: bunnie Date: Thu, 4 Dec 2025 15:38:57 +0800 Subject: [PATCH] loader hardening The loader is only lightly hardened at this point. Some minmal random delay + bollards inserted. The signature check routine inherits all the hardening of boot0 because it uses the same code. PLL mode is checked at least once. more loader hardening will happen in a future PR - at the moment, we're focused on boot0/boot1 hardening. --- loader/src/main.rs | 35 ++++++++++++++++--- loader/src/platform/bao1x/bao1x.rs | 55 +++++++++--------------------- loader/src/platform/bao1x/swap.rs | 9 +++-- 3 files changed, 54 insertions(+), 45 deletions(-) diff --git a/loader/src/main.rs b/loader/src/main.rs index 46a06b6ec..11b08dff5 100644 --- a/loader/src/main.rs +++ b/loader/src/main.rs @@ -254,6 +254,14 @@ pub unsafe extern "C" fn rust_entry(signed_buffer: *const usize, signature: u32) } } + #[cfg(feature = "bao1x")] + let mut csprng = bao1x_hal::hardening::Csprng::new(); + #[cfg(feature = "bao1x")] + csprng.random_delay(); + #[cfg(feature = "bao1x")] + // it's security-important to ensure we're running off the PLL + bao1x_hal::hardening::check_pll(); + // Run kernel image validation now that the heap is set up. #[cfg(feature = "bao1x")] let detached_app = { @@ -270,13 +278,18 @@ pub unsafe extern "C" fn rust_entry(signed_buffer: *const usize, signature: u32) ], false, None, + Some(&mut csprng), ) { - Ok((k, tag)) => { + Ok((k, k2, tag)) => { println!( - "*** Kernel signature check by key @ {}({}) OK ***", + "*** Kernel signature check by key @ {}/{}({}) OK ***", k, + k2, core::str::from_utf8(&tag).unwrap_or("invalid tag") ); + if k != k2 { + bao1x_hal::sigcheck::die_no_std(); + } // k is just a nominal slot number. If either match, assume we are dealing with a // developer image. if tag == *bao1x_api::pubkeys::KEYSLOT_INITIAL_TAGS[bao1x_api::pubkeys::DEVELOPER_KEY_SLOT] @@ -313,15 +326,20 @@ pub unsafe extern "C" fn rust_entry(signed_buffer: *const usize, signature: u32) &[FunctionCode::App as u32, FunctionCode::UpdatedApp as u32], false, None, + Some(&mut csprng), ) { - Ok((k, tag)) => { + Ok((k, k2, tag)) => { println!( - "*** Detached app signature check by key @ {}({}) OK ***", + "*** Detached app signature check by key @ {}/{}({}) OK ***", k, + k2, core::str::from_utf8(&tag).unwrap_or("invalid tag") ); // k is just a nominal slot number. If either match, assume we are dealing with a // developer image. + if k != k2 { + bao1x_hal::sigcheck::die_no_std(); + } if tag == *bao1x_api::pubkeys::KEYSLOT_INITIAL_TAGS[bao1x_api::pubkeys::DEVELOPER_KEY_SLOT] || k == bao1x_api::pubkeys::DEVELOPER_KEY_SLOT @@ -353,6 +371,15 @@ pub unsafe extern "C" fn rust_entry(signed_buffer: *const usize, signature: u32) false } }; + + #[cfg(feature = "bao1x")] + { + // Follow up the mesh check in loader - it takes 100ms or so for the mesh to settle, we can't afford + // to wait that long in boot1. + let one_way = bao1x_hal::acram::OneWayCounter::new(); + bao1x_hal::hardening::mesh_check_and_react(&mut csprng, &one_way); + } + #[cfg(not(feature = "bao1x"))] let detached_app = false; diff --git a/loader/src/platform/bao1x/bao1x.rs b/loader/src/platform/bao1x/bao1x.rs index bc224bc5b..425835cb0 100644 --- a/loader/src/platform/bao1x/bao1x.rs +++ b/loader/src/platform/bao1x/bao1x.rs @@ -256,8 +256,7 @@ pub unsafe fn init_clock_asic(freq_hz: u32) -> u32 { const UNIT_MHZ: u32 = 1000 * 1000; const PFD_F_MHZ: u32 = 16; const FREQ_0: u32 = 16 * UNIT_MHZ; - const FREQ_OSC_MHZ: u32 = 48; // Actually 48MHz - const M: u32 = FREQ_OSC_MHZ / PFD_F_MHZ; // - 1; // OSC input was 24, replace with 48 + const M: u32 = bao1x_api::FREQ_OSC_MHZ / PFD_F_MHZ; // - 1; // OSC input was 24, replace with 48 const TBL_Q: [u16; 7] = [ // keep later DIV even number as possible @@ -290,19 +289,19 @@ pub unsafe fn init_clock_asic(freq_hz: u32) -> u32 { daric_cgu.add(utra::sysctrl::SFR_CGUFD_CFGFDCR_0_4_4.offset()).write_volatile(0x070f); // pclk // calculate perclk divider. Target 100MHz. - let (min_cycle, fd, perclk) = if let Some((min_cycle, fd, perclk)) = clk_to_per(freq_hz / 1_000_000, 100) - { - daric_cgu - .add(utra::sysctrl::SFR_CGUFDPER.offset()) - .write_volatile((min_cycle as u32) << 16 | (fd as u32) << 8 | fd as u32); - (min_cycle, fd, perclk * 1_000_000) - } else if freq_hz > 400_000_000 { - daric_cgu.add(utra::sysctrl::SFR_CGUFDPER.offset()).write_volatile(0x07_ff_ff); - (7, 0xff, freq_hz / 8) - } else { - daric_cgu.add(utra::sysctrl::SFR_CGUFDPER.offset()).write_volatile(0x03_ff_ff); - (3, 0xff, freq_hz / 4) - }; + let (min_cycle, fd, perclk) = + if let Some((min_cycle, fd, perclk)) = bao1x_api::clk_to_per(freq_hz / 1_000_000, 100) { + daric_cgu + .add(utra::sysctrl::SFR_CGUFDPER.offset()) + .write_volatile((min_cycle as u32) << 16 | (fd as u32) << 8 | fd as u32); + (min_cycle, fd, perclk * 1_000_000) + } else if freq_hz > 400_000_000 { + daric_cgu.add(utra::sysctrl::SFR_CGUFDPER.offset()).write_volatile(0x07_ff_ff); + (7, 0xff, freq_hz / 8) + } else { + daric_cgu.add(utra::sysctrl::SFR_CGUFDPER.offset()).write_volatile(0x03_ff_ff); + (3, 0xff, freq_hz / 4) + }; /* perclk fields: min-cycle-lp | min-cycle | fd-lp | fd @@ -354,13 +353,13 @@ pub unsafe fn init_clock_asic(freq_hz: u32) -> u32 { crate::println!("...done"); cgu.wo(sysctrl::SFR_CGUSEL1, 1); - cgu.wo(sysctrl::SFR_CGUFSCR, FREQ_OSC_MHZ); + cgu.wo(sysctrl::SFR_CGUFSCR, bao1x_api::FREQ_OSC_MHZ); cgu.wo(sysctrl::SFR_CGUSET, 0x32); let duart = utra::duart::HW_DUART_BASE as *mut u32; duart.add(utra::duart::SFR_CR.offset()).write_volatile(0); // set the ETUC now that we're on the xosc. - duart.add(utra::duart::SFR_ETUC.offset()).write_volatile(FREQ_OSC_MHZ); + duart.add(utra::duart::SFR_ETUC.offset()).write_volatile(bao1x_api::FREQ_OSC_MHZ); duart.add(utra::duart::SFR_CR.offset()).write_volatile(1); if freq_hz <= 1_000_000 { @@ -516,28 +515,6 @@ pub fn coreuser_config() { crate::println!("coreuser locked!"); } -/// Takes in the top clock in MHz, desired perclk in MHz, and returns a tuple of -/// (min cycle, fd, actual freq) -/// *tested* -#[allow(dead_code)] -pub fn clk_to_per(top_in_mhz: u32, perclk_in_mhz: u32) -> Option<(u8, u8, u32)> { - let fd_platonic = ((256 * perclk_in_mhz) / (top_in_mhz / 2)).min(256); - if fd_platonic > 0 { - let fd = fd_platonic - 1; - let min_cycle = (2 * (256 / (fd + 1))).max(1); - let min_freq = top_in_mhz / min_cycle; - let target_freq = top_in_mhz * (fd + 1) / 512; - let actual_freq = target_freq.max(min_freq); - if fd < 256 && min_cycle < 256 && min_cycle > 0 { - Some(((min_cycle - 1) as u8, fd as u8, actual_freq)) - } else { - None - } - } else { - None - } -} - /// Delay with a given system clock frequency. Useful during power mode switching. #[allow(dead_code)] pub fn delay_at_sysfreq(ms: usize, sysclk_freq: u32) { diff --git a/loader/src/platform/bao1x/swap.rs b/loader/src/platform/bao1x/swap.rs index 9fbc50aa7..929a3a79d 100644 --- a/loader/src/platform/bao1x/swap.rs +++ b/loader/src/platform/bao1x/swap.rs @@ -242,13 +242,18 @@ impl SwapHal { &[FunctionCode::Swap as u32, FunctionCode::UpdatedSwap as u32], false, Some(&mut hal.flash_spim), + None, ) { - Ok((k, tag)) => { + Ok((k, k2, tag)) => { println!( - "*** Swap signature check by key @ {}({}) OK ***", + "*** Swap signature check by key @ {}/{}({}) OK ***", k, + k2, core::str::from_utf8(&tag).unwrap_or("invalid tag") ); + if k != k2 { + bao1x_hal::sigcheck::die_no_std(); + } // k is just a nominal slot number. If either match, assume we are dealing // with a developer image. if tag