diff --git a/Cargo.lock b/Cargo.lock index ad380e2f0d..c485817485 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3011,6 +3011,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hif" version = "0.3.1" @@ -3488,6 +3494,7 @@ dependencies = [ "drv-lpc55-syscon-api", "drv-lpc55-update-api", "drv-update-api", + "hex-literal", "hubpack", "idol", "idol-runtime", diff --git a/Cargo.toml b/Cargo.toml index d9e8d5cb29..3445f40ddc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,7 @@ getrandom = { version = "0.2", default-features = false } goblin = { version = "0.4.3", default-features = true } # goblin::Object doesn't work without everything enabled heapless = { version = "0.7.17", default-features = false } heck = { version = "0.5.0", default-features = false } +hex-literal = { version = "0.4.1" } hkdf = { version = "0.12", default-features = false } hmac = { version = "0.12.1", default-features = false } hubpack = { version = "0.1.2", default-features = false } diff --git a/app/lpc55xpresso/app-sprot.toml b/app/lpc55xpresso/app-sprot.toml index bb6394f1bd..b1ecb6151f 100644 --- a/app/lpc55xpresso/app-sprot.toml +++ b/app/lpc55xpresso/app-sprot.toml @@ -57,7 +57,7 @@ name = "lpc55-update-server" priority = 3 stacksize = 3000 start = true -sections = {bootstate = "usbsram"} +sections = {bootstate = "usbsram", transient_override = "override"} uses = ["flash_controller", "hash_crypt"] notifications = ["flash-irq", "hashcrypt-irq"] interrupts = {"flash_controller.irq" = "flash-irq", "hash_crypt.irq" = "hashcrypt-irq"} diff --git a/app/lpc55xpresso/app.toml b/app/lpc55xpresso/app.toml index 9aea1b0431..cc03b0de53 100644 --- a/app/lpc55xpresso/app.toml +++ b/app/lpc55xpresso/app.toml @@ -51,7 +51,7 @@ priority = 3 max-sizes = {flash = 26720, ram = 16704} stacksize = 8192 start = true -sections = {bootstate = "usbsram"} +sections = {bootstate = "usbsram", transient_override = "override"} uses = ["flash_controller", "hash_crypt"] notifications = ["flash-irq", "hashcrypt-irq"] interrupts = {"flash_controller.irq" = "flash-irq", "hash_crypt.irq" = "hashcrypt-irq"} diff --git a/app/oxide-rot-1/app-dev.toml b/app/oxide-rot-1/app-dev.toml index ef67386fd1..50a945c016 100644 --- a/app/oxide-rot-1/app-dev.toml +++ b/app/oxide-rot-1/app-dev.toml @@ -55,7 +55,7 @@ name = "lpc55-update-server" priority = 3 stacksize = 8192 start = true -sections = {bootstate = "usbsram"} +sections = {bootstate = "usbsram", transient_override = "override"} uses = ["flash_controller", "hash_crypt"] notifications = ["flash-irq", "hashcrypt-irq"] interrupts = {"flash_controller.irq" = "flash-irq", "hash_crypt.irq" = "hashcrypt-irq"} diff --git a/app/oxide-rot-1/app.toml b/app/oxide-rot-1/app.toml index 986cb15a14..9e12953bad 100644 --- a/app/oxide-rot-1/app.toml +++ b/app/oxide-rot-1/app.toml @@ -43,7 +43,7 @@ name = "lpc55-update-server" priority = 3 stacksize = 8192 start = true -sections = {bootstate = "usbsram"} +sections = {bootstate = "usbsram", transient_override = "override"} uses = ["flash_controller", "hash_crypt"] notifications = ["flash-irq", "hashcrypt-irq"] interrupts = {"flash_controller.irq" = "flash-irq", "hash_crypt.irq" = "hashcrypt-irq"} diff --git a/app/rot-carrier/app.toml b/app/rot-carrier/app.toml index 09e71bba97..fcdd0fac52 100644 --- a/app/rot-carrier/app.toml +++ b/app/rot-carrier/app.toml @@ -49,7 +49,7 @@ priority = 3 # TODO size this appropriately stacksize = 8192 start = true -sections = {bootstate = "usbsram"} +sections = {bootstate = "usbsram", transient_override = "override"} uses = ["flash_controller", "hash_crypt"] notifications = ["flash-irq", "hashcrypt-irq"] interrupts = {"flash_controller.irq" = "flash-irq", "hash_crypt.irq" = "hashcrypt-irq"} diff --git a/chips/lpc55/memory.toml b/chips/lpc55/memory.toml index 47be0adc47..844b85d2c2 100644 --- a/chips/lpc55/memory.toml +++ b/chips/lpc55/memory.toml @@ -93,6 +93,22 @@ write = true execute = false dma = true +[[override]] +name = "a" +address = 0x3003ffe0 +size = 32 +read = true +write = true +execute = false + +[[override]] +name = "b" +address = 0x3003ffe0 +size = 32 +read = true +write = true +execute = false + # Info about the images loaded into flash and dumped by stage0 into USB SRAM # for hubris use. [[usbsram]] diff --git a/drv/lpc55-update-server/Cargo.toml b/drv/lpc55-update-server/Cargo.toml index e9756bea43..ba7be0b404 100644 --- a/drv/lpc55-update-server/Cargo.toml +++ b/drv/lpc55-update-server/Cargo.toml @@ -21,6 +21,7 @@ mutable-statics = { path = "../../lib/mutable-statics" } cfg-if.workspace = true hubpack.workspace = true +hex-literal.workspace = true idol-runtime.workspace = true lpc55-pac.workspace = true num-traits.workspace = true diff --git a/drv/lpc55-update-server/src/main.rs b/drv/lpc55-update-server/src/main.rs index 7e013d63fb..c98be3b977 100644 --- a/drv/lpc55-update-server/src/main.rs +++ b/drv/lpc55-update-server/src/main.rs @@ -18,6 +18,7 @@ use drv_lpc55_update_api::{ SlotId, SwitchDuration, UpdateTarget, VersionedRotBootInfo, }; use drv_update_api::UpdateError; +use hex_literal::hex; use idol_runtime::{ ClientError, Leased, LenLimit, NotificationHandler, RequestError, R, W, }; @@ -40,6 +41,10 @@ const PAGE_SIZE: u32 = BYTES_PER_FLASH_PAGE as u32; #[link_section = ".bootstate"] static BOOTSTATE: MaybeUninit<[u8; 0x1000]> = MaybeUninit::uninit(); +#[used] +#[link_section = ".transient_override"] +static mut TRANSIENT_OVERRIDE: MaybeUninit<[u8; 32]> = MaybeUninit::uninit(); + #[derive(Copy, Clone, PartialEq)] enum UpdateState { NoUpdate, @@ -503,6 +508,12 @@ impl idl::InOrderUpdateImpl for ServerImpl<'_> { ringbuf_entry!(Trace::State(self.state)); self.next_block = None; self.fw_cache.fill(0); + // The sequence: [update, set transient preference, update] is legal. + // Clear any stale transient preference before update. + // Stage0 doesn't support transient override. + if component == RotComponent::Hubris { + set_hubris_transient_override(None); + } Ok(()) } @@ -907,8 +918,11 @@ impl ServerImpl<'_> { ) -> Result<(), RequestError> { match duration { SwitchDuration::Once => { - // TODO deposit command token into buffer - return Err(UpdateError::NotImplemented.into()); + // TODO check Rollback policy vs epoch before activating. + // TODO: prep-image-update should clear transient selection. + // e.g. update, activate, update, reboot should not have + // transient boot set. + set_hubris_transient_override(Some(slot)); } SwitchDuration::Forever => { // Locate and return the authoritative CFPA flash word number @@ -1337,6 +1351,35 @@ fn bootstate() -> Result { RotBootStateV2::load_from_addr(addr) } +fn set_transient_override(preference: [u8; 32]) { + // Safety: Data is consumed by Bootleby on next boot. + // There are no concurrent writers possible. + // Calling this function multiple times is ok. + // Bootleby is careful to vet contents before acting. + unsafe { + TRANSIENT_OVERRIDE.write(preference); + } +} + +pub fn set_hubris_transient_override(bank: Option) { + // Preference constants are taken from bootleby:src/lib.rs + const PREFER_SLOT_A: [u8; 32] = hex!( + "edb23f2e9b399c3d57695262f29615910ed10c8d9b261bfc2076b8c16c84f66d" + ); + const PREFER_SLOT_B: [u8; 32] = hex!( + "70ed2914e6fdeeebbb02763b96da9faa0160b7fc887425f4d45547071d0ce4ba" + ); + // Bootleby writes all zeros after reading. We write all ones to reset. + const PREFER_NOTHING: [u8; 32] = [0xffu8; 32]; + + match bank { + // Do we need a value that says we were here and cleared? + None => set_transient_override(PREFER_NOTHING), + Some(SlotId::A) => set_transient_override(PREFER_SLOT_A), + Some(SlotId::B) => set_transient_override(PREFER_SLOT_B), + } +} + fn round_up_to_flash_page(offset: u32) -> Option { offset.checked_next_multiple_of(BYTES_PER_FLASH_PAGE as u32) }