1 #include <attn/attn_common.hpp>
2 #include <attn/attn_handler.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     // check TI data area if it is available
28     if (nullptr != i_tiDataArea)
29     {
30         // HB v. PHYP TI logic: Only hosboot will fill in hbTerminateType
31         // and it will be non-zero. Only hostboot will fill out source and
32         // it it will be non-zero. Only PHYP will fill in srcFormat and it
33         // will be non-zero.
34         if ((0 == i_tiDataArea->hbTerminateType) &&
35             (0 == i_tiDataArea->source) && (0 != i_tiDataArea->srcFormat))
36         {
37             handlePhypTi(i_tiDataArea);
38         }
39         else
40         {
41             handleHbTi(i_tiDataArea);
42         }
43     }
44     else
45     {
46         // TI data was not available This should not happen since we provide
47         // a default TI info in the case where get TI info was not successful.
48         eventAttentionFail((int)AttnSection::tiHandler | ATTN_INFO_NULL);
49         rc = RC_NOT_HANDLED;
50     }
51 
52     return rc;
53 }
54 
55 /**
56  * @brief Handle a PHYP terminate immediate special attention
57  *
58  * The TI info data area will contain information pertaining to the TI
59  * condition. We will wither quiesce the host or initiate a MPIPL depending
60  * depending on the auto reboot configuration. We will also create a PEL which
61  * will contain the TI info data and FFDC data captured in the system journal.
62  *
63  * @param i_tiDataArea pointer to TI information filled in by hostboot
64  */
65 void handlePhypTi(TiDataArea* i_tiDataArea)
66 {
67     trace<level::INFO>("PHYP TI");
68 
69     if (autoRebootEnabled())
70     {
71         // If autoreboot is enabled we will start crash (mpipl) mode target
72         transitionHost(HostState::Crash);
73     }
74     else
75     {
76         // If autoreboot is disabled we will quiesce the host
77         transitionHost(HostState::Quiesce);
78     }
79 
80     // gather additional data for PEL
81     std::map<std::string, std::string> tiAdditionalData;
82 
83     if (nullptr != i_tiDataArea)
84     {
85         parsePhypOpalTiInfo(tiAdditionalData, i_tiDataArea);
86 
87         tiAdditionalData["Subsystem"] =
88             std::to_string(static_cast<uint8_t>(pel::SubsystemID::hypervisor));
89 
90         // Copy all ascii src chars to additional data
91         char srcChar[33]; // 32 ascii chars + null term
92         memcpy(srcChar, &(i_tiDataArea->asciiData0), 32);
93         srcChar[32]                  = 0;
94         tiAdditionalData["SrcAscii"] = std::string{srcChar};
95 
96         // TI event
97         eventTerminate(tiAdditionalData, (char*)i_tiDataArea);
98     }
99     else
100     {
101         // TI data was not available This should not happen since we provide
102         // a default TI info in the case where get TI info was not successful.
103         eventAttentionFail((int)AttnSection::handlePhypTi | ATTN_INFO_NULL);
104     }
105 }
106 
107 /**
108  * @brief Handle a hostboot terminate immediate special attention
109  *
110  * The TI info data area will contain information pertaining to the TI
111  * condition. The course of action to take regarding the host state will
112  * depend on the contents of the TI info data area. We will also create a
113  * PEL containing the TI info data and FFDC data captured in the system
114  * journal.
115  *
116  * @param i_tiDataArea pointer to TI information filled in by hostboot
117  */
118 void handleHbTi(TiDataArea* i_tiDataArea)
119 {
120     trace<level::INFO>("HB TI");
121 
122     bool hbDumpRequested = true; // HB dump is common case
123     bool generatePel     = true; // assume PEL will be created
124     bool terminateHost   = true; // transition host state
125 
126     // handle specific hostboot reason codes
127     if (nullptr != i_tiDataArea)
128     {
129         std::stringstream ss; // stream object for tracing
130         std::string strobj;   // string object for tracing
131 
132         switch (i_tiDataArea->hbTerminateType)
133         {
134             case TI_WITH_PLID:
135             case TI_WITH_EID:
136 
137                 // trace this value
138                 ss.str(std::string()); // empty the stream
139                 ss.clear();            // clear the stream
140                 ss << "TI with PLID/EID: " << std::hex << std::showbase
141                    << std::setw(8) << std::setfill('0')
142                    << be32toh(i_tiDataArea->asciiData1);
143                 strobj = ss.str();
144                 trace<level::INFO>(strobj.c_str());
145 
146                 // see if HB dump is requested
147                 if (0 == i_tiDataArea->hbDumpFlag)
148                 {
149                     hbDumpRequested = false; // no HB dump requested
150                 }
151                 break;
152             case TI_WITH_SRC:
153                 // Reason code is byte 2 and 3 of 4 byte srcWord12HbWord0
154                 uint16_t reasonCode = be32toh(i_tiDataArea->srcWord12HbWord0);
155 
156                 // trace this value
157                 ss.str(std::string()); // empty the stream
158                 ss.clear();            // clear the stream
159                 ss << "TI with SRC: " << std::hex << std::showbase
160                    << std::setw(4) << std::setfill('0') << (int)reasonCode;
161                 strobj = ss.str();
162                 trace<level::INFO>(strobj.c_str());
163 
164                 switch (reasonCode)
165                 {
166                     case HB_SRC_SHUTDOWN_REQUEST:
167                         trace<level::INFO>("shutdown request");
168                         generatePel     = false;
169                         hbDumpRequested = false;
170                         break;
171                     case HB_SRC_KEY_TRANSITION:
172                         // Note: Should never see this so lets leave
173                         // hbDumpRequested == true so we can figure out why
174                         // we are here.
175                         trace<level::INFO>("key transition");
176                         terminateHost = false;
177                         break;
178                     case HB_SRC_INSUFFICIENT_HW:
179                         trace<level::INFO>("insufficient hardware");
180                         break;
181                     case HB_SRC_TPM_FAIL:
182                         trace<level::INFO>("TPM fail");
183                         break;
184                     case HB_SRC_ROM_VERIFY:
185                         trace<level::INFO>("ROM verify");
186                         break;
187                     case HB_SRC_EXT_MISMATCH:
188                         trace<level::INFO>("EXT mismatch");
189                         break;
190                     case HB_SRC_ECC_UE:
191                         trace<level::INFO>("ECC UE");
192                         break;
193                     case HB_SRC_UNSUPPORTED_MODE:
194                         trace<level::INFO>("unsupported mode");
195                         break;
196                     case HB_SRC_UNSUPPORTED_SFCRANGE:
197                         trace<level::INFO>("unsupported SFC range");
198                         break;
199                     case HB_SRC_PARTITION_TABLE:
200                         trace<level::INFO>("partition table invalid");
201                         break;
202                     case HB_SRC_UNSUPPORTED_HARDWARE:
203                         trace<level::INFO>("unsupported hardware");
204                         break;
205                     case HB_SRC_PNOR_CORRUPTION:
206                         trace<level::INFO>("PNOR corruption");
207                         break;
208                     default:
209                         trace<level::INFO>("reason: other");
210                 }
211 
212                 break;
213         }
214     }
215 
216     if (true == terminateHost)
217     {
218         // if hostboot dump is requested initiate dump
219         if (hbDumpRequested)
220         {
221             // Until HB dump support available just quiesce the host - once
222             // dump support is available the dump component will transition
223             // (ipl/halt) the host.
224             transitionHost(HostState::Quiesce);
225         }
226         else
227         {
228             // Quiese the host - when the host is quiesced it will either
229             // "halt" or IPL depending on autoreboot setting.
230             transitionHost(HostState::Quiesce);
231         }
232     }
233 
234     // gather additional data for PEL
235     std::map<std::string, std::string> tiAdditionalData;
236 
237     if (nullptr != i_tiDataArea)
238     {
239         parseHbTiInfo(tiAdditionalData, i_tiDataArea);
240 
241         if (true == generatePel)
242         {
243             tiAdditionalData["Subsystem"] = std::to_string(
244                 static_cast<uint8_t>(pel::SubsystemID::hostboot));
245 
246             // Translate hex src value to ascii. This results in an 8 character
247             // SRC (hostboot SRC is 32 bits)
248             std::stringstream src;
249             src << std::setw(8) << std::setfill('0') << std::uppercase
250                 << std::hex << be32toh(i_tiDataArea->srcWord12HbWord0);
251             tiAdditionalData["SrcAscii"] = src.str();
252 
253             eventTerminate(tiAdditionalData, (char*)i_tiDataArea);
254         }
255     }
256     else
257     {
258         // TI data was not available This should not happen since we provide
259         // a default TI info in the case where get TI info was not successful.
260         eventAttentionFail((int)AttnSection::handleHbTi | ATTN_INFO_NULL);
261     }
262 }
263 
264 /** @brief Parse the TI info data area into map as PHYP/OPAL data */
265 void parsePhypOpalTiInfo(std::map<std::string, std::string>& i_map,
266                          TiDataArea* i_tiDataArea)
267 {
268     if (nullptr == i_tiDataArea)
269     {
270         return;
271     }
272 
273     std::stringstream ss;
274 
275     ss << std::hex << std::showbase;
276     ss << "0x00 TI Area Valid:" << (int)i_tiDataArea->tiAreaValid << ":";
277     ss << "0x01 Command:" << (int)i_tiDataArea->command << ":";
278     ss << "0x02 Num. Data Bytes:" << be16toh(i_tiDataArea->numDataBytes) << ":";
279     ss << "0x04 Reserved:" << (int)i_tiDataArea->reserved1 << ":";
280     ss << "0x06 HWDump Type:" << be16toh(i_tiDataArea->hardwareDumpType) << ":";
281     ss << "0x08 SRC Format:" << (int)i_tiDataArea->srcFormat << ":";
282     ss << "0x09 SRC Flags:" << (int)i_tiDataArea->srcFlags << ":";
283     ss << "0x0a Num. ASCII Words:" << (int)i_tiDataArea->numAsciiWords << ":";
284     ss << "0x0b Num. Hex Words:" << (int)i_tiDataArea->numHexWords << ":";
285     ss << "0x0e Length of SRC:" << be16toh(i_tiDataArea->lenSrc) << ":";
286     ss << "0x10 SRC Word 12:" << be32toh(i_tiDataArea->srcWord12HbWord0) << ":";
287     ss << "0x14 SRC Word 13:" << be32toh(i_tiDataArea->srcWord13HbWord2) << ":";
288     ss << "0x18 SRC Word 14:" << be32toh(i_tiDataArea->srcWord14HbWord3) << ":";
289     ss << "0x1c SRC Word 15:" << be32toh(i_tiDataArea->srcWord15HbWord4) << ":";
290     ss << "0x20 SRC Word 16:" << be32toh(i_tiDataArea->srcWord16HbWord5) << ":";
291     ss << "0x24 SRC Word 17:" << be32toh(i_tiDataArea->srcWord17HbWord6) << ":";
292     ss << "0x28 SRC Word 18:" << be32toh(i_tiDataArea->srcWord18HbWord7) << ":";
293     ss << "0x2c SRC Word 19:" << be32toh(i_tiDataArea->srcWord19HbWord8) << ":";
294     ss << "0x30 ASCII Data:" << be32toh(i_tiDataArea->asciiData0) << ":";
295     ss << "0x34 ASCII Data:" << be32toh(i_tiDataArea->asciiData1) << ":";
296     ss << "0x38 ASCII Data:" << be32toh(i_tiDataArea->asciiData2) << ":";
297     ss << "0x3c ASCII Data:" << be32toh(i_tiDataArea->asciiData3) << ":";
298     ss << "0x40 ASCII Data:" << be32toh(i_tiDataArea->asciiData4) << ":";
299     ss << "0x44 ASCII Data:" << be32toh(i_tiDataArea->asciiData5) << ":";
300     ss << "0x48 ASCII Data:" << be32toh(i_tiDataArea->asciiData6) << ":";
301     ss << "0x4c ASCII Data:" << be32toh(i_tiDataArea->asciiData7) << ":";
302     ss << "0x50 Location:" << (int)i_tiDataArea->location << ":";
303     ss << "0x51 Code Sections:" << (int)i_tiDataArea->codeSection << ":";
304     ss << "0x52 Additional Size:" << (int)i_tiDataArea->additionalSize << ":";
305     ss << "0x53 Additional Data:" << (int)i_tiDataArea->andData;
306 
307     std::string key, value;
308     char delim = ':';
309 
310     while (std::getline(ss, key, delim))
311     {
312         std::getline(ss, value, delim);
313         i_map[key] = value;
314     }
315 }
316 
317 /** @brief Parse the TI info data area into map as hostboot data */
318 void parseHbTiInfo(std::map<std::string, std::string>& i_map,
319                    TiDataArea* i_tiDataArea)
320 {
321     if (nullptr == i_tiDataArea)
322     {
323         return;
324     }
325 
326     std::stringstream ss;
327 
328     ss << std::hex << std::showbase;
329     ss << "0x00 TI Area Valid:" << (int)i_tiDataArea->tiAreaValid << ":";
330     ss << "0x04 Reserved:" << (int)i_tiDataArea->reserved1 << ":";
331     ss << "0x05 HB_Term. Type:" << (int)i_tiDataArea->hbTerminateType << ":";
332     ss << "0x0c HB Dump Flag:" << (int)i_tiDataArea->hbDumpFlag << ":";
333     ss << "0x0d Source:" << (int)i_tiDataArea->source << ":";
334     ss << "0x10 HB Word 0:" << be32toh(i_tiDataArea->srcWord12HbWord0) << ":";
335     ss << "0x14 HB Word 2:" << be32toh(i_tiDataArea->srcWord13HbWord2) << ":";
336     ss << "0x18 HB Word 3:" << be32toh(i_tiDataArea->srcWord14HbWord3) << ":";
337     ss << "0x1c HB Word 4:" << be32toh(i_tiDataArea->srcWord15HbWord4) << ":";
338     ss << "0x20 HB Word 5:" << be32toh(i_tiDataArea->srcWord16HbWord5) << ":";
339     ss << "0x24 HB Word 6:" << be32toh(i_tiDataArea->srcWord17HbWord6) << ":";
340     ss << "0x28 HB Word 7:" << be32toh(i_tiDataArea->srcWord18HbWord7) << ":";
341     ss << "0x2c HB Word 8:" << be32toh(i_tiDataArea->srcWord19HbWord8) << ":";
342     ss << "0x30 error_data:" << be32toh(i_tiDataArea->asciiData0) << ":";
343     ss << "0x34 EID:" << be32toh(i_tiDataArea->asciiData1);
344 
345     std::string key, value;
346     char delim = ':';
347 
348     while (std::getline(ss, key, delim))
349     {
350         std::getline(ss, value, delim);
351         i_map[key] = value;
352     }
353 }
354 
355 /** @brief Read state of autoreboot propertyi via dbus */
356 bool autoRebootEnabled()
357 {
358     // Use dbus get-property interface to read the autoreboot property
359     auto bus = sdbusplus::bus::new_system();
360     auto method =
361         bus.new_method_call("xyz.openbmc_project.Settings",
362                             "/xyz/openbmc_project/control/host0/auto_reboot",
363                             "org.freedesktop.DBus.Properties", "Get");
364 
365     method.append("xyz.openbmc_project.Control.Boot.RebootPolicy",
366                   "AutoReboot");
367 
368     bool autoReboot = false; // assume autoreboot attribute not available
369 
370     try
371     {
372         auto reply = bus.call(method);
373 
374         std::variant<bool> result;
375         reply.read(result);
376         autoReboot = std::get<bool>(result);
377     }
378     catch (const sdbusplus::exception::SdBusError& e)
379     {
380         trace<level::INFO>("autoRebootEnbabled exception");
381         std::string traceMsg = std::string(e.what(), maxTraceLen);
382         trace<level::ERROR>(traceMsg.c_str());
383     }
384 
385     return autoReboot;
386 }
387 
388 } // namespace attn
389