1 /** 2 * Copyright © 2021 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 extern "C" 18 { 19 #include <libpdbg.h> 20 } 21 22 #include "fapi_data_process.hpp" 23 #include "pel.hpp" 24 #include "sbe_ffdc_handler.hpp" 25 #include "temporary_file.hpp" 26 27 #include <ekb/hwpf/fapi2/include/return_code_defs.H> 28 #include <ekb/hwpf/fapi2/include/target_types.H> 29 #include <libekb.H> 30 31 #include <phosphor-logging/lg2.hpp> 32 33 #include <format> 34 #include <new> 35 36 namespace openpower 37 { 38 namespace pels 39 { 40 namespace sbe 41 { 42 43 constexpr uint32_t sbeMaxFfdcPackets = 20; 44 constexpr uint16_t p10FfdcMagicCode = 0xFFDC; 45 constexpr uint16_t pozFfdcMagicCode = 0xFBAD; 46 constexpr uint16_t p10FfdcSkipWords = 2; 47 constexpr uint16_t pozFfdcSkipWords = 3; 48 49 struct p10FfdcHeader 50 { 51 uint32_t magic_bytes:16; 52 uint32_t lengthinWords:16; 53 uint32_t seqId:16; 54 uint32_t cmdClass:8; 55 uint32_t cmd:8; 56 uint32_t fapiRc; 57 } __attribute__((packed)); 58 59 struct pozFfdcHeader 60 { 61 uint32_t magicByte:16; 62 uint32_t lengthinWords:16; 63 uint32_t seqId:16; 64 uint32_t cmdClass:8; 65 uint32_t cmd:8; 66 uint32_t slid:16; 67 uint32_t severity:8; 68 uint32_t chipId:8; 69 uint32_t fapiRc; 70 } __attribute__((packed)); 71 72 using namespace phosphor::logging; 73 74 SbeFFDC::SbeFFDC(const AdditionalData& aData, const PelFFDC& files) : 75 ffdcType(FFDC_TYPE_NONE), chipType(fapi2::TARGET_TYPE_PROC_CHIP) 76 { 77 lg2::info("SBE FFDC processing requested"); 78 79 // SRC6 field in the additional data contains Processor position 80 // associated to the SBE FFDC 81 //"[0:15] chip position" 82 auto src6 = aData.getValue("SRC6"); 83 if (src6 == std::nullopt) 84 { 85 lg2::error("Fail to extract SRC6 data: failing to get proc index"); 86 return; 87 } 88 try 89 { 90 chipPos = (std::stoi(src6.value()) & 0xFFFF0000) >> 16; 91 } 92 catch (const std::exception& err) 93 { 94 lg2::error("Conversion failure errormsg({ERR})", "ERR", err); 95 return; 96 } 97 auto type = aData.getValue("CHIP_TYPE"); 98 if (type != std::nullopt) 99 { 100 try 101 { 102 chipType = std::stoi(type.value()); 103 } 104 catch (const std::exception& err) 105 { 106 lg2::error("Conversion failure errormsg({ERR})", "ERR", err); 107 return; 108 } 109 } 110 111 if (files.empty()) 112 { 113 lg2::info("SbeFFDC : No files found, skipping ffdc processing"); 114 return; 115 } 116 117 for (const auto& file : files) 118 { 119 if ((file.format == UserDataFormat::custom) && 120 (file.subType == sbeFFDCSubType)) 121 { 122 // Process SBE file. 123 parse(file.fd); 124 } 125 } 126 } 127 128 void SbeFFDC::parse(int fd) 129 { 130 lg2::info("SBE FFDC file fd:({FD}), parsing started", "FD", fd); 131 132 uint32_t ffdcBufOffset = 0; 133 uint32_t pktCount = 0; 134 135 // get SBE FFDC data. 136 auto ffdcData = util::readFD(fd); 137 if (ffdcData.empty()) 138 { 139 lg2::error("Empty SBE FFDC file fd:({FD}), skipping", "FD", fd); 140 return; 141 } 142 143 while ((ffdcBufOffset < ffdcData.size()) && (sbeMaxFfdcPackets != pktCount)) 144 { 145 sbeFfdcPacketType ffdcPkt; 146 // Next un-extracted FFDC Packet 147 uint16_t magicBytes = 148 *(reinterpret_cast<uint16_t*>(ffdcData.data() + ffdcBufOffset)); 149 magicBytes = ntohs(magicBytes); 150 uint32_t pktLenWords = 0; 151 uint16_t lenWords = 0; 152 if (magicBytes == p10FfdcMagicCode) 153 { 154 p10FfdcHeader* ffdc = reinterpret_cast<p10FfdcHeader*>( 155 ffdcData.data() + ffdcBufOffset); 156 lenWords = ntohs(ffdc->lengthinWords); 157 auto fapiRc = ntohl(ffdc->fapiRc); 158 159 lg2::info( 160 "P10 FFDC magic: {MAGIC_BYTES} length in words:{LEN_WORDS} " 161 "Fapirc:{FAPI_RC}", 162 "MAGIC_BYTES", magicBytes, "LEN_WORDS", lenWords, "FAPI_RC", 163 fapiRc); 164 165 ffdcPkt.fapiRc = fapiRc; 166 // Not interested in the first 2 words (these are not ffdc) 167 pktLenWords = lenWords - p10FfdcSkipWords; 168 ffdcPkt.ffdcLengthInWords = pktLenWords; 169 if (pktLenWords) 170 { 171 ffdcPkt.ffdcData = new uint32_t[pktLenWords]; 172 memcpy(ffdcPkt.ffdcData, 173 ((reinterpret_cast<uint32_t*>(ffdc)) + p10FfdcSkipWords), 174 (pktLenWords * sizeof(uint32_t))); 175 } 176 else 177 { 178 lg2::error("FFDC packet size is zero skipping"); 179 return; 180 } 181 pktCount++; 182 } 183 else if (magicBytes == pozFfdcMagicCode) 184 { 185 pozFfdcHeader* ffdc = reinterpret_cast<pozFfdcHeader*>( 186 ffdcData.data() + ffdcBufOffset); 187 lenWords = ntohs(ffdc->lengthinWords); 188 auto fapiRc = ntohl(ffdc->fapiRc); 189 190 lg2::info( 191 "P0Z FFDC magic: {MAGIC_BYTES} length in words:{LEN_WORDS} " 192 "Fapirc:{FAPI_RC}", 193 "MAGIC_BYTES", magicBytes, "LEN_WORDS", lenWords, "FAPI_RC", 194 fapiRc); 195 196 ffdcPkt.fapiRc = fapiRc; 197 // Not interested in the first 3 words (these are not ffdc) 198 pktLenWords = lenWords - pozFfdcSkipWords; 199 ffdcPkt.ffdcLengthInWords = pktLenWords; 200 if (pktLenWords) 201 { 202 ffdcPkt.ffdcData = new uint32_t[pktLenWords]; 203 memcpy(ffdcPkt.ffdcData, 204 ((reinterpret_cast<uint32_t*>(ffdc)) + pozFfdcSkipWords), 205 (pktLenWords * sizeof(uint32_t))); 206 } 207 else 208 { 209 lg2::error("FFDC packet size is zero skipping"); 210 return; 211 } 212 } 213 else 214 { 215 lg2::error("Invalid FFDC magic code in Header: Skipping "); 216 return; 217 } 218 219 // SBE FFDC processing is not required for SBE Plat errors RCs. 220 // Plat errors processing is driven by SBE provided user data 221 // plugins, which need to link with PEL tool infrastructure. 222 if (ffdcPkt.fapiRc != fapi2::FAPI2_RC_PLAT_ERR_SEE_DATA) 223 { 224 process(ffdcPkt); 225 } 226 else 227 { 228 lg2::info("SBE FFDC: Internal FFDC packet"); 229 } 230 231 // Update Buffer offset in Bytes 232 ffdcBufOffset += lenWords * sizeof(uint32_t); 233 } 234 if (pktCount == sbeMaxFfdcPackets) 235 { 236 lg2::error("Received more than the limit of ({SBEMAXFFDCPACKETS}) FFDC " 237 "packets, processing only ({PKTCOUNT})", 238 "SBEMAXFFDCPACKETS", sbeMaxFfdcPackets, "PKTCOUNT", 239 pktCount); 240 } 241 } 242 243 void SbeFFDC::process(const sbeFfdcPacketType& ffdcPkt) 244 { 245 using json = nlohmann::json; 246 247 // formated FFDC data structure after FFDC packet processing 248 FFDC ffdc; 249 250 if (!pdbg_targets_init(NULL)) 251 { 252 lg2::error("pdbg_targets_init failed, skipping ffdc processing"); 253 return; 254 } 255 256 if (libekb_init()) 257 { 258 lg2::error("libekb_init failed, skipping ffdc processing"); 259 return; 260 } 261 262 try 263 { 264 // libekb provided wrapper function to convert SBE FFDC 265 // in to known ffdc structure. 266 libekb_get_sbe_ffdc(ffdc, ffdcPkt, chipPos, chipType); 267 } 268 catch (...) 269 { 270 lg2::error("libekb_get_sbe_ffdc failed, skipping ffdc processing"); 271 return; 272 } 273 274 // update FFDC type class membeir for hwp specific packet 275 // Assumption SBE FFDC contains only one hwp FFDC packet. 276 ffdcType = ffdc.ffdc_type; 277 278 // To store callouts details in json format as per pel expectation. 279 json pelJSONFmtCalloutDataList; 280 pelJSONFmtCalloutDataList = json::array(); 281 282 // To store other user data from FFDC. 283 openpower::pels::phal::FFDCData ffdcUserData; 284 285 // Get FFDC and required info to include in PEL 286 openpower::pels::phal::convertFAPItoPELformat( 287 ffdc, pelJSONFmtCalloutDataList, ffdcUserData); 288 289 // Get callout information and sore in to file. 290 auto calloutData = pelJSONFmtCalloutDataList.dump(); 291 util::TemporaryFile ffdcFile(calloutData.c_str(), calloutData.size()); 292 293 // Create json callout type pel FFDC file structre. 294 PelFFDCfile pf; 295 pf.format = openpower::pels::UserDataFormat::json; 296 pf.subType = openpower::pels::jsonCalloutSubtype; 297 pf.version = 0x01; 298 pf.fd = ffdcFile.getFd(); 299 ffdcFiles.push_back(pf); 300 301 // save the file path to delete the file after usage. 302 paths.emplace_back(ffdcFile.getPath(), pf.fd); 303 304 // Format ffdc user data and create new file. 305 std::string data; 306 for (auto& d : ffdcUserData) 307 { 308 data += d.first + " = " + d.second + "\n"; 309 } 310 util::TemporaryFile pelDataFile(data.c_str(), data.size()); 311 PelFFDCfile pdf; 312 pdf.format = openpower::pels::UserDataFormat::text; 313 pdf.version = 0x01; 314 pdf.fd = pelDataFile.getFd(); 315 pdf.subType = 0; 316 ffdcFiles.push_back(pdf); 317 318 paths.emplace_back(pelDataFile.getPath(), pdf.fd); 319 } 320 321 std::optional<LogSeverity> SbeFFDC::getSeverity() 322 { 323 if (ffdcType == FFDC_TYPE_SPARE_CLOCK_INFO) 324 { 325 lg2::info( 326 "Found spare clock error, changing severity to informational"); 327 return LogSeverity::Informational; 328 } 329 return std::nullopt; 330 } 331 332 } // namespace sbe 333 } // namespace pels 334 } // namespace openpower 335