1 #include "oem_event_manager.hpp" 2 3 #include "requester/handler.hpp" 4 #include "requester/request.hpp" 5 6 #include <config.h> 7 #include <libpldm/pldm.h> 8 #include <libpldm/utils.h> 9 #include <systemd/sd-journal.h> 10 11 #include <phosphor-logging/lg2.hpp> 12 #include <xyz/openbmc_project/Logging/Entry/server.hpp> 13 14 #include <algorithm> 15 #include <map> 16 #include <sstream> 17 #include <string> 18 #include <unordered_map> 19 20 namespace pldm 21 { 22 namespace oem_ampere 23 { 24 namespace boot_stage = boot::stage; 25 26 constexpr const char* ampereEventRegistry = "OpenBMC.0.1.AmpereEvent.OK"; 27 constexpr const char* ampereWarningRegistry = 28 "OpenBMC.0.1.AmpereWarning.Warning"; 29 constexpr const char* ampereCriticalRegistry = 30 "OpenBMC.0.1.AmpereCritical.Critical"; 31 constexpr const char* BIOSFWPanicRegistry = 32 "OpenBMC.0.1.BIOSFirmwarePanicReason.Warning"; 33 constexpr auto maxDIMMIdxBitNum = 24; 34 35 /* 36 An array of possible boot status of a boot stage. 37 The index maps with byte 0 of boot code. 38 */ 39 std::array<std::string, 3> bootStatMsg = {" booting", " completed", " failed"}; 40 41 /* 42 An array of possible boot status of DDR training stage. 43 The index maps with byte 0 of boot code. 44 */ 45 std::array<std::string, 3> ddrTrainingMsg = { 46 " progress started", " in-progress", " progress completed"}; 47 48 /* 49 In Ampere systems, BMC only directly communicates with MCTP/PLDM SoC 50 EPs through SMBus and PCIe. When host boots up, SMBUS interface 51 comes up first. In this interface, BMC is bus owner. 52 53 mctpd will set the EID 0x14 for S0 and 0x16 for S1 (if available). 54 pldmd will always use TID 1 for S0 and TID 2 for S1 (if available). 55 */ 56 EventToMsgMap_t tidToSocketNameMap = {{1, "SOCKET 0"}, {2, "SOCKET 1"}}; 57 58 /* 59 A map between sensor IDs and their names in string. 60 Using pldm::oem::sensor_ids 61 */ 62 EventToMsgMap_t sensorIdToStrMap = {{PCIE_HOT_PLUG, "PCIE_HOT_PLUG"}, 63 {BOOT_OVERALL, "BOOT_OVERALL"}}; 64 65 /* 66 A map between the boot stages and logging strings. 67 Using pldm::oem::boot::stage::boot_stage 68 */ 69 EventToMsgMap_t bootStageToMsgMap = { 70 {boot_stage::SECPRO, "SECpro"}, 71 {boot_stage::MPRO, "Mpro"}, 72 {boot_stage::ATF_BL1, "ATF BL1"}, 73 {boot_stage::ATF_BL2, "ATF BL2"}, 74 {boot_stage::DDR_INITIALIZATION, "DDR initialization"}, 75 {boot_stage::DDR_TRAINING, "DDR training"}, 76 {boot_stage::S0_DDR_TRAINING_FAILURE, "DDR training failure"}, 77 {boot_stage::ATF_BL31, "ATF BL31"}, 78 {boot_stage::ATF_BL32, "ATF BL32"}, 79 {boot_stage::S1_DDR_TRAINING_FAILURE, "DDR training failure"}, 80 {boot_stage::UEFI_STATUS_CLASS_CODE_MIN, 81 "ATF BL33 (UEFI) booting status = "}}; 82 83 /* 84 A map between log level and the registry used for Redfish SEL log 85 Using pldm::oem::log_level 86 */ 87 std::unordered_map<log_level, std::string> logLevelToRedfishMsgIdMap = { 88 {log_level::OK, ampereEventRegistry}, 89 {log_level::WARNING, ampereWarningRegistry}, 90 {log_level::CRITICAL, ampereCriticalRegistry}, 91 {log_level::BIOSFWPANIC, BIOSFWPanicRegistry}}; 92 93 std::string 94 OemEventManager::prefixMsgStrCreation(pldm_tid_t tid, uint16_t sensorId) 95 { 96 std::string description; 97 if (!tidToSocketNameMap.contains(tid)) 98 { 99 description += "TID " + std::to_string(tid) + ": "; 100 } 101 else 102 { 103 description += tidToSocketNameMap[tid] + ": "; 104 } 105 106 if (!sensorIdToStrMap.contains(sensorId)) 107 { 108 description += "Sensor ID " + std::to_string(sensorId) + ": "; 109 } 110 else 111 { 112 description += sensorIdToStrMap[sensorId] + ": "; 113 } 114 115 return description; 116 } 117 118 void OemEventManager::sendJournalRedfish(const std::string& description, 119 log_level& logLevel) 120 { 121 if (description.empty()) 122 { 123 return; 124 } 125 126 if (!logLevelToRedfishMsgIdMap.contains(logLevel)) 127 { 128 lg2::error("Invalid {LEVEL} Description {DES}", "LEVEL", logLevel, 129 "DES", description); 130 return; 131 } 132 auto redfishMsgId = logLevelToRedfishMsgIdMap[logLevel]; 133 lg2::info("MESSAGE={DES}", "DES", description, "REDFISH_MESSAGE_ID", 134 redfishMsgId, "REDFISH_MESSAGE_ARGS", description); 135 } 136 137 std::string OemEventManager::dimmIdxsToString(uint32_t dimmIdxs) 138 { 139 std::string description; 140 for (const auto bitIdx : std::views::iota(0, maxDIMMIdxBitNum)) 141 { 142 if (dimmIdxs & (static_cast<uint32_t>(1) << bitIdx)) 143 { 144 description += " #" + std::to_string(bitIdx); 145 } 146 } 147 return description; 148 } 149 150 void OemEventManager::handleBootOverallEvent( 151 pldm_tid_t /*tid*/, uint16_t /*sensorId*/, uint32_t presentReading) 152 { 153 log_level logLevel{log_level::OK}; 154 std::string description; 155 std::stringstream strStream; 156 157 uint8_t byte0 = (presentReading & 0x000000ff); 158 uint8_t byte1 = (presentReading & 0x0000ff00) >> 8; 159 uint8_t byte2 = (presentReading & 0x00ff0000) >> 16; 160 uint8_t byte3 = (presentReading & 0xff000000) >> 24; 161 /* 162 * Handle SECpro, Mpro, ATF BL1, ATF BL2, ATF BL31, 163 * ATF BL32 and DDR initialization 164 */ 165 if (bootStageToMsgMap.contains(byte3)) 166 { 167 // Boot stage adding 168 description += bootStageToMsgMap[byte3]; 169 170 switch (byte3) 171 { 172 case boot_stage::DDR_TRAINING: 173 if (byte0 >= ddrTrainingMsg.size()) 174 { 175 logLevel = log_level::BIOSFWPANIC; 176 description += " unknown status"; 177 } 178 else 179 { 180 description += ddrTrainingMsg[byte0]; 181 } 182 if (0x01 == byte0) 183 { 184 // Add complete percentage 185 description += " at " + std::to_string(byte1) + "%"; 186 } 187 break; 188 case boot_stage::S0_DDR_TRAINING_FAILURE: 189 case boot_stage::S1_DDR_TRAINING_FAILURE: 190 // ddr_training_status_msg() 191 logLevel = log_level::BIOSFWPANIC; 192 description += " at DIMMs:"; 193 // dimmIdxs = presentReading & 0x00ffffff; 194 description += dimmIdxsToString(presentReading & 0x00ffffff); 195 description += " of socket "; 196 description += 197 (boot_stage::S0_DDR_TRAINING_FAILURE == byte3) ? "0" : "1"; 198 break; 199 default: 200 if (byte0 >= bootStatMsg.size()) 201 { 202 logLevel = log_level::BIOSFWPANIC; 203 description += " unknown status"; 204 } 205 else 206 { 207 description += bootStatMsg[byte0]; 208 } 209 break; 210 } 211 212 // Sensor report action is fail 213 if (boot::status::BOOT_STATUS_FAILURE == byte2) 214 { 215 logLevel = log_level::BIOSFWPANIC; 216 } 217 } 218 else 219 { 220 if (byte3 <= boot_stage::UEFI_STATUS_CLASS_CODE_MAX) 221 { 222 description += 223 bootStageToMsgMap[boot_stage::UEFI_STATUS_CLASS_CODE_MIN]; 224 225 strStream 226 << "Segment (0x" << std::setfill('0') << std::hex 227 << std::setw(8) << static_cast<uint32_t>(presentReading) 228 << "); Status Class (0x" << std::setw(2) 229 << static_cast<uint32_t>(byte3) << "); Status SubClass (0x" 230 << std::setw(2) << static_cast<uint32_t>(byte2) 231 << "); Operation Code (0x" << std::setw(4) 232 << static_cast<uint32_t>((presentReading & 0xffff0000) >> 16) 233 << ")" << std::dec; 234 235 description += strStream.str(); 236 } 237 } 238 239 // Log to Redfish event 240 sendJournalRedfish(description, logLevel); 241 } 242 243 int OemEventManager::processNumericSensorEvent( 244 pldm_tid_t tid, uint16_t sensorId, const uint8_t* sensorData, 245 size_t sensorDataLength) 246 { 247 uint8_t eventState = 0; 248 uint8_t previousEventState = 0; 249 uint8_t sensorDataSize = 0; 250 uint32_t presentReading; 251 auto rc = decode_numeric_sensor_data( 252 sensorData, sensorDataLength, &eventState, &previousEventState, 253 &sensorDataSize, &presentReading); 254 if (rc) 255 { 256 lg2::error( 257 "Failed to decode numericSensorState event for terminus ID {TID}, error {RC} ", 258 "TID", tid, "RC", rc); 259 return rc; 260 } 261 262 switch (sensorId) 263 { 264 case BOOT_OVERALL: 265 handleBootOverallEvent(tid, sensorId, presentReading); 266 break; 267 case PCIE_HOT_PLUG: 268 handlePCIeHotPlugEvent(tid, sensorId, presentReading); 269 break; 270 default: 271 std::string description; 272 std::stringstream strStream; 273 log_level logLevel = log_level::OK; 274 275 description += "SENSOR_EVENT : NUMERIC_SENSOR_STATE: "; 276 description += prefixMsgStrCreation(tid, sensorId); 277 strStream << std::setfill('0') << std::hex << "eventState 0x" 278 << std::setw(2) << static_cast<uint32_t>(eventState) 279 << " previousEventState 0x" << std::setw(2) 280 << static_cast<uint32_t>(previousEventState) 281 << " sensorDataSize 0x" << std::setw(2) 282 << static_cast<uint32_t>(sensorDataSize) 283 << " presentReading 0x" << std::setw(8) 284 << static_cast<uint32_t>(presentReading) << std::dec; 285 description += strStream.str(); 286 287 sendJournalRedfish(description, logLevel); 288 break; 289 } 290 return PLDM_SUCCESS; 291 } 292 293 int OemEventManager::processStateSensorEvent(pldm_tid_t tid, uint16_t sensorId, 294 const uint8_t* sensorData, 295 size_t sensorDataLength) 296 { 297 uint8_t sensorOffset = 0; 298 uint8_t eventState = 0; 299 uint8_t previousEventState = 0; 300 301 auto rc = 302 decode_state_sensor_data(sensorData, sensorDataLength, &sensorOffset, 303 &eventState, &previousEventState); 304 if (rc) 305 { 306 lg2::error( 307 "Failed to decode stateSensorState event for terminus ID {TID}, error {RC}", 308 "TID", tid, "RC", rc); 309 return rc; 310 } 311 312 std::string description; 313 std::stringstream strStream; 314 log_level logLevel = log_level::OK; 315 316 description += "SENSOR_EVENT : STATE_SENSOR_STATE: "; 317 description += prefixMsgStrCreation(tid, sensorId); 318 strStream << std::setfill('0') << std::hex << "sensorOffset 0x" 319 << std::setw(2) << static_cast<uint32_t>(sensorOffset) 320 << "eventState 0x" << std::setw(2) 321 << static_cast<uint32_t>(eventState) << " previousEventState 0x" 322 << std::setw(2) << static_cast<uint32_t>(previousEventState) 323 << std::dec; 324 description += strStream.str(); 325 326 sendJournalRedfish(description, logLevel); 327 328 return PLDM_SUCCESS; 329 } 330 331 int OemEventManager::processSensorOpStateEvent( 332 pldm_tid_t tid, uint16_t sensorId, const uint8_t* sensorData, 333 size_t sensorDataLength) 334 { 335 uint8_t present_op_state = 0; 336 uint8_t previous_op_state = 0; 337 338 auto rc = decode_sensor_op_data(sensorData, sensorDataLength, 339 &present_op_state, &previous_op_state); 340 if (rc) 341 { 342 lg2::error( 343 "Failed to decode sensorOpState event for terminus ID {TID}, error {RC}", 344 "TID", tid, "RC", rc); 345 return rc; 346 } 347 348 std::string description; 349 std::stringstream strStream; 350 log_level logLevel = log_level::OK; 351 352 description += "SENSOR_EVENT : SENSOR_OP_STATE: "; 353 description += prefixMsgStrCreation(tid, sensorId); 354 strStream << std::setfill('0') << std::hex << "present_op_state 0x" 355 << std::setw(2) << static_cast<uint32_t>(present_op_state) 356 << "previous_op_state 0x" << std::setw(2) 357 << static_cast<uint32_t>(previous_op_state) << std::dec; 358 description += strStream.str(); 359 360 sendJournalRedfish(description, logLevel); 361 362 return PLDM_SUCCESS; 363 } 364 365 int OemEventManager::handleSensorEvent( 366 const pldm_msg* request, size_t payloadLength, uint8_t /* formatVersion */, 367 pldm_tid_t tid, size_t eventDataOffset) 368 { 369 /* This OEM event handler is only used for SoC terminus*/ 370 if (!tidToSocketNameMap.contains(tid)) 371 { 372 return PLDM_SUCCESS; 373 } 374 auto eventData = 375 reinterpret_cast<const uint8_t*>(request->payload) + eventDataOffset; 376 auto eventDataSize = payloadLength - eventDataOffset; 377 378 uint16_t sensorId = 0; 379 uint8_t sensorEventClassType = 0; 380 size_t eventClassDataOffset = 0; 381 auto rc = 382 decode_sensor_event_data(eventData, eventDataSize, &sensorId, 383 &sensorEventClassType, &eventClassDataOffset); 384 if (rc) 385 { 386 lg2::error("Failed to decode sensor event data return code {RC}.", "RC", 387 rc); 388 return rc; 389 } 390 const uint8_t* sensorData = eventData + eventClassDataOffset; 391 size_t sensorDataLength = eventDataSize - eventClassDataOffset; 392 393 switch (sensorEventClassType) 394 { 395 case PLDM_NUMERIC_SENSOR_STATE: 396 { 397 return processNumericSensorEvent(tid, sensorId, sensorData, 398 sensorDataLength); 399 } 400 case PLDM_STATE_SENSOR_STATE: 401 { 402 return processStateSensorEvent(tid, sensorId, sensorData, 403 sensorDataLength); 404 } 405 case PLDM_SENSOR_OP_STATE: 406 { 407 return processSensorOpStateEvent(tid, sensorId, sensorData, 408 sensorDataLength); 409 } 410 default: 411 std::string description; 412 std::stringstream strStream; 413 log_level logLevel = log_level::OK; 414 415 description += "SENSOR_EVENT : Unsupported Sensor Class " + 416 std::to_string(sensorEventClassType) + ": "; 417 description += prefixMsgStrCreation(tid, sensorId); 418 strStream << std::setfill('0') << std::hex 419 << std::setw(sizeof(sensorData) * 2) << "Sensor data: "; 420 421 auto dataPtr = sensorData; 422 for ([[maybe_unused]] const auto& i : 423 std::views::iota(0, (int)sensorDataLength)) 424 { 425 strStream << "0x" << static_cast<uint32_t>(*dataPtr); 426 dataPtr += sizeof(sensorData); 427 } 428 429 description += strStream.str(); 430 431 sendJournalRedfish(description, logLevel); 432 } 433 lg2::info("Unsupported class type {CLASSTYPE}", "CLASSTYPE", 434 sensorEventClassType); 435 return PLDM_ERROR; 436 } 437 438 void OemEventManager::handlePCIeHotPlugEvent(pldm_tid_t tid, uint16_t sensorId, 439 uint32_t presentReading) 440 { 441 std::string description; 442 std::stringstream strStream; 443 PCIeHotPlugEventRecord_t record{presentReading}; 444 445 std::string sAction = (!record.bits.action) ? "Insertion" : "Removal"; 446 std::string sOpStatus = (!record.bits.opStatus) ? "Successful" : "Failed"; 447 log_level logLevel = 448 (!record.bits.opStatus) ? log_level::OK : log_level::WARNING; 449 450 description += prefixMsgStrCreation(tid, sensorId); 451 452 strStream << "Segment (0x" << std::setfill('0') << std::hex << std::setw(2) 453 << static_cast<uint32_t>(record.bits.segment) << "); Bus (0x" 454 << std::setw(2) << static_cast<uint32_t>(record.bits.bus) 455 << "); Device (0x" << std::setw(2) 456 << static_cast<uint32_t>(record.bits.device) << "); Function (0x" 457 << std::setw(2) << static_cast<uint32_t>(record.bits.function) 458 << "); Action (" << sAction << "); Operation status (" 459 << sOpStatus << "); Media slot number (" << std::dec 460 << static_cast<uint32_t>(record.bits.mediaSlot) << ")"; 461 462 description += strStream.str(); 463 464 // Log to Redfish event 465 sendJournalRedfish(description, logLevel); 466 } 467 468 } // namespace oem_ampere 469 } // namespace pldm 470