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