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