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