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