blob: 4979ed92af824bc3e34f4ebb76c4ec33173339b0 [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! Mock board: a device table exercising every device archetype the
//! orchestrator manages. Not a real board — consumed by host tests and QEMU
//! runs until a hardware target declares its own table.
#![no_std]
use core::time::Duration;
use openprot_orchestrator_sm::ComponentAttrs;
use orchestrator_config::{
assert_retry_reaches_every_image, chain_of, BootCheckpoint, ChainEntries, DeviceConfig, Golden,
ImageLayout, Region, Slot, SlotId,
};
/// The mock board's boot-probe vocabulary. The schema carries these
/// opaquely; only this board's `EvidenceReader` gives them meaning.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MockProbe {
/// A boot-complete GPIO line, by index.
Gpio(u8),
/// The device's MCTP endpoint answers as ready.
MctpReady,
/// The device sends a heartbeat message (latched; the reset path
/// clears it).
Heartbeat,
}
/// The BMC's images: two 2 MiB slots at the bottom of its firmware
/// partition, the golden image above them.
const BMC_LAYOUT: ImageLayout = ImageLayout::new(
const {
&[
Slot::new(SlotId(0), Region::new(0x00_0000, 0x20_0000)),
Slot::new(SlotId(1), Region::new(0x20_0000, 0x20_0000)),
]
},
Some(Golden::new(Region::new(0x40_0000, 0x20_0000))),
);
/// Declaration order is the boot order: the orchestrator releases devices
/// top to bottom, one at a time. This table is the authority — the
/// orchestrator's chain of trust is built from it, never beside it.
///
/// The mock board's reset controller addresses reset lines by plain index,
/// so the reset id type is `u8`.
pub const MANAGED_DEVICES: [DeviceConfig<u8, MockProbe>; 2] = [
// Direct-flash SPI device (BMC archetype): the eRoT fronts its flash.
// Single checkpoint: it raises a boot-complete GPIO.
DeviceConfig::new(
"bmc",
7,
&[BootCheckpoint::new(
"boot-complete",
MockProbe::Gpio(12),
Duration::from_secs(90),
)],
// A/B plus the golden image, in the flash the eRoT owns. Offsets
// are counted from the start of the BMC's flash area; real
// boards declare their own.
Some(BMC_LAYOUT),
// No iRoT of its own, so the eRoT's check is the only one and the
// boot-complete GPIO is the only signal it sends back.
ComponentAttrs::passive_required(),
),
// PLDM device (NIC archetype): self-updating, SPDM-capable. Two
// checkpoints, exercising the multi-checkpoint path: transport up
// first, then proof the workload is alive.
DeviceConfig::new(
"nic",
3,
&[
BootCheckpoint::new("mctp-ready", MockProbe::MctpReady, Duration::from_secs(20)),
BootCheckpoint::new("heartbeat", MockProbe::Heartbeat, Duration::from_secs(10)),
],
// Self-updating device: it owns its images and its boot selection,
// so the eRoT addresses no byte range for it and declares no
// layout.
None,
// SPDM-capable, so it has an iRoT that verifies itself and reports
// ready once it has.
ComponentAttrs::active_required(),
),
];
/// The orchestrator's chain for this board, derived from the table above so
/// the two cannot name different components.
pub const CHAIN: ChainEntries<{ MANAGED_DEVICES.len() }> = chain_of(&MANAGED_DEVICES);
/// Board-local checks the schema constructors cannot do — they know the
/// schema's shape, not this board's meanings. Const-fence pattern: a bad
/// probe fails the build.
const fn validate_probes(devices: &[DeviceConfig<u8, MockProbe>]) {
let mut i = 0;
while i < devices.len() {
let checkpoints = devices[i].checkpoints();
let mut c = 0;
while c < checkpoints.len() {
if let MockProbe::Gpio(line) = *checkpoints[c].probe() {
// The mock ready-line bank packs 32 lines, SGPIO-style.
assert!(line < 32, "gpio probe names a line outside the bank");
}
c += 1;
}
i += 1;
}
}
const _: () = validate_probes(&MANAGED_DEVICES);
/// How many times the orchestrator restores a component before it gives up.
/// Four, because the BMC has three images and the attempt that restores the
/// last one is never booted, so three would stop one boot short of the
/// golden image.
pub const MAX_RETRY: u8 = 4;
const _: () = assert_retry_reaches_every_image(MAX_RETRY, &MANAGED_DEVICES);