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/file_io_type_lid.hpp b/oem/ibm/libpldmresponder/file_io_type_lid.hpp
index e5e5b5f..841099c 100644
--- a/oem/ibm/libpldmresponder/file_io_type_lid.hpp
+++ b/oem/ibm/libpldmresponder/file_io_type_lid.hpp
@@ -125,7 +125,7 @@
         {
             flags = O_WRONLY | O_CREAT | O_TRUNC | O_SYNC;
         }
-        auto fd = open(lidPath.c_str(), flags);
+        auto fd = open(lidPath.c_str(), flags, S_IRUSR);
         if (fd == -1)
         {
             std::cerr << "Could not open file for writing  " << lidPath.c_str()
@@ -219,7 +219,7 @@
                 return PLDM_DATA_OUT_OF_RANGE;
             }
         }
-        auto fd = open(lidPath.c_str(), flags);
+        auto fd = open(lidPath.c_str(), flags, S_IRUSR);
         if (fd == -1)
         {
             std::cerr << "could not open file " << lidPath.c_str() << "\n";
diff --git a/oem/ibm/libpldmresponder/inband_code_update.cpp b/oem/ibm/libpldmresponder/inband_code_update.cpp
index 723caad..27ce945 100644
--- a/oem/ibm/libpldmresponder/inband_code_update.cpp
+++ b/oem/ibm/libpldmresponder/inband_code_update.cpp
@@ -138,7 +138,6 @@
                   << "ERROR=" << e.what() << std::endl;
         rc = PLDM_ERROR;
     }
-    newImageId.clear();
     return rc;
 }
 
@@ -198,7 +197,7 @@
                 msg.read(iface, props);
                 processPriorityChangeNotification(props);
             }));
-    fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
+    fwUpdateMatcher.push_back(std::make_unique<sdbusplus::bus::match::match>(
         pldm::utils::DBusHandler::getBus(),
         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
@@ -206,6 +205,7 @@
             DBusInterfaceAdded interfaces;
             sdbusplus::message::object_path path;
             msg.read(path, interfaces);
+
             for (auto& interface : interfaces)
             {
                 if (interface.first ==
@@ -214,6 +214,7 @@
                     auto imageInterface =
                         "xyz.openbmc_project.Software.Activation";
                     auto imageObjPath = path.str.c_str();
+
                     try
                     {
                         auto propVal = dBusIntf->getDbusPropertyVariant(
@@ -224,20 +225,82 @@
                             isCodeUpdateInProgress())
                         {
                             newImageId = path.str;
+                            if (!imageActivationMatch)
+                            {
+                                imageActivationMatch = std::make_unique<
+                                    sdbusplus::bus::match::match>(
+                                    pldm::utils::DBusHandler::getBus(),
+                                    propertiesChanged(newImageId,
+                                                      "xyz.openbmc_project."
+                                                      "Software.Activation"),
+                                    [this](sdbusplus::message::message& msg) {
+                                        DbusChangedProps props;
+                                        std::string iface;
+                                        msg.read(iface, props);
+                                        const auto itr =
+                                            props.find("Activation");
+                                        if (itr != props.end())
+                                        {
+                                            PropertyValue value = itr->second;
+                                            auto propVal =
+                                                std::get<std::string>(value);
+                                            if (propVal ==
+                                                "xyz.openbmc_project.Software."
+                                                "Activation.Activations.Active")
+                                            {
+                                                CodeUpdateState state =
+                                                    CodeUpdateState::END;
+                                                setCodeUpdateProgress(false);
+                                                auto sensorId =
+                                                    getFirmwareUpdateSensor();
+                                                sendStateSensorEvent(
+                                                    sensorId,
+                                                    PLDM_STATE_SENSOR_STATE, 0,
+                                                    uint8_t(state),
+                                                    uint8_t(CodeUpdateState::
+                                                                START));
+                                                newImageId.clear();
+                                            }
+                                            else if (propVal ==
+                                                         "xyz.openbmc_project."
+                                                         "Software.Activation."
+                                                         "Activations.Failed" ||
+                                                     propVal ==
+                                                         "xyz.openbmc_"
+                                                         "project.Software."
+                                                         "Activation."
+                                                         "Activations."
+                                                         "Invalid")
+                                            {
+                                                CodeUpdateState state =
+                                                    CodeUpdateState::FAIL;
+                                                setCodeUpdateProgress(false);
+                                                auto sensorId =
+                                                    getFirmwareUpdateSensor();
+                                                sendStateSensorEvent(
+                                                    sensorId,
+                                                    PLDM_STATE_SENSOR_STATE, 0,
+                                                    uint8_t(state),
+                                                    uint8_t(CodeUpdateState::
+                                                                START));
+                                                newImageId.clear();
+                                            }
+                                        }
+                                    });
+                            }
                             auto rc = setRequestedActivation();
-                            CodeUpdateState state = CodeUpdateState::END;
                             if (rc != PLDM_SUCCESS)
                             {
-                                state = CodeUpdateState::FAIL;
+                                CodeUpdateState state = CodeUpdateState::FAIL;
+                                setCodeUpdateProgress(false);
+                                auto sensorId = getFirmwareUpdateSensor();
+                                sendStateSensorEvent(
+                                    sensorId, PLDM_STATE_SENSOR_STATE, 0,
+                                    uint8_t(state),
+                                    uint8_t(CodeUpdateState::START));
                                 std::cerr
                                     << "could not set RequestedActivation \n";
                             }
-                            setCodeUpdateProgress(false);
-                            auto sensorId = getFirmwareUpdateSensor();
-                            sendStateSensorEvent(
-                                sensorId, PLDM_STATE_SENSOR_STATE, 0,
-                                uint8_t(state),
-                                uint8_t(CodeUpdateState::START));
                             break;
                         }
                     }
@@ -247,7 +310,7 @@
                     }
                 }
             }
-        });
+        }));
 }
 
 void CodeUpdate::processPriorityChangeNotification(
diff --git a/oem/ibm/libpldmresponder/inband_code_update.hpp b/oem/ibm/libpldmresponder/inband_code_update.hpp
index 77f55f1..2bd02f4 100644
--- a/oem/ibm/libpldmresponder/inband_code_update.hpp
+++ b/oem/ibm/libpldmresponder/inband_code_update.hpp
@@ -39,6 +39,7 @@
         nextBootSide = Tside;
         markerLidSensorId = PLDM_INVALID_EFFECTER_ID;
         firmwareUpdateSensorId = PLDM_INVALID_EFFECTER_ID;
+        imageActivationMatch = nullptr;
     }
 
     /* @brief Method to return the current boot side
@@ -183,7 +184,7 @@
     std::vector<std::unique_ptr<sdbusplus::bus::match::match>>
         captureNextBootSideChange; //!< vector to catch the D-Bus property
                                    //!< change for next boot side
-    std::unique_ptr<sdbusplus::bus::match::match>
+    std::vector<std::unique_ptr<sdbusplus::bus::match::match>>
         fwUpdateMatcher; //!< pointer to capture the interface added signal for
                          //!< new image
     pldm::responder::oem_platform::Handler*
@@ -191,6 +192,9 @@
     uint16_t markerLidSensorId;
     uint16_t firmwareUpdateSensorId;
 
+    /** @brief D-Bus property changed signal match for image activation */
+    std::unique_ptr<sdbusplus::bus::match::match> imageActivationMatch;
+
     /* @brief Method to take action when the subscribed D-Bus property is
      *        changed
      * @param[in] chProperties - list of properties which have changed
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
diff --git a/oem/ibm/libpldmresponder/oem_ibm_handler.hpp b/oem/ibm/libpldmresponder/oem_ibm_handler.hpp
index 854c96b..b574262 100644
--- a/oem/ibm/libpldmresponder/oem_ibm_handler.hpp
+++ b/oem/ibm/libpldmresponder/oem_ibm_handler.hpp
@@ -1,4 +1,5 @@
 #pragma once
+#include "libpldm/entity.h"
 #include "libpldm/platform.h"
 
 #include "inband_code_update.hpp"
@@ -15,6 +16,7 @@
 
 #define PLDM_OEM_IBM_FIRMWARE_UPDATE_STATE 32768
 #define PLDM_OEM_IBM_BOOT_STATE 32769
+#define PLDM_OEM_IBM_SYSTEM_POWER_STATE 32771
 
 static constexpr auto PLDM_OEM_IBM_ENTITY_FIRMWARE_UPDATE = 24577;
 static constexpr auto PLDM_OEM_IBM_VERIFICATION_STATE = 32770;
@@ -39,6 +41,11 @@
     MIN_MIF_FAIL = 0x4,
 };
 
+enum SystemPowerStates
+{
+    POWER_CYCLE_HARD = 0x1,
+};
+
 class Handler : public oem_platform::Handler
 {
   public:
@@ -127,6 +134,13 @@
      */
     void _processStartUpdate(sdeventplus::source::EventBase& source);
 
+    /** @brief _processSystemReboot processes the actual work that needs to be
+     *  carried out after the System Power State effecter is set to reboot
+     *  the system
+     *  @param[in] source - sdeventplus event source
+     */
+    void _processSystemReboot(sdeventplus::source::EventBase& source);
+
     ~Handler() = default;
 
     pldm::responder::CodeUpdate* codeUpdate; //!< pointer to CodeUpdate object
@@ -146,11 +160,16 @@
     /** @brief sdeventplus event source */
     std::unique_ptr<sdeventplus::source::Defer> assembleImageEvent;
     std::unique_ptr<sdeventplus::source::Defer> startUpdateEvent;
+    std::unique_ptr<sdeventplus::source::Defer> systemRebootEvent;
 
     /** @brief reference of main event loop of pldmd, primarily used to schedule
      *  work
      */
     sdeventplus::Event& event;
+
+  private:
+    /** @brief D-Bus property changed signal match for CurrentPowerState*/
+    std::unique_ptr<sdbusplus::bus::match::match> chassisOffMatch;
 };
 
 /** @brief Method to encode code update event msg