IPMI set CurrentHostState property

Register an IPMI handler to receive the command from BIC
netFn: 0x38 cmd: 0x0C

BIC sends the GPIO value for host power state to BMC
On: 0x1
Off: 0x0

This handler starts the corresponding target to set
"CurrentHostState" property
On: obmc-host-startmin@$slot.target
Off: obmc-host-stop@@$slot.target

Test Case:
Set "RequestedHostTransition" property on phosphor-state-manager
to On/Off/Reboot/ForceWarmReboot and check the host status

Signed-off-by: Bonnie Lo <Bonnie_Lo@wiwynn.com>
Change-Id: I891f38d7e836238fa3ba8287df5a855cf2d9a8d9
diff --git a/include/biccommands.hpp b/include/biccommands.hpp
index e8742d1..8702380 100644
--- a/include/biccommands.hpp
+++ b/include/biccommands.hpp
@@ -4,8 +4,19 @@
     CMD_OEM_BIC_INFO = 0x1,
     CMD_OEM_GET_BIC_GPIO_STATE = 0x3,
     CMD_OEM_SEND_POST_BUFFER_TO_BMC = 0x8,
+    CMD_OEM_SET_HOST_POWER_STATE = 0x0C,
 };
 
 const char* dbusObj = "/xyz/openbmc_project/state/boot/raw";
 
 const char* dbusService = "xyz.openbmc_project.State.Boot.Raw";
+
+constexpr auto systemdService = "org.freedesktop.systemd1";
+constexpr auto systemdObjPath = "/org/freedesktop/systemd1";
+constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
+
+enum class HostPowerState : uint8_t
+{
+    HOST_POWER_OFF = 0x0,
+    HOST_POWER_ON = 0x1,
+};
diff --git a/src/biccommands.cpp b/src/biccommands.cpp
index 5212452..13907e1 100644
--- a/src/biccommands.cpp
+++ b/src/biccommands.cpp
@@ -155,6 +155,57 @@
     return ipmi::responseSuccess(respIana, gpioState);
 }
 
+//----------------------------------------------------------------------
+// ipmiOemSetHostPowerState (CMD_OEM_SET_HOST_POWER_STATE)
+// This Function will handle BIC incomming IPMI request for
+// setting host current state for netfn=0x38 and cmd=0x0C
+// send the response back to the sender.
+//----------------------------------------------------------------------
+
+ipmi::RspType<IanaType> ipmiOemSetHostPowerState(ipmi::Context::ptr ctx,
+                                                 IanaType reqIana,
+                                                 uint8_t status)
+{
+    std::string targetUnit;
+
+    switch (static_cast<HostPowerState>(status))
+    {
+        case HostPowerState::HOST_POWER_ON:
+            targetUnit = "obmc-host-startmin@.target";
+            break;
+        case HostPowerState::HOST_POWER_OFF:
+            targetUnit = "obmc-host-stop@.target";
+            break;
+        default:
+            phosphor::logging::log<phosphor::logging::level::ERR>(
+                "IPMI ipmiOemHostPowerStatus power status error");
+            return ipmi::responseUnspecifiedError();
+    }
+
+    int mousePos = targetUnit.find('@');
+    targetUnit.insert(mousePos + 1, std::to_string(ctx->hostIdx + 1));
+
+    auto conn = getSdBus();
+    auto method = conn->new_method_call(systemdService, systemdObjPath,
+                                        systemdInterface, "StartUnit");
+    method.append(targetUnit);
+    method.append("replace");
+
+    try
+    {
+        conn->call_noreply(method);
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "IPMI ipmiOemHostPowerStatus Failed in call method",
+            phosphor::logging::entry("ERROR=%s", e.what()));
+        return ipmi::responseUnspecifiedError();
+    }
+
+    return ipmi::responseSuccess(reqIana);
+}
+
 [[maybe_unused]] static void registerBICFunctions(void)
 {
 
@@ -172,6 +223,10 @@
         ipmi::prioOemBase, ipmi::netFnOemFive,
         static_cast<Cmd>(fb_bic_cmds::CMD_OEM_GET_BIC_GPIO_STATE),
         ipmi::Privilege::User, ipmiOemGetBicGpioState);
+    ipmi::registerHandler(
+        ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
+        static_cast<Cmd>(fb_bic_cmds::CMD_OEM_SET_HOST_POWER_STATE),
+        ipmi::Privilege::User, ipmiOemSetHostPowerState);
     return;
 }