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 "json_utils.hpp" 19 #include "paths.hpp" 20 #include "pel_values.hpp" 21 22 #include <phosphor-logging/log.hpp> 23 24 namespace openpower 25 { 26 namespace pels 27 { 28 namespace pv = openpower::pels::pel_values; 29 namespace rg = openpower::pels::message; 30 using namespace phosphor::logging; 31 32 constexpr size_t ccinSize = 4; 33 34 void SRC::unflatten(Stream& stream) 35 { 36 stream >> _header >> _version >> _flags >> _reserved1B >> _wordCount >> 37 _reserved2B >> _size; 38 39 for (auto& word : _hexData) 40 { 41 stream >> word; 42 } 43 44 _asciiString = std::make_unique<src::AsciiString>(stream); 45 46 if (hasAdditionalSections()) 47 { 48 // The callouts section is currently the only extra subsection type 49 _callouts = std::make_unique<src::Callouts>(stream); 50 } 51 } 52 53 void SRC::flatten(Stream& stream) const 54 { 55 stream << _header << _version << _flags << _reserved1B << _wordCount 56 << _reserved2B << _size; 57 58 for (auto& word : _hexData) 59 { 60 stream << word; 61 } 62 63 _asciiString->flatten(stream); 64 65 if (_callouts) 66 { 67 _callouts->flatten(stream); 68 } 69 } 70 71 SRC::SRC(Stream& pel) 72 { 73 try 74 { 75 unflatten(pel); 76 validate(); 77 } 78 catch (const std::exception& e) 79 { 80 log<level::ERR>("Cannot unflatten SRC", entry("ERROR=%s", e.what())); 81 _valid = false; 82 } 83 } 84 85 SRC::SRC(const message::Entry& regEntry, const AdditionalData& additionalData, 86 const DataInterfaceBase& dataIface) 87 { 88 _header.id = static_cast<uint16_t>(SectionID::primarySRC); 89 _header.version = srcSectionVersion; 90 _header.subType = srcSectionSubtype; 91 _header.componentID = regEntry.componentID; 92 93 _version = srcVersion; 94 95 _flags = 0; 96 if (regEntry.src.powerFault.value_or(false)) 97 { 98 _flags |= powerFaultEvent; 99 } 100 101 _reserved1B = 0; 102 103 _wordCount = numSRCHexDataWords + 1; 104 105 _reserved2B = 0; 106 107 // There are multiple fields encoded in the hex data words. 108 std::for_each(_hexData.begin(), _hexData.end(), 109 [](auto& word) { word = 0; }); 110 setBMCFormat(); 111 setBMCPosition(); 112 setMotherboardCCIN(dataIface); 113 114 // Partition dump status and partition boot type always 0 for BMC errors. 115 // 116 // TODO: Fill in other fields that aren't available yet. 117 118 // Fill in the last 4 words from the AdditionalData property contents. 119 setUserDefinedHexWords(regEntry, additionalData); 120 121 _asciiString = std::make_unique<src::AsciiString>(regEntry); 122 123 // TODO: add callouts using the Callouts object 124 125 _size = baseSRCSize; 126 _size += _callouts ? _callouts->flattenedSize() : 0; 127 _header.size = Section::flattenedSize() + _size; 128 129 _valid = true; 130 } 131 132 void SRC::setUserDefinedHexWords(const message::Entry& regEntry, 133 const AdditionalData& ad) 134 { 135 if (!regEntry.src.hexwordADFields) 136 { 137 return; 138 } 139 140 // Save the AdditionalData value corresponding to the 141 // adName key in _hexData[wordNum]. 142 for (const auto& [wordNum, adName] : *regEntry.src.hexwordADFields) 143 { 144 // Can only set words 6 - 9 145 if (!isUserDefinedWord(wordNum)) 146 { 147 log<level::WARNING>("SRC user data word out of range", 148 entry("WORD_NUM=%d", wordNum), 149 entry("ERROR_NAME=%s", regEntry.name.c_str())); 150 continue; 151 } 152 153 auto value = ad.getValue(adName); 154 if (value) 155 { 156 _hexData[getWordIndexFromWordNum(wordNum)] = 157 std::strtoul(value.value().c_str(), nullptr, 0); 158 } 159 else 160 { 161 log<level::WARNING>("Source for user data SRC word not found", 162 entry("ADDITIONALDATA_KEY=%s", adName.c_str()), 163 entry("ERROR_NAME=%s", regEntry.name.c_str())); 164 } 165 } 166 } 167 168 void SRC::setMotherboardCCIN(const DataInterfaceBase& dataIface) 169 { 170 uint32_t ccin = 0; 171 auto ccinString = dataIface.getMotherboardCCIN(); 172 173 try 174 { 175 if (ccinString.size() == ccinSize) 176 { 177 ccin = std::stoi(ccinString, 0, 16); 178 } 179 } 180 catch (std::exception& e) 181 { 182 log<level::WARNING>("Could not convert motherboard CCIN to a number", 183 entry("CCIN=%s", ccinString.c_str())); 184 return; 185 } 186 187 // Set the first 2 bytes 188 _hexData[1] |= ccin << 16; 189 } 190 191 void SRC::validate() 192 { 193 bool failed = false; 194 195 if ((header().id != static_cast<uint16_t>(SectionID::primarySRC)) && 196 (header().id != static_cast<uint16_t>(SectionID::secondarySRC))) 197 { 198 log<level::ERR>("Invalid SRC section ID", 199 entry("ID=0x%X", header().id)); 200 failed = true; 201 } 202 203 // Check the version in the SRC, not in the header 204 if (_version != srcVersion) 205 { 206 log<level::ERR>("Invalid SRC version", entry("VERSION=0x%X", _version)); 207 failed = true; 208 } 209 210 _valid = failed ? false : true; 211 } 212 213 bool SRC::isBMCSRC() const 214 { 215 auto as = asciiString(); 216 if (as.length() >= 2) 217 { 218 uint8_t errorType = strtoul(as.substr(0, 2).c_str(), nullptr, 16); 219 return (errorType == static_cast<uint8_t>(SRCType::bmcError) || 220 errorType == static_cast<uint8_t>(SRCType::powerError)); 221 } 222 return false; 223 } 224 225 std::optional<std::string> SRC::getErrorDetails(message::Registry& registry, 226 DetailLevel type, 227 bool toCache) const 228 { 229 const std::string jsonIndent(indentLevel, 0x20); 230 std::string errorOut; 231 if (isBMCSRC()) 232 { 233 auto entry = registry.lookup("0x" + asciiString().substr(4, 4), 234 rg::LookupType::reasonCode, toCache); 235 if (entry) 236 { 237 errorOut.append(jsonIndent + "\"Error Details\": {\n"); 238 auto errorMsg = getErrorMessage(*entry); 239 if (errorMsg) 240 { 241 if (type == DetailLevel::message) 242 { 243 return errorMsg.value(); 244 } 245 else 246 { 247 jsonInsert(errorOut, "Message", errorMsg.value(), 2); 248 } 249 } 250 if (entry->src.hexwordADFields) 251 { 252 std::map<size_t, std::string> adFields = 253 entry->src.hexwordADFields.value(); 254 for (const auto& hexwordMap : adFields) 255 { 256 jsonInsert(errorOut, hexwordMap.second, 257 getNumberString("0x%X", 258 _hexData[getWordIndexFromWordNum( 259 hexwordMap.first)]), 260 2); 261 } 262 } 263 errorOut.erase(errorOut.size() - 2); 264 errorOut.append("\n"); 265 errorOut.append(jsonIndent + "},\n"); 266 return errorOut; 267 } 268 } 269 return std::nullopt; 270 } 271 272 std::optional<std::string> 273 SRC::getErrorMessage(const message::Entry& regEntry) const 274 { 275 try 276 { 277 if (regEntry.doc.messageArgSources) 278 { 279 size_t msgLen = regEntry.doc.message.length(); 280 char msg[msgLen + 1]; 281 strcpy(msg, regEntry.doc.message.c_str()); 282 std::vector<uint32_t> argSourceVals; 283 std::string message; 284 const auto& argValues = regEntry.doc.messageArgSources.value(); 285 for (size_t i = 0; i < argValues.size(); ++i) 286 { 287 argSourceVals.push_back(_hexData[getWordIndexFromWordNum( 288 argValues[i].back() - '0')]); 289 } 290 const char* msgPointer = msg; 291 while (*msgPointer) 292 { 293 if (*msgPointer == '%') 294 { 295 msgPointer++; 296 size_t wordIndex = *msgPointer - '0'; 297 if (isdigit(*msgPointer) && wordIndex >= 1 && 298 static_cast<uint16_t>(wordIndex) <= 299 argSourceVals.size()) 300 { 301 message.append(getNumberString( 302 "0x%X", argSourceVals[wordIndex - 1])); 303 } 304 else 305 { 306 message.append("%" + std::string(1, *msgPointer)); 307 } 308 } 309 else 310 { 311 message.push_back(*msgPointer); 312 } 313 msgPointer++; 314 } 315 return message; 316 } 317 else 318 { 319 return regEntry.doc.message; 320 } 321 } 322 catch (const std::exception& e) 323 { 324 log<level::ERR>("Cannot get error message from registry entry", 325 entry("ERROR=%s", e.what())); 326 } 327 return std::nullopt; 328 } 329 330 std::optional<std::string> SRC::getCallouts() const 331 { 332 if (!_callouts) 333 { 334 return std::nullopt; 335 } 336 std::string printOut; 337 const std::string jsonIndent(indentLevel, 0x20); 338 const auto& callout = _callouts->callouts(); 339 const auto& compDescrp = pv::failingComponentType; 340 printOut.append(jsonIndent + "\"Callout Section\": {\n"); 341 jsonInsert(printOut, "Callout Count", std::to_string(callout.size()), 2); 342 printOut.append(jsonIndent + jsonIndent + "\"Callouts\": ["); 343 for (auto& entry : callout) 344 { 345 printOut.append("{\n"); 346 if (entry->fruIdentity()) 347 { 348 jsonInsert( 349 printOut, "FRU Type", 350 compDescrp.at(entry->fruIdentity()->failingComponentType()), 3); 351 jsonInsert(printOut, "Priority", 352 pv::getValue(entry->priority(), 353 pel_values::calloutPriorityValues), 354 3); 355 if (!entry->locationCode().empty()) 356 { 357 jsonInsert(printOut, "Location Code", entry->locationCode(), 3); 358 } 359 if (entry->fruIdentity()->getPN().has_value()) 360 { 361 jsonInsert(printOut, "Part Number", 362 entry->fruIdentity()->getPN().value(), 3); 363 } 364 if (entry->fruIdentity()->getMaintProc().has_value()) 365 { 366 jsonInsert(printOut, "Procedure Number", 367 entry->fruIdentity()->getMaintProc().value(), 3); 368 if (pv::procedureDesc.find( 369 entry->fruIdentity()->getMaintProc().value()) != 370 pv::procedureDesc.end()) 371 { 372 jsonInsert( 373 printOut, "Description", 374 pv::procedureDesc.at( 375 entry->fruIdentity()->getMaintProc().value()), 376 3); 377 } 378 } 379 if (entry->fruIdentity()->getCCIN().has_value()) 380 { 381 jsonInsert(printOut, "CCIN", 382 entry->fruIdentity()->getCCIN().value(), 3); 383 } 384 if (entry->fruIdentity()->getSN().has_value()) 385 { 386 jsonInsert(printOut, "Serial Number", 387 entry->fruIdentity()->getSN().value(), 3); 388 } 389 } 390 if (entry->pceIdentity()) 391 { 392 const auto& pceIdentMtms = entry->pceIdentity()->mtms(); 393 if (!pceIdentMtms.machineTypeAndModel().empty()) 394 { 395 jsonInsert(printOut, "PCE MTMS", 396 pceIdentMtms.machineTypeAndModel() + "_" + 397 pceIdentMtms.machineSerialNumber(), 398 3); 399 } 400 if (!entry->pceIdentity()->enclosureName().empty()) 401 { 402 jsonInsert(printOut, "PCE Name", 403 entry->pceIdentity()->enclosureName(), 3); 404 } 405 } 406 if (entry->mru()) 407 { 408 const auto& mruCallouts = entry->mru()->mrus(); 409 std::string mruId; 410 for (auto& element : mruCallouts) 411 { 412 if (!mruId.empty()) 413 { 414 mruId.append(", " + getNumberString("%08X", element.id)); 415 } 416 else 417 { 418 mruId.append(getNumberString("%08X", element.id)); 419 } 420 } 421 jsonInsert(printOut, "MRU Id", mruId, 3); 422 } 423 printOut.erase(printOut.size() - 2); 424 printOut.append("\n" + jsonIndent + jsonIndent + "}, "); 425 }; 426 printOut.erase(printOut.size() - 2); 427 printOut.append("]\n" + jsonIndent + "}"); 428 return printOut; 429 } 430 431 std::optional<std::string> SRC::getJSON(message::Registry& registry) const 432 { 433 std::string ps; 434 jsonInsert(ps, pv::sectionVer, getNumberString("%d", _header.version), 1); 435 jsonInsert(ps, pv::subSection, getNumberString("%d", _header.subType), 1); 436 jsonInsert(ps, pv::createdBy, getNumberString("0x%X", _header.componentID), 437 1); 438 jsonInsert(ps, "SRC Version", getNumberString("0x%02X", _version), 1); 439 jsonInsert(ps, "SRC Format", getNumberString("0x%02X", _hexData[0] & 0xFF), 440 1); 441 jsonInsert(ps, "Virtual Progress SRC", 442 pv::boolString.at(_flags & virtualProgressSRC), 1); 443 jsonInsert(ps, "I5/OS Service Event Bit", 444 pv::boolString.at(_flags & i5OSServiceEventBit), 1); 445 jsonInsert(ps, "Hypervisor Dump Initiated", 446 pv::boolString.at(_flags & hypDumpInit), 1); 447 jsonInsert(ps, "Power Control Net Fault", 448 pv::boolString.at(isPowerFaultEvent()), 1); 449 450 if (isBMCSRC()) 451 { 452 std::string ccinString; 453 uint32_t ccin = _hexData[1] >> 16; 454 455 if (ccin) 456 { 457 ccinString = getNumberString("%04X", ccin); 458 } 459 // The PEL spec calls it a backplane, so call it that here. 460 jsonInsert(ps, "Backplane CCIN", ccinString, 1); 461 } 462 463 auto errorDetails = getErrorDetails(registry, DetailLevel::json, true); 464 if (errorDetails) 465 { 466 ps.append(errorDetails.value()); 467 } 468 jsonInsert(ps, "Valid Word Count", getNumberString("0x%02X", _wordCount), 469 1); 470 std::string refcode = asciiString(); 471 std::string extRefcode; 472 size_t pos = refcode.find(0x20); 473 if (pos != std::string::npos) 474 { 475 size_t nextPos = refcode.find_first_not_of(0x20, pos); 476 if (nextPos != std::string::npos) 477 { 478 extRefcode = trimEnd(refcode.substr(nextPos)); 479 } 480 refcode.erase(pos); 481 } 482 jsonInsert(ps, "Reference Code", refcode, 1); 483 if (!extRefcode.empty()) 484 { 485 jsonInsert(ps, "Extended Reference Code", extRefcode, 1); 486 } 487 for (size_t i = 2; i <= _wordCount; i++) 488 { 489 jsonInsert( 490 ps, "Hex Word " + std::to_string(i), 491 getNumberString("%08X", _hexData[getWordIndexFromWordNum(i)]), 1); 492 } 493 auto calloutJson = getCallouts(); 494 if (calloutJson) 495 { 496 ps.append(calloutJson.value()); 497 } 498 else 499 { 500 ps.erase(ps.size() - 2); 501 } 502 return ps; 503 } 504 505 } // namespace pels 506 } // namespace openpower 507