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