xref: /openbmc/phosphor-logging/extensions/openpower-pels/sbe_ffdc_handler.cpp (revision 8a09b982ddeb0c1e13190d9cd196e06a778d6140)
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 
SbeFFDC(const AdditionalData & aData,const PelFFDC & files)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 
parse(int fd)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 
process(const sbeFfdcPacketType & ffdcPkt)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 
getSeverity()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