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