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