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);
+}