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