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