From 2bf68fdbac65db7209ac856e18c35eb4b9e24ad5 Mon Sep 17 00:00:00 2001 From: Daniil Mordanov Date: Sun, 9 Aug 2026 02:10:32 +0700 Subject: [PATCH] orchestrator: carry device count in table type Signed-off-by: Daniil Mordanov --- services/orchestrator/config/src/lib.rs | 16 ++++++++-------- services/orchestrator/sm/src/model.rs | 13 +++---------- services/orchestrator/sm/src/tests.rs | 4 ++-- target/mock/devices.rs | 2 +- 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/services/orchestrator/config/src/lib.rs b/services/orchestrator/config/src/lib.rs index b15f569a..9fb16bf6 100644 --- a/services/orchestrator/config/src/lib.rs +++ b/services/orchestrator/config/src/lib.rs @@ -259,11 +259,11 @@ impl DeviceConfig { /// the proof, and downstream conversions (the orchestrator's chain of /// trust) need no failure path of their own. #[derive(Debug, Clone, Copy)] -pub struct DeviceTable { - devices: &'static [DeviceConfig], +pub struct DeviceTable { + devices: &'static [DeviceConfig; N], } -impl DeviceTable { +impl DeviceTable { /// Declares the board's device table. `const`, so a bad table is a /// build error. /// @@ -276,10 +276,10 @@ impl DeviceTable { /// dangling and self dependencies: a dependency is always walked /// before its dependents). #[must_use] - pub const fn new(devices: &'static [DeviceConfig]) -> Self { - assert!(!devices.is_empty(), "device table must not be empty"); + pub const fn new(devices: &'static [DeviceConfig; N]) -> Self { + assert!(N != 0, "device table must not be empty"); assert!( - devices.len() <= u8::MAX as usize, + N <= u8::MAX as usize, "device table exceeds the orchestrator's cursor bound" ); let mut i = 0; @@ -313,7 +313,7 @@ impl DeviceTable { /// The devices, in declaration order — which is the boot order. #[must_use] - pub const fn devices(&self) -> &'static [DeviceConfig] { + pub const fn devices(&self) -> &'static [DeviceConfig; N] { self.devices } } @@ -394,7 +394,7 @@ mod tests { #[test] #[should_panic(expected = "device table must not be empty")] fn rejects_an_empty_table() { - let _ = DeviceTable::new(&[] as &[DeviceConfig]); + let _ = DeviceTable::new(&[] as &[DeviceConfig; 0]); } #[test] diff --git a/services/orchestrator/sm/src/model.rs b/services/orchestrator/sm/src/model.rs index 361c6c4b..fce13c8f 100644 --- a/services/orchestrator/sm/src/model.rs +++ b/services/orchestrator/sm/src/model.rs @@ -358,17 +358,8 @@ impl Chain { /// home yet — nothing consumes it — so every component gets the default /// region. /// - /// # Panics - /// - /// Panics if `N` is smaller than the table. Unreachable when `N` is - /// derived from the same table (`table.devices().len()`), which is the - /// only intended call shape. - pub fn from_table(table: &orchestrator_config::DeviceTable) -> Self { + pub fn from_table(table: &orchestrator_config::DeviceTable) -> Self { let devices = table.devices(); - assert!( - devices.len() <= N, - "chain capacity N is smaller than the device table" - ); let mut entries = heapless::Vec::new(); for (i, device) in devices.iter().enumerate() { let depends_on = device.depends_on().map(|dep| { @@ -384,6 +375,8 @@ impl Chain { recovery_region: RegionId::new(0), depends_on, }; + // `DeviceTable` and `Chain` share the same const + // capacity, so this push cannot fail. let _ = entries.push((ComponentId::new(i as u8), attrs)); } Self { entries } diff --git a/services/orchestrator/sm/src/tests.rs b/services/orchestrator/sm/src/tests.rs index 1be69b78..818c9d0a 100644 --- a/services/orchestrator/sm/src/tests.rs +++ b/services/orchestrator/sm/src/tests.rs @@ -1566,9 +1566,9 @@ fn chain_derives_from_device_table() { &[CHECKPOINT], ) .with_depends_on("root"); - const TABLE: DeviceTable = DeviceTable::new(&[ROOT, LEAF]); + const TABLE: DeviceTable = DeviceTable::new(&[ROOT, LEAF]); - let entries = Chain::::from_table(&TABLE).into_entries(); + let entries = Chain::<2>::from_table(&TABLE).into_entries(); assert_eq!( entries.as_slice(), &[ diff --git a/target/mock/devices.rs b/target/mock/devices.rs index b1227044..48b2c1ea 100644 --- a/target/mock/devices.rs +++ b/target/mock/devices.rs @@ -33,7 +33,7 @@ pub enum MockSignal { /// /// The mock board's reset controller addresses reset lines by plain index, /// so the reset id type is `u8`. -pub const MANAGED_DEVICES: DeviceTable = DeviceTable::new(&[ +pub const MANAGED_DEVICES: DeviceTable = DeviceTable::new(&[ // Direct-flash SPI device (BMC archetype): the eRoT fronts its flash. // No iRoT, so the eRoT's check is the only trust gate (Passive), and // the platform is pointless without its BMC (Required). Single