Skip to content

docs: PLDM/orchestrator IPC design - #458

Closed
chrysh wants to merge 8 commits into
OpenPRoT:mainfrom
9elements:worktree-docs-pldm-ipc
Closed

chrysh wants to merge 8 commits into
OpenPRoT:mainfrom
9elements:worktree-docs-pldm-ipc

Conversation

@chrysh

@chrysh chrysh commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • PLDM/orchestrator IPC design as a sequence diagram, plus a write-access containment section
  • Two kernel channels (notify + intake), both initiated by PLDM
  • Firmware bytes stay out of IPC (direct to flash, PLDM tracks progress locally)
  • Write access is confined to the staging region for the duration of the transfer: typed StagingWindow in PLDM, backed by an SMC write filter the orchestrator controls
  • Verified: USER signal latches (level-triggered), MCTP server buffers 4 messages, poll_stage bounded by flash erase time

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.
Loading

Test plan

  • Review the diagram and the write-access containment section in docs/src/design/orchestrator/pldm-orchestrator-ipc.md
  • Comment on the two open questions

Assisted-by: Claude

@chrysh
chrysh marked this pull request as ready for review September 7, 2026 15:19
@chrysh
chrysh force-pushed the worktree-docs-pldm-ipc branch 3 times, most recently from 84c7421 to 7ed6174 Compare September 8, 2026 07:56
@chrysh

chrysh commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

My open questions (mostly to myself) after talking with @embediver, and me answering them myself:

  1. Does the PLDM process have direct write access to the flash? What if it is a malicious PLDM service?

The SPIPF filter watches the host, not the RoT's own writes. If PLDM code is compromised, the software-only Updatable trait contract is all that stands between it and the active slot.

  1. How/where is the async Verification thread spawned?

No async thread. In pw_kernel, you need to define the processes at compile time, they cannot be spawned dynamically. Therefore, we decided for the IncrementalVerifier setup, where the orchestrator verifies one chunk per loop, so that it does not block.

  1. How does the SPI Monitor influence the write and boot process?

The SPIPF hardware sits between the host SPI master and the flash chip. It does three
things:

a. Mux control (MuxSelect): RoT takes exclusive flash access during boot/verify (RotControl), then hands back to host (HostControl)
b. Address privilege filtering: 256 MiB space divided into 16 KB blocks, each independently allow/deny for read and write. The RoT configures RegionPolicy::deny(start, length, Write) over the active slot, then calls lock() to make the policy immutable until reset
c. Command filtering: allow-list of SPI opcodes, so write-enabling commands (WREN, page program, sector erase) can be excluded entirely

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.

  1. Do we have just one flash chip, e.g. with one active slot region and the inactive region? How do we write protect the active slot/memory region? SPI Monitor?

Slot layout is deliberately abstract in the codebase. The concrete layout (addresses, sizes, how many chips) lives in board-specific code that doesn't exist yet. The hardware supports multiple flash chips via FMC (CE0/CE1) and SPI1/SPI2 controllers.
Write protection of the active slot comes from the SPIPF address privilege bitmap: deny writes to the active region, lock the policy. Whether that's one chip with two regions or two chips with separate chip-selects is a board decision.

# 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

@rusty1968 rusty1968 Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

@chrysh chrysh Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

@rusty1968 rusty1968 Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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?

@rusty1968

Copy link
Copy Markdown
Collaborator

@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,

@rusty1968 rusty1968 Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

What do you mean by not in the data path? The orchestrator will tell pldm where to put the firmware though, I assume?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

@rusty1968 rusty1968 Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

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
…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
@chrysh
chrysh force-pushed the worktree-docs-pldm-ipc branch from 7a63481 to 63dcb7b Compare September 10, 2026 07:20
@chrysh

chrysh commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Snapshotting the sequence diagram as it stands at 7a63481, so this thread keeps
a fixed version to argue against while the diagram itself changes. The next
revision folds in the FdOps blocking question, the flash service in #365, and
out-of-transport activation from pldm-lib #20.

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.
Loading

@chrysh

chrysh commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

@CourtneyDrant pldm-lib #20 adds ActivatePendingComponent and the
handle_pending_component callback, which is the out-of-transport path Rusty
mentions above. Three questions before I revise the diagram here.

  1. Is out-of-transport additive, or the path you expect production to use? If
    it is primary, the transfer loop in this diagram (RequestFirmwareData, the
    staging window, the coverage check on Complete) describes a path nobody
    takes.

  2. With out-of-transport, who writes the image into flash before
    ActivatePendingComponent arrives, and how does the RoT learn where it landed
    and how long it is? There is no intake session to carry that, and
    handle_pending_component gets classification, identifier and index only.

  3. Does the FD support both entry points at once? Then the orchestrator needs a
    path for an activation with no preceding intake session, where it verifies
    an image it never reserved staging for.

I will draw whichever you confirm.

chrysh added a commit to 9elements/openprot that referenced this pull request Sep 10, 2026
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
chrysh added a commit to 9elements/openprot that referenced this pull request Sep 10, 2026
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
chrysh added a commit to 9elements/openprot that referenced this pull request Sep 10, 2026
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
chrysh added a commit to 9elements/openprot that referenced this pull request Sep 10, 2026
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
chrysh added a commit to 9elements/openprot that referenced this pull request Sep 10, 2026
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
chrysh added a commit to 9elements/openprot that referenced this pull request Sep 10, 2026
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
@chrysh

chrysh commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

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.

@chrysh chrysh closed this Sep 14, 2026
chrysh added a commit to 9elements/openprot that referenced this pull request Sep 14, 2026
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
chrysh added a commit to 9elements/openprot that referenced this pull request Sep 14, 2026
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
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.

2 participants