Skip to content

fwmanager: Add BootMonitor capability and GpioBootMonitor adapter - #368

Merged
FerralCoder merged 3 commits into
OpenPRoT:mainfrom
9elements:add-boot-monitor
Jul 31, 2026
Merged

fwmanager: Add BootMonitor capability and GpioBootMonitor adapter#368
FerralCoder merged 3 commits into
OpenPRoT:mainfrom
9elements:add-boot-monitor

Conversation

@chrysh

@chrysh chrysh commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

BootMonitor reports a device's boot liveness as BootStatus in the api leaf crate; GpioBootMonitor implements it over one HAL GpioPort input line. MonitorError meets the core::error::Error bound and keeps the concrete HAL error reachable through source(). new() rejects empty pin masks; boot latches are cleared by the reset path, never the observer.

Closes 9elements#6.

(Replaces 9elements#20, which was opened against the fork's main by mistake.)

Comment thread services/fwmanager/api/src/boot_monitor.rs
Comment thread services/fwmanager/hal-adapters/src/gpio_boot_monitor.rs
Comment thread services/fwmanager/api/src/boot_monitor.rs Outdated
Comment thread services/fwmanager/api/src/boot_monitor.rs
Comment thread services/fwmanager/hal-adapters/src/gpio_boot_monitor.rs Outdated
Comment thread services/fwmanager/hal-adapters/src/gpio_boot_monitor.rs Outdated
chrysh added a commit to 9elements/openprot that referenced this pull request Jul 29, 2026
InReset is orchestrator state, not something a monitor can observe —
a device held in reset reads exactly like one still booting, so the
variant could never be reported. Remove it from BootStatus.

Document on boot_status() that any given backend may only ever produce
a subset of BootStatus depending on the signals it can access (a single
ready pin yields only Booting/Booted); this is a capability difference
between backends, and consumers must still handle the full set.

Addresses review feedback on PR OpenPRoT#368.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
chrysh added a commit to 9elements/openprot that referenced this pull request Jul 29, 2026
Owning the port swallowed a whole GPIO bank per monitor, making a
second monitor on the same bank impossible. On the AST1060 SGPIOM a
bank packs 32 unrelated lines, so distinct devices' ready signals
routinely share one bank by construction.

The monitor only reads (read_input takes &self), so hold &'a P instead:
several monitors can now watch different lines of the same bank, with
platform configuration keeping the bank alive for as long as its
monitors. Also note in the adapter docs that a single ready line only
ever yields the Booting/Booted subset of BootStatus.

Addresses review feedback on PR OpenPRoT#368.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
chrysh added a commit to 9elements/openprot that referenced this pull request Jul 29, 2026
InReset is orchestrator state, not something a monitor can observe —
a device held in reset reads exactly like one still booting, so the
variant could never be reported. Remove it from BootStatus.

Document on boot_status() that any given backend may only ever produce
a subset of BootStatus depending on the signals it can access (a single
ready pin yields only Booting/Booted); this is a capability difference
between backends, and consumers must still handle the full set.

Addresses review feedback on PR OpenPRoT#368.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
chrysh added a commit to 9elements/openprot that referenced this pull request Jul 29, 2026
Owning the port swallowed a whole GPIO bank per monitor, making a
second monitor on the same bank impossible. On the AST1060 SGPIOM a
bank packs 32 unrelated lines, so distinct devices' ready signals
routinely share one bank by construction.

The monitor only reads (read_input takes &self), so hold &'a P instead:
several monitors can now watch different lines of the same bank, with
platform configuration keeping the bank alive for as long as its
monitors. Also note in the adapter docs that a single ready line only
ever yields the Booting/Booted subset of BootStatus.

Addresses review feedback on PR OpenPRoT#368.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
@chrysh
chrysh force-pushed the add-boot-monitor branch from 44d7caf to 66f76f8 Compare July 29, 2026 21:13
@rusty1968
rusty1968 requested a review from JesseMelon July 29, 2026 22:32

@leongross leongross left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

chrysh added 3 commits July 31, 2026 06:18
BootMonitor reports a device's boot liveness as BootStatus in the api
leaf crate; GpioBootMonitor implements it over one HAL GpioPort input
line. MonitorError meets the core::error::Error bound and keeps the
concrete HAL error reachable through source(). new() rejects empty pin
masks; boot latches are cleared by the reset path, never the observer.

Closes: #6
Assisted-by: Claude:claude-opus-4-8
Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
InReset is orchestrator state, not something a monitor can observe —
a device held in reset reads exactly like one still booting, so the
variant could never be reported. Remove it from BootStatus.

Document on boot_status() that any given backend may only ever produce
a subset of BootStatus depending on the signals it can access (a single
ready pin yields only Booting/Booted); this is a capability difference
between backends, and consumers must still handle the full set.

Addresses review feedback on PR OpenPRoT#368.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
Owning the port swallowed a whole GPIO bank per monitor, making a
second monitor on the same bank impossible. On the AST1060 SGPIOM a
bank packs 32 unrelated lines, so distinct devices' ready signals
routinely share one bank by construction.

The monitor only reads (read_input takes &self), so hold &'a P instead:
several monitors can now watch different lines of the same bank, with
platform configuration keeping the bank alive for as long as its
monitors. Also note in the adapter docs that a single ready line only
ever yields the Booting/Booted subset of BootStatus.

Addresses review feedback on PR OpenPRoT#368.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
Comment thread services/fwmanager/api/src/boot_monitor.rs
@rusty1968

Copy link
Copy Markdown
Collaborator

API usage model

Consider this bare metal toy example just for illustration purposes.

use fwmanager_api::{BootMonitor, BootStatus};
use orchestrator_sm::{ComponentId, Event, Orchestrator, Platform};

/// The shell's per-component boot watch. Called after the SM has emitted
/// `Effect::ReleaseReset(id)` for `id` and the platform actually let it go.
///
/// This is the *only* place `BootStatus` crosses into the SM: a `Booted` read
/// becomes `Event::Booted`, a device-reported `Failed` short-circuits into
/// recovery, and "still `Booting` when the budget runs out" is the watchdog
/// firing `Event::Timeout`. Reset / authenticate / measure never appear here —
/// the SM already drove those before it emitted `ReleaseReset`.
fn watch_boot<const N: usize, const E: usize, M, P>(
    sm: &mut Orchestrator<N, E>,
    platform: &mut P,
    monitor: &M,
    id: ComponentId,
    poll_budget: usize,
) where
    M: BootMonitor,
    P: Platform,
{
    for _ in 0..poll_budget {
        match monitor.boot_status() {
            // Came up: hand the SM its post-release liveness event. The SM
            // clears this component's boot-progress watchdog and advances the
            // chain walk (or, for an Active part, this pairs with ComponentReady).
            Ok(BootStatus::Booted) => {
                sm.dispatch(platform, Event::Booted(id));
                return;
            }

            // Released but not up yet — not a failure. Keep waiting inside the
            // budget; the SM is unaware a poll even happened.
            Ok(BootStatus::Booting) => continue,

            // Device reported a fault. The SM has no dedicated "boot failed"
            // event — a device that doesn't come up enters recovery via the
            // boot-progress watchdog — so surface an early, explicit fault as
            // that same Timeout path instead of waiting the window out.
            Ok(BootStatus::Failed) => {
                sm.dispatch(platform, Event::Timeout(id));
                return;
            }

            // Couldn't even read the signal. Treat an unreadable liveness
            // channel as no-progress and let recovery decide.
            Err(_e) => {
                sm.dispatch(platform, Event::Timeout(id));
                return;
            }
        }
    }

    // Budget exhausted, never saw Booted: the boot-progress watchdog fires.
    // The SM routes Timeout(id) for an awaiting component straight into recovery.
    sm.dispatch(platform, Event::Timeout(id));
}

@FerralCoder
FerralCoder merged commit eac24ef into OpenPRoT:main Jul 31, 2026
5 checks passed
FerralCoder pushed a commit that referenced this pull request Jul 31, 2026
InReset is orchestrator state, not something a monitor can observe —
a device held in reset reads exactly like one still booting, so the
variant could never be reported. Remove it from BootStatus.

Document on boot_status() that any given backend may only ever produce
a subset of BootStatus depending on the signals it can access (a single
ready pin yields only Booting/Booted); this is a capability difference
between backends, and consumers must still handle the full set.

Addresses review feedback on PR #368.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Downstream device boot monitoring

5 participants