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