oem-ibm: Support system reboot after inband code update

This commit adds a bmc effecter that causes a system reboot(
phyp, chassis and bmc)

After bmc sends an end update successful event, Host will
set this new effecter. Upon getting the setStateEffecterStates
call bmc-pldm does the following:
1. Set the power restore to reboot the host once the BMC
   is rebooted
2. power off Host and Chassis
3. reboot the bmc

Tested with pldmtool
Steps after patching changes:
1. Boot host to STANDBY
2. Fetch the effecter to note the effecterId
   Ex: ./pldmtool platform GetPDR -d 28
3. Trigger the system reboot by toggling the effecter
   ./pldmtool platform SetStateEffecterStates -i 10 -c 1
   -d 1 1

Signed-off-by: Sagar Srinivas <sagar.srinivas@ibm.com>
Change-Id: Id2d4201a7d61ae335adc64f2583571a37dc7879d
diff --git a/oem/ibm/libpldmresponder/oem_ibm_handler.cpp b/oem/ibm/libpldmresponder/oem_ibm_handler.cpp
index 2f06bc5..43e0de4 100644
--- a/oem/ibm/libpldmresponder/oem_ibm_handler.cpp
+++ b/oem/ibm/libpldmresponder/oem_ibm_handler.cpp
@@ -120,6 +120,19 @@
                     // sendCodeUpdateEvent(effecterId, REJECT, END);
                 }
             }
+            else if (entityType == PLDM_ENTITY_SYSTEM_CHASSIS &&
+                     stateSetId == PLDM_OEM_IBM_SYSTEM_POWER_STATE)
+            {
+                if (stateField[currState].effecter_state == POWER_CYCLE_HARD)
+                {
+                    systemRebootEvent =
+                        std::make_unique<sdeventplus::source::Defer>(
+                            event,
+                            std::bind(std::mem_fn(&oem_ibm_platform::Handler::
+                                                      _processSystemReboot),
+                                      this, std::placeholders::_1));
+                }
+            }
             else
             {
                 rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
@@ -134,8 +147,8 @@
 }
 
 void buildAllCodeUpdateEffecterPDR(platform::Handler* platformHandler,
-                                   uint16_t entityInstance, uint16_t stateSetID,
-                                   pdr_utils::Repo& repo)
+                                   uint16_t entityType, uint16_t entityInstance,
+                                   uint16_t stateSetID, pdr_utils::Repo& repo)
 {
     size_t pdrSize = 0;
     pdrSize = sizeof(pldm_state_effecter_pdr) +
@@ -156,7 +169,7 @@
     pdr->hdr.length = sizeof(pldm_state_effecter_pdr) - sizeof(pldm_pdr_hdr);
     pdr->terminus_handle = pdr::BmcPldmTerminusHandle;
     pdr->effecter_id = platformHandler->getNextEffecterId();
-    pdr->entity_type = PLDM_OEM_IBM_ENTITY_FIRMWARE_UPDATE;
+    pdr->entity_type = entityType;
     pdr->entity_instance = entityInstance;
     pdr->container_id = 0;
     pdr->effecter_semantic_id = 0;
@@ -175,6 +188,8 @@
         state->states[0].byte = 6;
     else if (stateSetID == PLDM_OEM_IBM_FIRMWARE_UPDATE_STATE)
         state->states[0].byte = 126;
+    else if (stateSetID == PLDM_OEM_IBM_SYSTEM_POWER_STATE)
+        state->states[0].byte = 2;
     pldm::responder::pdr_utils::PdrEntry pdrEntry{};
     pdrEntry.data = entry.data();
     pdrEntry.size = pdrSize;
@@ -232,12 +247,18 @@
 void pldm::responder::oem_ibm_platform::Handler::buildOEMPDR(
     pdr_utils::Repo& repo)
 {
-    buildAllCodeUpdateEffecterPDR(platformHandler, ENTITY_INSTANCE_0,
-                                  PLDM_OEM_IBM_BOOT_STATE, repo);
-    buildAllCodeUpdateEffecterPDR(platformHandler, ENTITY_INSTANCE_1,
-                                  PLDM_OEM_IBM_BOOT_STATE, repo);
-    buildAllCodeUpdateEffecterPDR(platformHandler, ENTITY_INSTANCE_0,
-                                  PLDM_OEM_IBM_FIRMWARE_UPDATE_STATE, repo);
+    buildAllCodeUpdateEffecterPDR(
+        platformHandler, PLDM_OEM_IBM_ENTITY_FIRMWARE_UPDATE, ENTITY_INSTANCE_0,
+        PLDM_OEM_IBM_BOOT_STATE, repo);
+    buildAllCodeUpdateEffecterPDR(
+        platformHandler, PLDM_OEM_IBM_ENTITY_FIRMWARE_UPDATE, ENTITY_INSTANCE_1,
+        PLDM_OEM_IBM_BOOT_STATE, repo);
+    buildAllCodeUpdateEffecterPDR(
+        platformHandler, PLDM_OEM_IBM_ENTITY_FIRMWARE_UPDATE, ENTITY_INSTANCE_0,
+        PLDM_OEM_IBM_FIRMWARE_UPDATE_STATE, repo);
+    buildAllCodeUpdateEffecterPDR(platformHandler, PLDM_ENTITY_SYSTEM_CHASSIS,
+                                  ENTITY_INSTANCE_0,
+                                  PLDM_OEM_IBM_SYSTEM_POWER_STATE, repo);
 
     buildAllCodeUpdateSensorPDR(
         platformHandler, PLDM_OEM_IBM_ENTITY_FIRMWARE_UPDATE, ENTITY_INSTANCE_0,
@@ -329,7 +350,6 @@
     uint16_t sensorId, enum sensor_event_class_states sensorEventClass,
     uint8_t sensorOffset, uint8_t eventState, uint8_t prevEventState)
 {
-
     std::vector<uint8_t> sensorEventDataVec{};
     size_t sensorEventSize = PLDM_SENSOR_EVENT_DATA_MIN_LENGTH + 1;
     sensorEventDataVec.resize(sensorEventSize);
@@ -397,6 +417,80 @@
                          uint8_t(CodeUpdateState::END));
 }
 
+void pldm::responder::oem_ibm_platform::Handler::_processSystemReboot(
+    sdeventplus::source::EventBase& /*source */)
+{
+    pldm::utils::PropertyValue value =
+        "xyz.openbmc_project.State.Chassis.Transition.Off";
+    pldm::utils::DBusMapping dbusMapping{"/xyz/openbmc_project/state/chassis0",
+                                         "xyz.openbmc_project.State.Chassis",
+                                         "RequestedPowerTransition", "string"};
+    try
+    {
+        dBusIntf->setDbusProperty(dbusMapping, value);
+    }
+    catch (const std::exception& e)
+    {
+
+        std::cerr << "Chassis State transition to Off failed,"
+                  << "unable to set property RequestedPowerTransition"
+                  << "ERROR=" << e.what() << "\n";
+    }
+
+    using namespace sdbusplus::bus::match::rules;
+    chassisOffMatch = std::make_unique<sdbusplus::bus::match::match>(
+        pldm::utils::DBusHandler::getBus(),
+        propertiesChanged("/xyz/openbmc_project/state/chassis0",
+                          "xyz.openbmc_project.State.Chassis"),
+        [this](sdbusplus::message::message& msg) {
+            DbusChangedProps props{};
+            std::string intf;
+            msg.read(intf, props);
+            const auto itr = props.find("CurrentPowerState");
+            if (itr != props.end())
+            {
+                PropertyValue value = itr->second;
+                auto propVal = std::get<std::string>(value);
+                if (propVal ==
+                    "xyz.openbmc_project.State.Chassis.PowerState.Off")
+                {
+                    pldm::utils::DBusMapping dbusMapping{
+                        "/xyz/openbmc_project/control/host0/"
+                        "power_restore_policy/one_time",
+                        "xyz.openbmc_project.Control.Power.RestorePolicy",
+                        "PowerRestorePolicy", "string"};
+                    value = "xyz.openbmc_project.Control.Power.RestorePolicy."
+                            "Policy.AlwaysOn";
+                    try
+                    {
+                        dBusIntf->setDbusProperty(dbusMapping, value);
+                    }
+                    catch (const std::exception& e)
+                    {
+                        std::cerr << "Setting one-time restore policy failed,"
+                                  << "unable to set property PowerRestorePolicy"
+                                  << "ERROR=" << e.what() << "\n";
+                    }
+                    dbusMapping = pldm::utils::DBusMapping{
+                        "/xyz/openbmc_project/state/bmc0",
+                        "xyz.openbmc_project.State.BMC",
+                        "RequestedBMCTransition", "string"};
+                    value = "xyz.openbmc_project.State.BMC.Transition.Reboot";
+                    try
+                    {
+                        dBusIntf->setDbusProperty(dbusMapping, value);
+                    }
+                    catch (const std::exception& e)
+                    {
+                        std::cerr << "BMC state transition to reboot failed,"
+                                  << "unable to set property "
+                                     "RequestedBMCTransition"
+                                  << "ERROR=" << e.what() << "\n";
+                    }
+                }
+            }
+        });
+}
 } // namespace oem_ibm_platform
 } // namespace responder
 } // namespace pldm