fw_update: Add encode req & decode resp for get_downstream_fw_params

Add support for Get Downstream Firmware Parameters to ask all
downstream devices' Firmware Parameters.

The code is developed based on the definition of
'GetDownstreamFirmwareParameters' in DSP0267_1.1.0. Section 10.5

Change-Id: I291ca3b623be6119434b70494bb9a12b22f600b9
Signed-off-by: Chris Wang <chris.wang.wiwynn@gmail.com>
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7f88a0..ae750a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@
 4. platform: Define macros for the responded transferflags
 5. pdr: Add pldm_pdr_get_terminus_handle() API
 6. pdr: Add related decode_entity_auxiliary_names_pdr() APIs
+7. fw_update: Add encode req & decode resp for get_downstream_fw_params
 
 ### Deprecated
 
diff --git a/include/libpldm/firmware_update.h b/include/libpldm/firmware_update.h
index b59e777..73dabe3 100644
--- a/include/libpldm/firmware_update.h
+++ b/include/libpldm/firmware_update.h
@@ -51,7 +51,43 @@
 /** @brief Minimum length of device descriptor, 2 bytes for descriptor type,
  *         2 bytes for descriptor length and at least 1 byte of descriptor data
  */
-#define PLDM_FWUP_DEVICE_DESCRIPTOR_MIN_LEN    5
+#define PLDM_FWUP_DEVICE_DESCRIPTOR_MIN_LEN 5
+
+/** @brief Length of GetDownstreamFirmwareParameters request defined in DSP0267_1.1.0
+ *
+ * 4 bytes for Data Transfer Handle
+ * 1 byte for Transfer Operation Flag
+ */
+#define PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_REQ_BYTES 5
+
+/** @brief Minimum length of GetDownstreamFirmwareParameters response from
+ * DSP0267_1.1.0 if the completion code is success.
+ *
+ * 1 byte for completion code
+ * 4 bytes for next data transfer handle
+ * 1 byte for transfer flag
+ * 4 bytes for FDP capabilities during update
+ * 2 bytes for downstream device count
+ */
+#define PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_RESP_MIN_LEN 12
+
+/** @brief Minimum length of DownstreamDeviceParameterTable entry from
+ * DSP0267_1.1.0 table 21 - DownstreamDeviceParameterTable
+ *
+ * 2 bytes for Downstream Device Index
+ * 4 bytes for Active Component Comparison Stamp
+ * 1 byte for Active Component Version String Type
+ * 1 byte for Active Component Version String Length
+ * 8 bytes for Active Component Release Date
+ * 4 bytes for Pending Component Comparison Stamp
+ * 1 byte for Pending Component Version String Type
+ * 1 byte for Pending Component Version String Length
+ * 8 bytes for Pending Component Release Date
+ * 2 bytes for Component Activation Methods
+ * 4 bytes for Capabilities During Update
+ */
+#define PLDM_DOWNSTREAM_DEVICE_PARAMETER_ENTRY_MIN_LEN 36
+
 #define PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES 0
 #define PLDM_FWUP_BASELINE_TRANSFER_SIZE       32
 #define PLDM_FWUP_MIN_OUTSTANDING_REQ	       1
@@ -61,6 +97,14 @@
 #define PLDM_CANCEL_UPDATE_COMPONENT_REQ_BYTES 0
 #define PLDM_CANCEL_UPDATE_REQ_BYTES	       0
 
+/** @brief PLDM component release data size in bytes defined in DSP0267_1.1.0
+ * Table 14 - ComponentParameterTable and Table 21 - ComponentParameterTable
+ *
+ * The size can be used in `ASCII[8] - ActiveComponentReleaseDate` and
+ * `ASCII[8] - PendingComponentReleaseDate` fields in the tables above.
+ */
+#define PLDM_FWUP_COMPONENT_RELEASE_DATA_LEN 8
+
 /** @brief PLDM Firmware update commands
  */
 enum pldm_firmware_update_commands {
@@ -68,6 +112,7 @@
 	PLDM_GET_FIRMWARE_PARAMETERS = 0x02,
 	PLDM_QUERY_DOWNSTREAM_DEVICES = 0x03,
 	PLDM_QUERY_DOWNSTREAM_IDENTIFIERS = 0x04,
+	PLDM_QUERY_DOWNSTREAM_FIRMWARE_PARAMETERS = 0x05,
 	PLDM_REQUEST_UPDATE = 0x10,
 	PLDM_PASS_COMPONENT_TABLE = 0x13,
 	PLDM_UPDATE_COMPONENT = 0x14,
@@ -524,6 +569,85 @@
 	uint8_t downstream_descriptor_count;
 };
 
+/** @struct pldm_query_downstream_firmware_param_req
+ *
+ *  Structure representing QueryDownstreamFirmwareParameters request
+ */
+struct pldm_get_downstream_firmware_params_req {
+	uint32_t data_transfer_handle;
+	uint8_t transfer_operation_flag;
+};
+
+/** @struct pldm_query_downstream_firmware_param_resp
+ *
+ *  Structure representing the fixed part of QueryDownstreamFirmwareParameters
+ *  response in Table 19 - GetDownstreamFirmwareParameters command format, and
+ *  Table 20 - QueryDownstreamFirmwareParameters response definition in
+ *  DSP0267_1.1.0.
+ *
+ *  Squash the two tables into one since the definition of Table 20 is `Portion
+ *  of GetDownstreamFirmwareParameters response`
+ */
+struct pldm_get_downstream_firmware_params_resp {
+	uint8_t completion_code;
+	uint32_t next_data_transfer_handle;
+	uint8_t transfer_flag;
+	bitfield32_t fdp_capabilities_during_update;
+	uint16_t downstream_device_count;
+};
+
+/** @struct pldm_downstream_device_parameter_entry
+ *
+ *  Structure representing downstream device parameter table entry defined in
+ *  Table 21 - DownstreamDeviceParameterTable in DSP0267_1.1.0
+ *
+ *  Clients should not allocate memory for this struct to decode the response,
+ *  use `pldm_downstream_device_parameter_entry_versions` instead to make sure
+ *  that the active and pending component version strings are copied from the
+ *  message buffer.
+ */
+struct pldm_downstream_device_parameter_entry {
+	uint16_t downstream_device_index;
+	uint32_t active_comp_comparison_stamp;
+	uint8_t active_comp_ver_str_type;
+	uint8_t active_comp_ver_str_len;
+	/* Append 1 bytes for null termination so that it can be used as a
+	 * Null-terminated string.
+	 */
+	char active_comp_release_date[PLDM_FWUP_COMPONENT_RELEASE_DATA_LEN + 1];
+	uint32_t pending_comp_comparison_stamp;
+	uint8_t pending_comp_ver_str_type;
+	uint8_t pending_comp_ver_str_len;
+	/* Append 1 bytes for null termination so that it can be used as a
+	 * Null-terminated string.
+	 */
+	char pending_comp_release_date[PLDM_FWUP_COMPONENT_RELEASE_DATA_LEN + 1];
+	bitfield16_t comp_activation_methods;
+	bitfield32_t capabilities_during_update;
+	const char *active_comp_ver_str;
+	const char *pending_comp_ver_str;
+};
+
+/** @struct pldm_downstream_device_parameter_entry_versions
+ *
+ *  Structure representing downstream device parameter table entry with
+ *  copies of active and pending component version strings to avoid the
+ *  message buffer is subsequently freed.
+ *
+ *  Clients should allocate memory for this struct then decode the response
+ *  instead of using `pldm_downstream_device_parameter_entry`.
+ */
+struct pldm_downstream_device_parameter_entry_versions {
+	struct pldm_downstream_device_parameter_entry entry;
+	/* The "Length of ComponentVersionString" field is 1 byte, so
+	 * "ComponentVersionString" can be at most 255 characters, allocate
+	 * memory for it and append 1 bytes for null termination so that it
+	 * can be used as a Null-terminated string.
+	 */
+	char active_comp_ver_str[UINT8_MAX + 1];
+	char pending_comp_ver_str[UINT8_MAX + 1];
+};
+
 /** @struct pldm_request_update_req
  *
  *  Structure representing fixed part of Request Update request
@@ -891,6 +1015,97 @@
 	struct pldm_query_downstream_identifiers_resp *resp_data,
 	struct variable_field *downstream_devices);
 
+/**
+ * @brief Encodes request message for Get Downstream Firmware Parameters.
+ *
+ * @param[in] instance_id - The instance ID of the PLDM entity.
+ * @param[in] data_transfer_handle - The handle for the data transfer.
+ * @param[in] transfer_operation_flag - The flag indicating the transfer operation.
+ * @param[in,out] msg - A pointer to the PLDM message structure to store the encoded message.
+ * @param[in] payload_length - The length of the payload.
+ *
+ * @return 0 on success, otherwise -EINVAL if the input parameters' memory
+ *         are not allocated, -EOVERFLOW if the payload length is not enough
+ *         to encode the message, -EBADMSG if the message is not valid.
+ *
+ * @note Caller is responsible for memory alloc and dealloc of param
+ *        'msg.payload'
+ */
+int encode_get_downstream_firmware_params_req(
+	uint8_t instance_id, uint32_t data_transfer_handle,
+	enum transfer_op_flag transfer_operation_flag, struct pldm_msg *msg,
+	size_t payload_length);
+
+/**
+ * @brief Decode response message for Get Downstream Firmware Parameters
+ *
+ * @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
+ * @param[out] downstream_device_param_table - Pointer to the variable field structure
+ *                                           to store the decoded downstream device
+ *                                           parameter table
+ * @return 0 on success, otherwise -EINVAL if the input parameters' memory
+ *         are not allocated, -EOVERFLOW if the payload length is not enough
+ *         to decode the message, -EBADMSG if the message is not valid.
+ *
+ * @note Caller is responsible for memory alloc and dealloc of param
+ *        'resp_data' and 'downstream_device_param_table'
+ */
+int decode_get_downstream_firmware_params_resp(
+	const struct pldm_msg *msg, size_t payload_length,
+	struct pldm_get_downstream_firmware_params_resp *resp_data,
+	struct variable_field *downstream_device_param_table);
+
+/**
+ * @brief Decode the next downstream device parameter table entry
+ *
+ * @param[in,out] data - A variable field covering the table entries in the
+ *                       response message data. @p data is updated to point to
+ *                       the remaining entries once the current entry has been
+ *                       decoded.
+
+ * @param[out] entry - The struct object into which the current table entry will
+ *                     be decoded
+
+ * @param[out] versions - A variable field covering the active and
+ *                        pending component version strings in the
+ *                        response message data. The component version
+ *                        strings can be decoded into @p entry using
+ *                        decode_downstream_device_parameter_table_entry_versions()
+ *
+ * @return 0 on success, otherwise -EINVAL if the input parameters' memory
+ *         are not allocated, -EOVERFLOW if the payload length is not enough
+ *         to decode the entry.
+ *
+ * @note Caller is responsible for memory alloc and dealloc of param
+ * 	  'entry', 'active_comp_ver_str' and 'pending_comp_ver_str'
+ */
+int decode_downstream_device_parameter_table_entry(
+	struct variable_field *data,
+	struct pldm_downstream_device_parameter_entry *entry,
+	struct variable_field *versions);
+
+/**
+ * @brief Decode the downstream device parameter table entry versions
+ *
+ * @param[in] versions - pointer to version strings raw data
+ * @param[in,out] entry - pointer to the decoded downstream device parameter table entry
+ * @param[out] active - pointer to active component version string, the object pointed-to
+ *                      must be at least as large as `entry->active_comp_ver_str_len + 1`
+ * @param[out] pending - pointer to pending component version string, the object pointed-to
+ *                      must be at least as large as `entry->pending_comp_ver_str_len + 1`
+ *
+ * @note Caller is responsible for memory alloc and dealloc of all the params,
+ *    and the param `entry` should be the instance which has successfully decoded
+ *    by `decode_downstream_device_parameter_table_entry()`.
+ *
+ */
+int decode_downstream_device_parameter_table_entry_versions(
+	const struct variable_field *versions,
+	struct pldm_downstream_device_parameter_entry *entry, char *active,
+	char *pending);
+
 /** @brief Create PLDM request message for RequestUpdate
  *
  *  @param[in] instance_id - Message's instance id
diff --git a/src/dsp/firmware_update.c b/src/dsp/firmware_update.c
index 8b4a1ed..80310b2 100644
--- a/src/dsp/firmware_update.c
+++ b/src/dsp/firmware_update.c
@@ -1,4 +1,5 @@
 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
+#include "dsp/base.h"
 #include "msgbuf.h"
 #include <libpldm/firmware_update.h>
 #include <libpldm/utils.h>
@@ -1035,6 +1036,200 @@
 	return pldm_msgbuf_destroy(buf);
 }
 
+LIBPLDM_ABI_TESTING
+int encode_get_downstream_firmware_params_req(
+	uint8_t instance_id, uint32_t data_transfer_handle,
+	enum transfer_op_flag transfer_operation_flag, struct pldm_msg *msg,
+	size_t payload_length)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	int rc;
+
+	if (msg == NULL) {
+		return -EINVAL;
+	}
+
+	rc = pldm_msgbuf_init_errno(
+		buf, PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_REQ_BYTES,
+		msg->payload, payload_length);
+	if (rc < 0) {
+		return rc;
+	}
+
+	if (!is_transfer_operation_flag_valid(transfer_operation_flag)) {
+		return -EBADMSG;
+	}
+
+	struct pldm_header_info header = { 0 };
+	header.instance = instance_id;
+	header.msg_type = PLDM_REQUEST;
+	header.pldm_type = PLDM_FWUP;
+	header.command = PLDM_QUERY_DOWNSTREAM_FIRMWARE_PARAMETERS;
+	rc = pack_pldm_header_errno(&header, &msg->hdr);
+	if (rc < 0) {
+		return rc;
+	}
+
+	pldm_msgbuf_insert(buf, data_transfer_handle);
+	// Data correctness has been verified, cast it to 1-byte data directly.
+	pldm_msgbuf_insert(buf, (uint8_t)transfer_operation_flag);
+
+	return pldm_msgbuf_destroy(buf);
+}
+
+LIBPLDM_ABI_TESTING
+int decode_get_downstream_firmware_params_resp(
+	const struct pldm_msg *msg, size_t payload_length,
+	struct pldm_get_downstream_firmware_params_resp *resp_data,
+	struct variable_field *downstream_device_param_table)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	int rc;
+
+	if (msg == NULL || resp_data == NULL ||
+	    downstream_device_param_table == NULL) {
+		return -EINVAL;
+	}
+
+	rc = pldm_msgbuf_init_errno(buf, PLDM_OPTIONAL_COMMAND_RESP_MIN_LEN,
+				    msg->payload, payload_length);
+	if (rc < 0) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_extract(buf, resp_data->completion_code);
+	if (rc < 0) {
+		return rc;
+	}
+	if (PLDM_SUCCESS != resp_data->completion_code) {
+		return 0;
+	}
+
+	if (payload_length < PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_RESP_MIN_LEN) {
+		return -EBADMSG;
+	}
+
+	pldm_msgbuf_extract(buf, resp_data->next_data_transfer_handle);
+	pldm_msgbuf_extract(buf, resp_data->transfer_flag);
+	pldm_msgbuf_extract(buf,
+			    resp_data->fdp_capabilities_during_update.value);
+	pldm_msgbuf_extract(buf, resp_data->downstream_device_count);
+
+	return pldm_msgbuf_span_remaining(
+		buf, (void **)&downstream_device_param_table->ptr,
+		&downstream_device_param_table->length);
+}
+
+LIBPLDM_ABI_TESTING
+int decode_downstream_device_parameter_table_entry(
+	struct variable_field *data,
+	struct pldm_downstream_device_parameter_entry *entry,
+	struct variable_field *versions)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	void *cursor = NULL;
+	size_t remaining;
+	int rc;
+
+	if (data == NULL || entry == NULL || versions == NULL) {
+		return -EINVAL;
+	}
+
+	rc = pldm_msgbuf_init_errno(
+		buf, PLDM_DOWNSTREAM_DEVICE_PARAMETER_ENTRY_MIN_LEN, data->ptr,
+		data->length);
+	if (rc < 0) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract(buf, entry->downstream_device_index);
+	pldm_msgbuf_extract(buf, entry->active_comp_comparison_stamp);
+	pldm_msgbuf_extract(buf, entry->active_comp_ver_str_type);
+	rc = pldm_msgbuf_extract(buf, entry->active_comp_ver_str_len);
+	if (rc < 0) {
+		return rc;
+	}
+	pldm_msgbuf_extract_array(buf, entry->active_comp_release_date,
+				  PLDM_FWUP_COMPONENT_RELEASE_DATA_LEN);
+	// Fill the last byte with NULL character
+	entry->active_comp_release_date[PLDM_FWUP_COMPONENT_RELEASE_DATA_LEN] =
+		'\0';
+
+	pldm_msgbuf_extract(buf, entry->pending_comp_comparison_stamp);
+	pldm_msgbuf_extract(buf, entry->pending_comp_ver_str_type);
+	rc = pldm_msgbuf_extract(buf, entry->pending_comp_ver_str_len);
+	if (rc < 0) {
+		return rc;
+	}
+	pldm_msgbuf_extract_array(buf, entry->pending_comp_release_date,
+				  PLDM_FWUP_COMPONENT_RELEASE_DATA_LEN);
+	// Fill the last byte with NULL character
+	entry->pending_comp_release_date[PLDM_FWUP_COMPONENT_RELEASE_DATA_LEN] =
+		'\0';
+
+	pldm_msgbuf_extract(buf, entry->comp_activation_methods.value);
+	pldm_msgbuf_extract(buf, entry->capabilities_during_update.value);
+	const size_t versions_len = entry->active_comp_ver_str_len +
+				    entry->pending_comp_ver_str_len;
+	rc = pldm_msgbuf_span_required(buf, versions_len,
+				       (void **)&versions->ptr);
+	if (rc < 0) {
+		return rc;
+	}
+	versions->length = versions_len;
+
+	rc = pldm_msgbuf_span_remaining(buf, &cursor, &remaining);
+	if (rc < 0) {
+		return rc;
+	}
+
+	data->ptr = cursor;
+	data->length = remaining;
+
+	return 0;
+}
+
+LIBPLDM_ABI_TESTING
+int decode_downstream_device_parameter_table_entry_versions(
+	const struct variable_field *versions,
+	struct pldm_downstream_device_parameter_entry *entry, char *active,
+	char *pending)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	int rc;
+
+	if (versions == NULL || versions->ptr == NULL || !versions->length ||
+	    entry == NULL || active == NULL || pending == NULL) {
+		return -EINVAL;
+	}
+
+	/* This API should be called after decode_downstream_device_parameter_table_entry
+	 * has successfully decoded the entry, assume the entry data is valid here.
+	 */
+	const size_t versions_len = entry->active_comp_ver_str_len +
+				    entry->pending_comp_ver_str_len;
+	rc = pldm_msgbuf_init_errno(buf, versions_len, versions->ptr,
+				    versions->length);
+	if (rc < 0) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract_array(buf, active, entry->active_comp_ver_str_len);
+	active[entry->active_comp_ver_str_len] = '\0';
+	pldm_msgbuf_extract_array(buf, pending,
+				  entry->pending_comp_ver_str_len);
+	pending[entry->pending_comp_ver_str_len] = '\0';
+
+	entry->active_comp_ver_str = active;
+	entry->pending_comp_ver_str = pending;
+
+	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/dsp/firmware_update.cpp b/tests/dsp/firmware_update.cpp
index ce494a9..8e96708 100644
--- a/tests/dsp/firmware_update.cpp
+++ b/tests/dsp/firmware_update.cpp
@@ -1684,6 +1684,421 @@
 }
 #endif
 
+#ifdef LIBPLDM_API_TESTING
+TEST(GetDownstreamFirmwareParameters, goodPathEncodeRequest)
+{
+    constexpr uint8_t instanceId = 1;
+    constexpr uint32_t dataTransferHandle = 0x0;
+    constexpr enum transfer_op_flag transferOperationFlag = PLDM_GET_FIRSTPART;
+    constexpr size_t payload_length =
+        PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_REQ_BYTES;
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + payload_length> requestMsg{};
+    auto requestPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    auto rc = encode_get_downstream_firmware_params_req(
+        instanceId, dataTransferHandle, transferOperationFlag, requestPtr,
+        payload_length);
+    EXPECT_EQ(rc, 0);
+
+    std::array<uint8_t, hdrSize + PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_REQ_BYTES>
+        expectedReq{0x81, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01};
+    EXPECT_EQ(requestMsg, expectedReq);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetDownstreamFirmwareParameters, encodeRequestInvalidTransferOperationFlag)
+{
+    constexpr uint8_t instanceId = 1;
+    constexpr uint32_t dataTransferHandle = 0x0;
+    // Setup invalid transfer operation flag
+    constexpr enum transfer_op_flag transferOperationFlag =
+        PLDM_ACKNOWLEDGEMENT_ONLY;
+    constexpr size_t payload_length =
+        PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_REQ_BYTES;
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + payload_length> requestMsg{};
+    auto requestPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    auto rc = encode_get_downstream_firmware_params_req(
+        instanceId, dataTransferHandle, transferOperationFlag, requestPtr,
+        payload_length);
+    EXPECT_EQ(rc, -EBADMSG);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetDownstreamFirmwareParameters, encodeRequestErrorBufSize)
+{
+    constexpr uint8_t instanceId = 1;
+    constexpr uint32_t dataTransferHandle = 0x0;
+    // Setup invalid transfer operation flag
+    constexpr enum transfer_op_flag transferOperationFlag =
+        PLDM_ACKNOWLEDGEMENT_ONLY;
+    constexpr size_t payload_length =
+        PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_REQ_BYTES -
+        1 /* inject erro length*/;
+
+    std::array<uint8_t, sizeof(pldm_msg_hdr) + payload_length> requestMsg{};
+    auto requestPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    auto rc = encode_get_downstream_firmware_params_req(
+        instanceId, dataTransferHandle, transferOperationFlag, requestPtr,
+        payload_length);
+    EXPECT_EQ(rc, -EOVERFLOW);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetDownstreamFirmwareParameters, goodPathDecodeResponse)
+{
+    /** Count is not fixed here taking it as 1, and the downstream device's
+     *  version strings length are set to 8
+     */
+    constexpr uint16_t downstreamDeviceCount = 1;
+    constexpr uint8_t activeComponentVersionStringLength = 8;
+    constexpr uint8_t pendingComponentVersionStringLength = 8;
+    constexpr size_t downstreamDeviceParamTableLen =
+        sizeof(pldm_component_parameter_entry) +
+        activeComponentVersionStringLength +
+        pendingComponentVersionStringLength;
+    constexpr uint8_t complition_code_resp = PLDM_SUCCESS;
+    constexpr uint32_t next_data_transfer_handle_resp = 0x0;
+    constexpr uint8_t transfer_flag_resp = PLDM_START_AND_END;
+    constexpr bitfield32_t fdp_capabilities_during_update = {.value = 0x0002};
+
+    std::array<uint8_t, hdrSize +
+                            PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_RESP_MIN_LEN +
+                            downstreamDeviceParamTableLen>
+        responseMsg{};
+
+    int rc = 0;
+
+    struct pldm_msgbuf _buf;
+    struct pldm_msgbuf* buf = &_buf;
+    rc = pldm_msgbuf_init_errno(buf, 0, responseMsg.data() + hdrSize,
+                                responseMsg.size() - hdrSize);
+    EXPECT_EQ(rc, 0);
+
+    pldm_msgbuf_insert_uint8(buf, complition_code_resp);
+    pldm_msgbuf_insert_uint32(buf, next_data_transfer_handle_resp);
+    pldm_msgbuf_insert_uint8(buf, transfer_flag_resp);
+    pldm_msgbuf_insert_uint32(buf, fdp_capabilities_during_update.value);
+    pldm_msgbuf_insert_uint16(buf, downstreamDeviceCount);
+
+    /** Filling paramter table, the correctness of the downstream devices data
+     *  is not checked in this test case so filling with 0xff
+     */
+    std::fill_n(responseMsg.data() + hdrSize +
+                    PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_RESP_MIN_LEN,
+                downstreamDeviceParamTableLen, 0xff);
+    auto table = reinterpret_cast<pldm_component_parameter_entry*>(
+        responseMsg.data() + hdrSize +
+        PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_RESP_MIN_LEN);
+    table->active_comp_ver_str_len = activeComponentVersionStringLength;
+    table->pending_comp_ver_str_len = pendingComponentVersionStringLength;
+
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+    struct pldm_get_downstream_firmware_params_resp resp_data = {};
+    struct variable_field downstreamDeviceParamTable = {};
+
+    rc = decode_get_downstream_firmware_params_resp(
+        response, responseMsg.size() - hdrSize, &resp_data,
+        &downstreamDeviceParamTable);
+
+    EXPECT_EQ(rc, 0);
+    EXPECT_EQ(resp_data.completion_code, complition_code_resp);
+    EXPECT_EQ(resp_data.next_data_transfer_handle,
+              next_data_transfer_handle_resp);
+    EXPECT_EQ(resp_data.transfer_flag, transfer_flag_resp);
+    EXPECT_EQ(resp_data.downstream_device_count, downstreamDeviceCount);
+    EXPECT_EQ(downstreamDeviceParamTable.length, downstreamDeviceParamTableLen);
+    EXPECT_EQ(true,
+              std::equal(downstreamDeviceParamTable.ptr,
+                         downstreamDeviceParamTable.ptr +
+                             downstreamDeviceParamTable.length,
+                         responseMsg.begin() + hdrSize +
+                             PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_RESP_MIN_LEN,
+                         responseMsg.end()));
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetDownstreamFirmwareParameters, decodeResponseInvalidLength)
+{
+    /** Count is not fixed here taking it as 1, and the downstream device's
+     *  version strings length are set to 8
+     */
+    constexpr uint16_t downstreamDeviceCount = 1;
+    constexpr uint8_t activeComponentVersionStringLength = 8;
+    constexpr uint8_t pendingComponentVersionStringLength = 8;
+    constexpr size_t downstreamDeviceParamTableLen =
+        PLDM_DOWNSTREAM_DEVICE_PARAMETER_ENTRY_MIN_LEN +
+        activeComponentVersionStringLength +
+        pendingComponentVersionStringLength;
+    constexpr uint8_t complition_code_resp = PLDM_SUCCESS;
+    constexpr uint32_t next_data_transfer_handle_resp = 0x0;
+    constexpr uint8_t transfer_flag_resp = PLDM_START_AND_END;
+    constexpr bitfield32_t fdp_capabilities_during_update = {.value = 0x0002};
+
+    std::array<uint8_t,
+               hdrSize + PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_RESP_MIN_LEN +
+                   downstreamDeviceParamTableLen - 1 /* inject error length*/>
+        responseMsg{};
+
+    int rc = 0;
+
+    struct pldm_msgbuf _buf;
+    struct pldm_msgbuf* buf = &_buf;
+    rc = pldm_msgbuf_init_errno(buf, 0, responseMsg.data() + hdrSize,
+                                responseMsg.size() - hdrSize);
+    EXPECT_EQ(rc, 0);
+
+    pldm_msgbuf_insert_uint8(buf, complition_code_resp);
+    pldm_msgbuf_insert_uint32(buf, next_data_transfer_handle_resp);
+    pldm_msgbuf_insert_uint8(buf, transfer_flag_resp);
+    pldm_msgbuf_insert_uint32(buf, fdp_capabilities_during_update.value);
+    pldm_msgbuf_insert_uint16(buf, downstreamDeviceCount);
+
+    /** Filling paramter table, the correctness of the downstream devices data
+     *  is not checked in this test case so filling with 0xff
+     */
+    std::fill_n(responseMsg.data() + hdrSize +
+                    PLDM_GET_DOWNSTREAM_FIRMWARE_PARAMS_RESP_MIN_LEN,
+                downstreamDeviceParamTableLen - 1 /* inject error length*/,
+                0xff);
+
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+    struct pldm_get_downstream_firmware_params_resp resp_data = {};
+    struct variable_field downstreamDeviceParamTable = {};
+
+    rc = decode_get_downstream_firmware_params_resp(
+        response, responseMsg.size() - hdrSize, &resp_data,
+        &downstreamDeviceParamTable);
+    EXPECT_EQ(rc, 0);
+
+    pldm_downstream_device_parameter_entry entry{};
+    variable_field versions{};
+
+    /** In test mode, this will trigger an assert failure and cause the unit
+     * test to fail if only testing by the rc. Use ASSERT_DEATH to test this
+     * scenario.
+     *
+     *  The 1st parameter is the function under test.
+     *  The 2nd parameter compares the output of the program.
+     */
+#ifdef NDEBUG
+    EXPECT_NE(decode_downstream_device_parameter_table_entry(
+                  &downstreamDeviceParamTable, &entry, &versions),
+              0);
+#else
+    EXPECT_DEATH(
+        decode_downstream_device_parameter_table_entry(
+            &downstreamDeviceParamTable, &entry, &versions),
+        // This error doesn't output any error message, leave it be empty
+        "");
+#endif
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetDownstreamFirmwareParameters, goodPathDecodeDownstreamDeviceParamTable)
+{
+    // Arbitrary downstream device index
+    constexpr uint16_t downstreamDeviceIndex = 1;
+    // Arbitrary value for component classification
+    constexpr uint32_t comparisonStamp = 0x12345678;
+    // Arbitrary value for component activation methods
+    constexpr uint16_t compActivationMethods = 0xbbdd;
+    // Arbitrary value for capabilities during update
+    constexpr uint32_t capabilitiesDuringUpdate = 0xbadbeefe;
+    // ActiveCompImageSetVerStrLen is not fixed here taking it as 8
+    constexpr uint8_t activeCompVerStrLen = 8;
+    // PendingCompImageSetVerStrLen is not fixed here taking it as 8
+    constexpr uint8_t pendingCompVerStrLen = 8;
+    // Arbitrary value for release date
+    constexpr char release_date[8] = {'2', '0', '2', '4', '0', '6', '2', '1'};
+    // Arbitrary version strings
+    constexpr char activeCompVerStr[activeCompVerStrLen] = {'1', '2', '3', '4',
+                                                            '5', '6', '7', '8'};
+    constexpr char pendingCompVerStr[pendingCompVerStrLen] = {
+        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
+
+    std::array<uint8_t, PLDM_DOWNSTREAM_DEVICE_PARAMETER_ENTRY_MIN_LEN +
+                            activeCompVerStrLen + pendingCompVerStrLen>
+        responseMsg{};
+
+    int rc = 0;
+
+    struct pldm_msgbuf _buf;
+    struct pldm_msgbuf* buf = &_buf;
+    rc = pldm_msgbuf_init_errno(buf,
+                                PLDM_DOWNSTREAM_DEVICE_PARAMETER_ENTRY_MIN_LEN,
+                                responseMsg.data(), responseMsg.size());
+    EXPECT_EQ(rc, 0);
+
+    pldm_msgbuf_insert_uint16(buf, downstreamDeviceIndex);
+    pldm_msgbuf_insert_uint32(buf, comparisonStamp);
+    pldm_msgbuf_insert_uint8(buf, (uint8_t)PLDM_STR_TYPE_ASCII);
+    pldm_msgbuf_insert_uint8(buf, activeCompVerStrLen);
+    pldm_msgbuf_insert_array_char(buf, release_date, sizeof(release_date));
+    pldm_msgbuf_insert_uint32(buf, comparisonStamp);
+    pldm_msgbuf_insert_uint8(buf, (uint8_t)PLDM_STR_TYPE_ASCII);
+    pldm_msgbuf_insert_uint8(buf, pendingCompVerStrLen);
+    pldm_msgbuf_insert_array_char(buf, release_date, sizeof(release_date));
+    pldm_msgbuf_insert_uint16(buf, compActivationMethods);
+    pldm_msgbuf_insert_uint32(buf, capabilitiesDuringUpdate);
+    pldm_msgbuf_insert_array_char(buf, activeCompVerStr,
+                                  sizeof(activeCompVerStr));
+    pldm_msgbuf_insert_array_char(buf, pendingCompVerStr,
+                                  sizeof(pendingCompVerStr));
+
+    variable_field rawData = {.ptr = responseMsg.data(),
+                              .length = responseMsg.size()};
+    struct pldm_downstream_device_parameter_entry_versions entry_version = {};
+    struct variable_field versions = {};
+    const uint8_t* original_ptr = rawData.ptr;
+
+    rc = decode_downstream_device_parameter_table_entry(
+        &rawData, &entry_version.entry, &versions);
+
+    EXPECT_EQ(rc, 0);
+    EXPECT_EQ(rawData.ptr, original_ptr +
+                               PLDM_DOWNSTREAM_DEVICE_PARAMETER_ENTRY_MIN_LEN +
+                               entry_version.entry.active_comp_ver_str_len +
+                               entry_version.entry.pending_comp_ver_str_len);
+    EXPECT_EQ(rawData.length, 0);
+
+    // Further decode the version strings
+    rc = decode_downstream_device_parameter_table_entry_versions(
+        &versions, &entry_version.entry, entry_version.active_comp_ver_str,
+        entry_version.pending_comp_ver_str);
+    struct pldm_downstream_device_parameter_entry entry = entry_version.entry;
+    EXPECT_EQ(rc, 0);
+
+    // Verify the decoded table entry
+    EXPECT_EQ(entry.downstream_device_index, downstreamDeviceIndex);
+    EXPECT_EQ(entry.active_comp_comparison_stamp, comparisonStamp);
+    EXPECT_EQ(entry.active_comp_ver_str_type, PLDM_STR_TYPE_ASCII);
+    EXPECT_EQ(entry.active_comp_ver_str_len, activeCompVerStrLen);
+    EXPECT_EQ(0, memcmp(entry.active_comp_release_date, release_date,
+                        sizeof(release_date)));
+    EXPECT_EQ(entry.pending_comp_comparison_stamp, comparisonStamp);
+    EXPECT_EQ(entry.pending_comp_ver_str_type, PLDM_STR_TYPE_ASCII);
+    EXPECT_EQ(entry.pending_comp_ver_str_len, pendingCompVerStrLen);
+    EXPECT_EQ(0, memcmp(entry.pending_comp_release_date, release_date,
+                        sizeof(release_date)));
+    EXPECT_EQ(entry.comp_activation_methods.value, compActivationMethods);
+    EXPECT_EQ(entry.capabilities_during_update.value, capabilitiesDuringUpdate);
+    EXPECT_EQ(entry.active_comp_ver_str_len + entry.pending_comp_ver_str_len,
+              versions.length);
+    EXPECT_EQ(0, memcmp(versions.ptr, activeCompVerStr, activeCompVerStrLen));
+    EXPECT_EQ(0, memcmp(versions.ptr + entry.active_comp_ver_str_len,
+                        pendingCompVerStr, pendingCompVerStrLen));
+
+    // Verify version strings
+    EXPECT_EQ(0, memcmp(entry_version.entry.active_comp_ver_str,
+                        activeCompVerStr, activeCompVerStrLen));
+    EXPECT_EQ('\0',
+              entry_version.entry.active_comp_ver_str[activeCompVerStrLen]);
+    EXPECT_EQ(0, memcmp(entry_version.entry.pending_comp_ver_str,
+                        pendingCompVerStr, pendingCompVerStrLen));
+    EXPECT_EQ('\0',
+              entry_version.entry.pending_comp_ver_str[pendingCompVerStrLen]);
+    EXPECT_EQ(0, memcmp(entry_version.active_comp_ver_str, activeCompVerStr,
+                        activeCompVerStrLen));
+    EXPECT_EQ('\0', entry_version.active_comp_ver_str[activeCompVerStrLen]);
+    EXPECT_EQ(0, memcmp(entry_version.pending_comp_ver_str, pendingCompVerStr,
+                        pendingCompVerStrLen));
+    EXPECT_EQ('\0', entry_version.pending_comp_ver_str[pendingCompVerStrLen]);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetDownstreamFirmwareParameters, goodPathDecodeDownstreamTableVersions)
+{
+    // Arbitrary component version string length
+    constexpr uint8_t activeCompVerStrLen = 8;
+    constexpr uint8_t pendingCompVerStrLen = 8;
+    // Arbitrary ActiveVersionStr and pendingVersionStr
+    constexpr char versionsStr[] = {'1', '2', '3', '4', '5', '6', '7', '8',
+                                    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
+    const struct variable_field versions = {
+        .ptr = reinterpret_cast<const uint8_t*>(versionsStr),
+        .length = sizeof(versionsStr)};
+
+    struct pldm_downstream_device_parameter_entry_versions entryVersion = {};
+    entryVersion.entry.active_comp_ver_str_len = activeCompVerStrLen;
+    entryVersion.entry.pending_comp_ver_str_len = pendingCompVerStrLen;
+
+    int rc = decode_downstream_device_parameter_table_entry_versions(
+        &versions, &entryVersion.entry, entryVersion.active_comp_ver_str,
+        entryVersion.pending_comp_ver_str);
+
+    EXPECT_EQ(rc, 0);
+    EXPECT_EQ(0, memcmp(entryVersion.active_comp_ver_str, versions.ptr,
+                        activeCompVerStrLen));
+    EXPECT_EQ('\0', entryVersion.active_comp_ver_str[activeCompVerStrLen]);
+    EXPECT_EQ(0,
+              memcmp(entryVersion.pending_comp_ver_str,
+                     versions.ptr + activeCompVerStrLen, pendingCompVerStrLen));
+    EXPECT_EQ('\0', entryVersion.pending_comp_ver_str[activeCompVerStrLen]);
+    EXPECT_EQ(0, memcmp(entryVersion.entry.active_comp_ver_str, versions.ptr,
+                        activeCompVerStrLen));
+    EXPECT_EQ('\0',
+              entryVersion.entry.pending_comp_ver_str[activeCompVerStrLen]);
+    EXPECT_EQ(0,
+              memcmp(entryVersion.entry.pending_comp_ver_str,
+                     versions.ptr + activeCompVerStrLen, pendingCompVerStrLen));
+    EXPECT_EQ('\0',
+              entryVersion.entry.pending_comp_ver_str[activeCompVerStrLen]);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetDownstreamFirmwareParameters, decodeInvalidDownstreamTableVersions)
+{
+    // Arbitrary ActiveVersionStr and pendingVersionStr
+    constexpr char versionsStr[] = {'1', '2', '3', '4', '5', '6', '7', '8',
+                                    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
+    const struct variable_field versions = {
+        .ptr = reinterpret_cast<const uint8_t*>(versionsStr),
+        .length = sizeof(versionsStr)};
+
+    struct pldm_downstream_device_parameter_entry_versions entryVersion = {};
+
+    int rc = decode_downstream_device_parameter_table_entry_versions(
+        &versions, nullptr, entryVersion.active_comp_ver_str,
+        entryVersion.pending_comp_ver_str);
+    EXPECT_EQ(rc, -EINVAL);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(GetDownstreamFirmwareParameters, decodeOverflowDownstreamTableVersions)
+{
+    // Arbitrary component version string length
+    constexpr uint8_t activeCompVerStrLen = 8;
+    constexpr uint8_t pendingCompVerStrLen = 8;
+    // Arbitrary ActiveVersionStr and pendingVersionStr
+    constexpr char versionsStr[] = {'1', '2', '3', '4', '5', '6', '7', '8',
+                                    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
+    const struct variable_field versions = {
+        .ptr = reinterpret_cast<const uint8_t*>(versionsStr),
+        .length = sizeof(versionsStr) - 1 // Inject error length
+    };
+
+    struct pldm_downstream_device_parameter_entry_versions entryVersion = {};
+    entryVersion.entry.active_comp_ver_str_len = activeCompVerStrLen;
+    entryVersion.entry.pending_comp_ver_str_len = pendingCompVerStrLen;
+
+    EXPECT_EQ(decode_downstream_device_parameter_table_entry_versions(
+                  &versions, &entryVersion.entry,
+                  entryVersion.active_comp_ver_str,
+                  entryVersion.pending_comp_ver_str),
+              -EOVERFLOW);
+}
+#endif
+
 TEST(RequestUpdate, goodPathEncodeRequest)
 {
     constexpr uint8_t instanceId = 1;