From 7ed617417abd20bf0a32658914327dcb30251486 Mon Sep 17 00:00:00 2001 From: Christina Quast Date: Tue, 8 Sep 2026 09:09:21 +0200 Subject: [PATCH 1/8] docs: add PLDM/orchestrator IPC design diagram 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 --- .../orchestrator/pldm-orchestrator-ipc.md | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 docs/src/design/orchestrator/pldm-orchestrator-ipc.md diff --git a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md new file mode 100644 index 000000000..318e5a496 --- /dev/null +++ b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md @@ -0,0 +1,110 @@ +# 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 +channel for pre-transfer veto, and an intake channel for control messages +(Offer, Complete, Poll, Activate, Abort) and the async effect chain. Firmware +bytes go direct to flash, never through IPC. + +Design decisions: + +- 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, + checks is_busy on the next call. Uses FlashDriver's split API, not + BlockingFlash. +- USER signal is level-triggered (verified from Pigweed kernel source): OR'd + into the peer's active_signals bitfield, persists until lowered. No lost + wakeups. +- MCTP server (separate process) buffers 4 messages while PLDM is in a + transact. Overflow drops silently, no backpressure to the bus. +- Transfer loop is zero-IPC: PLDM writes firmware bytes direct to flash and + tracks progress locally. Complete carries the byte count for the + orchestrator's coverage check (early-fail only, verify hashes the staged + image anyway). On a write error PLDM can nudge and report Failed via Poll, + no extra IPC verb needed. + +```mermaid +sequenceDiagram + participant UA as UA (BMC)
remote, over MCTP + participant PLDM as PLDM FirmwareDevice
single thread: run_terminus + participant Orch as Orchestrator
single thread: object_wait loop + participant Flash as Shared Storage
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,
reserve staging + Orch-->>PLDM: IntakeStatus::Receiving { 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,
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:
services UA on MCTP
MCTP responsive + + Note over Orch, Flash: EFFECT CHAIN (non-blocking steps)
1. poll_pending
2. SM: Ready -> Updating
3. poll_stage (one step)
4. return to object_wait
repeat 3-4 until phase done
IPC responsive between steps + + Orch-->>Flash: PayloadSource::read_at + Flash-->>Orch: payload bytes + + Orch->>PLDM: object_set_peer_user_signal
(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
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 + Note right of Orch: activation effect (async):
bump SVN in OTP (irreversible),
nudge + Poll reports Activated + + Note over UA, Orch: between Offer and Activate + UA->>PLDM: CancelUpdate (MCTP, 0x1D) + activate PLDM + PLDM->>Orch: Abort + Note right of Orch: in-flight flash step completes
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),
orchestrator-side timeout releases the staging reservation. + + Note over UA, Flash: Blocking direction: always PLDM -> Orchestrator, never the reverse.
Every IPC response is immediate. Effects run async via poll_stage (one step, return, repeat).
PLDM stays free to service UA on MCTP. USER signal nudge replaces blind polling.
FD initiates TransferComplete, VerifyComplete, ApplyComplete. UA initiates ActivateFirmware. +``` From 33f2baae69b56b6d2ee1cf3eb8f23b1cec4062e8 Mon Sep 17 00:00:00 2001 From: Christina Quast Date: Tue, 8 Sep 2026 12:14:06 +0200 Subject: [PATCH 2/8] docs: fix write-error path and CancelUpdate consistency 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 --- docs/src/design/orchestrator/pldm-orchestrator-ipc.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md index 318e5a496..8df09d34a 100644 --- a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md +++ b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md @@ -21,8 +21,8 @@ Design decisions: - Transfer loop is zero-IPC: PLDM writes firmware bytes direct to flash and tracks progress locally. Complete carries the byte count for the orchestrator's coverage check (early-fail only, verify hashes the staged - image anyway). On a write error PLDM can nudge and report Failed via Poll, - no extra IPC verb needed. + image anyway). On a write error PLDM sends Abort to release staging and + reports the failure to the UA in TransferComplete's result code. ```mermaid sequenceDiagram @@ -96,7 +96,7 @@ sequenceDiagram Note right of Orch: activation effect (async):
bump SVN in OTP (irreversible),
nudge + Poll reports Activated Note over UA, Orch: between Offer and Activate - UA->>PLDM: CancelUpdate (MCTP, 0x1D) + UA->>PLDM: CancelUpdate (MCTP) activate PLDM PLDM->>Orch: Abort Note right of Orch: in-flight flash step completes
and is discarded @@ -106,5 +106,5 @@ sequenceDiagram Note over Orch: If PLDM dies mid-transfer (no Complete, no Abort),
orchestrator-side timeout releases the staging reservation. - Note over UA, Flash: Blocking direction: always PLDM -> Orchestrator, never the reverse.
Every IPC response is immediate. Effects run async via poll_stage (one step, return, repeat).
PLDM stays free to service UA on MCTP. USER signal nudge replaces blind polling.
FD initiates TransferComplete, VerifyComplete, ApplyComplete. UA initiates ActivateFirmware. + Note over UA, Flash: Blocking direction: always PLDM -> Orchestrator, never the reverse.
Every IPC response is immediate. Effects run async via poll_stage (one step, return, repeat).
PLDM stays free to service UA on MCTP. USER signal nudge replaces blind polling.
FD initiates TransferComplete, VerifyComplete, ApplyComplete. UA initiates ActivateFirmware and CancelUpdate. ``` From 8dcf85382aa9bd74e60776e20b04ea8fe10cb54b Mon Sep 17 00:00:00 2001 From: Christina Quast Date: Tue, 8 Sep 2026 12:30:16 +0200 Subject: [PATCH 3/8] docs: address review on the PLDM/orchestrator IPC page 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 --- docs/src/SUMMARY.md | 1 + .../orchestrator/pldm-orchestrator-ipc.md | 37 ++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 6160138de..ac879a90f 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -36,3 +36,4 @@ * [Verification Model](./design/orchestrator/orchestrator-model.md) * [State Machine](./design/orchestrator/orchestrator-machine.md) * [Platform Architecture](./design/orchestrator/orchestrator-platform.md) + * [PLDM/Orchestrator IPC](./design/orchestrator/pldm-orchestrator-ipc.md) diff --git a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md index 8df09d34a..a95cb01cf 100644 --- a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md +++ b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md @@ -11,13 +11,18 @@ Design decisions: - 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, - checks is_busy on the next call. Uses FlashDriver's split API, not - BlockingFlash. + checks is_busy on the next call and then complete_op, which is where the + operation's error surfaces. Uses FlashDriver's split API, not BlockingFlash. - USER signal is level-triggered (verified from Pigweed kernel source): OR'd into the peer's active_signals bitfield, persists until lowered. No lost wakeups. - MCTP server (separate process) buffers 4 messages while PLDM is in a - transact. Overflow drops silently, no backpressure to the bus. + transact. Overflow drops silently, no backpressure to the bus. Recovery from + a dropped message is PLDM's, not this seam's: pldm-lib defaults FD_T1 (update + mode idle) to 120s and FD_T2 (RequestFirmwareData retry) to 5s. +- A Rejected veto becomes an error completion code in the RequestUpdate + response, ALREADY_IN_UPDATE_MODE when the reason is an update already + running; the UA retries. - Transfer loop is zero-IPC: PLDM writes firmware bytes direct to flash and tracks progress locally. Complete carries the byte count for the orchestrator's coverage check (early-fail only, verify hashes the staged @@ -92,8 +97,10 @@ sequenceDiagram PLDM->>Orch: Activate Orch-->>PLDM: IntakeStatus::Activating deactivate PLDM - PLDM-->>UA: ActivateFirmware response + PLDM-->>UA: ActivateFirmware response (accepted, not done) Note right of Orch: activation effect (async):
bump SVN in OTP (irreversible),
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) @@ -104,7 +111,25 @@ sequenceDiagram deactivate PLDM PLDM-->>UA: CancelUpdate response - Note over Orch: If PLDM dies mid-transfer (no Complete, no Abort),
orchestrator-side timeout releases the staging reservation. + Note over Orch: If PLDM dies mid-transfer (no Complete, no Abort),
orchestrator-side timeout releases the staging reservation.
The transfer loop is zero-IPC, so this bounds total transfer time:
it must exceed worst-case transfer plus FD_T1 (120s),
so a live PLDM always aborts first. - Note over UA, Flash: Blocking direction: always PLDM -> Orchestrator, never the reverse.
Every IPC response is immediate. Effects run async via poll_stage (one step, return, repeat).
PLDM stays free to service UA on MCTP. USER signal nudge replaces blind polling.
FD initiates TransferComplete, VerifyComplete, ApplyComplete. UA initiates ActivateFirmware and CancelUpdate. + Note over UA, Flash: Blocking direction: always PLDM -> Orchestrator, never the reverse.
Every IPC response is immediate. Effects run async via poll_stage (one step, return, repeat).
PLDM stays free to service UA on MCTP. USER signal nudge replaces blind polling.
FD initiates TransferComplete, VerifyComplete, ApplyComplete. UA initiates ActivateFirmware, GetStatus and CancelUpdate. ``` + +## Open questions + +Who owns the SPI flash controller. The diagram has PLDM writing the staging +region and the orchestrator reading it, but FlashDriver takes `&mut self` and +says nothing about multiple clients. Either each process drives its own +controller over disjoint regions, or one process owns the driver and the other +reaches it over IPC. Sequencing keeps the two off the same bytes at the same +time (the orchestrator reads only after Complete), so this is about the driver +and the controller, not about the protocol. + +Whether the kernel can tell the orchestrator that PLDM's channel closed. That +would replace the transfer-time timeout, which has to be generous. + +The ActivateFirmware response says accepted, so the UA learns the outcome of +the irreversible SVN bump only from GetStatus. If activation fails after the +response, there is no rollback: the doc needs a line on what the UA is expected +to do. From 882daad2631d899b54f8f06564bd8169c9b3ab4a Mon Sep 17 00:00:00 2001 From: Christina Quast Date: Tue, 8 Sep 2026 12:39:35 +0200 Subject: [PATCH 4/8] docs: add write-access containment section to PLDM IPC design 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 --- .../orchestrator/pldm-orchestrator-ipc.md | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md index a95cb01cf..c2623c595 100644 --- a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md +++ b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md @@ -23,6 +23,13 @@ Design decisions: - A Rejected veto becomes an error completion code in the RequestUpdate response, ALREADY_IN_UPDATE_MODE when the reason is an update already running; the UA retries. +- Write access is two layers: a typed StagingWindow inside PLDM (Rust, catches + offset bugs) backed by an SMC write filter PLDM cannot reprogram (catches a + compromised process). The orchestrator opens the filter on Offer and closes + it on Complete/Abort/timeout. How the filter registers are kept out of + 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 tracks progress locally. Complete carries the byte count for the orchestrator's coverage check (early-fail only, verify hashes the staged @@ -116,6 +123,39 @@ sequenceDiagram Note over UA, Flash: Blocking direction: always PLDM -> Orchestrator, never the reverse.
Every IPC response is immediate. Effects run async via poll_stage (one step, return, repeat).
PLDM stays free to service UA on MCTP. USER signal nudge replaces blind polling.
FD initiates TransferComplete, VerifyComplete, ApplyComplete. UA initiates ActivateFirmware, GetStatus and CancelUpdate. ``` +## Write-access containment + +PLDM writes firmware bytes direct to flash (zero-IPC, see above), so write +access must be confined to the inactive slot and only for the duration of the +transfer. Two layers, each catching a different class of failure: + +The first layer is a typed StagingWindow inside the PLDM process. When PLDM +receives a Receiving response it constructs the window: a bounded handle over +the inactive slot (base address + length, capped to slot size). All writes go +through the window; it translates offsets and rejects anything outside the +region. The window is dropped on Complete, Abort, or timeout, so PLDM holds +no flash handle outside an active transfer. This catches offset bugs and +use-after-transfer bugs but not a compromised process, because PLDM still has +the underlying flash mapped. Whether Receiving carries an explicit base or the +staging region is board-static is an open question (see below). + +The second layer is a hardware write filter that PLDM cannot reprogram. The +SMC raises SmcInterrupt::WriteProtected on writes outside an allowed region +(interrupts.rs:59). The orchestrator (or a dedicated flash-service process) +opens the filter for the staging region on Offer and closes it on +Complete/Abort/timeout. PLDM needs the SMC control registers that drive +erase/program commands, but must not be able to touch the filter/write-protect +registers. Whether those register sets are separable (distinct MPU pages, +separate controller/CS, or lock-until-reset bits) depends on the AST10x0 +register layout and is folded into the "who owns the SPI flash controller" +open question below. + +The net effect: bugs hit the Rust window check, a compromised process hits the +hardware filter, and both "inactive slot only" and "only during an update" are +enforced. Even a fully rogue PLDM can at worst corrupt the staging area and +fail verify; the active image is never written by PLDM at any point in the +flow, and activation is orchestrator-side metadata plus the SVN bump in OTP. + ## Open questions Who owns the SPI flash controller. The diagram has PLDM writing the staging @@ -124,7 +164,16 @@ says nothing about multiple clients. Either each process drives its own controller over disjoint regions, or one process owns the driver and the other reaches it over IPC. Sequencing keeps the two off the same bytes at the same time (the orchestrator reads only after Complete), so this is about the driver -and the controller, not about the protocol. +and the controller, not about the protocol. A related constraint from the +write-access containment section: PLDM needs the erase/program control +registers but must not reach the write-protect/filter registers. Whether those +register sets fall on separate MPU pages on the AST10x0 (datasheet needed) +determines whether pw_kernel can enforce the split, or whether a dedicated +flash-service process must own the entire SMC and proxy writes. + +Whether Receiving carries an explicit base address for the staging region or +the region is board-static. The typed StagingWindow needs a base; today +Receiving only carries total. Whether the kernel can tell the orchestrator that PLDM's channel closed. That would replace the transfer-time timeout, which has to be generous. From 87aa749033dde39aa14608c3f03b9904cc86b221 Mon Sep 17 00:00:00 2001 From: Christina Quast Date: Tue, 8 Sep 2026 12:40:54 +0200 Subject: [PATCH 5/8] docs: drop stale line-number cite from write-access section Assisted-by: Claude --- docs/src/design/orchestrator/pldm-orchestrator-ipc.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md index c2623c595..489074630 100644 --- a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md +++ b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md @@ -140,8 +140,7 @@ the underlying flash mapped. Whether Receiving carries an explicit base or the staging region is board-static is an open question (see below). The second layer is a hardware write filter that PLDM cannot reprogram. The -SMC raises SmcInterrupt::WriteProtected on writes outside an allowed region -(interrupts.rs:59). The orchestrator (or a dedicated flash-service process) +SMC raises SmcInterrupt::WriteProtected on writes outside an allowed region. The orchestrator (or a dedicated flash-service process) opens the filter for the staging region on Offer and closes it on Complete/Abort/timeout. PLDM needs the SMC control registers that drive erase/program commands, but must not be able to touch the filter/write-protect From 0634d3d22b7e121ba1b88c48d3b7547e09db3b36 Mon Sep 17 00:00:00 2001 From: Christina Quast Date: Tue, 8 Sep 2026 12:46:20 +0200 Subject: [PATCH 6/8] docs: rewrap the write-filter paragraph Assisted-by: Claude --- .../orchestrator/pldm-orchestrator-ipc.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md index 489074630..2ad593ae1 100644 --- a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md +++ b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md @@ -139,15 +139,15 @@ use-after-transfer bugs but not a compromised process, because PLDM still has the underlying flash mapped. Whether Receiving carries an explicit base or the staging region is board-static is an open question (see below). -The second layer is a hardware write filter that PLDM cannot reprogram. The -SMC raises SmcInterrupt::WriteProtected on writes outside an allowed region. The orchestrator (or a dedicated flash-service process) -opens the filter for the staging region on Offer and closes it on -Complete/Abort/timeout. PLDM needs the SMC control registers that drive -erase/program commands, but must not be able to touch the filter/write-protect -registers. Whether those register sets are separable (distinct MPU pages, -separate controller/CS, or lock-until-reset bits) depends on the AST10x0 -register layout and is folded into the "who owns the SPI flash controller" -open question below. +The second layer is a hardware write filter that PLDM cannot reprogram. The SMC +raises SmcInterrupt::WriteProtected on writes outside an allowed region. The +orchestrator (or a dedicated flash-service process) opens the filter for the +staging region on Offer and closes it on Complete/Abort/timeout. PLDM needs the +SMC control registers that drive erase/program commands, but must not be able +to touch the filter/write-protect registers. Whether those register sets are +separable (distinct MPU pages, separate controller/CS, or lock-until-reset +bits) depends on the AST10x0 register layout and is folded into the "who owns +the SPI flash controller" open question below. The net effect: bugs hit the Rust window check, a compromised process hits the hardware filter, and both "inactive slot only" and "only during an update" are From 6ab555cea2a59be63e56dbe95dd5c51b4aea8a6b Mon Sep 17 00:00:00 2001 From: Christina Quast Date: Tue, 8 Sep 2026 12:56:38 +0200 Subject: [PATCH 7/8] docs: Receiving carries the staging base, sharpen the PLDM-death question 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 --- .../orchestrator/pldm-orchestrator-ipc.md | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md index 2ad593ae1..9c583fb09 100644 --- a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md +++ b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md @@ -23,6 +23,10 @@ Design decisions: - A Rejected veto becomes an error completion code in the RequestUpdate response, ALREADY_IN_UPDATE_MODE when the reason is an update already running; the UA retries. +- Receiving carries the staging base address, not just the total. The + orchestrator picks the region and programs the SMC write filter for it, so + the window PLDM writes through and the window the hardware allows come from + one place. PLDM holds no board layout. - Write access is two layers: a typed StagingWindow inside PLDM (Rust, catches offset bugs) backed by an SMC write filter PLDM cannot reprogram (catches a compromised process). The orchestrator opens the filter on Offer and closes @@ -57,8 +61,8 @@ sequenceDiagram activate PLDM PLDM->>Orch: Offer { target: TargetId, total: u64 } - Note right of Orch: validate target + length,
reserve staging - Orch-->>PLDM: IntakeStatus::Receiving { total } + Note right of Orch: validate target + length,
reserve staging,
open the SMC write filter + Orch-->>PLDM: IntakeStatus::Receiving { base: FlashAddress, total } deactivate PLDM loop FD pulls chunks from UA via RequestFirmwareData @@ -130,14 +134,13 @@ access must be confined to the inactive slot and only for the duration of the transfer. Two layers, each catching a different class of failure: The first layer is a typed StagingWindow inside the PLDM process. When PLDM -receives a Receiving response it constructs the window: a bounded handle over -the inactive slot (base address + length, capped to slot size). All writes go +receives a Receiving response it constructs the window from the base and total +it carries: a bounded handle over the staging region, capped to its length. All writes go through the window; it translates offsets and rejects anything outside the region. The window is dropped on Complete, Abort, or timeout, so PLDM holds no flash handle outside an active transfer. This catches offset bugs and use-after-transfer bugs but not a compromised process, because PLDM still has -the underlying flash mapped. Whether Receiving carries an explicit base or the -staging region is board-static is an open question (see below). +the underlying flash mapped. The second layer is a hardware write filter that PLDM cannot reprogram. The SMC raises SmcInterrupt::WriteProtected on writes outside an allowed region. The @@ -170,12 +173,16 @@ register sets fall on separate MPU pages on the AST10x0 (datasheet needed) determines whether pw_kernel can enforce the split, or whether a dedicated flash-service process must own the entire SMC and proxy writes. -Whether Receiving carries an explicit base address for the staging region or -the region is board-static. The typed StagingWindow needs a base; today -Receiving only carries total. - -Whether the kernel can tell the orchestrator that PLDM's channel closed. That -would replace the transfer-time timeout, which has to be generous. +How the orchestrator learns that PLDM died, short of the timeout. Abort is a +message a live PLDM sends; the timeout covers the case where it can send +nothing. pw_kernel has no peer-closed signal: the set is READABLE, WRITEABLE, +ERROR, JOINABLE, USER and the interrupt bits. The one existing path is +ChannelInitiatorObject::reset, which raises ERROR on the handler, and only if a +transaction was in flight and only once someone joins the dead process. During +the zero-IPC transfer loop no transaction is in flight, so the orchestrator +sees nothing. Replacing the timeout means the orchestrator waits on JOINABLE on +PLDM's process object, or the supervisor that joins PLDM tells it. That is a +supervisor question, not a channel one. The ActivateFirmware response says accepted, so the UA learns the outcome of the irreversible SVN bump only from GetStatus. If activation fails after the From 7a63481127aa29ec181191ba274852e15da4f461 Mon Sep 17 00:00:00 2001 From: Christina Quast Date: Tue, 8 Sep 2026 12:59:30 +0200 Subject: [PATCH 8/8] docs: state what activation reports instead of what the UA should do 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 --- .../orchestrator/pldm-orchestrator-ipc.md | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md index 9c583fb09..7d80d0795 100644 --- a/docs/src/design/orchestrator/pldm-orchestrator-ipc.md +++ b/docs/src/design/orchestrator/pldm-orchestrator-ipc.md @@ -23,6 +23,14 @@ Design decisions: - A Rejected veto becomes an error completion code in the RequestUpdate response, ALREADY_IN_UPDATE_MODE when the reason is an update already running; the UA retries. +- Activation reports, it does not roll back. The ActivateFirmware response + carries the spec's estimated_time and means accepted; the outcome of the SVN + bump reaches the UA as GetStatus AuxStateStatus (GenericError is the only + failure value the spec offers), and GetFirmwareParameters shows which version + is actually active. A failed activation leaves a bootable system, because + PLDM never writes the active image and staging is inert, so re-running the + update from RequestUpdate is always available. What the UA does with that is + the UA's. - Receiving carries the staging base address, not just the total. The orchestrator picks the region and programs the SMC write filter for it, so the window PLDM writes through and the window the hardware allows come from @@ -135,12 +143,12 @@ transfer. Two layers, each catching a different class of failure: The first layer is a typed StagingWindow inside the PLDM process. When PLDM receives a Receiving response it constructs the window from the base and total -it carries: a bounded handle over the staging region, capped to its length. All writes go -through the window; it translates offsets and rejects anything outside the -region. The window is dropped on Complete, Abort, or timeout, so PLDM holds -no flash handle outside an active transfer. This catches offset bugs and -use-after-transfer bugs but not a compromised process, because PLDM still has -the underlying flash mapped. +it carries: a bounded handle over the staging region, capped to its length. All +writes go through the window; it translates offsets and rejects anything +outside the region. The window is dropped on Complete, Abort, or timeout, so +PLDM holds no flash handle outside an active transfer. This catches offset bugs +and use-after-transfer bugs but not a compromised process, because PLDM still +has the underlying flash mapped. The second layer is a hardware write filter that PLDM cannot reprogram. The SMC raises SmcInterrupt::WriteProtected on writes outside an allowed region. The @@ -183,8 +191,3 @@ the zero-IPC transfer loop no transaction is in flight, so the orchestrator sees nothing. Replacing the timeout means the orchestrator waits on JOINABLE on PLDM's process object, or the supervisor that joins PLDM tells it. That is a supervisor question, not a channel one. - -The ActivateFirmware response says accepted, so the UA learns the outcome of -the irreversible SVN bump only from GetStatus. If activation fails after the -response, there is no rollback: the doc needs a line on what the UA is expected -to do.