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         ffdcBufOffset += ffdc->lengthinWords;
147         ++pktCount;
148     }
149     if (pktCount == sbeMaxFfdcPackets)
150     {
151         log<level::ERR>(fmt::format("Received more than the limit of ({})"
152                                     " FFDC packets, processing only ({})",
153                                     sbeMaxFfdcPackets, pktCount)
154                             .c_str());
155     }
156 }
157 
158 void SbeFFDC::process(const sbeFfdcPacketType& ffdcPkt)
159 {
160     using json = nlohmann::json;
161 
162     // formated FFDC data structure after FFDC packet processing
163     FFDC ffdc;
164 
165     if (!pdbg_targets_init(NULL))
166     {
167         log<level::ERR>("pdbg_targets_init failed, skipping ffdc processing");
168         return;
169     }
170 
171     if (libekb_init())
172     {
173         log<level::ERR>("libekb_init failed, skipping ffdc processing");
174         return;
175     }
176 
177     try
178     {
179         // libekb provided wrapper function to convert SBE FFDC
180         // in to known ffdc structure.
181         libekb_get_sbe_ffdc(ffdc, ffdcPkt, procPos);
182     }
183     catch (...)
184     {
185         log<level::ERR>("libekb_get_sbe_ffdc failed, skipping ffdc processing");
186         return;
187     }
188 
189     // To store callouts details in json format as per pel expectation.
190     json pelJSONFmtCalloutDataList;
191     pelJSONFmtCalloutDataList = json::array();
192 
193     // To store other user data from FFDC.
194     openpower::pels::phal::FFDCData ffdcUserData;
195 
196     // Get FFDC and required info to include in PEL
197     openpower::pels::phal::convertFAPItoPELformat(
198         ffdc, pelJSONFmtCalloutDataList, ffdcUserData);
199 
200     // Get callout information and sore in to file.
201     auto calloutData = pelJSONFmtCalloutDataList.dump();
202     util::TemporaryFile ffdcFile(calloutData.c_str(), calloutData.size());
203 
204     // Create json callout type pel FFDC file structre.
205     PelFFDCfile pf;
206     pf.format = openpower::pels::UserDataFormat::json;
207     pf.subType = openpower::pels::jsonCalloutSubtype;
208     pf.version = 0x01;
209     pf.fd = ffdcFile.getFd();
210     ffdcFiles.push_back(pf);
211 
212     // save the file path to delete the file after usage.
213     paths.push_back(ffdcFile.getPath());
214 
215     // Format ffdc user data and create new file.
216     std::string data;
217     for (auto& d : ffdcUserData)
218     {
219         data += d.first + " = " + d.second + "\n";
220     }
221     util::TemporaryFile pelDataFile(data.c_str(), data.size());
222     PelFFDCfile pdf;
223     pdf.format = openpower::pels::UserDataFormat::text;
224     pdf.version = 0x01;
225     pdf.fd = pelDataFile.getFd();
226     ffdcFiles.push_back(pdf);
227 
228     paths.push_back(pelDataFile.getPath());
229 }
230 
231 } // namespace sbe
232 } // namespace pels
233 } // namespace openpower
234