xref: /openbmc/ibm-logging/policy_find.cpp (revision 6a2b8956487404dd2f7e4aa06055c24a2b7271e1)
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