Skip to content

pldm: Add the IPC client crate - #510

Merged
chrysh merged 4 commits into
OpenPRoT:ocp-global-demo-wipfrom
9elements:pldm-ipc-client
Sep 30, 2026
Merged

chrysh merged 4 commits into
OpenPRoT:ocp-global-demo-wipfrom
9elements:pldm-ipc-client

Conversation

@chrysh

@chrysh chrysh commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR: Add the API that makes the orchestrator give out grants through the interface PldmOps.

Independent of the update-pump stack: the new code is one crate, based directly on ocp-global-demo-wip. It also edits five files that already exist (services/pldm/api/src/{error,lib,wire}.rs, services/pldm/server/src/lib.rs and docs/src/design/ipc-service-stack.md), none of which #504-#513 touch.

FdIpcClient<T: AsyncTransport> is the orchestrator's side of the PLDM IPC channel, one typed method per operation the firmware device answers. Each encodes a request and starts a round-trip; poll() collects the reply. Split-phase because the orchestrator runs the same loop it supervises boots on, and the FD answers on its own schedule: it shares its loop with MCTP traffic from the update agent.

The client records what it asked. The response frame carries a code and a payload but not the opcode it answers, so the pending operation is what says whether the payload is a status. One round-trip at a time, which the transport already enforces.

A refusal comes back as ClientError::Refused with the FD's code: the device's decision, not a fault in the channel, and it ends the round-trip like any other answer. That is the job PldmIpcError was written for and nothing ever wrapped it, so the last commit drops it.

14 host tests over util_service::Loopback against the real FdIpcServer, so the same encode and decode paths are exercised end to end with no kernel. One uses Delayed for the not-ready path, which the loopback alone cannot reach: that was the open coverage gap from the #482 review. Three stub transports cover what the loopback cannot produce either: a poll that fails, a frame too short to decode, and a cancel the channel refuses. Each one ends the round-trip and leaves the client free to send again.

WireError gains a core::error::Error impl so it can be the source() of a client error.

A grant's reply is Reply::Acked: the FD took the request, not a verdict on the work it starts. QueryStatus is what reports how that went.

Commits:

  1. Rename FdServer and FdHandler to FdIpcServer and FdIpcHandler, matching FlashIpcServer and the client this PR adds.
  2. Say that DenyActivate revokes a stored grant, which the op doc left out.
  3. Add the client crate.
  4. Drop the unused PldmIpcError.

Next on this seam: wiring the client into the orchestrator event loop, where the FD's QueryStatus phases meet the update pump.

FdIpcServer and FdIpcHandler, matching FlashIpcServer and the client on
the other end. Fd says which peer, Ipc says which channel: the FD also
answers the update agent over MCTP, and that side is not this one.

Assisted-by: Claude
@chrysh
chrysh force-pushed the pldm-ipc-client branch 2 times, most recently from 86ad45a to 9695b78 Compare September 29, 2026 14:40
The FD stores a grant until the UA asks to activate, so a later deny has
something to take back. The doc only named the refusal case.

Assisted-by: Claude
One typed method per operation the firmware device answers, each
encoding a request and starting a round-trip, with the reply collected
by poll(). Split-phase because the orchestrator runs the same loop it
supervises boots on, and the FD answers on its own schedule: it shares
its loop with MCTP traffic from the update agent.

The client records what it asked. The response frame carries a code and
a payload but not the opcode it answers, so the pending operation is
what says whether the payload is a status. One round-trip at a time,
which the transport already enforces.

Refusals come back as ClientError::Refused with the FD's code: a
decision by the device, not a fault in the channel, and it ends the
round-trip like any other answer.

WireError gains a core::error::Error impl so it can be the source of a
client error.

Assisted-by: Claude
It was put there for the orchestrator's client layer, which now reports
a refusal as ClientError::Refused carrying the same ResponseCode.
Nothing wrapped it.

Assisted-by: Claude
@chrysh
chrysh marked this pull request as ready for review September 30, 2026 09:36
@chrysh
chrysh merged commit 31ce90b into OpenPRoT:ocp-global-demo-wip Sep 30, 2026
1 check passed
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.

1 participant