From a4be1c6f6218c0bd89feac112ac4bfe69abdbd8d Mon Sep 17 00:00:00 2001 From: Courtney Drant Date: Wed, 15 Jul 2026 19:54:13 -0700 Subject: [PATCH 1/6] Fixes for License check --- Cargo.toml | 2 ++ deny.toml | 16 ++++++++++++++++ pldm-common/Cargo.toml | 1 + pldm-interface/Cargo.toml | 1 + 4 files changed, 20 insertions(+) create mode 100644 deny.toml diff --git a/Cargo.toml b/Cargo.toml index 112c4cc..d49a10f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "pldm-lib" version = "0.1.0" authors = ["Caliptra contributors", "OpenPRoT contributors"] edition = "2021" +license.workspace = true [workspace] members = [ @@ -14,6 +15,7 @@ members = [ version = "0.1.0" authors = ["Caliptra contributors", "OpenPRoT contributors"] edition = "2021" +license = "Apache-2.0" [workspace.dependencies] pldm-common = { path = "pldm-common" } diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..28072fc --- /dev/null +++ b/deny.toml @@ -0,0 +1,16 @@ +[licenses] +allow = [ + "Apache-2.0", + "MIT", + "Unicode-3.0", +] + +[bans] +multiple-versions = "warn" + +[advisories] +ignore = [] + +[sources] +unknown-registry = "warn" +unknown-git = "warn" diff --git a/pldm-common/Cargo.toml b/pldm-common/Cargo.toml index ac80d23..b2db91e 100644 --- a/pldm-common/Cargo.toml +++ b/pldm-common/Cargo.toml @@ -5,6 +5,7 @@ name = "pldm-common" version.workspace = true edition.workspace = true authors.workspace = true +license.workspace = true [dependencies] zerocopy.workspace = true diff --git a/pldm-interface/Cargo.toml b/pldm-interface/Cargo.toml index 384a42e..41f5661 100644 --- a/pldm-interface/Cargo.toml +++ b/pldm-interface/Cargo.toml @@ -5,6 +5,7 @@ name = "pldm-interface" version.workspace = true edition.workspace = true authors.workspace = true +license.workspace = true [dependencies] zerocopy.workspace = true From 09b065c29a7bc98738c538c9a1c61f57d084ff11 Mon Sep 17 00:00:00 2001 From: Courtney Drant Date: Thu, 27 Aug 2026 15:58:36 -0700 Subject: [PATCH 2/6] Added README.md to describe FdOps traits and their usage in fd_context. Adjusted 'now' default to safer value of 0, disabling timestamps. Users will need to implement now using a counter from their environment. --- pldm-interface/src/firmware_device/README.md | 228 ++++++++++++++++++ .../src/firmware_device/fd_context.rs | 5 + pldm-interface/src/firmware_device/fd_ops.rs | 2 +- 3 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 pldm-interface/src/firmware_device/README.md diff --git a/pldm-interface/src/firmware_device/README.md b/pldm-interface/src/firmware_device/README.md new file mode 100644 index 0000000..64fa921 --- /dev/null +++ b/pldm-interface/src/firmware_device/README.md @@ -0,0 +1,228 @@ +# Firmware Device Context and Operations + +This directory implements the Firmware Device (FD) side of the PLDM firmware +update protocol. The implementation is split into three parts: + +- `fd_context.rs` implements protocol message handling and drives the firmware + update state machine. +- `fd_internal.rs` stores protocol state, the active component, request state, + progress, transfer limits, and timeout data. +- `fd_ops.rs` defines `FdOps`, the platform callback interface used by the + protocol implementation to inspect or modify the actual device. + +## `FirmwareDeviceContext` + +`FirmwareDeviceContext<'a, O: FdOps>` combines an `FdInternal` state object +with a borrowed platform implementation of `FdOps`. Construct it with +`FirmwareDeviceContext::new(&ops)`. The context does not own the platform +implementation. + +The context has two roles: + +1. Its `*_rsp` methods handle requests sent by the Update Agent (UA), validate + the current state, invoke the required `FdOps` callback, encode a response, + and update `FdInternal`. +2. In the `Download`, `Verify`, and `Apply` states, `fd_progress` drives + FD-initiated requests. `handle_response` accepts the matching UA response + and advances the state machine. + +A successful update normally follows this state sequence: + +```text +Idle + -> LearnComponents RequestUpdate + -> ReadyXfer final PassComponentTable + -> Download accepted UpdateComponent + -> Verify successful TransferComplete exchange + -> Apply successful VerifyComplete exchange + -> ReadyXfer successful ApplyComplete exchange + -> Activate -> Idle successful ActivateFirmware +``` + +Additional components can be updated by repeating the `ReadyXfer` through +`Apply` portion before activation. Cancellation, operation failure, or a T1 +timeout can take the flow to another state or back to `Idle`. + +`FdOpsError` values are generally converted to `MsgHandlerError::FdOps` and +returned to the caller. The exception is `query_download_progress`: its error +is intentionally ignored by `get_status_rsp`, which reports the default +progress value instead. + +## `FdOps` callback map + +Every required method is implemented by the platform integrating this crate. +`get_non_functional_component_info` and `now` have defaults, although a real +device will normally override `now` with a monotonic millisecond clock. + +### `get_device_identifiers` + +Fills the provided descriptor array and returns the number of valid entries. +The first returned entry is used as the initial descriptor and any remaining +entries are encoded as additional descriptors. + +Called by `FirmwareDeviceContext::query_devid_rsp` while handling +`QueryDeviceIdentifiers`. The returned count must fit the provided slice and +must include at least one descriptor. + +### `get_firmware_parms` + +Populates `FirmwareParameters` with the device's active and pending firmware +information and component parameter entries. + +Called by: + +- `get_firmware_parameters_rsp`, to answer `GetFirmwareParameters`. +- `pass_component_rsp`, before checking a component with + `handle_component(PassComponent)`. +- `update_component_rsp`, before checking a component with + `handle_component(UpdateComponent)`. + +### `get_xfer_size` + +Chooses the transfer size the FD will use, given the maximum transfer size +offered by the UA. The context stores the result in `FdInternal`, which later +caps each `RequestFirmwareData` chunk to that size. + +Called by `request_update_rsp` after the UA's size has passed the PLDM baseline +size check and before the context enters `LearnComponents`. + +### `handle_component` + +Checks whether the device can process a component and returns the appropriate +`ComponentResponseCode`. The `ComponentOperation` argument distinguishes the +two call sites: + +- `pass_component_rsp` passes `PassComponent` while the context is learning + the UA's component table. +- `update_component_rsp` passes `UpdateComponent` when the UA requests an + actual update. If the result is `CompCanBeUpdated`, the context enters + `Download` and enables FD-initiated progress. + +The accompanying `FirmwareParameters` are freshly obtained through +`get_firmware_parms` at each call site. + +### `query_download_offset_and_length` + +Returns the next component-relative byte offset and requested byte count. This +allows the platform to select missing ranges or resume a partial transfer. The +context validates the range against the component image size, permits the PLDM +maximum padding, and caps the request to the negotiated transfer size. + +Called by `fd_progress_download` when the context is in `Download`, the current +FD request is ready, and transfer completion has not yet been reported. The +result is encoded into `RequestFirmwareData`. + +### `download_fw_data` + +Consumes one successful `RequestFirmwareData` response. `offset` is the offset +previously selected for the request, `data` is the returned firmware chunk, +and `component` is the active component. The returned `TransferResult` controls +whether downloading continues or a `TransferComplete` request is prepared. + +Called by `process_request_fw_data_rsp`, which is reached from +`handle_response` for a matching `RequestFirmwareData` response in the +`Download` state. + +### `is_download_complete` + +Reports whether all data required for the active component has been received. + +Called by `process_request_fw_data_rsp` only after `download_fw_data` returns +`TransferSuccess`. A `true` result marks the FD request complete so the next +`fd_progress` call sends `TransferComplete`; a `false` result makes another +firmware-data request eligible. + +### `query_download_progress` + +Writes the current download percentage into `ProgressPercent` for status +reporting. + +Called by `get_status_rsp` when the current state is `Download`. Errors from +this callback are ignored and leave the response at its default progress. + +### `verify` + +Performs or advances verification of the active component and updates the +reported percentage. Returning `VerifySuccess` with progress below 100 means +verification is still in progress; `fd_progress` returns no message and calls +the callback again later. At completion or on failure, the context sends a +`VerifyComplete` request containing the returned result. + +Called by `pldm_fd_progress_verify`, selected by `fd_progress` in the `Verify` +state when an FD request may be sent. + +### `apply` + +Performs or advances application of the active component and updates the +reported percentage. Returning `ApplySuccess` with progress below 100 keeps +the operation in progress. At completion or on failure, the context sends an +`ApplyComplete` request containing the returned result. + +Called by `pldm_fd_progress_apply`, selected by `fd_progress` in the `Apply` +state when an FD request may be sent. + +### `activate` + +Activates the newly applied firmware. It receives the UA's self-contained +activation request and may set the estimated activation time. It returns the +PLDM completion code directly; implementations should return +`PLDM_FWUP_INCOMPLETE_UPDATE` if expected components were not updated. + +Called by `activate_firmware_rsp` in `ReadyXfer`. Success or +`ActivationNotRequired` moves the context through `Activate` and then to +`Idle` with `ActivateFw` as the status reason. + +### `cancel_update_component` + +Stops and cleans up work on the active component. + +Called from three paths: + +- `cancel_update_component_rsp`, for a component cancellation during + `Download`, `Verify`, or an incomplete/failed `Apply`. +- `cancel_update_rsp`, for a complete update cancellation in the same active + operation states. +- `fd_progress`, when a sent FD request exceeds the T1 timeout in `Download`, + `Verify`, or `Apply`; the context then transitions to `Idle` and rejects a + late UA response. + +### `get_non_functional_component_info` + +Returns the indication and bitmap describing components that will not function +after leaving update mode. The default implementation reports that all +components are functioning with an empty bitmap. + +Called by `cancel_update_rsp` to populate every `CancelUpdate` response after +the context has determined whether an active component operation should be +cancelled. + +### `now` + +Returns the current time in milliseconds as `PldmFdTime`. Production +implementations should use a monotonic time source because the context uses +saturating elapsed-time calculations and rejects retry calculations when time +moves backwards. The trait's fixed default value is mainly useful as a stub. + +Called throughout `fd_context.rs` to: + +- set or refresh the T1 activity timestamp in `set_fd_t1_ts`, including UA + request/response handling and verify/apply work; +- timestamp outgoing `RequestFirmwareData`, `TransferComplete`, + `VerifyComplete`, and `ApplyComplete` requests; +- detect T1 response timeouts in `fd_progress`; +- determine T2 retransmission eligibility in `should_send_fd_request`. + +## Driving initiator mode + +The owner of `FirmwareDeviceContext` should use +`should_start_initiator_mode`/`should_stop_initiator_mode` to coordinate its +transport loop. While initiator mode is active, call `fd_progress` to obtain the +next encoded FD request. A return value of zero during verify or apply means +local work is still progressing and no PLDM message should be sent. Pass a UA +response to `handle_response`; it accepts only the command and instance ID of +the currently sent request. + +`fd_progress` also handles T2 retries for sent requests and T1 cancellation +when the UA remains silent. The embedding application is responsible for +scheduling calls often enough for those timers and platform operations to make +progress. \ No newline at end of file diff --git a/pldm-interface/src/firmware_device/fd_context.rs b/pldm-interface/src/firmware_device/fd_context.rs index c09da30..e0bde2a 100644 --- a/pldm-interface/src/firmware_device/fd_context.rs +++ b/pldm-interface/src/firmware_device/fd_context.rs @@ -1191,6 +1191,11 @@ mod tests { NonFunctioningComponentBitmap::new(0), )) } + + fn now(&self) -> pldm_common::protocol::firmware_update::PldmFdTime { + // Return a fixed timestamp for testing + crate::config::DEFAULT_FD_T1_TIMEOUT + 1 + } } fn new_test_fd_ctx() -> FirmwareDeviceContext<'static, TestFdOps> { diff --git a/pldm-interface/src/firmware_device/fd_ops.rs b/pldm-interface/src/firmware_device/fd_ops.rs index a0cd73d..bfa285a 100644 --- a/pldm-interface/src/firmware_device/fd_ops.rs +++ b/pldm-interface/src/firmware_device/fd_ops.rs @@ -269,6 +269,6 @@ pub trait FdOps { /// /// * `PldmFdTime` - The current timestamp in milliseconds. fn now(&self) -> PldmFdTime { - 0xbaddbadd + 0 } } From 117bee9749e7809d76c36546449db87c8092602b Mon Sep 17 00:00:00 2001 From: CourtneyDrant Date: Wed, 2 Sep 2026 13:44:29 -0700 Subject: [PATCH 3/6] Update pldm-interface/src/firmware_device/README.md Co-authored-by: Christina Quast --- pldm-interface/src/firmware_device/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pldm-interface/src/firmware_device/README.md b/pldm-interface/src/firmware_device/README.md index 64fa921..811e3a6 100644 --- a/pldm-interface/src/firmware_device/README.md +++ b/pldm-interface/src/firmware_device/README.md @@ -43,10 +43,11 @@ Additional components can be updated by repeating the `ReadyXfer` through `Apply` portion before activation. Cancellation, operation failure, or a T1 timeout can take the flow to another state or back to `Idle`. -`FdOpsError` values are generally converted to `MsgHandlerError::FdOps` and -returned to the caller. The exception is `query_download_progress`: its error -is intentionally ignored by `get_status_rsp`, which reports the default -progress value instead. +Every `FdOpsError` is converted to `MsgHandlerError::FdOps` and returned to the +caller, except for `query_download_progress`: `get_status_rsp` ignores that +error and leaves whatever the callback wrote in `ProgressPercent`. If the +callback wrote nothing, that is `ProgressPercent::default()`, which is 101 +(`PROGRESS_PERCENT_NOT_SUPPORTED`). ## `FdOps` callback map From 67292789997438d621a2593739c590b71ddf516c Mon Sep 17 00:00:00 2001 From: CourtneyDrant Date: Wed, 2 Sep 2026 13:45:18 -0700 Subject: [PATCH 4/6] Update pldm-interface/src/firmware_device/README.md Co-authored-by: Christina Quast --- pldm-interface/src/firmware_device/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pldm-interface/src/firmware_device/README.md b/pldm-interface/src/firmware_device/README.md index 811e3a6..88456b1 100644 --- a/pldm-interface/src/firmware_device/README.md +++ b/pldm-interface/src/firmware_device/README.md @@ -109,9 +109,11 @@ allows the platform to select missing ranges or resume a partial transfer. The context validates the range against the component image size, permits the PLDM maximum padding, and caps the request to the negotiated transfer size. -Called by `fd_progress_download` when the context is in `Download`, the current -FD request is ready, and transfer completion has not yet been reported. The -result is encoded into `RequestFirmwareData`. +Called by `fd_progress_download` when the context is in `Download`, an FD +request may be sent (state `Ready`, or `Sent` with the T2 retry time elapsed), +and transfer completion has not yet been reported. The result is encoded into +`RequestFirmwareData`. The callback runs again on every T2 retry, so it can +return a different range while a `RequestFirmwareData` is still outstanding. ### `download_fw_data` From ef5ce79706cc1b22bf0a1f3edbe0ec2d1ee48d3a Mon Sep 17 00:00:00 2001 From: CourtneyDrant Date: Wed, 2 Sep 2026 13:48:33 -0700 Subject: [PATCH 5/6] Update pldm-interface/src/firmware_device/README.md Co-authored-by: Christina Quast --- pldm-interface/src/firmware_device/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pldm-interface/src/firmware_device/README.md b/pldm-interface/src/firmware_device/README.md index 88456b1..b79d56d 100644 --- a/pldm-interface/src/firmware_device/README.md +++ b/pldm-interface/src/firmware_device/README.md @@ -185,9 +185,10 @@ Called from three paths: `Download`, `Verify`, or an incomplete/failed `Apply`. - `cancel_update_rsp`, for a complete update cancellation in the same active operation states. -- `fd_progress`, when a sent FD request exceeds the T1 timeout in `Download`, - `Verify`, or `Apply`; the context then transitions to `Idle` and rejects a - late UA response. +- `fd_progress`, when an FD request is outstanding (`FdReqState::Sent`) in + `Download`, `Verify`, or `Apply` and the UA has been quiet for longer than + the T1 timeout; the context then transitions to `Idle` and rejects a late UA + response. ### `get_non_functional_component_info` From d784476b627a8e57c6a4d4199d5fdcda461a9ee4 Mon Sep 17 00:00:00 2001 From: CourtneyDrant Date: Wed, 2 Sep 2026 13:50:10 -0700 Subject: [PATCH 6/6] Update pldm-interface/src/firmware_device/fd_context.rs Co-authored-by: Christina Quast --- pldm-interface/src/firmware_device/fd_context.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pldm-interface/src/firmware_device/fd_context.rs b/pldm-interface/src/firmware_device/fd_context.rs index e0bde2a..e5b438f 100644 --- a/pldm-interface/src/firmware_device/fd_context.rs +++ b/pldm-interface/src/firmware_device/fd_context.rs @@ -1193,7 +1193,8 @@ mod tests { } fn now(&self) -> pldm_common::protocol::firmware_update::PldmFdTime { - // Return a fixed timestamp for testing + // Fixed clock past T1, so a test that leaves fd_t1_update_ts at 0 + // is already timed out. crate::config::DEFAULT_FD_T1_TIMEOUT + 1 } }