oemcommands: add https boot commands

1. netfn 0x30, cmd 0x57: GET_HTTPS_BOOT_DATA:
for BIOS to read cert data during POST

2. netfn 0x30, cmd 0x58: GET_HTTPS_BOOT_ATTR:
for BIOS to read cert info during POST
param 0: cert size
param 1: CRC32 of the cert

Change-Id: I258072916061f7859d101f2b4f8cdda018314b0c
Signed-off-by: Cosmo Chou <cosmo.chou@quantatw.com>
diff --git a/include/oemcommands.hpp b/include/oemcommands.hpp
index 21d10ae..3893bdf 100644
--- a/include/oemcommands.hpp
+++ b/include/oemcommands.hpp
@@ -49,6 +49,8 @@
     CMD_OEM_GET_80PORT_RECORD = 0x49,
     CMD_OEM_SET_BOOT_ORDER = 0x52,
     CMD_OEM_GET_BOOT_ORDER = 0x53,
+    CMD_OEM_GET_HTTPS_BOOT_DATA = 0x57,
+    CMD_OEM_GET_HTTPS_BOOT_ATTR = 0x58,
     CMD_OEM_SET_MACHINE_CONFIG_INFO = 0x6A,
     CMD_OEM_LEGACY_SET_PPR = 0x6E,
     CMD_OEM_LEGACY_GET_PPR = 0x6F,
@@ -243,6 +245,12 @@
     uint8_t data[];
 } qDriveInfo_t;
 
+enum class HttpsBootAttr : uint8_t
+{
+    certSize = 0x00,
+    certCrc = 0x01
+};
+
 enum class BankType : uint8_t
 {
     mca = 0x01,
@@ -283,6 +291,12 @@
 
 #pragma pack(push, 1)
 
+struct HttpsDataReq
+{
+    uint16_t offset;
+    uint8_t length;
+};
+
 struct CrdCmdHdr
 {
     uint8_t version;
diff --git a/src/oemcommands.cpp b/src/oemcommands.cpp
index c721f0d..6d9af87 100644
--- a/src/oemcommands.cpp
+++ b/src/oemcommands.cpp
@@ -17,6 +17,7 @@
 
 #include "xyz/openbmc_project/Common/error.hpp"
 
+#include <boost/crc.hpp>
 #include <commandutils.hpp>
 #include <ipmid/api-types.hpp>
 #include <ipmid/api.hpp>
@@ -71,6 +72,8 @@
 
 nlohmann::json oemData __attribute__((init_priority(101)));
 
+constexpr const char* certPath = "/mnt/data/host/bios-rootcert";
+
 static constexpr size_t GUID_SIZE = 16;
 // TODO Make offset and location runtime configurable to ensure we
 // can make each define their own locations.
@@ -2098,6 +2101,84 @@
     return sendDCMICmd(ctx, ipmi::dcmi::cmdActDeactivatePwrLimit, reqData);
 }
 
+// Https Boot related functions
+ipmi::RspType<std::vector<uint8_t>>
+    ipmiOemGetHttpsData([[maybe_unused]] ipmi::Context::ptr ctx,
+                        std::vector<uint8_t> reqData)
+{
+    if (reqData.size() < sizeof(HttpsDataReq))
+        return ipmi::responseReqDataLenInvalid();
+
+    const auto* pReq = reinterpret_cast<const HttpsDataReq*>(reqData.data());
+    std::error_code ec;
+    auto fileSize = std::filesystem::file_size(certPath, ec);
+    if (ec)
+        return ipmi::responseUnspecifiedError();
+
+    if (pReq->offset >= fileSize)
+        return ipmi::responseInvalidFieldRequest();
+
+    std::ifstream file(certPath, std::ios::binary);
+    if (!file)
+        return ipmi::responseUnspecifiedError();
+
+    auto readLen = std::min<uint16_t>(pReq->length, fileSize - pReq->offset);
+    std::vector<uint8_t> resData(readLen + 1);
+    resData[0] = readLen;
+    file.seekg(pReq->offset);
+    file.read(reinterpret_cast<char*>(resData.data() + 1), readLen);
+
+    return ipmi::responseSuccess(resData);
+}
+
+ipmi::RspType<std::vector<uint8_t>>
+    ipmiOemGetHttpsAttr([[maybe_unused]] ipmi::Context::ptr ctx,
+                        std::vector<uint8_t> reqData)
+{
+    if (reqData.size() < sizeof(HttpsBootAttr))
+        return ipmi::responseReqDataLenInvalid();
+
+    std::vector<uint8_t> resData;
+
+    switch (static_cast<HttpsBootAttr>(reqData[0]))
+    {
+        case HttpsBootAttr::certSize:
+        {
+            std::error_code ec;
+            auto fileSize = std::filesystem::file_size(certPath, ec);
+            if (ec || fileSize > std::numeric_limits<uint16_t>::max())
+                return ipmi::responseUnspecifiedError();
+
+            uint16_t size = static_cast<uint16_t>(fileSize);
+            resData.resize(sizeof(uint16_t));
+            std::memcpy(resData.data(), &size, sizeof(uint16_t));
+            break;
+        }
+        case HttpsBootAttr::certCrc:
+        {
+            std::ifstream file(certPath, std::ios::binary);
+            if (!file)
+                return ipmi::responseUnspecifiedError();
+
+            boost::crc_32_type result;
+            char data[1024];
+            while (file.read(data, sizeof(data)))
+                result.process_bytes(data, file.gcount());
+            if (file.gcount() > 0)
+                result.process_bytes(data, file.gcount());
+
+            uint32_t crc = result.checksum();
+            resData.resize(sizeof(uint32_t));
+            std::memcpy(resData.data(), &crc, sizeof(uint32_t));
+            break;
+        }
+        default:
+            return ipmi::responseInvalidFieldRequest();
+    }
+
+    return ipmi::responseSuccess(resData);
+}
+
 // OEM Crashdump related functions
 static ipmi_ret_t setDumpState(CrdState& currState, CrdState newState)
 {
@@ -2641,6 +2722,14 @@
                           ipmiOemSetBootOrder); // Set Boot Order
 
     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
+                          CMD_OEM_GET_HTTPS_BOOT_DATA, ipmi::Privilege::User,
+                          ipmiOemGetHttpsData);
+
+    ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
+                          CMD_OEM_GET_HTTPS_BOOT_ATTR, ipmi::Privilege::User,
+                          ipmiOemGetHttpsAttr);
+
+    ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemOne,
                           CMD_OEM_CRASHDUMP, ipmi::Privilege::User,
                           ipmiOemCrashdump);