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