Conversation
chrysh
force-pushed
the
docs-pldm-server-ipc
branch
2 times, most recently
from
September 10, 2026 18:21
daf28d3 to
bcdec55
Compare
chrysh
force-pushed
the
docs-pldm-server-ipc
branch
6 times, most recently
from
September 11, 2026 10:18
e66be4b to
3843315
Compare
leongross
reviewed
Sep 11, 2026
| activate Orch | ||
| Orch->>FD: channel_transact: QueryStatus | ||
| FD-->>Orch: Status::ActivationPending | ||
| Note right of Orch: bump SVN (irreversible),<br/>close SMC write filter |
Member
There was a problem hiding this comment.
Iirc, the SVN bumping should not happen automatically but only via a manual invocation, once the firmware is considered proven in use? @FerralCoder
chrysh
marked this pull request as ready for review
September 14, 2026 08:43
2 tasks
Design doc for the PLDM service as IPC server, orchestrator as client. Reverses the direction from the notify/intake design: the orchestrator calls into PLDM via channel_transact, PLDM nudges back with USER signals. Covers both in-transport and out-of-transport image transfer. Assisted-by: Claude
Design doc for an alternative to OpenPRoT#458: PLDM-FD runs as IPC server, the orchestrator is its client. The FD drives the PLDM protocol and executes firmware operations through FdOps callbacks (fw_data_download calls the flash device server, verify calls the crypto service, apply and activate call the device server). The orchestrator is a gatekeeper: it grants or denies each phase (verify, apply, activate) but does not execute operations itself. Firmware lands in the staging region and is verified in place, no intermediate copies. Platform owns slot logic. Assisted-by: Claude
ServiceCall<Req, Resp> is the universal IPC primitive: start() sends non-blocking, the caller adds the completion signal() to its WaitGroup, and try_recv() picks up the result after wake. No process ever blocks on another; object_wait on the WaitGroup sleeps the thread until any signal fires. The orchestrator never blocks. Its WaitGroup multiplexes FD nudge signals, ServiceCall completions (SVN bump, write filter), CompromiseDetected, and timers. The platform driver is a trait linked at build time, not a service. apply and activate use board-specific logic from the trait, then execute the actual I/O via the device server. FdOps::verify delegates the entire operation to the crypto service in a single ServiceCall. The crypto service owns the full read-hash-check pipeline. Assisted-by: Claude
Names the five crates and which two are kernel-tagged, so the protocol path builds and tests on the host with a loopback, the way services/i2c splits. Adds the header layout and the error vocabulary the op table implied but never wrote down. Records the blocking rule: the ops are ServiceCalls, and a blocking channel_transact is the alternative only with a timeout sized against the boot watchdogs, since a transact in flight delays every one of them. The diagram arrows said channel_transact and now match. Fixes the nudge citation. The level-triggered USER signal is i2c's, not the MCTP server's; drive_pending is a deferred channel_respond, not a wake. Assisted-by: Claude
chrysh
force-pushed
the
docs-pldm-server-ipc
branch
2 times, most recently
from
September 14, 2026 18:59
4242d48 to
f1b64a2
Compare
Bumping the security revision at ActivationPending spends the anti-rollback floor before any boot is judged, which leaves the superseded image below the floor and makes a trial-boot revert impossible. DSP0267 1.3.0 separates the two. The UA sets Security Revision Number Delayed Update on UpdateComponent and sends UpdateSecurityRevision (0x22, section 12.19) once it is satisfied with the running image. Both sequences now end with that phase, gated on a confirmed trial. The orchestrator still owns the floor write; GrantSvnCommit only tells the FD it may answer the UA. Replaces the before-or-after-Activate open question with the two the split raises: what to do when the UA omits the delayed-update flag, and when it never sends 0x22 at all. Assisted-by: Claude
chrysh
force-pushed
the
docs-pldm-server-ipc
branch
from
September 14, 2026 20:21
f1b64a2 to
493bd7d
Compare
Adds the containment section the design assumed but never wrote down. The FD holds no flash handle, so the device server enforces the staging window and the SMC write filter backs it in hardware. Both cover staging only: apply and activate are FD-initiated ServiceCalls gated inside the FD's own callback, so a compromised FD can still reach them. That gap is stated, not closed. Answers the FD-death open question with restart-and-nudge. pw_kernel has no peer-closed signal, and channel objects are reset only when a supervisor joins the dead process, so the orchestrator learns about the death from the restarted FD reporting Idle while it still believes an update is running. Two gaps stay open: there is no supervisor in this tree, and an FD that never restarts still needs a backstop timer above worst-case transfer plus FD_T1. Adds activation reporting: the response means accepted, the outcome comes from GetStatus and GetFirmwareParameters, and a failed activation leaves the old image booting. Also records that the FD answers ALREADY_IN_UPDATE_MODE from its own state machine, without the orchestrator. Corrects who clears the nudge. The doc said the orchestrator lowers the signal, which it cannot: object_set_peer_user_signal acts on the peer, so the orchestrator has no call to clear its own. The FD clears when it answers QueryStatus and raises again on the next change. Assisted-by: Claude
A crypto service that never answers, Status::Error being listed as a nudge trigger but never defined, FdOps::cancel_update_component appearing in no diagram, verify failure detail that stops at a bool, and the missing completion code for refusing a non-delayed SVN update. The refusal one carries a timing constraint worth stating with it: the FD answers UpdateComponent before the orchestrator ever sees the offer, so a policy refusal cannot wait for the orchestrator. Assisted-by: Claude
pldm-lib calls both callbacks repeatedly through fd_progress, and the grant gate lives inside that poll. Verify showed it in two notes, apply not at all. Both polls are loop boxes now, in each sequence. Assisted-by: Claude
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Alternative to #458: PLDM-FD is the IPC server, orchestrator is its client.
opdecoded throughTryFrom<u8>so an unknown value is a decode error; each side treats the other as untrusted and neither dispatch panicsFull design doc:
docs/src/design/orchestrator/pldm-server-ipc.mdIn-transport sequence
sequenceDiagram participant UA as UA (BMC)<br/>remote, over MCTP participant FD as PLDM-FD (server)<br/>dispatch loop + run_terminus participant Orch as Orchestrator (client)<br/>ServiceCall participant DevSrv as Device Server<br/>manages the SPI flash participant Crypto as Crypto Service<br/>hash + signature verification Note over UA, Crypto: Blue background: orchestrator IPC. Green background: FdOps service IPC. Note over UA, Orch: NEGOTIATION (PLDM protocol, MCTP only) UA->>FD: RequestUpdate (MCTP) FD-->>UA: RequestUpdate response (accepted) UA->>FD: PassComponentTable (MCTP) FD-->>UA: PassComponentTable response UA->>FD: UpdateComponent (MCTP) FD-->>UA: UpdateComponent response Note over FD, Orch: FD has an offer, nudge the orchestrator FD->>Orch: USER signal (nudge: offer ready) rect rgb(230, 240, 255) activate Orch Orch->>FD: ServiceCall: QueryStatus FD-->>Orch: Status::OfferPending { target, total, mode: InTransport } Note right of Orch: validate target + total,<br/>platform driver picks<br/>staging address,<br/>reserve staging,<br/>open SMC write filter Orch->>FD: ServiceCall: AcceptOffer { base: FlashAddress } FD-->>Orch: Ok deactivate Orch end Note over UA, DevSrv: TRANSFER (FD pulls from UA, FdOps writes to flash) rect rgb(230, 255, 230) loop FdOps::fw_data_download per chunk FD->>UA: RequestFirmwareData (MCTP) UA-->>FD: firmware chunk FD->>DevSrv: ServiceCall: write chunk DevSrv-->>FD: signal: Ok end end Note over FD, Orch: transfer done, ask orchestrator to grant verify FD->>UA: TransferComplete (MCTP) FD->>Orch: USER signal (nudge: verify pending) rect rgb(230, 240, 255) activate Orch Orch->>FD: ServiceCall: QueryStatus FD-->>Orch: Status::VerifyPending Note right of Orch: check isolation, update policy Orch->>FD: ServiceCall: GrantVerify FD-->>Orch: Ok deactivate Orch end Note over FD, Crypto: FdOps::verify rect rgb(230, 255, 230) FD->>Crypto: ServiceCall::start(VerifyRequest { addr, size }) Crypto->>DevSrv: read staged image DevSrv-->>Crypto: image data loop fd_progress poll Note over FD: verify() returns 0%, no signal yet end Crypto-->>FD: signal: Verdict Note over FD: verify() poll: try_recv -> 100% + verdict end FD->>UA: VerifyComplete (MCTP) Note over FD, Orch: verify done, ask orchestrator to grant apply FD->>Orch: USER signal (nudge: apply pending) rect rgb(230, 240, 255) activate Orch Orch->>FD: ServiceCall: QueryStatus FD-->>Orch: Status::ApplyPending { verify_ok: true } Orch->>FD: ServiceCall: GrantApply FD-->>Orch: Ok deactivate Orch end Note over FD, DevSrv: FdOps::apply rect rgb(230, 255, 230) FD->>DevSrv: ServiceCall::start(apply: commit staged image) loop fd_progress poll Note over FD: apply() returns 0%, no signal yet end DevSrv-->>FD: signal: Ok Note over FD: apply() poll: try_recv -> 100% end FD->>UA: ApplyComplete (MCTP) Note over FD, Orch: ACTIVATION (FdOps::activate) UA->>FD: ActivateFirmware (MCTP) FD->>Orch: USER signal (nudge: activation requested) rect rgb(230, 240, 255) activate Orch Orch->>FD: ServiceCall: QueryStatus FD-->>Orch: Status::ActivationPending Note right of Orch: close SMC write filter Orch->>FD: ServiceCall: Activate FD-->>Orch: Ok deactivate Orch end rect rgb(230, 255, 230) FD->>DevSrv: ServiceCall: FdOps::activate: set boot preference DevSrv-->>FD: signal: Ok end FD-->>UA: ActivateFirmware response (accepted) Note over UA, DevSrv: SVN COMMIT (later, FD back in IDLE, new image running) Note right of Orch: judge the boot, then<br/>TrialBoot::confirm or revert UA->>FD: UpdateSecurityRevision (MCTP, 0x22) FD->>Orch: USER signal (nudge: SVN commit requested) rect rgb(230, 240, 255) activate Orch Orch->>FD: ServiceCall: QueryStatus FD-->>Orch: Status::SvnCommitPending { component } Note right of Orch: a confirmed trial only,<br/>else DenySvnCommit Orch->>DevSrv: ServiceCall: SvnFloor::advance DevSrv-->>Orch: signal: Ok Orch->>FD: ServiceCall: GrantSvnCommit FD-->>Orch: Ok deactivate Orch end FD-->>UA: UpdateSecurityRevision response (success) Note over UA, Orch: CANCEL (between AcceptOffer and Activate) UA->>FD: CancelUpdate (MCTP) FD->>Orch: USER signal (nudge: cancelled) rect rgb(230, 240, 255) activate Orch Orch->>FD: ServiceCall: QueryStatus FD-->>Orch: Status::Cancelled Note right of Orch: release staging,<br/>close SMC write filter Orch->>FD: ServiceCall: AckCancel FD-->>Orch: Ok deactivate Orch end FD-->>UA: CancelUpdate responseOut-of-transport sequence
sequenceDiagram participant UA as UA (BMC)<br/>remote, over MCTP participant FD as PLDM-FD (server)<br/>dispatch loop + run_terminus participant Orch as Orchestrator (client)<br/>ServiceCall participant DevSrv as Device Server<br/>manages the SPI flash participant Crypto as Crypto Service<br/>hash + signature verification Note over UA, Crypto: Blue background: orchestrator IPC. Green background: FdOps service IPC. Note over UA, Orch: NEGOTIATION (same as in-transport) UA->>FD: RequestUpdate (MCTP) FD-->>UA: RequestUpdate response (accepted) UA->>FD: PassComponentTable (MCTP) FD-->>UA: PassComponentTable response UA->>FD: UpdateComponent (MCTP, out-of-transport) FD-->>UA: UpdateComponent response Note over FD, Orch: FD has an offer, nudge the orchestrator FD->>Orch: USER signal (nudge: offer ready) rect rgb(230, 240, 255) activate Orch Orch->>FD: ServiceCall: QueryStatus FD-->>Orch: Status::OfferPending { target, total, mode: OutOfTransport } Note right of Orch: validate target + total,<br/>platform driver picks<br/>staging address Orch->>FD: ServiceCall: AcceptOffer { base: FlashAddress } Note left of FD: FD does not write in<br/>out-of-transport, but the<br/>orchestrator communicates the<br/>base address to the third party<br/>that pre-stages the image FD-->>Orch: Ok deactivate Orch end Note over FD, Orch: no transfer phase, image already staged FD->>UA: TransferComplete (MCTP) Note over FD, Orch: ask orchestrator to grant verify FD->>Orch: USER signal (nudge: verify pending) rect rgb(230, 240, 255) activate Orch Orch->>FD: ServiceCall: QueryStatus FD-->>Orch: Status::VerifyPending Note right of Orch: check isolation, update policy Orch->>FD: ServiceCall: GrantVerify FD-->>Orch: Ok deactivate Orch end Note over FD, Crypto: FdOps::verify rect rgb(230, 255, 230) FD->>Crypto: ServiceCall::start(VerifyRequest { addr, size }) Crypto->>DevSrv: read staged image DevSrv-->>Crypto: image data loop fd_progress poll Note over FD: verify() returns 0%, no signal yet end Crypto-->>FD: signal: Verdict Note over FD: verify() poll: try_recv -> 100% + verdict end FD->>UA: VerifyComplete (MCTP) Note over FD, Orch: verify done, ask orchestrator to grant apply FD->>Orch: USER signal (nudge: apply pending) rect rgb(230, 240, 255) activate Orch Orch->>FD: ServiceCall: QueryStatus FD-->>Orch: Status::ApplyPending { verify_ok: true } Orch->>FD: ServiceCall: GrantApply FD-->>Orch: Ok deactivate Orch end Note over FD, DevSrv: FdOps::apply rect rgb(230, 255, 230) FD->>DevSrv: ServiceCall::start(apply: commit staged image) loop fd_progress poll Note over FD: apply() returns 0%, no signal yet end DevSrv-->>FD: signal: Ok Note over FD: apply() poll: try_recv -> 100% end FD->>UA: ApplyComplete (MCTP) Note over FD, Orch: ACTIVATION (FdOps::activate, same as in-transport) UA->>FD: ActivateFirmware (MCTP) FD->>Orch: USER signal (nudge: activation requested) rect rgb(230, 240, 255) activate Orch Orch->>FD: ServiceCall: QueryStatus FD-->>Orch: Status::ActivationPending Orch->>FD: ServiceCall: Activate FD-->>Orch: Ok deactivate Orch end rect rgb(230, 255, 230) FD->>DevSrv: ServiceCall: FdOps::activate: set boot preference DevSrv-->>FD: signal: Ok end FD-->>UA: ActivateFirmware response (accepted) Note over UA, DevSrv: SVN COMMIT (later, FD back in IDLE, new image running) Note right of Orch: judge the boot, then<br/>TrialBoot::confirm or revert UA->>FD: UpdateSecurityRevision (MCTP, 0x22) FD->>Orch: USER signal (nudge: SVN commit requested) rect rgb(230, 240, 255) activate Orch Orch->>FD: ServiceCall: QueryStatus FD-->>Orch: Status::SvnCommitPending { component } Note right of Orch: a confirmed trial only,<br/>else DenySvnCommit Orch->>DevSrv: ServiceCall: SvnFloor::advance DevSrv-->>Orch: signal: Ok Orch->>FD: ServiceCall: GrantSvnCommit FD-->>Orch: Ok deactivate Orch end FD-->>UA: UpdateSecurityRevision response (success) Note over UA, Orch: CANCEL (between AcceptOffer and Activate) UA->>FD: CancelUpdate (MCTP) FD->>Orch: USER signal (nudge: cancelled) rect rgb(230, 240, 255) activate Orch Orch->>FD: ServiceCall: QueryStatus FD-->>Orch: Status::Cancelled Note right of Orch: discard the accepted offer Orch->>FD: ServiceCall: AckCancel FD-->>Orch: Ok deactivate Orch end FD-->>UA: CancelUpdate responseSecurity revision commit
Activation does not raise the anti-rollback floor. If it did, a trial boot
could never be reverted: the superseded image would sit below the new floor
and refuse to run. DSP0267 1.3.0 keeps the two apart. The UA sets the
Security Revision Number Delayed Update option (
UpdateOptionFlagsbit 2) onUpdateComponent, the FD applies and activates without touching the revision,
and the UA sends UpdateSecurityRevision (command 0x22, section 12.19) once it
is satisfied with the image. Until that command arrives a downgrade is still
allowed, which is the window the trial boot lives in.
The FD accepts 0x22 only in the IDLE state, so it arrives outside update mode,
minutes or days after activation. It acts on the active running image, not a
pending one, which is what makes it safe to gate on the boot verdict.
The orchestrator owns the write. On a confirmed trial it advances
SvnFloorfor that component, then sends GrantSvnCommit so the FD can answer the UA. The
FD relays the request and the answer and never touches the floor, the same
split the rest of this design uses for anything irreversible. On an
unconfirmed or absent trial the orchestrator denies with PolicyViolation and
the FD returns UPDATE_SECURITY_REVISION_NOT_PERMITTED. DSP0267 has no code for
a policy refusal, so that capability code is the nearest fit.
IPC operations
FdOps and IPC services
Test plan
Assisted-by: Claude