Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions services/orchestrator/driver/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Licensed under the Apache-2.0 license
# SPDX-License-Identifier: Apache-2.0

load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")

rust_library(
name = "orchestrator_driver",
srcs = [
"src/board.rs",
"src/driver.rs",
"src/lib.rs",
"src/tests.rs",
],
crate_name = "openprot_orchestrator_driver",
edition = "2024",
visibility = ["//visibility:public"],
deps = [
"//services/orchestrator/capabilities:orchestrator_capabilities",
"//services/orchestrator/sm:orchestrator_sm",
"@rust_crates//:heapless",
],
)

# Host tests: build on the host platform, no kernel/QEMU.
rust_test(
name = "orchestrator_driver_test",
crate = ":orchestrator_driver",
edition = "2024",
)
27 changes: 27 additions & 0 deletions services/orchestrator/driver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- Licensed under the Apache-2.0 license -->
<!-- SPDX-License-Identifier: Apache-2.0 -->

# orchestrator platform driver (`openprot_orchestrator_driver`)

The effect-executing layer around the orchestrator state machine. `PlatformDriver`
implements the SM's `Platform` seam: one method per `Effect`, each
documenting its obligation from the platform-boundary contract
([orchestrator-model.md §6](../../../docs/src/design/orchestrator/orchestrator-model.md)). Unimplemented executors return
`DriverError::NotImplemented`; the SM fail-closes on them.

Everything device-specific arrives through the seams in `board.rs`
(`ImageSource`, `Verifier`, bundled in `Board`); executor-produced events
return to the SM via `PlatformDriver::take_event`. The event loop dispatches an
outside event, then keeps dispatching what the executors produced until
`take_event` returns `None`:

```rust
orch.dispatch(&mut driver, event);
while let Some(ev) = driver.take_event() {
orch.dispatch(&mut driver, ev);
}
```

Implemented executors: `read_firmware`, `verify_firmware`. Everything else
returns `NotImplemented` until its pillar lands (boot walk, recovery,
update path, attestation, reporting).
129 changes: 129 additions & 0 deletions services/orchestrator/driver/src/board.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

//! What the board supplies to the driver: traits and wiring data only.
//! Boards (or test mocks) implement these.

use openprot_orchestrator_sm::ComponentId;

pub use orchestrator_capabilities::BootControl;

/// Access to one component's active firmware image, however it is reached —
/// interposed flash, a PLDM/MCTP transfer, a RAM copy in tests.
pub trait ImageSource {
/// The error type reported by this source.
type Error: core::error::Error;

/// Makes the image readable (claim the flash, open the transfer).
/// Idempotent; a later `open` re-stages the image.
fn open(&mut self) -> Result<(), Self::Error>;

/// Image length in bytes.
fn size(&mut self) -> Result<usize, Self::Error>;

/// Reads `buf.len()` bytes starting at byte `offset` of the image.
fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Result<(), Self::Error>;
}

impl<S: ImageSource> ImageSource for &mut S {
type Error = S::Error;

#[inline(always)]
fn open(&mut self) -> Result<(), Self::Error> {
(**self).open()
}

#[inline(always)]
fn size(&mut self) -> Result<usize, Self::Error> {
(**self).size()
}

#[inline(always)]
fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Result<(), Self::Error> {
(**self).read_at(offset, buf)
}
}

/// Judges a component's firmware image; board wiring decides what
/// "authentic" means.
pub trait Verifier {
/// The error type reported by this verifier.
type Error: core::error::Error;

/// Judges `id`'s image, reading it from `image`.
///
/// # Errors
///
/// Only when the check could not be performed (crypto fault, missing
/// key, unreadable source). A checked-and-bad image is
/// `Ok(Verdict::Rejected)` — an actuation fault must not forge a
/// verdict.
fn verify(
&mut self,
id: ComponentId,
image: &mut impl ImageSource,
) -> Result<Verdict, Self::Error>;
}

impl<V: Verifier> Verifier for &mut V {
type Error = V::Error;

#[inline(always)]
fn verify(
&mut self,
id: ComponentId,
image: &mut impl ImageSource,
) -> Result<Verdict, Self::Error> {
(**self).verify(id, image)
}
}

/// A [`Verifier`]'s judgment of one image.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Verdict {
/// Reported as `Event::VerificationPassed`.
Authentic,
/// Reported as `Event::VerificationFailed`.
Rejected,
}

/// One board's type choices, named by a marker type. A new seam adds an
/// associated type here and a field on [`Board`] — never another parameter.
pub trait BoardTypes {
/// Image access for the managed components.
type Image: ImageSource;
/// Judges images for every component.
type Verifier: Verifier;
/// Reset actuation for the managed components.
type Reset: BootControl;
// Later seams: Evidence (checkpoint walk), Recovery, Staging.
}

/// Everything the board supplies, built once at bring-up and handed to
/// `PlatformDriver::new`. Fields are public: executors may need two parts at once
/// (disjoint borrows).
///
/// ```ignore
/// struct Ast1060Board;
/// impl BoardTypes for Ast1060Board {
/// type Image = SpiFlashImage; // interposed flash, offsets from the slot layout
/// type Verifier = ManifestVerifier; // signature + SVN via the crypto engine
/// type Reset = GpioReset; // one reset line per device
/// }
/// let board = Board::<Ast1060Board, 2> {
/// images: [bmc_image, cpld_image],
/// verifier,
/// resets: [bmc_reset, cpld_reset],
/// };
/// ```
pub struct Board<B: BoardTypes, const N: usize> {
/// `images[i]` belongs to `ComponentId(i)` — device index = chain
/// position = table declaration order.
pub images: [B::Image; N],
/// Judges images for every component.
pub verifier: B::Verifier,
/// `resets[i]` is `ComponentId(i)`'s reset line, same indexing as
/// `images`.
pub resets: [B::Reset; N],
// Later seams add fields, e.g. evidence readers.
}
Loading