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 // [ 0:15] chip model 49 // [16:23] reserved space in chip ID 50 // [24:31] chip EC level 51 o_word6 = i_signature.getChip().getType(); 52 53 // [ 0:15] chip position 54 // [16:23] node position 55 // [24:31] signature attention type 56 auto chipPos = util::pdbg::getChipPos(i_signature.getChip()); 57 uint8_t nodePos = 0; // TODO: multi-node support 58 auto attn = i_signature.getAttnType(); 59 60 o_word7 = (chipPos & 0xffff) << 16 | (nodePos & 0xff) << 8 | (attn & 0xff); 61 62 // [ 0:15] signature ID 63 // [16:23] signature instance 64 // [24:31] signature bit position 65 o_word8 = i_signature.toUint32(); 66 67 // Word 9 is currently unused 68 } 69 70 //------------------------------------------------------------------------------ 71 72 void __setSrc(const libhei::Signature& i_rootCause, 73 std::map<std::string, std::string>& io_logData) 74 { 75 uint32_t word6 = 0, word7 = 0, word8 = 0; 76 __getSrc(i_rootCause, word6, word7, word8); 77 78 io_logData["SRC6"] = std::to_string(word6); 79 io_logData["SRC7"] = std::to_string(word7); 80 io_logData["SRC8"] = std::to_string(word8); 81 } 82 83 //------------------------------------------------------------------------------ 84 85 void __addCalloutList(const ServiceData& i_servData, 86 std::vector<util::FFDCFile>& io_userDataFiles) 87 { 88 // Create a new entry for the user data section containing the callout list. 89 io_userDataFiles.emplace_back(util::FFDCFormat::JSON, FFDC_CALLOUTS, 90 FFDC_VERSION1); 91 92 // Use a file stream to write the JSON to file. 93 std::ofstream o{io_userDataFiles.back().getPath()}; 94 o << i_servData.getCalloutList(); 95 } 96 97 //------------------------------------------------------------------------------ 98 99 void __addCalloutFFDC(const ServiceData& i_servData, 100 std::vector<util::FFDCFile>& io_userDataFiles) 101 { 102 // Create a new entry for the user data section containing the FFDC. 103 io_userDataFiles.emplace_back(util::FFDCFormat::Custom, FFDC_CALLOUT_FFDC, 104 FFDC_VERSION1); 105 106 // Use a file stream to write the JSON to file. 107 std::ofstream o{io_userDataFiles.back().getPath()}; 108 o << i_servData.getCalloutFFDC(); 109 } 110 111 //------------------------------------------------------------------------------ 112 113 void __captureSignatureList(const libhei::IsolationData& i_isoData, 114 std::vector<util::FFDCFile>& io_userDataFiles) 115 { 116 // Create a new entry for this user data section regardless if there are any 117 // signatures in the list. 118 io_userDataFiles.emplace_back(util::FFDCFormat::Custom, FFDC_SIGNATURES, 119 FFDC_VERSION1); 120 121 // Create a streamer for easy writing to the FFDC file. 122 auto path = io_userDataFiles.back().getPath(); 123 util::BinFileWriter stream{path}; 124 125 // The first 4 bytes in the FFDC contains the number of signatures in the 126 // list. Then, the list of signatures will follow. 127 128 auto list = i_isoData.getSignatureList(); 129 130 uint32_t numSigs = list.size(); 131 stream << numSigs; 132 133 for (const auto& sig : list) 134 { 135 // Each signature will use the same format as the SRC (12 bytes each). 136 uint32_t word6 = 0, word7 = 0, word8 = 0; 137 __getSrc(sig, word6, word7, word8); 138 stream << word6 << word7 << word8; 139 } 140 141 // If the stream failed for any reason, remove the FFDC file. 142 if (!stream.good()) 143 { 144 trace::err("Unable to write signature list FFDC file: %s", 145 path.string().c_str()); 146 io_userDataFiles.pop_back(); 147 } 148 } 149 150 //------------------------------------------------------------------------------ 151 152 void __captureRegisterDump(const libhei::IsolationData& i_isoData, 153 std::vector<util::FFDCFile>& io_userDataFiles) 154 { 155 // Create a new entry for this user data section regardless if there are any 156 // registers in the dump. 157 io_userDataFiles.emplace_back(util::FFDCFormat::Custom, FFDC_REGISTER_DUMP, 158 FFDC_VERSION1); 159 160 // Create a streamer for easy writing to the FFDC file. 161 auto path = io_userDataFiles.back().getPath(); 162 util::BinFileWriter stream{path}; 163 164 // The first 4 bytes in the FFDC contains the number of chips with register 165 // data. Then the data for each chip will follow. 166 167 auto dump = i_isoData.getRegisterDump(); 168 169 uint32_t numChips = dump.size(); 170 stream << numChips; 171 172 for (const auto& entry : dump) 173 { 174 auto chip = entry.first; 175 auto regList = entry.second; 176 177 // Each chip will have the following information: 178 // 4 byte chip model/EC 179 // 2 byte chip position 180 // 1 byte node position 181 // 4 byte number of registers 182 // Then the data for each register will follow. 183 184 uint32_t chipType = chip.getType(); 185 uint16_t chipPos = util::pdbg::getChipPos(chip); 186 uint8_t nodePos = 0; // TODO: multi-node support 187 uint32_t numRegs = regList.size(); 188 stream << chipType << chipPos << nodePos << numRegs; 189 190 for (const auto& reg : regList) 191 { 192 // Each register will have the following information: 193 // 3 byte register ID 194 // 1 byte register instance 195 // 1 byte data size 196 // * byte data buffer (* depends on value of data size) 197 198 libhei::RegisterId_t regId = reg.regId; // 3 byte 199 libhei::Instance_t regInst = reg.regInst; // 1 byte 200 201 auto tmp = libhei::BitString::getMinBytes(reg.data->getBitLen()); 202 if (255 < tmp) 203 { 204 trace::inf("Register data execeeded 255 and was truncated: " 205 "regId=0x%06x regInst=%u", 206 regId, regInst); 207 tmp = 255; 208 } 209 uint8_t dataSize = tmp; 210 211 stream << regId << regInst << dataSize; 212 213 stream.write(reg.data->getBufAddr(), dataSize); 214 } 215 } 216 217 // If the stream failed for any reason, remove the FFDC file. 218 if (!stream.good()) 219 { 220 trace::err("Unable to write register dump FFDC file: %s", 221 path.string().c_str()); 222 io_userDataFiles.pop_back(); 223 } 224 } 225 226 //------------------------------------------------------------------------------ 227 228 void __captureHostbootScratchRegisters( 229 std::vector<util::FFDCFile>& io_userDataFiles) 230 { 231 // Get the Hostboot scratch registers from the primary processor. 232 233 uint32_t cfamAddr = 0x283C; 234 uint32_t cfamValue = 0; 235 236 uint64_t scomAddr = 0x4602F489; 237 uint64_t scomValue = 0; 238 239 auto priProc = util::pdbg::getPrimaryProcessor(); 240 if (nullptr == priProc) 241 { 242 trace::err("Unable to get primary processor"); 243 } 244 else 245 { 246 if (0 != util::pdbg::getCfam(priProc, cfamAddr, cfamValue)) 247 { 248 cfamValue = 0; // just in case 249 } 250 251 if (0 != util::pdbg::getScom(priProc, scomAddr, scomValue)) 252 { 253 scomValue = 0; // just in case 254 } 255 } 256 257 // Create a new entry for this user data section. 258 io_userDataFiles.emplace_back(util::FFDCFormat::Custom, 259 FFDC_HB_SCRATCH_REGS, FFDC_VERSION1); 260 261 // Create a streamer for easy writing to the FFDC file. 262 auto path = io_userDataFiles.back().getPath(); 263 util::BinFileWriter stream{path}; 264 265 // Add the data (CFAM addr/val, then SCOM addr/val). 266 stream << cfamAddr << cfamValue << scomAddr << scomValue; 267 268 // If the stream failed for any reason, remove the FFDC file. 269 if (!stream.good()) 270 { 271 trace::err("Unable to write register dump FFDC file: %s", 272 path.string().c_str()); 273 io_userDataFiles.pop_back(); 274 } 275 } 276 277 //------------------------------------------------------------------------------ 278 279 std::string __getMessageRegistry(AnalysisType i_type) 280 { 281 282 if (AnalysisType::SYSTEM_CHECKSTOP == i_type) 283 { 284 return "org.open_power.HwDiags.Error.Checkstop"; 285 } 286 else if (AnalysisType::TERMINATE_IMMEDIATE == i_type) 287 { 288 return "org.open_power.HwDiags.Error.Predictive"; 289 } 290 291 return "org.open_power.HwDiags.Error.Informational"; // default 292 } 293 294 //------------------------------------------------------------------------------ 295 296 std::string __getMessageSeverity(AnalysisType i_type) 297 { 298 // Default severity is informational (no service action required). 299 LogSvr::Entry::Level severity = LogSvr::Entry::Level::Informational; 300 301 if (AnalysisType::SYSTEM_CHECKSTOP == i_type) 302 { 303 // System checkstops are always unrecoverable errors (service action 304 // required). 305 severity = LogSvr::Entry::Level::Error; 306 } 307 else if (AnalysisType::TERMINATE_IMMEDIATE == i_type) 308 { 309 // TIs will be reported as a predicive error (service action required). 310 severity = LogSvr::Entry::Level::Warning; 311 } 312 313 // Convert the message severity to a string. 314 return LogSvr::Entry::convertLevelToString(severity); 315 } 316 317 //------------------------------------------------------------------------------ 318 319 uint32_t commitPel(const ServiceData& i_servData) 320 { 321 uint32_t o_plid = 0; // default, zero indicates PEL was not created 322 323 // The message registry will require additional log data to fill in keywords 324 // and additional log data. 325 std::map<std::string, std::string> logData; 326 327 // Keep track of the temporary files associated with the user data FFDC. 328 // WARNING: Once the objects stored in this vector go out of scope, the 329 // temporary files will be deleted. So they must remain in scope 330 // until the PEL is submitted. 331 std::vector<util::FFDCFile> userDataFiles; 332 333 // Set the subsystem in the primary SRC. 334 i_servData.addSrcSubsystem(logData); 335 336 // Set words 6-9 of the SRC. 337 __setSrc(i_servData.getRootCause(), logData); 338 339 // Add the list of callouts to the PEL. 340 __addCalloutList(i_servData, userDataFiles); 341 342 // Add the Hostboot scratch register to the PEL. 343 __captureHostbootScratchRegisters(userDataFiles); 344 345 // Add the callout FFDC to the PEL. 346 __addCalloutFFDC(i_servData, userDataFiles); 347 348 // Capture the complete signature list. 349 __captureSignatureList(i_servData.getIsolationData(), userDataFiles); 350 351 // Capture the complete signature list. 352 __captureRegisterDump(i_servData.getIsolationData(), userDataFiles); 353 354 // Now, that all of the user data files have been created, transform the 355 // data into the proper format for the PEL. 356 std::vector<util::FFDCTuple> userData; 357 util::transformFFDC(userDataFiles, userData); 358 359 // Get the message registry entry for this failure. 360 auto message = __getMessageRegistry(i_servData.getAnalysisType()); 361 362 // Get the message severity for this failure. 363 auto severity = __getMessageSeverity(i_servData.getAnalysisType()); 364 365 // Create the PEL 366 o_plid = util::dbus::createPel(message, severity, logData, userData); 367 368 if (0 == o_plid) 369 { 370 trace::err("Error while creating event log entry"); 371 } 372 373 // Return the platorm log ID of the error. 374 return o_plid; 375 } 376 377 } // namespace analyzer 378