chassishandler: Add support for boot type flag (Legacy/EFI)

Add support for IPMI boot type field of a
"Get System Boot Options" сommand:

[1] - BIOS boot type (for BIOS that support both legacy and EFI boots)
  0b = "PC compatible" boot (legacy)
  1b = Extensible Firmware Interface Boot (EFI)

For the architectures that don't implement Legacy/EFI boot type
property (for example POWER), always return EFI boot type.

Signed-off-by: Konstantin Aladyshev <aladyshev22@gmail.com>
Change-Id: Ifc052b988af86f02a7665ed0477b364c243e9f71
diff --git a/chassishandler.cpp b/chassishandler.cpp
index 4a3252a..db513ab 100644
--- a/chassishandler.cpp
+++ b/chassishandler.cpp
@@ -30,6 +30,7 @@
 #include <xyz/openbmc_project/Common/error.hpp>
 #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
 #include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
+#include <xyz/openbmc_project/Control/Boot/Type/server.hpp>
 #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
 #include <xyz/openbmc_project/State/Host/server.hpp>
 #include <xyz/openbmc_project/State/PowerOnHours/server.hpp>
@@ -39,9 +40,6 @@
 
 static ChassisIDState chassisIDState = ChassisIDState::reserved;
 static constexpr uint8_t setParmVersion = 0x01;
-static constexpr uint8_t setParmBootFlagsPermanent = 0x40;
-static constexpr uint8_t setParmBootFlagsValidOneTime = 0x80;
-static constexpr uint8_t setParmBootFlagsValidPermanent = 0xC0;
 
 constexpr size_t sizeVersion = 2;
 constexpr size_t DEFAULT_IDENTIFY_TIME_OUT = 15;
@@ -133,6 +131,7 @@
 {
 
 constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
+constexpr auto bootTypeIntf = "xyz.openbmc_project.Control.Boot.Type";
 constexpr auto bootSourceIntf = "xyz.openbmc_project.Control.Boot.Source";
 constexpr auto powerRestoreIntf =
     "xyz.openbmc_project.Control.Power.RestorePolicy";
@@ -148,8 +147,8 @@
     if (objectsPtr == nullptr)
     {
         objectsPtr = std::make_unique<settings::Objects>(
-            dbus, std::vector<std::string>{bootModeIntf, bootSourceIntf,
-                                           powerRestoreIntf});
+            dbus, std::vector<std::string>{bootModeIntf, bootTypeIntf,
+                                           bootSourceIntf, powerRestoreIntf});
     }
     return *objectsPtr;
 }
@@ -1587,6 +1586,9 @@
 using IpmiValue = uint8_t;
 constexpr auto ipmiDefault = 0;
 
+std::map<IpmiValue, Type::Types> typeIpmiToDbus = {{0x00, Type::Types::Legacy},
+                                                   {0x01, Type::Types::EFI}};
+
 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
     {0x01, Source::Sources::Network},
     {0x02, Source::Sources::Disk},
@@ -1601,6 +1603,9 @@
     {0x06, Mode::Modes::Setup},
     {ipmiDefault, Mode::Modes::Regular}};
 
+std::map<Type::Types, IpmiValue> typeDbusToIpmi = {{Type::Types::Legacy, 0x00},
+                                                   {Type::Types::EFI, 0x01}};
+
 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
     {Source::Sources::Network, 0x01},
     {Source::Sources::Disk, 0x02},
@@ -1669,6 +1674,44 @@
     return ipmi::ccSuccess;
 }
 
+/** @brief Set the property value for boot type
+ *  @param[in] type - boot type value
+ *  @return On failure return IPMI error.
+ */
+static ipmi::Cc setBootType(const Type::Types& type)
+{
+    using namespace chassis::internal;
+    using namespace chassis::internal::cache;
+    std::variant<std::string> property = convertForMessage(type);
+    settings::Objects& objects = getObjects();
+    std::tuple<settings::Path, settings::boot::OneTimeEnabled> bootSetting;
+    try
+    {
+        bootSetting = settings::boot::setting(objects, bootTypeIntf);
+    }
+    catch (const std::exception& e)
+    {
+        // Return immediately if BootType interface is not present.
+        // This interface is not relevant for some Host architectures
+        // (for example POWER). In this case we don't won't IPMI to
+        // return an error, but want to just skip this function.
+        return ipmi::ccSuccess;
+    }
+    const auto& bootTypeSetting = std::get<settings::Path>(bootSetting);
+    auto method = dbus.new_method_call(
+        objects.service(bootTypeSetting, bootTypeIntf).c_str(),
+        bootTypeSetting.c_str(), ipmi::PROP_INTF, "Set");
+    method.append(bootTypeIntf, "BootType", property);
+    auto reply = dbus.call(method);
+    if (reply.is_method_error())
+    {
+        log<level::ERR>("Error in BootType Set");
+        report<InternalFailure>();
+        return ipmi::ccUnspecifiedError;
+    }
+    return ipmi::ccSuccess;
+}
+
 static constexpr uint8_t setComplete = 0x0;
 static constexpr uint8_t setInProgress = 0x1;
 static uint8_t transferStatus = setComplete;
@@ -1758,6 +1801,45 @@
             auto bootSource =
                 Source::convertSourcesFromString(std::get<std::string>(result));
 
+            Type::Types bootType;
+            bool bootTypeIntfPresent = true;
+            try
+            {
+                bootSetting = settings::boot::setting(objects, bootTypeIntf);
+            }
+            catch (const std::exception& e)
+            {
+                bootTypeIntfPresent = false;
+            }
+
+            if (bootTypeIntfPresent)
+            {
+                const auto& bootTypeSetting =
+                    std::get<settings::Path>(bootSetting);
+                method = dbus.new_method_call(
+                    objects.service(bootTypeSetting, bootTypeIntf).c_str(),
+                    bootTypeSetting.c_str(), ipmi::PROP_INTF, "Get");
+                method.append(bootTypeIntf, "BootType");
+                reply = dbus.call(method);
+                if (reply.is_method_error())
+                {
+                    log<level::ERR>(
+                        "ipmiChassisGetSysBootOptions: Error in BootType Get");
+                    report<InternalFailure>();
+                    return ipmi::responseUnspecifiedError();
+                }
+                else
+                {
+                    reply.read(result);
+                    bootType = Type::convertTypesFromString(
+                        std::get<std::string>(result));
+                }
+            }
+            else
+            {
+                bootType = Type::Types::EFI;
+            }
+
             bootSetting = settings::boot::setting(objects, bootModeIntf);
             const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
             method = dbus.new_method_call(
@@ -1787,12 +1869,14 @@
                 bootOption = modeDbusToIpmi.at(bootMode);
             }
 
-            uint8_t bootOptionParam = oneTimeEnabled
-                                          ? setParmBootFlagsValidOneTime
-                                          : setParmBootFlagsValidPermanent;
-            response.pack(bootOptionParameter, reserved1, bootOptionParam,
-                          uint2_t{}, uint4_t{bootOption}, uint2_t{}, uint8_t{},
-                          uint8_t{}, uint8_t{});
+            IpmiValue biosBootType = typeDbusToIpmi.at(bootType);
+            uint1_t permanent = oneTimeEnabled ? 0 : 1;
+            uint1_t validFlag = 1;
+
+            response.pack(bootOptionParameter, reserved1, uint5_t{},
+                          uint1_t{biosBootType}, uint1_t{permanent},
+                          uint1_t{validFlag}, uint2_t{}, uint4_t{bootOption},
+                          uint2_t{}, uint8_t{}, uint8_t{}, uint8_t{});
             return ipmi::responseSuccess(std::move(response));
         }
         catch (InternalFailure& e)
@@ -1938,6 +2022,8 @@
 
             auto modeItr =
                 modeIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector));
+            auto typeItr =
+                typeIpmiToDbus.find(static_cast<uint8_t>(biosBootType));
             auto sourceItr =
                 sourceIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector));
             if (sourceIpmiToDbus.end() != sourceItr)
@@ -1958,6 +2044,18 @@
                     setBootMode(Mode::Modes::Regular);
                 }
             }
+
+            if (typeIpmiToDbus.end() != typeItr)
+            {
+                rc = setBootType(typeItr->second);
+                if (rc != ipmi::ccSuccess)
+                {
+                    log<level::ERR>("ipmiChassisSetSysBootOptions: Error in "
+                                    "setting boot type");
+                    return ipmi::responseUnspecifiedError();
+                }
+            }
+
             if (modeIpmiToDbus.end() != modeItr)
             {
                 rc = setBootMode(modeItr->second);
@@ -1977,6 +2075,7 @@
                 }
             }
             if ((modeIpmiToDbus.end() == modeItr) &&
+                (typeIpmiToDbus.end() == typeItr) &&
                 (sourceIpmiToDbus.end() == sourceItr))
             {
                 // return error if boot option is not supported