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 #include <libpdbg.h> 19 } 20 21 #include "fapi_data_process.hpp" 22 #include "pel.hpp" 23 #include "sbe_ffdc_handler.hpp" 24 #include "temporary_file.hpp" 25 26 #include <ekb/hwpf/fapi2/include/return_code_defs.H> 27 #include <fmt/format.h> 28 #include <libekb.H> 29 30 #include <new> 31 #include <phosphor-logging/log.hpp> 32 33 namespace openpower 34 { 35 namespace pels 36 { 37 namespace sbe 38 { 39 40 using namespace phosphor::logging; 41 42 SbeFFDC::SbeFFDC(const AdditionalData& aData, const PelFFDC& files) : 43 ffdcType(FFDC_TYPE_NONE) 44 { 45 log<level::INFO>("SBE FFDC processing requested"); 46 47 // SRC6 field in the additional data contains Processor position 48 // associated to the SBE FFDC 49 //"[0:15] chip position" 50 auto src6 = aData.getValue("SRC6"); 51 if (src6 == std::nullopt) 52 { 53 log<level::ERR>("Fail to extract SRC6 data: failing to get proc index"); 54 return; 55 } 56 try 57 { 58 procPos = (std::stoi(src6.value()) & 0xFFFF0000) >> 16; 59 } 60 catch (const std::exception& err) 61 { 62 log<level::ERR>( 63 fmt::format("Conversion failure errormsg({})", err.what()).c_str()); 64 return; 65 } 66 67 if (files.empty()) 68 { 69 log<level::INFO>("SbeFFDC : No files found, skipping ffdc processing"); 70 return; 71 } 72 73 for (const auto& file : files) 74 { 75 if ((file.format == UserDataFormat::custom) && 76 (file.subType == sbeFFDCSubType)) 77 { 78 // Process SBE file. 79 parse(file.fd); 80 } 81 } 82 } 83 84 void SbeFFDC::parse(int fd) 85 { 86 log<level::INFO>( 87 fmt::format("SBE FFDC file fd:({}), parsing started", fd).c_str()); 88 89 uint32_t ffdcBufOffset = 0; 90 uint32_t pktCount = 0; 91 sbeFfdcPacketType ffdcPkt; 92 93 // get SBE FFDC data. 94 auto ffdcData = util::readFD(fd); 95 if (ffdcData.empty()) 96 { 97 log<level::ERR>( 98 fmt::format("Empty SBE FFDC file fd:({}), skipping", fd).c_str()); 99 return; 100 } 101 102 while ((ffdcBufOffset < ffdcData.size()) && (sbeMaxFfdcPackets != pktCount)) 103 { 104 // Next un-extracted FFDC Packet 105 fapiFfdcBufType* ffdc = 106 reinterpret_cast<fapiFfdcBufType*>(ffdcData.data() + ffdcBufOffset); 107 auto magicBytes = ntohs(ffdc->magic_bytes); 108 auto lenWords = ntohs(ffdc->lengthinWords); 109 auto fapiRc = ntohl(ffdc->fapiRc); 110 111 auto msg = fmt::format("FFDC magic: {} length in words:{} Fapirc:{}", 112 magicBytes, lenWords, fapiRc); 113 log<level::INFO>(msg.c_str()); 114 115 if (magicBytes != ffdcMagicCode) 116 { 117 log<level::ERR>("Invalid FFDC magic code in Header: Skipping "); 118 return; 119 } 120 ffdcPkt.fapiRc = fapiRc; 121 // Not interested in the first 2 words (these are not ffdc) 122 auto pktLenWords = lenWords - (2 * ffdcPkgOneWord); 123 ffdcPkt.ffdcLengthInWords = pktLenWords; 124 if (pktLenWords) 125 { 126 // Memory freeing will be taking care by ffdcPkt structure 127 // destructor 128 ffdcPkt.ffdcData = new uint32_t[pktLenWords]; 129 memcpy(ffdcPkt.ffdcData, 130 ((reinterpret_cast<uint32_t*>(ffdc)) + 131 (2 * ffdcPkgOneWord)), // skip first 2 words 132 (pktLenWords * sizeof(uint32_t))); 133 } 134 else 135 { 136 log<level::ERR>("FFDC packet size is zero skipping"); 137 return; 138 } 139 140 // SBE FFDC processing is not required for SBE Plat errors RCs. 141 // Plat errors processing is driven by SBE provided user data 142 // plugins, which need to link with PEL tool infrastructure. 143 if (ffdcPkt.fapiRc != fapi2::FAPI2_RC_PLAT_ERR_SEE_DATA) 144 { 145 process(ffdcPkt); 146 } 147 else 148 { 149 log<level::INFO>("SBE FFDC: Internal FFDC packet"); 150 } 151 152 // Update Buffer offset in Bytes 153 ffdcBufOffset += lenWords * sizeof(uint32_t); 154 ++pktCount; 155 } 156 if (pktCount == sbeMaxFfdcPackets) 157 { 158 log<level::ERR>(fmt::format("Received more than the limit of ({})" 159 " FFDC packets, processing only ({})", 160 sbeMaxFfdcPackets, pktCount) 161 .c_str()); 162 } 163 } 164 165 void SbeFFDC::process(const sbeFfdcPacketType& ffdcPkt) 166 { 167 using json = nlohmann::json; 168 169 // formated FFDC data structure after FFDC packet processing 170 FFDC ffdc; 171 172 if (!pdbg_targets_init(NULL)) 173 { 174 log<level::ERR>("pdbg_targets_init failed, skipping ffdc processing"); 175 return; 176 } 177 178 if (libekb_init()) 179 { 180 log<level::ERR>("libekb_init failed, skipping ffdc processing"); 181 return; 182 } 183 184 try 185 { 186 // libekb provided wrapper function to convert SBE FFDC 187 // in to known ffdc structure. 188 libekb_get_sbe_ffdc(ffdc, ffdcPkt, procPos); 189 } 190 catch (...) 191 { 192 log<level::ERR>("libekb_get_sbe_ffdc failed, skipping ffdc processing"); 193 return; 194 } 195 196 // update FFDC type class membeir for hwp specific packet 197 // Assumption SBE FFDC contains only one hwp FFDC packet. 198 ffdcType = ffdc.ffdc_type; 199 200 // To store callouts details in json format as per pel expectation. 201 json pelJSONFmtCalloutDataList; 202 pelJSONFmtCalloutDataList = json::array(); 203 204 // To store other user data from FFDC. 205 openpower::pels::phal::FFDCData ffdcUserData; 206 207 // Get FFDC and required info to include in PEL 208 openpower::pels::phal::convertFAPItoPELformat( 209 ffdc, pelJSONFmtCalloutDataList, ffdcUserData); 210 211 // Get callout information and sore in to file. 212 auto calloutData = pelJSONFmtCalloutDataList.dump(); 213 util::TemporaryFile ffdcFile(calloutData.c_str(), calloutData.size()); 214 215 // Create json callout type pel FFDC file structre. 216 PelFFDCfile pf; 217 pf.format = openpower::pels::UserDataFormat::json; 218 pf.subType = openpower::pels::jsonCalloutSubtype; 219 pf.version = 0x01; 220 pf.fd = ffdcFile.getFd(); 221 ffdcFiles.push_back(pf); 222 223 // save the file path to delete the file after usage. 224 paths.push_back(ffdcFile.getPath()); 225 226 // Format ffdc user data and create new file. 227 std::string data; 228 for (auto& d : ffdcUserData) 229 { 230 data += d.first + " = " + d.second + "\n"; 231 } 232 util::TemporaryFile pelDataFile(data.c_str(), data.size()); 233 PelFFDCfile pdf; 234 pdf.format = openpower::pels::UserDataFormat::text; 235 pdf.version = 0x01; 236 pdf.fd = pelDataFile.getFd(); 237 ffdcFiles.push_back(pdf); 238 239 paths.push_back(pelDataFile.getPath()); 240 } 241 242 std::optional<LogSeverity> SbeFFDC::getSeverity() 243 { 244 if (ffdcType == FFDC_TYPE_SPARE_CLOCK_INFO) 245 { 246 log<level::INFO>( 247 "Found spare clock error, changing severity to informational"); 248 return LogSeverity::Informational; 249 } 250 return std::nullopt; 251 } 252 253 } // namespace sbe 254 } // namespace pels 255 } // namespace openpower 256