blob: 70acaa45d75f79f7e95a33a73919bfdc2bf98829 [file] [log] [blame]
Ben Tynerb1ebfcb2020-05-08 18:52:48 -05001#include <unistd.h>
2
Ben Tyner188f1092021-02-01 09:33:06 -06003#include <attn/attn_dbus.hpp>
Ben Tynerb797b3e2020-06-29 10:12:05 -05004#include <attn/attn_logging.hpp>
Ben Tynerf5210bb2021-01-05 12:58:10 -06005#include <attn/pel/pel_minimal.hpp>
Ben Tynerb1ebfcb2020-05-08 18:52:48 -05006#include <phosphor-logging/log.hpp>
Ben Tynerf5210bb2021-01-05 12:58:10 -06007
Ben Tynerb1ebfcb2020-05-08 18:52:48 -05008namespace attn
9{
10
Ben Tynerf5210bb2021-01-05 12:58:10 -060011/** @brief Journal entry of type INFO using phosphor logging */
Ben Tynerb1ebfcb2020-05-08 18:52:48 -050012template <>
13void trace<INFO>(const char* i_message)
14{
15 phosphor::logging::log<phosphor::logging::level::INFO>(i_message);
16}
17
Ben Tynerf5210bb2021-01-05 12:58:10 -060018/** @brief Tuple containing information about ffdc files */
19using FFDCTuple =
20 std::tuple<util::FFDCFormat, uint8_t, uint8_t, sdbusplus::message::unix_fd>;
21
22/** @brief Gather messages from the journal */
23std::vector<std::string> sdjGetMessages(const std::string& field,
24 const std::string& fieldValue,
25 unsigned int max);
26
27/**
28 * Create FFDCTuple objects corresponding to the specified FFDC files.
29 *
30 * The D-Bus method to create an error log requires a vector of tuples to
31 * pass in the FFDC file information.
32 *
33 * @param files - FFDC files
34 * @return vector of FFDCTuple objects
35 */
36std::vector<FFDCTuple>
37 createFFDCTuples(const std::vector<util::FFDCFile>& files)
38{
39 std::vector<FFDCTuple> ffdcTuples{};
Zane Shelleya79f6c82021-01-12 16:38:49 -060040 util::transformFFDC(files, ffdcTuples);
Ben Tynerf5210bb2021-01-05 12:58:10 -060041
42 return ffdcTuples;
43}
44
45/**
46 * @brief Create an FFDCFile object containing raw data
47 *
48 * Throws an exception if an error occurs.
49 *
50 * @param i_buffer - raw data to add to ffdc faw data file
51 * @param i_size - size of the raw data
52 * @return FFDCFile object
53 */
54util::FFDCFile createFFDCRawFile(void* i_buffer, size_t i_size)
55{
56 util::FFDCFile file{util::FFDCFormat::Custom};
57
58 // Write buffer to file and then reset file description file offset
59 int fd = file.getFileDescriptor();
60 write(fd, static_cast<char*>(i_buffer), i_size);
61 lseek(fd, 0, SEEK_SET);
62
63 return file;
64}
65
66/**
67 * @brief Create an FFDCFile object containing the specified lines of text data
68 *
69 * Throws an exception if an error occurs.
70 *
71 * @param lines - lines of text data to write to file
72 * @return FFDCFile object
73 */
74util::FFDCFile createFFDCTraceFile(const std::vector<std::string>& lines)
75{
76 // Create FFDC file of type Text
77 util::FFDCFile file{util::FFDCFormat::Text};
78 int fd = file.getFileDescriptor();
79
80 // Write FFDC lines to file
81 std::string buffer;
82 for (const std::string& line : lines)
83 {
84 // Copy line to buffer. Add newline if necessary.
85 buffer = line;
86 if (line.empty() || (line.back() != '\n'))
87 {
88 buffer += '\n';
89 }
90
91 // write buffer to file
92 write(fd, buffer.c_str(), buffer.size());
93 }
94
95 // Seek to beginning of file so error logging system can read data
96 lseek(fd, 0, SEEK_SET);
97
98 return file;
99}
100
101/**
102 * Create FDDC files from journal messages of relevant executables
103 *
104 * Parse the system journal looking for log entries created by the executables
105 * of interest for logging. For each of these entries create a ffdc trace file
106 * that will be used to create ffdc log entries. These files will be pushed
107 * onto the stack of ffdc files.
108 *
109 * @param i_files - vector of ffdc files that will become log entries
110 */
111void createFFDCTraceFiles(std::vector<util::FFDCFile>& i_files)
112{
113 // Executables of interest
114 std::vector<std::string> executables{"openpower-hw-diags"};
115
116 for (const std::string& executable : executables)
117 {
118 try
119 {
120 // get journal messages
121 std::vector<std::string> messages =
122 sdjGetMessages("SYSLOG_IDENTIFIER", executable, 30);
123
124 // Create FFDC file containing the journal messages
125 if (!messages.empty())
126 {
127 i_files.emplace_back(createFFDCTraceFile(messages));
128 }
129 }
130 catch (const std::exception& e)
131 {
132 std::stringstream ss;
133 ss << "createFFDCFiles: " << e.what();
134 trace<level::INFO>(ss.str().c_str());
135 }
136 }
137}
138
139/**
140 * Create FFDCFile objects containing debug data to store in the error log.
141 *
142 * If an error occurs, the error is written to the journal but an exception
143 * is not thrown.
144 *
145 * @param i_buffer - raw data (if creating raw dump ffdc entry in log)
146 * @return vector of FFDCFile objects
147 */
148std::vector<util::FFDCFile> createFFDCFiles(char* i_buffer = nullptr,
149 size_t i_size = 0)
150{
151 std::vector<util::FFDCFile> files{};
152
153 // Create raw dump file
154 if ((nullptr != i_buffer) && (0 != i_size))
155 {
156 files.emplace_back(createFFDCRawFile(i_buffer, i_size));
157 }
158
159 // Create trace dump file
160 createFFDCTraceFiles(files);
161
162 return files;
163}
164
165/**
Ben Tynerf5210bb2021-01-05 12:58:10 -0600166 * Create a PEL from an existing PEL
167 *
168 * Create a new PEL based on the specified raw PEL and submit the new PEL
169 * to the backend logging code as a raw PEL. Note that additional data map
170 * here contains data to be committed to the PEL and it can also be used to
171 * create the PEL as it contains needed information.
172 *
173 * @param i_buffer - buffer containing a raw PEL
174 * @param i_additional - additional data to be added to the new PEL
175 */
176void createPelCustom(std::vector<uint8_t>& i_rawPel,
177 std::map<std::string, std::string> i_additional)
178{
179 // create PEL object from buffer
180 auto tiPel = std::make_unique<pel::PelMinimal>(i_rawPel);
181
182 // The additional data contains the TI info as well as the value for the
183 // subystem that provided the TI info. Get the subystem from additional
184 // data and then populate the prmary SRC and SRC words for the custom PEL
185 // based on the sybsystem's TI info.
186 uint8_t subsystem = std::stoi(i_additional["Subsystem"]);
187 tiPel->setSubsystem(subsystem);
188
189 if (static_cast<uint8_t>(pel::SubsystemID::hypervisor) == subsystem)
190 {
191 // populate hypervisor SRC words
192 tiPel->setSrcWords(std::array<uint32_t, pel::numSrcWords>{
193 (uint32_t)std::stoul(i_additional["0x10 SRC Word 12"], 0, 16),
194 (uint32_t)std::stoul(i_additional["0x14 SRC Word 13"], 0, 16),
195 (uint32_t)std::stoul(i_additional["0x18 SRC Word 14"], 0, 16),
196 (uint32_t)std::stoul(i_additional["0x1c SRC Word 15"], 0, 16),
197 (uint32_t)std::stoul(i_additional["0x20 SRC Word 16"], 0, 16),
198 (uint32_t)std::stoul(i_additional["0x24 SRC Word 17"], 0, 16),
199 (uint32_t)std::stoul(i_additional["0x28 SRC Word 18"], 0, 16),
200 (uint32_t)std::stoul(i_additional["0x2c SRC Word 19"], 0, 16)});
201
202 // populate hypervisor primary SRC
203 std::array<char, pel::asciiStringSize> srcChars{'0'};
204 std::string srcString = i_additional["SrcAscii"];
205 srcString.copy(srcChars.data(),
206 std::min(srcString.size(), pel::asciiStringSize), 0);
207 tiPel->setAsciiString(srcChars);
208 }
209 else
210 {
211 // Populate hostboot SRC words - note HB word 0 from the shared info
212 // data (additional data "0x10 HB Word") is reflected in the PEL as
213 // "reason code" so we zero it here. Also note that the first word
214 // in this group of words starts at word 0 and word 1 does not exits.
215 tiPel->setSrcWords(std::array<uint32_t, pel::numSrcWords>{
216 (uint32_t)0x00000000,
217 (uint32_t)std::stoul(i_additional["0x14 HB Word 2"], 0, 16),
218 (uint32_t)std::stoul(i_additional["0x18 HB Word 3"], 0, 16),
219 (uint32_t)std::stoul(i_additional["0x1c HB Word 4"], 0, 16),
220 (uint32_t)std::stoul(i_additional["0x20 HB Word 5"], 0, 16),
221 (uint32_t)std::stoul(i_additional["0x24 HB Word 6"], 0, 16),
222 (uint32_t)std::stoul(i_additional["0x28 HB Word 7"], 0, 16),
223 (uint32_t)std::stoul(i_additional["0x2c HB Word 8"], 0, 16)});
224
225 // populate hostboot primary SRC
226 std::array<char, pel::asciiStringSize> srcChars{'0'};
227 std::string srcString = i_additional["0x30 error_data"];
228 srcString.copy(srcChars.data(),
229 std::min(srcString.size(), pel::asciiStringSize), 0);
230 tiPel->setAsciiString(srcChars);
231 }
232
233 // set severity, event type and action flags
234 tiPel->setSeverity(static_cast<uint8_t>(pel::Severity::termination));
235 tiPel->setType(static_cast<uint8_t>(pel::EventType::na));
236 tiPel->setAction(static_cast<uint16_t>(pel::ActionFlags::service |
237 pel::ActionFlags::report |
238 pel::ActionFlags::call));
239
240 // The raw PEL that we used as the basis for this custom PEL contains the
241 // attention handler trace data and does not needed to be in this PEL so
242 // we remove it here.
243 tiPel->setSectionCount(tiPel->getSectionCount() - 1);
244
245 // Update the raw PEL with the new custom PEL data
246 tiPel->raw(i_rawPel);
247
248 // create PEL from raw data
249 createPelRaw(i_rawPel);
250}
251
252/**
253 * Log an event handled by the attention handler
254 *
255 * Basic (non TI) events will generate a standard message-registry based PEL
256 *
257 * TI events will create two PEL's. One PEL will be informational and will
258 * contain trace information relevent to attention handler. The second PEL
259 * will be specific to the TI type (including the primary SRC) and will be
260 * based off of the TI information provided to the attention handler through
261 * shared TI info data area.
262 *
263 * @param i_event - The event type
264 * @param i_additional - Additional PEL data
265 * @param i_ffdc - FFDC PEL data
266 */
267void event(EventType i_event, std::map<std::string, std::string>& i_additional,
268 const std::vector<util::FFDCFile>& i_ffdc)
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500269{
270 bool eventValid = false; // assume no event created
Ben Tynerf5210bb2021-01-05 12:58:10 -0600271 bool tiEvent = false; // assume not a terminate event
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500272
273 std::string eventName;
274
275 switch (i_event)
276 {
277 case EventType::Checkstop:
278 eventName = "org.open_power.HwDiags.Error.Checkstop";
279 eventValid = true;
280 break;
281 case EventType::Terminate:
282 eventName = "org.open_power.Attn.Error.Terminate";
283 eventValid = true;
Ben Tynerf5210bb2021-01-05 12:58:10 -0600284 tiEvent = true;
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500285 break;
286 case EventType::Vital:
287 eventName = "org.open_power.Attn.Error.Vital";
288 eventValid = true;
289 break;
290 case EventType::HwDiagsFail:
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500291 case EventType::AttentionFail:
292 eventName = "org.open_power.Attn.Error.Fail";
293 eventValid = true;
294 break;
295 default:
296 eventValid = false;
297 break;
298 }
299
300 if (true == eventValid)
301 {
Ben Tynerf5210bb2021-01-05 12:58:10 -0600302 // Create PEL with additional data and FFDC data. The newly created
303 // PEL's platform log-id will be returned.
304 auto pelId =
305 createPel(eventName, i_additional, createFFDCTuples(i_ffdc));
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500306
Ben Tynerf5210bb2021-01-05 12:58:10 -0600307 // If this is a TI event we will create an additional PEL that is
308 // specific to the subsystem that generated the TI.
309 if (true == tiEvent)
310 {
311 // get file descriptor and size of information PEL
Ben Tyner188f1092021-02-01 09:33:06 -0600312 int pelFd = getPel(pelId);
Ben Tyner1b1915e2020-10-23 15:13:38 -0500313
Ben Tynerf5210bb2021-01-05 12:58:10 -0600314 // if PEL found, read into buffer
315 if (-1 != pelFd)
316 {
317 auto pelSize = lseek(pelFd, 0, SEEK_END);
318 lseek(pelFd, 0, SEEK_SET);
Ben Tyner1b1915e2020-10-23 15:13:38 -0500319
Ben Tynerf5210bb2021-01-05 12:58:10 -0600320 // read information PEL into buffer
321 std::vector<uint8_t> buffer(pelSize);
322 read(pelFd, buffer.data(), buffer.size());
323 close(pelFd);
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500324
Ben Tynerf5210bb2021-01-05 12:58:10 -0600325 // create PEL from buffer
326 createPelCustom(buffer, i_additional);
327 }
328 }
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500329 }
330}
331
Ben Tynerf5210bb2021-01-05 12:58:10 -0600332/**
333 * Commit special attention TI event to log
334 *
335 * Create a event log with provided additional information and standard
336 * FFDC data plus TI FFDC data
337 *
338 * @param i_additional - Additional log data
339 * @param i_ti_InfoData - TI FFDC data
340 */
341void eventTerminate(std::map<std::string, std::string> i_additionalData,
342 char* i_tiInfoData)
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500343{
Ben Tynerb6401ed2021-01-11 09:08:07 -0600344 uint32_t tiInfoSize = 56; // assume not hypervisor TI
345
346 uint8_t subsystem = std::stoi(i_additionalData["Subsystem"]);
347
348 // If hypervisor
349 if (static_cast<uint8_t>(pel::SubsystemID::hypervisor) == subsystem)
350 {
351 tiInfoSize = 1024; // assume hypervisor max
352
353 // hypervisor may just want some of the data
354 if (0 == (*(i_tiInfoData + 0x09) & 0x01))
355 {
356 uint32_t* additionalLength = (uint32_t*)(i_tiInfoData + 0x50);
357 uint32_t tiAdditional = be32toh(*additionalLength);
358
359 tiInfoSize = std::min(tiInfoSize, (84 + tiAdditional));
360 }
361 }
362
363 std::string traceMsg = "TI info size = " + std::to_string(tiInfoSize);
364 trace<level::INFO>(traceMsg.c_str());
365
Ben Tynerf5210bb2021-01-05 12:58:10 -0600366 event(EventType::Terminate, i_additionalData,
Ben Tynerb6401ed2021-01-11 09:08:07 -0600367 createFFDCFiles(i_tiInfoData, tiInfoSize));
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500368}
369
Ben Tynerf5210bb2021-01-05 12:58:10 -0600370/** @brief Commit SBE vital event to log */
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500371void eventVital()
372{
Ben Tynerf5210bb2021-01-05 12:58:10 -0600373 // Additional data for log
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500374 std::map<std::string, std::string> additionalData;
375
Ben Tynerf5210bb2021-01-05 12:58:10 -0600376 // Create log event with additional data and FFDC data
377 event(EventType::Vital, additionalData, createFFDCFiles(nullptr, 0));
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500378}
379
Ben Tynerf5210bb2021-01-05 12:58:10 -0600380/**
Ben Tynerf5210bb2021-01-05 12:58:10 -0600381 * Commit attention handler failure event to log
382 *
383 * Create an event log containing the specified error code.
384 *
385 * @param i_error - Error code
386 */
387void eventAttentionFail(int i_error)
388{
389 // Additional data for log
390 std::map<std::string, std::string> additionalData;
391 additionalData["ERROR_CODE"] = std::to_string(i_error);
392
393 // Create log event with additional data and FFDC data
394 event(EventType::AttentionFail, additionalData,
395 createFFDCFiles(nullptr, 0));
396}
397
398/**
399 * Parse systemd journal message field
400 *
401 * Parse the journal looking for the specified field and return the journal
402 * data for that field.
403 *
404 * @param journal - The journal to parse
405 * @param field - Field containing the data to retrieve
406 * @return Data for the speciefied field
407 */
Ben Tyner1b1915e2020-10-23 15:13:38 -0500408std::string sdjGetFieldValue(sd_journal* journal, const char* field)
409{
410 const char* data{nullptr};
411 size_t length{0};
Ben Tyner1b1915e2020-10-23 15:13:38 -0500412
413 // get field value
414 if (0 == sd_journal_get_data(journal, field, (const void**)&data, &length))
415 {
Zane Shelley9fb657f2021-01-12 15:30:58 -0600416 size_t prefix{0};
417
Ben Tyner1b1915e2020-10-23 15:13:38 -0500418 // The data returned by sd_journal_get_data will be prefixed with the
419 // field name and "="
420 const void* eq = memchr(data, '=', length);
421 if (nullptr != eq)
422 {
423 // get just data following the "="
424 prefix = (const char*)eq - data + 1;
425 }
426 else
427 {
428 // all the data (should not happen)
429 prefix = 0;
430 std::string value{}; // empty string
431 }
432
433 return std::string{data + prefix, length - prefix};
434 }
435 else
436 {
437 return std::string{}; // empty string
438 }
439}
440
Ben Tynerf5210bb2021-01-05 12:58:10 -0600441/**
442 * Gather messages from the journal
443 *
444 * Fetch journal entry data for all entries with the specified field equal to
445 * the specified value.
446 *
447 * @param field - Field to search on
448 * @param fieldValue - Value to search for
449 * @param max - Maximum number of messages fetch
450 * @return Vector of journal entry data
451 */
Ben Tyner1b1915e2020-10-23 15:13:38 -0500452std::vector<std::string> sdjGetMessages(const std::string& field,
453 const std::string& fieldValue,
454 unsigned int max)
455{
456 sd_journal* journal;
457 std::vector<std::string> messages;
458
459 if (0 == sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY))
460 {
461 SD_JOURNAL_FOREACH_BACKWARDS(journal)
462 {
463 // Get input field
464 std::string value = sdjGetFieldValue(journal, field.c_str());
465
466 // Compare field value and read data
467 if (value == fieldValue)
468 {
469 // Get SYSLOG_IDENTIFIER field (process that logged message)
470 std::string syslog =
471 sdjGetFieldValue(journal, "SYSLOG_IDENTIFIER");
472
473 // Get _PID field
474 std::string pid = sdjGetFieldValue(journal, "_PID");
475
476 // Get MESSAGE field
477 std::string message = sdjGetFieldValue(journal, "MESSAGE");
478
479 // Get timestamp
480 uint64_t usec{0};
481 if (0 == sd_journal_get_realtime_usec(journal, &usec))
482 {
483
484 // Convert realtime microseconds to date format
485 char dateBuffer[80];
486 std::string date;
487 std::time_t timeInSecs = usec / 1000000;
488 strftime(dateBuffer, sizeof(dateBuffer), "%b %d %H:%M:%S",
489 std::localtime(&timeInSecs));
490 date = dateBuffer;
491
492 // Store value to messages
493 value = date + " " + syslog + "[" + pid + "]: " + message;
494 messages.insert(messages.begin(), value);
495 }
496 }
497
498 // limit maximum number of messages
499 if (messages.size() >= max)
500 {
501 break;
502 }
503 }
504
505 sd_journal_close(journal); // close journal when done
506 }
507
508 return messages;
509}
510
Ben Tynerb1ebfcb2020-05-08 18:52:48 -0500511} // namespace attn