Skip to content
2 changes: 1 addition & 1 deletion docs/src/design/orchestrator/orchestrator-platform.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ inside the orchestrator process:
- **Device capabilities** (`services/orchestrator/capabilities`) — the narrow contracts
the state machine's effects are executed against, e.g. `BootControl`
(hold a device in reset / release it). HAL bindings live in
`services/orchestrator/hal-adapters`.
`services/orchestrator/adapters/hal`.
- **Board device table** (`services/orchestrator/config`, schema; values in
`target/<board>/devices.rs`) — declares the managed devices: reset signal,
boot checkpoints and windows, commit policy. Validated at compile time, so
Expand Down
24 changes: 24 additions & 0 deletions services/orchestrator/adapters/pldm/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 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_pldm_adapter",
srcs = [
"src/lib.rs",
],
crate_name = "openprot_orchestrator_pldm_adapter",
edition = "2024",
visibility = ["//visibility:public"],
deps = [
"//services/orchestrator/sm:orchestrator_sm",
"//services/pldm:pldm_service",
],
)

# Host tests: build on the host platform, no kernel/QEMU.
rust_test(
name = "orchestrator_pldm_adapter_test",
crate = ":orchestrator_pldm_adapter",
)
88 changes: 88 additions & 0 deletions services/orchestrator/adapters/pldm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

//! PLDM-backed adapter for the Boot Orchestrator's update-request input.
//!
//! [`UpdateRequestLatch`] binds the PLDM firmware-device service's
//! [`FdEventSink`] seam to the orchestrator's
//! [`Event::UpdateRequest`]: the PLDM run loop notifies the latch when the
//! Update Agent's `RequestUpdate` is accepted, and the orchestrator run loop
//! drains it with [`take`](UpdateRequestLatch::take). This crate depends on
//! both stacks by design — the PLDM service stays orchestrator-free and the
//! orchestrator stays transport-free, the same rule that keeps HAL adapters
//! out of `orchestrator-capabilities`.

#![cfg_attr(not(test), no_std)]
#![forbid(unsafe_code)]
#![warn(missing_docs)]

use openprot_orchestrator_sm::Event;
use openprot_pldm_service::firmware_device::{FdEvent, FdEventSink};

/// Latches an accepted PLDM `RequestUpdate` until the orchestrator run loop
/// drains it as [`Event::UpdateRequest`].
///
/// A `bool` latch, not a counter: the FD rejects a second `RequestUpdate`
/// while an update is in progress (`ALREADY_IN_UPDATE_MODE`), so at most one
/// accepted request can be outstanding per update cycle. Should a completed
/// or cancelled cycle admit a new `RequestUpdate` before the previous latch
/// is drained, the two coalesce into one [`Event::UpdateRequest`] — which is
/// what the state machine would do anyway (an update already being handled
/// defers further requests).
#[derive(Default)]
pub struct UpdateRequestLatch {
pending: bool,
}

impl UpdateRequestLatch {
/// A latch with nothing pending.
pub const fn new() -> Self {
Self { pending: false }
}

/// Drain the latch: [`Event::UpdateRequest`] if a `RequestUpdate` was
/// accepted since the last call, else `None`.
pub fn take(&mut self) -> Option<Event> {
self.pending.then(|| {
self.pending = false;
Event::UpdateRequest
})
}
}

/// Latches [`FdEvent::UpdateRequested`]; other FD lifecycle events have no
/// orchestrator mapping yet and are dropped here by design.
impl FdEventSink for UpdateRequestLatch {
fn notify(&mut self, event: FdEvent) {
if matches!(event, FdEvent::UpdateRequested) {
self.pending = true;
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn empty_latch_yields_nothing() {
assert_eq!(UpdateRequestLatch::new().take(), None);
}

#[test]
fn accepted_request_yields_one_event() {
let mut latch = UpdateRequestLatch::new();
latch.notify(FdEvent::UpdateRequested);
assert_eq!(latch.take(), Some(Event::UpdateRequest));
assert_eq!(latch.take(), None, "a drained latch must not re-fire");
}

#[test]
fn undrained_notifications_coalesce() {
let mut latch = UpdateRequestLatch::new();
latch.notify(FdEvent::UpdateRequested);
latch.notify(FdEvent::UpdateRequested);
assert_eq!(latch.take(), Some(Event::UpdateRequest));
assert_eq!(latch.take(), None);
}
}
1 change: 1 addition & 0 deletions services/orchestrator/capabilities/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ rust_library(
"src/boot_control.rs",
"src/boot_watch.rs",
"src/evidence.rs",
"src/incremental_verifier.rs",
"src/lib.rs",
"src/lockdown_latch.rs",
"src/svn_floor.rs",
Expand Down
Loading
Loading