xref: /openbmc/openpower-vpd-parser/vpd-manager/src/manager.cpp (revision 393c0fade4690d37e7dd7227730b2bcaef9214db)
1 #include "config.h"
2 
3 #include "manager.hpp"
4 
5 #include "constants.hpp"
6 #include "exceptions.hpp"
7 #include "logger.hpp"
8 #include "parser.hpp"
9 #include "parser_factory.hpp"
10 #include "parser_interface.hpp"
11 #include "single_fab.hpp"
12 #include "types.hpp"
13 #include "utility/dbus_utility.hpp"
14 #include "utility/json_utility.hpp"
15 #include "utility/vpd_specific_utility.hpp"
16 
17 #include <boost/asio/steady_timer.hpp>
18 #include <sdbusplus/bus/match.hpp>
19 #include <sdbusplus/message.hpp>
20 
21 namespace vpd
22 {
23 Manager::Manager(
24     const std::shared_ptr<boost::asio::io_context>& ioCon,
25     const std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
26     const std::shared_ptr<sdbusplus::asio::connection>& asioConnection) :
27     m_ioContext(ioCon), m_interface(iFace), m_asioConnection(asioConnection)
28 {
29 #ifdef IBM_SYSTEM
30     if (!dbusUtility::isChassisPowerOn())
31     {
32         SingleFab l_singleFab;
33         const int& l_rc = l_singleFab.singleFabImOverride();
34 
35         if (l_rc == constants::FAILURE)
36         {
37             throw std::runtime_error(
38                 std::string(__FUNCTION__) +
39                 " : Found an invalid system configuration. Needs manual intervention. BMC is being quiesced.");
40         }
41     }
42 #endif
43 
44     try
45     {
46         // For backward compatibility. Should be depricated.
47         iFace->register_method(
48             "WriteKeyword",
49             [this](const sdbusplus::message::object_path i_path,
50                    const std::string i_recordName, const std::string i_keyword,
51                    const types::BinaryVector i_value) -> int {
52                 return this->updateKeyword(
53                     i_path, std::make_tuple(i_recordName, i_keyword, i_value));
54             });
55 
56         // Register methods under com.ibm.VPD.Manager interface
57         iFace->register_method(
58             "UpdateKeyword",
59             [this](const types::Path i_vpdPath,
60                    const types::WriteVpdParams i_paramsToWriteData) -> int {
61                 return this->updateKeyword(i_vpdPath, i_paramsToWriteData);
62             });
63 
64         iFace->register_method(
65             "WriteKeywordOnHardware",
66             [this](const types::Path i_fruPath,
67                    const types::WriteVpdParams i_paramsToWriteData) -> int {
68                 return this->updateKeywordOnHardware(i_fruPath,
69                                                      i_paramsToWriteData);
70             });
71 
72         iFace->register_method(
73             "ReadKeyword",
74             [this](const types::Path i_fruPath,
75                    const types::ReadVpdParams i_paramsToReadData)
76                 -> types::DbusVariantType {
77                 return this->readKeyword(i_fruPath, i_paramsToReadData);
78             });
79 
80         iFace->register_method(
81             "CollectFRUVPD",
82             [this](const sdbusplus::message::object_path& i_dbusObjPath) {
83                 this->collectSingleFruVpd(i_dbusObjPath);
84             });
85 
86         iFace->register_method(
87             "deleteFRUVPD",
88             [this](const sdbusplus::message::object_path& i_dbusObjPath) {
89                 this->deleteSingleFruVpd(i_dbusObjPath);
90             });
91 
92         iFace->register_method(
93             "GetExpandedLocationCode",
94             [this](const std::string& i_unexpandedLocationCode,
95                    uint16_t& i_nodeNumber) -> std::string {
96                 return this->getExpandedLocationCode(i_unexpandedLocationCode,
97                                                      i_nodeNumber);
98             });
99 
100         iFace->register_method("GetFRUsByExpandedLocationCode",
101                                [this](const std::string& i_expandedLocationCode)
102                                    -> types::ListOfPaths {
103                                    return this->getFrusByExpandedLocationCode(
104                                        i_expandedLocationCode);
105                                });
106 
107         iFace->register_method(
108             "GetFRUsByUnexpandedLocationCode",
109             [this](const std::string& i_unexpandedLocationCode,
110                    uint16_t& i_nodeNumber) -> types::ListOfPaths {
111                 return this->getFrusByUnexpandedLocationCode(
112                     i_unexpandedLocationCode, i_nodeNumber);
113             });
114 
115         iFace->register_method(
116             "GetHardwarePath",
117             [this](const sdbusplus::message::object_path& i_dbusObjPath)
118                 -> std::string { return this->getHwPath(i_dbusObjPath); });
119 
120         iFace->register_method("PerformVPDRecollection", [this]() {
121             this->performVpdRecollection();
122         });
123 
124         // Indicates FRU VPD collection for the system has not started.
125         iFace->register_property_rw<std::string>(
126             "CollectionStatus", sdbusplus::vtable::property_::emits_change,
127             [this](const std::string l_currStatus, const auto&) {
128                 m_vpdCollectionStatus = l_currStatus;
129                 return 0;
130             },
131             [this](const auto&) { return m_vpdCollectionStatus; });
132 
133         // If required, instantiate OEM specific handler here.
134 #ifdef IBM_SYSTEM
135         m_ibmHandler = std::make_shared<IbmHandler>(
136             m_worker, m_backupAndRestoreObj, m_interface, m_ioContext,
137             m_asioConnection);
138 #else
139         m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
140         m_interface->set_property("CollectionStatus", std::string("Completed"));
141 #endif
142     }
143     catch (const std::exception& e)
144     {
145         logging::logMessage(
146             "Manager class instantiation failed. " + std::string(e.what()));
147 
148         vpd::EventLogger::createSyncPel(
149             vpd::EventLogger::getErrorType(e), vpd::types::SeverityType::Error,
150             __FILE__, __FUNCTION__, 0, vpd::EventLogger::getErrorMsg(e),
151             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
152     }
153 }
154 
155 int Manager::updateKeyword(const types::Path i_vpdPath,
156                            const types::WriteVpdParams i_paramsToWriteData)
157 {
158     if (i_vpdPath.empty())
159     {
160         logging::logMessage("Given VPD path is empty.");
161         return -1;
162     }
163 
164     types::Path l_fruPath;
165     nlohmann::json l_sysCfgJsonObj{};
166 
167     if (m_worker.get() != nullptr)
168     {
169         l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
170 
171         // Get the EEPROM path
172         if (!l_sysCfgJsonObj.empty())
173         {
174             l_fruPath =
175                 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath);
176         }
177     }
178 
179     if (l_fruPath.empty())
180     {
181         l_fruPath = i_vpdPath;
182     }
183 
184     try
185     {
186         std::shared_ptr<Parser> l_parserObj =
187             std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
188         auto l_rc = l_parserObj->updateVpdKeyword(i_paramsToWriteData);
189 
190         if (l_rc != constants::FAILURE && m_backupAndRestoreObj)
191         {
192             if (m_backupAndRestoreObj->updateKeywordOnPrimaryOrBackupPath(
193                     l_fruPath, i_paramsToWriteData) < constants::VALUE_0)
194             {
195                 logging::logMessage(
196                     "Write success, but backup and restore failed for file[" +
197                     l_fruPath + "]");
198             }
199         }
200 
201         // update keyword in inherited FRUs
202         if (l_rc != constants::FAILURE)
203         {
204             vpdSpecificUtility::updateKwdOnInheritedFrus(
205                 l_fruPath, i_paramsToWriteData, l_sysCfgJsonObj);
206         }
207 
208         // update common interface(s) properties
209         if (l_rc != constants::FAILURE)
210         {
211             vpdSpecificUtility::updateCiPropertyOfInheritedFrus(
212                 l_fruPath, i_paramsToWriteData, l_sysCfgJsonObj);
213         }
214 
215         return l_rc;
216     }
217     catch (const std::exception& l_exception)
218     {
219         // TODO:: error log needed
220         logging::logMessage("Update keyword failed for file[" + i_vpdPath +
221                             "], reason: " + std::string(l_exception.what()));
222         return -1;
223     }
224 }
225 
226 int Manager::updateKeywordOnHardware(
227     const types::Path i_fruPath,
228     const types::WriteVpdParams i_paramsToWriteData) noexcept
229 {
230     try
231     {
232         if (i_fruPath.empty())
233         {
234             throw std::runtime_error("Given FRU path is empty");
235         }
236 
237         nlohmann::json l_sysCfgJsonObj{};
238 
239         if (m_worker.get() != nullptr)
240         {
241             l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
242         }
243 
244         std::shared_ptr<Parser> l_parserObj =
245             std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj);
246         return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData);
247     }
248     catch (const std::exception& l_exception)
249     {
250         EventLogger::createAsyncPel(
251             types::ErrorType::InvalidEeprom, types::SeverityType::Informational,
252             __FILE__, __FUNCTION__, 0,
253             "Update keyword on hardware failed for file[" + i_fruPath +
254                 "], reason: " + std::string(l_exception.what()),
255             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
256 
257         return constants::FAILURE;
258     }
259 }
260 
261 types::DbusVariantType Manager::readKeyword(
262     const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData)
263 {
264     try
265     {
266         nlohmann::json l_jsonObj{};
267 
268         if (m_worker.get() != nullptr)
269         {
270             l_jsonObj = m_worker->getSysCfgJsonObj();
271         }
272 
273         std::error_code ec;
274 
275         // Check if given path is filesystem path
276         if (!std::filesystem::exists(i_fruPath, ec) && (ec))
277         {
278             throw std::runtime_error(
279                 "Given file path " + i_fruPath + " not found.");
280         }
281 
282         logging::logMessage("Performing VPD read on " + i_fruPath);
283 
284         std::shared_ptr<vpd::Parser> l_parserObj =
285             std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj);
286 
287         std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance =
288             l_parserObj->getVpdParserInstance();
289 
290         return (
291             l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData));
292     }
293     catch (const std::exception& e)
294     {
295         logging::logMessage(
296             e.what() + std::string(". VPD manager read operation failed for ") +
297             i_fruPath);
298         throw types::DeviceError::ReadFailure();
299     }
300 }
301 
302 void Manager::collectSingleFruVpd(
303     const sdbusplus::message::object_path& i_dbusObjPath)
304 {
305     if (m_vpdCollectionStatus != "Completed")
306     {
307         logging::logMessage(
308             "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
309             std::string(i_dbusObjPath));
310         return;
311     }
312 
313     if (m_worker.get() != nullptr)
314     {
315         m_worker->collectSingleFruVpd(i_dbusObjPath);
316     }
317 }
318 
319 void Manager::deleteSingleFruVpd(
320     const sdbusplus::message::object_path& i_dbusObjPath)
321 {
322     try
323     {
324         if (std::string(i_dbusObjPath).empty())
325         {
326             throw std::runtime_error(
327                 "Given DBus object path is empty. Aborting FRU VPD deletion.");
328         }
329 
330         if (m_worker.get() == nullptr)
331         {
332             throw std::runtime_error(
333                 "Worker object not found, can't perform FRU VPD deletion for: " +
334                 std::string(i_dbusObjPath));
335         }
336 
337         m_worker->deleteFruVpd(std::string(i_dbusObjPath));
338     }
339     catch (const std::exception& l_ex)
340     {
341         // TODO: Log PEL
342         logging::logMessage(l_ex.what());
343     }
344 }
345 
346 bool Manager::isValidUnexpandedLocationCode(
347     const std::string& i_unexpandedLocationCode)
348 {
349     if ((i_unexpandedLocationCode.length() <
350          constants::UNEXP_LOCATION_CODE_MIN_LENGTH) ||
351         ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") !=
352           constants::STR_CMP_SUCCESS) &&
353          (i_unexpandedLocationCode.compare(0, 4, "Umts") !=
354           constants::STR_CMP_SUCCESS)) ||
355         ((i_unexpandedLocationCode.length() >
356           constants::UNEXP_LOCATION_CODE_MIN_LENGTH) &&
357          (i_unexpandedLocationCode.find("-") != 4)))
358     {
359         return false;
360     }
361 
362     return true;
363 }
364 
365 std::string Manager::getExpandedLocationCode(
366     const std::string& i_unexpandedLocationCode,
367     [[maybe_unused]] const uint16_t i_nodeNumber)
368 {
369     if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
370     {
371         phosphor::logging::elog<types::DbusInvalidArgument>(
372             types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
373             types::InvalidArgument::ARGUMENT_VALUE(
374                 i_unexpandedLocationCode.c_str()));
375     }
376 
377     const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
378     if (!l_sysCfgJsonObj.contains("frus"))
379     {
380         logging::logMessage("Missing frus tag in system config JSON");
381     }
382 
383     const nlohmann::json& l_listOfFrus =
384         l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
385 
386     for (const auto& l_frus : l_listOfFrus.items())
387     {
388         for (const auto& l_aFru : l_frus.value())
389         {
390             if (l_aFru["extraInterfaces"].contains(
391                     constants::locationCodeInf) &&
392                 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
393                     "LocationCode", "") == i_unexpandedLocationCode)
394             {
395                 return std::get<std::string>(dbusUtility::readDbusProperty(
396                     l_aFru["serviceName"], l_aFru["inventoryPath"],
397                     constants::locationCodeInf, "LocationCode"));
398             }
399         }
400     }
401     phosphor::logging::elog<types::DbusInvalidArgument>(
402         types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
403         types::InvalidArgument::ARGUMENT_VALUE(
404             i_unexpandedLocationCode.c_str()));
405 }
406 
407 types::ListOfPaths Manager::getFrusByUnexpandedLocationCode(
408     const std::string& i_unexpandedLocationCode,
409     [[maybe_unused]] const uint16_t i_nodeNumber)
410 {
411     types::ListOfPaths l_inventoryPaths;
412 
413     if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
414     {
415         phosphor::logging::elog<types::DbusInvalidArgument>(
416             types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
417             types::InvalidArgument::ARGUMENT_VALUE(
418                 i_unexpandedLocationCode.c_str()));
419     }
420 
421     const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
422     if (!l_sysCfgJsonObj.contains("frus"))
423     {
424         logging::logMessage("Missing frus tag in system config JSON");
425     }
426 
427     const nlohmann::json& l_listOfFrus =
428         l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
429 
430     for (const auto& l_frus : l_listOfFrus.items())
431     {
432         for (const auto& l_aFru : l_frus.value())
433         {
434             if (l_aFru["extraInterfaces"].contains(
435                     constants::locationCodeInf) &&
436                 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
437                     "LocationCode", "") == i_unexpandedLocationCode)
438             {
439                 l_inventoryPaths.push_back(
440                     l_aFru.at("inventoryPath")
441                         .get_ref<const nlohmann::json::string_t&>());
442             }
443         }
444     }
445 
446     if (l_inventoryPaths.empty())
447     {
448         phosphor::logging::elog<types::DbusInvalidArgument>(
449             types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
450             types::InvalidArgument::ARGUMENT_VALUE(
451                 i_unexpandedLocationCode.c_str()));
452     }
453 
454     return l_inventoryPaths;
455 }
456 
457 std::string Manager::getHwPath(
458     const sdbusplus::message::object_path& i_dbusObjPath)
459 {
460     // Dummy code to supress unused variable warning. To be removed.
461     logging::logMessage(std::string(i_dbusObjPath));
462 
463     return std::string{};
464 }
465 
466 std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode(
467     const std::string& i_expandedLocationCode)
468 {
469     /**
470      * Location code should always start with U and fulfil minimum length
471      * criteria.
472      */
473     if (i_expandedLocationCode[0] != 'U' ||
474         i_expandedLocationCode.length() <
475             constants::EXP_LOCATION_CODE_MIN_LENGTH)
476     {
477         phosphor::logging::elog<types::DbusInvalidArgument>(
478             types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
479             types::InvalidArgument::ARGUMENT_VALUE(
480                 i_expandedLocationCode.c_str()));
481     }
482 
483     std::string l_fcKwd;
484 
485     auto l_fcKwdValue = dbusUtility::readDbusProperty(
486         "xyz.openbmc_project.Inventory.Manager",
487         "/xyz/openbmc_project/inventory/system/chassis/motherboard",
488         "com.ibm.ipzvpd.VCEN", "FC");
489 
490     if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue))
491     {
492         l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
493     }
494 
495     // Get the first part of expanded location code to check for FC or TM.
496     std::string l_firstKwd = i_expandedLocationCode.substr(1, 4);
497 
498     std::string l_unexpandedLocationCode{};
499     uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER;
500 
501     // Check if this value matches the value of FC keyword.
502     if (l_fcKwd.substr(0, 4) == l_firstKwd)
503     {
504         /**
505          * Period(.) should be there in expanded location code to seggregate
506          * FC, node number and SE values.
507          */
508         size_t l_nodeStartPos = i_expandedLocationCode.find('.');
509         if (l_nodeStartPos == std::string::npos)
510         {
511             phosphor::logging::elog<types::DbusInvalidArgument>(
512                 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
513                 types::InvalidArgument::ARGUMENT_VALUE(
514                     i_expandedLocationCode.c_str()));
515         }
516 
517         size_t l_nodeEndPos =
518             i_expandedLocationCode.find('.', l_nodeStartPos + 1);
519         if (l_nodeEndPos == std::string::npos)
520         {
521             phosphor::logging::elog<types::DbusInvalidArgument>(
522                 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
523                 types::InvalidArgument::ARGUMENT_VALUE(
524                     i_expandedLocationCode.c_str()));
525         }
526 
527         // Skip 3 bytes for '.ND'
528         l_nodeNummber = std::stoi(i_expandedLocationCode.substr(
529             l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3)));
530 
531         /**
532          * Confirm if there are other details apart FC, node number and SE
533          * in location code
534          */
535         if (i_expandedLocationCode.length() >
536             constants::EXP_LOCATION_CODE_MIN_LENGTH)
537         {
538             l_unexpandedLocationCode =
539                 i_expandedLocationCode[0] + std::string("fcs") +
540                 i_expandedLocationCode.substr(
541                     l_nodeEndPos + 1 + constants::SE_KWD_LENGTH,
542                     std::string::npos);
543         }
544         else
545         {
546             l_unexpandedLocationCode = "Ufcs";
547         }
548     }
549     else
550     {
551         std::string l_tmKwd;
552         // Read TM keyword value.
553         auto l_tmKwdValue = dbusUtility::readDbusProperty(
554             "xyz.openbmc_project.Inventory.Manager",
555             "/xyz/openbmc_project/inventory/system/chassis/motherboard",
556             "com.ibm.ipzvpd.VSYS", "TM");
557 
558         if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue))
559         {
560             l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
561         }
562 
563         // Check if the substr matches to TM keyword value.
564         if (l_tmKwd.substr(0, 4) == l_firstKwd)
565         {
566             /**
567              * System location code will not have node number and any other
568              * details.
569              */
570             l_unexpandedLocationCode = "Umts";
571         }
572         // The given location code is neither "fcs" or "mts".
573         else
574         {
575             phosphor::logging::elog<types::DbusInvalidArgument>(
576                 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
577                 types::InvalidArgument::ARGUMENT_VALUE(
578                     i_expandedLocationCode.c_str()));
579         }
580     }
581 
582     return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber);
583 }
584 
585 types::ListOfPaths Manager::getFrusByExpandedLocationCode(
586     const std::string& i_expandedLocationCode)
587 {
588     std::tuple<std::string, uint16_t> l_locationAndNodePair =
589         getUnexpandedLocationCode(i_expandedLocationCode);
590 
591     return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair),
592                                            std::get<1>(l_locationAndNodePair));
593 }
594 
595 void Manager::performVpdRecollection()
596 {
597     if (m_worker.get() != nullptr)
598     {
599         m_worker->performVpdRecollection();
600     }
601 }
602 } // namespace vpd
603