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 enum FfdcSubType_t : uint8_t
26 {
27     FFDC_SIGNATURES = 0x01,
28     FFDC_REGISTER_DUMP = 0x02,
29     FFDC_CALLOUT_FFDC = 0x03,
30     FFDC_HB_SCRATCH_REGS = 0x04,
31     FFDC_SCRATCH_SIG = 0x05,
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 
__getSrc(const libhei::Signature & i_signature,uint32_t & o_word6,uint32_t & o_word7,uint32_t & o_word8)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 = (chipPos & 0xffff) << 16 | (nodePos & 0xff) << 8 |
67                   (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 
__setSrc(const libhei::Signature & i_rootCause,std::map<std::string,std::string> & io_logData)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 
__addCalloutList(const ServiceData & i_servData,std::vector<util::FFDCFile> & io_userDataFiles)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 
__addCalloutFFDC(const ServiceData & i_servData,std::vector<util::FFDCFile> & io_userDataFiles)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 
__captureSignatureList(const libhei::IsolationData & i_isoData,std::vector<util::FFDCFile> & io_userDataFiles)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 
__captureRegisterDump(const libhei::IsolationData & i_isoData,std::vector<util::FFDCFile> & io_userDataFiles)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 
__captureHostbootScratchRegisters(std::vector<util::FFDCFile> & io_userDataFiles)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 
__captureScratchRegSignature(std::vector<util::FFDCFile> & io_userDataFiles)287 void __captureScratchRegSignature(std::vector<util::FFDCFile>& io_userDataFiles)
288 {
289     // If analysis was interrupted by a system checkstop, there may exist an
290     // error signature within Hostboot scratch registers 9 (scom: 0x00050180,
291     // fsi: 0x2980) and 10 (scom: 0x00050181, fsi: 0x2981) which indicates the
292     // signature from the interrupted analysis. If data exists within those
293     // registers a user data section will be created in the PEL to record it.
294 
295     uint32_t reg9Addr = 0x2980;
296     uint32_t reg10Addr = 0x2981;
297 
298     uint32_t chipId = 0; // stored in reg9
299     uint32_t sigId = 0;  // stored in reg10
300 
301     auto priProc = util::pdbg::getPrimaryProcessor();
302     if (nullptr == priProc)
303     {
304         trace::err("Unable to get primary processor");
305     }
306     else
307     {
308         if (0 != util::pdbg::getCfam(priProc, reg9Addr, chipId))
309         {
310             chipId = 0; // just in case
311         }
312 
313         if (0 != util::pdbg::getCfam(priProc, reg10Addr, sigId))
314         {
315             sigId = 0; // just in case
316         }
317     }
318 
319     // If any non-zero data was found in the registers, add them to the FFDC.
320     if (0 != chipId || 0 != sigId)
321     {
322         // Create a new entry for this user data section.
323         io_userDataFiles.emplace_back(util::FFDCFormat::Custom,
324                                       FFDC_SCRATCH_SIG, FFDC_VERSION1);
325 
326         // Create a streamer for easy writing to the FFDC file.
327         auto path = io_userDataFiles.back().getPath();
328         util::BinFileWriter stream{path};
329 
330         stream << chipId << sigId;
331 
332         // If the stream failed for any reason, remove the FFDC file.
333         if (!stream.good())
334         {
335             trace::err("Unable to write register dump FFDC file: %s",
336                        path.string().c_str());
337             io_userDataFiles.pop_back();
338         }
339     }
340 }
341 
342 //------------------------------------------------------------------------------
343 
__getMessageRegistry(AnalysisType i_type)344 std::string __getMessageRegistry(AnalysisType i_type)
345 {
346     if (AnalysisType::SYSTEM_CHECKSTOP == i_type)
347     {
348         return "org.open_power.HwDiags.Error.Checkstop";
349     }
350     else if (AnalysisType::TERMINATE_IMMEDIATE == i_type)
351     {
352         return "org.open_power.HwDiags.Error.Predictive";
353     }
354 
355     return "org.open_power.HwDiags.Error.Informational"; // default
356 }
357 
358 //------------------------------------------------------------------------------
359 
__getMessageSeverity(AnalysisType i_type)360 std::string __getMessageSeverity(AnalysisType i_type)
361 {
362     // Default severity is informational (no service action required).
363     LogSvr::Entry::Level severity = LogSvr::Entry::Level::Informational;
364 
365     if (AnalysisType::SYSTEM_CHECKSTOP == i_type)
366     {
367         // System checkstops are always unrecoverable errors (service action
368         // required).
369         severity = LogSvr::Entry::Level::Error;
370     }
371     else if (AnalysisType::TERMINATE_IMMEDIATE == i_type)
372     {
373         // TIs will be reported as a predicive error (service action required).
374         severity = LogSvr::Entry::Level::Warning;
375     }
376 
377     // Convert the message severity to a string.
378     return LogSvr::Entry::convertLevelToString(severity);
379 }
380 
381 //------------------------------------------------------------------------------
382 
commitPel(const ServiceData & i_servData)383 uint32_t commitPel(const ServiceData& i_servData)
384 {
385     uint32_t o_plid = 0; // default, zero indicates PEL was not created
386 
387     // The message registry will require additional log data to fill in keywords
388     // and additional log data.
389     std::map<std::string, std::string> logData;
390 
391     // Keep track of the temporary files associated with the user data FFDC.
392     // WARNING: Once the objects stored in this vector go out of scope, the
393     //          temporary files will be deleted. So they must remain in scope
394     //          until the PEL is submitted.
395     std::vector<util::FFDCFile> userDataFiles;
396 
397     // Set the subsystem in the primary SRC.
398     i_servData.addSrcSubsystem(logData);
399 
400     // Set words 6-9 of the SRC.
401     __setSrc(i_servData.getRootCause(), logData);
402 
403     // Add the list of callouts to the PEL.
404     __addCalloutList(i_servData, userDataFiles);
405 
406     // Add the Hostboot scratch register to the PEL.
407     __captureHostbootScratchRegisters(userDataFiles);
408 
409     // Add the signature stored in the scratch regs if it exists.
410     __captureScratchRegSignature(userDataFiles);
411 
412     // Add the callout FFDC to the PEL.
413     __addCalloutFFDC(i_servData, userDataFiles);
414 
415     // Capture the complete signature list.
416     __captureSignatureList(i_servData.getIsolationData(), userDataFiles);
417 
418     // Capture the complete signature list.
419     __captureRegisterDump(i_servData.getIsolationData(), userDataFiles);
420 
421     // Now, that all of the user data files have been created, transform the
422     // data into the proper format for the PEL.
423     std::vector<util::FFDCTuple> userData;
424     util::transformFFDC(userDataFiles, userData);
425 
426     // Get the message registry entry for this failure.
427     auto message = __getMessageRegistry(i_servData.getAnalysisType());
428 
429     // Get the message severity for this failure.
430     auto severity = __getMessageSeverity(i_servData.getAnalysisType());
431 
432     // Create the PEL
433     o_plid = util::dbus::createPel(message, severity, logData, userData);
434 
435     if (0 == o_plid)
436     {
437         trace::err("Error while creating event log entry");
438     }
439 
440     // Return the platorm log ID of the error.
441     return o_plid;
442 }
443 
444 } // namespace analyzer
445