libpldm: implement encode/decode APIs for GetPDR

This commit implements the encode response and decode request of
GetPDR command which is defined in PLDM Platform spec DSP0248_1.1.1
This enables the PLDM responder to receive the request and process
the response packet.

Signed-off-by: Sampa Misra <sampmisr@in.ibm.com>
Change-Id: I9c17e0a4dd4e02b3c8b50eb55958c687c988f421
diff --git a/libpldm/platform.c b/libpldm/platform.c
index f9c8351..fc9cc56 100644
--- a/libpldm/platform.c
+++ b/libpldm/platform.c
@@ -97,3 +97,70 @@
 
 	return PLDM_SUCCESS;
 }
+
+int decode_get_pdr_req(const struct pldm_msg *msg, size_t payload_length,
+		       uint32_t *record_hndl, uint32_t *data_transfer_hndl,
+		       uint8_t *transfer_op_flag, uint16_t *request_cnt,
+		       uint16_t *record_chg_num)
+{
+	if (msg == NULL || record_hndl == NULL || data_transfer_hndl == NULL ||
+	    transfer_op_flag == NULL || request_cnt == NULL ||
+	    record_chg_num == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+	if (payload_length != PLDM_GET_PDR_REQ_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	struct pldm_get_pdr_req *request =
+	    (struct pldm_get_pdr_req *)msg->payload;
+	*record_hndl = le32toh(request->record_handle);
+	*data_transfer_hndl = le32toh(request->data_transfer_handle);
+	*transfer_op_flag = request->transfer_op_flag;
+	*request_cnt = le16toh(request->request_count);
+	*record_chg_num = le16toh(request->record_change_number);
+
+	return PLDM_SUCCESS;
+}
+
+int encode_get_pdr_resp(uint8_t instance_id, uint8_t completion_code,
+			uint32_t next_record_hndl,
+			uint32_t next_data_transfer_hndl, uint8_t transfer_flag,
+			uint16_t resp_cnt, const uint8_t *record_data,
+			uint8_t transfer_crc, struct pldm_msg *msg)
+{
+	struct pldm_header_info header = {0};
+	int rc = PLDM_SUCCESS;
+
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+	struct pldm_get_pdr_resp *response =
+	    (struct pldm_get_pdr_resp *)msg->payload;
+
+	response->completion_code = completion_code;
+
+	header.msg_type = PLDM_RESPONSE;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_PLATFORM;
+	header.command = PLDM_GET_PDR;
+	if ((rc = pack_pldm_header(&header, &(msg->hdr))) > PLDM_SUCCESS) {
+		return rc;
+	}
+
+	if (response->completion_code == PLDM_SUCCESS) {
+		response->next_record_handle = htole32(next_record_hndl);
+		response->next_data_transfer_handle =
+		    htole32(next_data_transfer_hndl);
+		response->transfer_flag = transfer_flag;
+		response->response_count = htole16(resp_cnt);
+		if (record_data != NULL && resp_cnt > 0) {
+			memcpy(response->record_data, record_data, resp_cnt);
+		}
+		uint8_t *dst = msg->payload;
+		dst += (sizeof(struct pldm_get_pdr_resp) - 1) + resp_cnt;
+		*dst = transfer_crc;
+	}
+
+	return PLDM_SUCCESS;
+}
diff --git a/libpldm/platform.h b/libpldm/platform.h
index d29dc8a..746ae4f 100644
--- a/libpldm/platform.h
+++ b/libpldm/platform.h
@@ -15,12 +15,17 @@
 /* Response lengths are inclusive of completion code */
 #define PLDM_SET_STATE_EFFECTER_STATES_RESP_BYTES 1
 
+#define PLDM_GET_PDR_REQ_BYTES 13
+/* Minimum response length */
+#define PLDM_GET_PDR_MIN_RESP_BYTES 12
+
 enum set_request { PLDM_NO_CHANGE = 0x00, PLDM_REQUEST_SET = 0x01 };
 
 enum effecter_state { PLDM_INVALID_VALUE = 0xFF };
 
 enum pldm_platform_commands {
 	PLDM_SET_STATE_EFFECTER_STATES = 0x39,
+	PLDM_GET_PDR = 0x51,
 };
 
 /** @brief PLDM PDR types
@@ -97,6 +102,33 @@
 	set_effecter_state_field field[8];
 } __attribute__((packed));
 
+/** @struct pldm_get_pdr_resp
+ *
+ *  structure representing GetPDR response packet
+ *  transfer CRC is not part of the structure and will be
+ *  added at the end of last packet in multipart transfer
+ */
+struct pldm_get_pdr_resp {
+	uint8_t completion_code;
+	uint32_t next_record_handle;
+	uint32_t next_data_transfer_handle;
+	uint8_t transfer_flag;
+	uint16_t response_count;
+	uint8_t record_data[1];
+} __attribute__((packed));
+
+/** @struct pldm_get_pdr_req
+ *
+ *  structure representing GetPDR request packet
+ */
+struct pldm_get_pdr_req {
+	uint32_t record_handle;
+	uint32_t data_transfer_handle;
+	uint8_t transfer_op_flag;
+	uint16_t request_count;
+	uint16_t record_change_number;
+} __attribute__((packed));
+
 /* Responder */
 
 /* SetStateEffecterStates */
@@ -139,6 +171,58 @@
 					 uint8_t *comp_effecter_count,
 					 set_effecter_state_field *field);
 
+/* GetPDR */
+
+/** @brief Create a PLDM response message for GetPDR
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] completion_code - PLDM completion code
+ *  @param[in] next_record_hndl - The recordHandle for the PDR that is next in
+ *        the PDR Repository
+ *  @param[in] next_data_transfer_hndl - A handle that identifies the next
+ *        portion of the PDR data to be transferred, if any
+ *  @param[in] transfer_flag - Indicates the portion of PDR data being
+ *        transferred
+ *  @param[in] resp_cnt - The number of recordData bytes returned in this
+ *        response
+ *  @param[in] record_data - PDR data bytes of length resp_cnt
+ *  @param[in] transfer_crc - A CRC-8 for the overall PDR. This is present only
+ *        in the last part of a PDR being transferred
+ *  @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_get_pdr_resp(uint8_t instance_id, uint8_t completion_code,
+			uint32_t next_record_hndl,
+			uint32_t next_data_transfer_hndl, uint8_t transfer_flag,
+			uint16_t resp_cnt, const uint8_t *record_data,
+			uint8_t transfer_crc, struct pldm_msg *msg);
+
+/** @brief Decode GetPDR request data
+ *
+ *  @param[in] msg - Request message
+ *  @param[in] payload_length - Length of request message payload
+ *  @param[out] record_hndl - The recordHandle value for the PDR to be retrieved
+ *  @param[out] data_transfer_hndl - Handle used to identify a particular
+ *         multipart PDR data transfer operation
+ *  @param[out] transfer_op_flag - Flag to indicate the first or subsequent
+ *         portion of transfer
+ *  @param[out] request_cnt - The maximum number of record bytes requested
+ *  @param[out] record_chg_num - Used to determine whether the PDR has changed
+ *        while PDR transfer is going on
+ *  @return pldm_completion_codes
+ */
+
+int decode_get_pdr_req(const struct pldm_msg *msg, size_t payload_length,
+		       uint32_t *record_hndl, uint32_t *data_transfer_hndl,
+		       uint8_t *transfer_op_flag, uint16_t *request_cnt,
+		       uint16_t *record_chg_num);
+
+/* Requester */
+
+/* SetStateEffecterStates */
+
 /** @brief Create a PLDM request message for SetStateEffecterStates
  *
  *  @param[in] instance_id - Message's instance id
diff --git a/test/libpldm_platform_test.cpp b/test/libpldm_platform_test.cpp
index 1010bd8..154bb59 100644
--- a/test/libpldm_platform_test.cpp
+++ b/test/libpldm_platform_test.cpp
@@ -138,3 +138,100 @@
 
     ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
 }
+
+TEST(GetPDR, testGoodEncodeResponse)
+{
+    uint8_t completionCode = 0;
+    uint32_t nextRecordHndl = 0x12;
+    uint32_t nextDataTransferHndl = 0x13;
+    uint8_t transferFlag = PLDM_START_AND_END;
+    uint16_t respCnt = 0x5;
+    std::vector<uint8_t> recordData{1, 2, 3, 4, 5};
+    uint8_t transferCRC = 0;
+
+    // + size of record data and transfer CRC
+    std::vector<uint8_t> responseMsg(hdrSize + PLDM_GET_PDR_MIN_RESP_BYTES +
+                                     recordData.size() + 1);
+    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+
+    auto rc = encode_get_pdr_resp(0, PLDM_SUCCESS, nextRecordHndl,
+                                  nextDataTransferHndl, transferFlag, respCnt,
+                                  recordData.data(), transferCRC, response);
+
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    struct pldm_get_pdr_resp* resp =
+        reinterpret_cast<struct pldm_get_pdr_resp*>(response->payload);
+
+    ASSERT_EQ(completionCode, resp->completion_code);
+    ASSERT_EQ(nextRecordHndl, resp->next_record_handle);
+    ASSERT_EQ(nextDataTransferHndl, resp->next_data_transfer_handle);
+    ASSERT_EQ(transferFlag, resp->transfer_flag);
+    ASSERT_EQ(respCnt, resp->response_count);
+    ASSERT_EQ(0,
+              memcmp(recordData.data(), resp->record_data, recordData.size()));
+}
+
+TEST(GetPDR, testBadEncodeResponse)
+{
+    uint32_t nextRecordHndl = 0x12;
+    uint32_t nextDataTransferHndl = 0x13;
+    uint8_t transferFlag = PLDM_START_AND_END;
+    uint16_t respCnt = 0x5;
+    std::vector<uint8_t> recordData{1, 2, 3, 4, 5};
+    uint8_t transferCRC = 0;
+
+    auto rc = encode_get_pdr_resp(0, PLDM_SUCCESS, nextRecordHndl,
+                                  nextDataTransferHndl, transferFlag, respCnt,
+                                  recordData.data(), transferCRC, nullptr);
+
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(GetPDR, testGoodDecodeRequest)
+{
+    std::array<uint8_t, hdrSize + PLDM_GET_PDR_REQ_BYTES> requestMsg{};
+
+    uint32_t recordHndl = 0x32;
+    uint32_t dataTransferHndl = 0x11;
+    uint8_t transferOpFlag = PLDM_GET_FIRSTPART;
+    uint16_t requestCnt = 0x5;
+    uint16_t recordChangeNum = 0;
+
+    uint32_t retRecordHndl = 0;
+    uint32_t retDataTransferHndl = 0;
+    uint8_t retTransferOpFlag = 0;
+    uint16_t retRequestCnt = 0;
+    uint16_t retRecordChangeNum = 0;
+
+    auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    struct pldm_get_pdr_req* request =
+        reinterpret_cast<struct pldm_get_pdr_req*>(req->payload);
+
+    request->record_handle = recordHndl;
+    request->data_transfer_handle = dataTransferHndl;
+    request->transfer_op_flag = transferOpFlag;
+    request->request_count = requestCnt;
+    request->record_change_number = recordChangeNum;
+
+    auto rc = decode_get_pdr_req(
+        req, requestMsg.size() - hdrSize, &retRecordHndl, &retDataTransferHndl,
+        &retTransferOpFlag, &retRequestCnt, &retRecordChangeNum);
+
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(retRecordHndl, recordHndl);
+    ASSERT_EQ(retDataTransferHndl, dataTransferHndl);
+    ASSERT_EQ(retTransferOpFlag, transferOpFlag);
+    ASSERT_EQ(retRequestCnt, requestCnt);
+    ASSERT_EQ(retRecordChangeNum, recordChangeNum);
+}
+
+TEST(GetPDR, testBadDecodeRequest)
+{
+    std::array<uint8_t, PLDM_GET_PDR_REQ_BYTES> requestMsg{};
+    auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    auto rc = decode_get_pdr_req(req, requestMsg.size(), NULL, NULL, NULL, NULL,
+                                 NULL);
+
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}