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