oem-ibm: libpldm: add file IO APIs

This commit implements encode and decode APIs for in-band readFile and
writeFile commands.

In-band file IO is preferred over DMA for smaller files.

Change-Id: I92ec011560ba39aed497f533074479236abeb766
Signed-off-by: vkaverap <vkaverap@in.ibm.com>
diff --git a/oem/ibm/libpldm/file_io.c b/oem/ibm/libpldm/file_io.c
index 7806ae6..dc8ad6b 100644
--- a/oem/ibm/libpldm/file_io.c
+++ b/oem/ibm/libpldm/file_io.c
@@ -155,3 +155,229 @@
 
 	return PLDM_SUCCESS;
 }
+
+int decode_read_file_req(const struct pldm_msg *msg, size_t payload_length,
+			 uint32_t *file_handle, uint32_t *offset,
+			 uint32_t *length)
+{
+	if (msg == NULL || file_handle == NULL || offset == NULL ||
+	    length == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (payload_length != PLDM_READ_FILE_REQ_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	struct pldm_read_file_req *request =
+	    (struct pldm_read_file_req *)msg->payload;
+
+	*file_handle = le32toh(request->file_handle);
+	*offset = le32toh(request->offset);
+	*length = le32toh(request->length);
+
+	return PLDM_SUCCESS;
+}
+
+int encode_read_file_req(uint8_t instance_id, uint32_t file_handle,
+			 uint32_t offset, uint32_t length, struct pldm_msg *msg)
+{
+	struct pldm_header_info header = {0};
+	int rc = PLDM_SUCCESS;
+
+	header.msg_type = PLDM_REQUEST;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_OEM;
+	header.command = PLDM_READ_FILE;
+
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (length == 0) {
+		return PLDM_INVALID_READ_LENGTH;
+	}
+
+	if ((rc = pack_pldm_header(&header, &(msg->hdr))) > PLDM_SUCCESS) {
+		return rc;
+	}
+
+	struct pldm_read_file_req *request =
+	    (struct pldm_read_file_req *)msg->payload;
+
+	request->file_handle = htole32(file_handle);
+	request->offset = htole32(offset);
+	request->length = htole32(length);
+
+	return PLDM_SUCCESS;
+}
+
+int decode_read_file_resp(const struct pldm_msg *msg, size_t payload_length,
+			  uint8_t *completion_code, uint32_t *length,
+			  size_t *file_data_offset)
+{
+	if (msg == NULL || completion_code == NULL || length == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (payload_length < PLDM_READ_FILE_RESP_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	struct pldm_read_file_resp *response =
+	    (struct pldm_read_file_resp *)msg->payload;
+
+	*completion_code = response->completion_code;
+	if (*completion_code == PLDM_SUCCESS) {
+		*length = le32toh(response->length);
+		if (payload_length != PLDM_READ_FILE_RESP_BYTES + *length) {
+			return PLDM_ERROR_INVALID_LENGTH;
+		}
+		*file_data_offset = sizeof(*completion_code) + sizeof(*length);
+	}
+
+	return PLDM_SUCCESS;
+}
+
+int encode_read_file_resp(uint8_t instance_id, uint8_t completion_code,
+			  uint32_t length, struct pldm_msg *msg)
+{
+	struct pldm_header_info header = {0};
+	int rc = PLDM_SUCCESS;
+
+	header.msg_type = PLDM_RESPONSE;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_OEM;
+	header.command = PLDM_READ_FILE;
+
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if ((rc = pack_pldm_header(&header, &(msg->hdr))) > PLDM_SUCCESS) {
+		return rc;
+	}
+
+	struct pldm_read_file_resp *response =
+	    (struct pldm_read_file_resp *)msg->payload;
+	response->completion_code = completion_code;
+
+	if (response->completion_code == PLDM_SUCCESS) {
+		response->length = htole32(length);
+	}
+
+	return PLDM_SUCCESS;
+}
+
+int decode_write_file_req(const struct pldm_msg *msg, size_t payload_length,
+			  uint32_t *file_handle, uint32_t *offset,
+			  uint32_t *length, size_t *file_data_offset)
+{
+	if (msg == NULL || file_handle == NULL || length == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (payload_length < PLDM_WRITE_FILE_REQ_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	struct pldm_write_file_req *request =
+	    (struct pldm_write_file_req *)msg->payload;
+
+	*file_handle = le32toh(request->file_handle);
+	*offset = le32toh(request->offset);
+	*length = le32toh(request->length);
+	if (payload_length != PLDM_WRITE_FILE_REQ_BYTES + *length) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+	*file_data_offset =
+	    sizeof(*file_handle) + sizeof(*offset) + sizeof(*length);
+
+	return PLDM_SUCCESS;
+}
+
+int encode_write_file_req(uint8_t instance_id, uint32_t file_handle,
+			  uint32_t offset, uint32_t length,
+			  struct pldm_msg *msg)
+{
+	struct pldm_header_info header = {0};
+	int rc = PLDM_SUCCESS;
+
+	header.msg_type = PLDM_REQUEST;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_OEM;
+	header.command = PLDM_WRITE_FILE;
+
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if ((rc = pack_pldm_header(&header, &(msg->hdr))) > PLDM_SUCCESS) {
+		return rc;
+	}
+
+	if (length == 0) {
+		return PLDM_INVALID_WRITE_LENGTH;
+	}
+
+	struct pldm_write_file_req *request =
+	    (struct pldm_write_file_req *)msg->payload;
+
+	request->file_handle = htole32(file_handle);
+	request->offset = htole32(offset);
+	request->length = htole32(length);
+
+	return PLDM_SUCCESS;
+}
+
+int decode_write_file_resp(const struct pldm_msg *msg, size_t payload_length,
+			   uint8_t *completion_code, uint32_t *length)
+{
+	if (msg == NULL || completion_code == NULL || length == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (payload_length != PLDM_WRITE_FILE_RESP_BYTES) {
+		return PLDM_ERROR_INVALID_LENGTH;
+	}
+
+	struct pldm_write_file_resp *response =
+	    (struct pldm_write_file_resp *)msg->payload;
+
+	*completion_code = le32toh(response->completion_code);
+	if (response->completion_code == PLDM_SUCCESS) {
+		*length = le32toh(response->length);
+	}
+
+	return PLDM_SUCCESS;
+}
+
+int encode_write_file_resp(uint8_t instance_id, uint8_t completion_code,
+			   uint32_t length, struct pldm_msg *msg)
+{
+	struct pldm_header_info header = {0};
+	int rc = PLDM_SUCCESS;
+
+	header.msg_type = PLDM_RESPONSE;
+	header.instance = instance_id;
+	header.pldm_type = PLDM_OEM;
+	header.command = PLDM_WRITE_FILE;
+
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if ((rc = pack_pldm_header(&header, &(msg->hdr))) > PLDM_SUCCESS) {
+		return rc;
+	}
+
+	struct pldm_write_file_resp *response =
+	    (struct pldm_write_file_resp *)msg->payload;
+	response->completion_code = completion_code;
+
+	if (response->completion_code == PLDM_SUCCESS) {
+		response->length = htole32(length);
+	}
+
+	return PLDM_SUCCESS;
+}
diff --git a/oem/ibm/libpldm/file_io.h b/oem/ibm/libpldm/file_io.h
index b3a0f73..437897c 100644
--- a/oem/ibm/libpldm/file_io.h
+++ b/oem/ibm/libpldm/file_io.h
@@ -14,6 +14,8 @@
  */
 enum pldm_fileio_commands {
 	PLDM_GET_FILE_TABLE = 0x1,
+	PLDM_READ_FILE = 0x4,
+	PLDM_WRITE_FILE = 0x5,
 	PLDM_READ_FILE_INTO_MEMORY = 0x6,
 	PLDM_WRITE_FILE_FROM_MEMORY = 0x7,
 };
@@ -40,6 +42,10 @@
 #define PLDM_RW_FILE_MEM_RESP_BYTES 5
 #define PLDM_GET_FILE_TABLE_REQ_BYTES 6
 #define PLDM_GET_FILE_TABLE_MIN_RESP_BYTES 6
+#define PLDM_READ_FILE_REQ_BYTES 12
+#define PLDM_READ_FILE_RESP_BYTES 5
+#define PLDM_WRITE_FILE_REQ_BYTES 12
+#define PLDM_WRITE_FILE_RESP_BYTES 5
 
 /** @struct pldm_read_write_file_memory_req
  *
@@ -178,6 +184,163 @@
 			       uint8_t transfer_flag, const uint8_t *table_data,
 			       size_t table_size, struct pldm_msg *msg);
 
+/** @struct pldm_read_file_req
+ *
+ *  Structure representing ReadFile request
+ */
+struct pldm_read_file_req {
+	uint32_t file_handle; //!< Handle to file
+	uint32_t offset;      //!< Offset to file where read starts
+	uint32_t length;      //!< Bytes to be read
+} __attribute__((packed));
+
+/** @struct pldm_read_file_resp
+ *
+ *  Structure representing ReadFile response data
+ */
+struct pldm_read_file_resp {
+	uint8_t completion_code; //!< Completion code
+	uint32_t length;	 //!< Number of bytes read
+	uint8_t file_data[1];    //!< Address of this is where file data starts
+} __attribute__((packed));
+
+/** @struct pldm_write_file_req
+ *
+ *  Structure representing WriteFile request
+ */
+struct pldm_write_file_req {
+	uint32_t file_handle; //!< Handle to file
+	uint32_t offset;      //!< Offset to file where write starts
+	uint32_t length;      //!< Bytes to be written
+	uint8_t file_data[1]; //!< Address of this is where file data starts
+} __attribute__((packed));
+
+/** @struct pldm_write_file_resp
+ *
+ *  Structure representing WriteFile response data
+ */
+struct pldm_write_file_resp {
+	uint8_t completion_code; //!< Completion code
+	uint32_t length;	 //!< Bytes written
+} __attribute__((packed));
+
+/** @brief Decode Read File commands request
+ *
+ *  @param[in] msg - PLDM request message payload
+ *  @param[in] payload_length - Length of request payload
+ *  @param[out] file_handle - A handle to the file
+ *  @param[out] offset - Offset to the file at which the read should begin
+ *  @param[out] length - Number of bytes read
+ *  @return pldm_completion_codes
+ */
+int decode_read_file_req(const struct pldm_msg *msg, size_t payload_length,
+			 uint32_t *file_handle, uint32_t *offset,
+			 uint32_t *length);
+
+/** @brief Encode Read File commands request
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] file_handle - A handle to the file
+ *  @param[in] offset - Offset to the file at which the read should begin
+ *  @param[in] length - Number of bytes read
+ *  @param[in,out] msg - Message will be written to this
+ *  @return pldm_completion_codes
+ *  @note  Caller is responsible for memory alloc and dealloc of param 'msg'
+ */
+int encode_read_file_req(uint8_t instance_id, uint32_t file_handle,
+			 uint32_t offset, uint32_t length,
+			 struct pldm_msg *msg);
+
+/** @brief Decode Read File commands response
+ *
+ *  @param[in] msg - PLDM response message payload
+ *  @param[in] payload_length - Length of request payload
+ *  @param[out] completion_code - PLDM completion code
+ *  @param[out] length - Number of bytes read. This could be less than what the
+ *                       requester asked for.
+ *  @param[out] file_data_offset - Offset where file data should be read in pldm
+ * msg.
+ *  @return pldm_completion_codes
+ */
+int decode_read_file_resp(const struct pldm_msg *msg, size_t payload_length,
+			  uint8_t *completion_code, uint32_t *length,
+			  size_t *file_data_offset);
+
+/** @brief Create a PLDM response for Read File
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] completion_code - PLDM completion code
+ *  @param[in] length - Number of bytes read. This could be less than what the
+ *                      requester asked for.
+ *  @param[in,out] msg - Message will be written to this
+ *  @return pldm_completion_codes
+ *  @note  Caller is responsible for memory alloc and dealloc of param 'msg'.
+ *  Although read file command response includes file data, this function
+ *  does not encode the file data to prevent additional copying of the data.
+ *  The position of file data is calculated by caller from address and size
+ *  of other input arguments.
+ */
+int encode_read_file_resp(uint8_t instance_id, uint8_t completion_code,
+			  uint32_t length, struct pldm_msg *msg);
+
+/** @brief Decode Write File commands request
+ *
+ *  @param[in] msg - PLDM request message payload
+ *  @param[in] payload_length - Length of request payload
+ *  @param[out] file_handle - A handle to the file
+ *  @param[out] offset - Offset to the file at which the write should begin
+ *  @param[out] length - Number of bytes to write
+ *  @param[out] file_data_offset - Offset where file data write begins in pldm
+ * msg.
+ *  @return pldm_completion_codes
+ */
+int decode_write_file_req(const struct pldm_msg *msg, size_t payload_length,
+			  uint32_t *file_handle, uint32_t *offset,
+			  uint32_t *length, size_t *file_data_offset);
+
+/** @brief Create a PLDM request for Write File
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] file_handle - A handle to the file
+ *  @param[in] offset - Offset to the file at which the read should begin
+ *  @param[in] length - Number of bytes written. This could be less than what
+ *                      the requester asked for.
+ *  @param[in,out] msg - Message will be written to this
+ *  @return pldm_completion_codes
+ *  @note  Caller is responsible for memory alloc and dealloc of param 'msg'.
+ *  Although write file command request includes file data, this function
+ *  does not encode the file data to prevent additional copying of the data.
+ *  The position of file data is calculated by caller from address and size
+ *  of other input arguments.
+ */
+int encode_write_file_req(uint8_t instance_id, uint32_t file_handle,
+			  uint32_t offset, uint32_t length,
+			  struct pldm_msg *msg);
+
+/** @brief Decode Write File commands response
+ *
+ *  @param[in] msg - PLDM request message payload
+ *  @param[in] payload_length - Length of request payload
+ *  @param[out] completion_code - PLDM completion code
+ *  @param[out] length - Number of bytes written
+ *  @return pldm_completion_codes
+ */
+int decode_write_file_resp(const struct pldm_msg *msg, size_t payload_length,
+			   uint8_t *completion_code, uint32_t *length);
+
+/** @brief Create a PLDM response for Write File
+ *
+ *  @param[in] instance_id - Message's instance id
+ *  @param[in] completion_code - PLDM completion code
+ *  @param[in] length - Number of bytes written. This could be less than what
+ *                      the requester asked for.
+ *  @param[in,out] msg - Message will be written to this
+ *  @return pldm_completion_codes
+ *  @note  Caller is responsible for memory alloc and dealloc of param 'msg'
+ */
+int encode_write_file_resp(uint8_t instance_id, uint8_t completion_code,
+			   uint32_t length, struct pldm_msg *msg);
+
 #ifdef __cplusplus
 }
 #endif