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