platform-mc: Support CPER Event handler
Handle CPER event(0x07) which is defined in `Table 11 - PLDM Event
Type` and section `16.17 eventData format for CPEREvent` in DSP0248
v1.3.0.
The code supports:
1. Handle the PLDM event which has eventClass as CPEREvent (0x07).
2. Store the CPER data in PLDM CPER event to file at `/var/cper/`.
3. Call `CreateDump` method of `xyz.openbmc_project.Dump.Manager` D-Bus
service to create dump fault log.
4. The user can find the dump fault logs in Redfish FaultLog entries
thru URL `/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries`. Each
CPER entry includes the URL to download the created CPER data file
`/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<id>/attachment`.
5. The user can use `cper-parser` in `libcper` to parse the CPER data in
the attached file.
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: I85c53933183178c6b5acdfc12c805e8a4cf1ca2a
diff --git a/platform-mc/event_manager.cpp b/platform-mc/event_manager.cpp
index 1b9c6c3..b50daf2 100644
--- a/platform-mc/event_manager.cpp
+++ b/platform-mc/event_manager.cpp
@@ -58,6 +58,12 @@
}
}
+ /* EventClass CPEREvent as `Table 11 - PLDM Event Types` DSP0248 V1.3.0 */
+ if (eventClass == PLDM_CPER_EVENT)
+ {
+ return processCperEvent(tid, eventId, eventData, eventDataSize);
+ }
+
lg2::info("Unsupported class type {CLASSTYPE}", "CLASSTYPE", eventClass);
return PLDM_ERROR;
@@ -304,5 +310,137 @@
return PLDM_SUCCESS;
}
+int EventManager::processCperEvent(pldm_tid_t tid, uint16_t eventId,
+ const uint8_t* eventData,
+ const size_t eventDataSize)
+{
+ if (eventDataSize < PLDM_PLATFORM_CPER_EVENT_MIN_LENGTH)
+ {
+ lg2::error(
+ "Error : Invalid CPER Event data length for eventId {EVENTID}.",
+ "EVENTID", eventId);
+ return PLDM_ERROR;
+ }
+ const size_t cperEventDataSize =
+ eventDataSize - PLDM_PLATFORM_CPER_EVENT_MIN_LENGTH;
+ const size_t msgDataLen =
+ sizeof(pldm_platform_cper_event) + cperEventDataSize;
+ std::string terminusName = "";
+ auto msgData = std::make_unique<unsigned char[]>(msgDataLen);
+ auto cperEvent = new (msgData.get()) pldm_platform_cper_event;
+
+ auto rc = decode_pldm_platform_cper_event(eventData, eventDataSize,
+ cperEvent, msgDataLen);
+
+ if (rc)
+ {
+ lg2::error(
+ "Failed to decode CPER event for eventId {EVENTID} of terminus ID {TID} error {RC}.",
+ "EVENTID", eventId, "TID", tid, "RC", rc);
+ return rc;
+ }
+
+ if (termini.contains(tid) && !termini[tid])
+ {
+ auto tmp = termini[tid]->getTerminusName();
+ if (tmp && !tmp.value().empty())
+ {
+ terminusName = static_cast<std::string>(tmp.value());
+ }
+ }
+
+ // Save event data to file
+ std::filesystem::path dirName{"/var/cper"};
+ if (!std::filesystem::exists(dirName))
+ {
+ try
+ {
+ std::filesystem::create_directory(dirName);
+ }
+ catch (const std::filesystem::filesystem_error& e)
+ {
+ lg2::error("Failed to create /var/cper directory: {ERROR}", "ERROR",
+ e);
+ return PLDM_ERROR;
+ }
+ }
+
+ std::string fileName{dirName.string() + "/cper-XXXXXX"};
+ auto fd = mkstemp(fileName.data());
+ if (fd < 0)
+ {
+ lg2::error("Failed to generate temp file, error {ERRORNO}", "ERRORNO",
+ std::strerror(errno));
+ return PLDM_ERROR;
+ }
+ close(fd);
+
+ std::ofstream ofs;
+ ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit |
+ std::ofstream::eofbit);
+
+ try
+ {
+ ofs.open(fileName);
+ ofs.write(reinterpret_cast<const char*>(
+ pldm_platform_cper_event_event_data(cperEvent)),
+ cperEvent->event_data_length);
+ if (cperEvent->format_type == PLDM_PLATFORM_CPER_EVENT_WITH_HEADER)
+ {
+ rc = createCperDumpEntry("CPER", fileName, terminusName);
+ }
+ else
+ {
+ rc = createCperDumpEntry("CPERSection", fileName, terminusName);
+ }
+ ofs.close();
+ }
+ catch (const std::ofstream::failure& e)
+ {
+ lg2::error("Failed to save CPER to '{FILENAME}', error - {ERROR}.",
+ "FILENAME", fileName, "ERROR", e);
+ return PLDM_ERROR;
+ }
+ return rc;
+}
+
+int EventManager::createCperDumpEntry(const std::string& dataType,
+ const std::string& dataPath,
+ const std::string& typeName)
+{
+ auto createDump =
+ [](std::map<std::string, std::variant<std::string, uint64_t>>&
+ addData) {
+ static constexpr auto dumpObjPath =
+ "/xyz/openbmc_project/dump/faultlog";
+ static constexpr auto dumpInterface =
+ "xyz.openbmc_project.Dump.Create";
+ auto& bus = pldm::utils::DBusHandler::getBus();
+
+ try
+ {
+ auto service = pldm::utils::DBusHandler().getService(
+ dumpObjPath, dumpInterface);
+ auto method = bus.new_method_call(service.c_str(), dumpObjPath,
+ dumpInterface, "CreateDump");
+ method.append(addData);
+ bus.call_noreply(method);
+ }
+ catch (const std::exception& e)
+ {
+ lg2::error(
+ "Failed to create D-Bus Dump entry, error - {ERROR}.",
+ "ERROR", e);
+ }
+ };
+
+ std::map<std::string, std::variant<std::string, uint64_t>> addData;
+ addData["Type"] = dataType;
+ addData["PrimaryLogId"] = dataPath;
+ addData["AdditionalTypeName"] = typeName;
+ createDump(addData);
+ return PLDM_SUCCESS;
+}
+
} // namespace platform_mc
} // namespace pldm
diff --git a/platform-mc/event_manager.hpp b/platform-mc/event_manager.hpp
index f40db44..c654eb7 100644
--- a/platform-mc/event_manager.hpp
+++ b/platform-mc/event_manager.hpp
@@ -87,6 +87,30 @@
const uint8_t* sensorData,
size_t sensorDataLength);
+ /** @brief Helper method to process the PLDM CPER event class
+ *
+ * @param[in] tid - tid where the event is from
+ * @param[in] eventId - Event ID which is the source of event
+ * @param[in] eventData - CPER event data
+ * @param[in] eventDataSize - event data length
+ *
+ * @return PLDM completion code
+ */
+ int processCperEvent(pldm_tid_t tid, uint16_t eventId,
+ const uint8_t* eventData, const size_t eventDataSize);
+
+ /** @brief Helper method to create CPER dump log
+ *
+ * @param[in] dataType - CPER event data type
+ * @param[in] dataPath - CPER event data fault log file path
+ * @param[in] typeName - Terminus name which creates CPER event
+ *
+ * @return PLDM completion code
+ */
+ int createCperDumpEntry(const std::string& dataType,
+ const std::string& dataPath,
+ const std::string& typeName);
+
/** @brief Reference of terminusManager */
TerminusManager& terminusManager;
diff --git a/platform-mc/manager.hpp b/platform-mc/manager.hpp
index a4fd466..c499f88 100644
--- a/platform-mc/manager.hpp
+++ b/platform-mc/manager.hpp
@@ -101,7 +101,7 @@
sensorManager.stopPolling(tid);
}
- /** @brief Sensor event handler funtion
+ /** @brief Sensor event handler function
*
* @param[in] request - Event message
* @param[in] payloadLength - Event message payload size
@@ -118,8 +118,32 @@
auto eventData = reinterpret_cast<const uint8_t*>(request->payload) +
eventDataOffset;
auto eventDataSize = payloadLength - eventDataOffset;
- eventManager.handlePlatformEvent(tid, 0x00, PLDM_SENSOR_EVENT,
- eventData, eventDataSize);
+ eventManager.handlePlatformEvent(tid, PLDM_PLATFORM_EVENT_ID_NULL,
+ PLDM_SENSOR_EVENT, eventData,
+ eventDataSize);
+ return PLDM_SUCCESS;
+ }
+
+ /** @brief CPER event handler function
+ *
+ * @param[in] request - Event message
+ * @param[in] payloadLength - Event message payload size
+ * @param[in] tid - Terminus ID
+ * @param[in] eventDataOffset - Event data offset
+ *
+ * @return PLDM error code: PLDM_SUCCESS when there is no error in handling
+ * the event
+ */
+ int handleCperEvent(const pldm_msg* request, size_t payloadLength,
+ uint8_t /* formatVersion */, uint8_t tid,
+ size_t eventDataOffset)
+ {
+ auto eventData =
+ const_cast<const uint8_t*>(request->payload) + eventDataOffset;
+ auto eventDataSize = payloadLength - eventDataOffset;
+ eventManager.handlePlatformEvent(tid, PLDM_PLATFORM_EVENT_ID_NULL,
+ PLDM_CPER_EVENT, eventData,
+ eventDataSize);
return PLDM_SUCCESS;
}
diff --git a/platform-mc/terminus.hpp b/platform-mc/terminus.hpp
index dd766c6..464897f 100644
--- a/platform-mc/terminus.hpp
+++ b/platform-mc/terminus.hpp
@@ -122,8 +122,12 @@
}
/** @brief The getter to get terminus's mctp medium */
- std::string_view getTerminusName()
+ std::optional<std::string_view> getTerminusName()
{
+ if (terminusName.empty())
+ {
+ return std::nullopt;
+ }
return terminusName;
}
diff --git a/platform-mc/test/platform_manager_test.cpp b/platform-mc/test/platform_manager_test.cpp
index 0d66a0b..09f94e5 100644
--- a/platform-mc/test/platform_manager_test.cpp
+++ b/platform-mc/test/platform_manager_test.cpp
@@ -187,7 +187,7 @@
EXPECT_EQ(true, terminus->initialized);
EXPECT_EQ(2, terminus->pdrs.size());
EXPECT_EQ(1, terminus->numericSensors.size());
- EXPECT_EQ("S0", terminus->getTerminusName());
+ EXPECT_EQ("S0", terminus->getTerminusName().value());
}
TEST_F(PlatformManagerTest, parseTerminusNameTest)
@@ -345,7 +345,7 @@
stdexec::sync_wait(platformManager.initTerminus());
EXPECT_EQ(true, terminus->initialized);
EXPECT_EQ(2, terminus->pdrs.size());
- EXPECT_EQ("S0", terminus->getTerminusName());
+ EXPECT_EQ("S0", terminus->getTerminusName().value());
}
TEST_F(PlatformManagerTest, initTerminusDontSupportGetPDRTest)
diff --git a/platform-mc/test/terminus_test.cpp b/platform-mc/test/terminus_test.cpp
index 609deed..a8dcbc1 100644
--- a/platform-mc/test/terminus_test.cpp
+++ b/platform-mc/test/terminus_test.cpp
@@ -105,7 +105,7 @@
EXPECT_EQ("en", names[0][0].first);
EXPECT_EQ("TEMP1", names[0][0].second);
EXPECT_EQ(2, t1.pdrs.size());
- EXPECT_EQ("S0", t1.getTerminusName());
+ EXPECT_EQ("S0", t1.getTerminusName().value());
}
TEST(TerminusTest, parseSensorAuxiliaryMultiNamesPDRTest)
@@ -224,7 +224,7 @@
EXPECT_EQ("fr", names[0][2].first);
EXPECT_EQ("TEMP12", names[0][2].second);
EXPECT_EQ(2, t1.pdrs.size());
- EXPECT_EQ("S0", t1.getTerminusName());
+ EXPECT_EQ("S0", t1.getTerminusName().value());
}
TEST(TerminusTest, parseSensorAuxiliaryNamesMultiSensorsPDRTest)
@@ -345,7 +345,7 @@
EXPECT_EQ("fr", names[1][1].first);
EXPECT_EQ("TEMP12", names[1][1].second);
EXPECT_EQ(2, t1.pdrs.size());
- EXPECT_EQ("S0", t1.getTerminusName());
+ EXPECT_EQ("S0", t1.getTerminusName().value());
}
TEST(TerminusTest, parsePDRTestNoSensorPDR)