PEL: Prevent deletion if it's associated with HWIsolation

- This ensures that PELs linked to HWIsolation records are protected
from accidental deletion.
- Shows error message of "Call failed: The service is temporarily
unavailable.", when attempting to delete such a PEL individually.
- If trying to Delete all, will skip such PELs without showing any
message.

Tested:
Sample output:
```bash
$ busctl call xyz.openbmc_project.Logging /xyz/openbmc_project/
logging xyz.openbmc_project.Collection.DeleteAll DeleteAll

$ busctl call xyz.openbmc_project.Logging /xyz/openbmc_project/
logging/entry/2 xyz.openbmc_project.Object.Delete Delete
Call failed: The service is temporarily unavailable.

```
Change-Id: I2d28de91bbb0fbc2a991e3d5e5631814d41fe044
Signed-off-by: Harsh Agarwal <Harsh.Agarwal@ibm.com>
diff --git a/extensions.cpp b/extensions.cpp
index 2fc8792..c24c764 100644
--- a/extensions.cpp
+++ b/extensions.cpp
@@ -29,6 +29,12 @@
     return deleteProhibitedFunctions;
 }
 
+LogIDsWithHwIsolationFunctions& Extensions::getLogIDWithHwIsolationFunctions()
+{
+    static LogIDsWithHwIsolationFunctions logIDWithHwIsolationFunctions{};
+    return logIDWithHwIsolationFunctions;
+}
+
 Extensions::DefaultErrorCaps& Extensions::getDefaultErrorCaps()
 {
     static DefaultErrorCaps defaultErrorCaps = DefaultErrorCaps::enable;
diff --git a/extensions.hpp b/extensions.hpp
index c10e6a0..ff74137 100644
--- a/extensions.hpp
+++ b/extensions.hpp
@@ -50,6 +50,16 @@
  */
 using DeleteProhibitedFunction = std::function<void(uint32_t, bool&)>;
 
+/**
+ * @brief The function type that will return list of Hw Isolated
+ *        log IDs
+ * @param[out] std::vector<uint32_t>& - Hw Isolated log IDs
+ */
+using LogIDWithHwIsolationFunction =
+    std::function<void(std::vector<uint32_t>&)>;
+using LogIDsWithHwIsolationFunctions =
+    std::vector<LogIDWithHwIsolationFunction>;
+
 using StartupFunctions = std::vector<StartupFunction>;
 using CreateFunctions = std::vector<CreateFunction>;
 using DeleteFunctions = std::vector<DeleteFunction>;
@@ -159,6 +169,19 @@
     }
 
     /**
+     * @brief Constructor to register a LogID with HwIsolation function
+     *
+     * Functions registered with this contructor will be called
+     * before phosphor-log-manager deletes all event log.
+     *
+     * @param[in] func - The function to register
+     */
+    explicit Extensions(LogIDWithHwIsolationFunction func)
+    {
+        getLogIDWithHwIsolationFunctions().emplace_back(func);
+    }
+
+    /**
      * @brief Constructor to disable event log capping
      *
      * This constructor should only be called by the
@@ -197,6 +220,13 @@
     static DeleteProhibitedFunctions& getDeleteProhibitedFunctions();
 
     /**
+     * @brief Returns the LogIDWithHwIsolationFunction functions
+     * @return LogIDsWithHwIsolationFunctions - the LogIDWithHwIsolationFunction
+     * functions
+     */
+    static LogIDsWithHwIsolationFunctions& getLogIDWithHwIsolationFunctions();
+
+    /**
      * @brief Returns the DefaultErrorCaps value
      * @return DefaultErrorCaps - the DefaultErrorCaps value
      */
diff --git a/extensions/openpower-pels/data_interface.cpp b/extensions/openpower-pels/data_interface.cpp
index 146b92f..1975e95 100644
--- a/extensions/openpower-pels/data_interface.cpp
+++ b/extensions/openpower-pels/data_interface.cpp
@@ -984,6 +984,42 @@
 #endif
 }
 
+DBusPathList DataInterface::getAssociatedPaths(
+    const DBusPath& associatedPath, const DBusPath& subtree, int32_t depth,
+    const DBusInterfaceList& interfaces) const
+{
+    DBusPathList paths;
+    try
+    {
+        auto method = _bus.new_method_call(
+            service_name::objectMapper, object_path::objectMapper,
+            interface::objectMapper, "GetAssociatedSubTreePaths");
+        method.append(sdbusplus::message::object_path(associatedPath),
+                      sdbusplus::message::object_path(subtree), depth,
+                      interfaces);
+
+        auto reply = _bus.call(method, dbusTimeout);
+        reply.read(paths);
+    }
+    catch (const std::exception& e)
+    {
+        std::string ifaces(
+            std::ranges::fold_left_first(
+                interfaces,
+                [](std::string ifaces, const std::string& iface) {
+                    return ifaces + ", " + iface;
+                })
+                .value_or(""));
+
+        lg2::error("Failed getting associated paths: {ERROR}. "
+                   "AssociatedPath: {ASSOIC_PATH} Subtree: {SUBTREE} "
+                   "Interfaces: {IFACES}",
+                   "ERROR", e, "ASSOIC_PATH", associatedPath, "SUBTREE",
+                   subtree, "IFACES", ifaces);
+    }
+    return paths;
+}
+
 void DataInterface::startFruPlugWatch()
 {
     // Add a watch on inventory InterfacesAdded and then find all
diff --git a/extensions/openpower-pels/data_interface.hpp b/extensions/openpower-pels/data_interface.hpp
index 713f4cd..c6cfe4e 100644
--- a/extensions/openpower-pels/data_interface.hpp
+++ b/extensions/openpower-pels/data_interface.hpp
@@ -537,6 +537,23 @@
      */
     void addDIMMLocCode(const std::string& locCode, bool isFRUDIMM);
 
+    /**
+     * @brief Finds all D-Bus Associated paths that contain any of the
+     *        interfaces passed in, by using GetAssociatedSubTreePaths.
+     *
+     * @param[in] associatedPath - The D-Bus object path
+     * @param[in] subtree - The subtree path for which the result should be
+     *                      fetched
+     * @param[in] depth - The maximum subtree depth for which results should be
+     *                    fetched
+     * @param[in] interfaces - The desired interfaces
+     *
+     * @return The D-Bus paths.
+     */
+    virtual DBusPathList getAssociatedPaths(
+        const DBusPath& associatedPath, const DBusPath& subtree, int32_t depth,
+        const DBusInterfaceList& interfaces) const = 0;
+
   protected:
     /**
      * @brief Sets the host on/off state and runs any
@@ -907,6 +924,23 @@
     std::optional<std::vector<uint8_t>>
         getDIProperty(const std::string& locationCode) const override;
 
+    /**
+     * @brief Finds all D-Bus Associated paths that contain any of the
+     *        interfaces passed in, by using GetAssociatedSubTreePaths.
+     *
+     * @param[in] associatedPath - The D-Bus object path
+     * @param[in] subtree - The subtree path for which the result should be
+     *                      fetched
+     * @param[in] depth - The maximum subtree depth for which results should be
+     *                    fetched
+     * @param[in] interfaces - The desired interfaces
+     *
+     * @return The D-Bus paths.
+     */
+    DBusPathList getAssociatedPaths(
+        const DBusPath& associatedPath, const DBusPath& subtree, int32_t depth,
+        const DBusInterfaceList& interfaces) const override;
+
   private:
     /**
      * @brief Reads the BMC firmware version string and puts it into
diff --git a/extensions/openpower-pels/entry_points.cpp b/extensions/openpower-pels/entry_points.cpp
index dd00103..aba0457 100644
--- a/extensions/openpower-pels/entry_points.cpp
+++ b/extensions/openpower-pels/entry_points.cpp
@@ -87,5 +87,12 @@
 
 REGISTER_EXTENSION_FUNCTION(pelDeleteProhibited)
 
+void getLogIDWithHwIsolation(std::vector<uint32_t>& logIDs)
+{
+    manager->getLogIDWithHwIsolation(logIDs);
+}
+
+REGISTER_EXTENSION_FUNCTION(getLogIDWithHwIsolation)
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/manager.cpp b/extensions/openpower-pels/manager.cpp
index a362e01..03254d2 100644
--- a/extensions/openpower-pels/manager.cpp
+++ b/extensions/openpower-pels/manager.cpp
@@ -301,8 +301,28 @@
     _repo.remove(id);
 }
 
-bool Manager::isDeleteProhibited(uint32_t /*obmcLogID*/)
+void Manager::getLogIDWithHwIsolation(std::vector<uint32_t>& idsWithHwIsoEntry)
 {
+    idsWithHwIsoEntry = _dataIface->getLogIDWithHwIsolation();
+}
+
+bool Manager::isDeleteProhibited(uint32_t obmcLogID)
+{
+    auto entryPath{std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID)};
+    auto entry = _pelEntries.find(entryPath);
+    if (entry != _pelEntries.end())
+    {
+        if (entry->second->guard())
+        {
+            auto hwIsolationAssocPaths = _dataIface->getAssociatedPaths(
+                entryPath += "/isolated_hw_entry", "/", 0,
+                {"xyz.openbmc_project.HardwareIsolation.Entry"});
+            if (!hwIsolationAssocPaths.empty())
+            {
+                return true;
+            }
+        }
+    }
     return false;
 }
 
diff --git a/extensions/openpower-pels/manager.hpp b/extensions/openpower-pels/manager.hpp
index 462949d..2429aec 100644
--- a/extensions/openpower-pels/manager.hpp
+++ b/extensions/openpower-pels/manager.hpp
@@ -125,6 +125,14 @@
      */
     void erase(uint32_t obmcLogID);
 
+    /**
+     * @brief Get the list of event log ids that have an associated
+     *        hardware isolation entry.
+     *
+     * @param[in] idsWithHwIsoEntry - List to store the list of log ids
+     */
+    void getLogIDWithHwIsolation(std::vector<uint32_t>& idsWithHwIsoEntry);
+
     /** @brief Says if an OpenBMC event log may not be manually deleted at this
      *         time because its corresponding PEL cannot be.
      *
diff --git a/log_manager.cpp b/log_manager.cpp
index 4643bff..4a46cd8 100644
--- a/log_manager.cpp
+++ b/log_manager.cpp
@@ -487,6 +487,72 @@
     return;
 }
 
+size_t Manager::eraseAll()
+{
+    std::vector<uint32_t> logIDWithHwIsolation;
+    for (auto& func : Extensions::getLogIDWithHwIsolationFunctions())
+    {
+        try
+        {
+            func(logIDWithHwIsolation);
+        }
+        catch (const std::exception& e)
+        {
+            lg2::error("An extension's LogIDWithHwIsolation function threw an "
+                       "exception: {ERROR}",
+                       "ERROR", e);
+        }
+    }
+    size_t entriesSize = entries.size();
+    auto iter = entries.begin();
+    if (logIDWithHwIsolation.empty())
+    {
+        while (iter != entries.end())
+        {
+            auto e = iter->first;
+            ++iter;
+            erase(e);
+        }
+        entryId = 0;
+    }
+    else
+    {
+        while (iter != entries.end())
+        {
+            auto e = iter->first;
+            ++iter;
+            try
+            {
+                if (!std::ranges::contains(logIDWithHwIsolation, e))
+                {
+                    erase(e);
+                }
+                else
+                {
+                    entriesSize--;
+                }
+            }
+            catch (const sdbusplus::xyz::openbmc_project::Common::Error::
+                       Unavailable& e)
+            {
+                entriesSize--;
+            }
+        }
+        if (!entries.empty())
+        {
+            entryId = std::ranges::max_element(entries, [](const auto& a,
+                                                           const auto& b) {
+                          return a.first < b.first;
+                      })->first;
+        }
+        else
+        {
+            entryId = 0;
+        }
+    }
+    return entriesSize;
+}
+
 void Manager::erase(uint32_t entryId)
 {
     auto entryFound = entries.find(entryId);
@@ -500,10 +566,15 @@
                 func(entryId, prohibited);
                 if (prohibited)
                 {
-                    // Future work remains to throw an error here.
-                    return;
+                    throw sdbusplus::xyz::openbmc_project::Common::Error::
+                        Unavailable();
                 }
             }
+            catch (const sdbusplus::xyz::openbmc_project::Common::Error::
+                       Unavailable& e)
+            {
+                throw;
+            }
             catch (const std::exception& e)
             {
                 lg2::error("An extension's deleteProhibited function threw an "
diff --git a/log_manager.hpp b/log_manager.hpp
index cc42d8a..436f572 100644
--- a/log_manager.hpp
+++ b/log_manager.hpp
@@ -116,20 +116,7 @@
      *
      *  @return size_t - count of erased entries
      */
-    size_t eraseAll()
-    {
-        size_t entriesSize = entries.size();
-        auto iter = entries.begin();
-        while (iter != entries.end())
-        {
-            auto e = iter->first;
-            ++iter;
-            erase(e);
-        }
-        entryId = 0;
-
-        return entriesSize;
-    }
+    size_t eraseAll();
 
     /** @brief Returns the count of high severity errors
      *
diff --git a/test/extensions_test.cpp b/test/extensions_test.cpp
index bfd2268..d5f29eb 100644
--- a/test/extensions_test.cpp
+++ b/test/extensions_test.cpp
@@ -37,6 +37,16 @@
     prohibited = true;
 }
 
+void logIDWithHwIsolation1(std::vector<uint32_t>& logIDs)
+{
+    logIDs.push_back(1);
+}
+
+void logIDWithHwIsolation2(std::vector<uint32_t>& logIDs)
+{
+    logIDs.push_back(2);
+}
+
 DISABLE_LOG_ENTRY_CAPS()
 REGISTER_EXTENSION_FUNCTION(startup1)
 REGISTER_EXTENSION_FUNCTION(startup2)
@@ -44,6 +54,8 @@
 REGISTER_EXTENSION_FUNCTION(create2)
 REGISTER_EXTENSION_FUNCTION(deleteProhibited1)
 REGISTER_EXTENSION_FUNCTION(deleteProhibited2)
+REGISTER_EXTENSION_FUNCTION(logIDWithHwIsolation1)
+REGISTER_EXTENSION_FUNCTION(logIDWithHwIsolation2)
 REGISTER_EXTENSION_FUNCTION(deleteLog1)
 REGISTER_EXTENSION_FUNCTION(deleteLog2)
 
@@ -82,5 +94,24 @@
         EXPECT_TRUE(prohibited);
     }
 
+    EXPECT_EQ(Extensions::getLogIDWithHwIsolationFunctions().size(), 2);
+    std::vector<uint32_t> ids;
+    for (size_t i = 0; i < 2; ++i)
+    {
+        auto getLogIDWithHwIsolation =
+            Extensions::getLogIDWithHwIsolationFunctions()[i];
+        getLogIDWithHwIsolation(ids);
+        if (i == 0)
+        {
+            EXPECT_EQ(ids.size(), 1);
+            EXPECT_EQ(ids[0], 1);
+        }
+        if (i == 1)
+        {
+            EXPECT_EQ(ids.size(), 2);
+            EXPECT_EQ(ids[1], 2);
+        }
+    }
+
     EXPECT_TRUE(Extensions::disableDefaultLogCaps());
 }
diff --git a/test/openpower-pels/mocks.hpp b/test/openpower-pels/mocks.hpp
index 3b74871..ae8d385 100644
--- a/test/openpower-pels/mocks.hpp
+++ b/test/openpower-pels/mocks.hpp
@@ -68,6 +68,10 @@
     MOCK_METHOD(std::vector<uint8_t>, getRawProgressSRC, (), (const override));
     MOCK_METHOD(std::optional<std::vector<uint8_t>>, getDIProperty,
                 (const std::string&), (const override));
+    MOCK_METHOD(DBusPathList, getAssociatedPaths,
+                (const DBusPath&, const DBusPath&, int32_t,
+                 const DBusInterfaceList&),
+                (const override));
 
     void changeHostState(bool newState)
     {
diff --git a/test/openpower-pels/pel_manager_test.cpp b/test/openpower-pels/pel_manager_test.cpp
index 3689bdf..e45fd55 100644
--- a/test/openpower-pels/pel_manager_test.cpp
+++ b/test/openpower-pels/pel_manager_test.cpp
@@ -31,6 +31,7 @@
 
 using ::testing::NiceMock;
 using ::testing::Return;
+using json = nlohmann::json;
 
 class TestLogger
 {
@@ -1235,3 +1236,242 @@
     mockIface->fruPresent("U1234-A4");
     checkDeconfigured(true);
 }
+
+int createHWIsolatedCalloutFile()
+{
+    json jsonCalloutDataList(nlohmann::json::value_t::array);
+    json jsonDimmCallout;
+
+    jsonDimmCallout["LocationCode"] = "Ufcs-DIMM0";
+    jsonDimmCallout["EntityPath"] = {35, 1, 0, 2, 0, 3, 0, 0, 0, 0, 0,
+                                     0,  0, 0, 0, 0, 0, 0, 0, 0, 0};
+    jsonDimmCallout["GuardType"] = "GARD_Predictive";
+    jsonDimmCallout["Deconfigured"] = false;
+    jsonDimmCallout["Guarded"] = true;
+    jsonDimmCallout["Priority"] = "M";
+    jsonCalloutDataList.emplace_back(std::move(jsonDimmCallout));
+
+    std::string calloutData(jsonCalloutDataList.dump());
+    std::string calloutFile("/tmp/phalPELCalloutsJson.XXXXXX");
+    int fileFD = -1;
+
+    fileFD = mkostemp(calloutFile.data(), O_RDWR);
+    if (fileFD == -1)
+    {
+        perror("Failed to create PELCallouts file");
+        return -1;
+    }
+
+    ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size());
+    if (rc == -1)
+    {
+        perror("Failed to write PELCallouts file");
+        close(fileFD);
+        return -1;
+    }
+
+    // Ensure we seek to the beginning of the file
+    rc = lseek(fileFD, 0, SEEK_SET);
+    if (rc == -1)
+    {
+        perror("Failed to set SEEK_SET for PELCallouts file");
+        close(fileFD);
+        return -1;
+    }
+    return fileFD;
+}
+
+void appendFFDCEntry(int fd, uint8_t subTypeJson, uint8_t version,
+                     phosphor::logging::FFDCEntries& ffdcEntries)
+{
+    phosphor::logging::FFDCEntry ffdcEntry =
+        std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server::
+                            Create::FFDCFormat::JSON,
+                        subTypeJson, version, fd);
+    ffdcEntries.push_back(ffdcEntry);
+}
+
+TEST_F(ManagerTest, TestPELDeleteWithoutHWIsolation)
+{
+    const auto registry = R"(
+    {
+        "PELs":
+        [{
+            "Name": "xyz.openbmc_project.Error.Test",
+            "SRC":
+            {
+                "ReasonCode": "0x2030"
+            },
+            "Documentation": {
+                "Description": "Test Error",
+                "Message": "Test Error"
+            }
+        }]
+    }
+    )";
+
+    auto path = getPELReadOnlyDataPath();
+    fs::create_directories(path);
+    path /= "message_registry.json";
+
+    std::ofstream registryFile{path};
+    registryFile << registry;
+    registryFile.close();
+
+    std::unique_ptr<DataInterfaceBase> dataIface =
+        std::make_unique<MockDataInterface>();
+
+    MockDataInterface* mockIface =
+        reinterpret_cast<MockDataInterface*>(dataIface.get());
+
+    EXPECT_CALL(*mockIface, getInventoryFromLocCode("Ufcs-DIMM0", 0, false))
+        .WillOnce(Return(std::vector<std::string>{
+            "/xyz/openbmc_project/inventory/system/chassis/motherboard/dimm0"}));
+
+    // Mock the scenario where the hardware isolation guard is flagged
+    // but is not associated, resulting in an empty list being returned.
+    EXPECT_CALL(
+        *mockIface,
+        getAssociatedPaths(
+            ::testing::StrEq(
+                "/xyz/openbmc_project/logging/entry/42/isolated_hw_entry"),
+            ::testing::StrEq("/"), 0,
+            ::testing::ElementsAre(
+                "xyz.openbmc_project.HardwareIsolation.Entry")))
+        .WillRepeatedly(Return(std::vector<std::string>{}));
+
+    std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
+    openpower::pels::Manager manager{
+        logManager, std::move(dataIface),
+        std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
+                  std::placeholders::_2, std::placeholders::_3),
+        std::move(journal)};
+    std::vector<std::string> additionalData;
+    std::vector<std::string> associations;
+
+    // Check when there's no PEL with given id.
+    {
+        EXPECT_FALSE(manager.isDeleteProhibited(42));
+    }
+    // creating without ffdcEntries
+    manager.create("xyz.openbmc_project.Error.Test", 42, 0,
+                   phosphor::logging::Entry::Level::Error, additionalData,
+                   associations);
+    auto pelFile = findAnyPELInRepo();
+    auto data = readPELFile(*pelFile);
+    PEL pel_unguarded(*data);
+    {
+        // Verify that the guard flag is false.
+        EXPECT_FALSE(pel_unguarded.getGuardFlag());
+        // Check that `isDeleteProhibited` returns false when the guard flag is
+        // false.
+        EXPECT_FALSE(manager.isDeleteProhibited(42));
+    }
+    manager.erase(42);
+    EXPECT_FALSE(findAnyPELInRepo());
+
+    int fd = createHWIsolatedCalloutFile();
+    ASSERT_NE(fd, -1);
+    uint8_t subTypeJson = 0xCA;
+    uint8_t version = 0x01;
+    phosphor::logging::FFDCEntries ffdcEntries;
+    appendFFDCEntry(fd, subTypeJson, version, ffdcEntries);
+    manager.create("xyz.openbmc_project.Error.Test", 42, 0,
+                   phosphor::logging::Entry::Level::Error, additionalData,
+                   associations, ffdcEntries);
+    close(fd);
+
+    auto pelPathInRepo = findAnyPELInRepo();
+    auto unguardedData = readPELFile(*pelPathInRepo);
+    PEL pel(*unguardedData);
+    {
+        // Verify guard flag set to true
+        EXPECT_TRUE(pel.getGuardFlag());
+        // Check even if guard flag is true, if dbus call returns empty
+        // array list then `isDeleteProhibited` returns false
+        EXPECT_FALSE(manager.isDeleteProhibited(42));
+    }
+    manager.erase(42);
+}
+
+TEST_F(ManagerTest, TestPELDeleteWithHWIsolation)
+{
+    const auto registry = R"(
+    {
+        "PELs":
+        [{
+            "Name": "xyz.openbmc_project.Error.Test",
+            "Severity": "critical_system_term",
+            "SRC":
+            {
+                "ReasonCode": "0x2030"
+            },
+            "Documentation": {
+                "Description": "Test Error",
+                "Message": "Test Error"
+            }
+        }]
+    }
+    )";
+
+    auto path = getPELReadOnlyDataPath();
+    fs::create_directories(path);
+    path /= "message_registry.json";
+
+    std::ofstream registryFile{path};
+    registryFile << registry;
+    registryFile.close();
+
+    std::unique_ptr<DataInterfaceBase> dataIface =
+        std::make_unique<MockDataInterface>();
+
+    MockDataInterface* mockIface =
+        reinterpret_cast<MockDataInterface*>(dataIface.get());
+
+    EXPECT_CALL(*mockIface, getInventoryFromLocCode("Ufcs-DIMM0", 0, false))
+        .WillOnce(Return(std::vector<std::string>{
+            "/xyz/openbmc_project/inventory/system/chassis/motherboard/dimm0"}));
+
+    EXPECT_CALL(
+        *mockIface,
+        getAssociatedPaths(
+            ::testing::StrEq(
+                "/xyz/openbmc_project/logging/entry/42/isolated_hw_entry"),
+            ::testing::StrEq("/"), 0,
+            ::testing::ElementsAre(
+                "xyz.openbmc_project.HardwareIsolation.Entry")))
+        .WillRepeatedly(Return(std::vector<std::string>{
+            "/xyz/openbmc_project/hardware_isolation/entry/1"}));
+
+    std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
+    openpower::pels::Manager manager{
+        logManager, std::move(dataIface),
+        std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
+                  std::placeholders::_2, std::placeholders::_3),
+        std::move(journal)};
+    std::vector<std::string> additionalData;
+    std::vector<std::string> associations;
+
+    int fd = createHWIsolatedCalloutFile();
+    ASSERT_NE(fd, -1);
+    uint8_t subTypeJson = 0xCA;
+    uint8_t version = 0x01;
+    phosphor::logging::FFDCEntries ffdcEntries;
+    appendFFDCEntry(fd, subTypeJson, version, ffdcEntries);
+    manager.create("xyz.openbmc_project.Error.Test", 42, 0,
+                   phosphor::logging::Entry::Level::Error, additionalData,
+                   associations, ffdcEntries);
+    close(fd);
+
+    auto pelFile = findAnyPELInRepo();
+    EXPECT_TRUE(pelFile);
+    auto data = readPELFile(*pelFile);
+    PEL pel(*data);
+    EXPECT_TRUE(pel.valid());
+    // Test case where the guard flag is set to true and the hardware isolation
+    // guard is associated, which should result in `isDeleteProhibited`
+    // returning true as expected.
+    EXPECT_TRUE(pel.getGuardFlag());
+    EXPECT_TRUE(manager.isDeleteProhibited(42));
+    manager.erase(42);
+}