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