xref: /openbmc/openpower-vpd-parser/vpd-manager/oem-handler/ibm_handler.cpp (revision b5fab80a2955b33d56c07f47bf5c20057b3d1de3)
1 #include "config.h"
2 
3 #include "ibm_handler.hpp"
4 
5 #include "listener.hpp"
6 #include "parser.hpp"
7 
8 #include <utility/common_utility.hpp>
9 #include <utility/dbus_utility.hpp>
10 #include <utility/json_utility.hpp>
11 #include <utility/vpd_specific_utility.hpp>
12 
13 namespace vpd
14 {
IbmHandler(std::shared_ptr<Worker> & o_worker,std::shared_ptr<BackupAndRestore> & o_backupAndRestoreObj,const std::shared_ptr<sdbusplus::asio::dbus_interface> & i_iFace,const std::shared_ptr<sdbusplus::asio::dbus_interface> & i_progressiFace,const std::shared_ptr<boost::asio::io_context> & i_ioCon,const std::shared_ptr<sdbusplus::asio::connection> & i_asioConnection)15 IbmHandler::IbmHandler(
16     std::shared_ptr<Worker>& o_worker,
17     std::shared_ptr<BackupAndRestore>& o_backupAndRestoreObj,
18     const std::shared_ptr<sdbusplus::asio::dbus_interface>& i_iFace,
19     const std::shared_ptr<sdbusplus::asio::dbus_interface>& i_progressiFace,
20     const std::shared_ptr<boost::asio::io_context>& i_ioCon,
21     const std::shared_ptr<sdbusplus::asio::connection>& i_asioConnection) :
22     m_worker(o_worker), m_backupAndRestoreObj(o_backupAndRestoreObj),
23     m_interface(i_iFace), m_progressInterface(i_progressiFace),
24     m_ioContext(i_ioCon), m_asioConnection(i_asioConnection),
25     m_logger(Logger::getLoggerInstance())
26 {
27     uint16_t l_errCode{0};
28 
29     // check VPD collection mode
30     const auto l_vpdCollectionMode =
31         commonUtility::isFieldModeEnabled()
32             ? types::VpdCollectionMode::DEFAULT_MODE
33             : commonUtility::getVpdCollectionMode(l_errCode);
34 
35     if (l_errCode)
36     {
37         m_logger->logMessage(
38             "Error while trying to read VPD collection mode: " +
39             commonUtility::getErrCodeMsg(l_errCode));
40     }
41 
42     if (dbusUtility::isChassisPowerOn())
43     {
44         // At power on, less number of FRU(s) needs collection. we can scale
45         // down the threads to reduce CPU utilization.
46         m_worker = std::make_shared<Worker>(
47             INVENTORY_JSON_DEFAULT, constants::VALUE_1, l_vpdCollectionMode);
48     }
49     else
50     {
51         // Initialize with default configuration
52         m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT,
53                                             constants::MAX_THREADS,
54                                             l_vpdCollectionMode);
55     }
56 
57     // Set up minimal things that is needed before bus name is claimed.
58     performInitialSetup();
59 
60     if (!m_sysCfgJsonObj.empty() &&
61         jsonUtility::isBackupAndRestoreRequired(m_sysCfgJsonObj, l_errCode))
62     {
63         try
64         {
65             m_backupAndRestoreObj =
66                 std::make_shared<BackupAndRestore>(m_sysCfgJsonObj);
67         }
68         catch (const std::exception& l_ex)
69         {
70             logging::logMessage("Back up and restore instantiation failed. {" +
71                                 std::string(l_ex.what()) + "}");
72 
73             EventLogger::createSyncPel(
74                 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
75                 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
76                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
77         }
78     }
79     else if (l_errCode)
80     {
81         logging::logMessage(
82             "Failed to check if backup & restore required. Error : " +
83             commonUtility::getErrCodeMsg(l_errCode));
84     }
85 
86     // Instantiate Listener object
87     m_eventListener = std::make_shared<Listener>(m_worker, m_asioConnection);
88     m_eventListener->registerAssetTagChangeCallback();
89     m_eventListener->registerHostStateChangeCallback();
90     m_eventListener->registerPresenceChangeCallback();
91 
92     // Instantiate GpioMonitor class
93     m_gpioMonitor =
94         std::make_shared<GpioMonitor>(m_sysCfgJsonObj, m_worker, m_ioContext);
95 }
96 
SetTimerToDetectVpdCollectionStatus()97 void IbmHandler::SetTimerToDetectVpdCollectionStatus()
98 {
99     // Keeping max retry for 2 minutes. TODO: Make it configurable based on
100     // system type.
101     static constexpr auto MAX_RETRY = 12;
102 
103     static boost::asio::steady_timer l_timer(*m_ioContext);
104     static uint8_t l_timerRetry = 0;
105 
106     auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10));
107 
108     (l_asyncCancelled == 0)
109         ? logging::logMessage("Collection Timer started")
110         : logging::logMessage("Collection Timer re-started");
111 
112     l_timer.async_wait([this](const boost::system::error_code& ec) {
113         if (ec == boost::asio::error::operation_aborted)
114         {
115             throw std::runtime_error(
116                 "Timer to detect thread collection status was aborted");
117         }
118 
119         if (ec)
120         {
121             throw std::runtime_error(
122                 "Timer to detect thread collection failed");
123         }
124 
125         if (m_worker->isAllFruCollectionDone())
126         {
127             // cancel the timer
128             l_timer.cancel();
129             processFailedEeproms();
130 
131             // update VPD for powerVS system.
132             ConfigurePowerVsSystem();
133 
134             std::cout << "m_worker->isSystemVPDOnDBus() completed" << std::endl;
135             m_progressInterface->set_property(
136                 "Status", std::string(constants::vpdCollectionCompleted));
137 
138             if (m_backupAndRestoreObj)
139             {
140                 m_backupAndRestoreObj->backupAndRestore();
141             }
142 
143             if (m_eventListener)
144             {
145                 m_eventListener->registerCorrPropCallBack();
146             }
147 
148             // terminate collection logger
149             m_logger->terminateVpdCollectionLogging();
150         }
151         else
152         {
153             auto l_threadCount = m_worker->getActiveThreadCount();
154             if (l_timerRetry == MAX_RETRY)
155             {
156                 l_timer.cancel();
157                 logging::logMessage("Taking too long. Active thread = " +
158                                     std::to_string(l_threadCount));
159 
160                 // terminate collection logger
161                 m_logger->terminateVpdCollectionLogging();
162             }
163             else
164             {
165                 l_timerRetry++;
166                 logging::logMessage("Collection is in progress for [" +
167                                     std::to_string(l_threadCount) + "] FRUs.");
168 
169                 SetTimerToDetectVpdCollectionStatus();
170             }
171         }
172     });
173 }
174 
checkAndUpdatePowerVsVpd(const nlohmann::json & i_powerVsJsonObj,std::vector<std::string> & o_failedPathList)175 void IbmHandler::checkAndUpdatePowerVsVpd(
176     const nlohmann::json& i_powerVsJsonObj,
177     std::vector<std::string>& o_failedPathList)
178 {
179     for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
180     {
181         nlohmann::json l_sysCfgJsonObj{};
182         if (m_worker.get() != nullptr)
183         {
184             l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
185         }
186 
187         // The utility method will handle emty JSON case. No explicit
188         // handling required here.
189         uint16_t l_errCode = 0;
190         auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
191             l_sysCfgJsonObj, l_fruPath, l_errCode);
192 
193         // Mark it as failed if inventory path not found in JSON.
194         if (l_inventoryPath.empty())
195         {
196             if (l_errCode)
197             {
198                 logging::logMessage(
199                     "Failed to get inventory object path from JSON for FRU [" +
200                     l_fruPath +
201                     "], error : " + commonUtility::getErrCodeMsg(l_errCode));
202             }
203 
204             o_failedPathList.push_back(l_fruPath);
205             continue;
206         }
207 
208         // check if the FRU is present
209         if (!dbusUtility::isInventoryPresent(l_inventoryPath))
210         {
211             logging::logMessage(
212                 "Inventory not present, skip updating part number. Path: " +
213                 l_inventoryPath);
214             continue;
215         }
216 
217         // check if the FRU needs CCIN check before updating PN.
218         if (l_recJson.contains("CCIN"))
219         {
220             const auto& l_ccinFromDbus =
221                 vpdSpecificUtility::getCcinFromDbus(l_inventoryPath, l_errCode);
222 
223             // Not an ideal situation as CCIN can't be empty.
224             if (l_ccinFromDbus.empty())
225             {
226                 if (l_errCode)
227                 {
228                     m_logger->logMessage(
229                         "Failed to get CCIN value from DBus, error : " +
230                         commonUtility::getErrCodeMsg(l_errCode));
231                 }
232 
233                 o_failedPathList.push_back(l_fruPath);
234                 continue;
235             }
236 
237             std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"];
238 
239             if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(),
240                      l_ccinFromDbus) == l_ccinListFromJson.end())
241             {
242                 // Don't update PN in this case.
243                 continue;
244             }
245         }
246 
247         for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
248         {
249             // Record name can't be CCIN, skip processing as it is there for PN
250             // update based on CCIN check.
251             if (l_recordName == constants::kwdCCIN)
252             {
253                 continue;
254             }
255 
256             for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
257             {
258                 // Is value of type array.
259                 if (!l_kwdValue.is_array())
260                 {
261                     o_failedPathList.push_back(l_fruPath);
262                     continue;
263                 }
264 
265                 // Get current FRU Part number.
266                 auto l_retVal = dbusUtility::readDbusProperty(
267                     constants::pimServiceName, l_inventoryPath,
268                     constants::viniInf, constants::kwdFN);
269 
270                 auto l_ptrToFn = std::get_if<types::BinaryVector>(&l_retVal);
271 
272                 if (!l_ptrToFn)
273                 {
274                     o_failedPathList.push_back(l_fruPath);
275                     continue;
276                 }
277 
278                 types::BinaryVector l_binaryKwdValue =
279                     l_kwdValue.get<types::BinaryVector>();
280                 if (l_binaryKwdValue == (*l_ptrToFn))
281                 {
282                     continue;
283                 }
284 
285                 // Update part number only if required.
286                 std::shared_ptr<Parser> l_parserObj =
287                     std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
288                 if (l_parserObj->updateVpdKeyword(std::make_tuple(
289                         l_recordName, l_kwdName, l_binaryKwdValue)) ==
290                     constants::FAILURE)
291                 {
292                     o_failedPathList.push_back(l_fruPath);
293                     continue;
294                 }
295 
296                 // update the Asset interface Spare part number explicitly.
297                 if (!dbusUtility::callPIM(types::ObjectMap{
298                         {l_inventoryPath,
299                          {{constants::assetInf,
300                            {{"SparePartNumber",
301                              std::string(l_binaryKwdValue.begin(),
302                                          l_binaryKwdValue.end())}}}}}}))
303                 {
304                     logging::logMessage(
305                         "Updating Spare Part Number under Asset interface failed for path [" +
306                         l_inventoryPath + "]");
307                 }
308 
309                 // Just needed for logging.
310                 std::string l_initialPartNum((*l_ptrToFn).begin(),
311                                              (*l_ptrToFn).end());
312                 std::string l_finalPartNum(l_binaryKwdValue.begin(),
313                                            l_binaryKwdValue.end());
314                 logging::logMessage(
315                     "FRU Part number updated for path [" + l_inventoryPath +
316                     "]" + "From [" + l_initialPartNum + "]" + " to [" +
317                     l_finalPartNum + "]");
318             }
319         }
320     }
321 }
322 
ConfigurePowerVsSystem()323 void IbmHandler::ConfigurePowerVsSystem()
324 {
325     std::vector<std::string> l_failedPathList;
326     try
327     {
328         types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
329         if (l_imValue.empty())
330         {
331             throw DbusException("Invalid IM value read from Dbus");
332         }
333 
334         uint16_t l_errCode = 0;
335         if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue, l_errCode))
336         {
337             // TODO: Should booting be blocked in case of some
338             // misconfigurations?
339             if (l_errCode)
340             {
341                 logging::logMessage(
342                     "Failed to check if the system is powerVs Configuration, error : " +
343                     commonUtility::getErrCodeMsg(l_errCode));
344             }
345 
346             return;
347         }
348 
349         const nlohmann::json& l_powerVsJsonObj =
350             jsonUtility::getPowerVsJson(l_imValue, l_errCode);
351 
352         if (l_powerVsJsonObj.empty())
353         {
354             throw std::runtime_error("PowerVS Json not found. Error : " +
355                                      commonUtility::getErrCodeMsg(l_errCode));
356         }
357 
358         checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList);
359 
360         if (!l_failedPathList.empty())
361         {
362             throw std::runtime_error(
363                 "Part number update failed for following paths: ");
364         }
365     }
366     catch (const std::exception& l_ex)
367     {
368         // TODO log appropriate PEL
369     }
370 }
371 
processFailedEeproms()372 void IbmHandler::processFailedEeproms()
373 {
374     if (m_worker.get() != nullptr)
375     {
376         // TODO:
377         // - iterate through list of EEPROMs for which thread creation has
378         // failed
379         // - For each failed EEPROM, trigger VPD collection
380         m_worker->getFailedEepromPaths().clear();
381     }
382 }
383 
enableMuxChips()384 void IbmHandler::enableMuxChips()
385 {
386     if (m_sysCfgJsonObj.empty())
387     {
388         // config JSON should not be empty at this point of execution.
389         throw std::runtime_error("Config JSON is empty. Can't enable muxes");
390         return;
391     }
392 
393     if (!m_sysCfgJsonObj.contains("muxes"))
394     {
395         logging::logMessage("No mux defined for the system in config JSON");
396         return;
397     }
398 
399     // iterate over each MUX detail and enable them.
400     for (const auto& item : m_sysCfgJsonObj["muxes"])
401     {
402         if (item.contains("holdidlepath"))
403         {
404             std::string cmd = "echo 0 > ";
405             cmd += item["holdidlepath"];
406 
407             logging::logMessage("Enabling mux with command = " + cmd);
408 
409             commonUtility::executeCmd(cmd);
410             continue;
411         }
412 
413         logging::logMessage(
414             "Mux Entry does not have hold idle path. Can't enable the mux");
415     }
416 }
417 
performInitialSetup()418 void IbmHandler::performInitialSetup()
419 {
420     try
421     {
422         if (m_worker.get() == nullptr)
423         {
424             throw std::runtime_error(
425                 "Worker object not found. Can't perform initial setup.");
426         }
427 
428         m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
429         if (!dbusUtility::isChassisPowerOn())
430         {
431             m_worker->setDeviceTreeAndJson();
432 
433             // Since the above function setDeviceTreeAndJson can change the json
434             // which is used, we would need to reacquire the json object again
435             // here.
436             m_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
437         }
438 
439         // Update BMC postion for RBMC prototype system
440         // Ignore BMC position update in case of any error
441         uint16_t l_errCode = 0;
442         if (isRbmcPrototypeSystem(l_errCode))
443         {
444             size_t l_bmcPosition = std::numeric_limits<size_t>::max();
445             checkAndUpdateBmcPosition(l_bmcPosition);
446 
447             if (dbusUtility::callPIM(types::ObjectMap{
448                     {sdbusplus::message::object_path(constants::systemInvPath),
449                      {{constants::rbmcPositionInterface,
450                        {{"Position", l_bmcPosition}}}}}}))
451             {
452                 m_logger->logMessage(
453                     "Updating BMC position failed for path [" +
454                     std::string(constants::systemInvPath) +
455                     "], bmc position: " + std::to_string(l_bmcPosition));
456 
457                 // ToDo: Check is PEL required
458             }
459         }
460         else if (l_errCode != 0)
461         {
462             m_logger->logMessage(
463                 "Unable to determine whether system is RBMC system or not, reason: " +
464                 commonUtility::getErrCodeMsg(l_errCode));
465         }
466 
467         // Enable all mux which are used for connecting to the i2c on the
468         // pcie slots for pcie cards. These are not enabled by kernel due to
469         // an issue seen with Castello cards, where the i2c line hangs on a
470         // probe.
471         enableMuxChips();
472 
473         // Nothing needs to be done. Service restarted or BMC re-booted for
474         // some reason at system power on.
475     }
476     catch (const std::exception& l_ex)
477     {
478         m_worker->setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH,
479                                               constants::vpdCollectionFailed);
480         // Any issue in system's inital set up is handled in this catch. Error
481         // will not propogate to manager.
482         EventLogger::createSyncPel(
483             EventLogger::getErrorType(l_ex), types::SeverityType::Critical,
484             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
485             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
486     }
487 }
488 
collectAllFruVpd()489 void IbmHandler::collectAllFruVpd()
490 {
491     // initialize VPD collection logger
492     m_logger->initiateVpdCollectionLogging();
493 
494     // Setting status to "InProgress", before trigeering VPD collection.
495     m_progressInterface->set_property(
496         "Status", std::string(constants::vpdCollectionInProgress));
497     m_worker->collectFrusFromJson();
498     SetTimerToDetectVpdCollectionStatus();
499 }
500 
isRbmcPrototypeSystem(uint16_t & o_errCode) const501 bool IbmHandler::isRbmcPrototypeSystem(uint16_t& o_errCode) const noexcept
502 {
503     types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
504     if (l_imValue.empty())
505     {
506         o_errCode = error_code::DBUS_FAILURE;
507         return false;
508     }
509 
510     if (constants::rbmcPrototypeSystemImValue == l_imValue)
511     {
512         return true;
513     }
514 
515     return false;
516 }
517 
checkAndUpdateBmcPosition(size_t & o_bmcPosition) const518 void IbmHandler::checkAndUpdateBmcPosition(size_t& o_bmcPosition) const noexcept
519 {
520     if (m_worker.get() == nullptr)
521     {
522         m_logger->logMessage("Worker object not found");
523         return;
524     }
525 
526     const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
527     if (l_sysCfgJsonObj.empty())
528     {
529         m_logger->logMessage(
530             "System config JSON is empty, unable to find BMC position");
531         return;
532     }
533 
534     uint16_t l_errCode = 0;
535     std::string l_motherboardEepromPath = jsonUtility::getFruPathFromJson(
536         l_sysCfgJsonObj, constants::systemVpdInvPath, l_errCode);
537 
538     if (!l_motherboardEepromPath.empty())
539     {
540         o_bmcPosition = constants::VALUE_1;
541         std::error_code l_ec;
542         if (std::filesystem::exists(l_motherboardEepromPath, l_ec))
543         {
544             o_bmcPosition = constants::VALUE_0;
545         }
546     }
547     else if (l_errCode)
548     {
549         m_logger->logMessage("Unable to determine BMC position, reason: " +
550                              commonUtility::getErrCodeMsg(l_errCode));
551     }
552     else
553     {
554         m_logger->logMessage("Unable to determine BMC position, as FRU path[" +
555                              std::string(constants::systemVpdInvPath) +
556                              "], not found in the system config JSON.");
557     }
558 }
559 } // namespace vpd
560