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