oem-ampere: eventManager: Handle `BootProgress` sensor event
Add Ampere OEM code to handle the `sensorEvent` for PLDM `BootProgress`
sensor. In Ampere system, the SOC termini will have the TID 1 or 2. The
Ampere OEM EventManager will check the terminus TID to confirm about the
terminus type. Base on the value of `BootProgress` sensor, the OEM code
will add the Redfish Log to report the boot progress of Ampere SoC.
Tested:
1. Power on the host.
2. Check the Redfish SEL log.
Signed-off-by: Chau Ly <chaul@amperecomputing.com>
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: Icc51537ef17ee8eb4b5b571eafeea7b5d7763cbe
diff --git a/oem/ampere/event/oem_event_manager.cpp b/oem/ampere/event/oem_event_manager.cpp
new file mode 100644
index 0000000..e99288d
--- /dev/null
+++ b/oem/ampere/event/oem_event_manager.cpp
@@ -0,0 +1,427 @@
+#include "oem_event_manager.hpp"
+
+#include "requester/handler.hpp"
+#include "requester/request.hpp"
+
+#include <config.h>
+#include <libpldm/pldm.h>
+#include <libpldm/utils.h>
+#include <systemd/sd-journal.h>
+
+#include <phosphor-logging/lg2.hpp>
+#include <xyz/openbmc_project/Logging/Entry/server.hpp>
+
+#include <algorithm>
+#include <map>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+
+namespace pldm
+{
+namespace oem_ampere
+{
+namespace boot_stage = boot::stage;
+
+constexpr const char* BIOSFWPanicRegistry =
+ "OpenBMC.0.1.BIOSFirmwarePanicReason.Warning";
+constexpr auto maxDIMMIdxBitNum = 24;
+
+/*
+ An array of possible boot status of a boot stage.
+ The index maps with byte 0 of boot code.
+*/
+std::array<std::string, 3> bootStatMsg = {" booting", " completed", " failed"};
+
+/*
+ An array of possible boot status of DDR training stage.
+ The index maps with byte 0 of boot code.
+*/
+std::array<std::string, 3> ddrTrainingMsg = {
+ " progress started", " in-progress", " progress completed"};
+
+/*
+ In Ampere systems, BMC only directly communicates with MCTP/PLDM SoC
+ EPs through SMBus and PCIe. When host boots up, SMBUS interface
+ comes up first. In this interface, BMC is bus owner.
+
+ mctpd will set the EID 0x14 for S0 and 0x16 for S1 (if available).
+ pldmd will always use TID 1 for S0 and TID 2 for S1 (if available).
+*/
+EventToMsgMap_t tidToSocketNameMap = {{1, "SOCKET 0"}, {2, "SOCKET 1"}};
+
+/*
+ A map between sensor IDs and their names in string.
+ Using pldm::oem::sensor_ids
+*/
+EventToMsgMap_t sensorIdToStrMap = {{BOOT_OVERALL, "BOOT_OVERALL"}};
+
+/*
+ A map between the boot stages and logging strings.
+ Using pldm::oem::boot::stage::boot_stage
+*/
+EventToMsgMap_t bootStageToMsgMap = {
+ {boot_stage::SECPRO, "SECpro"},
+ {boot_stage::MPRO, "Mpro"},
+ {boot_stage::ATF_BL1, "ATF BL1"},
+ {boot_stage::ATF_BL2, "ATF BL2"},
+ {boot_stage::DDR_INITIALIZATION, "DDR initialization"},
+ {boot_stage::DDR_TRAINING, "DDR training"},
+ {boot_stage::S0_DDR_TRAINING_FAILURE, "DDR training failure"},
+ {boot_stage::ATF_BL31, "ATF BL31"},
+ {boot_stage::ATF_BL32, "ATF BL32"},
+ {boot_stage::S1_DDR_TRAINING_FAILURE, "DDR training failure"},
+ {boot_stage::UEFI_STATUS_CLASS_CODE_MIN,
+ "ATF BL33 (UEFI) booting status = "}};
+
+/*
+ A map between log level and the registry used for Redfish SEL log
+ Using pldm::oem::log_level
+*/
+std::unordered_map<log_level, std::string> logLevelToRedfishMsgIdMap = {
+ {log_level::BIOSFWPANIC, BIOSFWPanicRegistry}};
+
+std::string
+ OemEventManager::prefixMsgStrCreation(pldm_tid_t tid, uint16_t sensorId)
+{
+ std::string description;
+ if (!tidToSocketNameMap.contains(tid))
+ {
+ description += "TID " + std::to_string(tid) + ": ";
+ }
+ else
+ {
+ description += tidToSocketNameMap[tid] + ": ";
+ }
+
+ if (!sensorIdToStrMap.contains(sensorId))
+ {
+ description += "Sensor ID " + std::to_string(sensorId) + ": ";
+ }
+ else
+ {
+ description += sensorIdToStrMap[sensorId] + ": ";
+ }
+
+ return description;
+}
+
+void OemEventManager::sendJournalRedfish(const std::string& description,
+ log_level& logLevel)
+{
+ if (description.empty())
+ {
+ return;
+ }
+
+ if (!logLevelToRedfishMsgIdMap.contains(logLevel))
+ {
+ lg2::error("Invalid {LEVEL} Description {DES}", "LEVEL", logLevel,
+ "DES", description);
+ return;
+ }
+ auto redfishMsgId = logLevelToRedfishMsgIdMap[logLevel];
+ lg2::info("MESSAGE={DES}", "DES", description, "REDFISH_MESSAGE_ID",
+ redfishMsgId, "REDFISH_MESSAGE_ARGS", description);
+}
+
+std::string OemEventManager::dimmIdxsToString(uint32_t dimmIdxs)
+{
+ std::string description;
+ for (const auto bitIdx : std::views::iota(0, maxDIMMIdxBitNum))
+ {
+ if (dimmIdxs & (static_cast<uint32_t>(1) << bitIdx))
+ {
+ description += " #" + std::to_string(bitIdx);
+ }
+ }
+ return description;
+}
+
+void OemEventManager::handleBootOverallEvent(
+ pldm_tid_t /*tid*/, uint16_t /*sensorId*/, uint32_t presentReading)
+{
+ log_level logLevel{log_level::OK};
+ std::string description;
+ std::stringstream strStream;
+
+ uint8_t byte0 = (presentReading & 0x000000ff);
+ uint8_t byte1 = (presentReading & 0x0000ff00) >> 8;
+ uint8_t byte2 = (presentReading & 0x00ff0000) >> 16;
+ uint8_t byte3 = (presentReading & 0xff000000) >> 24;
+ /*
+ * Handle SECpro, Mpro, ATF BL1, ATF BL2, ATF BL31,
+ * ATF BL32 and DDR initialization
+ */
+ if (bootStageToMsgMap.contains(byte3))
+ {
+ // Boot stage adding
+ description += bootStageToMsgMap[byte3];
+
+ switch (byte3)
+ {
+ case boot_stage::DDR_TRAINING:
+ if (byte0 >= ddrTrainingMsg.size())
+ {
+ logLevel = log_level::BIOSFWPANIC;
+ description += " unknown status";
+ }
+ else
+ {
+ description += ddrTrainingMsg[byte0];
+ }
+ if (0x01 == byte0)
+ {
+ // Add complete percentage
+ description += " at " + std::to_string(byte1) + "%";
+ }
+ break;
+ case boot_stage::S0_DDR_TRAINING_FAILURE:
+ case boot_stage::S1_DDR_TRAINING_FAILURE:
+ // ddr_training_status_msg()
+ logLevel = log_level::BIOSFWPANIC;
+ description += " at DIMMs:";
+ // dimmIdxs = presentReading & 0x00ffffff;
+ description += dimmIdxsToString(presentReading & 0x00ffffff);
+ description += " of socket ";
+ description +=
+ (boot_stage::S0_DDR_TRAINING_FAILURE == byte3) ? "0" : "1";
+ break;
+ default:
+ if (byte0 >= bootStatMsg.size())
+ {
+ logLevel = log_level::BIOSFWPANIC;
+ description += " unknown status";
+ }
+ else
+ {
+ description += bootStatMsg[byte0];
+ }
+ break;
+ }
+
+ // Sensor report action is fail
+ if (boot::status::BOOT_STATUS_FAILURE == byte2)
+ {
+ logLevel = log_level::BIOSFWPANIC;
+ }
+ }
+ else
+ {
+ if (byte3 <= boot_stage::UEFI_STATUS_CLASS_CODE_MAX)
+ {
+ description +=
+ bootStageToMsgMap[boot_stage::UEFI_STATUS_CLASS_CODE_MIN];
+
+ strStream
+ << "Segment (0x" << std::setfill('0') << std::hex
+ << std::setw(8) << static_cast<uint32_t>(presentReading)
+ << "), Status Class (0x" << std::setw(2)
+ << static_cast<uint32_t>(byte3) << "), Status SubClass (0x"
+ << std::setw(2) << static_cast<uint32_t>(byte2)
+ << "), Operation Code (0x" << std::setw(4)
+ << static_cast<uint32_t>((presentReading & 0xffff0000) >> 16)
+ << ")" << std::dec;
+
+ description += strStream.str();
+ }
+ }
+
+ // Log to Redfish event
+ sendJournalRedfish(description, logLevel);
+}
+
+int OemEventManager::processNumericSensorEvent(
+ pldm_tid_t tid, uint16_t sensorId, const uint8_t* sensorData,
+ size_t sensorDataLength)
+{
+ uint8_t eventState = 0;
+ uint8_t previousEventState = 0;
+ uint8_t sensorDataSize = 0;
+ uint32_t presentReading;
+ auto rc = decode_numeric_sensor_data(
+ sensorData, sensorDataLength, &eventState, &previousEventState,
+ &sensorDataSize, &presentReading);
+ if (rc)
+ {
+ lg2::error(
+ "Failed to decode numericSensorState event for terminus ID {TID}, error {RC} ",
+ "TID", tid, "RC", rc);
+ return rc;
+ }
+
+ switch (sensorId)
+ {
+ case BOOT_OVERALL:
+ handleBootOverallEvent(tid, sensorId, presentReading);
+ break;
+ default:
+ std::string description;
+ std::stringstream strStream;
+ log_level logLevel = log_level::OK;
+
+ description += "SENSOR_EVENT : NUMERIC_SENSOR_STATE: ";
+ description += prefixMsgStrCreation(tid, sensorId);
+ strStream << std::setfill('0') << std::hex << "eventState 0x"
+ << std::setw(2) << static_cast<uint32_t>(eventState)
+ << " previousEventState 0x" << std::setw(2)
+ << static_cast<uint32_t>(previousEventState)
+ << " sensorDataSize 0x" << std::setw(2)
+ << static_cast<uint32_t>(sensorDataSize)
+ << " presentReading 0x" << std::setw(8)
+ << static_cast<uint32_t>(presentReading) << std::dec;
+ description += strStream.str();
+
+ sendJournalRedfish(description, logLevel);
+ break;
+ }
+ return PLDM_SUCCESS;
+}
+
+int OemEventManager::processStateSensorEvent(pldm_tid_t tid, uint16_t sensorId,
+ const uint8_t* sensorData,
+ size_t sensorDataLength)
+{
+ uint8_t sensorOffset = 0;
+ uint8_t eventState = 0;
+ uint8_t previousEventState = 0;
+
+ auto rc =
+ decode_state_sensor_data(sensorData, sensorDataLength, &sensorOffset,
+ &eventState, &previousEventState);
+ if (rc)
+ {
+ lg2::error(
+ "Failed to decode stateSensorState event for terminus ID {TID}, error {RC}",
+ "TID", tid, "RC", rc);
+ return rc;
+ }
+
+ std::string description;
+ std::stringstream strStream;
+ log_level logLevel = log_level::OK;
+
+ description += "SENSOR_EVENT : STATE_SENSOR_STATE: ";
+ description += prefixMsgStrCreation(tid, sensorId);
+ strStream << std::setfill('0') << std::hex << "sensorOffset 0x"
+ << std::setw(2) << static_cast<uint32_t>(sensorOffset)
+ << "eventState 0x" << std::setw(2)
+ << static_cast<uint32_t>(eventState) << " previousEventState 0x"
+ << std::setw(2) << static_cast<uint32_t>(previousEventState)
+ << std::dec;
+ description += strStream.str();
+
+ sendJournalRedfish(description, logLevel);
+
+ return PLDM_SUCCESS;
+}
+
+int OemEventManager::processSensorOpStateEvent(
+ pldm_tid_t tid, uint16_t sensorId, const uint8_t* sensorData,
+ size_t sensorDataLength)
+{
+ uint8_t present_op_state = 0;
+ uint8_t previous_op_state = 0;
+
+ auto rc = decode_sensor_op_data(sensorData, sensorDataLength,
+ &present_op_state, &previous_op_state);
+ if (rc)
+ {
+ lg2::error(
+ "Failed to decode sensorOpState event for terminus ID {TID}, error {RC}",
+ "TID", tid, "RC", rc);
+ return rc;
+ }
+
+ std::string description;
+ std::stringstream strStream;
+ log_level logLevel = log_level::OK;
+
+ description += "SENSOR_EVENT : SENSOR_OP_STATE: ";
+ description += prefixMsgStrCreation(tid, sensorId);
+ strStream << std::setfill('0') << std::hex << "present_op_state 0x"
+ << std::setw(2) << static_cast<uint32_t>(present_op_state)
+ << "previous_op_state 0x" << std::setw(2)
+ << static_cast<uint32_t>(previous_op_state) << std::dec;
+ description += strStream.str();
+
+ sendJournalRedfish(description, logLevel);
+
+ return PLDM_SUCCESS;
+}
+
+int OemEventManager::handleSensorEvent(
+ const pldm_msg* request, size_t payloadLength, uint8_t /* formatVersion */,
+ pldm_tid_t tid, size_t eventDataOffset)
+{
+ /* This OEM event handler is only used for SoC terminus*/
+ if (!tidToSocketNameMap.contains(tid))
+ {
+ return PLDM_SUCCESS;
+ }
+ auto eventData =
+ reinterpret_cast<const uint8_t*>(request->payload) + eventDataOffset;
+ auto eventDataSize = payloadLength - eventDataOffset;
+
+ uint16_t sensorId = 0;
+ uint8_t sensorEventClassType = 0;
+ size_t eventClassDataOffset = 0;
+ auto rc =
+ decode_sensor_event_data(eventData, eventDataSize, &sensorId,
+ &sensorEventClassType, &eventClassDataOffset);
+ if (rc)
+ {
+ lg2::error("Failed to decode sensor event data return code {RC}.", "RC",
+ rc);
+ return rc;
+ }
+ const uint8_t* sensorData = eventData + eventClassDataOffset;
+ size_t sensorDataLength = eventDataSize - eventClassDataOffset;
+
+ switch (sensorEventClassType)
+ {
+ case PLDM_NUMERIC_SENSOR_STATE:
+ {
+ return processNumericSensorEvent(tid, sensorId, sensorData,
+ sensorDataLength);
+ }
+ case PLDM_STATE_SENSOR_STATE:
+ {
+ return processStateSensorEvent(tid, sensorId, sensorData,
+ sensorDataLength);
+ }
+ case PLDM_SENSOR_OP_STATE:
+ {
+ return processSensorOpStateEvent(tid, sensorId, sensorData,
+ sensorDataLength);
+ }
+ default:
+ std::string description;
+ std::stringstream strStream;
+ log_level logLevel = log_level::OK;
+
+ description += "SENSOR_EVENT : Unsupported Sensor Class " +
+ std::to_string(sensorEventClassType) + ": ";
+ description += prefixMsgStrCreation(tid, sensorId);
+ strStream << std::setfill('0') << std::hex
+ << std::setw(sizeof(sensorData) * 2) << "Sensor data: ";
+
+ auto dataPtr = sensorData;
+ for ([[maybe_unused]] const auto& i :
+ std::views::iota(0, (int)sensorDataLength))
+ {
+ strStream << "0x" << static_cast<uint32_t>(*dataPtr);
+ dataPtr += sizeof(sensorData);
+ }
+
+ description += strStream.str();
+
+ sendJournalRedfish(description, logLevel);
+ }
+ lg2::info("Unsupported class type {CLASSTYPE}", "CLASSTYPE",
+ sensorEventClassType);
+ return PLDM_ERROR;
+}
+
+} // namespace oem_ampere
+} // namespace pldm