PEL: Clear deconfig flag after callout replaced

Making use of the previous commit's framework to call a function when a
fan or power supply becomes present, add code to the Manager class to
register a callback that will clear the deconfig flag for all PELs
created with the power-thermal or fan component ID that have the
location code of the replaced fan/PS as a callout.

This way, the degraded mode reporting code will no longer pick up those
PELs in its report as since the hardware was replaced those PELs are no
longer relevant.

This is necessary only for fans or power supplies because they're the
only N+1 hardware that can be hot plugged at runtime.  And also because
this is what the IBM service team wants.

Tested:
Simulated missing hardware (changed present D-Bus property for fans,
toggled PSU presence GPIO in the simulator for PSs).  Saw errors get
created for it, then simulated replacing it and saw those errors have
their deconfig flag cleared, verifying before and after with peltool:

```
// Remove and replace fan
phosphor-fan-monitor: Fan /system/chassis/motherboard/fan0 presence state change to false
phosphor-log-manager: Created PEL 0x50000002 (BMC ID 2) with SRC 110076F1
phosphor-fan-monitor: Fan /system/chassis/motherboard/fan0 presence state change to true
phosphor-log-manager: Detected FRU /xyz/openbmc_project/inventory/system/chassis/motherboard/fan0 (U78DB.ND0.1234567-A0) present
phosphor-log-manager: Clearing deconfig flag in PEL 0x50000002 with SRC 110076F1 because U78DB.ND0.1234567-A0 was replaced

// Remove and replace PS
phosphor-log-manager: Created PEL 0x50000003 (BMC ID 3) with SRC 110015F6
...
phosphor-psu-monitor: Updating inventory present property. present:true invpath:/system/chassis/motherboard/powersupply0 name:powersupply0
phosphor-log-manager: Detected FRU /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0 (U78DB.ND0.1234567-E0) present
phosphor-log-manager: Clearing deconfig flag in PEL 0x50000003 with SRC 110015F6 because U78DB.ND0.1234567-E0 was replaced
```

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: Iee05b4a612ca8f438f8c89f37b4e7b529a131a9f
diff --git a/extensions/openpower-pels/manager.cpp b/extensions/openpower-pels/manager.cpp
index 9f2a13e..61eb36b 100644
--- a/extensions/openpower-pels/manager.cpp
+++ b/extensions/openpower-pels/manager.cpp
@@ -54,6 +54,8 @@
 } // namespace additional_data
 
 constexpr auto defaultLogMessage = "xyz.openbmc_project.Logging.Error.Default";
+constexpr uint32_t bmcThermalCompID = 0x2700;
+constexpr uint32_t bmcFansCompID = 0x2800;
 
 Manager::~Manager()
 {
@@ -1032,5 +1034,74 @@
     _obmcLogDeleteEventSource.reset();
 }
 
+bool Manager::clearPowerThermalDeconfigFlag(const std::string& locationCode,
+                                            openpower::pels::PEL& pel)
+{
+    // The requirements state that only power-thermal or
+    // fan PELs need their deconfig flag cleared.
+    static const std::vector<uint32_t> compIDs{bmcThermalCompID, bmcFansCompID};
+
+    if (std::find(compIDs.begin(), compIDs.end(),
+                  pel.privateHeader().header().componentID) == compIDs.end())
+    {
+        return false;
+    }
+
+    auto src = pel.primarySRC();
+    const auto& callouts = (*src)->callouts();
+    if (!callouts)
+    {
+        return false;
+    }
+
+    for (const auto& callout : callouts->callouts())
+    {
+        // Look for the passed in location code in a callout that
+        // is either a normal HW callout or a symbolic FRU with
+        // a trusted location code callout.
+        if ((callout->locationCode() != locationCode) ||
+            !callout->fruIdentity())
+        {
+            continue;
+        }
+
+        if ((callout->fruIdentity()->failingComponentType() !=
+             src::FRUIdentity::hardwareFRU) &&
+            (callout->fruIdentity()->failingComponentType() !=
+             src::FRUIdentity::symbolicFRUTrustedLocCode))
+        {
+            continue;
+        }
+
+        log<level::INFO>(
+            fmt::format(
+                "Clearing deconfig flag in PEL {:#x} with SRC {} because {} was replaced",
+                pel.id(), (*src)->asciiString().substr(0, 8), locationCode)
+                .c_str());
+        (*src)->clearErrorStatusFlag(SRC::ErrorStatusFlags::deconfigured);
+        return true;
+    }
+    return false;
+}
+
+void Manager::hardwarePresent(const std::string& locationCode)
+{
+    Repository::PELUpdateFunc handlePowerThermalHardwarePresent =
+        [locationCode](openpower::pels::PEL& pel) {
+        return Manager::clearPowerThermalDeconfigFlag(locationCode, pel);
+    };
+
+    // If the PEL was created by the BMC and has the deconfig flag set,
+    // it's a candidate to have the deconfig flag cleared.
+    for (const auto& [id, attributes] : _repo.getAttributesMap())
+    {
+        if ((attributes.creator == static_cast<uint8_t>(CreatorID::openBMC)) &&
+            attributes.deconfig)
+        {
+            _repo.updatePEL(attributes.path, handlePowerThermalHardwarePresent);
+        }
+    }
+}
+
 } // namespace pels
 } // namespace openpower