* [PATCH] platform/chrome: cros_ec_proto: Fix deferred response payload handling
@ 2026-08-10 21:43 Rob Barnes
2026-08-13 6:49 ` Tzung-Bi Shih
0 siblings, 1 reply; 2+ messages in thread
From: Rob Barnes @ 2026-08-10 21:43 UTC (permalink / raw)
To: bleung, tzungbi; +Cc: tomhughes, chrome-platform, linux-kernel
From: Rob Barnes <robbarnes@google.com>
Subject: [PATCH] platform/chrome: cros_ec_proto: Fix deferred response
payload handling
When cros_ec_wait_until_complete() succeeds after an
EC_RES_IN_PROGRESS status, it previously returned the 4-byte
transfer size of EC_CMD_GET_COMMS_STATUS. For 0-byte payload
commands (such as EC_CMD_FLASH_ERASE), userspace received 4
bytes instead of 0, causing response size validation errors.
For commands expecting non-zero response payloads, the kernel
exited without issuing EC_CMD_RESEND_RESPONSE, leaving response
buffers uninitialized.
Refactor cros_ec_wait_until_complete() to pass orig_msg, issue
EC_CMD_RESEND_RESPONSE into orig_msg->data when insize > 0, and
return 0 for 0-byte response commands. Save and restore orig_msg
fields (command, outsize, version) around the resend request to
prevent unintended caller struct mutations. Update existing KUnit
tests and add test cases for 4-byte and 16-byte response
payloads.
Signed-off-by: Rob Barnes <robbarnes@google.com>
---
drivers/platform/chrome/cros_ec_proto.c | 25 ++-
drivers/platform/chrome/cros_ec_proto_test.c | 151 +++++++++++++++++-
2 files changed, 168 insertions(+), 8 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_proto.c
b/drivers/platform/chrome/cros_ec_proto.c
index a655322b599e..45d6e9bce746 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -147,7 +147,7 @@ static int prepare_tx_legacy(struct cros_ec_device *ec_dev,
return EC_MSG_TX_PROTO_BYTES + msg->outsize;
}
-static int cros_ec_wait_until_complete(struct cros_ec_device *ec_dev,
uint32_t *result)
+static int cros_ec_wait_until_complete(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *orig_msg)
{
struct {
struct cros_ec_command msg;
@@ -162,7 +162,7 @@ static int cros_ec_wait_until_complete(struct
cros_ec_device *ec_dev, uint32_t *
if (ret < 0)
return ret;
- *result = msg->result;
+ orig_msg->result = msg->result;
if (msg->result != EC_RES_SUCCESS)
return ret;
@@ -170,8 +170,28 @@ static int cros_ec_wait_until_complete(struct
cros_ec_device *ec_dev, uint32_t *
break;
}
- if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
- return ret;
+ if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) {
+ /*
+ * If original command requested response payload, retrieve it via
+ * EC_CMD_RESEND_RESPONSE into orig_msg->data. Otherwise return 0
+ * for zero-payload commands.
+ */
+ if (orig_msg->insize > 0) {
+ uint32_t orig_cmd = orig_msg->command;
+ uint32_t orig_outsize = orig_msg->outsize;
+ uint32_t orig_version = orig_msg->version;
+
+ orig_msg->command = EC_CMD_RESEND_RESPONSE;
+ orig_msg->outsize = 0;
+ orig_msg->version = 0;
+ ret = cros_ec_xfer_command(ec_dev, orig_msg);
+ orig_msg->command = orig_cmd;
+ orig_msg->outsize = orig_outsize;
+ orig_msg->version = orig_version;
+ return ret;
+ }
+ return 0;
+ }
}
if (i >= EC_COMMAND_RETRIES)
@@ -185,7 +205,7 @@ static int cros_ec_send_command(struct
cros_ec_device *ec_dev, struct cros_ec_co
int ret = cros_ec_xfer_command(ec_dev, msg);
if (msg->result == EC_RES_IN_PROGRESS)
- ret = cros_ec_wait_until_complete(ec_dev, &msg->result);
+ ret = cros_ec_wait_until_complete(ec_dev, msg);
return ret;
}
diff --git a/drivers/platform/chrome/cros_ec_proto_test.c
b/drivers/platform/chrome/cros_ec_proto_test.c
index 63e38671e95a..609ac8c5108f 100644
--- a/drivers/platform/chrome/cros_ec_proto_test.c
+++ b/drivers/platform/chrome/cros_ec_proto_test.c
@@ -1756,7 +1756,7 @@ static void
cros_ec_proto_test_cmd_xfer_protocol_v2_no_op(struct kunit *test)
KUNIT_EXPECT_EQ(test, ret, -EIO);
}
-static void cros_ec_proto_test_cmd_xfer_in_progress_normal(struct kunit *test)
+static void cros_ec_proto_test_cmd_xfer_in_progress_payload_0bytes(struct
kunit *test)
{
struct cros_ec_proto_test_priv *priv = test->priv;
struct cros_ec_device *ec_dev = &priv->ec_dev;
@@ -1786,7 +1786,7 @@ static void
cros_ec_proto_test_cmd_xfer_in_progress_normal(struct kunit *test)
}
ret = cros_ec_cmd_xfer(ec_dev, &msg);
- KUNIT_EXPECT_EQ(test, ret, sizeof(struct ec_response_get_comms_status));
+ KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, msg.result, EC_RES_SUCCESS);
@@ -1811,6 +1811,149 @@ static void
cros_ec_proto_test_cmd_xfer_in_progress_normal(struct kunit *test)
KUNIT_EXPECT_EQ(test, cros_kunit_ec_pkt_xfer_mock_called, 2);
}
+static void cros_ec_proto_test_cmd_xfer_in_progress_payload_4bytes(struct
kunit *test)
+{
+ struct cros_ec_proto_test_priv *priv = test->priv;
+ struct cros_ec_device *ec_dev = &priv->ec_dev;
+ struct ec_xfer_mock *mock;
+ u8 resp_data[4] = {0x11, 0x22, 0x33, 0x44};
+ struct {
+ struct cros_ec_command msg;
+ u8 data[4];
+ } buf;
+ struct cros_ec_command *msg = &buf.msg;
+ int ret;
+
+ memset(&buf, 0, sizeof(buf));
+ msg->version = 1;
+ msg->command = 0x1234;
+ msg->outsize = 2;
+ msg->insize = sizeof(buf.data);
+
+ ec_dev->pkt_xfer = cros_kunit_ec_pkt_xfer_mock;
+
+ /* For initial command returning EC_RES_IN_PROGRESS. */
+ {
+ mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_IN_PROGRESS, 0);
+ KUNIT_ASSERT_PTR_NE(test, mock, NULL);
+ }
+
+ /* For EC_CMD_GET_COMMS_STATUS status polling. */
+ {
+ struct ec_response_get_comms_status *data;
+
+ mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data));
+ KUNIT_ASSERT_PTR_NE(test, mock, NULL);
+
+ data = (struct ec_response_get_comms_status *)mock->o_data;
+ data->flags = 0;
+ }
+
+ /* For EC_CMD_RESEND_RESPONSE returning 4 bytes payload. */
+ {
+ mock = cros_kunit_ec_xfer_mock_add(test, sizeof(resp_data));
+ KUNIT_ASSERT_PTR_NE(test, mock, NULL);
+ memcpy(mock->o_data, resp_data, sizeof(resp_data));
+ }
+
+ ret = cros_ec_cmd_xfer(ec_dev, msg);
+ KUNIT_EXPECT_EQ(test, ret, 4);
+ KUNIT_EXPECT_EQ(test, msg->result, EC_RES_SUCCESS);
+ KUNIT_EXPECT_EQ(test, msg->command, 0x1234);
+ KUNIT_EXPECT_EQ(test, msg->outsize, 2);
+ KUNIT_EXPECT_EQ(test, msg->version, 1);
+ KUNIT_EXPECT_EQ(test, memcmp(msg->data, resp_data, sizeof(resp_data)), 0);
+
+ /* Verify mock sequence */
+ {
+ mock = cros_kunit_ec_xfer_mock_next();
+ KUNIT_EXPECT_PTR_NE(test, mock, NULL);
+
+ mock = cros_kunit_ec_xfer_mock_next();
+ KUNIT_EXPECT_PTR_NE(test, mock, NULL);
+ KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_COMMS_STATUS);
+
+ mock = cros_kunit_ec_xfer_mock_next();
+ KUNIT_EXPECT_PTR_NE(test, mock, NULL);
+ KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_RESEND_RESPONSE);
+ KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0);
+ KUNIT_EXPECT_EQ(test, mock->msg.version, 0);
+ KUNIT_EXPECT_EQ(test, mock->msg.insize, 4);
+ }
+
+ KUNIT_EXPECT_EQ(test, cros_kunit_ec_pkt_xfer_mock_called, 3);
+}
+
+static void cros_ec_proto_test_cmd_xfer_in_progress_payload_gt4bytes(struct
kunit *test)
+{
+ struct cros_ec_proto_test_priv *priv = test->priv;
+ struct cros_ec_device *ec_dev = &priv->ec_dev;
+ struct ec_xfer_mock *mock;
+ u8 resp_data[16];
+ struct {
+ struct cros_ec_command msg;
+ u8 data[16];
+ } buf;
+ struct cros_ec_command *msg = &buf.msg;
+ int ret, i;
+
+ for (i = 0; i < sizeof(resp_data); ++i)
+ resp_data[i] = (u8)(i + 1);
+
+ memset(&buf, 0, sizeof(buf));
+ msg->version = 0;
+ msg->command = 0x5678;
+ msg->insize = sizeof(buf.data);
+
+ ec_dev->pkt_xfer = cros_kunit_ec_pkt_xfer_mock;
+
+ /* For initial command returning EC_RES_IN_PROGRESS. */
+ {
+ mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_IN_PROGRESS, 0);
+ KUNIT_ASSERT_PTR_NE(test, mock, NULL);
+ }
+
+ /* For EC_CMD_GET_COMMS_STATUS status polling. */
+ {
+ struct ec_response_get_comms_status *data;
+
+ mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data));
+ KUNIT_ASSERT_PTR_NE(test, mock, NULL);
+
+ data = (struct ec_response_get_comms_status *)mock->o_data;
+ data->flags = 0;
+ }
+
+ /* For EC_CMD_RESEND_RESPONSE returning 16 bytes payload. */
+ {
+ mock = cros_kunit_ec_xfer_mock_add(test, sizeof(resp_data));
+ KUNIT_ASSERT_PTR_NE(test, mock, NULL);
+ memcpy(mock->o_data, resp_data, sizeof(resp_data));
+ }
+
+ ret = cros_ec_cmd_xfer(ec_dev, msg);
+ KUNIT_EXPECT_EQ(test, ret, 16);
+ KUNIT_EXPECT_EQ(test, msg->result, EC_RES_SUCCESS);
+ KUNIT_EXPECT_EQ(test, memcmp(msg->data, resp_data, sizeof(resp_data)), 0);
+
+ /* Verify mock sequence */
+ {
+ mock = cros_kunit_ec_xfer_mock_next();
+ KUNIT_EXPECT_PTR_NE(test, mock, NULL);
+
+ mock = cros_kunit_ec_xfer_mock_next();
+ KUNIT_EXPECT_PTR_NE(test, mock, NULL);
+ KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_COMMS_STATUS);
+
+ mock = cros_kunit_ec_xfer_mock_next();
+ KUNIT_EXPECT_PTR_NE(test, mock, NULL);
+ KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_RESEND_RESPONSE);
+ KUNIT_EXPECT_EQ(test, mock->msg.insize, 16);
+ }
+
+ KUNIT_EXPECT_EQ(test, cros_kunit_ec_pkt_xfer_mock_called, 3);
+}
+
static void cros_ec_proto_test_cmd_xfer_in_progress_retries_eagain(struct
kunit *test)
{
struct cros_ec_proto_test_priv *priv = test->priv;
@@ -2715,7 +2858,9 @@ static struct kunit_case cros_ec_proto_test_cases[] = {
KUNIT_CASE(cros_ec_proto_test_cmd_xfer_protocol_v3_no_op),
KUNIT_CASE(cros_ec_proto_test_cmd_xfer_protocol_v2_normal),
KUNIT_CASE(cros_ec_proto_test_cmd_xfer_protocol_v2_no_op),
- KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_normal),
+ KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_payload_0bytes),
+ KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_payload_4bytes),
+ KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_payload_gt4bytes),
KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_retries_eagain),
KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_retries_status_processing),
KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_xfer_error),
--
2.45.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] platform/chrome: cros_ec_proto: Fix deferred response payload handling
2026-08-10 21:43 [PATCH] platform/chrome: cros_ec_proto: Fix deferred response payload handling Rob Barnes
@ 2026-08-13 6:49 ` Tzung-Bi Shih
0 siblings, 0 replies; 2+ messages in thread
From: Tzung-Bi Shih @ 2026-08-13 6:49 UTC (permalink / raw)
To: Rob Barnes; +Cc: bleung, tomhughes, chrome-platform, linux-kernel
On Mon, Aug 10, 2026 at 02:43:29PM -0700, Rob Barnes wrote:
> Subject: [PATCH] platform/chrome: cros_ec_proto: Fix deferred response
> payload handling
The subject shouldn't be here.
> ... Save and restore orig_msg
> fields (command, outsize, version) around the resend request to
> prevent unintended caller struct mutations. Update existing KUnit
> tests and add test cases for 4-byte and 16-byte response
> payloads.
They are somehow redundant; can be removed.
> -static int cros_ec_wait_until_complete(struct cros_ec_device *ec_dev,
> uint32_t *result)
./scripts/checkpatch.pl --strict:
ERROR: patch seems to be corrupt (line wrapped?)
resulting in the patch can't be applied (accompanying with other corrupts).
> +static int cros_ec_wait_until_complete(struct cros_ec_device *ec_dev,
> + struct cros_ec_command *orig_msg)
./scripts/checkpatch.pl --strict:
CHECK: Alignment should match open parenthesis
> @@ -170,8 +170,28 @@ static int cros_ec_wait_until_complete(struct
> cros_ec_device *ec_dev, uint32_t *
> break;
> }
>
> - if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
> - return ret;
> + if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) {
> + /*
> + * If original command requested response payload, retrieve it via
> + * EC_CMD_RESEND_RESPONSE into orig_msg->data. Otherwise return 0
> + * for zero-payload commands.
> + */
How about exiting earlier if `orig_msg->insize` is 0?
> + if (orig_msg->insize > 0) {
> + uint32_t orig_cmd = orig_msg->command;
> + uint32_t orig_outsize = orig_msg->outsize;
> + uint32_t orig_version = orig_msg->version;
./scripts/checkpatch.pl --strict:
CHECK: Prefer kernel type 'u32' over 'uint32_t'
> diff --git a/drivers/platform/chrome/cros_ec_proto_test.c
...
> -static void cros_ec_proto_test_cmd_xfer_in_progress_normal(struct kunit *test)
> +static void cros_ec_proto_test_cmd_xfer_in_progress_payload_0bytes(struct
^
0byte?
> +static void cros_ec_proto_test_cmd_xfer_in_progress_payload_4bytes(struct
> kunit *test)
> +{
...
> + memset(&buf, 0, sizeof(buf));
> + msg->version = 1;
> + msg->command = 0x1234;
> + msg->outsize = 2;
> + msg->insize = sizeof(buf.data);
...
> + ret = cros_ec_cmd_xfer(ec_dev, msg);
> + KUNIT_EXPECT_EQ(test, ret, 4);
> + KUNIT_EXPECT_EQ(test, msg->result, EC_RES_SUCCESS);
> + KUNIT_EXPECT_EQ(test, msg->command, 0x1234);
> + KUNIT_EXPECT_EQ(test, msg->outsize, 2);
> + KUNIT_EXPECT_EQ(test, msg->version, 1);
If it wants to check the header is immutable, it should also check `insize`.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-13 6:49 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-10 21:43 [PATCH] platform/chrome: cros_ec_proto: Fix deferred response payload handling Rob Barnes
2026-08-13 6:49 ` Tzung-Bi Shih
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox