1 #include <unistd.h> 2 3 #include <analyzer/service_data.hpp> 4 #include <analyzer/util.hpp> 5 #include <hei_main.hpp> 6 #include <phosphor-logging/elog.hpp> 7 #include <sdbusplus/bus.hpp> 8 #include <util/bin_stream.hpp> 9 #include <util/dbus.hpp> 10 #include <util/ffdc_file.hpp> 11 #include <util/pdbg.hpp> 12 #include <util/trace.hpp> 13 #include <xyz/openbmc_project/Logging/Create/server.hpp> 14 #include <xyz/openbmc_project/Logging/Entry/server.hpp> 15 16 #include <fstream> 17 #include <memory> 18 19 namespace LogSvr = sdbusplus::xyz::openbmc_project::Logging::server; 20 21 namespace analyzer 22 { 23 //------------------------------------------------------------------------------ 24 25 enum FfdcSubType_t : uint8_t 26 { 27 FFDC_SIGNATURES = 0x01, 28 FFDC_REGISTER_DUMP = 0x02, 29 FFDC_CALLOUT_FFDC = 0x03, 30 FFDC_HB_SCRATCH_REGS = 0x04, 31 FFDC_SCRATCH_SIG = 0x05, 32 33 // For the callout section, the value of '0xCA' is required per the 34 // phosphor-logging openpower-pel extention spec. 35 FFDC_CALLOUTS = 0xCA, 36 }; 37 38 enum FfdcVersion_t : uint8_t 39 { 40 FFDC_VERSION1 = 0x01, 41 }; 42 43 //------------------------------------------------------------------------------ 44 45 void __getSrc(const libhei::Signature& i_signature, uint32_t& o_word6, 46 uint32_t& o_word7, uint32_t& o_word8) 47 { 48 o_word6 = o_word7 = o_word8 = 0; // default 49 50 // Note that the chip could be null if there was no root cause attention 51 // found during analysis. 52 if (nullptr != i_signature.getChip().getChip()) 53 { 54 // [ 0:15] chip model 55 // [16:23] reserved space in chip ID 56 // [24:31] chip EC level 57 o_word6 = i_signature.getChip().getType(); 58 59 // [ 0:15] chip position 60 // [16:23] node position 61 // [24:31] signature attention type 62 auto chipPos = util::pdbg::getChipPos(i_signature.getChip()); 63 uint8_t nodePos = 0; // TODO: multi-node support 64 auto attn = i_signature.getAttnType(); 65 66 o_word7 = 67 (chipPos & 0xffff) << 16 | (nodePos & 0xff) << 8 | (attn & 0xff); 68 69 // [ 0:15] signature ID 70 // [16:23] signature instance 71 // [24:31] signature bit position 72 o_word8 = i_signature.toUint32(); 73 74 // Word 9 is currently unused 75 } 76 } 77 78 //------------------------------------------------------------------------------ 79 80 void __setSrc(const libhei::Signature& i_rootCause, 81 std::map<std::string, std::string>& io_logData) 82 { 83 uint32_t word6 = 0, word7 = 0, word8 = 0; 84 __getSrc(i_rootCause, word6, word7, word8); 85 86 io_logData["SRC6"] = std::to_string(word6); 87 io_logData["SRC7"] = std::to_string(word7); 88 io_logData["SRC8"] = std::to_string(word8); 89 } 90 91 //------------------------------------------------------------------------------ 92 93 void __addCalloutList(const ServiceData& i_servData, 94 std::vector<util::FFDCFile>& io_userDataFiles) 95 { 96 // Create a new entry for the user data section containing the callout list. 97 io_userDataFiles.emplace_back(util::FFDCFormat::JSON, FFDC_CALLOUTS, 98 FFDC_VERSION1); 99 100 // Use a file stream to write the JSON to file. 101 std::ofstream o{io_userDataFiles.back().getPath()}; 102 o << i_servData.getCalloutList(); 103 } 104 105 //------------------------------------------------------------------------------ 106 107 void __addCalloutFFDC(const ServiceData& i_servData, 108 std::vector<util::FFDCFile>& io_userDataFiles) 109 { 110 // Create a new entry for the user data section containing the FFDC. 111 io_userDataFiles.emplace_back(util::FFDCFormat::Custom, FFDC_CALLOUT_FFDC, 112 FFDC_VERSION1); 113 114 // Use a file stream to write the JSON to file. 115 std::ofstream o{io_userDataFiles.back().getPath()}; 116 o << i_servData.getCalloutFFDC(); 117 } 118 119 //------------------------------------------------------------------------------ 120 121 void __captureSignatureList(const libhei::IsolationData& i_isoData, 122 std::vector<util::FFDCFile>& io_userDataFiles) 123 { 124 // Create a new entry for this user data section regardless if there are any 125 // signatures in the list. 126 io_userDataFiles.emplace_back(util::FFDCFormat::Custom, FFDC_SIGNATURES, 127 FFDC_VERSION1); 128 129 // Create a streamer for easy writing to the FFDC file. 130 auto path = io_userDataFiles.back().getPath(); 131 util::BinFileWriter stream{path}; 132 133 // The first 4 bytes in the FFDC contains the number of signatures in the 134 // list. Then, the list of signatures will follow. 135 136 auto list = i_isoData.getSignatureList(); 137 138 uint32_t numSigs = list.size(); 139 stream << numSigs; 140 141 for (const auto& sig : list) 142 { 143 // Each signature will use the same format as the SRC (12 bytes each). 144 uint32_t word6 = 0, word7 = 0, word8 = 0; 145 __getSrc(sig, word6, word7, word8); 146 stream << word6 << word7 << word8; 147 } 148 149 // If the stream failed for any reason, remove the FFDC file. 150 if (!stream.good()) 151 { 152 trace::err("Unable to write signature list FFDC file: %s", 153 path.string().c_str()); 154 io_userDataFiles.pop_back(); 155 } 156 } 157 158 //------------------------------------------------------------------------------ 159 160 void __captureRegisterDump(const libhei::IsolationData& i_isoData, 161 std::vector<util::FFDCFile>& io_userDataFiles) 162 { 163 // Create a new entry for this user data section regardless if there are any 164 // registers in the dump. 165 io_userDataFiles.emplace_back(util::FFDCFormat::Custom, FFDC_REGISTER_DUMP, 166 FFDC_VERSION1); 167 168 // Create a streamer for easy writing to the FFDC file. 169 auto path = io_userDataFiles.back().getPath(); 170 util::BinFileWriter stream{path}; 171 172 // The first 4 bytes in the FFDC contains the number of chips with register 173 // data. Then the data for each chip will follow. 174 175 auto dump = i_isoData.getRegisterDump(); 176 177 uint32_t numChips = dump.size(); 178 stream << numChips; 179 180 for (const auto& entry : dump) 181 { 182 auto chip = entry.first; 183 auto regList = entry.second; 184 185 // Each chip will have the following information: 186 // 4 byte chip model/EC 187 // 2 byte chip position 188 // 1 byte node position 189 // 4 byte number of registers 190 // Then the data for each register will follow. 191 192 uint32_t chipType = chip.getType(); 193 uint16_t chipPos = util::pdbg::getChipPos(chip); 194 uint8_t nodePos = 0; // TODO: multi-node support 195 uint32_t numRegs = regList.size(); 196 stream << chipType << chipPos << nodePos << numRegs; 197 198 for (const auto& reg : regList) 199 { 200 // Each register will have the following information: 201 // 3 byte register ID 202 // 1 byte register instance 203 // 1 byte data size 204 // * byte data buffer (* depends on value of data size) 205 206 libhei::RegisterId_t regId = reg.regId; // 3 byte 207 libhei::Instance_t regInst = reg.regInst; // 1 byte 208 209 auto tmp = libhei::BitString::getMinBytes(reg.data->getBitLen()); 210 if (255 < tmp) 211 { 212 trace::inf("Register data execeeded 255 and was truncated: " 213 "regId=0x%06x regInst=%u", 214 regId, regInst); 215 tmp = 255; 216 } 217 uint8_t dataSize = tmp; 218 219 stream << regId << regInst << dataSize; 220 221 stream.write(reg.data->getBufAddr(), dataSize); 222 } 223 } 224 225 // If the stream failed for any reason, remove the FFDC file. 226 if (!stream.good()) 227 { 228 trace::err("Unable to write register dump FFDC file: %s", 229 path.string().c_str()); 230 io_userDataFiles.pop_back(); 231 } 232 } 233 234 //------------------------------------------------------------------------------ 235 236 void __captureHostbootScratchRegisters( 237 std::vector<util::FFDCFile>& io_userDataFiles) 238 { 239 // Get the Hostboot scratch registers from the primary processor. 240 241 uint32_t cfamAddr = 0x283C; 242 uint32_t cfamValue = 0; 243 244 uint64_t scomAddr = 0x4602F489; 245 uint64_t scomValue = 0; 246 247 auto priProc = util::pdbg::getPrimaryProcessor(); 248 if (nullptr == priProc) 249 { 250 trace::err("Unable to get primary processor"); 251 } 252 else 253 { 254 if (0 != util::pdbg::getCfam(priProc, cfamAddr, cfamValue)) 255 { 256 cfamValue = 0; // just in case 257 } 258 259 if (0 != util::pdbg::getScom(priProc, scomAddr, scomValue)) 260 { 261 scomValue = 0; // just in case 262 } 263 } 264 265 // Create a new entry for this user data section. 266 io_userDataFiles.emplace_back(util::FFDCFormat::Custom, 267 FFDC_HB_SCRATCH_REGS, FFDC_VERSION1); 268 269 // Create a streamer for easy writing to the FFDC file. 270 auto path = io_userDataFiles.back().getPath(); 271 util::BinFileWriter stream{path}; 272 273 // Add the data (CFAM addr/val, then SCOM addr/val). 274 stream << cfamAddr << cfamValue << scomAddr << scomValue; 275 276 // If the stream failed for any reason, remove the FFDC file. 277 if (!stream.good()) 278 { 279 trace::err("Unable to write register dump FFDC file: %s", 280 path.string().c_str()); 281 io_userDataFiles.pop_back(); 282 } 283 } 284 285 //------------------------------------------------------------------------------ 286 287 void __captureScratchRegSignature(std::vector<util::FFDCFile>& io_userDataFiles) 288 { 289 // If analysis was interrupted by a system checkstop, there may exist an 290 // error signature within Hostboot scratch registers 9 (scom: 0x00050180, 291 // fsi: 0x2980) and 10 (scom: 0x00050181, fsi: 0x2981) which indicates the 292 // signature from the interrupted analysis. If data exists within those 293 // registers a user data section will be created in the PEL to record it. 294 295 uint32_t reg9Addr = 0x2980; 296 uint32_t reg10Addr = 0x2981; 297 298 uint32_t chipId = 0; // stored in reg9 299 uint32_t sigId = 0; // stored in reg10 300 301 auto priProc = util::pdbg::getPrimaryProcessor(); 302 if (nullptr == priProc) 303 { 304 trace::err("Unable to get primary processor"); 305 } 306 else 307 { 308 if (0 != util::pdbg::getCfam(priProc, reg9Addr, chipId)) 309 { 310 chipId = 0; // just in case 311 } 312 313 if (0 != util::pdbg::getCfam(priProc, reg10Addr, sigId)) 314 { 315 sigId = 0; // just in case 316 } 317 } 318 319 // If any non-zero data was found in the registers, add them to the FFDC. 320 if (0 != chipId || 0 != sigId) 321 { 322 // Create a new entry for this user data section. 323 io_userDataFiles.emplace_back(util::FFDCFormat::Custom, 324 FFDC_SCRATCH_SIG, FFDC_VERSION1); 325 326 // Create a streamer for easy writing to the FFDC file. 327 auto path = io_userDataFiles.back().getPath(); 328 util::BinFileWriter stream{path}; 329 330 stream << chipId << sigId; 331 332 // If the stream failed for any reason, remove the FFDC file. 333 if (!stream.good()) 334 { 335 trace::err("Unable to write register dump FFDC file: %s", 336 path.string().c_str()); 337 io_userDataFiles.pop_back(); 338 } 339 } 340 } 341 342 //------------------------------------------------------------------------------ 343 344 std::string __getMessageRegistry(AnalysisType i_type) 345 { 346 if (AnalysisType::SYSTEM_CHECKSTOP == i_type) 347 { 348 return "org.open_power.HwDiags.Error.Checkstop"; 349 } 350 else if (AnalysisType::TERMINATE_IMMEDIATE == i_type) 351 { 352 return "org.open_power.HwDiags.Error.Predictive"; 353 } 354 355 return "org.open_power.HwDiags.Error.Informational"; // default 356 } 357 358 //------------------------------------------------------------------------------ 359 360 std::string __getMessageSeverity(AnalysisType i_type) 361 { 362 // Default severity is informational (no service action required). 363 LogSvr::Entry::Level severity = LogSvr::Entry::Level::Informational; 364 365 if (AnalysisType::SYSTEM_CHECKSTOP == i_type) 366 { 367 // System checkstops are always unrecoverable errors (service action 368 // required). 369 severity = LogSvr::Entry::Level::Error; 370 } 371 else if (AnalysisType::TERMINATE_IMMEDIATE == i_type) 372 { 373 // TIs will be reported as a predicive error (service action required). 374 severity = LogSvr::Entry::Level::Warning; 375 } 376 377 // Convert the message severity to a string. 378 return LogSvr::Entry::convertLevelToString(severity); 379 } 380 381 //------------------------------------------------------------------------------ 382 383 uint32_t commitPel(const ServiceData& i_servData) 384 { 385 uint32_t o_plid = 0; // default, zero indicates PEL was not created 386 387 // The message registry will require additional log data to fill in keywords 388 // and additional log data. 389 std::map<std::string, std::string> logData; 390 391 // Keep track of the temporary files associated with the user data FFDC. 392 // WARNING: Once the objects stored in this vector go out of scope, the 393 // temporary files will be deleted. So they must remain in scope 394 // until the PEL is submitted. 395 std::vector<util::FFDCFile> userDataFiles; 396 397 // Set the subsystem in the primary SRC. 398 i_servData.addSrcSubsystem(logData); 399 400 // Set words 6-9 of the SRC. 401 __setSrc(i_servData.getRootCause(), logData); 402 403 // Add the list of callouts to the PEL. 404 __addCalloutList(i_servData, userDataFiles); 405 406 // Add the Hostboot scratch register to the PEL. 407 __captureHostbootScratchRegisters(userDataFiles); 408 409 // Add the signature stored in the scratch regs if it exists. 410 __captureScratchRegSignature(userDataFiles); 411 412 // Add the callout FFDC to the PEL. 413 __addCalloutFFDC(i_servData, userDataFiles); 414 415 // Capture the complete signature list. 416 __captureSignatureList(i_servData.getIsolationData(), userDataFiles); 417 418 // Capture the complete signature list. 419 __captureRegisterDump(i_servData.getIsolationData(), userDataFiles); 420 421 // Now, that all of the user data files have been created, transform the 422 // data into the proper format for the PEL. 423 std::vector<util::FFDCTuple> userData; 424 util::transformFFDC(userDataFiles, userData); 425 426 // Get the message registry entry for this failure. 427 auto message = __getMessageRegistry(i_servData.getAnalysisType()); 428 429 // Get the message severity for this failure. 430 auto severity = __getMessageSeverity(i_servData.getAnalysisType()); 431 432 // Create the PEL 433 o_plid = util::dbus::createPel(message, severity, logData, userData); 434 435 if (0 == o_plid) 436 { 437 trace::err("Error while creating event log entry"); 438 } 439 440 // Return the platorm log ID of the error. 441 return o_plid; 442 } 443 444 } // namespace analyzer 445