diff --git a/Cargo.lock b/Cargo.lock index 9fe3256cf..9adec6ce9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2675,6 +2675,7 @@ dependencies = [ "cortex-m", "cortex-m-rt", "digest", + "drv-lpc55-flash", "hubpack", "lib-dice", "lib-lpc55-usart", diff --git a/drv/lpc55-flash/src/lib.rs b/drv/lpc55-flash/src/lib.rs index 16356eb72..b6b83caf1 100644 --- a/drv/lpc55-flash/src/lib.rs +++ b/drv/lpc55-flash/src/lib.rs @@ -20,6 +20,10 @@ pub const BYTES_PER_FLASH_WORD: usize = 16; /// Number of bytes per flash page. pub const BYTES_PER_FLASH_PAGE: usize = 512; +/// Flash words per flash page +pub const WORDS_PER_FLASH_PAGE: usize = + BYTES_PER_FLASH_PAGE / BYTES_PER_FLASH_WORD; + /// Flash driver handle. Wraps a pointer to the flash register set and provides /// encapsulated operations. /// @@ -336,6 +340,67 @@ impl<'a> Flash<'a> { w }); } + + pub fn write_page( + &mut self, + addr: u32, + flash_page: &[u8; BYTES_PER_FLASH_PAGE], + wait: fn() -> (), + ) -> Result<(), FlashTimeout> { + let start_word = addr / BYTES_PER_FLASH_WORD as u32; + self.start_erase_range( + start_word..=start_word + ((WORDS_PER_FLASH_PAGE as u32) - 1), + ); + self.wait_for_erase_or_program(wait)?; + + for (i, row) in + flash_page.chunks_exact(BYTES_PER_FLASH_WORD).enumerate() + { + let row: &[u8; BYTES_PER_FLASH_WORD] = row.try_into().unwrap(); + + self.start_write_row(i as u32, row); + while !self.poll_write_result() {} + } + + self.start_program(start_word); + self.wait_for_erase_or_program(wait)?; + Ok(()) + } + + fn wait_for_erase_or_program( + &mut self, + wait: fn() -> (), + ) -> Result<(), FlashTimeout> { + loop { + if let Some(result) = self.poll_erase_or_program_result() { + return result; + } + + self.enable_interrupt_sources(); + wait(); + self.disable_interrupt_sources(); + } + } + + pub fn is_page_range_programmed(&mut self, addr: u32, len: u32) -> bool { + for i in (addr..addr + len).step_by(BYTES_PER_FLASH_PAGE) { + let word = i / (BYTES_PER_FLASH_WORD as u32); + // the blank check is inclusive so we need to end before + // the last word + let end_word = (word + WORDS_PER_FLASH_PAGE as u32) - 1; + self.start_blank_check(word..=end_word); + loop { + if let Some(s) = self.poll_blank_check_result() { + match s { + ProgramState::Blank => return false, + _ => break, + } + } + } + } + + true + } } /// Raw status bits from the flash controller. diff --git a/drv/lpc55-update-server/src/main.rs b/drv/lpc55-update-server/src/main.rs index 6f0881e27..6b8882f54 100644 --- a/drv/lpc55-update-server/src/main.rs +++ b/drv/lpc55-update-server/src/main.rs @@ -11,7 +11,7 @@ use core::convert::Infallible; use core::mem::MaybeUninit; -use drv_lpc55_flash::{BYTES_PER_FLASH_PAGE, BYTES_PER_FLASH_WORD}; +use drv_lpc55_flash::BYTES_PER_FLASH_PAGE; use drv_lpc55_update_api::{ RawCabooseError, RotBootInfo, SlotId, SwitchDuration, UpdateTarget, }; @@ -403,11 +403,13 @@ impl idl::InOrderUpdateImpl for ServerImpl<'_> { // Note that the page write machinery uses page numbers. This // should probably change. But, for now, we must divide our word // number by 32. - do_raw_page_write( - &mut self.flash, - CFPA_SCRATCH_FLASH_WORD / 32, - &cfpa_bytes, - )?; + self.flash + .write_page( + CFPA_SCRATCH_FLASH_WORD, + &cfpa_bytes, + wait_for_flash_interrupt, + ) + .map_err(|_| UpdateError::FlashError)?; } } @@ -713,82 +715,16 @@ fn do_block_write( None => return Err(UpdateError::OutOfBounds), }; - // write_addr is a byte address; convert it back to a page number, but this - // time, an absolute page number in the flash device. - let page_num = write_addr / BYTES_PER_FLASH_PAGE as u32; - - do_raw_page_write(flash, page_num, flash_page) -} - -/// Performs an erase-write sequence to a single page within the raw flash -/// device. This function is capable of writing outside of any image slot, which -/// is important for doing CFPA updates. If you're writing to an image slot, use -/// `do_block_write`. -fn do_raw_page_write( - flash: &mut drv_lpc55_flash::Flash<'_>, - page_num: u32, - flash_page: &[u8; BYTES_PER_FLASH_PAGE], -) -> Result<(), UpdateError> { - // We regularly need the number of flash words per flash page below, and - // specifically as a u32, so: - static_assertions::const_assert_eq!( - BYTES_PER_FLASH_PAGE % BYTES_PER_FLASH_WORD, - 0 - ); - const WORDS_PER_PAGE: u32 = - (BYTES_PER_FLASH_PAGE / BYTES_PER_FLASH_WORD) as u32; - - // The hardware operates in terms of word numbers, never page numbers. - // Convert the page number to the number of the first word in that page. - // (This is equivalent to multiplying by 32 but named constants are nice.) - let word_num = page_num * WORDS_PER_PAGE; - - // Step one: erase the page. Note that this range is INCLUSIVE. The hardware - // will happily erase multiple pages if you let it. We don't want that here. - flash.start_erase_range(word_num..=word_num + (WORDS_PER_PAGE - 1)); - wait_for_erase_or_program(flash)?; - - // Step two: Transfer each 16-byte flash word (page row) into the write - // registers in the flash controller. - for (i, row) in flash_page.chunks_exact(BYTES_PER_FLASH_WORD).enumerate() { - // TODO: this will be unnecessary if array_chunks stabilizes - let row: &[u8; BYTES_PER_FLASH_WORD] = row.try_into().unwrap_lite(); - - flash.start_write_row(i as u32, row); - while !flash.poll_write_result() { - // spin - supposed to be very quick in hardware. - } - } - - // Step three: program the whole page into non-volatile storage by naming - // the first word in the target page. (Any word in the page will do, - // actually, but we've conveniently got the first word available.) - flash.start_program(word_num); - wait_for_erase_or_program(flash)?; - - Ok(()) + flash + .write_page(write_addr, flash_page, wait_for_flash_interrupt) + .map_err(|_| UpdateError::FlashError) } -/// Utility function that does an interrupt-driven poll and sleep while the -/// flash controller finishes a write or erase. -fn wait_for_erase_or_program( - flash: &mut drv_lpc55_flash::Flash<'_>, -) -> Result<(), UpdateError> { - loop { - if let Some(result) = flash.poll_erase_or_program_result() { - return result.map_err(|_| UpdateError::FlashError); - } - - flash.enable_interrupt_sources(); - sys_irq_control(notifications::FLASH_IRQ_MASK, true); - // RECV from the kernel cannot produce an error, so ignore it. - let _ = sys_recv_closed( - &mut [], - notifications::FLASH_IRQ_MASK, - TaskId::KERNEL, - ); - flash.disable_interrupt_sources(); - } +fn wait_for_flash_interrupt() { + sys_irq_control(notifications::FLASH_IRQ_MASK, true); + // RECV from the kernel cannot produce an error, so ignore it. + let _ = + sys_recv_closed(&mut [], notifications::FLASH_IRQ_MASK, TaskId::KERNEL); } fn same_image(which: UpdateTarget) -> bool { diff --git a/drv/update-api/hiffy-rot-flash.sh b/drv/update-api/hiffy-rot-flash.sh index 455046c4d..24c461a08 100755 --- a/drv/update-api/hiffy-rot-flash.sh +++ b/drv/update-api/hiffy-rot-flash.sh @@ -11,13 +11,13 @@ # NOTE: Make sure you are not runnning this on an ancient version of bash on Mac OSX -set -x +set -ex image=$1 image_type=$2 block_size=512 -binsize=`ls -la $image | cut -w -f 5` +binsize=`ls -la $image | cut -d ' ' -f 5` numblocks=$(("${binsize}" / "${block_size}")) total_before_last_block=$((numblocks * "${block_size}")) lastblocksize=$(("${binsize}" - "${total_before_last_block}")) @@ -63,10 +63,10 @@ write_blocks() { humility -a "${ARCHIVE}" hiffy -c Update.prep_image_update -a image_type="${image_type}" # Begin by invalidating the header which resides at offset 0x130 (in block zero). -write_blocks 0 0 /dev/zero -write_blocks 1 "${loopend}" "${image}" +#write_blocks 0 0 /dev/zero +write_blocks 0 "${loopend}" "${image}" humility -a "${ARCHIVE}" hiffy -c Update.write_one_block -a block_num="${numblocks}" -i <(dd if="${image}" bs=1 count="${lastblocksize}" skip="${total_before_last_block}") # Lastly, write the correct block zero. -write_blocks 0 0 "${image}" +#write_blocks 0 0 "${image}" humility -a "${ARCHIVE}" hiffy -c Update.finish_image_update diff --git a/lib/lpc55-romapi/src/lib.rs b/lib/lpc55-romapi/src/lib.rs index 2c60ed475..e37523b36 100644 --- a/lib/lpc55-romapi/src/lib.rs +++ b/lib/lpc55-romapi/src/lib.rs @@ -102,7 +102,7 @@ pub struct FfrKeyStore { prince2_key_code: [u32; 13], } -pub const FLASH_PAGE_SIZE: usize = 512; +const FLASH_PAGE_SIZE: usize = 512; const ACTIVATION_CODE_SIZE: usize = 1192; @@ -383,35 +383,6 @@ struct FlashConfig { mode_config: FlashModeConfig, } -fn get_system_clock_speed_mhz() -> u32 { - let syscon = unsafe { &*lpc55_pac::SYSCON::ptr() }; - - let a = syscon.mainclksela.read().bits(); - let b = syscon.mainclkselb.read().bits(); - let div = syscon.ahbclkdiv.read().bits(); - - // corresponds to FRO 96 MHz, see 4.5.34 in user manual - const EXPECTED_MAINCLKSELA: u32 = 3; - // corresponds to Main Clock A, see 4.5.45 in user manual - const EXPECTED_MAINCLKSELB: u32 = 0; - - // We expect the 96MHz clock to be used based on the ROM. - // If it's not there are probably more (bad) surprises coming - // and panicking is reasonable - if a != EXPECTED_MAINCLKSELA || b != EXPECTED_MAINCLKSELB { - panic!(); - } - - if div == 0 { - 96 - } else { - 48 - } -} - -// Magic from the docs! -const ERASE_KEY: u32 = 0x6b65666c; - const LPC55_ROM_TABLE: *const BootloaderTree = 0x130010f0 as *const BootloaderTree; @@ -430,18 +401,6 @@ pub fn bootrom() -> &'static BootRom { unsafe { &*(LPC55_BOOT_ROM) } } -fn handle_flash_status(ret: u32) -> Result<(), FlashStatus> { - let result = match FlashStatus::from_u32(ret) { - Some(a) => a, - None => return Err(FlashStatus::Unknown), - }; - - match result { - FlashStatus::Success => Ok(()), - a => Err(a), - } -} - fn handle_skboot_status(ret: u32) -> Result<(), ()> { let result = match SkbootStatus::from_u32(ret) { Some(a) => a, @@ -479,15 +438,6 @@ fn handle_bootloader_status(ret: u32) -> Result<(), BootloaderStatus> { } } -// Checks that address and length are flash page aligned -fn check_addr_len_alignment(addr: u32, len: u32) -> Result<(), FlashStatus> { - if addr % 512 == 0 && len % 512 == 0 { - Ok(()) - } else { - Err(FlashStatus::AlignmentError) - } -} - #[allow(clippy::result_unit_err)] pub unsafe fn authenticate_image(addr: u32) -> Result<(), ()> { let mut result: u32 = 0; @@ -539,247 +489,3 @@ pub unsafe fn load_sb2_image( image.len() as u32, )) } - -pub unsafe fn flash_erase(addr: u32, len: u32) -> Result<(), FlashStatus> { - // XXX More validation of buffer? - // We expect the caller to have dropped the clocks appropriately - let mut f: FlashConfig = Default::default(); - f.mode_config.sys_freq_in_mhz = get_system_clock_speed_mhz(); - - check_addr_len_alignment(addr, len)?; - - handle_flash_status((bootloader_tree() - .flash_driver - .version1_flash_driver - .flash_init)(&mut f))?; - - handle_flash_status((bootloader_tree() - .flash_driver - .version1_flash_driver - .ffr_init)(&mut f))?; - - handle_flash_status((bootloader_tree() - .flash_driver - .version1_flash_driver - .flash_erase)(&mut f, addr, len, ERASE_KEY)) -} - -pub unsafe fn flash_write( - addr: u32, - buffer: *mut u8, - len: u32, -) -> Result<(), FlashStatus> { - // XXX More validation of buffer? - // XXX docs say we need to drop the clocks? - let mut f: FlashConfig = Default::default(); - f.mode_config.sys_freq_in_mhz = get_system_clock_speed_mhz(); - - check_addr_len_alignment(addr, len)?; - - handle_flash_status((bootloader_tree() - .flash_driver - .version1_flash_driver - .flash_init)(&mut f))?; - - handle_flash_status((bootloader_tree() - .flash_driver - .version1_flash_driver - .ffr_init)(&mut f))?; - - // XXX so much more validation needed - - handle_flash_status((bootloader_tree() - .flash_driver - .version1_flash_driver - .flash_program)(&mut f, addr, buffer, len)) -} - -/* - * The LPC55 will hard fault if it accesses an unprogrammed area. This function - * uses the ROM APIs to make sure the flash is programmed before we access - */ -pub fn validate_programmed(start: u32, len: u32) -> bool { - let mut f: FlashConfig = Default::default(); - f.mode_config.sys_freq_in_mhz = get_system_clock_speed_mhz(); - - let ret = handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .flash_init)(&mut f) - }); - - if ret.is_err() { - return false; - } - - let ret = handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .ffr_init)(&mut f) - }); - - if ret.is_err() { - return false; - } - - // flash_verify_erased returns true iff all flash pages in the range are - // erased. If at least one is programmed, it returns false. Since we want - // to know that all pages are programmed, we need to check each page - // individually. - let page_size = FLASH_PAGE_SIZE as u32; - let page_aligned_start = start - (start % page_size); - let page_aligned_end = { - let end = start + len; - (end + page_size - 1) - ((end + page_size - 1) % page_size) - }; - - for page in - (page_aligned_start..page_aligned_end).step_by(page_size as usize) - { - let v = handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .flash_verify_erase)(&mut f, page, page_size) - }); - - match v { - // Page is erased - Ok(_) => return false, - // Page is programmed - Err(FlashStatus::CommandFailure) => continue, - // Some other error was encountered - Err(_) => return false, - } - } - - true -} - -pub fn get_key_code( - idx: FFRKeyType, - key_code: &mut [u32; 13], -) -> Result<(), FlashStatus> { - let mut f: FlashConfig = Default::default(); - f.mode_config.sys_freq_in_mhz = get_system_clock_speed_mhz(); - - handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .flash_init)(&mut f) - })?; - - handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .ffr_init)(&mut f) - })?; - - handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .ffr_keystore_get_kc)(&mut f, key_code, idx as u32) - }) -} - -pub fn get_activation_code( - ac: &mut [u32; ACTIVATION_CODE_SIZE / 4], -) -> Result<(), FlashStatus> { - let mut f: FlashConfig = Default::default(); - f.mode_config.sys_freq_in_mhz = get_system_clock_speed_mhz(); - - handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .flash_init)(&mut f) - })?; - - handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .ffr_init)(&mut f) - })?; - - handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .ffr_keystore_get_ac)(&mut f, ac) - }) -} - -pub fn write_keystore(key_store: &mut FfrKeyStore) -> Result<(), FlashStatus> { - let mut f: FlashConfig = Default::default(); - f.mode_config.sys_freq_in_mhz = get_system_clock_speed_mhz(); - - handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .flash_init)(&mut f) - })?; - - handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .ffr_init)(&mut f) - })?; - - handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .ffr_keystore_write)(&mut f, key_store) - }) -} - -pub fn get_cmpa_data( - data: &mut [u32], - offset: u32, - len: u32, -) -> Result<(), FlashStatus> { - assert!(len <= (data.len() as u32)); - - let mut f: FlashConfig = Default::default(); - f.mode_config.sys_freq_in_mhz = get_system_clock_speed_mhz(); - - handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .flash_init)(&mut f) - })?; - - handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .ffr_init)(&mut f) - })?; - - handle_flash_status(unsafe { - (bootloader_tree() - .flash_driver - .version1_flash_driver - .ffr_get_customer_data)( - &mut f, data.as_mut_ptr(), offset, len - ) - }) -} - -// Keep this as a sample function for now -pub fn get_bootloader_version() -> u32 { - let version = &bootloader_tree().version; - (version.bugfix as u32) - | ((version.minor as u32) << 8) - | ((version.major as u32) << 16) - | ((version.name as u32) << 24) -} diff --git a/lib/lpc55-rot-startup/Cargo.toml b/lib/lpc55-rot-startup/Cargo.toml index 4a79c7532..5f718d8cb 100644 --- a/lib/lpc55-rot-startup/Cargo.toml +++ b/lib/lpc55-rot-startup/Cargo.toml @@ -30,6 +30,7 @@ lib-lpc55-usart = { path = "../lpc55-usart", optional = true } lpc55_romapi = { path = "../lpc55-romapi" } stage0-handoff = { path = "../stage0-handoff"} unwrap-lite = { path = "../unwrap-lite" } +drv-lpc55-flash.path = "../../drv/lpc55-flash" [build-dependencies] build-util = { path = "../../build/util" } diff --git a/lib/lpc55-rot-startup/src/dice.rs b/lib/lpc55-rot-startup/src/dice.rs index 9ef3ede78..ff53cb6fe 100644 --- a/lib/lpc55-rot-startup/src/dice.rs +++ b/lib/lpc55-rot-startup/src/dice.rs @@ -4,6 +4,7 @@ use crate::Handoff; use core::mem; +use drv_lpc55_flash::Flash; use lib_dice::{ AliasCertBuilder, AliasData, AliasOkm, Cdi, CdiL1, CertData, CertSerialNumber, DeviceIdCertBuilder, DeviceIdOkm, IntermediateCert, @@ -32,14 +33,17 @@ pub struct MfgResult { } /// Generate stuff associated with the manufacturing process. -fn gen_mfg_artifacts(peripherals: &Peripherals) -> MfgResult { +fn gen_mfg_artifacts( + peripherals: &Peripherals, + flash: &mut Flash, +) -> MfgResult { // Select manufacturing process based on feature. This module assumes // that one of the manufacturing flavors has been enabled. cfg_if::cfg_if! { if #[cfg(feature = "dice-mfg")] { - dice_mfg_usart::gen_mfg_artifacts_usart(peripherals) + dice_mfg_usart::gen_mfg_artifacts_usart(peripherals, flash) } else if #[cfg(feature = "dice-self")] { - gen_mfg_artifacts_self(peripherals) + gen_mfg_artifacts_self(peripherals, flash) } else { compile_error!("No DICE manufacturing process selected."); } @@ -50,7 +54,10 @@ fn gen_mfg_artifacts(peripherals: &Peripherals) -> MfgResult { /// certificates. This is expected to be useful for development systems that /// cannot easily have identities certified by an external CA. #[cfg(feature = "dice-self")] -fn gen_mfg_artifacts_self(peripherals: &Peripherals) -> MfgResult { +fn gen_mfg_artifacts_self( + peripherals: &Peripherals, + _flash: &mut Flash, +) -> MfgResult { use core::ops::{Deref, DerefMut}; use lib_dice::{DiceMfg, PersistIdSeed, SelfMfg}; use zeroize::Zeroizing; @@ -203,7 +210,7 @@ fn gen_rng_artifacts(cdi_l1: &CdiL1, handoff: &Handoff) { // will contain sensitive material, from commingling with the caller. Do not // remove it without reconsidering our stack zeroization approach. #[inline(never)] -pub fn run(handoff: &Handoff, peripherals: &Peripherals) { +pub fn run(handoff: &Handoff, peripherals: &Peripherals, flash: &mut Flash) { // The memory we use to handoff DICE artifacts is already enabled // in `main()`; @@ -215,7 +222,7 @@ pub fn run(handoff: &Handoff, peripherals: &Peripherals) { None => return, }; - let mut mfg_data = gen_mfg_artifacts(&peripherals); + let mut mfg_data = gen_mfg_artifacts(&peripherals, flash); let deviceid_keypair = gen_deviceid_artifacts( &cdi, diff --git a/lib/lpc55-rot-startup/src/dice_mfg_usart.rs b/lib/lpc55-rot-startup/src/dice_mfg_usart.rs index 13d459577..1d5ce0a48 100644 --- a/lib/lpc55-rot-startup/src/dice_mfg_usart.rs +++ b/lib/lpc55-rot-startup/src/dice_mfg_usart.rs @@ -4,6 +4,7 @@ use crate::dice::{MfgResult, KEYCODE_LEN, KEY_INDEX, SEED_LEN}; use core::ops::{Deref, DerefMut}; +use drv_lpc55_flash::{Flash, BYTES_PER_FLASH_PAGE}; use hubpack::SerializedSize; use lib_dice::{ CertSerialNumber, DiceMfg, IntermediateCert, PersistIdCert, PersistIdSeed, @@ -15,13 +16,13 @@ use lpc55_puf::Puf; use salty::signature::Keypair; use serde::{Deserialize, Serialize}; use static_assertions as sa; +use unwrap_lite::UnwrapLite; use zeroize::Zeroizing; macro_rules! flash_page_align { ($size:expr) => { - if $size % lpc55_romapi::FLASH_PAGE_SIZE != 0 { - ($size & !(lpc55_romapi::FLASH_PAGE_SIZE - 1)) - + lpc55_romapi::FLASH_PAGE_SIZE + if $size % BYTES_PER_FLASH_PAGE != 0 { + ($size & !(BYTES_PER_FLASH_PAGE - 1)) + BYTES_PER_FLASH_PAGE } else { $size } @@ -34,13 +35,9 @@ sa::const_assert!( >= flash_page_align!(DiceState::MAX_SIZE) ); -// ensure FLASH_DICE_MFG start and end are alligned -sa::const_assert!( - FLASH_DICE_MFG.end as usize % lpc55_romapi::FLASH_PAGE_SIZE == 0 -); -sa::const_assert!( - FLASH_DICE_MFG.start as usize % lpc55_romapi::FLASH_PAGE_SIZE == 0 -); +// ensure DICE_FLASH start and end are alligned +sa::const_assert!(FLASH_DICE_MFG.end as usize % BYTES_PER_FLASH_PAGE == 0); +sa::const_assert!(FLASH_DICE_MFG.start as usize % BYTES_PER_FLASH_PAGE == 0); const VERSION: u32 = 0; const MAGIC: [u8; 12] = [ @@ -109,7 +106,7 @@ impl DiceState { Ok(state) } - pub fn to_flash(&self) -> Result { + pub fn to_flash(&self, flash: &mut Flash) -> Result { let mut buf = [0u8; Self::ALIGNED_MAX_SIZE]; let header = Header::default(); @@ -119,40 +116,46 @@ impl DiceState { let offset = hubpack::serialize(&mut buf[offset..], self) .map_err(|_| DiceStateError::Serialize)?; - // SAFETY: This unsafe block relies on the caller verifying that the - // flash region being programmed is correctly aligned and sufficiently - // large to hold Self::MAX bytes. We do this by static assertion. - // TODO: error handling - unsafe { - lpc55_romapi::flash_erase( - FLASH_DICE_MFG.start as *const u32 as u32, - Self::ALIGNED_MAX_SIZE as u32, - ) - .expect("flash_erase"); - lpc55_romapi::flash_write( - FLASH_DICE_MFG.start as *const u32 as u32, - &mut buf as *mut u8, - Self::ALIGNED_MAX_SIZE as u32, - ) - .expect("flash_write"); + for (i, page) in buf.chunks_exact(BYTES_PER_FLASH_PAGE).enumerate() { + let page: &[u8; BYTES_PER_FLASH_PAGE] = + page.try_into().unwrap_lite(); + flash + .write_page( + (FLASH_DICE_MFG.start as usize + i * BYTES_PER_FLASH_PAGE) + as u32, + page, + delay, + ) + .expect("flash write"); } Ok(offset) } - pub fn is_programmed() -> bool { - lpc55_romapi::validate_programmed( - FLASH_DICE_MFG.start, + pub fn is_programmed(flash: &mut Flash) -> bool { + flash.is_page_range_programmed( + FLASH_DICE_MFG.start as u32, flash_page_align!(Header::MAX_SIZE + Self::MAX_SIZE) as u32, ) } } +fn delay() { + // Timeouts timeouts etc, this just delays for a few cycles until + // we check the flash again + for _ in 0..100 { + cortex_m::asm::nop(); + } +} + /// Generate platform identity key from PUF and manufacture the system /// by certifying this identity. The certification process uses the usart /// peripheral to exchange manufacturing data, CSR & cert with the /// manufacturing line. -fn gen_artifacts_from_mfg(peripherals: &Peripherals) -> MfgResult { +fn gen_artifacts_from_mfg( + peripherals: &Peripherals, + flash: &mut Flash, +) -> MfgResult { let puf = Puf::new(&peripherals.PUF); // Create key code for an ed25519 seed using the PUF. We use this seed @@ -201,7 +204,7 @@ fn gen_artifacts_from_mfg(peripherals: &Peripherals) -> MfgResult { intermediate_cert: dice_data.intermediate_cert, }; - dice_state.to_flash().unwrap(); + dice_state.to_flash(flash).unwrap(); MfgResult { cert_serial_number: Default::default(), @@ -246,11 +249,14 @@ fn gen_artifacts_from_flash(peripherals: &Peripherals) -> MfgResult { } } -pub fn gen_mfg_artifacts_usart(peripherals: &Peripherals) -> MfgResult { - if DiceState::is_programmed() { +pub fn gen_mfg_artifacts_usart( + peripherals: &Peripherals, + flash: &mut Flash, +) -> MfgResult { + if DiceState::is_programmed(flash) { gen_artifacts_from_flash(peripherals) } else { - gen_artifacts_from_mfg(peripherals) + gen_artifacts_from_mfg(peripherals, flash) } } diff --git a/lib/lpc55-rot-startup/src/images.rs b/lib/lpc55-rot-startup/src/images.rs index 24241025f..04814e402 100644 --- a/lib/lpc55-rot-startup/src/images.rs +++ b/lib/lpc55-rot-startup/src/images.rs @@ -3,12 +3,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use abi::{ImageHeader, ImageVectors}; -use lpc55_romapi::FLASH_PAGE_SIZE; +use drv_lpc55_flash::{Flash, BYTES_PER_FLASH_PAGE}; use sha3::{Digest, Sha3_256}; use stage0_handoff::{ImageVersion, RotImageDetails}; use unwrap_lite::UnwrapLite; -pub fn get_image_b() -> Option { +pub fn get_image_b(flash: &mut Flash) -> Option { let imageb = unsafe { &__IMAGE_B_BASE }; let img = Image { @@ -16,14 +16,14 @@ pub fn get_image_b() -> Option { vector: imageb, }; - if img.validate() { + if img.validate(flash) { Some(img) } else { None } } -pub fn get_image_a() -> Option { +pub fn get_image_a(flash: &mut Flash) -> Option { let imagea = unsafe { &__IMAGE_A_BASE }; let img = Image { @@ -31,7 +31,7 @@ pub fn get_image_a() -> Option { vector: imagea, }; - if img.validate() { + if img.validate(flash) { Some(img) } else { None @@ -51,18 +51,18 @@ extern "C" { static __vector_size: [u8; 0]; } -// FLASH_PAGE_SIZE is a usize so redefine the constant here to avoid having +// BYTES_PER_FLASH_PAGE is a usize so redefine the constant here to avoid having // to do the u32 change everywhere -const PAGE_SIZE: u32 = FLASH_PAGE_SIZE as u32; +const PAGE_SIZE: u32 = BYTES_PER_FLASH_PAGE as u32; pub struct Image { flash: Range, vector: &'static ImageVectors, } -pub fn image_details(img: Image) -> RotImageDetails { +pub fn image_details(img: Image, flash: &mut Flash) -> RotImageDetails { RotImageDetails { - digest: img.get_fwid(), + digest: img.get_fwid(flash), version: img.get_image_version(), } } @@ -86,11 +86,11 @@ impl Image { } /// Make sure all of the image flash is programmed - fn validate(&self) -> bool { + fn validate(&self, flash: &mut Flash) -> bool { let img_start = self.get_img_start(); // Start by making sure we can access the page where the vectors live - let valid = lpc55_romapi::validate_programmed(img_start, PAGE_SIZE); + let valid = flash.is_page_range_programmed(img_start, PAGE_SIZE); if !valid { return false; @@ -100,7 +100,7 @@ impl Image { // Next validate the header location is programmed let valid = - lpc55_romapi::validate_programmed(header_ptr as u32, PAGE_SIZE); + flash.is_page_range_programmed(header_ptr as u32, PAGE_SIZE); if !valid { return false; @@ -112,7 +112,7 @@ impl Image { let header = unsafe { &*header_ptr }; // Next make sure the marked image length is programmed - let valid = lpc55_romapi::validate_programmed( + let valid = flash.is_page_range_programmed( img_start, (header.total_image_len + (PAGE_SIZE - 1)) & !(PAGE_SIZE - 1), ); @@ -129,11 +129,11 @@ impl Image { return true; } - pub fn get_fwid(&self) -> [u8; 32] { + pub fn get_fwid(&self, flash: &mut Flash) -> [u8; 32] { let mut hash = Sha3_256::new(); - for start in self.flash.clone().step_by(FLASH_PAGE_SIZE) { - if lpc55_romapi::validate_programmed(start, PAGE_SIZE) { + for start in self.flash.clone().step_by(BYTES_PER_FLASH_PAGE) { + if flash.is_page_range_programmed(start, PAGE_SIZE) { // SAFETY: The addresses used in this unsafe code are all // generated by build.rs from data in the build environment. // The safety of this code is an extension of our trust in @@ -141,7 +141,7 @@ impl Image { let page = unsafe { core::slice::from_raw_parts( start as *const u8, - FLASH_PAGE_SIZE, + BYTES_PER_FLASH_PAGE, ) }; hash.update(page); diff --git a/lib/lpc55-rot-startup/src/lib.rs b/lib/lpc55-rot-startup/src/lib.rs index bed43846b..545be19b2 100644 --- a/lib/lpc55-rot-startup/src/lib.rs +++ b/lib/lpc55-rot-startup/src/lib.rs @@ -130,6 +130,8 @@ pub fn startup( let mpu = &core_peripherals.MPU; + let mut flash = drv_lpc55_flash::Flash::new(&peripherals.FLASH); + // Turn on the memory used by the handoff subsystem to dump // `RotUpdateDetails` and DICE information required by hubris. // @@ -140,7 +142,7 @@ pub fn startup( apply_memory_protection(mpu); #[cfg(any(feature = "dice-mfg", feature = "dice-self"))] - dice::run(&handoff, &peripherals); + dice::run(&handoff, &peripherals, &mut flash); nuke_stack(); @@ -149,8 +151,8 @@ pub fn startup( // Write the image details to handoff RAM. Use the address of the current // function to determine which image is running. - let img_a = images::get_image_a(); - let img_b = images::get_image_b(); + let img_a = images::get_image_a(&mut flash); + let img_b = images::get_image_b(&mut flash); let here = startup as *const u8; let active = if img_a.as_ref().map(|i| i.contains(here)).unwrap_or(false) { RotSlot::A @@ -159,8 +161,8 @@ pub fn startup( } else { panic!(); }; - let a = img_a.map(images::image_details); - let b = img_b.map(images::image_details); + let a = img_a.map(|i| images::image_details(i, &mut flash)); + let b = img_b.map(|i| images::image_details(i, &mut flash)); let details = RotBootState { active, a, b };