1 /**
2 * Copyright © 2019 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "src.hpp"
17
18 #include "device_callouts.hpp"
19 #include "json_utils.hpp"
20 #include "pel_values.hpp"
21 #ifdef PELTOOL
22 #include <Python.h>
23
24 #include <nlohmann/json.hpp>
25
26 #include <sstream>
27 #endif
28 #include <phosphor-logging/lg2.hpp>
29
30 #include <format>
31
32 namespace openpower
33 {
34 namespace pels
35 {
36 namespace pv = openpower::pels::pel_values;
37 namespace rg = openpower::pels::message;
38 using namespace std::string_literals;
39
40 constexpr size_t ccinSize = 4;
41
42 #ifdef PELTOOL
43 using orderedJSON = nlohmann::ordered_json;
44
pyDecRef(PyObject * pyObj)45 void pyDecRef(PyObject* pyObj)
46 {
47 Py_XDECREF(pyObj);
48 }
49
50 /**
51 * @brief Returns a JSON string to append to SRC section.
52 *
53 * The returning string will contain a JSON object, but without
54 * the outer {}. If the input JSON isn't a JSON object (dict), then
55 * one will be created with the input added to a 'SRC Details' key.
56 *
57 * @param[in] json - The JSON to convert to a string
58 *
59 * @return std::string - The JSON string
60 */
prettyJSON(const orderedJSON & json)61 std::string prettyJSON(const orderedJSON& json)
62 {
63 orderedJSON output;
64 if (!json.is_object())
65 {
66 output["SRC Details"] = json;
67 }
68 else
69 {
70 for (const auto& [key, value] : json.items())
71 {
72 output["SRC Details"][key] = value;
73 }
74 }
75
76 // Let nlohmann do the pretty printing.
77 std::stringstream stream;
78 stream << std::setw(4) << output;
79
80 auto jsonString = stream.str();
81
82 // Now it looks like:
83 // {
84 // "Key": "Value",
85 // ...
86 // }
87
88 // Replace the { and the following newline, and the } and its
89 // preceeding newline.
90 jsonString.erase(0, 2);
91
92 auto pos = jsonString.find_last_of('}');
93 jsonString.erase(pos - 1);
94
95 return jsonString;
96 }
97
98 /**
99 * @brief Call Python modules to parse the data into a JSON string
100 *
101 * The module to call is based on the Creator Subsystem ID under the namespace
102 * "srcparsers". For example: "srcparsers.xsrc.xsrc" where "x" is the Creator
103 * Subsystem ID in ASCII lowercase.
104 *
105 * All modules must provide the following:
106 * Function: parseSRCToJson
107 * Argument list:
108 * 1. (str) ASCII string (Hex Word 1)
109 * 2. (str) Hex Word 2
110 * 3. (str) Hex Word 3
111 * 4. (str) Hex Word 4
112 * 5. (str) Hex Word 5
113 * 6. (str) Hex Word 6
114 * 7. (str) Hex Word 7
115 * 8. (str) Hex Word 8
116 * 9. (str) Hex Word 9
117 *-Return data:
118 * 1. (str) JSON string
119 *
120 * @param[in] hexwords - Vector of strings of Hexwords 1-9
121 * @param[in] creatorID - The creatorID from the Private Header section
122 * @return std::optional<std::string> - The JSON string if it could be created,
123 * else std::nullopt
124 */
getPythonJSON(std::vector<std::string> & hexwords,uint8_t creatorID)125 std::optional<std::string> getPythonJSON(std::vector<std::string>& hexwords,
126 uint8_t creatorID)
127 {
128 PyObject *pName, *pModule, *eType, *eValue, *eTraceback;
129 std::string pErrStr;
130 std::string module = getNumberString("%c", tolower(creatorID)) + "src";
131 pName = PyUnicode_FromString(
132 std::string("srcparsers." + module + "." + module).c_str());
133 std::unique_ptr<PyObject, decltype(&pyDecRef)> modNamePtr(pName, &pyDecRef);
134 pModule = PyImport_Import(pName);
135 if (pModule == NULL)
136 {
137 pErrStr = "No error string found";
138 PyErr_Fetch(&eType, &eValue, &eTraceback);
139 if (eType)
140 {
141 Py_XDECREF(eType);
142 }
143 if (eTraceback)
144 {
145 Py_XDECREF(eTraceback);
146 }
147 if (eValue)
148 {
149 PyObject* pStr = PyObject_Str(eValue);
150 Py_XDECREF(eValue);
151 if (pStr)
152 {
153 pErrStr = PyUnicode_AsUTF8(pStr);
154 Py_XDECREF(pStr);
155 }
156 }
157 }
158 else
159 {
160 std::unique_ptr<PyObject, decltype(&pyDecRef)> modPtr(
161 pModule, &pyDecRef);
162 std::string funcToCall = "parseSRCToJson";
163 PyObject* pKey = PyUnicode_FromString(funcToCall.c_str());
164 std::unique_ptr<PyObject, decltype(&pyDecRef)> keyPtr(pKey, &pyDecRef);
165 PyObject* pDict = PyModule_GetDict(pModule);
166 Py_INCREF(pDict);
167 if (!PyDict_Contains(pDict, pKey))
168 {
169 Py_DECREF(pDict);
170 lg2::error(
171 "Python module error. Function missing: {FUNC}, SRC = {SRC}, module = {MODULE}",
172 "FUNC", funcToCall, "SRC", hexwords.front(), "MODULE", module);
173 return std::nullopt;
174 }
175 PyObject* pFunc = PyDict_GetItemString(pDict, funcToCall.c_str());
176 Py_DECREF(pDict);
177 Py_INCREF(pFunc);
178 if (PyCallable_Check(pFunc))
179 {
180 PyObject* pArgs = PyTuple_New(9);
181 std::unique_ptr<PyObject, decltype(&pyDecRef)> argPtr(
182 pArgs, &pyDecRef);
183 for (size_t i = 0; i < 9; i++)
184 {
185 std::string arg{"00000000"};
186 if (i < hexwords.size())
187 {
188 arg = hexwords[i];
189 }
190 PyTuple_SetItem(pArgs, i, Py_BuildValue("s", arg.c_str()));
191 }
192 PyObject* pResult = PyObject_CallObject(pFunc, pArgs);
193 Py_DECREF(pFunc);
194 if (pResult)
195 {
196 std::unique_ptr<PyObject, decltype(&pyDecRef)> resPtr(
197 pResult, &pyDecRef);
198
199 if (pResult == Py_None)
200 {
201 return std::nullopt;
202 }
203
204 PyObject* pBytes =
205 PyUnicode_AsEncodedString(pResult, "utf-8", "~E~");
206 std::unique_ptr<PyObject, decltype(&pyDecRef)> pyBytePtr(
207 pBytes, &pyDecRef);
208 const char* output = PyBytes_AS_STRING(pBytes);
209 try
210 {
211 orderedJSON json = orderedJSON::parse(output);
212 if ((json.is_object() && !json.empty()) ||
213 (json.is_array() && json.size() > 0) ||
214 (json.is_string() && json != ""))
215 {
216 return prettyJSON(json);
217 }
218 }
219 catch (const std::exception& e)
220 {
221 lg2::error(
222 "Bad JSON from parser. Error = {ERROR}, SRC = {SRC}, module = {MODULE}",
223 "ERROR", e, "SRC", hexwords.front(), "MODULE", module);
224 return std::nullopt;
225 }
226 }
227 else
228 {
229 pErrStr = "No error string found";
230 PyErr_Fetch(&eType, &eValue, &eTraceback);
231 if (eType)
232 {
233 Py_XDECREF(eType);
234 }
235 if (eTraceback)
236 {
237 Py_XDECREF(eTraceback);
238 }
239 if (eValue)
240 {
241 PyObject* pStr = PyObject_Str(eValue);
242 Py_XDECREF(eValue);
243 if (pStr)
244 {
245 pErrStr = PyUnicode_AsUTF8(pStr);
246 Py_XDECREF(pStr);
247 }
248 }
249 }
250 }
251 }
252 if (!pErrStr.empty())
253 {
254 lg2::debug("Python exception thrown by parser. Error = {ERROR}, "
255 "SRC = {SRC}, module = {MODULE}",
256 "ERROR", pErrStr, "SRC", hexwords.front(), "MODULE", module);
257 }
258 return std::nullopt;
259 }
260 #endif
261
unflatten(Stream & stream)262 void SRC::unflatten(Stream& stream)
263 {
264 stream >> _header >> _version >> _flags >> _reserved1B >> _wordCount >>
265 _reserved2B >> _size;
266
267 for (auto& word : _hexData)
268 {
269 stream >> word;
270 }
271
272 _asciiString = std::make_unique<src::AsciiString>(stream);
273
274 if (hasAdditionalSections())
275 {
276 // The callouts section is currently the only extra subsection type
277 _callouts = std::make_unique<src::Callouts>(stream);
278 }
279 }
280
flatten(Stream & stream) const281 void SRC::flatten(Stream& stream) const
282 {
283 stream << _header << _version << _flags << _reserved1B << _wordCount
284 << _reserved2B << _size;
285
286 for (auto& word : _hexData)
287 {
288 stream << word;
289 }
290
291 _asciiString->flatten(stream);
292
293 if (_callouts)
294 {
295 _callouts->flatten(stream);
296 }
297 }
298
SRC(Stream & pel)299 SRC::SRC(Stream& pel)
300 {
301 try
302 {
303 unflatten(pel);
304 validate();
305 }
306 catch (const std::exception& e)
307 {
308 lg2::error("Cannot unflatten SRC, error = {ERROR}", "ERROR", e);
309 _valid = false;
310 }
311 }
312
SRC(const message::Entry & regEntry,const AdditionalData & additionalData,const nlohmann::json & jsonCallouts,const DataInterfaceBase & dataIface)313 SRC::SRC(const message::Entry& regEntry, const AdditionalData& additionalData,
314 const nlohmann::json& jsonCallouts, const DataInterfaceBase& dataIface)
315 {
316 _header.id = static_cast<uint16_t>(SectionID::primarySRC);
317 _header.version = srcSectionVersion;
318 _header.subType = srcSectionSubtype;
319 _header.componentID = regEntry.componentID;
320
321 _version = srcVersion;
322
323 _flags = 0;
324
325 _reserved1B = 0;
326
327 _wordCount = numSRCHexDataWords + 1;
328
329 _reserved2B = 0;
330
331 // There are multiple fields encoded in the hex data words.
332 std::for_each(_hexData.begin(), _hexData.end(),
333 [](auto& word) { word = 0; });
334
335 // Hex Word 2 Nibbles:
336 // MIGVEPFF
337 // M: Partition dump status = 0
338 // I: System boot state = TODO
339 // G: Partition Boot type = 0
340 // V: BMC dump status
341 // E: Platform boot mode = 0 (side = temporary, speed = fast)
342 // P: Platform dump status
343 // FF: SRC format, set below
344
345 setProgressCode(dataIface);
346 setBMCFormat();
347 setBMCPosition();
348 setMotherboardCCIN(dataIface);
349
350 if (regEntry.src.checkstopFlag)
351 {
352 setErrorStatusFlag(ErrorStatusFlags::hwCheckstop);
353 }
354
355 if (regEntry.src.deconfigFlag)
356 {
357 setErrorStatusFlag(ErrorStatusFlags::deconfigured);
358 }
359
360 // Fill in the last 4 words from the AdditionalData property contents.
361 setUserDefinedHexWords(regEntry, additionalData);
362
363 _asciiString = std::make_unique<src::AsciiString>(regEntry);
364
365 // Check for additional data - PEL_SUBSYSTEM
366 auto ss = additionalData.getValue("PEL_SUBSYSTEM");
367 if (ss)
368 {
369 auto eventSubsystem = std::stoul(*ss, nullptr, 16);
370 std::string subsystem =
371 pv::getValue(eventSubsystem, pel_values::subsystemValues);
372 if (subsystem == "invalid")
373 {
374 lg2::warning("SRC: Invalid SubSystem value: {VAL}", "VAL", lg2::hex,
375 eventSubsystem);
376 }
377 else
378 {
379 _asciiString->setByte(2, eventSubsystem);
380 }
381 }
382
383 addCallouts(regEntry, additionalData, jsonCallouts, dataIface);
384
385 _size = baseSRCSize;
386 _size += _callouts ? _callouts->flattenedSize() : 0;
387 _header.size = Section::headerSize() + _size;
388
389 _valid = true;
390 }
391
setUserDefinedHexWords(const message::Entry & regEntry,const AdditionalData & ad)392 void SRC::setUserDefinedHexWords(const message::Entry& regEntry,
393 const AdditionalData& ad)
394 {
395 if (!regEntry.src.hexwordADFields)
396 {
397 return;
398 }
399
400 // Save the AdditionalData value corresponding to the first element of
401 // adName tuple into _hexData[wordNum].
402 for (const auto& [wordNum, adName] : *regEntry.src.hexwordADFields)
403 {
404 // Can only set words 6 - 9
405 if (!isUserDefinedWord(wordNum))
406 {
407 std::string msg =
408 "SRC user data word out of range: " + std::to_string(wordNum);
409 addDebugData(msg);
410 continue;
411 }
412
413 auto value = ad.getValue(std::get<0>(adName));
414 if (value)
415 {
416 _hexData[getWordIndexFromWordNum(wordNum)] =
417 std::strtoul(value.value().c_str(), nullptr, 0);
418 }
419 else
420 {
421 std::string msg = "Source for user data SRC word not found: " +
422 std::get<0>(adName);
423 addDebugData(msg);
424 }
425 }
426 }
427
setMotherboardCCIN(const DataInterfaceBase & dataIface)428 void SRC::setMotherboardCCIN(const DataInterfaceBase& dataIface)
429 {
430 uint32_t ccin = 0;
431 auto ccinString = dataIface.getMotherboardCCIN();
432
433 try
434 {
435 if (ccinString.size() == ccinSize)
436 {
437 ccin = std::stoi(ccinString, nullptr, 16);
438 }
439 }
440 catch (const std::exception& e)
441 {
442 lg2::warning("Could not convert motherboard CCIN {CCIN} to a number",
443 "CCIN", ccinString);
444 return;
445 }
446
447 // Set the first 2 bytes
448 _hexData[1] |= ccin << 16;
449 }
450
validate()451 void SRC::validate()
452 {
453 bool failed = false;
454
455 if ((header().id != static_cast<uint16_t>(SectionID::primarySRC)) &&
456 (header().id != static_cast<uint16_t>(SectionID::secondarySRC)))
457 {
458 lg2::error("Invalid SRC section ID: {ID}", "ID", lg2::hex, header().id);
459 failed = true;
460 }
461
462 // Check the version in the SRC, not in the header
463 if (_version != srcVersion)
464 {
465 lg2::error("Invalid SRC version: {VERSION}", "VERSION", lg2::hex,
466 header().version);
467 failed = true;
468 }
469
470 _valid = failed ? false : true;
471 }
472
isBMCSRC() const473 bool SRC::isBMCSRC() const
474 {
475 auto as = asciiString();
476 if (as.length() >= 2)
477 {
478 uint8_t errorType = strtoul(as.substr(0, 2).c_str(), nullptr, 16);
479 return (errorType == static_cast<uint8_t>(SRCType::bmcError) ||
480 errorType == static_cast<uint8_t>(SRCType::powerError));
481 }
482 return false;
483 }
484
isHostbootSRC() const485 bool SRC::isHostbootSRC() const
486 {
487 auto as = asciiString();
488 if (as.length() >= 2)
489 {
490 uint8_t errorType = strtoul(as.substr(0, 2).c_str(), nullptr, 16);
491 return errorType == static_cast<uint8_t>(SRCType::hostbootError);
492 }
493 return false;
494 }
495
getErrorDetails(message::Registry & registry,DetailLevel type,bool toCache) const496 std::optional<std::string> SRC::getErrorDetails(
497 message::Registry& registry, DetailLevel type, bool toCache) const
498 {
499 const std::string jsonIndent(indentLevel, 0x20);
500 std::string errorOut;
501 if (isBMCSRC())
502 {
503 auto entry = registry.lookup("0x" + asciiString().substr(4, 4),
504 rg::LookupType::reasonCode, toCache);
505 if (entry)
506 {
507 errorOut.append(jsonIndent + "\"Error Details\": {\n");
508 auto errorMsg = getErrorMessage(*entry);
509 if (errorMsg)
510 {
511 if (type == DetailLevel::message)
512 {
513 return errorMsg.value();
514 }
515 else
516 {
517 jsonInsert(errorOut, "Message", errorMsg.value(), 2);
518 }
519 }
520 if (entry->src.hexwordADFields)
521 {
522 std::map<size_t, std::tuple<std::string, std::string>>
523 adFields = entry->src.hexwordADFields.value();
524 for (const auto& hexwordMap : adFields)
525 {
526 auto srcValue = getNumberString(
527 "0x%X",
528 _hexData[getWordIndexFromWordNum(hexwordMap.first)]);
529
530 auto srcKey = std::get<0>(hexwordMap.second);
531 auto srcDesc = std::get<1>(hexwordMap.second);
532
533 // Only include this hex word in the error details if the
534 // description exists.
535 if (!srcDesc.empty())
536 {
537 std::vector<std::string> valueDescr;
538 valueDescr.push_back(srcValue);
539 valueDescr.push_back(srcDesc);
540 jsonInsertArray(errorOut, srcKey, valueDescr, 2);
541 }
542 }
543 }
544 errorOut.erase(errorOut.size() - 2);
545 errorOut.append("\n");
546 errorOut.append(jsonIndent + "},\n");
547 return errorOut;
548 }
549 }
550 return std::nullopt;
551 }
552
getErrorMessage(const message::Entry & regEntry) const553 std::optional<std::string> SRC::getErrorMessage(
554 const message::Entry& regEntry) const
555 {
556 try
557 {
558 if (regEntry.doc.messageArgSources)
559 {
560 std::vector<uint32_t> argSourceVals;
561 std::string message;
562 const auto& argValues = regEntry.doc.messageArgSources.value();
563 for (size_t i = 0; i < argValues.size(); ++i)
564 {
565 argSourceVals.push_back(_hexData[getWordIndexFromWordNum(
566 argValues[i].back() - '0')]);
567 }
568
569 auto it = std::begin(regEntry.doc.message);
570 auto it_end = std::end(regEntry.doc.message);
571
572 while (it != it_end)
573 {
574 if (*it == '%')
575 {
576 ++it;
577
578 size_t wordIndex = *it - '0';
579 if (isdigit(*it) && wordIndex >= 1 &&
580 static_cast<uint16_t>(wordIndex) <=
581 argSourceVals.size())
582 {
583 message.append(getNumberString(
584 "0x%08X", argSourceVals[wordIndex - 1]));
585 }
586 else
587 {
588 message.append("%" + std::string(1, *it));
589 }
590 }
591 else
592 {
593 message.push_back(*it);
594 }
595 ++it;
596 }
597
598 return message;
599 }
600 else
601 {
602 return regEntry.doc.message;
603 }
604 }
605 catch (const std::exception& e)
606 {
607 lg2::error(
608 "Cannot get error message from registry entry, error = {ERROR}",
609 "ERROR", e);
610 }
611 return std::nullopt;
612 }
613
getCallouts() const614 std::optional<std::string> SRC::getCallouts() const
615 {
616 if (!_callouts)
617 {
618 return std::nullopt;
619 }
620 std::string printOut;
621 const std::string jsonIndent(indentLevel, 0x20);
622 const auto& callout = _callouts->callouts();
623 const auto& compDescrp = pv::failingComponentType;
624 printOut.append(jsonIndent + "\"Callout Section\": {\n");
625 jsonInsert(printOut, "Callout Count", std::to_string(callout.size()), 2);
626 printOut.append(jsonIndent + jsonIndent + "\"Callouts\": [");
627 for (auto& entry : callout)
628 {
629 printOut.append("{\n");
630 if (entry->fruIdentity())
631 {
632 jsonInsert(
633 printOut, "FRU Type",
634 compDescrp.at(entry->fruIdentity()->failingComponentType()), 3);
635 jsonInsert(printOut, "Priority",
636 pv::getValue(entry->priority(),
637 pel_values::calloutPriorityValues),
638 3);
639 if (!entry->locationCode().empty())
640 {
641 jsonInsert(printOut, "Location Code", entry->locationCode(), 3);
642 }
643 if (entry->fruIdentity()->getPN().has_value())
644 {
645 jsonInsert(printOut, "Part Number",
646 entry->fruIdentity()->getPN().value(), 3);
647 }
648 if (entry->fruIdentity()->getMaintProc().has_value())
649 {
650 jsonInsert(printOut, "Procedure",
651 entry->fruIdentity()->getMaintProc().value(), 3);
652 if (pv::procedureDesc.find(
653 entry->fruIdentity()->getMaintProc().value()) !=
654 pv::procedureDesc.end())
655 {
656 jsonInsert(
657 printOut, "Description",
658 pv::procedureDesc.at(
659 entry->fruIdentity()->getMaintProc().value()),
660 3);
661 }
662 }
663 if (entry->fruIdentity()->getCCIN().has_value())
664 {
665 jsonInsert(printOut, "CCIN",
666 entry->fruIdentity()->getCCIN().value(), 3);
667 }
668 if (entry->fruIdentity()->getSN().has_value())
669 {
670 jsonInsert(printOut, "Serial Number",
671 entry->fruIdentity()->getSN().value(), 3);
672 }
673 }
674 if (entry->pceIdentity())
675 {
676 const auto& pceIdentMtms = entry->pceIdentity()->mtms();
677 if (!pceIdentMtms.machineTypeAndModel().empty())
678 {
679 jsonInsert(printOut, "PCE MTMS",
680 pceIdentMtms.machineTypeAndModel() + "_" +
681 pceIdentMtms.machineSerialNumber(),
682 3);
683 }
684 if (!entry->pceIdentity()->enclosureName().empty())
685 {
686 jsonInsert(printOut, "PCE Name",
687 entry->pceIdentity()->enclosureName(), 3);
688 }
689 }
690 if (entry->mru())
691 {
692 const auto& mruCallouts = entry->mru()->mrus();
693 std::string mruId;
694 for (auto& element : mruCallouts)
695 {
696 if (!mruId.empty())
697 {
698 mruId.append(", " + getNumberString("%08X", element.id));
699 }
700 else
701 {
702 mruId.append(getNumberString("%08X", element.id));
703 }
704 }
705 jsonInsert(printOut, "MRU Id", mruId, 3);
706 }
707 printOut.erase(printOut.size() - 2);
708 printOut.append("\n" + jsonIndent + jsonIndent + "}, ");
709 };
710 printOut.erase(printOut.size() - 2);
711 printOut.append("]\n" + jsonIndent + "}");
712 return printOut;
713 }
714
getJSON(message::Registry & registry,const std::vector<std::string> & plugins,uint8_t creatorID) const715 std::optional<std::string> SRC::getJSON(message::Registry& registry,
716 const std::vector<std::string>& plugins
717 [[maybe_unused]],
718 uint8_t creatorID) const
719 {
720 std::string ps;
721 std::vector<std::string> hexwords;
722 jsonInsert(ps, pv::sectionVer, getNumberString("%d", _header.version), 1);
723 jsonInsert(ps, pv::subSection, getNumberString("%d", _header.subType), 1);
724 jsonInsert(ps, pv::createdBy,
725 getComponentName(_header.componentID, creatorID), 1);
726 jsonInsert(ps, "SRC Version", getNumberString("0x%02X", _version), 1);
727 jsonInsert(ps, "SRC Format", getNumberString("0x%02X", _hexData[0] & 0xFF),
728 1);
729 jsonInsert(ps, "Virtual Progress SRC",
730 pv::boolString.at(_flags & virtualProgressSRC), 1);
731 jsonInsert(ps, "I5/OS Service Event Bit",
732 pv::boolString.at(_flags & i5OSServiceEventBit), 1);
733 jsonInsert(ps, "Hypervisor Dump Initiated",
734 pv::boolString.at(_flags & hypDumpInit), 1);
735
736 if (isBMCSRC())
737 {
738 std::string ccinString;
739 uint32_t ccin = _hexData[1] >> 16;
740
741 if (ccin)
742 {
743 ccinString = getNumberString("%04X", ccin);
744 }
745 // The PEL spec calls it a backplane, so call it that here.
746 jsonInsert(ps, "Backplane CCIN", ccinString, 1);
747
748 jsonInsert(ps, "Terminate FW Error",
749 pv::boolString.at(
750 _hexData[3] &
751 static_cast<uint32_t>(ErrorStatusFlags::terminateFwErr)),
752 1);
753 }
754
755 if (isBMCSRC() || isHostbootSRC())
756 {
757 jsonInsert(ps, "Deconfigured",
758 pv::boolString.at(
759 _hexData[3] &
760 static_cast<uint32_t>(ErrorStatusFlags::deconfigured)),
761 1);
762
763 jsonInsert(
764 ps, "Guarded",
765 pv::boolString.at(
766 _hexData[3] & static_cast<uint32_t>(ErrorStatusFlags::guarded)),
767 1);
768 }
769
770 auto errorDetails = getErrorDetails(registry, DetailLevel::json, true);
771 if (errorDetails)
772 {
773 ps.append(errorDetails.value());
774 }
775 jsonInsert(ps, "Valid Word Count", getNumberString("0x%02X", _wordCount),
776 1);
777 std::string refcode = asciiString();
778 hexwords.push_back(refcode);
779 std::string extRefcode;
780 size_t pos = refcode.find(0x20);
781 if (pos != std::string::npos)
782 {
783 size_t nextPos = refcode.find_first_not_of(0x20, pos);
784 if (nextPos != std::string::npos)
785 {
786 extRefcode = trimEnd(refcode.substr(nextPos));
787 }
788 refcode.erase(pos);
789 }
790 jsonInsert(ps, "Reference Code", refcode, 1);
791 if (!extRefcode.empty())
792 {
793 jsonInsert(ps, "Extended Reference Code", extRefcode, 1);
794 }
795 for (size_t i = 2; i <= _wordCount; i++)
796 {
797 std::string tmpWord =
798 getNumberString("%08X", _hexData[getWordIndexFromWordNum(i)]);
799 jsonInsert(ps, "Hex Word " + std::to_string(i), tmpWord, 1);
800 hexwords.push_back(tmpWord);
801 }
802 auto calloutJson = getCallouts();
803 if (calloutJson)
804 {
805 ps.append(calloutJson.value());
806 ps.append(",\n");
807 }
808 std::string subsystem = getNumberString("%c", tolower(creatorID));
809 bool srcDetailExists = false;
810 #ifdef PELTOOL
811 if (std::find(plugins.begin(), plugins.end(), subsystem + "src") !=
812 plugins.end())
813 {
814 auto pyJson = getPythonJSON(hexwords, creatorID);
815 if (pyJson)
816 {
817 ps.append(pyJson.value());
818 srcDetailExists = true;
819 }
820 }
821 #endif
822 if (!srcDetailExists)
823 {
824 ps.erase(ps.size() - 2);
825 }
826 return ps;
827 }
828
addCallouts(const message::Entry & regEntry,const AdditionalData & additionalData,const nlohmann::json & jsonCallouts,const DataInterfaceBase & dataIface)829 void SRC::addCallouts(const message::Entry& regEntry,
830 const AdditionalData& additionalData,
831 const nlohmann::json& jsonCallouts,
832 const DataInterfaceBase& dataIface)
833 {
834 auto registryCallouts =
835 getRegistryCallouts(regEntry, additionalData, dataIface);
836
837 auto item = additionalData.getValue("CALLOUT_INVENTORY_PATH");
838 auto priority = additionalData.getValue("CALLOUT_PRIORITY");
839
840 std::optional<CalloutPriority> calloutPriority;
841
842 // Only H, M or L priority values.
843 if (priority && !(*priority).empty())
844 {
845 uint8_t p = (*priority)[0];
846 if (p == 'H' || p == 'M' || p == 'L')
847 {
848 calloutPriority = static_cast<CalloutPriority>(p);
849 }
850 }
851 // If the first registry callout says to use the passed in inventory
852 // path to get the location code for a symbolic FRU callout with a
853 // trusted location code, then do not add the inventory path as a
854 // normal FRU callout.
855 bool useInvForSymbolicFRULocCode =
856 !registryCallouts.empty() && registryCallouts[0].useInventoryLocCode &&
857 !registryCallouts[0].symbolicFRUTrusted.empty();
858
859 if (item && !useInvForSymbolicFRULocCode)
860 {
861 addInventoryCallout(*item, calloutPriority, std::nullopt, dataIface);
862 }
863
864 addDevicePathCallouts(additionalData, dataIface);
865
866 addRegistryCallouts(registryCallouts, dataIface,
867 (useInvForSymbolicFRULocCode) ? item : std::nullopt);
868
869 if (!jsonCallouts.empty())
870 {
871 addJSONCallouts(jsonCallouts, dataIface);
872 }
873 }
874
addLocationCodeOnlyCallout(const std::string & locationCode,const CalloutPriority priority)875 void SRC::addLocationCodeOnlyCallout(const std::string& locationCode,
876 const CalloutPriority priority)
877 {
878 std::string empty;
879 std::vector<src::MRU::MRUCallout> mrus;
880 auto callout = std::make_unique<src::Callout>(priority, locationCode, empty,
881 empty, empty, mrus);
882 createCalloutsObject();
883 _callouts->addCallout(std::move(callout));
884 }
885
addInventoryCallout(const std::string & inventoryPath,const std::optional<CalloutPriority> & priority,const std::optional<std::string> & locationCode,const DataInterfaceBase & dataIface,const std::vector<src::MRU::MRUCallout> & mrus)886 void SRC::addInventoryCallout(const std::string& inventoryPath,
887 const std::optional<CalloutPriority>& priority,
888 const std::optional<std::string>& locationCode,
889 const DataInterfaceBase& dataIface,
890 const std::vector<src::MRU::MRUCallout>& mrus)
891 {
892 std::string locCode;
893 std::string fn;
894 std::string ccin;
895 std::string sn;
896 std::unique_ptr<src::Callout> callout;
897
898 try
899 {
900 // Use the passed in location code if there otherwise look it up
901 if (locationCode)
902 {
903 locCode = *locationCode;
904 }
905 else
906 {
907 locCode = dataIface.getLocationCode(inventoryPath);
908 }
909
910 try
911 {
912 dataIface.getHWCalloutFields(inventoryPath, fn, ccin, sn);
913
914 CalloutPriority p =
915 priority ? priority.value() : CalloutPriority::high;
916
917 callout =
918 std::make_unique<src::Callout>(p, locCode, fn, ccin, sn, mrus);
919 }
920 catch (const sdbusplus::exception_t& e)
921 {
922 std::string msg =
923 "No VPD found for " + inventoryPath + ": " + e.what();
924 addDebugData(msg);
925
926 // Just create the callout with empty FRU fields
927 callout = std::make_unique<src::Callout>(
928 CalloutPriority::high, locCode, fn, ccin, sn, mrus);
929 }
930 }
931 catch (const sdbusplus::exception_t& e)
932 {
933 std::string msg = "Could not get location code for " + inventoryPath +
934 ": " + e.what();
935 addDebugData(msg);
936
937 // Don't add a callout in this case, because:
938 // 1) With how the inventory is primed, there is no case where
939 // a location code is expected to be missing. This implies
940 // the caller is passing in something invalid.
941 // 2) The addDebugData call above will put the passed in path into
942 // a user data section that can be seen by development for debug.
943 // 3) Even if we wanted to do a 'no_vpd_for_fru' sort of maint.
944 // procedure, we don't have a good way to indicate to the user
945 // anything about the intended callout (they won't see user data).
946 // 4) Creating a new standalone event log for this problem isn't
947 // possible from inside a PEL section.
948 }
949
950 if (callout)
951 {
952 createCalloutsObject();
953 _callouts->addCallout(std::move(callout));
954 }
955 }
956
getRegistryCallouts(const message::Entry & regEntry,const AdditionalData & additionalData,const DataInterfaceBase & dataIface)957 std::vector<message::RegistryCallout> SRC::getRegistryCallouts(
958 const message::Entry& regEntry, const AdditionalData& additionalData,
959 const DataInterfaceBase& dataIface)
960 {
961 std::vector<message::RegistryCallout> registryCallouts;
962
963 if (regEntry.callouts)
964 {
965 std::vector<std::string> systemNames;
966
967 try
968 {
969 systemNames = dataIface.getSystemNames();
970 }
971 catch (const std::exception& e)
972 {
973 // Compatible interface not available yet
974 }
975
976 try
977 {
978 registryCallouts = message::Registry::getCallouts(
979 regEntry.callouts.value(), systemNames, additionalData);
980 }
981 catch (const std::exception& e)
982 {
983 addDebugData(std::format(
984 "Error parsing PEL message registry callout JSON: {}",
985 e.what()));
986 }
987 }
988
989 return registryCallouts;
990 }
991
addRegistryCallouts(const std::vector<message::RegistryCallout> & callouts,const DataInterfaceBase & dataIface,std::optional<std::string> trustedSymbolicFRUInvPath)992 void SRC::addRegistryCallouts(
993 const std::vector<message::RegistryCallout>& callouts,
994 const DataInterfaceBase& dataIface,
995 std::optional<std::string> trustedSymbolicFRUInvPath)
996 {
997 try
998 {
999 for (const auto& callout : callouts)
1000 {
1001 addRegistryCallout(callout, dataIface, trustedSymbolicFRUInvPath);
1002
1003 // Only the first callout gets the inventory path
1004 if (trustedSymbolicFRUInvPath)
1005 {
1006 trustedSymbolicFRUInvPath = std::nullopt;
1007 }
1008 }
1009 }
1010 catch (const std::exception& e)
1011 {
1012 std::string msg =
1013 "Error parsing PEL message registry callout JSON: "s + e.what();
1014 addDebugData(msg);
1015 }
1016 }
1017
addRegistryCallout(const message::RegistryCallout & regCallout,const DataInterfaceBase & dataIface,const std::optional<std::string> & trustedSymbolicFRUInvPath)1018 void SRC::addRegistryCallout(
1019 const message::RegistryCallout& regCallout,
1020 const DataInterfaceBase& dataIface,
1021 const std::optional<std::string>& trustedSymbolicFRUInvPath)
1022 {
1023 std::unique_ptr<src::Callout> callout;
1024 auto locCode = regCallout.locCode;
1025 bool locExpanded = true;
1026
1027 if (!locCode.empty())
1028 {
1029 try
1030 {
1031 locCode = dataIface.expandLocationCode(locCode, 0);
1032 }
1033 catch (const std::exception& e)
1034 {
1035 auto msg = "Unable to expand location code " + locCode + ": " +
1036 e.what();
1037 addDebugData(msg);
1038 locExpanded = false;
1039 }
1040 }
1041
1042 // Via the PEL values table, get the priority enum.
1043 // The schema will have validated the priority was a valid value.
1044 auto priorityIt =
1045 pv::findByName(regCallout.priority, pv::calloutPriorityValues);
1046 assert(priorityIt != pv::calloutPriorityValues.end());
1047 auto priority =
1048 static_cast<CalloutPriority>(std::get<pv::fieldValuePos>(*priorityIt));
1049
1050 if (!regCallout.procedure.empty())
1051 {
1052 // Procedure callout
1053 callout = std::make_unique<src::Callout>(priority, regCallout.procedure,
1054 src::CalloutValueType::raw);
1055 }
1056 else if (!regCallout.symbolicFRU.empty())
1057 {
1058 // Symbolic FRU callout
1059 callout = std::make_unique<src::Callout>(
1060 priority, regCallout.symbolicFRU, locCode, false);
1061 }
1062 else if (!regCallout.symbolicFRUTrusted.empty())
1063 {
1064 // Symbolic FRU with trusted location code callout
1065 bool trusted = false;
1066
1067 // Use the location code from the inventory path if there is one.
1068 if (trustedSymbolicFRUInvPath)
1069 {
1070 try
1071 {
1072 locCode = dataIface.getLocationCode(*trustedSymbolicFRUInvPath);
1073 trusted = true;
1074 }
1075 catch (const std::exception& e)
1076 {
1077 addDebugData(
1078 std::format("Could not get location code for {}: {}",
1079 *trustedSymbolicFRUInvPath, e.what()));
1080 locCode.clear();
1081 }
1082 }
1083
1084 // Can only trust the location code if it isn't empty and is expanded.
1085 if (!locCode.empty() && locExpanded)
1086 {
1087 trusted = true;
1088 }
1089
1090 // The registry wants it to be trusted, but that requires a valid
1091 // location code for it to actually be.
1092 callout = std::make_unique<src::Callout>(
1093 priority, regCallout.symbolicFRUTrusted, locCode, trusted);
1094 }
1095 else
1096 {
1097 // A hardware callout
1098
1099 // If couldn't expand the location code, don't bother
1100 // looking up the inventory path.
1101 if (!locExpanded && !locCode.empty())
1102 {
1103 addLocationCodeOnlyCallout(locCode, priority);
1104 return;
1105 }
1106
1107 std::vector<std::string> inventoryPaths;
1108
1109 try
1110 {
1111 // Get the inventory item from the unexpanded location code
1112 inventoryPaths =
1113 dataIface.getInventoryFromLocCode(regCallout.locCode, 0, false);
1114 }
1115 catch (const std::exception& e)
1116 {
1117 std::string msg =
1118 "Unable to get inventory path from location code: " + locCode +
1119 ": " + e.what();
1120 addDebugData(msg);
1121 if (!locCode.empty())
1122 {
1123 // Still add a callout with just the location code.
1124 addLocationCodeOnlyCallout(locCode, priority);
1125 }
1126 return;
1127 }
1128
1129 // Just use first path returned since they all point to the same FRU.
1130 addInventoryCallout(inventoryPaths[0], priority, locCode, dataIface);
1131 }
1132
1133 if (callout)
1134 {
1135 createCalloutsObject();
1136 _callouts->addCallout(std::move(callout));
1137 }
1138 }
1139
addDevicePathCallouts(const AdditionalData & additionalData,const DataInterfaceBase & dataIface)1140 void SRC::addDevicePathCallouts(const AdditionalData& additionalData,
1141 const DataInterfaceBase& dataIface)
1142 {
1143 std::vector<device_callouts::Callout> callouts;
1144 auto i2cBus = additionalData.getValue("CALLOUT_IIC_BUS");
1145 auto i2cAddr = additionalData.getValue("CALLOUT_IIC_ADDR");
1146 auto devPath = additionalData.getValue("CALLOUT_DEVICE_PATH");
1147
1148 // A device callout contains either:
1149 // * CALLOUT_ERRNO, CALLOUT_DEVICE_PATH
1150 // * CALLOUT_ERRNO, CALLOUT_IIC_BUS, CALLOUT_IIC_ADDR
1151 // We don't care about the errno.
1152
1153 if (devPath)
1154 {
1155 try
1156 {
1157 callouts = device_callouts::getCallouts(*devPath,
1158 dataIface.getSystemNames());
1159 }
1160 catch (const std::exception& e)
1161 {
1162 addDebugData(e.what());
1163 callouts.clear();
1164 }
1165 }
1166 else if (i2cBus && i2cAddr)
1167 {
1168 size_t bus;
1169 uint8_t address;
1170
1171 try
1172 {
1173 // If /dev/i2c- is prepended, remove it
1174 if (i2cBus->find("/dev/i2c-") != std::string::npos)
1175 {
1176 *i2cBus = i2cBus->substr(9);
1177 }
1178
1179 bus = stoul(*i2cBus, nullptr, 0);
1180 address = stoul(*i2cAddr, nullptr, 0);
1181 }
1182 catch (const std::exception& e)
1183 {
1184 std::string msg =
1185 "Invalid CALLOUT_IIC_BUS " + *i2cBus + " or CALLOUT_IIC_ADDR " +
1186 *i2cAddr + " in AdditionalData property";
1187 addDebugData(msg);
1188 return;
1189 }
1190
1191 try
1192 {
1193 callouts = device_callouts::getI2CCallouts(
1194 bus, address, dataIface.getSystemNames());
1195 }
1196 catch (const std::exception& e)
1197 {
1198 addDebugData(e.what());
1199 callouts.clear();
1200 }
1201 }
1202
1203 for (const auto& callout : callouts)
1204 {
1205 // The priority shouldn't be invalid, but check just in case.
1206 CalloutPriority priority = CalloutPriority::high;
1207
1208 if (!callout.priority.empty())
1209 {
1210 auto p = pel_values::findByValue(
1211 static_cast<uint32_t>(callout.priority[0]),
1212 pel_values::calloutPriorityValues);
1213
1214 if (p != pel_values::calloutPriorityValues.end())
1215 {
1216 priority = static_cast<CalloutPriority>(callout.priority[0]);
1217 }
1218 else
1219 {
1220 auto msg =
1221 std::string{
1222 "Invalid priority found in dev callout JSON: "} +
1223 callout.priority[0];
1224 addDebugData(msg);
1225 }
1226 }
1227
1228 std::optional<std::string> locCode;
1229
1230 try
1231 {
1232 locCode = dataIface.expandLocationCode(callout.locationCode, 0);
1233 }
1234 catch (const std::exception& e)
1235 {
1236 auto msg = std::format("Unable to expand location code {}: {}",
1237 callout.locationCode, e.what());
1238 addDebugData(msg);
1239
1240 // Add the callout with just the unexpanded location code.
1241 addLocationCodeOnlyCallout(callout.locationCode, priority);
1242 continue;
1243 }
1244
1245 try
1246 {
1247 auto inventoryPaths = dataIface.getInventoryFromLocCode(
1248 callout.locationCode, 0, false);
1249
1250 // Just use first path returned since they all
1251 // point to the same FRU.
1252 addInventoryCallout(inventoryPaths[0], priority, locCode,
1253 dataIface);
1254 }
1255 catch (const std::exception& e)
1256 {
1257 std::string msg =
1258 "Unable to get inventory path from location code: " +
1259 callout.locationCode + ": " + e.what();
1260 addDebugData(msg);
1261 // Add the callout with just the location code.
1262 addLocationCodeOnlyCallout(callout.locationCode, priority);
1263 continue;
1264 }
1265
1266 // Until the code is there to convert these MRU value strings to
1267 // the official MRU values in the callout objects, just store
1268 // the MRU name in the debug UserData section.
1269 if (!callout.mru.empty())
1270 {
1271 std::string msg = "MRU: " + callout.mru;
1272 addDebugData(msg);
1273 }
1274
1275 // getCallouts() may have generated some debug data it stored
1276 // in a callout object. Save it as well.
1277 if (!callout.debug.empty())
1278 {
1279 addDebugData(callout.debug);
1280 }
1281 }
1282 }
1283
addJSONCallouts(const nlohmann::json & jsonCallouts,const DataInterfaceBase & dataIface)1284 void SRC::addJSONCallouts(const nlohmann::json& jsonCallouts,
1285 const DataInterfaceBase& dataIface)
1286 {
1287 if (jsonCallouts.empty())
1288 {
1289 return;
1290 }
1291
1292 if (!jsonCallouts.is_array())
1293 {
1294 addDebugData("Callout JSON isn't an array");
1295 return;
1296 }
1297
1298 for (const auto& callout : jsonCallouts)
1299 {
1300 try
1301 {
1302 addJSONCallout(callout, dataIface);
1303 }
1304 catch (const std::exception& e)
1305 {
1306 addDebugData(std::format(
1307 "Failed extracting callout data from JSON: {}", e.what()));
1308 }
1309 }
1310 }
1311
addJSONCallout(const nlohmann::json & jsonCallout,const DataInterfaceBase & dataIface)1312 void SRC::addJSONCallout(const nlohmann::json& jsonCallout,
1313 const DataInterfaceBase& dataIface)
1314 {
1315 auto priority = getPriorityFromJSON(jsonCallout);
1316 std::string locCode;
1317 std::string unexpandedLocCode;
1318 std::unique_ptr<src::Callout> callout;
1319
1320 // Expand the location code if it's there
1321 if (jsonCallout.contains("LocationCode"))
1322 {
1323 unexpandedLocCode = jsonCallout.at("LocationCode").get<std::string>();
1324
1325 try
1326 {
1327 locCode = dataIface.expandLocationCode(unexpandedLocCode, 0);
1328 }
1329 catch (const std::exception& e)
1330 {
1331 addDebugData(std::format("Unable to expand location code {}: {}",
1332 unexpandedLocCode, e.what()));
1333 // Use the value from the JSON so at least there's something
1334 locCode = unexpandedLocCode;
1335 }
1336 }
1337
1338 // Create either a procedure, symbolic FRU, or normal FRU callout.
1339 if (jsonCallout.contains("Procedure"))
1340 {
1341 auto procedure = jsonCallout.at("Procedure").get<std::string>();
1342
1343 // If it's the registry name instead of the raw name, convert.
1344 if (pv::maintenanceProcedures.find(procedure) !=
1345 pv::maintenanceProcedures.end())
1346 {
1347 procedure = pv::maintenanceProcedures.at(procedure);
1348 }
1349
1350 callout = std::make_unique<src::Callout>(
1351 static_cast<CalloutPriority>(priority), procedure,
1352 src::CalloutValueType::raw);
1353 }
1354 else if (jsonCallout.contains("SymbolicFRU"))
1355 {
1356 auto fru = jsonCallout.at("SymbolicFRU").get<std::string>();
1357
1358 // If it's the registry name instead of the raw name, convert.
1359 if (pv::symbolicFRUs.find(fru) != pv::symbolicFRUs.end())
1360 {
1361 fru = pv::symbolicFRUs.at(fru);
1362 }
1363
1364 bool trusted = false;
1365 if (jsonCallout.contains("TrustedLocationCode") && !locCode.empty())
1366 {
1367 trusted = jsonCallout.at("TrustedLocationCode").get<bool>();
1368 }
1369
1370 callout = std::make_unique<src::Callout>(
1371 static_cast<CalloutPriority>(priority), fru,
1372 src::CalloutValueType::raw, locCode, trusted);
1373 }
1374 else
1375 {
1376 // A hardware FRU
1377 std::string inventoryPath;
1378 std::vector<src::MRU::MRUCallout> mrus;
1379
1380 if (jsonCallout.contains("InventoryPath"))
1381 {
1382 inventoryPath = jsonCallout.at("InventoryPath").get<std::string>();
1383 }
1384 else
1385 {
1386 if (unexpandedLocCode.empty())
1387 {
1388 throw std::runtime_error{"JSON callout needs either an "
1389 "inventory path or location code"};
1390 }
1391
1392 try
1393 {
1394 auto inventoryPaths = dataIface.getInventoryFromLocCode(
1395 unexpandedLocCode, 0, false);
1396 // Just use first path returned since they all
1397 // point to the same FRU.
1398 inventoryPath = inventoryPaths[0];
1399 }
1400 catch (const std::exception& e)
1401 {
1402 addDebugData(std::format("Unable to get inventory path from "
1403 "location code: {}: {}",
1404 unexpandedLocCode, e.what()));
1405 addLocationCodeOnlyCallout(locCode, priority);
1406 return;
1407 }
1408 }
1409
1410 if (jsonCallout.contains("MRUs"))
1411 {
1412 mrus = getMRUsFromJSON(jsonCallout.at("MRUs"));
1413 }
1414
1415 // If the location code was also passed in, use that here too
1416 // so addInventoryCallout doesn't have to look it up.
1417 std::optional<std::string> lc;
1418 if (!locCode.empty())
1419 {
1420 lc = locCode;
1421 }
1422
1423 addInventoryCallout(inventoryPath, priority, lc, dataIface, mrus);
1424
1425 if (jsonCallout.contains("Deconfigured"))
1426 {
1427 if (jsonCallout.at("Deconfigured").get<bool>())
1428 {
1429 setErrorStatusFlag(ErrorStatusFlags::deconfigured);
1430 }
1431 }
1432
1433 if (jsonCallout.contains("Guarded"))
1434 {
1435 if (jsonCallout.at("Guarded").get<bool>())
1436 {
1437 setErrorStatusFlag(ErrorStatusFlags::guarded);
1438 }
1439 }
1440 }
1441
1442 if (callout)
1443 {
1444 createCalloutsObject();
1445 _callouts->addCallout(std::move(callout));
1446 }
1447 }
1448
getPriorityFromJSON(const nlohmann::json & json)1449 CalloutPriority SRC::getPriorityFromJSON(const nlohmann::json& json)
1450 {
1451 // Looks like:
1452 // {
1453 // "Priority": "H"
1454 // }
1455 auto p = json.at("Priority").get<std::string>();
1456 if (p.empty())
1457 {
1458 throw std::runtime_error{"Priority field in callout is empty"};
1459 }
1460
1461 auto priority = static_cast<CalloutPriority>(p.front());
1462
1463 // Validate it
1464 auto priorityIt = pv::findByValue(static_cast<uint32_t>(priority),
1465 pv::calloutPriorityValues);
1466 if (priorityIt == pv::calloutPriorityValues.end())
1467 {
1468 throw std::runtime_error{
1469 std::format("Invalid priority '{}' found in JSON callout", p)};
1470 }
1471
1472 return priority;
1473 }
1474
getMRUsFromJSON(const nlohmann::json & mruJSON)1475 std::vector<src::MRU::MRUCallout> SRC::getMRUsFromJSON(
1476 const nlohmann::json& mruJSON)
1477 {
1478 std::vector<src::MRU::MRUCallout> mrus;
1479
1480 // Looks like:
1481 // [
1482 // {
1483 // "ID": 100,
1484 // "Priority": "H"
1485 // }
1486 // ]
1487 if (!mruJSON.is_array())
1488 {
1489 addDebugData("MRU callout JSON is not an array");
1490 return mrus;
1491 }
1492
1493 for (const auto& mruCallout : mruJSON)
1494 {
1495 try
1496 {
1497 auto priority = getPriorityFromJSON(mruCallout);
1498 auto id = mruCallout.at("ID").get<uint32_t>();
1499
1500 src::MRU::MRUCallout mru{static_cast<uint32_t>(priority), id};
1501 mrus.push_back(std::move(mru));
1502 }
1503 catch (const std::exception& e)
1504 {
1505 addDebugData(std::format("Invalid MRU entry in JSON: {}: {}",
1506 mruCallout.dump(), e.what()));
1507 }
1508 }
1509
1510 return mrus;
1511 }
1512
getSrcStruct()1513 std::vector<uint8_t> SRC::getSrcStruct()
1514 {
1515 std::vector<uint8_t> data;
1516 Stream stream{data};
1517
1518 //------ Ref section 4.3 in PEL doc---
1519 //------ SRC Structure 40 bytes-------
1520 // Byte-0 | Byte-1 | Byte-2 | Byte-3 |
1521 // -----------------------------------
1522 // 02 | 08 | 00 | 09 | ==> Header
1523 // 00 | 00 | 00 | 48 | ==> Header
1524 // 00 | 00 | 00 | 00 | ==> Hex data word-2
1525 // 00 | 00 | 00 | 00 | ==> Hex data word-3
1526 // 00 | 00 | 00 | 00 | ==> Hex data word-4
1527 // 20 | 00 | 00 | 00 | ==> Hex data word-5
1528 // 00 | 00 | 00 | 00 | ==> Hex data word-6
1529 // 00 | 00 | 00 | 00 | ==> Hex data word-7
1530 // 00 | 00 | 00 | 00 | ==> Hex data word-8
1531 // 00 | 00 | 00 | 00 | ==> Hex data word-9
1532 // -----------------------------------
1533 // ASCII string - 8 bytes |
1534 // -----------------------------------
1535 // ASCII space NULL - 24 bytes |
1536 // -----------------------------------
1537 //_size = Base SRC struct: 8 byte header + hex data section + ASCII string
1538
1539 uint8_t flags = (_flags | postOPPanel);
1540
1541 stream << _version << flags << _reserved1B << _wordCount << _reserved2B
1542 << _size;
1543
1544 for (auto& word : _hexData)
1545 {
1546 stream << word;
1547 }
1548
1549 _asciiString->flatten(stream);
1550
1551 return data;
1552 }
1553
setProgressCode(const DataInterfaceBase & dataIface)1554 void SRC::setProgressCode(const DataInterfaceBase& dataIface)
1555 {
1556 std::vector<uint8_t> progressSRC;
1557
1558 try
1559 {
1560 progressSRC = dataIface.getRawProgressSRC();
1561 }
1562 catch (const std::exception& e)
1563 {
1564 lg2::error("Error getting progress code: {ERROR}", "ERROR", e);
1565 return;
1566 }
1567
1568 _hexData[2] = getProgressCode(progressSRC);
1569 }
1570
getProgressCode(std::vector<uint8_t> & rawProgressSRC)1571 uint32_t SRC::getProgressCode(std::vector<uint8_t>& rawProgressSRC)
1572 {
1573 uint32_t progressCode = 0;
1574
1575 // A valid progress SRC is at least 72 bytes
1576 if (rawProgressSRC.size() < 72)
1577 {
1578 return progressCode;
1579 }
1580
1581 try
1582 {
1583 // The ASCII string field in progress SRCs starts at offset 40.
1584 // Take the first 8 characters to put in the uint32:
1585 // "CC009189" -> 0xCC009189
1586 Stream stream{rawProgressSRC, 40};
1587 src::AsciiString aString{stream};
1588 auto progressCodeString = aString.get().substr(0, 8);
1589
1590 if (std::all_of(progressCodeString.begin(), progressCodeString.end(),
1591 [](char c) {
1592 return std::isxdigit(static_cast<unsigned char>(c));
1593 }))
1594 {
1595 progressCode = std::stoul(progressCodeString, nullptr, 16);
1596 }
1597 }
1598 catch (const std::exception& e)
1599 {}
1600
1601 return progressCode;
1602 }
1603
1604 } // namespace pels
1605 } // namespace openpower
1606