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
SbeFFDC(const AdditionalData & aData,const PelFFDC & files)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
parse(int fd)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
process(const sbeFfdcPacketType & ffdcPkt)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 try
251 {
252 // libekb provided wrapper function to convert SBE FFDC
253 // in to known ffdc structure.
254 libekb_get_sbe_ffdc(ffdc, ffdcPkt, chipPos, chipType);
255 }
256 catch (...)
257 {
258 lg2::error("libekb_get_sbe_ffdc failed, skipping ffdc processing");
259 return;
260 }
261
262 // update FFDC type class membeir for hwp specific packet
263 // Assumption SBE FFDC contains only one hwp FFDC packet.
264 ffdcType = ffdc.ffdc_type;
265
266 // To store callouts details in json format as per pel expectation.
267 json pelJSONFmtCalloutDataList;
268 pelJSONFmtCalloutDataList = json::array();
269
270 // To store other user data from FFDC.
271 openpower::pels::phal::FFDCData ffdcUserData;
272
273 // Get FFDC and required info to include in PEL
274 openpower::pels::phal::convertFAPItoPELformat(
275 ffdc, pelJSONFmtCalloutDataList, ffdcUserData);
276
277 // Get callout information and sore in to file.
278 auto calloutData = pelJSONFmtCalloutDataList.dump();
279 util::TemporaryFile ffdcFile(calloutData.c_str(), calloutData.size());
280
281 // Create json callout type pel FFDC file structre.
282 PelFFDCfile pf;
283 pf.format = openpower::pels::UserDataFormat::json;
284 pf.subType = openpower::pels::jsonCalloutSubtype;
285 pf.version = 0x01;
286 pf.fd = ffdcFile.getFd();
287 ffdcFiles.push_back(pf);
288
289 // save the file path to delete the file after usage.
290 paths.emplace_back(ffdcFile.getPath(), pf.fd);
291
292 // Format ffdc user data and create new file.
293 std::string data;
294 for (auto& d : ffdcUserData)
295 {
296 data += d.first + " = " + d.second + "\n";
297 }
298 util::TemporaryFile pelDataFile(data.c_str(), data.size());
299 PelFFDCfile pdf;
300 pdf.format = openpower::pels::UserDataFormat::text;
301 pdf.version = 0x01;
302 pdf.fd = pelDataFile.getFd();
303 pdf.subType = 0;
304 ffdcFiles.push_back(pdf);
305
306 paths.emplace_back(pelDataFile.getPath(), pdf.fd);
307 }
308
getSeverity()309 std::optional<LogSeverity> SbeFFDC::getSeverity()
310 {
311 if (ffdcType == FFDC_TYPE_SPARE_CLOCK_INFO)
312 {
313 lg2::info(
314 "Found spare clock error, changing severity to informational");
315 return LogSeverity::Informational;
316 }
317 return std::nullopt;
318 }
319
320 } // namespace sbe
321 } // namespace pels
322 } // namespace openpower
323