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