fw_update: Add encode req & decode resp for query_downstream_devices
Add support for Query Downstream Devices to ask if a endpoint supports
downstream devices.
The code is developed based on the definition of
'QueryDownstreamDevices' in DSP0267_1.1.0. Section 10.3
Change-Id: I5925290de5023eb48f675e736429fe9f257170c8
Signed-off-by: Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index be236be..9a1b2aa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,7 @@
Connection Types
13. pdr: Add decode_numeric_effecter_pdr_data()
14. oem: ibm: Support for the Real SAI entity id
+15. fw_update: Add encode req & decode resp for query_downstream_devices
### Changed
diff --git a/include/libpldm/base.h b/include/libpldm/base.h
index 544ae2a..01a0418 100644
--- a/include/libpldm/base.h
+++ b/include/libpldm/base.h
@@ -114,6 +114,21 @@
#define PLDM_TIMESTAMP104_SIZE 13
+/** @brief Minimum length of response for a optional PLDM command
+ *
+ * For a optional PLDM command, the command handler might not be
+ * implemented in a device's firmware, a response contains only CC
+ * might come in, such as ERROR_UNSUPPORTED_PLDM_CMD.
+ *
+ * The description can be found in DSP0240:
+ * > For an unsupported PLDM command, the ERROR_UNSUPPORTED_PLDM_CMD
+ * > completion code shall be returned unless the responder is in a
+ * > transient state (not ready), in which it cannot process the PLDM
+ * > command. If the responder is in a transient state, it may return
+ * > the ERROR_NOT_READY completion code.
+ */
+#define PLDM_OPTIONAL_COMMAND_RESP_MIN_LEN 1
+
/** @struct pldm_msg_hdr
*
* Structure representing PLDM message header fields
diff --git a/include/libpldm/firmware_update.h b/include/libpldm/firmware_update.h
index ede4bc7..eda6d7c 100644
--- a/include/libpldm/firmware_update.h
+++ b/include/libpldm/firmware_update.h
@@ -17,6 +17,18 @@
#define PLDM_FWUP_COMPONENT_BITMAP_MULTIPLE 8
#define PLDM_FWUP_INVALID_COMPONENT_COMPARISON_TIMESTAMP 0xffffffff
#define PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES 0
+
+/** @brief Length of QueryDownstreamDevices response defined in DSP0267_1.1.0
+ * Table 15 - QueryDownstreamDevices command format.
+ *
+ * 1 byte for completion code
+ * 1 byte for downstream device update supported
+ * 2 bytes for number of downstream devices
+ * 2 bytes for max number of downstream devices
+ * 4 bytes for capabilities
+ */
+#define PLDM_QUERY_DOWNSTREAM_DEVICES_RESP_BYTES 10
+
/** @brief Minimum length of device descriptor, 2 bytes for descriptor type,
* 2 bytes for descriptor length and atleast 1 byte of descriptor data
*/
@@ -35,6 +47,7 @@
enum pldm_firmware_update_commands {
PLDM_QUERY_DEVICE_IDENTIFIERS = 0x01,
PLDM_GET_FIRMWARE_PARAMETERS = 0x02,
+ PLDM_QUERY_DOWNSTREAM_DEVICES = 0x03,
PLDM_REQUEST_UPDATE = 0x10,
PLDM_PASS_COMPONENT_TABLE = 0x13,
PLDM_UPDATE_COMPONENT = 0x14,
@@ -324,6 +337,14 @@
PLDM_FWUP_COMPONENTS_NOT_FUNCTIONING = 1
};
+/** @brief Downstream device update supported in QueryDownstreamDevices response
+ * defined in DSP0267_1.1.0
+*/
+enum pldm_firmware_update_downstream_device_update_supported {
+ PLDM_FWUP_DOWNSTREAM_DEVICE_UPDATE_NOT_SUPPORTED = 0,
+ PLDM_FWUP_DOWNSTREAM_DEVICE_UPDATE_SUPPORTED = 1
+};
+
/** @struct pldm_package_header_information
*
* Structure representing fixed part of package header information
@@ -412,6 +433,20 @@
uint8_t pending_comp_image_set_ver_str_len;
} __attribute__((packed));
+/** @struct pldm_query_downstream_devices_resp
+ *
+ * Structure representing response of QueryDownstreamDevices.
+ * The definition can be found Table 15 - QueryDownstreamDevices command format
+ * in DSP0267_1.1.0
+ */
+struct pldm_query_downstream_devices_resp {
+ uint8_t completion_code;
+ uint8_t downstream_device_update_supported;
+ uint16_t number_of_downstream_devices;
+ uint16_t max_number_of_downstream_devices;
+ bitfield32_t capabilities;
+};
+
/** @struct pldm_component_parameter_entry
*
* Structure representing component parameter table entry.
@@ -738,6 +773,34 @@
struct variable_field *active_comp_ver_str,
struct variable_field *pending_comp_ver_str);
+/** @brief Create a PLDM request message for QueryDownstreamDevices
+ *
+ * @param[in] instance_id - Message's instance id
+ * @param[out] msg - Message will be written to this
+ *
+ * @return pldm_completion_codes
+ *
+ * @note Caller is responsible for memory alloc and dealloc of param
+ * 'msg.payload'
+ */
+int encode_query_downstream_devices_req(uint8_t instance_id,
+ struct pldm_msg *msg);
+
+/**
+ * @brief Decodes the response message for Querying Downstream Devices.
+ *
+ * @param[in] msg The PLDM message to decode.
+ * @param[in] payload_length The length of the message payload.
+ * @param[out] resp_data Pointer to the structure to store the decoded response data.
+ * @return pldm_completion_codes
+ *
+ * @note Caller is responsible for memory alloc and dealloc of param
+ * 'msg.payload'
+ */
+int decode_query_downstream_devices_resp(
+ const struct pldm_msg *msg, size_t payload_length,
+ struct pldm_query_downstream_devices_resp *resp_data);
+
/** @brief Create PLDM request message for RequestUpdate
*
* @param[in] instance_id - Message's instance id
diff --git a/src/firmware_update.c b/src/firmware_update.c
index 0788d4b..ed1816d 100644
--- a/src/firmware_update.c
+++ b/src/firmware_update.c
@@ -1,4 +1,5 @@
/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
+#include "msgbuf.h"
#include <libpldm/firmware_update.h>
#include <libpldm/utils.h>
@@ -74,6 +75,17 @@
}
}
+static bool is_downstream_device_update_support_valid(uint8_t resp)
+{
+ switch (resp) {
+ case PLDM_FWUP_DOWNSTREAM_DEVICE_UPDATE_NOT_SUPPORTED:
+ case PLDM_FWUP_DOWNSTREAM_DEVICE_UPDATE_SUPPORTED:
+ return true;
+ default:
+ return false;
+ }
+}
+
/** @brief Check whether ComponentResponse is valid
*
* @return true if ComponentResponse is valid, false if not
@@ -854,6 +866,68 @@
return PLDM_SUCCESS;
}
+LIBPLDM_ABI_TESTING
+int encode_query_downstream_devices_req(uint8_t instance_id,
+ struct pldm_msg *msg)
+{
+ if (msg == NULL) {
+ return PLDM_ERROR_INVALID_DATA;
+ }
+
+ return encode_pldm_header_only(PLDM_REQUEST, instance_id, PLDM_FWUP,
+ PLDM_QUERY_DOWNSTREAM_DEVICES, msg);
+}
+
+LIBPLDM_ABI_TESTING
+int decode_query_downstream_devices_resp(
+ const struct pldm_msg *msg, size_t payload_length,
+ struct pldm_query_downstream_devices_resp *resp_data)
+{
+ struct pldm_msgbuf _buf;
+ struct pldm_msgbuf *buf = &_buf;
+ int rc;
+
+ if (msg == NULL || resp_data == NULL || !payload_length) {
+ return PLDM_ERROR_INVALID_DATA;
+ }
+
+ rc = pldm_msgbuf_init(buf, PLDM_OPTIONAL_COMMAND_RESP_MIN_LEN,
+ msg->payload, payload_length);
+ if (rc) {
+ return rc;
+ }
+
+ rc = pldm_msgbuf_extract(buf, resp_data->completion_code);
+ if (rc) {
+ return rc;
+ }
+ if (PLDM_SUCCESS != resp_data->completion_code) {
+ // Return the CC directly without decoding the rest of the payload
+ return PLDM_SUCCESS;
+ }
+
+ if (payload_length < PLDM_QUERY_DOWNSTREAM_DEVICES_RESP_BYTES) {
+ return PLDM_ERROR_INVALID_LENGTH;
+ }
+
+ rc = pldm_msgbuf_extract(buf,
+ resp_data->downstream_device_update_supported);
+ if (rc) {
+ return rc;
+ }
+
+ if (!is_downstream_device_update_support_valid(
+ resp_data->downstream_device_update_supported)) {
+ return PLDM_ERROR_INVALID_DATA;
+ }
+
+ pldm_msgbuf_extract(buf, resp_data->number_of_downstream_devices);
+ pldm_msgbuf_extract(buf, resp_data->max_number_of_downstream_devices);
+ pldm_msgbuf_extract(buf, resp_data->capabilities.value);
+
+ return pldm_msgbuf_destroy_consumed(buf);
+}
+
LIBPLDM_ABI_STABLE
int encode_request_update_req(uint8_t instance_id, uint32_t max_transfer_size,
uint16_t num_of_comp,
diff --git a/tests/libpldm_firmware_update_test.cpp b/tests/libpldm_firmware_update_test.cpp
index 52e867a..3870fee 100644
--- a/tests/libpldm_firmware_update_test.cpp
+++ b/tests/libpldm_firmware_update_test.cpp
@@ -13,6 +13,8 @@
#include <string_view>
#include <vector>
+#include "msgbuf.h"
+
#include <gtest/gtest.h>
constexpr auto hdrSize = sizeof(pldm_msg_hdr);
@@ -1276,6 +1278,153 @@
outPendingCompVerStr.length));
}
+TEST(QueryDownstreamDevices, goodPathEncodeRequest)
+{
+ constexpr uint8_t instanceId = 1;
+ std::array<uint8_t, sizeof(pldm_msg_hdr)> requestMsg{};
+ auto requestPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+ auto rc = encode_query_downstream_devices_req(instanceId, requestPtr);
+
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+ EXPECT_EQ(requestPtr->hdr.request, PLDM_REQUEST);
+ EXPECT_EQ(requestPtr->hdr.instance_id, instanceId);
+ EXPECT_EQ(requestPtr->hdr.type, PLDM_FWUP);
+ EXPECT_EQ(requestPtr->hdr.command, PLDM_QUERY_DOWNSTREAM_DEVICES);
+}
+
+TEST(QueryDownstreamDevices, encodeRequestInvalidData)
+{
+ constexpr uint8_t instanceId = 1;
+
+ auto rc = encode_query_downstream_devices_req(instanceId, nullptr);
+
+ EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(QueryDownstreamDevices, goodPathDecodeResponse)
+{
+ uint8_t completion_code_resp = PLDM_SUCCESS;
+ uint8_t downstream_device_update_supported_resp =
+ PLDM_FWUP_DOWNSTREAM_DEVICE_UPDATE_SUPPORTED;
+ uint16_t number_of_downstream_devices_resp = 1;
+ uint16_t max_number_of_downstream_devices_resp = 1;
+ /** Capabilities of updating downstream devices
+ * FDP supports downstream devices dynamically attached [Bit position 0] &
+ * FDP supports downstream devices dynamically removed [Bit position 1]
+ */
+ bitfield32_t capabilities_resp = {.value = 0x0002};
+ int rc;
+
+ std::array<uint8_t, hdrSize + PLDM_QUERY_DOWNSTREAM_DEVICES_RESP_BYTES>
+ responseMsg{};
+
+ struct pldm_msgbuf _buf;
+ struct pldm_msgbuf* buf = &_buf;
+ rc = pldm_msgbuf_init(buf, 0, responseMsg.data() + hdrSize,
+ responseMsg.size() - hdrSize);
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ pldm_msgbuf_insert_uint8(buf, completion_code_resp);
+ pldm_msgbuf_insert_uint8(buf, downstream_device_update_supported_resp);
+ pldm_msgbuf_insert_uint16(buf, number_of_downstream_devices_resp);
+ pldm_msgbuf_insert_uint16(buf, max_number_of_downstream_devices_resp);
+ pldm_msgbuf_insert_uint32(buf, capabilities_resp.value);
+
+ auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+ struct pldm_query_downstream_devices_resp resp_data;
+
+ rc = decode_query_downstream_devices_resp(
+ response, responseMsg.size() - hdrSize, &resp_data);
+
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+ EXPECT_EQ(resp_data.completion_code, completion_code_resp);
+ EXPECT_EQ(resp_data.downstream_device_update_supported,
+ downstream_device_update_supported_resp);
+ EXPECT_EQ(resp_data.number_of_downstream_devices,
+ number_of_downstream_devices_resp);
+ EXPECT_EQ(resp_data.max_number_of_downstream_devices,
+ max_number_of_downstream_devices_resp);
+ EXPECT_EQ(resp_data.capabilities.value, capabilities_resp.value);
+}
+
+TEST(QueryDownstreamDevices, decodeRequestUndefinedValue)
+{
+ uint8_t completion_code_resp = PLDM_SUCCESS;
+ uint8_t downstream_device_update_supported_resp = 0xe; /*Undefined value*/
+ uint16_t number_of_downstream_devices_resp = 1;
+ uint16_t max_number_of_downstream_devices_resp = 1;
+ /** Capabilities of updating downstream devices
+ * FDP supports downstream devices dynamically attached [Bit position 0] &
+ * FDP supports downstream devices dynamically removed [Bit position 1]
+ */
+ bitfield32_t capabilities_resp = {.value = 0x0002};
+ int rc;
+
+ std::array<uint8_t, hdrSize + PLDM_QUERY_DOWNSTREAM_DEVICES_RESP_BYTES>
+ responseMsg{};
+
+ struct pldm_msgbuf _buf;
+ struct pldm_msgbuf* buf = &_buf;
+ rc = pldm_msgbuf_init(buf, 0, responseMsg.data() + hdrSize,
+ responseMsg.size() - hdrSize);
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ pldm_msgbuf_insert_uint8(buf, completion_code_resp);
+ pldm_msgbuf_insert_uint8(buf, downstream_device_update_supported_resp);
+ pldm_msgbuf_insert_uint16(buf, number_of_downstream_devices_resp);
+ pldm_msgbuf_insert_uint16(buf, max_number_of_downstream_devices_resp);
+ pldm_msgbuf_insert_uint32(buf, capabilities_resp.value);
+
+ auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+ struct pldm_query_downstream_devices_resp resp_data;
+
+ rc = decode_query_downstream_devices_resp(
+ response, responseMsg.size() - hdrSize, &resp_data);
+
+ ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(QueryDownstreamDevices, decodeRequestErrorBufSize)
+{
+ uint8_t completion_code_resp = PLDM_SUCCESS;
+ uint8_t downstream_device_update_supported_resp =
+ PLDM_FWUP_DOWNSTREAM_DEVICE_UPDATE_SUPPORTED;
+ uint16_t number_of_downstream_devices_resp = 1;
+ uint16_t max_number_of_downstream_devices_resp = 1;
+ /** Capabilities of updating downstream devices
+ * FDP supports downstream devices dynamically attached [Bit position 0] &
+ * FDP supports downstream devices dynamically removed [Bit position 1]
+ */
+ bitfield32_t capabilities_resp = {.value = 0x0002};
+ int rc;
+
+ std::array<uint8_t, hdrSize + PLDM_QUERY_DOWNSTREAM_DEVICES_RESP_BYTES -
+ 2 /* Inject error length*/>
+ responseMsg{};
+
+ struct pldm_msgbuf _buf;
+ struct pldm_msgbuf* buf = &_buf;
+ rc = pldm_msgbuf_init(buf, 0, responseMsg.data() + hdrSize,
+ responseMsg.size() - hdrSize);
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ pldm_msgbuf_insert_uint8(buf, completion_code_resp);
+ pldm_msgbuf_insert_uint8(buf, downstream_device_update_supported_resp);
+ pldm_msgbuf_insert_uint16(buf, number_of_downstream_devices_resp);
+ pldm_msgbuf_insert_uint16(buf, max_number_of_downstream_devices_resp);
+ // Inject error value
+ pldm_msgbuf_insert_uint16(buf, (uint16_t)capabilities_resp.value);
+
+ auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+ struct pldm_query_downstream_devices_resp resp_data;
+
+ rc = decode_query_downstream_devices_resp(
+ response, responseMsg.size() - hdrSize, &resp_data);
+
+ EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
+}
+
TEST(RequestUpdate, goodPathEncodeRequest)
{
constexpr uint8_t instanceId = 1;