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
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 
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 
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 
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 
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 
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 
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 
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