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