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