Skip to content

Commit 537adb7

Browse files
committed
Implement transient override for RoT Hubris boot selection.
See bootleby:src/lib.rs for the receiving end of the override selection.
1 parent 5680547 commit 537adb7

File tree

6 files changed

+71
-3
lines changed

6 files changed

+71
-3
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ getrandom = { version = "0.2", default-features = false }
8282
goblin = { version = "0.4.3", default-features = true } # goblin::Object doesn't work without everything enabled
8383
heapless = { version = "0.7.17", default-features = false }
8484
heck = { version = "0.5.0", default-features = false }
85+
hex-literal = { version = "0.4.1" }
8586
hkdf = { version = "0.12", default-features = false }
8687
hmac = { version = "0.12.1", default-features = false }
8788
hubpack = { version = "0.1.2", default-features = false }

app/rot-carrier/app.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ priority = 3
4949
# TODO size this appropriately
5050
stacksize = 8192
5151
start = true
52-
sections = {bootstate = "usbsram"}
52+
sections = {bootstate = "usbsram", transient_override = "override"}
5353
uses = ["flash_controller", "hash_crypt"]
5454
notifications = ["flash-irq", "hashcrypt-irq"]
5555
interrupts = {"flash_controller.irq" = "flash-irq", "hash_crypt.irq" = "hashcrypt-irq"}

chips/lpc55/memory.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,22 @@ write = true
9393
execute = false
9494
dma = true
9595

96+
[[override]]
97+
name = "a"
98+
address = 0x3003ffe0
99+
size = 32
100+
read = true
101+
write = true
102+
execute = false
103+
104+
[[override]]
105+
name = "b"
106+
address = 0x3003ffe0
107+
size = 32
108+
read = true
109+
write = true
110+
execute = false
111+
96112
# Info about the images loaded into flash and dumped by stage0 into USB SRAM
97113
# for hubris use.
98114
[[usbsram]]

drv/lpc55-update-server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ mutable-statics = { path = "../../lib/mutable-statics" }
2121

2222
cfg-if.workspace = true
2323
hubpack.workspace = true
24+
hex-literal.workspace = true
2425
idol-runtime.workspace = true
2526
lpc55-pac.workspace = true
2627
num-traits.workspace = true

drv/lpc55-update-server/src/main.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use drv_lpc55_update_api::{
1818
SlotId, SwitchDuration, UpdateTarget, VersionedRotBootInfo,
1919
};
2020
use drv_update_api::UpdateError;
21+
use hex_literal::hex;
2122
use idol_runtime::{
2223
ClientError, Leased, LenLimit, NotificationHandler, RequestError, R, W,
2324
};
@@ -40,6 +41,10 @@ const PAGE_SIZE: u32 = BYTES_PER_FLASH_PAGE as u32;
4041
#[link_section = ".bootstate"]
4142
static BOOTSTATE: MaybeUninit<[u8; 0x1000]> = MaybeUninit::uninit();
4243

44+
#[used]
45+
#[link_section = ".transient_override"]
46+
static mut TRANSIENT_OVERRIDE: MaybeUninit<[u8; 32]> = MaybeUninit::uninit();
47+
4348
#[derive(Copy, Clone, PartialEq)]
4449
enum UpdateState {
4550
NoUpdate,
@@ -503,6 +508,12 @@ impl idl::InOrderUpdateImpl for ServerImpl<'_> {
503508
ringbuf_entry!(Trace::State(self.state));
504509
self.next_block = None;
505510
self.fw_cache.fill(0);
511+
// The sequence: [update, set transient preference, update] is legal.
512+
// Clear any stale transient preference before update.
513+
// Stage0 doesn't support transient override.
514+
if component == RotComponent::Hubris {
515+
set_hubris_transient_override(None);
516+
}
506517
Ok(())
507518
}
508519

@@ -907,8 +918,11 @@ impl ServerImpl<'_> {
907918
) -> Result<(), RequestError<UpdateError>> {
908919
match duration {
909920
SwitchDuration::Once => {
910-
// TODO deposit command token into buffer
911-
return Err(UpdateError::NotImplemented.into());
921+
// TODO check Rollback policy vs epoch before activating.
922+
// TODO: prep-image-update should clear transient selection.
923+
// e.g. update, activate, update, reboot should not have
924+
// transient boot set.
925+
set_hubris_transient_override(Some(slot));
912926
}
913927
SwitchDuration::Forever => {
914928
// Locate and return the authoritative CFPA flash word number
@@ -1337,6 +1351,35 @@ fn bootstate() -> Result<RotBootStateV2, HandoffDataLoadError> {
13371351
RotBootStateV2::load_from_addr(addr)
13381352
}
13391353

1354+
fn set_transient_override(preference: [u8; 32]) {
1355+
// Safety: Data is consumed by Bootleby on next boot.
1356+
// There are no concurrent writers possible.
1357+
// Calling this function multiple times is ok.
1358+
// Bootleby is careful to vet contents before acting.
1359+
unsafe {
1360+
TRANSIENT_OVERRIDE.write(preference);
1361+
}
1362+
}
1363+
1364+
pub fn set_hubris_transient_override(bank: Option<SlotId>) {
1365+
// Preference constants are taken from bootleby:src/lib.rs
1366+
const PREFER_SLOT_A: [u8; 32] = hex!(
1367+
"edb23f2e9b399c3d57695262f29615910ed10c8d9b261bfc2076b8c16c84f66d"
1368+
);
1369+
const PREFER_SLOT_B: [u8; 32] = hex!(
1370+
"70ed2914e6fdeeebbb02763b96da9faa0160b7fc887425f4d45547071d0ce4ba"
1371+
);
1372+
// Bootleby writes all zeros after reading. We write all ones to reset.
1373+
const PREFER_NOTHING: [u8; 32] = [0xffu8; 32];
1374+
1375+
match bank {
1376+
// Do we need a value that says we were here and cleared?
1377+
None => set_transient_override(PREFER_NOTHING),
1378+
Some(SlotId::A) => set_transient_override(PREFER_SLOT_A),
1379+
Some(SlotId::B) => set_transient_override(PREFER_SLOT_B),
1380+
}
1381+
}
1382+
13401383
fn round_up_to_flash_page(offset: u32) -> Option<u32> {
13411384
offset.checked_next_multiple_of(BYTES_PER_FLASH_PAGE as u32)
13421385
}

0 commit comments

Comments
 (0)