xref: /openbmc/openpower-vpd-parser/vpd-manager/src/parser.cpp (revision a39aafa337352262596e07577fdf88cc294284c9)
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 PIM's Notify method to perform update
223             if (!dbusUtility::callPIM(std::move(l_dbusObjMap)))
224             {
225                 // Call to PIM's Notify method failed.
226                 std::string l_errMsg("Notify PIM is failed for object path: " +
227                                      l_inventoryObjPath);
228                 throw std::runtime_error(l_errMsg);
229             }
230         }
231 
232         if (l_errCode == error_code::ERROR_GETTING_REDUNDANT_PATH)
233         {
234             logging::logMessage(commonUtility::getErrCodeMsg(l_errCode));
235         }
236 
237         // Update keyword's value on redundant hardware if present
238         if (!l_redundantFruPath.empty())
239         {
240             if (updateVpdKeywordOnRedundantPath(l_redundantFruPath,
241                                                 i_paramsToWriteData) < 0)
242             {
243                 std::string l_errMsg(
244                     "Error while updating keyword's value on redundant path " +
245                     l_redundantFruPath);
246                 throw std::runtime_error(l_errMsg);
247             }
248         }
249 
250         // TODO: Check if revert is required when any of the writes fails.
251         // TODO: Handle error logging
252     }
253     catch (const std::exception& l_ex)
254     {
255         logging::logMessage("Update VPD Keyword failed for : " +
256                             l_keyWordIdentifier(i_paramsToWriteData) +
257                             " failed due to error: " + l_ex.what());
258 
259         // update failed, set return value to failure
260         l_bytesUpdatedOnHardware = constants::FAILURE;
261     }
262 
263     // Disable Reboot Guard
264     if (constants::FAILURE == dbusUtility::DisableRebootGuard())
265     {
266         EventLogger::createAsyncPel(
267             types::ErrorType::DbusFailure, types::SeverityType::Critical,
268             __FILE__, __FUNCTION__, 0,
269             std::string("Failed to disable BMC Reboot Guard while updating " +
270                         l_keyWordIdentifier(i_paramsToWriteData)),
271             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
272     }
273 
274     return l_bytesUpdatedOnHardware;
275 }
276 
updateVpdKeyword(const types::WriteVpdParams & i_paramsToWriteData)277 int Parser::updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData)
278 {
279     types::DbusVariantType o_updatedValue;
280     return updateVpdKeyword(i_paramsToWriteData, o_updatedValue);
281 }
282 
updateVpdKeywordOnRedundantPath(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData)283 int Parser::updateVpdKeywordOnRedundantPath(
284     const std::string& i_fruPath,
285     const types::WriteVpdParams& i_paramsToWriteData)
286 {
287     try
288     {
289         std::shared_ptr<Parser> l_parserObj =
290             std::make_shared<Parser>(i_fruPath, m_parsedJson);
291 
292         std::shared_ptr<ParserInterface> l_vpdParserInstance =
293             l_parserObj->getVpdParserInstance();
294 
295         return l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
296     }
297     catch (const std::exception& l_exception)
298     {
299         EventLogger::createSyncPel(
300             types::ErrorType::InvalidVpdMessage,
301             types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
302             "Error while updating keyword's value on redundant path " +
303                 i_fruPath + ", error: " + std::string(l_exception.what()),
304             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
305         return -1;
306     }
307 }
308 
updateVpdKeywordOnHardware(const types::WriteVpdParams & i_paramsToWriteData)309 int Parser::updateVpdKeywordOnHardware(
310     const types::WriteVpdParams& i_paramsToWriteData)
311 {
312     int l_bytesUpdatedOnHardware = constants::FAILURE;
313 
314     // A lambda to extract Record : Keyword string from i_paramsToWriteData
315     auto l_keyWordIdentifier =
316         [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string {
317         std::string l_keywordString{};
318         if (const types::IpzData* l_ipzData =
319                 std::get_if<types::IpzData>(&i_paramsToWriteData))
320         {
321             l_keywordString =
322                 std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData);
323         }
324         else if (const types::KwData* l_kwData =
325                      std::get_if<types::KwData>(&i_paramsToWriteData))
326         {
327             l_keywordString = std::get<0>(*l_kwData);
328         }
329         return l_keywordString;
330     };
331 
332     try
333     {
334         // Enable Reboot Guard
335         if (constants::FAILURE == dbusUtility::EnableRebootGuard())
336         {
337             EventLogger::createAsyncPel(
338                 types::ErrorType::DbusFailure,
339                 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
340                 std::string(
341                     "Failed to enable BMC Reboot Guard while updating " +
342                     l_keyWordIdentifier(i_paramsToWriteData)),
343                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
344 
345             return constants::FAILURE;
346         }
347 
348         std::shared_ptr<ParserInterface> l_vpdParserInstance =
349             getVpdParserInstance();
350         l_bytesUpdatedOnHardware =
351             l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
352     }
353     catch (const std::exception& l_exception)
354     {
355         types::ErrorType l_errorType;
356 
357         if (typeid(l_exception) == typeid(EccException))
358         {
359             l_errorType = types::ErrorType::EccCheckFailed;
360         }
361         else
362         {
363             l_errorType = types::ErrorType::InvalidVpdMessage;
364         }
365 
366         EventLogger::createAsyncPel(
367             l_errorType, types::SeverityType::Informational, __FILE__,
368             __FUNCTION__, 0,
369             "Error while updating keyword's value on hardware path [" +
370                 m_vpdFilePath + "], error: " + std::string(l_exception.what()),
371             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
372     }
373 
374     // Disable Reboot Guard
375     if (constants::FAILURE == dbusUtility::DisableRebootGuard())
376     {
377         EventLogger::createAsyncPel(
378             types::ErrorType::DbusFailure, types::SeverityType::Critical,
379             __FILE__, __FUNCTION__, 0,
380             std::string("Failed to disable BMC Reboot Guard while updating " +
381                         l_keyWordIdentifier(i_paramsToWriteData)),
382             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
383     }
384 
385     return l_bytesUpdatedOnHardware;
386 }
387 
388 } // namespace vpd
389