groupextcommands: Add SBMR send boot progress command

Add send boot progress command for postcode recording.
NetFn: 0x2C, Cmd: 0x02, Group: 0xAE (SBMR)

Tested Result:
```
root@bmc:~# ipmitool raw 0x2C 0x02 0xAE 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09
 ae
root@bmc:~# ipmitool raw 0x2C 0x02 0xAE 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19
 ae
root@bmc:~# ipmitool raw 0x2C 0x02 0xAE 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29
 ae
root@bmc:~# busctl call xyz.openbmc_project.State.Boot.PostCode0 /xyz/openbmc_project/State/Boot/PostCode0 xyz.openbmc_project.State.Boot.PostCode GetPostCodes q 1 -j
{
        "type" : "a(tay)",
        "data" : [
                [
                        [
                                72623859790382856,
                                [
                                        1,
                                        2,
                                        3,
                                        4,
                                        5,
                                        6,
                                        7,
                                        8,
                                        9
                                ]
                        ],
                        [
                                1230066625199609624,
                                [
                                        17,
                                        18,
                                        19,
                                        20,
                                        21,
                                        22,
                                        23,
                                        24,
                                        25
                                ]
                        ],
                        [
                                2387509390608836392,
                                [
                                        33,
                                        34,
                                        35,
                                        36,
                                        37,
                                        38,
                                        39,
                                        40,
                                        41
                                ]
                        ]
                ]
        ]
}
```

Change-Id: If064fe1300c63e334022a54054958a000200d9d8
Signed-off-by: Potin Lai <potin.lai@quantatw.com>
diff --git a/src/groupextcommands.cpp b/src/groupextcommands.cpp
new file mode 100644
index 0000000..54a3fc5
--- /dev/null
+++ b/src/groupextcommands.cpp
@@ -0,0 +1,76 @@
+#include <commandutils.hpp>
+#include <groupextcommands.hpp>
+#include <ipmid/api-types.hpp>
+#include <ipmid/api.hpp>
+#include <phosphor-logging/lg2.hpp>
+
+namespace ipmi
+{
+
+PHOSPHOR_LOG2_USING;
+
+uint64_t bigEndianToHost(uint64_t bigEndianValue)
+{
+    if (std::endian::native == std::endian::little)
+    {
+        return std::byteswap(bigEndianValue);
+    }
+
+    return bigEndianValue;
+}
+
+void registerSBMRFunctions() __attribute__((constructor));
+
+ipmi::RspType<> ipmiSBMRSendBootProgress(ipmi::Context::ptr ctx,
+                                         std::vector<uint8_t> data)
+{
+    using postcode_t = std::tuple<uint64_t, std::vector<uint8_t>>;
+
+    std::optional<size_t> hostId = findHost(ctx->hostIdx);
+
+    if (!hostId)
+    {
+        error("Invalid Host Id received");
+        return ipmi::responseInvalidCommand();
+    }
+
+    if (data.size() != 9)
+    {
+        error("Invalid request of boot progress length received: {LENGTH}",
+              "LENGTH", data.size());
+        return ipmi::responseReqDataLenInvalid();
+    }
+
+    try
+    {
+        auto primaryPostCode = reinterpret_cast<const uint64_t*>(data.data());
+        auto postCode = postcode_t(bigEndianToHost(*primaryPostCode), data);
+        auto conn = getSdBus();
+        auto hostbootRawObj = std::string(bootRawObjPrefix) +
+                              std::to_string(*hostId);
+        auto method =
+            conn->new_method_call(bootRawBusName, hostbootRawObj.data(),
+                                  "org.freedesktop.DBus.Properties", "Set");
+
+        method.append(bootRawIntf, "Value", std::variant<postcode_t>(postCode));
+
+        conn->call_noreply(method);
+    }
+    catch (std::exception& e)
+    {
+        error("postcode handler error: {ERROR}", "ERROR", e);
+        return ipmi::responseResponseError();
+    }
+
+    return ipmi::responseSuccess();
+}
+
+void registerSBMRFunctions()
+{
+    ipmi::registerGroupHandler(
+        ipmi::prioOemBase, ipmi::groupSBMR, ipmi::sbmr::cmdSendBootProgress,
+        ipmi::Privilege::Admin, ipmiSBMRSendBootProgress);
+    return;
+}
+
+} // end of namespace ipmi