14a6ea6afSMatt Spinler /**
24a6ea6afSMatt Spinler * Copyright © 2018 IBM Corporation
34a6ea6afSMatt Spinler *
44a6ea6afSMatt Spinler * Licensed under the Apache License, Version 2.0 (the "License");
54a6ea6afSMatt Spinler * you may not use this file except in compliance with the License.
64a6ea6afSMatt Spinler * You may obtain a copy of the License at
74a6ea6afSMatt Spinler *
84a6ea6afSMatt Spinler * http://www.apache.org/licenses/LICENSE-2.0
94a6ea6afSMatt Spinler *
104a6ea6afSMatt Spinler * Unless required by applicable law or agreed to in writing, software
114a6ea6afSMatt Spinler * distributed under the License is distributed on an "AS IS" BASIS,
124a6ea6afSMatt Spinler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134a6ea6afSMatt Spinler * See the License for the specific language governing permissions and
144a6ea6afSMatt Spinler * limitations under the License.
154a6ea6afSMatt Spinler */
1666e07073SMatt Spinler #include "policy_find.hpp"
1766e07073SMatt Spinler
188e24dbc0SMatt Spinler #include <phosphor-logging/log.hpp>
19*6a2b8956SPatrick Williams
20e3be64f1SMatt Spinler #include <sstream>
214a6ea6afSMatt Spinler
224a6ea6afSMatt Spinler namespace ibm
234a6ea6afSMatt Spinler {
244a6ea6afSMatt Spinler namespace logging
254a6ea6afSMatt Spinler {
264a6ea6afSMatt Spinler namespace policy
274a6ea6afSMatt Spinler {
284a6ea6afSMatt Spinler
29c57aa4b9SMatt Spinler static constexpr auto HOST_EVENT = "org.open_power.Host.Error.Event";
30c57aa4b9SMatt Spinler
318e24dbc0SMatt Spinler /**
328e24dbc0SMatt Spinler * Returns a property value from a map of properties.
338e24dbc0SMatt Spinler *
348e24dbc0SMatt Spinler * @tparam - T the property data type
358e24dbc0SMatt Spinler * @param[in] properties - the property map
368e24dbc0SMatt Spinler * @param[in] name - the property name
378e24dbc0SMatt Spinler *
388e24dbc0SMatt Spinler * @return optional<T> - the property value
398e24dbc0SMatt Spinler */
408e24dbc0SMatt Spinler template <typename T>
getProperty(const DbusPropertyMap & properties,const std::string & name)4154ea3ad8SMatt Spinler std::optional<T> getProperty(const DbusPropertyMap& properties,
428e24dbc0SMatt Spinler const std::string& name)
438e24dbc0SMatt Spinler {
448e24dbc0SMatt Spinler auto prop = properties.find(name);
458e24dbc0SMatt Spinler
468e24dbc0SMatt Spinler if (prop != properties.end())
478e24dbc0SMatt Spinler {
48b5af3a3fSPatrick Williams return std::get<T>(prop->second);
498e24dbc0SMatt Spinler }
508e24dbc0SMatt Spinler
518e24dbc0SMatt Spinler return {};
528e24dbc0SMatt Spinler }
538e24dbc0SMatt Spinler
548e24dbc0SMatt Spinler /**
55e3be64f1SMatt Spinler * Finds a value in the AdditionalData property, which is
56e3be64f1SMatt Spinler * an array of strings in the form of:
57e3be64f1SMatt Spinler *
58e3be64f1SMatt Spinler * NAME=VALUE
59e3be64f1SMatt Spinler *
60e3be64f1SMatt Spinler * @param[in] additionalData - the AdditionalData property contents
61e3be64f1SMatt Spinler * @param[in] name - the name of the value to find
62e3be64f1SMatt Spinler *
63c57aa4b9SMatt Spinler * @return optional<std::string> - the data value. Will not be empty if found.
64e3be64f1SMatt Spinler */
6554ea3ad8SMatt Spinler std::optional<std::string>
getAdditionalDataItem(const std::vector<std::string> & additionalData,const std::string & name)66259e7277SMatt Spinler getAdditionalDataItem(const std::vector<std::string>& additionalData,
67e3be64f1SMatt Spinler const std::string& name)
68e3be64f1SMatt Spinler {
69c57aa4b9SMatt Spinler std::string value;
70c57aa4b9SMatt Spinler
71e3be64f1SMatt Spinler for (const auto& item : additionalData)
72e3be64f1SMatt Spinler {
73e3be64f1SMatt Spinler if (item.find(name + "=") != std::string::npos)
74e3be64f1SMatt Spinler {
75c57aa4b9SMatt Spinler value = item.substr(item.find('=') + 1);
76c57aa4b9SMatt Spinler if (!item.empty())
77c57aa4b9SMatt Spinler {
78c57aa4b9SMatt Spinler return value;
79c57aa4b9SMatt Spinler }
80e3be64f1SMatt Spinler }
81e3be64f1SMatt Spinler }
82e3be64f1SMatt Spinler
83e3be64f1SMatt Spinler return {};
84e3be64f1SMatt Spinler }
85e3be64f1SMatt Spinler
86e3be64f1SMatt Spinler /**
87c57aa4b9SMatt Spinler * Returns a string version of the severity from the PEL
88c57aa4b9SMatt Spinler * log in the extended SEL data from the host, where a PEL stands
89c57aa4b9SMatt Spinler * for 'Platform Event Log' and is an IBM standard for error logging
90c57aa4b9SMatt Spinler * that OpenPower host firmware uses.
91c57aa4b9SMatt Spinler *
92c57aa4b9SMatt Spinler * The severity is the 11th byte in the 'User Header' section in a PEL
93c57aa4b9SMatt Spinler * that starts at byte 48. We only need the first nibble, which signifies
94c57aa4b9SMatt Spinler * the type - 'Recovered', 'Predictive', 'Critical', etc.
95c57aa4b9SMatt Spinler *
96c57aa4b9SMatt Spinler * type value | type | returned severity string
97c57aa4b9SMatt Spinler * ------------------------------------
98c57aa4b9SMatt Spinler * 1 Recovered Informational
99c57aa4b9SMatt Spinler * 2 Predictive Warning
100c57aa4b9SMatt Spinler * everything else na Critical
101c57aa4b9SMatt Spinler *
102c57aa4b9SMatt Spinler * @param[in] data - the PEL string in the form of "00 11 22 33 4e ff"
103c57aa4b9SMatt Spinler *
104c57aa4b9SMatt Spinler * @return optional<std::string> - the severity string as listed above
105c57aa4b9SMatt Spinler */
getESELSeverity(const std::string & data)10654ea3ad8SMatt Spinler std::optional<std::string> getESELSeverity(const std::string& data)
107c57aa4b9SMatt Spinler {
108c57aa4b9SMatt Spinler // The User Header section starts at byte 48, and take into account
109c57aa4b9SMatt Spinler // the input data is a space separated string representation of HEX data.
110c57aa4b9SMatt Spinler static constexpr auto UH_OFFSET = 48 * 4;
111c57aa4b9SMatt Spinler
112c57aa4b9SMatt Spinler // The eye catcher is "UH"
113c57aa4b9SMatt Spinler static constexpr auto UH_EYECATCHER = "55 48";
114c57aa4b9SMatt Spinler
115c57aa4b9SMatt Spinler // The severity is the 11th byte in the section, and take into
116c57aa4b9SMatt Spinler // account a byte is "BB "
117c57aa4b9SMatt Spinler static constexpr auto UH_SEV_OFFSET = 10 * 3;
118c57aa4b9SMatt Spinler
119c57aa4b9SMatt Spinler std::string severity = "Critical";
120c57aa4b9SMatt Spinler
121c57aa4b9SMatt Spinler // The only values that don't map to "Critical"
122c57aa4b9SMatt Spinler const std::map<std::string, std::string> sevTypes{{"1", "Informational"},
123c57aa4b9SMatt Spinler {"2", "Warning"}};
124c57aa4b9SMatt Spinler if (data.size() <= (UH_OFFSET + UH_SEV_OFFSET))
125c57aa4b9SMatt Spinler {
126c57aa4b9SMatt Spinler return {};
127c57aa4b9SMatt Spinler }
128c57aa4b9SMatt Spinler
129c57aa4b9SMatt Spinler // Sanity check that the User Header section is there.
130c57aa4b9SMatt Spinler auto userHeader = data.substr(UH_OFFSET, 5);
131c57aa4b9SMatt Spinler if (userHeader.compare(UH_EYECATCHER))
132c57aa4b9SMatt Spinler {
133c57aa4b9SMatt Spinler return {};
134c57aa4b9SMatt Spinler }
135c57aa4b9SMatt Spinler
136c57aa4b9SMatt Spinler // The severity type nibble is a full byte in the string.
137c57aa4b9SMatt Spinler auto sevType = data.substr(UH_OFFSET + UH_SEV_OFFSET, 1);
138c57aa4b9SMatt Spinler
139c57aa4b9SMatt Spinler auto sev = sevTypes.find(sevType);
140c57aa4b9SMatt Spinler if (sev != sevTypes.end())
141c57aa4b9SMatt Spinler {
142c57aa4b9SMatt Spinler severity = sev->second;
143c57aa4b9SMatt Spinler };
144c57aa4b9SMatt Spinler
145c57aa4b9SMatt Spinler return severity;
146c57aa4b9SMatt Spinler }
147c57aa4b9SMatt Spinler
148c57aa4b9SMatt Spinler /**
149c57aa4b9SMatt Spinler * Returns the search modifier to use, but if it isn't found
150c57aa4b9SMatt Spinler * in the table then code should then call getSearchModifier()
151c57aa4b9SMatt Spinler * and try again.
152c57aa4b9SMatt Spinler *
153c57aa4b9SMatt Spinler * This is to be tolerant of the policy table not having
154c57aa4b9SMatt Spinler * entries for every device path or FRU callout, and trying
155c57aa4b9SMatt Spinler * again gives code a chance to find the more generic entries
156c57aa4b9SMatt Spinler * for those classes of errors rather than not being found
157c57aa4b9SMatt Spinler * at all.
158c57aa4b9SMatt Spinler *
159c57aa4b9SMatt Spinler * e.g. If the device path is missing in the table, then it
160c57aa4b9SMatt Spinler * can still find the generic "Failed to read from an I2C
161c57aa4b9SMatt Spinler * device" entry.
162c57aa4b9SMatt Spinler *
163c57aa4b9SMatt Spinler * @param[in] message- the error message, like xyz.A.Error.B
164c57aa4b9SMatt Spinler * @param[in] properties - the property map for the error
165c57aa4b9SMatt Spinler *
166c57aa4b9SMatt Spinler * @return string - the search modifier
167c57aa4b9SMatt Spinler * may be empty if none found
168c57aa4b9SMatt Spinler */
getSearchModifierFirstTry(const std::string & message,const DbusPropertyMap & properties)169c57aa4b9SMatt Spinler std::string getSearchModifierFirstTry(const std::string& message,
170c57aa4b9SMatt Spinler const DbusPropertyMap& properties)
171c57aa4b9SMatt Spinler {
172*6a2b8956SPatrick Williams auto data = getProperty<std::vector<std::string>>(properties,
173*6a2b8956SPatrick Williams "AdditionalData");
174c57aa4b9SMatt Spinler
175c57aa4b9SMatt Spinler if (!data)
176c57aa4b9SMatt Spinler {
177c57aa4b9SMatt Spinler return std::string{};
178c57aa4b9SMatt Spinler }
179c57aa4b9SMatt Spinler
180c57aa4b9SMatt Spinler // Try the called out device path as the search modifier
181c57aa4b9SMatt Spinler auto devPath = getAdditionalDataItem(*data, "CALLOUT_DEVICE_PATH");
182c57aa4b9SMatt Spinler if (devPath)
183c57aa4b9SMatt Spinler {
184c57aa4b9SMatt Spinler return *devPath;
185c57aa4b9SMatt Spinler }
186c57aa4b9SMatt Spinler
187c57aa4b9SMatt Spinler // For Host.Error.Event errors, try <callout>||<severity string>
188c57aa4b9SMatt Spinler // as the search modifier.
189c57aa4b9SMatt Spinler if (message == HOST_EVENT)
190c57aa4b9SMatt Spinler {
191c57aa4b9SMatt Spinler auto callout = getAdditionalDataItem(*data, "CALLOUT_INVENTORY_PATH");
192c57aa4b9SMatt Spinler if (callout)
193c57aa4b9SMatt Spinler {
194c57aa4b9SMatt Spinler auto selData = getAdditionalDataItem(*data, "ESEL");
195c57aa4b9SMatt Spinler if (selData)
196c57aa4b9SMatt Spinler {
197c57aa4b9SMatt Spinler auto severity = getESELSeverity(*selData);
198c57aa4b9SMatt Spinler if (severity)
199c57aa4b9SMatt Spinler {
200c57aa4b9SMatt Spinler return *callout + "||" + *severity;
201c57aa4b9SMatt Spinler }
202c57aa4b9SMatt Spinler }
203c57aa4b9SMatt Spinler }
204c57aa4b9SMatt Spinler }
205c57aa4b9SMatt Spinler
206c57aa4b9SMatt Spinler return std::string{};
207c57aa4b9SMatt Spinler }
208c57aa4b9SMatt Spinler
209c57aa4b9SMatt Spinler /**
2108e24dbc0SMatt Spinler * Returns the search modifier to use.
2118e24dbc0SMatt Spinler *
2128e24dbc0SMatt Spinler * The modifier is used when the error name itself isn't granular
2138e24dbc0SMatt Spinler * enough to find a policy table entry. The modifier is determined
2148e24dbc0SMatt Spinler * using rules provided by the IBM service team.
2158e24dbc0SMatt Spinler *
2168e24dbc0SMatt Spinler * Not all errors need a modifier, so this function isn't
2178e24dbc0SMatt Spinler * guaranteed to find one.
2188e24dbc0SMatt Spinler *
2198e24dbc0SMatt Spinler * @param[in] properties - the property map for the error
2208e24dbc0SMatt Spinler *
2218e24dbc0SMatt Spinler * @return string - the search modifier
2228e24dbc0SMatt Spinler * may be empty if none found
2238e24dbc0SMatt Spinler */
getSearchModifier(const DbusPropertyMap & properties)224259e7277SMatt Spinler auto getSearchModifier(const DbusPropertyMap& properties)
2258e24dbc0SMatt Spinler {
226e3be64f1SMatt Spinler // The modifier may be one of several things within the
227e3be64f1SMatt Spinler // AdditionalData property. Try them all until one
228e3be64f1SMatt Spinler // is found.
2298e24dbc0SMatt Spinler
230*6a2b8956SPatrick Williams auto data = getProperty<std::vector<std::string>>(properties,
231*6a2b8956SPatrick Williams "AdditionalData");
2328e24dbc0SMatt Spinler
233e3be64f1SMatt Spinler if (!data)
2348e24dbc0SMatt Spinler {
235e3be64f1SMatt Spinler return std::string{};
2368e24dbc0SMatt Spinler }
2378e24dbc0SMatt Spinler
238e3be64f1SMatt Spinler // AdditionalData fields where the value is the modifier
239e3be64f1SMatt Spinler static const std::vector<std::string> ADFields{"CALLOUT_INVENTORY_PATH",
240e3be64f1SMatt Spinler "RAIL_NAME", "INPUT_NAME"};
241e3be64f1SMatt Spinler
24254ea3ad8SMatt Spinler std::optional<std::string> mod;
243e3be64f1SMatt Spinler for (const auto& field : ADFields)
244e3be64f1SMatt Spinler {
245e3be64f1SMatt Spinler mod = getAdditionalDataItem(*data, field);
246c57aa4b9SMatt Spinler if (mod)
247e3be64f1SMatt Spinler {
248e3be64f1SMatt Spinler return *mod;
249e3be64f1SMatt Spinler }
250e3be64f1SMatt Spinler }
251e3be64f1SMatt Spinler
252e3be64f1SMatt Spinler // Next are the AdditionalData fields where the value needs
253e3be64f1SMatt Spinler // to be massaged to get the modifier.
254e3be64f1SMatt Spinler
255e3be64f1SMatt Spinler // A device path, but we only care about the type
256e3be64f1SMatt Spinler mod = getAdditionalDataItem(*data, "CALLOUT_DEVICE_PATH");
257e3be64f1SMatt Spinler if (mod)
258e3be64f1SMatt Spinler {
259e3be64f1SMatt Spinler // The table only handles I2C and FSI
260e3be64f1SMatt Spinler if ((*mod).find("i2c") != std::string::npos)
261e3be64f1SMatt Spinler {
262e3be64f1SMatt Spinler return std::string{"I2C"};
263e3be64f1SMatt Spinler }
264e3be64f1SMatt Spinler else if ((*mod).find("fsi") != std::string::npos)
265e3be64f1SMatt Spinler {
266e3be64f1SMatt Spinler return std::string{"FSI"};
267e3be64f1SMatt Spinler }
268e3be64f1SMatt Spinler }
269e3be64f1SMatt Spinler
270e3be64f1SMatt Spinler // A hostboot procedure ID
271e3be64f1SMatt Spinler mod = getAdditionalDataItem(*data, "PROCEDURE");
272e3be64f1SMatt Spinler if (mod)
273e3be64f1SMatt Spinler {
274e3be64f1SMatt Spinler // Convert decimal (e.g. 109) to hex (e.g. 6D)
275e3be64f1SMatt Spinler std::ostringstream stream;
276e3be64f1SMatt Spinler try
277e3be64f1SMatt Spinler {
278e3be64f1SMatt Spinler stream << std::hex << std::stoul((*mod).c_str());
279e3be64f1SMatt Spinler auto value = stream.str();
280e3be64f1SMatt Spinler
281e3be64f1SMatt Spinler if (!value.empty())
282e3be64f1SMatt Spinler {
283259e7277SMatt Spinler std::transform(value.begin(), value.end(), value.begin(),
284259e7277SMatt Spinler toupper);
285e3be64f1SMatt Spinler return value;
286e3be64f1SMatt Spinler }
287e3be64f1SMatt Spinler }
288bf9ea8adSPatrick Williams catch (const std::exception& e)
289e3be64f1SMatt Spinler {
290e3be64f1SMatt Spinler using namespace phosphor::logging;
291e3be64f1SMatt Spinler log<level::ERR>("Invalid PROCEDURE value found",
29253530265SJoseph Reynolds entry("PROCEDURE=%s", mod->c_str()));
293e3be64f1SMatt Spinler }
294e3be64f1SMatt Spinler }
295e3be64f1SMatt Spinler
296e3be64f1SMatt Spinler return std::string{};
2978e24dbc0SMatt Spinler }
2988e24dbc0SMatt Spinler
find(const policy::Table & policy,const DbusPropertyMap & errorLogProperties)299259e7277SMatt Spinler PolicyProps find(const policy::Table& policy,
3004a6ea6afSMatt Spinler const DbusPropertyMap& errorLogProperties)
3014a6ea6afSMatt Spinler {
302259e7277SMatt Spinler auto errorMsg = getProperty<std::string>(errorLogProperties,
303259e7277SMatt Spinler "Message"); // e.g. xyz.X.Error.Y
3048e24dbc0SMatt Spinler if (errorMsg)
3058e24dbc0SMatt Spinler {
306c57aa4b9SMatt Spinler FindResult result;
3078e24dbc0SMatt Spinler
308c57aa4b9SMatt Spinler // Try with the FirstTry modifier first, and then the regular one.
309c57aa4b9SMatt Spinler
310*6a2b8956SPatrick Williams auto modifier = getSearchModifierFirstTry(*errorMsg,
311*6a2b8956SPatrick Williams errorLogProperties);
312c57aa4b9SMatt Spinler
313c57aa4b9SMatt Spinler if (!modifier.empty())
314c57aa4b9SMatt Spinler {
315c57aa4b9SMatt Spinler result = policy.find(*errorMsg, modifier);
316c57aa4b9SMatt Spinler }
317c57aa4b9SMatt Spinler
318c57aa4b9SMatt Spinler if (!result)
319c57aa4b9SMatt Spinler {
320c57aa4b9SMatt Spinler modifier = getSearchModifier(errorLogProperties);
321c57aa4b9SMatt Spinler
322c57aa4b9SMatt Spinler result = policy.find(*errorMsg, modifier);
323c57aa4b9SMatt Spinler }
3248e24dbc0SMatt Spinler
3258e24dbc0SMatt Spinler if (result)
3268e24dbc0SMatt Spinler {
3278e24dbc0SMatt Spinler return {(*result).get().ceid, (*result).get().msg};
3288e24dbc0SMatt Spinler }
3298e24dbc0SMatt Spinler }
3308e24dbc0SMatt Spinler else
3318e24dbc0SMatt Spinler {
3328e24dbc0SMatt Spinler using namespace phosphor::logging;
3338e24dbc0SMatt Spinler log<level::ERR>("No Message metadata found in an error");
3348e24dbc0SMatt Spinler }
3354a6ea6afSMatt Spinler
3364a6ea6afSMatt Spinler return {policy.defaultEID(), policy.defaultMsg()};
3374a6ea6afSMatt Spinler }
33866e07073SMatt Spinler } // namespace policy
33966e07073SMatt Spinler } // namespace logging
34066e07073SMatt Spinler } // namespace ibm
341