1 #include <unistd.h>
2 
3 #include <analyzer/analyzer_main.hpp>
4 #include <attn/attn_common.hpp>
5 #include <attn/attn_dbus.hpp>
6 #include <attn/attn_dump.hpp>
7 #include <attn/attn_logging.hpp>
8 #include <attn/pel/pel_minimal.hpp>
9 #include <phosphor-logging/log.hpp>
10 #include <util/ffdc.hpp>
11 #include <util/trace.hpp>
12 
13 namespace attn
14 {
15 
16 /** @brief Tuple containing information about ffdc files */
17 using FFDCTuple =
18     std::tuple<util::FFDCFormat, uint8_t, uint8_t, sdbusplus::message::unix_fd>;
19 
20 /**
21  * Create FFDCTuple objects corresponding to the specified FFDC files.
22  *
23  * The D-Bus method to create an error log requires a vector of tuples to
24  * pass in the FFDC file information.
25  *
26  * @param   files - FFDC files
27  * @return  vector of FFDCTuple objects
28  */
29 std::vector<FFDCTuple>
30     createFFDCTuples(const std::vector<util::FFDCFile>& files)
31 {
32     std::vector<FFDCTuple> ffdcTuples{};
33     util::transformFFDC(files, ffdcTuples);
34 
35     return ffdcTuples;
36 }
37 
38 /**
39  * @brief Create an FFDCFile object containing raw data
40  *
41  * Throws an exception if an error occurs.
42  *
43  * @param   i_buffer - raw data to add to ffdc faw data file
44  * @param   i_size - size of the raw data
45  * @return  FFDCFile object
46  */
47 util::FFDCFile createFFDCRawFile(void* i_buffer, size_t i_size)
48 {
49     util::FFDCFile file{util::FFDCFormat::Custom};
50 
51     // Write buffer to file and then reset file description file offset
52     int fd          = file.getFileDescriptor();
53     size_t numBytes = write(fd, static_cast<char*>(i_buffer), i_size);
54     if (i_size != numBytes)
55     {
56         trace::err("%s only %u of %u bytes written", file.getPath().c_str(),
57                    numBytes, i_size);
58     }
59 
60     lseek(fd, 0, SEEK_SET);
61 
62     return file;
63 }
64 
65 /**
66  * Create FFDCFile objects containing debug data to store in the error log.
67  *
68  * If an error occurs, the error is written to the journal but an exception
69  * is not thrown.
70  *
71  * @param   i_buffer - raw data (if creating raw dump ffdc entry in log)
72  * @return  vector of FFDCFile objects
73  */
74 std::vector<util::FFDCFile> createFFDCFiles(char* i_buffer = nullptr,
75                                             size_t i_size  = 0)
76 {
77     std::vector<util::FFDCFile> files{};
78 
79     // Create raw dump file
80     if ((nullptr != i_buffer) && (0 != i_size))
81     {
82         files.emplace_back(createFFDCRawFile(i_buffer, i_size));
83     }
84 
85     // Create trace dump file
86     util::createFFDCTraceFiles(files);
87 
88     return files;
89 }
90 
91 /**
92  * Create a PEL from an existing PEL
93  *
94  * Create a new PEL based on the specified raw PEL and submit the new PEL
95  * to the backend logging code as a raw PEL. Note that  additional data map
96  * here contains data to be committed to the PEL and it can also be used to
97  * create the PEL as it contains needed information.
98  *
99  * @param   i_rawPel - buffer containing a raw PEL
100  * @param   i_additional - additional data to be added to the new PEL
101  */
102 void createPelCustom(std::vector<uint8_t>& i_rawPel,
103                      std::map<std::string, std::string> i_additional)
104 {
105     // create PEL object from buffer
106     auto tiPel = std::make_unique<pel::PelMinimal>(i_rawPel);
107 
108     // The additional data contains the TI info as well as the value for the
109     // subystem that provided the TI info. Get the subystem from additional
110     // data and then populate the prmary SRC and SRC words for the custom PEL
111     // based on the sybsystem's TI info.
112     uint8_t subsystem = std::stoi(i_additional["Subsystem"]);
113     tiPel->setSubsystem(subsystem);
114 
115     // If recoverable attentions are active we will call the analyzer and
116     // then link the custom pel to analyzer pel.
117     std::map<std::string, std::string>::iterator it;
118     it = i_additional.find("recoverables");
119     if (it != i_additional.end() && "true" == it->second)
120     {
121         DumpParameters dumpParameters;
122         auto plid = analyzer::analyzeHardware(
123             analyzer::AnalysisType::TERMINATE_IMMEDIATE, dumpParameters);
124         if (0 != plid)
125         {
126             // Link the PLID if an attention was found and a PEL was generated.
127             tiPel->setPlid(plid);
128         }
129     }
130 
131     if (static_cast<uint8_t>(pel::SubsystemID::hypervisor) == subsystem)
132     {
133         // populate hypervisor SRC words
134         tiPel->setSrcWords(std::array<uint32_t, pel::numSrcWords>{
135             (uint32_t)std::stoul(i_additional["0x10 SRC Word 12"], 0, 16),
136             (uint32_t)std::stoul(i_additional["0x14 SRC Word 13"], 0, 16),
137             (uint32_t)std::stoul(i_additional["0x18 SRC Word 14"], 0, 16),
138             (uint32_t)std::stoul(i_additional["0x1c SRC Word 15"], 0, 16),
139             (uint32_t)std::stoul(i_additional["0x20 SRC Word 16"], 0, 16),
140             (uint32_t)std::stoul(i_additional["0x24 SRC Word 17"], 0, 16),
141             (uint32_t)std::stoul(i_additional["0x28 SRC Word 18"], 0, 16),
142             (uint32_t)std::stoul(i_additional["0x2c SRC Word 19"], 0, 16)});
143 
144         // Populate phyp primary SRC
145 
146         // char array for raw pel src
147         std::array<char, pel::asciiStringSize> srcChars{'0'};
148 
149         // src from TI info
150         std::string srcString = i_additional["SrcAscii"];
151 
152         // copy from string to char array
153         srcString.copy(srcChars.data(),
154                        std::min(srcString.size(), pel::asciiStringSize), 0);
155 
156         tiPel->setAsciiString(srcChars); // pel object src is char array
157 
158         // set symptom-id
159         auto symptomId = (i_additional["SrcAscii"].substr(0, 8) + '_');
160 
161         symptomId += (i_additional["0x10 SRC Word 12"]);
162         symptomId += (i_additional["0x14 SRC Word 13"] + '_');
163         symptomId += (i_additional["0x18 SRC Word 14"]);
164         symptomId += (i_additional["0x1c SRC Word 15"] + '_');
165         symptomId += (i_additional["0x20 SRC Word 16"]);
166         symptomId += (i_additional["0x24 SRC Word 17"] + '_');
167         symptomId += (i_additional["0x28 SRC Word 18"]);
168         symptomId += (i_additional["0x2c SRC Word 19"]);
169 
170         // setSymptomId will take care of required null-terminate and padding
171         tiPel->setSymptomId(symptomId);
172     }
173     else
174     {
175         // Populate hostboot SRC words - note HB word 0 from the shared info
176         // data (additional data "0x10 HB Word") is reflected in the PEL as
177         // "reason code" so we zero it here. Also note that the first word
178         // in this group of words starts at word 0 and word 1 does not exits.
179         tiPel->setSrcWords(std::array<uint32_t, pel::numSrcWords>{
180             (uint32_t)0x00000000,
181             (uint32_t)std::stoul(i_additional["0x14 HB Word 2"], 0, 16),
182             (uint32_t)std::stoul(i_additional["0x18 HB Word 3"], 0, 16),
183             (uint32_t)std::stoul(i_additional["0x1c HB Word 4"], 0, 16),
184             (uint32_t)std::stoul(i_additional["0x20 HB Word 5"], 0, 16),
185             (uint32_t)std::stoul(i_additional["0x24 HB Word 6"], 0, 16),
186             (uint32_t)std::stoul(i_additional["0x28 HB Word 7"], 0, 16),
187             (uint32_t)std::stoul(i_additional["0x2c HB Word 8"], 0, 16)});
188 
189         // Populate hostboot primary SRC
190 
191         // char array for raw pel src
192         std::array<char, pel::asciiStringSize> srcChars{'0'};
193 
194         // src from TI info
195         std::string srcString = i_additional["SrcAscii"];
196 
197         // copy from string to char array
198         srcString.copy(srcChars.data(),
199                        std::min(srcString.size(), pel::asciiStringSize), 0);
200 
201         tiPel->setAsciiString(srcChars); // pel object src is char array
202 
203         // set symptom-id
204         auto symptomId = (i_additional["SrcAscii"].substr(0, 8) + '_');
205 
206         symptomId += (i_additional["0x10 HB Word 0"]);       // note: word 1
207         symptomId += (i_additional["0x14 HB Word 2"] + '_'); // does not exist
208         symptomId += (i_additional["0x18 HB Word 3"]);
209         symptomId += (i_additional["0x1c HB Word 4"] + '_');
210         symptomId += (i_additional["0x20 HB Word 5"]);
211         symptomId += (i_additional["0x24 HB Word 6"] + '_');
212         symptomId += (i_additional["0x28 HB Word 7"]);
213         symptomId += (i_additional["0x2c HB Word 8"]);
214 
215         // setSymptomId will take care of required null-terminate and padding
216         tiPel->setSymptomId(symptomId);
217     }
218 
219     // set severity, event type and action flags
220     tiPel->setSeverity(static_cast<uint8_t>(pel::Severity::termination));
221     tiPel->setType(static_cast<uint8_t>(pel::EventType::na));
222     tiPel->setAction(static_cast<uint16_t>(pel::ActionFlags::service |
223                                            pel::ActionFlags::report |
224                                            pel::ActionFlags::call));
225 
226     // The raw PEL that we used as the basis for this custom PEL contains the
227     // attention handler trace data and does not needed to be in this PEL so
228     // we remove it here.
229     tiPel->setSectionCount(tiPel->getSectionCount() - 1);
230 
231     // Update the raw PEL with the new custom PEL data
232     tiPel->raw(i_rawPel);
233 
234     // create PEL from raw data
235     createPelRaw(i_rawPel);
236 }
237 
238 /**
239  * Log an event handled by the attention handler
240  *
241  * Basic (non TI) events will generate a standard message-registry based PEL
242  *
243  * TI events will create two PEL's. One PEL will be informational and will
244  * contain trace information relevent to attention handler. The second PEL
245  * will be specific to the TI type (including the primary SRC) and will be
246  * based off of the TI information provided to the attention handler through
247  * shared TI info data area.
248  *
249  * @param  i_event - The event type
250  * @param  i_additional - Additional PEL data
251  * @param  i_ffdc - FFDC PEL data
252  * @return Event log Id (0 if no event log generated)
253  */
254 uint32_t event(EventType i_event,
255                std::map<std::string, std::string>& i_additional,
256                const std::vector<util::FFDCFile>& i_ffdc)
257 {
258     uint32_t pelId = 0; // assume no event log generated
259 
260     bool eventValid = false; // assume no event created
261     bool tiEvent    = false; // assume not a terminate event
262 
263     std::string eventName;
264 
265     switch (i_event)
266     {
267         case EventType::Checkstop:
268             eventName  = "org.open_power.HwDiags.Error.Checkstop";
269             eventValid = true;
270             break;
271         case EventType::Terminate:
272             eventName  = "org.open_power.Attn.Error.Terminate";
273             eventValid = true;
274             tiEvent    = true;
275             break;
276         case EventType::Vital:
277             eventName  = "org.open_power.Attn.Error.Vital";
278             eventValid = true;
279             break;
280         case EventType::HwDiagsFail:
281         case EventType::AttentionFail:
282             eventName  = "org.open_power.Attn.Error.Fail";
283             eventValid = true;
284             break;
285         default:
286             eventValid = false;
287             break;
288     }
289 
290     if (true == eventValid)
291     {
292         // Create PEL with additional data and FFDC data. The newly created
293         // PEL's platform log-id will be returned.
294         pelId = createPel(eventName, i_additional, createFFDCTuples(i_ffdc));
295 
296         // If this is a TI event we will create an additional PEL that is
297         // specific to the subsystem that generated the TI.
298         if ((0 != pelId) && (true == tiEvent))
299         {
300             // get file descriptor and size of information PEL
301             int pelFd = getPel(pelId);
302 
303             // if PEL found, read into buffer
304             if (-1 != pelFd)
305             {
306                 auto pelSize = lseek(pelFd, 0, SEEK_END);
307                 lseek(pelFd, 0, SEEK_SET);
308 
309                 // read information PEL into buffer
310                 std::vector<uint8_t> buffer(pelSize);
311                 size_t numBytes = read(pelFd, buffer.data(), buffer.size());
312                 if (buffer.size() != numBytes)
313                 {
314                     trace::err("Error reading event log: %u of %u bytes read",
315                                numBytes, buffer.size());
316                 }
317                 else
318                 {
319                     // create PEL from buffer
320                     createPelCustom(buffer, i_additional);
321                 }
322 
323                 close(pelFd);
324             }
325 
326             uint8_t subsystem = std::stoi(i_additional["Subsystem"]);
327 
328             // If not hypervisor TI
329             if (static_cast<uint8_t>(pel::SubsystemID::hypervisor) != subsystem)
330             {
331                 // Request a dump and transition the host
332                 if ("true" == i_additional["Dump"])
333                 {
334                     // will not return until dump is complete
335                     requestDump(pelId, DumpParameters{0, DumpType::Hostboot});
336                 }
337             }
338         }
339     }
340     return pelId;
341 }
342 
343 /**
344  * Commit special attention TI event to log
345  *
346  * Create a event log with provided additional information and standard
347  * FFDC data plus TI FFDC data
348  *
349  * @param i_additional - Additional log data
350  * @param i_ti_InfoData - TI FFDC data
351  */
352 void eventTerminate(std::map<std::string, std::string> i_additionalData,
353                     char* i_tiInfoData)
354 {
355 
356     uint32_t tiInfoSize = 0; // assume TI info was not available
357 
358     if (nullptr != i_tiInfoData)
359     {
360         tiInfoSize = 56; // assume not hypervisor TI
361 
362         uint8_t subsystem = std::stoi(i_additionalData["Subsystem"]);
363 
364         // If hypervisor
365         if (static_cast<uint8_t>(pel::SubsystemID::hypervisor) == subsystem)
366         {
367             tiInfoSize = 1024; // assume hypervisor max
368 
369             // hypervisor may just want some of the data
370             if (0 == (*(i_tiInfoData + 0x09) & 0x01))
371             {
372                 uint32_t* additionalLength = (uint32_t*)(i_tiInfoData + 0x50);
373                 uint32_t tiAdditional      = be32toh(*additionalLength);
374                 tiInfoSize = std::min(tiInfoSize, (84 + tiAdditional));
375             }
376         }
377     }
378 
379     trace::inf("TI info size = %u", tiInfoSize);
380 
381     event(EventType::Terminate, i_additionalData,
382           createFFDCFiles(i_tiInfoData, tiInfoSize));
383 }
384 
385 /** @brief Commit SBE vital event to log, returns event log ID */
386 uint32_t eventVital()
387 {
388     // Additional data for log
389     std::map<std::string, std::string> additionalData;
390 
391     // Create log event with additional data and FFDC data
392     return event(EventType::Vital, additionalData, createFFDCFiles(nullptr, 0));
393 }
394 
395 /**
396  * Commit attention handler failure event to log
397  *
398  * Create an event log containing the specified error code.
399  *
400  * @param i_error - Error code
401  */
402 void eventAttentionFail(int i_error)
403 {
404     // Additional data for log
405     std::map<std::string, std::string> additionalData;
406     additionalData["ERROR_CODE"] = std::to_string(i_error);
407 
408     // Create log event with additional data and FFDC data
409     event(EventType::AttentionFail, additionalData,
410           createFFDCFiles(nullptr, 0));
411 }
412 
413 } // namespace attn
414