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