Conversation
84c7421 to
7ed6174
Compare
|
My open questions (mostly to myself) after talking with @embediver, and me answering them myself:
a. Mux control (MuxSelect): RoT takes exclusive flash access during boot/verify (RotControl), then hands back to host (HostControl) Boot sequence: Start → Hold (RoT exclusive) → ConfigurePolicy → Release (host) → RuntimeMonitoring Critical limitation: this filters the host's access. The RoT's own writes (through FMC or SPI1/SPI2) bypass the monitor entirely. That's why the malicious-PLDM question matters.
|
| # PLDM/Orchestrator IPC | ||
|
|
||
| How the PLDM FirmwareDevice service and the orchestrator communicate during a | ||
| firmware update. Two Pigweed kernel channels, both initiated by PLDM: a notify |
There was a problem hiding this comment.
The Orchestrator is a supervisor of the update scenario. It is not a channel handler - it is the initiator. It will register for notifications from PLDM when it is ready to do so. Use the I2C/MCTP as a template (with the bounded wait of course which is already part of the skeleton I have laid out). PLDM should be the server/handler, and the orchestrator the client that gets notified.
Look at what we already do today - The i2c server (handler) nudges its client via object_set_peer_user_signal on an async event, and the client then issues a transact to pull the latched data.
There was a problem hiding this comment.
In the flipped model, how does the Accepted/Rejected verdict reach PLDM before the RequestUpdate response goes back to the UA, a second transact?
channel_transact is an initiator-only. In the flipped model you are suggesting, every exchange where PLDM needs an answer (veto, Offer, Complete, Activate, Abort) becomes: latch → nudge → orchestrator pulls event → orchestrator pushes verdict in a second transact. Two round trips per exchange, sync or async, while PLDM holds the UA's request pending. Current design: one transact.
It would make the status-push direction cleaner (Authenticating/Staging/Staged), but I am not even sure how important this information is for PLDM.
@CourtneyDrant What is your opinion?
There was a problem hiding this comment.
@rusty1968 In the case of I2C, the client only pulls latched bytes. In our case, it's PLDM that needs the answer, and should be the one initiating the interaction, because PLDM needs to answer the UA. PLDM would need to hold UA state across: latch, nudge, orchestrator pulls, orchestrator pushes verdict in a second transact.
Furthermore, it moves blocking into the orchestrator's loop. channel_transact blocks the caller. I wanted the orchestrator to be always responsive.
That being said, if we as a group think the PLDM being the server is the better software design, I am happy to rewrite it. But given that the request direction is always UA -> FD, it seems to me that pldm being the client asking the orchestrator feels like the more natural direction.
There was a problem hiding this comment.
The flipped model works cleanly if the veto is allowed to land after the
RequestUpdate response: the orchestrator registers, gets nudged, pulls the
event, and gates whether the transfer starts. It does not work if the verdict
has to be in the response itself, because a handler cannot push to its
initiator, so PLDM would have to hold the UA's request across a nudge and a
second transact.
Which of the two do you have in mind? If it is the first, I will redraw with
the veto as a transfer gate and drop the in-response reject.
| ```mermaid | ||
| sequenceDiagram | ||
| participant UA as UA (BMC)<br/>remote, over MCTP | ||
| participant PLDM as PLDM FirmwareDevice<br/>single thread: run_terminus |
There was a problem hiding this comment.
I am thinking we should rename pldm as fw_update service. PLDM as a middleware crate is distinct from the service that uses it.
| PLDM's reach (separate MPU region, separate controller/CS, lock-until-reset) | ||
| depends on the AST10x0 register layout; see "Who owns the SPI flash | ||
| controller" in open questions. | ||
| - Transfer loop is zero-IPC: PLDM writes firmware bytes direct to flash and |
There was a problem hiding this comment.
There is an IPC channel between the fw_update service and the flash driver binding that manages the staging partition. Perhaps the more appropriate label should be zero Orchestrator IPC?
|
@CourtneyDrant , check this PR assumes in-transport transfer of bytes. |
|
|
||
| - Activate is on the wire (ActivateFirmware from the UA), not implicit after | ||
| staging. | ||
| - Flash seam is async: poll_stage calls start_erase/start_program, returns, |
There was a problem hiding this comment.
The flash service API is blocking - what you are referring to here is the FlashDriver trait whose implementor resides on the flash service process.
See #365
There was a problem hiding this comment.
Two questions then, because this decides whether the effect chain in this doc
works at all.
Does the flash service own the external SPI flash we stage into, or only
on-chip flash?
And is a split or deferred API on the service planned? FlashIpcClient makes
every erase, program and read a blocking transact, and FlashDriver's split
API (start_erase/is_busy/complete_op) stays inside the server process.
Without something equivalent across the boundary, the orchestrator's verifier
blocks for a full chunk read every loop pass and a staging erase blocks it for
10 to 100 ms. That is the "never wait in the handler" property the async effect
chain here is built on.
There was a problem hiding this comment.
Two questions then, because this decides whether the effect chain in this doc works at all.
Does the flash service own the external SPI flash we stage into, or only on-chip flash?
And is a split or deferred API on the service planned?
FlashIpcClientmakes every erase, program and read a blocking transact, andFlashDriver's split API (start_erase/is_busy/complete_op) stays inside the server process. Without something equivalent across the boundary, the orchestrator's verifier blocks for a full chunk read every loop pass and a staging erase blocks it for 10 to 100 ms. That is the "never wait in the handler" property the async effect chain here is built on.
It is one service per flash device, so the answer is yes count on being able to open a channel to the device server managing the spi where the image is stored. The orchestrator is not in the data path though.
There was a problem hiding this comment.
What do you mean by not in the data path? The orchestrator will tell pldm where to put the firmware though, I assume?
There was a problem hiding this comment.
What do you mean by not in the data path? The orchestrator will tell pldm where to put the firmware though, I assume?
The per-component staging area mapping is platform-specific - by not in the data path I meant to say the orchestrator doesperform any flash I/O.
| @@ -0,0 +1,193 @@ | |||
| # PLDM/Orchestrator IPC | |||
|
|
|||
| How the PLDM FirmwareDevice service and the orchestrator communicate during a | |||
There was a problem hiding this comment.
The firmware_device run_terminus_inner is currrently a tight loop interleaving the roles of initiator/responder - what you are assuming here is that we can fit the blocking requests to the orchestrator in the FdOps callbacks without disrupting the protocol.
There was a problem hiding this comment.
Agreed on the mechanism. handle_component, activate and
cancel_update_component all run inside the responder callback with the UA
waiting, so a transact there holds the UA's response for as long as the
orchestrator takes to answer.
FD_T1 and FD_T2 are ours to set, but the response timeout is the BMC's. Do you
have a number for what the UA tolerates? Your skeleton already carries a
bounded wait, so I assume you had one in mind.
There was a problem hiding this comment.
Agreed on the mechanism.
handle_component,activateandcancel_update_componentall run inside the responder callback with the UA waiting, so a transact there holds the UA's response for as long as the orchestrator takes to answer.FD_T1 and FD_T2 are ours to set, but the response timeout is the BMC's. Do you have a number for what the UA tolerates? Your skeleton already carries a bounded wait, so I assume you had one in mind.
The timeouts were not selected by me, let's follow up with @CourtneyDrant .
Sequence diagram covering the full firmware update flow between the PLDM FirmwareDevice service and the orchestrator. Two kernel channels (notify for pre-transfer veto, intake for control), zero-IPC transfer loop, async effect chain with nudge+Poll, and orchestrator-side timeout for dead PLDM. Assisted-by: Claude
Write-error recovery uses Abort (not nudge+Poll, which returns orchestrator state, not PLDM state). Drop opcode from CancelUpdate for consistency with other commands. Add CancelUpdate to the footer's UA-initiated list. Assisted-by: Claude
Add the page to SUMMARY.md; mdbook renders only listed chapters, so it was invisible in the built book. Doc fixes: poll_stage also calls complete_op, which is where the flash error surfaces. Dropped-message recovery is PLDM's own FD_T1/FD_T2. The staging reservation timeout bounds total transfer time, not inactivity, because the transfer loop is zero-IPC: it has to exceed worst-case transfer plus FD_T1 so a live PLDM always aborts first. ActivateFirmware responds accepted, not done, and the UA follows with GetStatus. A Rejected veto maps to a RequestUpdate error completion code. Open questions section records the undecided points: who owns the SPI controller, whether the kernel reports a closed channel, and what the UA does when activation fails after the response. Assisted-by: Claude
Two-layer approach: a typed StagingWindow inside PLDM (catches offset and use-after-transfer bugs) backed by an SMC hardware write filter the orchestrator controls (catches a compromised PLDM process). The filter/erase-program register split and Receiving base address are open questions pending AST10x0 datasheet review. Assisted-by: Claude
Assisted-by: Claude
Assisted-by: Claude
…tion The orchestrator picks the staging region and programs the SMC write filter for it, so it sends the base with Receiving: the window PLDM writes through and the window the hardware allows come from one place, and PLDM holds no board layout. The channel-closed question was too vague to answer. pw_kernel has no peer-closed signal, and the one path that raises ERROR on the handler needs an in-flight transaction plus a join of the dead process, neither of which happens during the zero-IPC transfer loop. Replacing the timeout is a supervisor question (wait on JOINABLE, or be told), not a channel one. Assisted-by: Claude
The UA is the BMC's software, so the doc says what the PRoT reports and guarantees and stops there: the ActivateFirmware response means accepted and carries estimated_time, failure reaches the UA as GetStatus GenericError, and a failed activation leaves a bootable system because PLDM never writes the active image. No rollback exists; the SVN bump is irreversible. Assisted-by: Claude
7a63481 to
63dcb7b
Compare
|
Snapshotting the sequence diagram as it stands at 7a63481, so this thread keeps sequenceDiagram
participant UA as UA (BMC)<br/>remote, over MCTP
participant PLDM as PLDM FirmwareDevice<br/>single thread: run_terminus
participant Orch as Orchestrator<br/>single thread: object_wait loop
participant Flash as Shared Storage<br/>ext. SPI flash
Note over UA, Orch: NOTIFY CHANNEL (pre-transfer veto)
UA->>PLDM: RequestUpdate (MCTP)
activate PLDM
PLDM->>Orch: channel_transact: Request::UpdateRequested
Note right of Orch: check state, policy
Orch-->>PLDM: Response::Accepted | Rejected
deactivate PLDM
PLDM-->>UA: RequestUpdate response (accept/reject)
Note over UA, Orch: if Accepted: INTAKE CHANNEL
activate PLDM
PLDM->>Orch: Offer { target: TargetId, total: u64 }
Note right of Orch: validate target + length,<br/>reserve staging,<br/>open the SMC write filter
Orch-->>PLDM: IntakeStatus::Receiving { base: FlashAddress, total }
deactivate PLDM
loop FD pulls chunks from UA via RequestFirmwareData
PLDM->>UA: RequestFirmwareData (MCTP)
UA-->>PLDM: firmware chunk response
PLDM-->>Flash: write firmware bytes (direct, no IPC)
Note right of PLDM: PLDM tracks its own write progress
end
activate PLDM
PLDM->>Orch: Complete { written: u64 }
Note right of Orch: check coverage,<br/>queue Pending::UpdateRequest
Orch-->>PLDM: IntakeStatus::Authenticating
deactivate PLDM
Note over UA, Flash: async: orchestrator event loop drains pending
PLDM->>UA: TransferComplete (MCTP)
Note over PLDM: PLDM FREE:<br/>services UA on MCTP<br/>MCTP responsive
Note over Orch, Flash: EFFECT CHAIN (non-blocking steps)<br/>1. poll_pending<br/>2. SM: Ready -> Updating<br/>3. poll_stage (one step)<br/>4. return to object_wait<br/>repeat 3-4 until phase done<br/>IPC responsive between steps
Orch-->>Flash: PayloadSource::read_at
Flash-->>Orch: payload bytes
Orch->>PLDM: object_set_peer_user_signal<br/>(dataless nudge, wakes WaitGroup)
loop wake on USER signal, poll status, send *Complete to UA
activate PLDM
PLDM->>Orch: Poll
Note right of Orch: read latched IntakeStatus
Orch-->>PLDM: Authenticating | Staging | Staged | Failed
deactivate PLDM
Note over PLDM, UA: when phase done:
PLDM->>UA: VerifyComplete (MCTP)
PLDM->>UA: ApplyComplete (MCTP)
Note right of PLDM: on failure: same commands<br/>with error completion code
end
UA->>PLDM: ActivateFirmware (MCTP, explicit)
activate PLDM
PLDM->>Orch: Activate
Orch-->>PLDM: IntakeStatus::Activating
deactivate PLDM
PLDM-->>UA: ActivateFirmware response (accepted, not done)
Note right of Orch: activation effect (async):<br/>bump SVN in OTP (irreversible),<br/>nudge + Poll reports Activated
UA->>PLDM: GetStatus (MCTP, until activation lands)
PLDM-->>UA: current state + AuxState
Note over UA, Orch: between Offer and Activate
UA->>PLDM: CancelUpdate (MCTP)
activate PLDM
PLDM->>Orch: Abort
Note right of Orch: in-flight flash step completes<br/>and is discarded
Orch-->>PLDM: IntakeStatus::Idle
deactivate PLDM
PLDM-->>UA: CancelUpdate response
Note over Orch: If PLDM dies mid-transfer (no Complete, no Abort),<br/>orchestrator-side timeout releases the staging reservation.<br/>The transfer loop is zero-IPC, so this bounds total transfer time:<br/>it must exceed worst-case transfer plus FD_T1 (120s),<br/>so a live PLDM always aborts first.
Note over UA, Flash: Blocking direction: always PLDM -> Orchestrator, never the reverse.<br/>Every IPC response is immediate. Effects run async via poll_stage (one step, return, repeat).<br/>PLDM stays free to service UA on MCTP. USER signal nudge replaces blind polling.<br/>FD initiates TransferComplete, VerifyComplete, ApplyComplete. UA initiates ActivateFirmware, GetStatus and CancelUpdate.
|
|
@CourtneyDrant pldm-lib #20 adds ActivatePendingComponent and the
I will draw whichever you confirm. |
Design doc for an alternative to OpenPRoT#458: PLDM service runs as IPC server, orchestrator is its client. The orchestrator owns slot selection, verify, apply, and SVN. In-transport, PLDM writes staged bytes through a channel to the per-device server; out-of-transport, a third party pre-stages the image and PLDM never writes. The orchestrator performs verify and apply directly, then reports the result to PLDM so it can signal the UA. All IPC ops return Reply immediately, no Pending needed. 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 commits via the device server). The orchestrator is a gatekeeper: it grants or denies each phase (verify, apply, activate) but does not execute operations itself. Platform owns slot logic. FdOps callbacks must not block for long because CancelUpdate can arrive asynchronously. 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
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
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
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
|
Superseded by #464, which inverts the direction: PLDM-FD is the IPC server and the orchestrator its client, granting or denying each phase. Closing this one. |
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
Summary
Related project board items (OpenPRoT PFR):
The settled design decisions and the open questions live in the page itself. Two questions are still open: who owns the SPI flash controller, and how the orchestrator learns that PLDM died short of the timeout.
sequenceDiagram participant UA as UA (BMC)<br/>remote, over MCTP participant PLDM as PLDM FirmwareDevice<br/>single thread: run_terminus participant Orch as Orchestrator<br/>single thread: object_wait loop participant Flash as Shared Storage<br/>ext. SPI flash Note over UA, Orch: NOTIFY CHANNEL (pre-transfer veto) UA->>PLDM: RequestUpdate (MCTP) activate PLDM PLDM->>Orch: channel_transact: Request::UpdateRequested Note right of Orch: check state, policy Orch-->>PLDM: Response::Accepted | Rejected deactivate PLDM PLDM-->>UA: RequestUpdate response (accept/reject) Note over UA, Orch: if Accepted: INTAKE CHANNEL activate PLDM PLDM->>Orch: Offer { target: TargetId, total: u64 } Note right of Orch: validate target + length,<br/>reserve staging,<br/>open the SMC write filter Orch-->>PLDM: IntakeStatus::Receiving { base: FlashAddress, total } deactivate PLDM loop FD pulls chunks from UA via RequestFirmwareData PLDM->>UA: RequestFirmwareData (MCTP) UA-->>PLDM: firmware chunk response PLDM-->>Flash: write firmware bytes (direct, no IPC) Note right of PLDM: PLDM tracks its own write progress end activate PLDM PLDM->>Orch: Complete { written: u64 } Note right of Orch: check coverage,<br/>queue Pending::UpdateRequest Orch-->>PLDM: IntakeStatus::Authenticating deactivate PLDM Note over UA, Flash: async: orchestrator event loop drains pending PLDM->>UA: TransferComplete (MCTP) Note over PLDM: PLDM FREE:<br/>services UA on MCTP<br/>MCTP responsive Note over Orch, Flash: EFFECT CHAIN (non-blocking steps)<br/>1. poll_pending<br/>2. SM: Ready -> Updating<br/>3. poll_stage (one step)<br/>4. return to object_wait<br/>repeat 3-4 until phase done<br/>IPC responsive between steps Orch-->>Flash: PayloadSource::read_at Flash-->>Orch: payload bytes Orch->>PLDM: object_set_peer_user_signal<br/>(dataless nudge, wakes WaitGroup) loop wake on USER signal, poll status, send *Complete to UA activate PLDM PLDM->>Orch: Poll Note right of Orch: read latched IntakeStatus Orch-->>PLDM: Authenticating | Staging | Staged | Failed deactivate PLDM Note over PLDM, UA: when phase done: PLDM->>UA: VerifyComplete (MCTP) PLDM->>UA: ApplyComplete (MCTP) Note right of PLDM: on failure: same commands<br/>with error completion code end UA->>PLDM: ActivateFirmware (MCTP, explicit) activate PLDM PLDM->>Orch: Activate Orch-->>PLDM: IntakeStatus::Activating deactivate PLDM PLDM-->>UA: ActivateFirmware response (accepted, not done) Note right of Orch: activation effect (async):<br/>bump SVN in OTP (irreversible),<br/>nudge + Poll reports Activated UA->>PLDM: GetStatus (MCTP, until activation lands) PLDM-->>UA: current state + AuxState Note over UA, Orch: between Offer and Activate UA->>PLDM: CancelUpdate (MCTP) activate PLDM PLDM->>Orch: Abort Note right of Orch: in-flight flash step completes<br/>and is discarded Orch-->>PLDM: IntakeStatus::Idle deactivate PLDM PLDM-->>UA: CancelUpdate response Note over Orch: If PLDM dies mid-transfer (no Complete, no Abort),<br/>orchestrator-side timeout releases the staging reservation.<br/>The transfer loop is zero-IPC, so this bounds total transfer time:<br/>it must exceed worst-case transfer plus FD_T1 (120s),<br/>so a live PLDM always aborts first. Note over UA, Flash: Blocking direction: always PLDM -> Orchestrator, never the reverse.<br/>Every IPC response is immediate. Effects run async via poll_stage (one step, return, repeat).<br/>PLDM stays free to service UA on MCTP. USER signal nudge replaces blind polling.<br/>FD initiates TransferComplete, VerifyComplete, ApplyComplete. UA initiates ActivateFirmware, GetStatus and CancelUpdate.Test plan
docs/src/design/orchestrator/pldm-orchestrator-ipc.mdAssisted-by: Claude