Implement API to pack and unpack PLDM header

- The pack_pldm_header API packs the PLDM header into the
  PLDM message.
- The unpack_pldm_header API unpacks the PLDM header from the
  PLDM message.

Change-Id: I60b96954395cdcd1cecf306b7aee4c4de6c0f35a
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/libpldm/base.c b/libpldm/base.c
index db32ca4..c85aead 100644
--- a/libpldm/base.c
+++ b/libpldm/base.c
@@ -3,6 +3,65 @@
 
 #include "base.h"
 
+int pack_pldm_header(const struct pldm_header_info *hdr,
+		     struct pldm_msg_hdr *msg)
+{
+	if (msg == NULL || hdr == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (hdr->msg_type != PLDM_RESPONSE && hdr->msg_type != PLDM_REQUEST &&
+	    hdr->msg_type != PLDM_ASYNC_REQUEST_NOTIFY) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (hdr->instance > PLDM_INSTANCE_MAX) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (hdr->pldm_type > (PLDM_MAX_TYPES - 1)) {
+		return PLDM_ERROR_INVALID_PLDM_TYPE;
+	}
+
+	uint8_t datagram = (hdr->msg_type == PLDM_ASYNC_REQUEST_NOTIFY) ? 1 : 0;
+
+	if (hdr->msg_type == PLDM_RESPONSE) {
+		msg->request = PLDM_RESPONSE;
+	} else if (hdr->msg_type == PLDM_REQUEST ||
+		   hdr->msg_type == PLDM_ASYNC_REQUEST_NOTIFY) {
+		msg->request = PLDM_REQUEST;
+	}
+	msg->datagram = datagram;
+	msg->reserved = 0;
+	msg->instance_id = hdr->instance;
+	msg->header_ver = 0;
+	msg->type = hdr->pldm_type;
+	msg->command = hdr->command;
+
+	return PLDM_SUCCESS;
+}
+
+int unpack_pldm_header(const struct pldm_msg_hdr *msg,
+		       struct pldm_header_info *hdr)
+{
+	if (msg == NULL) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	if (msg->request == PLDM_RESPONSE) {
+		hdr->msg_type = PLDM_RESPONSE;
+	} else {
+		hdr->msg_type =
+		    msg->datagram ? PLDM_ASYNC_REQUEST_NOTIFY : PLDM_REQUEST;
+	}
+
+	hdr->instance = msg->instance_id;
+	hdr->pldm_type = msg->type;
+	hdr->command = msg->command;
+
+	return PLDM_SUCCESS;
+}
+
 int encode_get_types_req(uint8_t instance_id, struct pldm_msg *msg)
 {
 	if (msg == NULL) {
diff --git a/libpldm/base.h b/libpldm/base.h
index 29b2c87..65be648 100644
--- a/libpldm/base.h
+++ b/libpldm/base.h
@@ -34,6 +34,18 @@
 	PLDM_ERROR_INVALID_PLDM_TYPE = 0x20
 };
 
+/** @enum MessageType
+ *
+ *  The different message types supported by the PLDM specification.
+ */
+typedef enum {
+	PLDM_RESPONSE,		   //!< PLDM response
+	PLDM_REQUEST,		   //!< PLDM request
+	PLDM_RESERVED,		   //!< Reserved
+	PLDM_ASYNC_REQUEST_NOTIFY, //!< Unacknowledged PLDM request messages
+} MessageType;
+
+#define PLDM_INSTANCE_MAX 31
 #define PLDM_MAX_TYPES 64
 #define PLDM_MAX_CMDS_PER_TYPE 256
 
@@ -100,6 +112,43 @@
 	uint8_t alpha;
 } __attribute__((packed));
 
+/** @struct pldm_header_info
+ *
+ *  The information needed to prepare PLDM header and this is passed to the
+ *  pack_pldm_header and unpack_pldm_header API.
+ */
+struct pldm_header_info {
+	MessageType msg_type;    //!< PLDM message type
+	uint8_t instance;	//!< PLDM instance id
+	uint8_t pldm_type;       //!< PLDM type
+	uint8_t command;	 //!< PLDM command code
+	uint8_t completion_code; //!< PLDM completion code, applies for response
+};
+
+/**
+ * @brief Populate the PLDM message with the PLDM header.The caller of this API
+ *        allocates buffer for the PLDM header when forming the PLDM message.
+ *        The buffer is passed to this API to pack the PLDM header.
+ *
+ * @param[in] hdr - Pointer to the PLDM header information
+ * @param[out] msg - Pointer to PLDM message header
+ *
+ * @return 0 on success, otherwise PLDM error codes.
+ */
+int pack_pldm_header(const struct pldm_header_info *hdr,
+		     struct pldm_msg_hdr *msg);
+
+/**
+ * @brief Unpack the PLDM header from the PLDM message.
+ *
+ * @param[in] msg - Pointer to the PLDM message header
+ * @param[out] hdr - Pointer to the PLDM header information
+ *
+ * @return 0 on success, otherwise PLDM error codes.
+ */
+int unpack_pldm_header(const struct pldm_msg_hdr *msg,
+		       struct pldm_header_info *hdr);
+
 /* Requester */
 
 /* GetPLDMTypes */
diff --git a/test/libpldm_base_test.cpp b/test/libpldm_base_test.cpp
index 3be7ddd..04d14c0 100644
--- a/test/libpldm_base_test.cpp
+++ b/test/libpldm_base_test.cpp
@@ -6,6 +6,187 @@
 
 #include <gtest/gtest.h>
 
+TEST(PackPLDMMessage, BadPathTest)
+{
+    struct pldm_header_info hdr;
+    struct pldm_header_info* hdr_ptr = NULL;
+    pldm_msg_hdr msg{};
+
+    // PLDM header information pointer is NULL
+    auto rc = pack_pldm_header(hdr_ptr, &msg);
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    // PLDM message pointer is NULL
+    rc = pack_pldm_header(&hdr, nullptr);
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    // PLDM header information pointer and PLDM message pointer is NULL
+    rc = pack_pldm_header(hdr_ptr, nullptr);
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    // RESERVED message type
+    hdr.msg_type = PLDM_RESERVED;
+    rc = pack_pldm_header(&hdr, &msg);
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    // Instance ID out of range
+    hdr.msg_type = PLDM_REQUEST;
+    hdr.instance = 32;
+    rc = pack_pldm_header(&hdr, &msg);
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+    // PLDM type out of range
+    hdr.msg_type = PLDM_REQUEST;
+    hdr.instance = 31;
+    hdr.pldm_type = 64;
+    rc = pack_pldm_header(&hdr, &msg);
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_PLDM_TYPE);
+}
+
+TEST(PackPLDMMessage, RequestMessageGoodPath)
+{
+    struct pldm_header_info hdr;
+    pldm_msg_hdr msg{};
+
+    // Message type is REQUEST and lower range of the field values
+    hdr.msg_type = PLDM_REQUEST;
+    hdr.instance = 0;
+    hdr.pldm_type = 0;
+    hdr.command = 0;
+
+    auto rc = pack_pldm_header(&hdr, &msg);
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(msg.request, 1);
+    ASSERT_EQ(msg.datagram, 0);
+    ASSERT_EQ(msg.instance_id, 0);
+    ASSERT_EQ(msg.type, 0);
+    ASSERT_EQ(msg.command, 0);
+
+    // Message type is REQUEST and upper range of the field values
+    hdr.instance = 31;
+    hdr.pldm_type = 63;
+    hdr.command = 255;
+
+    rc = pack_pldm_header(&hdr, &msg);
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(msg.request, 1);
+    ASSERT_EQ(msg.datagram, 0);
+    ASSERT_EQ(msg.instance_id, 31);
+    ASSERT_EQ(msg.type, 63);
+    ASSERT_EQ(msg.command, 255);
+
+    // Message type is PLDM_ASYNC_REQUEST_NOTIFY
+    hdr.msg_type = PLDM_ASYNC_REQUEST_NOTIFY;
+
+    rc = pack_pldm_header(&hdr, &msg);
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(msg.request, 1);
+    ASSERT_EQ(msg.datagram, 1);
+    ASSERT_EQ(msg.instance_id, 31);
+    ASSERT_EQ(msg.type, 63);
+    ASSERT_EQ(msg.command, 255);
+}
+
+TEST(PackPLDMMessage, ResponseMessageGoodPath)
+{
+    struct pldm_header_info hdr;
+    pldm_msg_hdr msg{};
+
+    // Message type is PLDM_RESPONSE and lower range of the field values
+    hdr.msg_type = PLDM_RESPONSE;
+    hdr.instance = 0;
+    hdr.pldm_type = 0;
+    hdr.command = 0;
+
+    auto rc = pack_pldm_header(&hdr, &msg);
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(msg.request, 0);
+    ASSERT_EQ(msg.datagram, 0);
+    ASSERT_EQ(msg.instance_id, 0);
+    ASSERT_EQ(msg.type, 0);
+    ASSERT_EQ(msg.command, 0);
+
+    // Message type is PLDM_RESPONSE and upper range of the field values
+    hdr.instance = 31;
+    hdr.pldm_type = 63;
+    hdr.command = 255;
+
+    rc = pack_pldm_header(&hdr, &msg);
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(msg.request, 0);
+    ASSERT_EQ(msg.datagram, 0);
+    ASSERT_EQ(msg.instance_id, 31);
+    ASSERT_EQ(msg.type, 63);
+    ASSERT_EQ(msg.command, 255);
+}
+
+TEST(UnpackPLDMMessage, BadPathTest)
+{
+    struct pldm_header_info hdr;
+
+    // PLDM message pointer is NULL
+    auto rc = unpack_pldm_header(nullptr, &hdr);
+    ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+}
+
+TEST(UnpackPLDMMessage, RequestMessageGoodPath)
+{
+    struct pldm_header_info hdr;
+    pldm_msg_hdr msg{};
+
+    // Unpack PLDM request message and lower range of field values
+    msg.request = 1;
+    auto rc = unpack_pldm_header(&msg, &hdr);
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(hdr.msg_type, PLDM_REQUEST);
+    ASSERT_EQ(hdr.instance, 0);
+    ASSERT_EQ(hdr.pldm_type, 0);
+    ASSERT_EQ(hdr.command, 0);
+
+    // Unpack PLDM async request message and lower range of field values
+    msg.datagram = 1;
+    rc = unpack_pldm_header(&msg, &hdr);
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(hdr.msg_type, PLDM_ASYNC_REQUEST_NOTIFY);
+
+    // Unpack PLDM request message and upper range of field values
+    msg.datagram = 0;
+    msg.instance_id = 31;
+    msg.type = 63;
+    msg.command = 255;
+    rc = unpack_pldm_header(&msg, &hdr);
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(hdr.msg_type, PLDM_REQUEST);
+    ASSERT_EQ(hdr.instance, 31);
+    ASSERT_EQ(hdr.pldm_type, 63);
+    ASSERT_EQ(hdr.command, 255);
+}
+
+TEST(UnpackPLDMMessage, ResponseMessageGoodPath)
+{
+    struct pldm_header_info hdr;
+    pldm_msg_hdr msg{};
+
+    // Unpack PLDM response message and lower range of field values
+    auto rc = unpack_pldm_header(&msg, &hdr);
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(hdr.msg_type, PLDM_RESPONSE);
+    ASSERT_EQ(hdr.instance, 0);
+    ASSERT_EQ(hdr.pldm_type, 0);
+    ASSERT_EQ(hdr.command, 0);
+
+    // Unpack PLDM response message and upper range of field values
+    msg.instance_id = 31;
+    msg.type = 63;
+    msg.command = 255;
+    rc = unpack_pldm_header(&msg, &hdr);
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(hdr.msg_type, PLDM_RESPONSE);
+    ASSERT_EQ(hdr.instance, 31);
+    ASSERT_EQ(hdr.pldm_type, 63);
+    ASSERT_EQ(hdr.command, 255);
+}
+
 TEST(GetPLDMCommands, testEncodeRequest)
 {
     uint8_t pldmType = 0x05;