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 26 enum FfdcSubType_t : uint8_t 27 { 28 FFDC_SIGNATURES = 0x01, 29 FFDC_REGISTER_DUMP = 0x02, 30 FFDC_CALLOUT_FFDC = 0x03, 31 FFDC_HB_SCRATCH_REGS = 0x04, 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 std::string __getMessageRegistry(AnalysisType i_type) 288 { 289 290 if (AnalysisType::SYSTEM_CHECKSTOP == i_type) 291 { 292 return "org.open_power.HwDiags.Error.Checkstop"; 293 } 294 else if (AnalysisType::TERMINATE_IMMEDIATE == i_type) 295 { 296 return "org.open_power.HwDiags.Error.Predictive"; 297 } 298 299 return "org.open_power.HwDiags.Error.Informational"; // default 300 } 301 302 //------------------------------------------------------------------------------ 303 304 std::string __getMessageSeverity(AnalysisType i_type) 305 { 306 // Default severity is informational (no service action required). 307 LogSvr::Entry::Level severity = LogSvr::Entry::Level::Informational; 308 309 if (AnalysisType::SYSTEM_CHECKSTOP == i_type) 310 { 311 // System checkstops are always unrecoverable errors (service action 312 // required). 313 severity = LogSvr::Entry::Level::Error; 314 } 315 else if (AnalysisType::TERMINATE_IMMEDIATE == i_type) 316 { 317 // TIs will be reported as a predicive error (service action required). 318 severity = LogSvr::Entry::Level::Warning; 319 } 320 321 // Convert the message severity to a string. 322 return LogSvr::Entry::convertLevelToString(severity); 323 } 324 325 //------------------------------------------------------------------------------ 326 327 uint32_t commitPel(const ServiceData& i_servData) 328 { 329 uint32_t o_plid = 0; // default, zero indicates PEL was not created 330 331 // The message registry will require additional log data to fill in keywords 332 // and additional log data. 333 std::map<std::string, std::string> logData; 334 335 // Keep track of the temporary files associated with the user data FFDC. 336 // WARNING: Once the objects stored in this vector go out of scope, the 337 // temporary files will be deleted. So they must remain in scope 338 // until the PEL is submitted. 339 std::vector<util::FFDCFile> userDataFiles; 340 341 // Set the subsystem in the primary SRC. 342 i_servData.addSrcSubsystem(logData); 343 344 // Set words 6-9 of the SRC. 345 __setSrc(i_servData.getRootCause(), logData); 346 347 // Add the list of callouts to the PEL. 348 __addCalloutList(i_servData, userDataFiles); 349 350 // Add the Hostboot scratch register to the PEL. 351 __captureHostbootScratchRegisters(userDataFiles); 352 353 // Add the callout FFDC to the PEL. 354 __addCalloutFFDC(i_servData, userDataFiles); 355 356 // Capture the complete signature list. 357 __captureSignatureList(i_servData.getIsolationData(), userDataFiles); 358 359 // Capture the complete signature list. 360 __captureRegisterDump(i_servData.getIsolationData(), userDataFiles); 361 362 // Now, that all of the user data files have been created, transform the 363 // data into the proper format for the PEL. 364 std::vector<util::FFDCTuple> userData; 365 util::transformFFDC(userDataFiles, userData); 366 367 // Get the message registry entry for this failure. 368 auto message = __getMessageRegistry(i_servData.getAnalysisType()); 369 370 // Get the message severity for this failure. 371 auto severity = __getMessageSeverity(i_servData.getAnalysisType()); 372 373 // Create the PEL 374 o_plid = util::dbus::createPel(message, severity, logData, userData); 375 376 if (0 == o_plid) 377 { 378 trace::err("Error while creating event log entry"); 379 } 380 381 // Return the platorm log ID of the error. 382 return o_plid; 383 } 384 385 } // namespace analyzer 386