Skip to content

Commit 065dde8

Browse files
committed
cmd_interface: Distinguish poll-again from stop-polling for the initiator
The polling caller could not implement a correct loop: a request in flight with T2 not yet elapsed surfaced as FdInitiatorModeError, the same error as calling in a non-initiator state, so waiting, done and misuse were indistinguishable. caliptra-mcu-sw grew ResponderAction and a cancellation flag for the responder side of the same problem; this puts the outcome in the return type instead. generate_initiator_request now returns InitiatorAction: - Request(n): transmit msg_buf[..n] (includes the MCTP type byte) - Waiting: request in flight or operation still running, poll again - Complete: the FD left initiator mode, stop polling The waiting case inside fd_progress becomes Ok(0) instead of an error, which also makes the T1 check reachable while waiting - before this, a silent UA could only be timed out on the T2 resend path. Errors are now real faults only, plus T1Timeout from the cancel path. Signed-off-by: Christina Quast <christina.quast@9elements.com>
1 parent 69edca0 commit 065dde8

2 files changed

Lines changed: 94 additions & 9 deletions

File tree

pldm-interface/src/cmd_interface.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ use pldm_common::util::mctp_transport::{
2828

2929
pub type PldmCompletionErrorCode = u8;
3030

31+
/// What the initiator polling loop should do next, as reported by
32+
/// [`CmdInterface::generate_initiator_request`].
33+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34+
pub enum InitiatorAction {
35+
/// A request was generated: transmit `msg_buf[..n]`.
36+
Request(usize),
37+
/// Nothing due — a request is in flight or the current operation is
38+
/// still running. Poll again.
39+
Waiting,
40+
/// The FD left initiator mode; stop polling.
41+
Complete,
42+
}
43+
3144
// Helper function to write a failure response message into payload
3245
pub(crate) fn generate_failure_response(
3346
payload: &mut [u8],
@@ -86,17 +99,31 @@ impl<'a, O: FdOps> CmdInterface<'a, O> {
8699
/// Sets the MCTP message-type byte at `msg_buf[0]` and writes the PLDM
87100
/// request starting at `msg_buf[1]`.
88101
///
89-
/// Returns `Ok(None)` when no request is pending (nothing to send).
90-
/// Returns `Ok(Some(n))` where `n` is the total number of bytes written
91-
/// to `msg_buf` (1 MCTP header byte + `n - 1` PLDM bytes) when a request
92-
/// was generated.
102+
/// Returns [`InitiatorAction::Request`]`(n)` when a request was
103+
/// generated; transmit `msg_buf[..n]` (1 MCTP header byte plus the
104+
/// encoded PLDM request). [`InitiatorAction::Waiting`] means nothing is
105+
/// due yet — poll again. [`InitiatorAction::Complete`] means the FD
106+
/// left initiator mode — stop polling.
107+
///
108+
/// # Errors
109+
///
110+
/// `T1Timeout`: no UA response arrived within T1 — the update was
111+
/// cancelled and the FD is back in Idle. A protocol outcome to report,
112+
/// not a transport fault to retry; the next poll returns `Complete`.
93113
pub fn generate_initiator_request(
94114
&mut self,
95115
msg_buf: &mut [u8],
96-
) -> Result<Option<usize>, MsgHandlerError> {
116+
) -> Result<InitiatorAction, MsgHandlerError> {
117+
if self.fd_ctx.should_stop_initiator_mode() {
118+
return Ok(InitiatorAction::Complete);
119+
}
97120
let payload = construct_mctp_pldm_msg(msg_buf).map_err(MsgHandlerError::Util)?;
98121
let pldm_len = self.fd_ctx.fd_progress(payload)?;
99-
Ok((pldm_len > 0).then_some(pldm_len))
122+
Ok(if pldm_len > 0 {
123+
InitiatorAction::Request(pldm_len + PLDM_MSG_OFFSET)
124+
} else {
125+
InitiatorAction::Waiting
126+
})
100127
}
101128

102129
/// Process a received FD-initiated PLDM response from `msg_buf`.

pldm-interface/src/firmware_device/fd_context.rs

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,9 @@ impl<'a, O: FdOps> FirmwareDeviceContext<'a, O> {
868868

869869
fn fd_progress_download(&mut self, payload: &mut [u8]) -> Result<usize, MsgHandlerError> {
870870
if !self.should_send_fd_request() {
871-
return Err(MsgHandlerError::FdInitiatorModeError);
871+
// A request is in flight (or nothing is due yet): not an
872+
// error, the caller polls again.
873+
return Ok(0);
872874
}
873875

874876
let instance_id = self.internal.alloc_next_instance_id().unwrap();
@@ -941,7 +943,9 @@ impl<'a, O: FdOps> FirmwareDeviceContext<'a, O> {
941943

942944
fn pldm_fd_progress_verify(&mut self, _payload: &mut [u8]) -> Result<usize, MsgHandlerError> {
943945
if !self.should_send_fd_request() {
944-
return Err(MsgHandlerError::FdInitiatorModeError);
946+
// A request is in flight (or nothing is due yet): not an
947+
// error, the caller polls again.
948+
return Ok(0);
945949
}
946950

947951
let mut res = VerifyResult::default();
@@ -985,7 +989,9 @@ impl<'a, O: FdOps> FirmwareDeviceContext<'a, O> {
985989

986990
fn pldm_fd_progress_apply(&mut self, _payload: &mut [u8]) -> Result<usize, MsgHandlerError> {
987991
if !self.should_send_fd_request() {
988-
return Err(MsgHandlerError::FdInitiatorModeError);
992+
// A request is in flight (or nothing is due yet): not an
993+
// error, the caller polls again.
994+
return Ok(0);
989995
}
990996

991997
let mut res = ApplyResult::default();
@@ -1596,4 +1602,56 @@ mod tests {
15961602
Some(GetStatusReasonCode::DownloadTimeout)
15971603
);
15981604
}
1605+
1606+
// A request in flight with T2 not yet elapsed is the normal polling
1607+
// state: Ok(0), not FdInitiatorModeError.
1608+
#[test]
1609+
fn test_fd_progress_waiting_is_not_an_error() {
1610+
let mut fd_ctx = new_test_fd_ctx();
1611+
let mut buffer = [0u8; 256];
1612+
let now = fd_ctx.ops.now();
1613+
1614+
fd_ctx.internal.set_fd_state(FirmwareDeviceState::Download);
1615+
fd_ctx.internal.set_fd_req(
1616+
FdReqState::Sent,
1617+
true,
1618+
Some(TransferResult::TransferSuccess as u8),
1619+
Some(0),
1620+
Some(FwUpdateCmd::TransferComplete as u8),
1621+
Some(now),
1622+
);
1623+
fd_ctx.internal.set_fd_t1_update_ts(now);
1624+
1625+
assert!(matches!(fd_ctx.fd_progress(&mut buffer), Ok(0)));
1626+
assert_eq!(
1627+
fd_ctx.internal.get_fd_state(),
1628+
FirmwareDeviceState::Download
1629+
);
1630+
}
1631+
1632+
// T1 must fire in the waiting state too, not only on the T2 resend
1633+
// path: request in flight, T2 not elapsed, but the last UA response
1634+
// is older than T1.
1635+
#[test]
1636+
fn test_fd_progress_t1_fires_while_waiting() {
1637+
let mut fd_ctx = new_test_fd_ctx();
1638+
let mut buffer = [0u8; 256];
1639+
let now = fd_ctx.ops.now();
1640+
1641+
fd_ctx.internal.set_fd_state(FirmwareDeviceState::Download);
1642+
fd_ctx.internal.set_fd_req(
1643+
FdReqState::Sent,
1644+
true,
1645+
Some(TransferResult::TransferSuccess as u8),
1646+
Some(0),
1647+
Some(FwUpdateCmd::TransferComplete as u8),
1648+
Some(now),
1649+
);
1650+
fd_ctx.internal.set_fd_t1_update_ts(0);
1651+
1652+
let result = fd_ctx.fd_progress(&mut buffer);
1653+
1654+
assert!(matches!(result, Err(MsgHandlerError::T1Timeout)));
1655+
assert_eq!(fd_ctx.internal.get_fd_state(), FirmwareDeviceState::Idle);
1656+
}
15991657
}

0 commit comments

Comments
 (0)