xref: /openbmc/openpower-vpd-parser/vpd-manager/src/parser.cpp (revision 480807cf8a3ef27eef4063bac422d69343b123d1)
1 #include "parser.hpp"
2 
3 #include "constants.hpp"
4 #include "event_logger.hpp"
5 
6 #include <utility/dbus_utility.hpp>
7 #include <utility/json_utility.hpp>
8 #include <utility/vpd_specific_utility.hpp>
9 
10 #include <fstream>
11 
12 namespace vpd
13 {
14 Parser::Parser(const std::string& vpdFilePath, nlohmann::json parsedJson) :
15     m_vpdFilePath(vpdFilePath), m_parsedJson(parsedJson)
16 {
17     std::error_code l_errCode;
18 
19     // ToDo: Add minimum file size check in all the concert praser classes,
20     // depends on their VPD type.
21     if (!std::filesystem::exists(m_vpdFilePath, l_errCode))
22     {
23         std::string l_message{"Parser object creation failed, file [" +
24                               m_vpdFilePath + "] doesn't exists."};
25 
26         if (l_errCode)
27         {
28             l_message += " Error message: " + l_errCode.message();
29         }
30 
31         throw std::runtime_error(l_message);
32     }
33 
34     // Read VPD offset if applicable.
35     if (!m_parsedJson.empty())
36     {
37         uint16_t l_errorCode = 0;
38 
39         m_vpdStartOffset =
40             jsonUtility::getVPDOffset(m_parsedJson, vpdFilePath, l_errorCode);
41 
42         if (l_errorCode)
43         {
44             logging::logMessage(
45                 "Failed to get vpd offset for path [" + m_vpdFilePath +
46                 "], error: " + vpdSpecificUtility::getErrCodeMsg(l_errorCode));
47         }
48     }
49 }
50 
51 std::shared_ptr<vpd::ParserInterface> Parser::getVpdParserInstance()
52 {
53     // Read the VPD data into a vector.
54     vpdSpecificUtility::getVpdDataInVector(m_vpdFilePath, m_vpdVector,
55                                            m_vpdStartOffset);
56 
57     // This will detect the type of parser required.
58     std::shared_ptr<vpd::ParserInterface> l_parser =
59         ParserFactory::getParser(m_vpdVector, m_vpdFilePath, m_vpdStartOffset);
60 
61     return l_parser;
62 }
63 
64 types::VPDMapVariant Parser::parse()
65 {
66     std::shared_ptr<vpd::ParserInterface> l_parser = getVpdParserInstance();
67     return l_parser->parse();
68 }
69 
70 int Parser::updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData,
71                              types::DbusVariantType& o_updatedValue)
72 {
73     int l_bytesUpdatedOnHardware = constants::FAILURE;
74 
75     // A lambda to extract Record : Keyword string from i_paramsToWriteData
76     auto l_keyWordIdentifier =
77         [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string {
78         std::string l_keywordString{};
79         if (const types::IpzData* l_ipzData =
80                 std::get_if<types::IpzData>(&i_paramsToWriteData))
81         {
82             l_keywordString =
83                 std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData);
84         }
85         else if (const types::KwData* l_kwData =
86                      std::get_if<types::KwData>(&i_paramsToWriteData))
87         {
88             l_keywordString = std::get<0>(*l_kwData);
89         }
90         return l_keywordString;
91     };
92 
93     try
94     {
95         // Enable Reboot Guard
96         if (constants::FAILURE == dbusUtility::EnableRebootGuard())
97         {
98             EventLogger::createAsyncPel(
99                 types::ErrorType::DbusFailure,
100                 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
101                 std::string(
102                     "Failed to enable BMC Reboot Guard while updating " +
103                     l_keyWordIdentifier(i_paramsToWriteData)),
104                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
105 
106             return constants::FAILURE;
107         }
108 
109         // Update keyword's value on hardware
110         try
111         {
112             std::shared_ptr<ParserInterface> l_vpdParserInstance =
113                 getVpdParserInstance();
114             l_bytesUpdatedOnHardware =
115                 l_vpdParserInstance->writeKeywordOnHardware(
116                     i_paramsToWriteData);
117         }
118         catch (const std::exception& l_exception)
119         {
120             std::string l_errMsg(
121                 "Error while updating keyword's value on hardware path " +
122                 m_vpdFilePath + ", error: " + std::string(l_exception.what()));
123 
124             // TODO : Log PEL
125 
126             throw std::runtime_error(l_errMsg);
127         }
128 
129         auto [l_fruPath, l_inventoryObjPath, l_redundantFruPath] =
130             jsonUtility::getAllPathsToUpdateKeyword(m_parsedJson,
131                                                     m_vpdFilePath);
132 
133         // If inventory D-bus object path is present, update keyword's value on
134         // DBus
135         if (!l_inventoryObjPath.empty())
136         {
137             types::Record l_recordName;
138             std::string l_interfaceName;
139             std::string l_propertyName;
140             types::DbusVariantType l_keywordValue;
141 
142             if (const types::IpzData* l_ipzData =
143                     std::get_if<types::IpzData>(&i_paramsToWriteData))
144             {
145                 l_recordName = std::get<0>(*l_ipzData);
146                 l_interfaceName = constants::ipzVpdInf + l_recordName;
147                 l_propertyName = std::get<1>(*l_ipzData);
148 
149                 try
150                 {
151                     // Read keyword's value from hardware to write the same on
152                     // D-bus.
153                     std::shared_ptr<ParserInterface> l_vpdParserInstance =
154                         getVpdParserInstance();
155 
156                     l_keywordValue =
157                         l_vpdParserInstance->readKeywordFromHardware(
158                             types::ReadVpdParams(
159                                 std::make_tuple(l_recordName, l_propertyName)));
160 
161                     // return the actual value updated on hardware
162                     o_updatedValue = l_keywordValue;
163                 }
164                 catch (const std::exception& l_exception)
165                 {
166                     // Unable to read keyword's value from hardware.
167                     std::string l_errMsg(
168                         "Error while reading keyword's value from hadware path " +
169                         m_vpdFilePath +
170                         ", error: " + std::string(l_exception.what()));
171 
172                     // TODO: Log PEL
173 
174                     throw std::runtime_error(l_errMsg);
175                 }
176             }
177             else
178             {
179                 // Input parameter type provided isn't compatible to perform
180                 // update.
181                 std::string l_errMsg(
182                     "Input parameter type isn't compatible to update keyword's value on DBus for object path: " +
183                     l_inventoryObjPath);
184                 throw std::runtime_error(l_errMsg);
185             }
186 
187             // Get D-bus name for the given keyword
188             l_propertyName =
189                 vpdSpecificUtility::getDbusPropNameForGivenKw(l_propertyName);
190 
191             // Create D-bus object map
192             types::ObjectMap l_dbusObjMap = {std::make_pair(
193                 l_inventoryObjPath,
194                 types::InterfaceMap{std::make_pair(
195                     l_interfaceName, types::PropertyMap{std::make_pair(
196                                          l_propertyName, l_keywordValue)})})};
197 
198             // Call PIM's Notify method to perform update
199             if (!dbusUtility::callPIM(std::move(l_dbusObjMap)))
200             {
201                 // Call to PIM's Notify method failed.
202                 std::string l_errMsg("Notify PIM is failed for object path: " +
203                                      l_inventoryObjPath);
204                 throw std::runtime_error(l_errMsg);
205             }
206         }
207 
208         // Update keyword's value on redundant hardware if present
209         if (!l_redundantFruPath.empty())
210         {
211             if (updateVpdKeywordOnRedundantPath(l_redundantFruPath,
212                                                 i_paramsToWriteData) < 0)
213             {
214                 std::string l_errMsg(
215                     "Error while updating keyword's value on redundant path " +
216                     l_redundantFruPath);
217                 throw std::runtime_error(l_errMsg);
218             }
219         }
220 
221         // TODO: Check if revert is required when any of the writes fails.
222         // TODO: Handle error logging
223     }
224     catch (const std::exception& l_ex)
225     {
226         logging::logMessage("Update VPD Keyword failed for : " +
227                             l_keyWordIdentifier(i_paramsToWriteData) +
228                             " failed due to error: " + l_ex.what());
229 
230         // update failed, set return value to failure
231         l_bytesUpdatedOnHardware = constants::FAILURE;
232     }
233 
234     // Disable Reboot Guard
235     if (constants::FAILURE == dbusUtility::DisableRebootGuard())
236     {
237         EventLogger::createAsyncPel(
238             types::ErrorType::DbusFailure, types::SeverityType::Critical,
239             __FILE__, __FUNCTION__, 0,
240             std::string("Failed to disable BMC Reboot Guard while updating " +
241                         l_keyWordIdentifier(i_paramsToWriteData)),
242             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
243     }
244 
245     return l_bytesUpdatedOnHardware;
246 }
247 
248 int Parser::updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData)
249 {
250     types::DbusVariantType o_updatedValue;
251     return updateVpdKeyword(i_paramsToWriteData, o_updatedValue);
252 }
253 
254 int Parser::updateVpdKeywordOnRedundantPath(
255     const std::string& i_fruPath,
256     const types::WriteVpdParams& i_paramsToWriteData)
257 {
258     try
259     {
260         std::shared_ptr<Parser> l_parserObj =
261             std::make_shared<Parser>(i_fruPath, m_parsedJson);
262 
263         std::shared_ptr<ParserInterface> l_vpdParserInstance =
264             l_parserObj->getVpdParserInstance();
265 
266         return l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
267     }
268     catch (const std::exception& l_exception)
269     {
270         EventLogger::createSyncPel(
271             types::ErrorType::InvalidVpdMessage,
272             types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
273             "Error while updating keyword's value on redundant path " +
274                 i_fruPath + ", error: " + std::string(l_exception.what()),
275             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
276         return -1;
277     }
278 }
279 
280 int Parser::updateVpdKeywordOnHardware(
281     const types::WriteVpdParams& i_paramsToWriteData)
282 {
283     int l_bytesUpdatedOnHardware = constants::FAILURE;
284 
285     // A lambda to extract Record : Keyword string from i_paramsToWriteData
286     auto l_keyWordIdentifier =
287         [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string {
288         std::string l_keywordString{};
289         if (const types::IpzData* l_ipzData =
290                 std::get_if<types::IpzData>(&i_paramsToWriteData))
291         {
292             l_keywordString =
293                 std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData);
294         }
295         else if (const types::KwData* l_kwData =
296                      std::get_if<types::KwData>(&i_paramsToWriteData))
297         {
298             l_keywordString = std::get<0>(*l_kwData);
299         }
300         return l_keywordString;
301     };
302 
303     try
304     {
305         // Enable Reboot Guard
306         if (constants::FAILURE == dbusUtility::EnableRebootGuard())
307         {
308             EventLogger::createAsyncPel(
309                 types::ErrorType::DbusFailure,
310                 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
311                 std::string(
312                     "Failed to enable BMC Reboot Guard while updating " +
313                     l_keyWordIdentifier(i_paramsToWriteData)),
314                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
315 
316             return constants::FAILURE;
317         }
318 
319         std::shared_ptr<ParserInterface> l_vpdParserInstance =
320             getVpdParserInstance();
321         l_bytesUpdatedOnHardware =
322             l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
323     }
324     catch (const std::exception& l_exception)
325     {
326         types::ErrorType l_errorType;
327 
328         if (typeid(l_exception) == typeid(EccException))
329         {
330             l_errorType = types::ErrorType::EccCheckFailed;
331         }
332         else
333         {
334             l_errorType = types::ErrorType::InvalidVpdMessage;
335         }
336 
337         EventLogger::createAsyncPel(
338             l_errorType, types::SeverityType::Informational, __FILE__,
339             __FUNCTION__, 0,
340             "Error while updating keyword's value on hardware path [" +
341                 m_vpdFilePath + "], error: " + std::string(l_exception.what()),
342             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
343     }
344 
345     // Disable Reboot Guard
346     if (constants::FAILURE == dbusUtility::DisableRebootGuard())
347     {
348         EventLogger::createAsyncPel(
349             types::ErrorType::DbusFailure, types::SeverityType::Critical,
350             __FILE__, __FUNCTION__, 0,
351             std::string("Failed to disable BMC Reboot Guard while updating " +
352                         l_keyWordIdentifier(i_paramsToWriteData)),
353             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
354     }
355 
356     return l_bytesUpdatedOnHardware;
357 }
358 
359 } // namespace vpd
360