chassishandler: GetSystemBootOptions to new API

Rewrite ipmiChassisGetSytemBootOptions to use newly introduced
IPMI provider API

Tested:
Verified using ipmi chassis bootparam command

ipmitool chassis bootparam get 5 //command

//response
Boot parameter version: 1
Boot parameter 5 is valid/unlocked
Boot parameter data: 8000000000
 Boot Flags :
   - Boot Flag Valid
   - Options apply to only next boot
   - BIOS PC Compatible (legacy) boot
   - Boot Device Selector : No override
   - BIOS verbosity : System Default
   - Console Redirection control : Console redirection occurs per BIOS
                                   configuration setting (default)
   - BIOS Mux Control Override : BIOS uses recommended setting of the
                                 mux at the end of POST

Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com>
Signed-off-by: srikanta mondal <srikantax.mondal@intel.com>
Change-Id: Iaf4c5cb61f6142b2112ad9c4d927b898ef94cb14
diff --git a/chassishandler.cpp b/chassishandler.cpp
index 28cb756..955e0a1 100644
--- a/chassishandler.cpp
+++ b/chassishandler.cpp
@@ -43,9 +43,6 @@
 static constexpr uint8_t setParmBootFlagsValidOneTime = 0x80;
 static constexpr uint8_t setParmBootFlagsValidPermanent = 0xC0;
 
-constexpr size_t SIZE_BOOT_OPTION = static_cast<size_t>(
-    BootOptionResponseSize::opalNetworkSettings); // Maximum size of the boot
-                                                  // option parameters
 constexpr size_t sizeVersion = 2;
 constexpr size_t DEFAULT_IDENTIFY_TIME_OUT = 15;
 
@@ -171,21 +168,7 @@
 
 } // namespace poh
 
-struct get_sys_boot_options_t
-{
-    uint8_t parameter;
-    uint8_t set;
-    uint8_t block;
-} __attribute__((packed));
-
-struct get_sys_boot_options_response_t
-{
-    uint8_t version;
-    uint8_t parm;
-    uint8_t data[SIZE_BOOT_OPTION];
-} __attribute__((packed));
-
-int getHostNetworkData(get_sys_boot_options_response_t* respptr)
+int getHostNetworkData(ipmi::message::Payload& payload)
 {
     ipmi::PropertyMap properties;
     int rc = 0;
@@ -237,7 +220,6 @@
         // don't send blank override.
         if ((MACAddress == ipmi::network::DEFAULT_MAC_ADDRESS))
         {
-            std::memset(respptr->data, 0, SIZE_BOOT_OPTION);
             rc = -1;
             return rc;
         }
@@ -248,22 +230,27 @@
             if ((ipAddress == ipmi::network::DEFAULT_ADDRESS) ||
                 (gateway == ipmi::network::DEFAULT_ADDRESS) || (!prefix))
             {
-                std::memset(respptr->data, 0, SIZE_BOOT_OPTION);
                 rc = -1;
                 return rc;
             }
         }
 
-        sscanf(MACAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
-               (respptr->data + macOffset), (respptr->data + macOffset + 1),
-               (respptr->data + macOffset + 2), (respptr->data + macOffset + 3),
-               (respptr->data + macOffset + 4),
-               (respptr->data + macOffset + 5));
+        std::string token;
+        std::stringstream ss(MACAddress);
 
-        respptr->data[macOffset + 6] = 0x00;
+        // First pack macOffset no of bytes in payload.
+        // Latter this PetiBoot-Specific data will be populated.
+        std::vector<uint8_t> payloadInitialBytes(macOffset);
+        payload.pack(payloadInitialBytes);
 
-        std::memcpy(respptr->data + addrTypeOffset, &isStatic,
-                    sizeof(isStatic));
+        while (std::getline(ss, token, ':'))
+        {
+            payload.pack(stoi(token, nullptr, 16));
+        }
+
+        payload.pack(0x00);
+
+        payload.pack(isStatic);
 
         uint8_t addressFamily = (std::get<std::string>(properties["Type"]) ==
                                  "xyz.openbmc_project.Network.IP.Protocol.IPv4")
@@ -275,39 +262,58 @@
                        : ipmi::network::IPV6_ADDRESS_SIZE_BYTE;
 
         // ipaddress and gateway would be in IPv4 format
+        std::vector<uint8_t> addrInBinary(addrSize);
         inet_pton(addressFamily, ipAddress.c_str(),
-                  (respptr->data + ipAddrOffset));
+                  reinterpret_cast<void*>(addrInBinary.data()));
 
-        uint8_t prefixOffset = ipAddrOffset + addrSize;
+        payload.pack(addrInBinary);
 
-        std::memcpy(respptr->data + prefixOffset, &prefix, sizeof(prefix));
+        payload.pack(prefix);
 
-        uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix));
-
+        std::vector<uint8_t> gatewayDetails(addrSize);
         inet_pton(addressFamily, gateway.c_str(),
-                  (respptr->data + gatewayOffset));
+                  reinterpret_cast<void*>(gatewayDetails.data()));
+        payload.pack(gatewayDetails);
     }
     catch (InternalFailure& e)
     {
         commit<InternalFailure>();
-        std::memset(respptr->data, 0, SIZE_BOOT_OPTION);
         rc = -1;
         return rc;
     }
 
     // PetiBoot-Specific
-    // If success then copy the first 9 bytes to the data
-    std::memcpy(respptr->data, netConfInitialBytes,
-                sizeof(netConfInitialBytes));
+    // If success then copy the first 9 bytes to the payload message
+    // payload first 2 bytes contain the parameter values. Skip that 2 bytes.
+    uint8_t skipFirstTwoBytes = 2;
+    size_t payloadSize = payload.size();
+    uint8_t* configDataStartingAddress = payload.data() + skipFirstTwoBytes;
 
-    std::memcpy(respptr->data + addrSizeOffset, &addrSize, sizeof(addrSize));
+    if (payloadSize < skipFirstTwoBytes + sizeof(netConfInitialBytes))
+    {
+        log<level::ERR>("Invalid net config ");
+        rc = -1;
+        return rc;
+    }
+    std::copy(netConfInitialBytes,
+              netConfInitialBytes + sizeof(netConfInitialBytes),
+              configDataStartingAddress);
+
+    if (payloadSize < skipFirstTwoBytes + addrSizeOffset + sizeof(addrSize))
+    {
+        log<level::ERR>("Invalid length of address size");
+        rc = -1;
+        return rc;
+    }
+    std::copy(&addrSize, &(addrSize) + sizeof(addrSize),
+              configDataStartingAddress + addrSizeOffset);
 
 #ifdef _IPMI_DEBUG_
     std::printf("\n===Printing the IPMI Formatted Data========\n");
 
     for (uint8_t pos = 0; pos < index; pos++)
     {
-        std::printf("%02x ", respptr->data[pos]);
+        std::printf("%02x ", payloadStartingAddress[pos]);
     }
 #endif
 
@@ -1664,33 +1670,47 @@
     return ipmi::ccSuccess;
 }
 
-ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
-                                             ipmi_request_t request,
-                                             ipmi_response_t response,
-                                             ipmi_data_len_t data_len,
-                                             ipmi_context_t context)
+/** @brief implements the Get Chassis system boot option
+ *  @param bootOptionParameter   - boot option parameter selector
+ *  @param reserved1    - reserved bit
+ *  @param setSelector  - selects a particular block or set of parameters
+ *                        under the given parameter selector
+ *                        write as 00h if parameter doesn't use a setSelector
+ *  @param blockSelector- selects a particular block within a set of
+ *                        parameters write as 00h if parameter doesn't use a
+ *                        blockSelector
+ *
+ *  @return IPMI completion code plus response data
+ *  @return Payload contains below parameters:
+ *   version             - parameter version
+ *   bootOptionParameter - boot option parameter selector
+ *   parmIndicator - parameter vaild/invaild indicator
+ *   data          - configuration parameter data
+ */
+ipmi::RspType<ipmi::message::Payload>
+    ipmiChassisGetSysBootOptions(uint7_t bootOptionParameter, bool reserved1,
+
+                                 uint8_t setSelector, uint8_t blockSelector)
 {
+    if (reserved1)
+    {
+        return ipmi::responseInvalidFieldRequest();
+    }
+
+    constexpr uint4_t version = 0x01;
+    ipmi::message::Payload response;
+    response.pack(version, uint4_t{});
     using namespace boot_options;
-    ipmi_ret_t rc = IPMI_CC_PARM_NOT_SUPPORTED;
-    char* p = NULL;
-    get_sys_boot_options_response_t* resp =
-        (get_sys_boot_options_response_t*)response;
-    get_sys_boot_options_t* reqptr = (get_sys_boot_options_t*)request;
+
     IpmiValue bootOption = ipmiDefault;
 
-    std::memset(resp, 0, sizeof(*resp));
-    resp->version = setParmVersion;
-    resp->parm = 5;
-    resp->data[0] = setParmBootFlagsValidOneTime;
     /*
      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
      * This is the only parameter used by petitboot.
      */
-    if (reqptr->parameter ==
+    if (static_cast<uint8_t>(bootOptionParameter) ==
         static_cast<uint8_t>(BootOptionParameter::bootFlags))
     {
-
-        *data_len = static_cast<uint8_t>(BootOptionResponseSize::bootFlags);
         using namespace chassis::internal;
         using namespace chassis::internal::cache;
 
@@ -1709,10 +1729,10 @@
             auto reply = dbus.call(method);
             if (reply.is_method_error())
             {
-                log<level::ERR>("Error in BootSource Get");
+                log<level::ERR>(
+                    "ipmiChassisGetSysBootOptions: Error in BootSource Get");
                 report<InternalFailure>();
-                *data_len = 0;
-                return IPMI_CC_UNSPECIFIED_ERROR;
+                return ipmi::responseUnspecifiedError();
             }
             std::variant<std::string> result;
             reply.read(result);
@@ -1728,10 +1748,10 @@
             reply = dbus.call(method);
             if (reply.is_method_error())
             {
-                log<level::ERR>("Error in BootMode Get");
+                log<level::ERR>(
+                    "ipmiChassisGetSysBootOptions: Error in BootMode Get");
                 report<InternalFailure>();
-                *data_len = 0;
-                return IPMI_CC_UNSPECIFIED_ERROR;
+                return ipmi::responseUnspecifiedError();
             }
             reply.read(result);
             auto bootMode =
@@ -1747,59 +1767,54 @@
             {
                 bootOption = modeDbusToIpmi.at(bootMode);
             }
-            resp->data[1] = (bootOption << 2);
 
-            resp->data[0] = oneTimeEnabled ? setParmBootFlagsValidOneTime
-                                           : setParmBootFlagsValidPermanent;
-
-            rc = IPMI_CC_OK;
+            uint8_t bootOptionParam = oneTimeEnabled
+                                          ? setParmBootFlagsValidOneTime
+                                          : setParmBootFlagsValidPermanent;
+            response.pack(bootOptionParameter, reserved1, bootOptionParam,
+                          uint2_t{}, uint4_t{bootOption}, uint2_t{}, uint8_t{},
+                          uint8_t{}, uint8_t{});
+            return ipmi::responseSuccess(std::move(response));
         }
         catch (InternalFailure& e)
         {
             cache::objectsPtr.reset();
             report<InternalFailure>();
-            *data_len = 0;
-            return IPMI_CC_UNSPECIFIED_ERROR;
+            return ipmi::responseUnspecifiedError();
         }
     }
-    else if (reqptr->parameter ==
-             static_cast<uint8_t>(BootOptionParameter::opalNetworkSettings))
-    {
-
-        *data_len =
-            static_cast<uint8_t>(BootOptionResponseSize::opalNetworkSettings);
-
-        resp->parm =
-            static_cast<uint8_t>(BootOptionParameter::opalNetworkSettings);
-
-        int ret = getHostNetworkData(resp);
-
-        if (ret < 0)
-        {
-
-            log<level::ERR>(
-                "getHostNetworkData failed for get_sys_boot_options.");
-            rc = IPMI_CC_UNSPECIFIED_ERROR;
-        }
-        else
-            rc = IPMI_CC_OK;
-    }
-
     else
     {
-        log<level::ERR>("Unsupported parameter",
-                        entry("PARAM=0x%x", reqptr->parameter));
+        if ((bootOptionParameter >= oemParmStart) &&
+            (bootOptionParameter <= oemParmEnd))
+        {
+            if (static_cast<uint8_t>(bootOptionParameter) ==
+                static_cast<uint8_t>(BootOptionParameter::opalNetworkSettings))
+            {
+                response.pack(bootOptionParameter, reserved1);
+                int ret = getHostNetworkData(response);
+                if (ret < 0)
+                {
+                    response.trailingOk = true;
+                    log<level::ERR>(
+                        "getHostNetworkData failed for GetSysBootOptions.");
+                    return ipmi::responseUnspecifiedError();
+                }
+                else
+                {
+                    return ipmi::responseSuccess(std::move(response));
+                }
+            }
+        }
+        else
+        {
+            log<level::ERR>(
+                "ipmiChassisGetSysBootOptions: Unsupported parameter",
+                entry("PARAM=0x%x", static_cast<uint8_t>(bootOptionParameter)));
+            return ipmi::responseUnspecifiedError();
+        }
     }
-
-    if (p)
-        free(p);
-
-    if (rc == IPMI_CC_OK)
-    {
-        *data_len += 2;
-    }
-
-    return rc;
+    return ipmi::responseUnspecifiedError();
 }
 
 ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx,
@@ -2133,9 +2148,10 @@
                           ipmi::Privilege::User, ipmiSetChassisCap);
 
     // <Get System Boot Options>
-    ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL,
-                           ipmi_chassis_get_sys_boot_options,
-                           PRIVILEGE_OPERATOR);
+    ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
+                          ipmi::chassis::cmdGetSystemBootOptions,
+                          ipmi::Privilege::Operator,
+                          ipmiChassisGetSysBootOptions);
 
     // <Get Chassis Status>
     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,