xref: /openbmc/openpower-vpd-parser/vpd-manager/src/parser.cpp (revision c6159a29119d5e08476ed85eaf1cf47ebf9bebdb)
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         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: " + commonUtility::getErrCodeMsg(l_errorCode));
47         }
48     }
49 }
50 
getVpdParserInstance()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 
parse()64 types::VPDMapVariant Parser::parse()
65 {
66     std::shared_ptr<vpd::ParserInterface> l_parser = getVpdParserInstance();
67     return l_parser->parse();
68 }
69 
updateVpdKeyword(const types::WriteVpdParams & i_paramsToWriteData,types::DbusVariantType & o_updatedValue)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         uint16_t l_errCode = 0;
130 
131         auto [l_fruPath, l_inventoryObjPath, l_redundantFruPath] =
132             jsonUtility::getAllPathsToUpdateKeyword(m_parsedJson, m_vpdFilePath,
133                                                     l_errCode);
134 
135         if (l_errCode == error_code::INVALID_INPUT_PARAMETER ||
136             l_errCode == error_code::INVALID_JSON)
137         {
138             throw std::runtime_error(
139                 "Failed to get paths to update keyword. Error : " +
140                 commonUtility::getErrCodeMsg(l_errCode));
141         }
142 
143         // If inventory D-bus object path is present, update keyword's value on
144         // DBus
145         if (!l_inventoryObjPath.empty())
146         {
147             types::Record l_recordName;
148             std::string l_interfaceName;
149             std::string l_propertyName;
150             types::DbusVariantType l_keywordValue;
151 
152             if (const types::IpzData* l_ipzData =
153                     std::get_if<types::IpzData>(&i_paramsToWriteData))
154             {
155                 l_recordName = std::get<0>(*l_ipzData);
156                 l_interfaceName = constants::ipzVpdInf + l_recordName;
157                 l_propertyName = std::get<1>(*l_ipzData);
158 
159                 try
160                 {
161                     // Read keyword's value from hardware to write the same on
162                     // D-bus.
163                     std::shared_ptr<ParserInterface> l_vpdParserInstance =
164                         getVpdParserInstance();
165 
166                     l_keywordValue =
167                         l_vpdParserInstance->readKeywordFromHardware(
168                             types::ReadVpdParams(
169                                 std::make_tuple(l_recordName, l_propertyName)));
170 
171                     // return the actual value updated on hardware
172                     o_updatedValue = l_keywordValue;
173                 }
174                 catch (const std::exception& l_exception)
175                 {
176                     // Unable to read keyword's value from hardware.
177                     std::string l_errMsg(
178                         "Error while reading keyword's value from hadware path " +
179                         m_vpdFilePath +
180                         ", error: " + std::string(l_exception.what()));
181 
182                     // TODO: Log PEL
183 
184                     throw std::runtime_error(l_errMsg);
185                 }
186             }
187             else
188             {
189                 // Input parameter type provided isn't compatible to perform
190                 // update.
191                 std::string l_errMsg(
192                     "Input parameter type isn't compatible to update keyword's value on DBus for object path: " +
193                     l_inventoryObjPath);
194                 throw std::runtime_error(l_errMsg);
195             }
196 
197             // Get D-bus name for the given keyword
198             l_propertyName =
199                 vpdSpecificUtility::getDbusPropNameForGivenKw(l_propertyName);
200 
201             // Create D-bus object map
202             types::ObjectMap l_dbusObjMap = {std::make_pair(
203                 l_inventoryObjPath,
204                 types::InterfaceMap{std::make_pair(
205                     l_interfaceName, types::PropertyMap{std::make_pair(
206                                          l_propertyName, l_keywordValue)})})};
207 
208             // Call PIM's Notify method to perform update
209             if (!dbusUtility::callPIM(std::move(l_dbusObjMap)))
210             {
211                 // Call to PIM's Notify method failed.
212                 std::string l_errMsg("Notify PIM is failed for object path: " +
213                                      l_inventoryObjPath);
214                 throw std::runtime_error(l_errMsg);
215             }
216         }
217 
218         if (l_errCode == error_code::ERROR_GETTING_REDUNDANT_PATH)
219         {
220             logging::logMessage(commonUtility::getErrCodeMsg(l_errCode));
221         }
222 
223         // Update keyword's value on redundant hardware if present
224         if (!l_redundantFruPath.empty())
225         {
226             if (updateVpdKeywordOnRedundantPath(l_redundantFruPath,
227                                                 i_paramsToWriteData) < 0)
228             {
229                 std::string l_errMsg(
230                     "Error while updating keyword's value on redundant path " +
231                     l_redundantFruPath);
232                 throw std::runtime_error(l_errMsg);
233             }
234         }
235 
236         // TODO: Check if revert is required when any of the writes fails.
237         // TODO: Handle error logging
238     }
239     catch (const std::exception& l_ex)
240     {
241         logging::logMessage("Update VPD Keyword failed for : " +
242                             l_keyWordIdentifier(i_paramsToWriteData) +
243                             " failed due to error: " + l_ex.what());
244 
245         // update failed, set return value to failure
246         l_bytesUpdatedOnHardware = constants::FAILURE;
247     }
248 
249     // Disable Reboot Guard
250     if (constants::FAILURE == dbusUtility::DisableRebootGuard())
251     {
252         EventLogger::createAsyncPel(
253             types::ErrorType::DbusFailure, types::SeverityType::Critical,
254             __FILE__, __FUNCTION__, 0,
255             std::string("Failed to disable BMC Reboot Guard while updating " +
256                         l_keyWordIdentifier(i_paramsToWriteData)),
257             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
258     }
259 
260     return l_bytesUpdatedOnHardware;
261 }
262 
updateVpdKeyword(const types::WriteVpdParams & i_paramsToWriteData)263 int Parser::updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData)
264 {
265     types::DbusVariantType o_updatedValue;
266     return updateVpdKeyword(i_paramsToWriteData, o_updatedValue);
267 }
268 
updateVpdKeywordOnRedundantPath(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData)269 int Parser::updateVpdKeywordOnRedundantPath(
270     const std::string& i_fruPath,
271     const types::WriteVpdParams& i_paramsToWriteData)
272 {
273     try
274     {
275         std::shared_ptr<Parser> l_parserObj =
276             std::make_shared<Parser>(i_fruPath, m_parsedJson);
277 
278         std::shared_ptr<ParserInterface> l_vpdParserInstance =
279             l_parserObj->getVpdParserInstance();
280 
281         return l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
282     }
283     catch (const std::exception& l_exception)
284     {
285         EventLogger::createSyncPel(
286             types::ErrorType::InvalidVpdMessage,
287             types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
288             "Error while updating keyword's value on redundant path " +
289                 i_fruPath + ", error: " + std::string(l_exception.what()),
290             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
291         return -1;
292     }
293 }
294 
updateVpdKeywordOnHardware(const types::WriteVpdParams & i_paramsToWriteData)295 int Parser::updateVpdKeywordOnHardware(
296     const types::WriteVpdParams& i_paramsToWriteData)
297 {
298     int l_bytesUpdatedOnHardware = constants::FAILURE;
299 
300     // A lambda to extract Record : Keyword string from i_paramsToWriteData
301     auto l_keyWordIdentifier =
302         [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string {
303         std::string l_keywordString{};
304         if (const types::IpzData* l_ipzData =
305                 std::get_if<types::IpzData>(&i_paramsToWriteData))
306         {
307             l_keywordString =
308                 std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData);
309         }
310         else if (const types::KwData* l_kwData =
311                      std::get_if<types::KwData>(&i_paramsToWriteData))
312         {
313             l_keywordString = std::get<0>(*l_kwData);
314         }
315         return l_keywordString;
316     };
317 
318     try
319     {
320         // Enable Reboot Guard
321         if (constants::FAILURE == dbusUtility::EnableRebootGuard())
322         {
323             EventLogger::createAsyncPel(
324                 types::ErrorType::DbusFailure,
325                 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
326                 std::string(
327                     "Failed to enable BMC Reboot Guard while updating " +
328                     l_keyWordIdentifier(i_paramsToWriteData)),
329                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
330 
331             return constants::FAILURE;
332         }
333 
334         std::shared_ptr<ParserInterface> l_vpdParserInstance =
335             getVpdParserInstance();
336         l_bytesUpdatedOnHardware =
337             l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
338     }
339     catch (const std::exception& l_exception)
340     {
341         types::ErrorType l_errorType;
342 
343         if (typeid(l_exception) == typeid(EccException))
344         {
345             l_errorType = types::ErrorType::EccCheckFailed;
346         }
347         else
348         {
349             l_errorType = types::ErrorType::InvalidVpdMessage;
350         }
351 
352         EventLogger::createAsyncPel(
353             l_errorType, types::SeverityType::Informational, __FILE__,
354             __FUNCTION__, 0,
355             "Error while updating keyword's value on hardware path [" +
356                 m_vpdFilePath + "], error: " + std::string(l_exception.what()),
357             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
358     }
359 
360     // Disable Reboot Guard
361     if (constants::FAILURE == dbusUtility::DisableRebootGuard())
362     {
363         EventLogger::createAsyncPel(
364             types::ErrorType::DbusFailure, types::SeverityType::Critical,
365             __FILE__, __FUNCTION__, 0,
366             std::string("Failed to disable BMC Reboot Guard while updating " +
367                         l_keyWordIdentifier(i_paramsToWriteData)),
368             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
369     }
370 
371     return l_bytesUpdatedOnHardware;
372 }
373 
374 } // namespace vpd
375