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