1 #include <attn/attn_common.hpp>
2 #include <attn/attn_dbus.hpp>
3 #include <attn/attn_logging.hpp>
4 #include <attn/pel/pel_common.hpp>
5 #include <attn/ti_handler.hpp>
6 #include <sdbusplus/bus.hpp>
7 #include <sdbusplus/exception.hpp>
8 
9 #include <iomanip>
10 #include <iostream>
11 
12 namespace attn
13 {
14 
15 /**
16  * @brief Determine if this is a HB or PHYP TI event
17  *
18  * Use the TI info data area to determine if this is either a HB or a PHYP
19  * TI event then handle the event.
20  *
21  * @param i_tiDataArea pointer to the TI info data
22  */
23 int tiHandler(TiDataArea* i_tiDataArea)
24 {
25     int rc = RC_SUCCESS;
26 
27     // capture some additional data for logs/traces
28     addHbStatusRegs();
29 
30     // check TI data area if it is available
31     if (nullptr != i_tiDataArea)
32     {
33         // HB v. PHYP TI logic: Only hosboot will fill in hbTerminateType
34         // and it will be non-zero. Only hostboot will fill out source and
35         // it it will be non-zero. Only PHYP will fill in srcFormat and it
36         // will be non-zero.
37         if ((0 == i_tiDataArea->hbTerminateType) &&
38             (0 == i_tiDataArea->source) && (0 != i_tiDataArea->srcFormat))
39         {
40             handlePhypTi(i_tiDataArea);
41         }
42         else
43         {
44             handleHbTi(i_tiDataArea);
45         }
46     }
47     else
48     {
49         // TI data was not available This should not happen since we provide
50         // a default TI info in the case where get TI info was not successful.
51         eventAttentionFail((int)AttnSection::tiHandler | ATTN_INFO_NULL);
52         rc = RC_NOT_HANDLED;
53     }
54 
55     return rc;
56 }
57 
58 /**
59  * @brief Handle a PHYP terminate immediate special attention
60  *
61  * The TI info data area will contain information pertaining to the TI
62  * condition. We will wither quiesce the host or initiate a MPIPL depending
63  * depending on the auto reboot configuration. We will also create a PEL which
64  * will contain the TI info data and FFDC data captured in the system journal.
65  *
66  * @param i_tiDataArea pointer to TI information filled in by hostboot
67  */
68 void handlePhypTi(TiDataArea* i_tiDataArea)
69 {
70     trace<level::INFO>("PHYP TI");
71 
72     // gather additional data for PEL
73     std::map<std::string, std::string> tiAdditionalData;
74 
75     if (nullptr != i_tiDataArea)
76     {
77         parsePhypOpalTiInfo(tiAdditionalData, i_tiDataArea);
78 
79         tiAdditionalData["Subsystem"] =
80             std::to_string(static_cast<uint8_t>(pel::SubsystemID::hypervisor));
81 
82         // Copy all ascii src chars to additional data
83         char srcChar[33]; // 32 ascii chars + null term
84         memcpy(srcChar, &(i_tiDataArea->asciiData0), 32);
85         srcChar[32]                  = 0;
86         tiAdditionalData["SrcAscii"] = std::string{srcChar};
87 
88         // TI event
89         eventTerminate(tiAdditionalData, (char*)i_tiDataArea);
90     }
91     else
92     {
93         // TI data was not available This should not happen since we provide
94         // a default TI info in the case where get TI info was not successful.
95         eventAttentionFail((int)AttnSection::handlePhypTi | ATTN_INFO_NULL);
96     }
97 
98     // We are finished creating the event log entries so transition host to
99     // the required state.
100     if (autoRebootEnabled())
101     {
102         // If autoreboot is enabled we will start crash (mpipl) mode target
103         transitionHost(HostState::Crash);
104     }
105     else
106     {
107         // If autoreboot is disabled we will quiesce the host
108         transitionHost(HostState::Quiesce);
109     }
110 }
111 
112 /**
113  * @brief Handle a hostboot terminate immediate special attention
114  *
115  * The TI info data area will contain information pertaining to the TI
116  * condition. The course of action to take regarding the host state will
117  * depend on the contents of the TI info data area. We will also create a
118  * PEL containing the TI info data and FFDC data captured in the system
119  * journal.
120  *
121  * @param i_tiDataArea pointer to TI information filled in by hostboot
122  */
123 void handleHbTi(TiDataArea* i_tiDataArea)
124 {
125     trace<level::INFO>("HB TI");
126 
127     bool hbDumpRequested = true; // HB dump is common case
128     bool generatePel     = true; // assume PEL will be created
129     bool terminateHost   = true; // transition host state
130 
131     // handle specific hostboot reason codes
132     if (nullptr != i_tiDataArea)
133     {
134         std::stringstream ss; // stream object for tracing
135         std::string strobj;   // string object for tracing
136 
137         switch (i_tiDataArea->hbTerminateType)
138         {
139             case TI_WITH_PLID:
140             case TI_WITH_EID:
141 
142                 // trace this value
143                 ss.str(std::string()); // empty the stream
144                 ss.clear();            // clear the stream
145                 ss << "TI with PLID/EID: " << std::hex << std::showbase
146                    << std::setw(8) << std::setfill('0')
147                    << be32toh(i_tiDataArea->asciiData1);
148                 strobj = ss.str();
149                 trace<level::INFO>(strobj.c_str());
150 
151                 // see if HB dump is requested
152                 if (0 == i_tiDataArea->hbDumpFlag)
153                 {
154                     hbDumpRequested = false; // no HB dump requested
155                 }
156                 break;
157             case TI_WITH_SRC:
158                 // Reason code is byte 2 and 3 of 4 byte srcWord12HbWord0
159                 uint16_t reasonCode = be32toh(i_tiDataArea->srcWord12HbWord0);
160 
161                 // trace this value
162                 ss.str(std::string()); // empty the stream
163                 ss.clear();            // clear the stream
164                 ss << "TI with SRC: " << std::hex << std::showbase
165                    << std::setw(4) << std::setfill('0') << (int)reasonCode;
166                 strobj = ss.str();
167                 trace<level::INFO>(strobj.c_str());
168 
169                 switch (reasonCode)
170                 {
171                     case HB_SRC_SHUTDOWN_REQUEST:
172                         trace<level::INFO>("shutdown request");
173                         generatePel     = false;
174                         hbDumpRequested = false;
175                         break;
176                     case HB_SRC_KEY_TRANSITION:
177                         // Note: Should never see this so lets leave
178                         // hbDumpRequested == true so we can figure out why
179                         // we are here.
180                         trace<level::INFO>("key transition");
181                         terminateHost = false;
182                         break;
183                     case HB_SRC_INSUFFICIENT_HW:
184                         trace<level::INFO>("insufficient hardware");
185                         break;
186                     case HB_SRC_TPM_FAIL:
187                         trace<level::INFO>("TPM fail");
188                         break;
189                     case HB_SRC_ROM_VERIFY:
190                         trace<level::INFO>("ROM verify");
191                         break;
192                     case HB_SRC_EXT_MISMATCH:
193                         trace<level::INFO>("EXT mismatch");
194                         break;
195                     case HB_SRC_ECC_UE:
196                         trace<level::INFO>("ECC UE");
197                         break;
198                     case HB_SRC_UNSUPPORTED_MODE:
199                         trace<level::INFO>("unsupported mode");
200                         break;
201                     case HB_SRC_UNSUPPORTED_SFCRANGE:
202                         trace<level::INFO>("unsupported SFC range");
203                         break;
204                     case HB_SRC_PARTITION_TABLE:
205                         trace<level::INFO>("partition table invalid");
206                         break;
207                     case HB_SRC_UNSUPPORTED_HARDWARE:
208                         trace<level::INFO>("unsupported hardware");
209                         break;
210                     case HB_SRC_PNOR_CORRUPTION:
211                         trace<level::INFO>("PNOR corruption");
212                         break;
213                     default:
214                         trace<level::INFO>("reason: other");
215                 }
216 
217                 break; // case TI_WITH_SRC
218         }
219     }
220 
221     if (true == generatePel)
222     {
223         if (nullptr != i_tiDataArea)
224         {
225             // gather additional data for PEL
226             std::map<std::string, std::string> tiAdditionalData;
227 
228             parseHbTiInfo(tiAdditionalData, i_tiDataArea);
229 
230             tiAdditionalData["Subsystem"] = std::to_string(
231                 static_cast<uint8_t>(pel::SubsystemID::hostboot));
232 
233             // Translate hex src value to ascii. This results in an 8
234             // character SRC (hostboot SRC is 32 bits)
235             std::stringstream src;
236             src << std::setw(8) << std::setfill('0') << std::uppercase
237                 << std::hex << be32toh(i_tiDataArea->srcWord12HbWord0);
238             tiAdditionalData["SrcAscii"] = src.str();
239 
240             // Request dump after generating event log?
241             tiAdditionalData["Dump"] =
242                 (true == hbDumpRequested) ? "true" : "false";
243 
244             // Generate event log
245             eventTerminate(tiAdditionalData, (char*)i_tiDataArea);
246         }
247         else
248         {
249             // TI data was not available This should not happen.
250             eventAttentionFail((int)AttnSection::handleHbTi | ATTN_INFO_NULL);
251         }
252     }
253 
254     if (true == terminateHost)
255     {
256         transitionHost(HostState::Quiesce);
257     }
258 }
259 
260 /** @brief Parse the TI info data area into map as PHYP/OPAL data */
261 void parsePhypOpalTiInfo(std::map<std::string, std::string>& i_map,
262                          TiDataArea* i_tiDataArea)
263 {
264     if (nullptr == i_tiDataArea)
265     {
266         return;
267     }
268 
269     std::stringstream ss;
270 
271     ss << "0x00 TI Area Valid:" << std::setw(2) << std::setfill('0') << std::hex
272        << (int)i_tiDataArea->tiAreaValid << ":";
273     ss << "0x01 Command:" << std::setw(2) << std::setfill('0') << std::hex
274        << (int)i_tiDataArea->command << ":";
275     ss << "0x02 Num. Data Bytes:" << std::setw(4) << std::setfill('0')
276        << std::hex << be16toh(i_tiDataArea->numDataBytes) << ":";
277     ss << "0x04 Reserved:" << std::setw(2) << std::setfill('0') << std::hex
278        << (int)i_tiDataArea->reserved1 << ":";
279     ss << "0x06 HWDump Type:" << std::setw(4) << std::setfill('0') << std::hex
280        << be16toh(i_tiDataArea->hardwareDumpType) << ":";
281     ss << "0x08 SRC Format:" << std::setw(2) << std::setfill('0') << std::hex
282        << (int)i_tiDataArea->srcFormat << ":";
283     ss << "0x09 SRC Flags:" << std::setw(2) << std::setfill('0') << std::hex
284        << (int)i_tiDataArea->srcFlags << ":";
285     ss << "0x0a Num. ASCII Words:" << std::setw(2) << std::setfill('0')
286        << std::hex << (int)i_tiDataArea->numAsciiWords << ":";
287     ss << "0x0b Num. Hex Words:" << std::setw(2) << std::setfill('0')
288        << std::hex << (int)i_tiDataArea->numHexWords << ":";
289     ss << "0x0e Length of SRC:" << std::setw(4) << std::setfill('0') << std::hex
290        << be16toh(i_tiDataArea->lenSrc) << ":";
291     ss << "0x10 SRC Word 12:" << std::setw(8) << std::setfill('0') << std::hex
292        << be32toh(i_tiDataArea->srcWord12HbWord0) << ":";
293     ss << "0x14 SRC Word 13:" << std::setw(8) << std::setfill('0') << std::hex
294        << be32toh(i_tiDataArea->srcWord13HbWord2) << ":";
295     ss << "0x18 SRC Word 14:" << std::setw(8) << std::setfill('0') << std::hex
296        << be32toh(i_tiDataArea->srcWord14HbWord3) << ":";
297     ss << "0x1c SRC Word 15:" << std::setw(8) << std::setfill('0') << std::hex
298        << be32toh(i_tiDataArea->srcWord15HbWord4) << ":";
299     ss << "0x20 SRC Word 16:" << std::setw(8) << std::setfill('0') << std::hex
300        << be32toh(i_tiDataArea->srcWord16HbWord5) << ":";
301     ss << "0x24 SRC Word 17:" << std::setw(8) << std::setfill('0') << std::hex
302        << be32toh(i_tiDataArea->srcWord17HbWord6) << ":";
303     ss << "0x28 SRC Word 18:" << std::setw(8) << std::setfill('0') << std::hex
304        << be32toh(i_tiDataArea->srcWord18HbWord7) << ":";
305     ss << "0x2c SRC Word 19:" << std::setw(8) << std::setfill('0') << std::hex
306        << be32toh(i_tiDataArea->srcWord19HbWord8) << ":";
307     ss << "0x30 ASCII Data:" << std::setw(8) << std::setfill('0') << std::hex
308        << be32toh(i_tiDataArea->asciiData0) << ":";
309     ss << "0x34 ASCII Data:" << std::setw(8) << std::setfill('0') << std::hex
310        << be32toh(i_tiDataArea->asciiData1) << ":";
311     ss << "0x38 ASCII Data:" << std::setw(8) << std::setfill('0') << std::hex
312        << be32toh(i_tiDataArea->asciiData2) << ":";
313     ss << "0x3c ASCII Data:" << std::setw(8) << std::setfill('0') << std::hex
314        << be32toh(i_tiDataArea->asciiData3) << ":";
315     ss << "0x40 ASCII Data:" << std::setw(8) << std::setfill('0') << std::hex
316        << be32toh(i_tiDataArea->asciiData4) << ":";
317     ss << "0x44 ASCII Data:" << std::setw(8) << std::setfill('0') << std::hex
318        << be32toh(i_tiDataArea->asciiData5) << ":";
319     ss << "0x48 ASCII Data:" << std::setw(8) << std::setfill('0') << std::hex
320        << be32toh(i_tiDataArea->asciiData6) << ":";
321     ss << "0x4c ASCII Data:" << std::setw(8) << std::setfill('0') << std::hex
322        << be32toh(i_tiDataArea->asciiData7) << ":";
323     ss << "0x50 Location:" << std::setw(2) << std::setfill('0') << std::hex
324        << (int)i_tiDataArea->location << ":";
325     ss << "0x51 Code Sections:" << std::setw(2) << std::setfill('0') << std::hex
326        << (int)i_tiDataArea->codeSection << ":";
327     ss << "0x52 Additional Size:" << std::setw(2) << std::setfill('0')
328        << std::hex << (int)i_tiDataArea->additionalSize << ":";
329     ss << "0x53 Additional Data:" << std::setw(2) << std::setfill('0')
330        << std::hex << (int)i_tiDataArea->andData;
331 
332     std::string key, value;
333     char delim = ':';
334 
335     while (std::getline(ss, key, delim))
336     {
337         std::getline(ss, value, delim);
338         i_map[key] = value;
339     }
340 }
341 
342 /** @brief Parse the TI info data area into map as hostboot data */
343 void parseHbTiInfo(std::map<std::string, std::string>& i_map,
344                    TiDataArea* i_tiDataArea)
345 {
346     if (nullptr == i_tiDataArea)
347     {
348         return;
349     }
350 
351     std::stringstream ss;
352 
353     ss << "0x00 TI Area Valid:" << std::setw(2) << std::setfill('0') << std::hex
354        << (int)i_tiDataArea->tiAreaValid << ":";
355     ss << "0x04 Reserved:" << std::setw(2) << std::setfill('0') << std::hex
356        << (int)i_tiDataArea->reserved1 << ":";
357     ss << "0x05 HB_Term. Type:" << std::setw(2) << std::setfill('0') << std::hex
358        << (int)i_tiDataArea->hbTerminateType << ":";
359     ss << "0x0c HB Dump Flag:" << std::setw(2) << std::setfill('0') << std::hex
360        << (int)i_tiDataArea->hbDumpFlag << ":";
361     ss << "0x0d Source:" << std::setw(2) << std::setfill('0') << std::hex
362        << (int)i_tiDataArea->source << ":";
363     ss << "0x10 HB Word 0:" << std::setw(8) << std::setfill('0') << std::hex
364        << be32toh(i_tiDataArea->srcWord12HbWord0) << ":";
365     ss << "0x14 HB Word 2:" << std::setw(8) << std::setfill('0') << std::hex
366        << be32toh(i_tiDataArea->srcWord13HbWord2) << ":";
367     ss << "0x18 HB Word 3:" << std::setw(8) << std::setfill('0') << std::hex
368        << be32toh(i_tiDataArea->srcWord14HbWord3) << ":";
369     ss << "0x1c HB Word 4:" << std::setw(8) << std::setfill('0') << std::hex
370        << be32toh(i_tiDataArea->srcWord15HbWord4) << ":";
371     ss << "0x20 HB Word 5:" << std::setw(8) << std::setfill('0') << std::hex
372        << be32toh(i_tiDataArea->srcWord16HbWord5) << ":";
373     ss << "0x24 HB Word 6:" << std::setw(8) << std::setfill('0') << std::hex
374        << be32toh(i_tiDataArea->srcWord17HbWord6) << ":";
375     ss << "0x28 HB Word 7:" << std::setw(8) << std::setfill('0') << std::hex
376        << be32toh(i_tiDataArea->srcWord18HbWord7) << ":";
377     ss << "0x2c HB Word 8:" << std::setw(8) << std::setfill('0') << std::hex
378        << be32toh(i_tiDataArea->srcWord19HbWord8) << ":";
379     ss << "0x30 error_data:" << std::setw(8) << std::setfill('0') << std::hex
380        << be32toh(i_tiDataArea->asciiData0) << ":";
381     ss << "0x34 EID:" << std::setw(8) << std::setfill('0') << std::hex
382        << be32toh(i_tiDataArea->asciiData1);
383 
384     std::string key, value;
385     char delim = ':';
386 
387     while (std::getline(ss, key, delim))
388     {
389         std::getline(ss, value, delim);
390         i_map[key] = value;
391     }
392 }
393 
394 /** @brief Read state of autoreboot propertyi via dbus */
395 bool autoRebootEnabled()
396 {
397     // Use dbus get-property interface to read the autoreboot property
398     auto bus = sdbusplus::bus::new_system();
399     auto method =
400         bus.new_method_call("xyz.openbmc_project.Settings",
401                             "/xyz/openbmc_project/control/host0/auto_reboot",
402                             "org.freedesktop.DBus.Properties", "Get");
403 
404     method.append("xyz.openbmc_project.Control.Boot.RebootPolicy",
405                   "AutoReboot");
406 
407     bool autoReboot = false; // assume autoreboot attribute not available
408 
409     try
410     {
411         auto reply = bus.call(method);
412 
413         std::variant<bool> result;
414         reply.read(result);
415         autoReboot = std::get<bool>(result);
416     }
417     catch (const sdbusplus::exception::SdBusError& e)
418     {
419         trace<level::INFO>("autoRebootEnbabled exception");
420         std::string traceMsg = std::string(e.what(), maxTraceLen);
421         trace<level::ERROR>(traceMsg.c_str());
422     }
423 
424     return autoReboot;
425 }
426 
427 /**
428  *  Callback for dump request properties change signal monitor
429  *
430  * @param[in] i_msg         Dbus message from the dbus match infrastructure
431  * @param[in] i_path        The object path we are monitoring
432  * @param[out] o_inProgress Used to break out of our dbus wait loop
433  * @reutn Always non-zero indicating no error, no cascading callbacks
434  */
435 uint dumpStatusChanged(sdbusplus::message::message& i_msg, std::string i_path,
436                        bool& o_inProgress)
437 {
438     // reply (msg) will be a property change message
439     std::string interface;
440     std::map<std::string, std::variant<std::string, uint8_t>> property;
441     i_msg.read(interface, property);
442 
443     // looking for property Status changes
444     std::string propertyType = "Status";
445     auto dumpStatus          = property.find(propertyType);
446 
447     if (dumpStatus != property.end())
448     {
449         const std::string* status =
450             std::get_if<std::string>(&(dumpStatus->second));
451 
452         if ((nullptr != status) && ("xyz.openbmc_project.Common.Progress."
453                                     "OperationStatus.InProgress" != *status))
454         {
455             // dump is done, trace some info and change in progress flag
456             trace<level::INFO>(i_path.c_str());
457             trace<level::INFO>((*status).c_str());
458             o_inProgress = false;
459         }
460     }
461 
462     return 1; // non-negative return code for successful callback
463 }
464 
465 /**
466  * Register a callback for dump progress status changes
467  *
468  * @param[in] i_path The object path of the dump to monitor
469  */
470 void monitorDump(const std::string& i_path)
471 {
472     bool inProgress = true; // callback will update this
473 
474     // setup the signal match rules and callback
475     std::string matchInterface = "xyz.openbmc_project.Common.Progress";
476     auto bus                   = sdbusplus::bus::new_system();
477 
478     std::unique_ptr<sdbusplus::bus::match_t> match =
479         std::make_unique<sdbusplus::bus::match_t>(
480             bus,
481             sdbusplus::bus::match::rules::propertiesChanged(
482                 i_path.c_str(), matchInterface.c_str()),
483             [&](auto& msg) {
484                 return dumpStatusChanged(msg, i_path, inProgress);
485             });
486 
487     // wait for dump status to be completed (complete == true)
488     trace<level::INFO>("hbdump requested");
489     while (true == inProgress)
490     {
491         bus.wait(0);
492         bus.process_discard();
493     }
494     trace<level::INFO>("hbdump completed");
495 }
496 
497 /** Request a dump from the dump manager */
498 void requestDump(const uint32_t logId)
499 {
500     constexpr auto path      = "/org/openpower/dump";
501     constexpr auto interface = "xyz.openbmc_project.Dump.Create";
502     constexpr auto function  = "CreateDump";
503 
504     sdbusplus::message::message method;
505 
506     if (0 == dbusMethod(path, interface, function, method))
507     {
508         try
509         {
510             // dbus call arguments
511             std::map<std::string, std::variant<std::string, uint64_t>>
512                 createParams;
513             createParams["com.ibm.Dump.Create.CreateParameters.DumpType"] =
514                 "com.ibm.Dump.Create.DumpType.Hostboot";
515             createParams["com.ibm.Dump.Create.CreateParameters.ErrorLogId"] =
516                 uint64_t(logId);
517             method.append(createParams);
518 
519             // using system dbus
520             auto bus      = sdbusplus::bus::new_system();
521             auto response = bus.call(method);
522 
523             // reply will be type dbus::ObjectPath
524             sdbusplus::message::object_path reply;
525             response.read(reply);
526 
527             // monitor dump progress
528             monitorDump(reply);
529         }
530         catch (const sdbusplus::exception::SdBusError& e)
531         {
532             trace<level::ERROR>("requestDump exception");
533             std::string traceMsg = std::string(e.what(), maxTraceLen);
534             trace<level::ERROR>(traceMsg.c_str());
535         }
536     }
537 }
538 
539 } // namespace attn
540