IPMI OEM Get 80 Port Record

Register an IPMI command handler
NetFn: 0x30 Cmd: 0x49

This handler is for user from host to get POST code sequence.

The maximum return size is 224 due to IPMB limitation.

Test Case:
Call IPMI OEM get 80 port record

Signed-off-by: Bonnie Lo <Bonnie_Lo@wiwynn.com>
Change-Id: Iaf8c060b1e8340280928e195c205455b88df5dfd
diff --git a/meson.build b/meson.build
index 4736ea0..fccd790 100644
--- a/meson.build
+++ b/meson.build
@@ -34,6 +34,7 @@
 
 conf_data = configuration_data()
 conf_data.set_quoted('INSTANCES',host_instances)
+conf_data.set('POST_CODE_BYTES', get_option('post-code-bytes'))
 
 configure_file(input: 'meson_config.h.in',
                output: 'config.h',
diff --git a/meson_config.h.in b/meson_config.h.in
index ff7109c..6b8af72 100644
--- a/meson_config.h.in
+++ b/meson_config.h.in
@@ -1,3 +1,5 @@
 #pragma once
 
 inline static const std::string hostInstances = @INSTANCES@;
+
+inline static const size_t postCodeSize = @POST_CODE_BYTES@;
diff --git a/meson_options.txt b/meson_options.txt
index 5c9d771..7f150ab 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,5 +1,6 @@
 option('tests', type: 'feature', description: 'Build tests')
 option('bic', type: 'feature', description: 'Enable bic handlers')
+option('post-code-bytes', type: 'integer', description: 'Post code byte size.', value: 1)
 option(
   'host-instances',
   type: 'string',
diff --git a/src/oemcommands.cpp b/src/oemcommands.cpp
index ca56c45..d781964 100644
--- a/src/oemcommands.cpp
+++ b/src/oemcommands.cpp
@@ -819,6 +819,69 @@
     return IPMI_CC_OK;
 }
 
+//----------------------------------------------------------------------
+// Get port 80 record (CMD_OEM_GET_80PORT_RECORD)
+//----------------------------------------------------------------------
+ipmi::RspType<std::vector<uint8_t>>
+    ipmiOemGet80PortRecord(ipmi::Context::ptr ctx)
+{
+    auto postCodeService = "xyz.openbmc_project.State.Boot.PostCode" +
+                           std::to_string(ctx->hostIdx + 1);
+    auto postCodeObjPath = "/xyz/openbmc_project/State/Boot/PostCode" +
+                           std::to_string(ctx->hostIdx + 1);
+    constexpr auto postCodeInterface =
+        "xyz.openbmc_project.State.Boot.PostCode";
+    const static uint16_t lastestPostCodeIndex = 1;
+    constexpr const auto maxPostCodeLen =
+        224; // The length must be lower than IPMB limitation
+    size_t startIndex = 0;
+
+    std::vector<std::tuple<uint64_t, std::vector<uint8_t>>> postCodes;
+    std::vector<uint8_t> resData;
+
+    auto conn = getSdBus();
+    /* Get the post codes by calling GetPostCodes method */
+    auto msg =
+        conn->new_method_call(postCodeService.c_str(), postCodeObjPath.c_str(),
+                              postCodeInterface, "GetPostCodes");
+    msg.append(lastestPostCodeIndex);
+
+    try
+    {
+        auto reply = conn->call(msg);
+        reply.read(postCodes);
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "IPMI Get80PortRecord Failed in call method",
+            phosphor::logging::entry("ERROR=%s", e.what()));
+        return ipmi::responseUnspecifiedError();
+    }
+
+    /* Get post code data */
+    for (size_t i = 0; i < postCodes.size(); ++i)
+    {
+        uint64_t primaryPostCode = std::get<uint64_t>(postCodes[i]);
+        for (int j = postCodeSize - 1; j >= 0; --j)
+        {
+            uint8_t postCode =
+                ((primaryPostCode >> (sizeof(uint64_t) * j)) & 0xFF);
+            resData.emplace_back(postCode);
+        }
+    }
+
+    std::vector<uint8_t> response;
+    if (resData.size() > maxPostCodeLen)
+    {
+        startIndex = resData.size() - maxPostCodeLen;
+    }
+
+    response.assign(resData.begin() + startIndex, resData.end());
+
+    return ipmi::responseSuccess(response);
+}
+
 /* Helper functions to set boot order */
 void setBootOrder(std::string bootObjPath, uint8_t* data,
                   std::string bootOrderKey)
@@ -2057,6 +2120,9 @@
     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_GET_BOARD_ID, NULL,
                          ipmiOemGetBoardID,
                          PRIVILEGE_USER); // Get Board ID
+    ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnOemOne,
+                          CMD_OEM_GET_80PORT_RECORD, ipmi::Privilege::User,
+                          ipmiOemGet80PortRecord); // Get 80 Port Record
     ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_MACHINE_CONFIG_INFO, NULL,
                          ipmiOemSetMachineCfgInfo,
                          PRIVILEGE_USER); // Set Machine Config Info