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 #include <libpdbg.h>
19 }
20 
21 #include "fapi_data_process.hpp"
22 #include "pel.hpp"
23 #include "sbe_ffdc_handler.hpp"
24 #include "temporary_file.hpp"
25 
26 #include <ekb/hwpf/fapi2/include/return_code_defs.H>
27 #include <fmt/format.h>
28 #include <libekb.H>
29 
30 #include <new>
31 #include <phosphor-logging/log.hpp>
32 
33 namespace openpower
34 {
35 namespace pels
36 {
37 namespace sbe
38 {
39 
40 using namespace phosphor::logging;
41 
42 SbeFFDC::SbeFFDC(const AdditionalData& aData, const PelFFDC& files) :
43     ffdcType(FFDC_TYPE_NONE)
44 {
45     log<level::INFO>("SBE FFDC processing requested");
46 
47     // SRC6 field in the additional data contains Processor position
48     // associated to the SBE FFDC
49     //"[0:15] chip position"
50     auto src6 = aData.getValue("SRC6");
51     if (src6 == std::nullopt)
52     {
53         log<level::ERR>("Fail to extract SRC6 data: failing to get proc index");
54         return;
55     }
56     try
57     {
58         procPos = (std::stoi(src6.value()) & 0xFFFF0000) >> 16;
59     }
60     catch (const std::exception& err)
61     {
62         log<level::ERR>(
63             fmt::format("Conversion failure errormsg({})", err.what()).c_str());
64         return;
65     }
66 
67     if (files.empty())
68     {
69         log<level::INFO>("SbeFFDC : No files found, skipping ffdc processing");
70         return;
71     }
72 
73     for (const auto& file : files)
74     {
75         if ((file.format == UserDataFormat::custom) &&
76             (file.subType == sbeFFDCSubType))
77         {
78             // Process SBE file.
79             parse(file.fd);
80         }
81     }
82 }
83 
84 void SbeFFDC::parse(int fd)
85 {
86     log<level::INFO>(
87         fmt::format("SBE FFDC file fd:({}), parsing started", fd).c_str());
88 
89     uint32_t ffdcBufOffset = 0;
90     uint32_t pktCount = 0;
91     sbeFfdcPacketType ffdcPkt;
92 
93     // get SBE FFDC data.
94     auto ffdcData = util::readFD(fd);
95     if (ffdcData.empty())
96     {
97         log<level::ERR>(
98             fmt::format("Empty SBE FFDC file fd:({}), skipping", fd).c_str());
99         return;
100     }
101 
102     while ((ffdcBufOffset < ffdcData.size()) && (sbeMaxFfdcPackets != pktCount))
103     {
104         // Next un-extracted FFDC Packet
105         fapiFfdcBufType* ffdc =
106             reinterpret_cast<fapiFfdcBufType*>(ffdcData.data() + ffdcBufOffset);
107         auto magicBytes = ntohs(ffdc->magic_bytes);
108         auto lenWords = ntohs(ffdc->lengthinWords);
109         auto fapiRc = ntohl(ffdc->fapiRc);
110 
111         auto msg = fmt::format("FFDC magic: {} length in words:{} Fapirc:{}",
112                                magicBytes, lenWords, fapiRc);
113         log<level::INFO>(msg.c_str());
114 
115         if (magicBytes != ffdcMagicCode)
116         {
117             log<level::ERR>("Invalid FFDC magic code in Header: Skipping ");
118             return;
119         }
120         ffdcPkt.fapiRc = fapiRc;
121         // Not interested in the first 2 words (these are not ffdc)
122         auto pktLenWords = lenWords - (2 * ffdcPkgOneWord);
123         ffdcPkt.ffdcLengthInWords = pktLenWords;
124         if (pktLenWords)
125         {
126             // Memory freeing will be taking care by ffdcPkt structure
127             // destructor
128             ffdcPkt.ffdcData = new uint32_t[pktLenWords];
129             memcpy(ffdcPkt.ffdcData,
130                    ((reinterpret_cast<uint32_t*>(ffdc)) +
131                     (2 * ffdcPkgOneWord)), // skip first 2 words
132                    (pktLenWords * sizeof(uint32_t)));
133         }
134         else
135         {
136             log<level::ERR>("FFDC packet size is zero skipping");
137             return;
138         }
139 
140         // SBE FFDC processing is not required for SBE Plat errors RCs.
141         // Plat errors processing is driven by SBE provided user data
142         // plugins, which need to link with PEL tool infrastructure.
143         if (ffdcPkt.fapiRc != fapi2::FAPI2_RC_PLAT_ERR_SEE_DATA)
144         {
145             process(ffdcPkt);
146         }
147         else
148         {
149             log<level::INFO>("SBE FFDC: Internal FFDC packet");
150         }
151 
152         // Update Buffer offset in Bytes
153         ffdcBufOffset += lenWords * sizeof(uint32_t);
154         ++pktCount;
155     }
156     if (pktCount == sbeMaxFfdcPackets)
157     {
158         log<level::ERR>(fmt::format("Received more than the limit of ({})"
159                                     " FFDC packets, processing only ({})",
160                                     sbeMaxFfdcPackets, pktCount)
161                             .c_str());
162     }
163 }
164 
165 void SbeFFDC::process(const sbeFfdcPacketType& ffdcPkt)
166 {
167     using json = nlohmann::json;
168 
169     // formated FFDC data structure after FFDC packet processing
170     FFDC ffdc;
171 
172     if (!pdbg_targets_init(NULL))
173     {
174         log<level::ERR>("pdbg_targets_init failed, skipping ffdc processing");
175         return;
176     }
177 
178     if (libekb_init())
179     {
180         log<level::ERR>("libekb_init failed, skipping ffdc processing");
181         return;
182     }
183 
184     try
185     {
186         // libekb provided wrapper function to convert SBE FFDC
187         // in to known ffdc structure.
188         libekb_get_sbe_ffdc(ffdc, ffdcPkt, procPos);
189     }
190     catch (...)
191     {
192         log<level::ERR>("libekb_get_sbe_ffdc failed, skipping ffdc processing");
193         return;
194     }
195 
196     // update FFDC type class membeir for hwp specific packet
197     // Assumption SBE FFDC contains only one hwp FFDC packet.
198     ffdcType = ffdc.ffdc_type;
199 
200     // To store callouts details in json format as per pel expectation.
201     json pelJSONFmtCalloutDataList;
202     pelJSONFmtCalloutDataList = json::array();
203 
204     // To store other user data from FFDC.
205     openpower::pels::phal::FFDCData ffdcUserData;
206 
207     // Get FFDC and required info to include in PEL
208     openpower::pels::phal::convertFAPItoPELformat(
209         ffdc, pelJSONFmtCalloutDataList, ffdcUserData);
210 
211     // Get callout information and sore in to file.
212     auto calloutData = pelJSONFmtCalloutDataList.dump();
213     util::TemporaryFile ffdcFile(calloutData.c_str(), calloutData.size());
214 
215     // Create json callout type pel FFDC file structre.
216     PelFFDCfile pf;
217     pf.format = openpower::pels::UserDataFormat::json;
218     pf.subType = openpower::pels::jsonCalloutSubtype;
219     pf.version = 0x01;
220     pf.fd = ffdcFile.getFd();
221     ffdcFiles.push_back(pf);
222 
223     // save the file path to delete the file after usage.
224     paths.push_back(ffdcFile.getPath());
225 
226     // Format ffdc user data and create new file.
227     std::string data;
228     for (auto& d : ffdcUserData)
229     {
230         data += d.first + " = " + d.second + "\n";
231     }
232     util::TemporaryFile pelDataFile(data.c_str(), data.size());
233     PelFFDCfile pdf;
234     pdf.format = openpower::pels::UserDataFormat::text;
235     pdf.version = 0x01;
236     pdf.fd = pelDataFile.getFd();
237     ffdcFiles.push_back(pdf);
238 
239     paths.push_back(pelDataFile.getPath());
240 }
241 
242 std::optional<LogSeverity> SbeFFDC::getSeverity()
243 {
244     if (ffdcType == FFDC_TYPE_SPARE_CLOCK_INFO)
245     {
246         log<level::INFO>(
247             "Found spare clock error, changing severity to informational");
248         return LogSeverity::Informational;
249     }
250     return std::nullopt;
251 }
252 
253 } // namespace sbe
254 } // namespace pels
255 } // namespace openpower
256