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