1 #include <unistd.h>
2 
3 #include <analyzer/service_data.hpp>
4 #include <analyzer/util.hpp>
5 #include <hei_main.hpp>
6 #include <phosphor-logging/elog.hpp>
7 #include <sdbusplus/bus.hpp>
8 #include <util/bin_stream.hpp>
9 #include <util/dbus.hpp>
10 #include <util/ffdc_file.hpp>
11 #include <util/pdbg.hpp>
12 #include <util/trace.hpp>
13 #include <xyz/openbmc_project/Logging/Create/server.hpp>
14 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
15 
16 #include <fstream>
17 #include <memory>
18 
19 namespace LogSvr = sdbusplus::xyz::openbmc_project::Logging::server;
20 
21 namespace analyzer
22 {
23 
24 //------------------------------------------------------------------------------
25 
26 enum FfdcSubType_t : uint8_t
27 {
28     FFDC_SIGNATURES      = 0x01,
29     FFDC_REGISTER_DUMP   = 0x02,
30     FFDC_CALLOUT_FFDC    = 0x03,
31     FFDC_HB_SCRATCH_REGS = 0x04,
32 
33     // For the callout section, the value of '0xCA' is required per the
34     // phosphor-logging openpower-pel extention spec.
35     FFDC_CALLOUTS = 0xCA,
36 };
37 
38 enum FfdcVersion_t : uint8_t
39 {
40     FFDC_VERSION1 = 0x01,
41 };
42 
43 //------------------------------------------------------------------------------
44 
45 void __getSrc(const libhei::Signature& i_signature, uint32_t& o_word6,
46               uint32_t& o_word7, uint32_t& o_word8)
47 {
48     o_word6 = o_word7 = o_word8 = 0; // default
49 
50     // Note that the chip could be null if there was no root cause attention
51     // found during analysis.
52     if (nullptr != i_signature.getChip().getChip())
53     {
54         // [ 0:15] chip model
55         // [16:23] reserved space in chip ID
56         // [24:31] chip EC level
57         o_word6 = i_signature.getChip().getType();
58 
59         // [ 0:15] chip position
60         // [16:23] node position
61         // [24:31] signature attention type
62         auto chipPos    = util::pdbg::getChipPos(i_signature.getChip());
63         uint8_t nodePos = 0; // TODO: multi-node support
64         auto attn       = i_signature.getAttnType();
65 
66         o_word7 =
67             (chipPos & 0xffff) << 16 | (nodePos & 0xff) << 8 | (attn & 0xff);
68 
69         // [ 0:15] signature ID
70         // [16:23] signature instance
71         // [24:31] signature bit position
72         o_word8 = i_signature.toUint32();
73 
74         // Word 9 is currently unused
75     }
76 }
77 
78 //------------------------------------------------------------------------------
79 
80 void __setSrc(const libhei::Signature& i_rootCause,
81               std::map<std::string, std::string>& io_logData)
82 {
83     uint32_t word6 = 0, word7 = 0, word8 = 0;
84     __getSrc(i_rootCause, word6, word7, word8);
85 
86     io_logData["SRC6"] = std::to_string(word6);
87     io_logData["SRC7"] = std::to_string(word7);
88     io_logData["SRC8"] = std::to_string(word8);
89 }
90 
91 //------------------------------------------------------------------------------
92 
93 void __addCalloutList(const ServiceData& i_servData,
94                       std::vector<util::FFDCFile>& io_userDataFiles)
95 {
96     // Create a new entry for the user data section containing the callout list.
97     io_userDataFiles.emplace_back(util::FFDCFormat::JSON, FFDC_CALLOUTS,
98                                   FFDC_VERSION1);
99 
100     // Use a file stream to write the JSON to file.
101     std::ofstream o{io_userDataFiles.back().getPath()};
102     o << i_servData.getCalloutList();
103 }
104 
105 //------------------------------------------------------------------------------
106 
107 void __addCalloutFFDC(const ServiceData& i_servData,
108                       std::vector<util::FFDCFile>& io_userDataFiles)
109 {
110     // Create a new entry for the user data section containing the FFDC.
111     io_userDataFiles.emplace_back(util::FFDCFormat::Custom, FFDC_CALLOUT_FFDC,
112                                   FFDC_VERSION1);
113 
114     // Use a file stream to write the JSON to file.
115     std::ofstream o{io_userDataFiles.back().getPath()};
116     o << i_servData.getCalloutFFDC();
117 }
118 
119 //------------------------------------------------------------------------------
120 
121 void __captureSignatureList(const libhei::IsolationData& i_isoData,
122                             std::vector<util::FFDCFile>& io_userDataFiles)
123 {
124     // Create a new entry for this user data section regardless if there are any
125     // signatures in the list.
126     io_userDataFiles.emplace_back(util::FFDCFormat::Custom, FFDC_SIGNATURES,
127                                   FFDC_VERSION1);
128 
129     // Create a streamer for easy writing to the FFDC file.
130     auto path = io_userDataFiles.back().getPath();
131     util::BinFileWriter stream{path};
132 
133     // The first 4 bytes in the FFDC contains the number of signatures in the
134     // list. Then, the list of signatures will follow.
135 
136     auto list = i_isoData.getSignatureList();
137 
138     uint32_t numSigs = list.size();
139     stream << numSigs;
140 
141     for (const auto& sig : list)
142     {
143         // Each signature will use the same format as the SRC (12 bytes each).
144         uint32_t word6 = 0, word7 = 0, word8 = 0;
145         __getSrc(sig, word6, word7, word8);
146         stream << word6 << word7 << word8;
147     }
148 
149     // If the stream failed for any reason, remove the FFDC file.
150     if (!stream.good())
151     {
152         trace::err("Unable to write signature list FFDC file: %s",
153                    path.string().c_str());
154         io_userDataFiles.pop_back();
155     }
156 }
157 
158 //------------------------------------------------------------------------------
159 
160 void __captureRegisterDump(const libhei::IsolationData& i_isoData,
161                            std::vector<util::FFDCFile>& io_userDataFiles)
162 {
163     // Create a new entry for this user data section regardless if there are any
164     // registers in the dump.
165     io_userDataFiles.emplace_back(util::FFDCFormat::Custom, FFDC_REGISTER_DUMP,
166                                   FFDC_VERSION1);
167 
168     // Create a streamer for easy writing to the FFDC file.
169     auto path = io_userDataFiles.back().getPath();
170     util::BinFileWriter stream{path};
171 
172     // The first 4 bytes in the FFDC contains the number of chips with register
173     // data. Then the data for each chip will follow.
174 
175     auto dump = i_isoData.getRegisterDump();
176 
177     uint32_t numChips = dump.size();
178     stream << numChips;
179 
180     for (const auto& entry : dump)
181     {
182         auto chip    = entry.first;
183         auto regList = entry.second;
184 
185         // Each chip will have the following information:
186         //   4 byte chip model/EC
187         //   2 byte chip position
188         //   1 byte node position
189         //   4 byte number of registers
190         // Then the data for each register will follow.
191 
192         uint32_t chipType = chip.getType();
193         uint16_t chipPos  = util::pdbg::getChipPos(chip);
194         uint8_t nodePos   = 0; // TODO: multi-node support
195         uint32_t numRegs  = regList.size();
196         stream << chipType << chipPos << nodePos << numRegs;
197 
198         for (const auto& reg : regList)
199         {
200             // Each register will have the following information:
201             //   3 byte register ID
202             //   1 byte register instance
203             //   1 byte data size
204             //   * byte data buffer (* depends on value of data size)
205 
206             libhei::RegisterId_t regId = reg.regId;   // 3 byte
207             libhei::Instance_t regInst = reg.regInst; // 1 byte
208 
209             auto tmp = libhei::BitString::getMinBytes(reg.data->getBitLen());
210             if (255 < tmp)
211             {
212                 trace::inf("Register data execeeded 255 and was truncated: "
213                            "regId=0x%06x regInst=%u",
214                            regId, regInst);
215                 tmp = 255;
216             }
217             uint8_t dataSize = tmp;
218 
219             stream << regId << regInst << dataSize;
220 
221             stream.write(reg.data->getBufAddr(), dataSize);
222         }
223     }
224 
225     // If the stream failed for any reason, remove the FFDC file.
226     if (!stream.good())
227     {
228         trace::err("Unable to write register dump FFDC file: %s",
229                    path.string().c_str());
230         io_userDataFiles.pop_back();
231     }
232 }
233 
234 //------------------------------------------------------------------------------
235 
236 void __captureHostbootScratchRegisters(
237     std::vector<util::FFDCFile>& io_userDataFiles)
238 {
239     // Get the Hostboot scratch registers from the primary processor.
240 
241     uint32_t cfamAddr  = 0x283C;
242     uint32_t cfamValue = 0;
243 
244     uint64_t scomAddr  = 0x4602F489;
245     uint64_t scomValue = 0;
246 
247     auto priProc = util::pdbg::getPrimaryProcessor();
248     if (nullptr == priProc)
249     {
250         trace::err("Unable to get primary processor");
251     }
252     else
253     {
254         if (0 != util::pdbg::getCfam(priProc, cfamAddr, cfamValue))
255         {
256             cfamValue = 0; // just in case
257         }
258 
259         if (0 != util::pdbg::getScom(priProc, scomAddr, scomValue))
260         {
261             scomValue = 0; // just in case
262         }
263     }
264 
265     // Create a new entry for this user data section.
266     io_userDataFiles.emplace_back(util::FFDCFormat::Custom,
267                                   FFDC_HB_SCRATCH_REGS, FFDC_VERSION1);
268 
269     // Create a streamer for easy writing to the FFDC file.
270     auto path = io_userDataFiles.back().getPath();
271     util::BinFileWriter stream{path};
272 
273     // Add the data (CFAM addr/val, then SCOM addr/val).
274     stream << cfamAddr << cfamValue << scomAddr << scomValue;
275 
276     // If the stream failed for any reason, remove the FFDC file.
277     if (!stream.good())
278     {
279         trace::err("Unable to write register dump FFDC file: %s",
280                    path.string().c_str());
281         io_userDataFiles.pop_back();
282     }
283 }
284 
285 //------------------------------------------------------------------------------
286 
287 std::string __getMessageRegistry(AnalysisType i_type)
288 {
289 
290     if (AnalysisType::SYSTEM_CHECKSTOP == i_type)
291     {
292         return "org.open_power.HwDiags.Error.Checkstop";
293     }
294     else if (AnalysisType::TERMINATE_IMMEDIATE == i_type)
295     {
296         return "org.open_power.HwDiags.Error.Predictive";
297     }
298 
299     return "org.open_power.HwDiags.Error.Informational"; // default
300 }
301 
302 //------------------------------------------------------------------------------
303 
304 std::string __getMessageSeverity(AnalysisType i_type)
305 {
306     // Default severity is informational (no service action required).
307     LogSvr::Entry::Level severity = LogSvr::Entry::Level::Informational;
308 
309     if (AnalysisType::SYSTEM_CHECKSTOP == i_type)
310     {
311         // System checkstops are always unrecoverable errors (service action
312         // required).
313         severity = LogSvr::Entry::Level::Error;
314     }
315     else if (AnalysisType::TERMINATE_IMMEDIATE == i_type)
316     {
317         // TIs will be reported as a predicive error (service action required).
318         severity = LogSvr::Entry::Level::Warning;
319     }
320 
321     // Convert the message severity to a string.
322     return LogSvr::Entry::convertLevelToString(severity);
323 }
324 
325 //------------------------------------------------------------------------------
326 
327 uint32_t commitPel(const ServiceData& i_servData)
328 {
329     uint32_t o_plid = 0; // default, zero indicates PEL was not created
330 
331     // The message registry will require additional log data to fill in keywords
332     // and additional log data.
333     std::map<std::string, std::string> logData;
334 
335     // Keep track of the temporary files associated with the user data FFDC.
336     // WARNING: Once the objects stored in this vector go out of scope, the
337     //          temporary files will be deleted. So they must remain in scope
338     //          until the PEL is submitted.
339     std::vector<util::FFDCFile> userDataFiles;
340 
341     // Set the subsystem in the primary SRC.
342     i_servData.addSrcSubsystem(logData);
343 
344     // Set words 6-9 of the SRC.
345     __setSrc(i_servData.getRootCause(), logData);
346 
347     // Add the list of callouts to the PEL.
348     __addCalloutList(i_servData, userDataFiles);
349 
350     // Add the Hostboot scratch register to the PEL.
351     __captureHostbootScratchRegisters(userDataFiles);
352 
353     // Add the callout FFDC to the PEL.
354     __addCalloutFFDC(i_servData, userDataFiles);
355 
356     // Capture the complete signature list.
357     __captureSignatureList(i_servData.getIsolationData(), userDataFiles);
358 
359     // Capture the complete signature list.
360     __captureRegisterDump(i_servData.getIsolationData(), userDataFiles);
361 
362     // Now, that all of the user data files have been created, transform the
363     // data into the proper format for the PEL.
364     std::vector<util::FFDCTuple> userData;
365     util::transformFFDC(userDataFiles, userData);
366 
367     // Get the message registry entry for this failure.
368     auto message = __getMessageRegistry(i_servData.getAnalysisType());
369 
370     // Get the message severity for this failure.
371     auto severity = __getMessageSeverity(i_servData.getAnalysisType());
372 
373     // Create the PEL
374     o_plid = util::dbus::createPel(message, severity, logData, userData);
375 
376     if (0 == o_plid)
377     {
378         trace::err("Error while creating event log entry");
379     }
380 
381     // Return the platorm log ID of the error.
382     return o_plid;
383 }
384 
385 } // namespace analyzer
386