1 /**
2  * Copyright © 2019 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "user_header.hpp"
17 
18 #include "json_utils.hpp"
19 #include "pel_types.hpp"
20 #include "pel_values.hpp"
21 #include "severity.hpp"
22 
23 #include <fmt/format.h>
24 
25 #include <phosphor-logging/log.hpp>
26 
27 #include <iostream>
28 
29 namespace openpower
30 {
31 namespace pels
32 {
33 
34 namespace pv = openpower::pels::pel_values;
35 using namespace phosphor::logging;
36 
37 void UserHeader::unflatten(Stream& stream)
38 {
39     stream >> _header >> _eventSubsystem >> _eventScope >> _eventSeverity >>
40         _eventType >> _reserved4Byte1 >> _problemDomain >> _problemVector >>
41         _actionFlags >> _states;
42 }
43 
44 void UserHeader::flatten(Stream& stream) const
45 {
46     stream << _header << _eventSubsystem << _eventScope << _eventSeverity
47            << _eventType << _reserved4Byte1 << _problemDomain << _problemVector
48            << _actionFlags << _states;
49 }
50 
51 UserHeader::UserHeader(const message::Entry& entry,
52                        phosphor::logging::Entry::Level severity,
53                        const AdditionalData& additionalData,
54                        const DataInterfaceBase& dataIface)
55 {
56     _header.id = static_cast<uint16_t>(SectionID::userHeader);
57     _header.size = UserHeader::flattenedSize();
58     _header.version = userHeaderVersion;
59     _header.subType = 0;
60     _header.componentID = static_cast<uint16_t>(ComponentID::phosphorLogging);
61 
62     std::optional<uint8_t> subsys;
63 
64     // Check for additional data - PEL_SUBSYSTEM
65     auto ss = additionalData.getValue("PEL_SUBSYSTEM");
66     if (ss)
67     {
68         auto eventSubsystem = std::stoul(*ss, NULL, 16);
69         std::string subsystemString = pv::getValue(eventSubsystem,
70                                                    pel_values::subsystemValues);
71         if (subsystemString == "invalid")
72         {
73             log<level::WARNING>(
74                 fmt::format(
75                     "UH: Invalid SubSystem value in PEL_SUBSYSTEM: {:#X}",
76                     eventSubsystem)
77                     .c_str());
78         }
79         else
80         {
81             subsys = eventSubsystem;
82         }
83     }
84     else
85     {
86         subsys = entry.subsystem;
87     }
88 
89     if (subsys)
90     {
91         _eventSubsystem = *subsys;
92     }
93     else
94     {
95         // Gotta use something, how about 'others'.
96         log<level::WARNING>(
97             "No PEL subystem value supplied for error, using 'others'");
98         _eventSubsystem = 0x70;
99     }
100 
101     _eventScope = entry.eventScope.value_or(
102         static_cast<uint8_t>(EventScope::entirePlatform));
103 
104     {
105         bool mfgSevStatus = false;
106         bool mfgActionFlagStatus = false;
107 
108         // Get the mfg severity & action flags
109         if (entry.mfgSeverity || entry.mfgActionFlags)
110         {
111             std::optional<uint8_t> sev = std::nullopt;
112             uint16_t val = 0;
113 
114             if (entry.mfgSeverity)
115             {
116                 // Find the mf severity possibly dependent on the system type.
117                 sev = getSeverity(entry.mfgSeverity.value(), dataIface);
118             }
119 
120             if (entry.mfgActionFlags)
121             {
122                 // Find the mfg action flags
123                 val = entry.mfgActionFlags.value();
124             }
125 
126             if (sev || val)
127             {
128                 bool mfgProp = dataIface.getQuiesceOnError();
129                 if (mfgProp)
130                 {
131                     if (sev)
132                     {
133                         _eventSeverity = *sev;
134                         mfgSevStatus = true;
135                     }
136 
137                     if (val)
138                     {
139                         _actionFlags = val;
140                         mfgActionFlagStatus = true;
141                     }
142                 }
143             }
144         }
145 
146         if (!mfgSevStatus)
147         {
148             // Get the severity from the registry if it's there, otherwise get
149             // it from the OpenBMC event log severity value.
150             if (!entry.severity)
151             {
152                 _eventSeverity = convertOBMCSeverityToPEL(severity);
153             }
154             else
155             {
156                 // Find the severity possibly dependent on the system type.
157                 auto sev = getSeverity(entry.severity.value(), dataIface);
158                 if (sev)
159                 {
160                     _eventSeverity = *sev;
161                 }
162                 else
163                 {
164                     // Either someone  screwed up the message registry
165                     // or getSystemNames failed.
166                     log<level::ERR>(
167                         "Failed finding the severity in the message registry",
168                         phosphor::logging::entry("ERROR=%s",
169                                                  entry.name.c_str()));
170 
171                     // Have to choose something, just use informational.
172                     _eventSeverity = 0;
173                 }
174             }
175         }
176 
177         // Convert Critical error (0x50) to Critical Error-System Termination
178         // (0x51), if the AdditionalData is set to SYSTEM_TERM
179         auto sevLevel = additionalData.getValue("SEVERITY_DETAIL");
180         if ((_eventSeverity & 0xF0) == 0x50)
181         {
182             if (sevLevel.value_or("") == "SYSTEM_TERM")
183             {
184                 // Change to Critical Error, System Termination
185                 _eventSeverity = 0x51;
186             }
187         }
188 
189         if (entry.eventType)
190         {
191             _eventType = *entry.eventType;
192         }
193         else
194         {
195             // There are different default event types for info errors
196             // vs non info ones.
197             auto sevType = static_cast<SeverityType>(_eventSeverity & 0xF0);
198             _eventType =
199                 (sevType == SeverityType::nonError)
200                     ? static_cast<uint8_t>(EventType::miscInformational)
201                     : static_cast<uint8_t>(EventType::notApplicable);
202         }
203 
204         _reserved4Byte1 = 0;
205 
206         // No uses for problem domain or vector
207         _problemDomain = 0;
208         _problemVector = 0;
209 
210         // These will be set in pel_rules::check() if they're still
211         // at the default value.
212         if (!mfgActionFlagStatus)
213         {
214             _actionFlags = entry.actionFlags.value_or(actionFlagsDefault);
215         }
216 
217         _states = 0;
218 
219         _valid = true;
220     }
221 }
222 
223 UserHeader::UserHeader(Stream& pel)
224 {
225     try
226     {
227         unflatten(pel);
228         validate();
229     }
230     catch (const std::exception& e)
231     {
232         log<level::ERR>(
233             fmt::format("Cannot unflatten user header: {}", e.what()).c_str());
234         _valid = false;
235     }
236 }
237 
238 void UserHeader::validate()
239 {
240     bool failed = false;
241     if (header().id != static_cast<uint16_t>(SectionID::userHeader))
242     {
243         log<level::ERR>(
244             fmt::format("Invalid user header section ID: {0:#x}", header().id)
245                 .c_str());
246         failed = true;
247     }
248 
249     if (header().version != userHeaderVersion)
250     {
251         log<level::ERR>(
252             fmt::format("Invalid user header version: {0:#x}", header().version)
253                 .c_str());
254         failed = true;
255     }
256 
257     _valid = (failed) ? false : true;
258 }
259 
260 std::optional<std::string> UserHeader::getJSON() const
261 {
262     std::string severity;
263     std::string subsystem;
264     std::string eventScope;
265     std::string eventType;
266     std::vector<std::string> actionFlags;
267     severity = pv::getValue(_eventSeverity, pel_values::severityValues);
268     subsystem = pv::getValue(_eventSubsystem, pel_values::subsystemValues);
269     eventScope = pv::getValue(_eventScope, pel_values::eventScopeValues);
270     eventType = pv::getValue(_eventType, pel_values::eventTypeValues);
271     actionFlags = pv::getValuesBitwise(_actionFlags,
272                                        pel_values::actionFlagsValues);
273 
274     std::string hostState{"Invalid"};
275     std::string hmcState{"Invalid"};
276     auto iter = pv::transmissionStates.find(
277         static_cast<TransmissionState>(hostTransmissionState()));
278     if (iter != pv::transmissionStates.end())
279     {
280         hostState = iter->second;
281     }
282     auto iter1 = pv::transmissionStates.find(
283         static_cast<TransmissionState>(hmcTransmissionState()));
284     if (iter1 != pv::transmissionStates.end())
285     {
286         hmcState = iter1->second;
287     }
288 
289     std::string uh;
290     jsonInsert(uh, pv::sectionVer, getNumberString("%d", userHeaderVersion), 1);
291     jsonInsert(uh, pv::subSection, getNumberString("%d", _header.subType), 1);
292     jsonInsert(uh, "Log Committed by",
293                getNumberString("0x%X", _header.componentID), 1);
294     jsonInsert(uh, "Subsystem", subsystem, 1);
295     jsonInsert(uh, "Event Scope", eventScope, 1);
296     jsonInsert(uh, "Event Severity", severity, 1);
297     jsonInsert(uh, "Event Type", eventType, 1);
298     jsonInsertArray(uh, "Action Flags", actionFlags, 1);
299     jsonInsert(uh, "Host Transmission", hostState, 1);
300     jsonInsert(uh, "HMC Transmission", hmcState, 1);
301     uh.erase(uh.size() - 2);
302     return uh;
303 }
304 
305 std::optional<uint8_t> UserHeader::getSeverity(
306     const std::vector<message::RegistrySeverity>& severities,
307     const DataInterfaceBase& dataIface) const
308 {
309     const uint8_t* s = nullptr;
310     std::vector<std::string> systemNames;
311 
312     // getSystemNames makes D-Bus calls, so only call it if we
313     // know we'll need it because there is a system name in the sev list
314     if (std::any_of(severities.begin(), severities.end(),
315                     [](const auto& sev) { return !sev.system.empty(); }))
316     {
317         try
318         {
319             systemNames = dataIface.getSystemNames();
320         }
321         catch (const std::exception& e)
322         {
323             log<level::ERR>("Failed trying to look up system names on D-Bus",
324                             entry("ERROR=%s", e.what()));
325             return std::nullopt;
326         }
327     }
328 
329     // Find the severity to use for this system type, or use the default
330     // entry (where no system type is specified).
331     for (const auto& sev : severities)
332     {
333         if (std::find(systemNames.begin(), systemNames.end(), sev.system) !=
334             systemNames.end())
335         {
336             s = &sev.severity;
337             break;
338         }
339         else if (sev.system.empty())
340         {
341             s = &sev.severity;
342         }
343     }
344 
345     if (s)
346     {
347         return *s;
348     }
349 
350     return std::nullopt;
351 }
352 
353 } // namespace pels
354 } // namespace openpower
355