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/ffdc_file.hpp> 10 #include <util/pdbg.hpp> 11 #include <util/trace.hpp> 12 #include <xyz/openbmc_project/Logging/Create/server.hpp> 13 #include <xyz/openbmc_project/Logging/Entry/server.hpp> 14 15 #include <fstream> 16 #include <memory> 17 18 namespace LogSvr = sdbusplus::xyz::openbmc_project::Logging::server; 19 20 namespace analyzer 21 { 22 23 //------------------------------------------------------------------------------ 24 25 enum FfdcSubType_t : uint8_t 26 { 27 FFDC_SIGNATURES = 0x01, 28 FFDC_REGISTER_DUMP = 0x02, 29 FFDC_GUARD = 0x03, 30 31 // For the callout section, the value of '0xCA' is required per the 32 // phosphor-logging openpower-pel extention spec. 33 FFDC_CALLOUTS = 0xCA, 34 }; 35 36 enum FfdcVersion_t : uint8_t 37 { 38 FFDC_VERSION1 = 0x01, 39 }; 40 41 //------------------------------------------------------------------------------ 42 43 bool __isCheckstop(const libhei::IsolationData& i_isoData) 44 { 45 // Look for any signature with a system checkstop attention. 46 auto list = i_isoData.getSignatureList(); 47 auto itr = std::find_if(list.begin(), list.end(), [](const auto& s) { 48 return libhei::ATTN_TYPE_CHECKSTOP == s.getAttnType(); 49 }); 50 51 return list.end() != itr; 52 } 53 54 //------------------------------------------------------------------------------ 55 56 void __getSrc(const libhei::Signature& i_signature, uint32_t& o_word6, 57 uint32_t& o_word7, uint32_t& o_word8) 58 { 59 // [ 0:15] chip model 60 // [16:23] reserved space in chip ID 61 // [24:31] chip EC level 62 o_word6 = i_signature.getChip().getType(); 63 64 // [ 0:15] chip position 65 // [16:23] unused 66 // [24:31] signature attention type 67 auto pos = util::pdbg::getChipPos(i_signature.getChip()); 68 auto attn = i_signature.getAttnType(); 69 70 o_word7 = (pos & 0xffff) << 16 | (attn & 0xff); 71 72 // [ 0:15] signature ID 73 // [16:23] signature instance 74 // [24:31] signature bit position 75 o_word8 = i_signature.toUint32(); 76 77 // Word 9 is currently unused 78 } 79 80 //------------------------------------------------------------------------------ 81 82 void __setSrc(const libhei::Signature& i_rootCause, 83 std::map<std::string, std::string>& io_logData) 84 { 85 uint32_t word6 = 0, word7 = 0, word8 = 0; 86 __getSrc(i_rootCause, word6, word7, word8); 87 88 io_logData["SRC6"] = std::to_string(word6); 89 io_logData["SRC7"] = std::to_string(word7); 90 io_logData["SRC8"] = std::to_string(word8); 91 } 92 93 //------------------------------------------------------------------------------ 94 95 void __addCalloutList(const ServiceData& i_servData, 96 std::vector<util::FFDCFile>& io_userDataFiles) 97 { 98 // Get the JSON output for the callout list. 99 nlohmann::json json; 100 i_servData.getCalloutList(json); 101 102 // Create a new entry for the user data section containing the callout list. 103 io_userDataFiles.emplace_back(util::FFDCFormat::JSON, FFDC_CALLOUTS, 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 << json; 109 } 110 111 //------------------------------------------------------------------------------ 112 113 void __addGuardList(const ServiceData& i_servData, 114 std::vector<util::FFDCFile>& io_userDataFiles) 115 { 116 // Get the JSON output for the guard list. 117 nlohmann::json json; 118 i_servData.getGuardList(json); 119 120 // Create a new entry for the user data section containing the guard list. 121 io_userDataFiles.emplace_back(util::FFDCFormat::JSON, FFDC_GUARD, 122 FFDC_VERSION1); 123 124 // Use a file stream to write the JSON to file. 125 std::ofstream o{io_userDataFiles.back().getPath()}; 126 o << json; 127 } 128 129 //------------------------------------------------------------------------------ 130 131 void __captureSignatureList(const libhei::IsolationData& i_isoData, 132 std::vector<util::FFDCFile>& io_userDataFiles) 133 { 134 // Create a new entry for this user data section regardless if there are any 135 // signatures in the list. 136 io_userDataFiles.emplace_back(util::FFDCFormat::Custom, FFDC_SIGNATURES, 137 FFDC_VERSION1); 138 139 // Create a streamer for easy writing to the FFDC file. 140 auto path = io_userDataFiles.back().getPath(); 141 util::BinFileWriter stream{path}; 142 143 // The first 4 bytes in the FFDC contains the number of signatures in the 144 // list. Then, the list of signatures will follow. 145 146 auto list = i_isoData.getSignatureList(); 147 148 uint32_t numSigs = list.size(); 149 stream << numSigs; 150 151 for (const auto& sig : list) 152 { 153 // Each signature will use the same format as the SRC (12 bytes each). 154 uint32_t word6 = 0, word7 = 0, word8 = 0; 155 __getSrc(sig, word6, word7, word8); 156 stream << word6 << word7 << word8; 157 } 158 159 // If the stream failed for any reason, remove the FFDC file. 160 if (!stream.good()) 161 { 162 trace::err("Unable to write signature list FFDC file: %s", 163 path.string().c_str()); 164 io_userDataFiles.pop_back(); 165 } 166 } 167 168 //------------------------------------------------------------------------------ 169 170 void __captureRegisterDump(const libhei::IsolationData& i_isoData, 171 std::vector<util::FFDCFile>& io_userDataFiles) 172 { 173 // Create a new entry for this user data section regardless if there are any 174 // registers in the dump. 175 io_userDataFiles.emplace_back(util::FFDCFormat::Custom, FFDC_REGISTER_DUMP, 176 FFDC_VERSION1); 177 178 // Create a streamer for easy writing to the FFDC file. 179 auto path = io_userDataFiles.back().getPath(); 180 util::BinFileWriter stream{path}; 181 182 // The first 4 bytes in the FFDC contains the number of chips with register 183 // data. Then the data for each chip will follow. 184 185 auto dump = i_isoData.getRegisterDump(); 186 187 uint32_t numChips = dump.size(); 188 stream << numChips; 189 190 for (const auto& entry : dump) 191 { 192 auto chip = entry.first; 193 auto regList = entry.second; 194 195 // Each chip will have the following information: 196 // 4 byte chip model/EC 197 // 2 byte chip position 198 // 4 byte number of registers 199 // Then the data for each register will follow. 200 201 uint32_t chipType = chip.getType(); 202 uint16_t chipPos = util::pdbg::getChipPos(chip); 203 uint32_t numRegs = regList.size(); 204 stream << chipType << chipPos << numRegs; 205 206 for (const auto& reg : regList) 207 { 208 // Each register will have the following information: 209 // 3 byte register ID 210 // 1 byte register instance 211 // 1 byte data size 212 // * byte data buffer (* depends on value of data size) 213 214 libhei::RegisterId_t regId = reg.regId; // 3 byte 215 libhei::Instance_t regInst = reg.regInst; // 1 byte 216 217 auto tmp = libhei::BitString::getMinBytes(reg.data->getBitLen()); 218 if (255 < tmp) 219 { 220 trace::inf("Register data execeeded 255 and was truncated: " 221 "regId=0x%06x regInst=%u", 222 regId, regInst); 223 tmp = 255; 224 } 225 uint8_t dataSize = tmp; 226 227 stream << regId << regInst << dataSize; 228 229 stream.write(reg.data->getBufAddr(), dataSize); 230 } 231 } 232 233 // If the stream failed for any reason, remove the FFDC file. 234 if (!stream.good()) 235 { 236 trace::err("Unable to write register dump FFDC file: %s", 237 path.string().c_str()); 238 io_userDataFiles.pop_back(); 239 } 240 } 241 242 //------------------------------------------------------------------------------ 243 244 std::string __getMessageRegistry(bool i_isCheckstop) 245 { 246 // For now, there are only two choices: 247 return i_isCheckstop ? "org.open_power.HwDiags.Error.Checkstop" 248 : "org.open_power.HwDiags.Error.Predictive"; 249 } 250 251 //------------------------------------------------------------------------------ 252 253 std::string __getMessageSeverity(bool i_isCheckstop) 254 { 255 // We could specify the PEL severity in the message registry entry. However, 256 // that would require multiple copies of each entry for each possible 257 // severity. As a workaround, we will not explicitly state the PEL severity 258 // in the message registry. Instead, the message severity will be converted 259 // into a PEL severity via the openpower-pels extention of phosphor-logging. 260 261 // Initially, we'll use a severity that will generate a predictive PEL. This 262 // is intended for Terminate Immediate (TI) errors and will require service. 263 LogSvr::Entry::Level severity = LogSvr::Entry::Level::Warning; 264 265 // If the reason for analysis was due to a system checsktop, the severity 266 // will be upgraded to a unrecoverable PEL. 267 if (i_isCheckstop) 268 severity = LogSvr::Entry::Level::Error; 269 270 // Convert the message severity to a string. 271 return LogSvr::Entry::convertLevelToString(severity); 272 } 273 274 //------------------------------------------------------------------------------ 275 276 void createPel(const libhei::IsolationData& i_isoData, 277 const ServiceData& i_servData) 278 { 279 // The message registry will require additional log data to fill in keywords 280 // and additional log data. 281 std::map<std::string, std::string> logData; 282 283 // Keep track of the temporary files associated with the user data FFDC. 284 // WARNING: Once the objects stored in this vector go out of scope, the 285 // temporary files will be deleted. So they must remain in scope 286 // until the PEL is submitted. 287 std::vector<util::FFDCFile> userDataFiles; 288 289 // In several cases, it is important to know if the reason for analysis was 290 // due to a system checsktop. 291 bool isCheckstop = __isCheckstop(i_isoData); 292 293 // Set words 6-9 of the SRC. 294 __setSrc(i_servData.getRootCause(), logData); 295 296 // Add the list of callouts to the PEL. 297 __addCalloutList(i_servData, userDataFiles); 298 299 // Add the list of guard requests to the PEL. 300 __addGuardList(i_servData, userDataFiles); 301 302 // Capture the complete signature list. 303 __captureSignatureList(i_isoData, userDataFiles); 304 305 // Capture the complete signature list. 306 __captureRegisterDump(i_isoData, userDataFiles); 307 308 // Now, that all of the user data files have been created, transform the 309 // data into the proper format for the PEL. 310 std::vector<util::FFDCTuple> userData; 311 util::transformFFDC(userDataFiles, userData); 312 313 // Get access to logging interface and method for creating log. 314 auto bus = sdbusplus::bus::new_default_system(); 315 316 // Using direct create method (for additional data). 317 auto method = bus.new_method_call( 318 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", 319 "xyz.openbmc_project.Logging.Create", "CreateWithFFDCFiles"); 320 321 // The "Create" method requires manually adding the process ID. 322 logData["_PID"] = std::to_string(getpid()); 323 324 // Get the message registry entry for this failure. 325 auto message = __getMessageRegistry(isCheckstop); 326 327 // Get the message severity for this failure. 328 auto severity = __getMessageSeverity(isCheckstop); 329 330 // Add the message, with additional log and user data. 331 method.append(message, severity, logData, userData); 332 333 // Log the event. 334 bus.call_noreply(method); 335 } 336 337 } // namespace analyzer 338