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