blob: fadd9657981683f243ac95ebca16bbcfe474bfb2 [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#include "config.h"
2
3#include "manager.hpp"
4
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05005#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"
Rekha Aparnaffdff312025-03-25 01:10:56 -050011#include "single_fab.hpp"
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050012#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
21namespace vpd
22{
23Manager::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{
Jinu Joy Thomas3419f5d2025-04-09 04:04:18 -050029#ifdef IBM_SYSTEM
Rekha Aparnaffdff312025-03-25 01:10:56 -050030 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
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050044 try
45 {
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050046 // 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; });
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530132
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);
Sunny Srivastavac74c8ef2025-04-16 12:45:27 +0530138#else
139 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
140 m_interface->set_property("CollectionStatus", std::string("Completed"));
141#endif
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500142 }
143 catch (const std::exception& e)
144 {
145 logging::logMessage(
Sunny Srivastava4c509c22025-03-25 12:43:40 +0530146 "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);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500152 }
153}
154
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500155int 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 {
RekhaAparna017fea9f52025-02-17 04:14:02 -0600174 l_fruPath =
175 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500176 }
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);
Anupama B R8dedd1e2025-03-24 07:43:47 -0500188 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 }
Souvik Roy2ee8a212025-04-24 02:37:59 -0500200
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
Anupama B R8dedd1e2025-03-24 07:43:47 -0500208 return l_rc;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500209 }
210 catch (const std::exception& l_exception)
211 {
212 // TODO:: error log needed
213 logging::logMessage("Update keyword failed for file[" + i_vpdPath +
214 "], reason: " + std::string(l_exception.what()));
215 return -1;
216 }
217}
218
219int Manager::updateKeywordOnHardware(
220 const types::Path i_fruPath,
221 const types::WriteVpdParams i_paramsToWriteData) noexcept
222{
223 try
224 {
225 if (i_fruPath.empty())
226 {
227 throw std::runtime_error("Given FRU path is empty");
228 }
229
230 nlohmann::json l_sysCfgJsonObj{};
231
232 if (m_worker.get() != nullptr)
233 {
234 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
235 }
236
237 std::shared_ptr<Parser> l_parserObj =
238 std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj);
239 return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData);
240 }
241 catch (const std::exception& l_exception)
242 {
243 EventLogger::createAsyncPel(
244 types::ErrorType::InvalidEeprom, types::SeverityType::Informational,
245 __FILE__, __FUNCTION__, 0,
246 "Update keyword on hardware failed for file[" + i_fruPath +
247 "], reason: " + std::string(l_exception.what()),
248 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
249
250 return constants::FAILURE;
251 }
252}
253
254types::DbusVariantType Manager::readKeyword(
255 const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData)
256{
257 try
258 {
259 nlohmann::json l_jsonObj{};
260
261 if (m_worker.get() != nullptr)
262 {
263 l_jsonObj = m_worker->getSysCfgJsonObj();
264 }
265
266 std::error_code ec;
267
268 // Check if given path is filesystem path
269 if (!std::filesystem::exists(i_fruPath, ec) && (ec))
270 {
271 throw std::runtime_error(
272 "Given file path " + i_fruPath + " not found.");
273 }
274
275 logging::logMessage("Performing VPD read on " + i_fruPath);
276
277 std::shared_ptr<vpd::Parser> l_parserObj =
278 std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj);
279
280 std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance =
281 l_parserObj->getVpdParserInstance();
282
283 return (
284 l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData));
285 }
286 catch (const std::exception& e)
287 {
288 logging::logMessage(
289 e.what() + std::string(". VPD manager read operation failed for ") +
290 i_fruPath);
291 throw types::DeviceError::ReadFailure();
292 }
293}
294
295void Manager::collectSingleFruVpd(
296 const sdbusplus::message::object_path& i_dbusObjPath)
297{
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530298 if (m_vpdCollectionStatus != "Completed")
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500299 {
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530300 logging::logMessage(
301 "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
302 std::string(i_dbusObjPath));
303 return;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500304 }
Priyanga Ramasamy46b73d92025-01-09 10:52:07 -0600305
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530306 if (m_worker.get() != nullptr)
307 {
308 m_worker->collectSingleFruVpd(i_dbusObjPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500309 }
310}
311
312void Manager::deleteSingleFruVpd(
313 const sdbusplus::message::object_path& i_dbusObjPath)
314{
315 try
316 {
317 if (std::string(i_dbusObjPath).empty())
318 {
319 throw std::runtime_error(
320 "Given DBus object path is empty. Aborting FRU VPD deletion.");
321 }
322
323 if (m_worker.get() == nullptr)
324 {
325 throw std::runtime_error(
326 "Worker object not found, can't perform FRU VPD deletion for: " +
327 std::string(i_dbusObjPath));
328 }
329
330 m_worker->deleteFruVpd(std::string(i_dbusObjPath));
331 }
332 catch (const std::exception& l_ex)
333 {
334 // TODO: Log PEL
335 logging::logMessage(l_ex.what());
336 }
337}
338
339bool Manager::isValidUnexpandedLocationCode(
340 const std::string& i_unexpandedLocationCode)
341{
342 if ((i_unexpandedLocationCode.length() <
343 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) ||
344 ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") !=
345 constants::STR_CMP_SUCCESS) &&
346 (i_unexpandedLocationCode.compare(0, 4, "Umts") !=
347 constants::STR_CMP_SUCCESS)) ||
348 ((i_unexpandedLocationCode.length() >
349 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) &&
350 (i_unexpandedLocationCode.find("-") != 4)))
351 {
352 return false;
353 }
354
355 return true;
356}
357
358std::string Manager::getExpandedLocationCode(
359 const std::string& i_unexpandedLocationCode,
360 [[maybe_unused]] const uint16_t i_nodeNumber)
361{
362 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
363 {
364 phosphor::logging::elog<types::DbusInvalidArgument>(
365 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
366 types::InvalidArgument::ARGUMENT_VALUE(
367 i_unexpandedLocationCode.c_str()));
368 }
369
370 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
371 if (!l_sysCfgJsonObj.contains("frus"))
372 {
373 logging::logMessage("Missing frus tag in system config JSON");
374 }
375
376 const nlohmann::json& l_listOfFrus =
377 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
378
379 for (const auto& l_frus : l_listOfFrus.items())
380 {
381 for (const auto& l_aFru : l_frus.value())
382 {
383 if (l_aFru["extraInterfaces"].contains(
384 constants::locationCodeInf) &&
385 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
386 "LocationCode", "") == i_unexpandedLocationCode)
387 {
388 return std::get<std::string>(dbusUtility::readDbusProperty(
389 l_aFru["serviceName"], l_aFru["inventoryPath"],
390 constants::locationCodeInf, "LocationCode"));
391 }
392 }
393 }
394 phosphor::logging::elog<types::DbusInvalidArgument>(
395 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
396 types::InvalidArgument::ARGUMENT_VALUE(
397 i_unexpandedLocationCode.c_str()));
398}
399
400types::ListOfPaths Manager::getFrusByUnexpandedLocationCode(
401 const std::string& i_unexpandedLocationCode,
402 [[maybe_unused]] const uint16_t i_nodeNumber)
403{
404 types::ListOfPaths l_inventoryPaths;
405
406 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
407 {
408 phosphor::logging::elog<types::DbusInvalidArgument>(
409 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
410 types::InvalidArgument::ARGUMENT_VALUE(
411 i_unexpandedLocationCode.c_str()));
412 }
413
414 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
415 if (!l_sysCfgJsonObj.contains("frus"))
416 {
417 logging::logMessage("Missing frus tag in system config JSON");
418 }
419
420 const nlohmann::json& l_listOfFrus =
421 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
422
423 for (const auto& l_frus : l_listOfFrus.items())
424 {
425 for (const auto& l_aFru : l_frus.value())
426 {
427 if (l_aFru["extraInterfaces"].contains(
428 constants::locationCodeInf) &&
429 l_aFru["extraInterfaces"][constants::locationCodeInf].value(
430 "LocationCode", "") == i_unexpandedLocationCode)
431 {
432 l_inventoryPaths.push_back(
433 l_aFru.at("inventoryPath")
434 .get_ref<const nlohmann::json::string_t&>());
435 }
436 }
437 }
438
439 if (l_inventoryPaths.empty())
440 {
441 phosphor::logging::elog<types::DbusInvalidArgument>(
442 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
443 types::InvalidArgument::ARGUMENT_VALUE(
444 i_unexpandedLocationCode.c_str()));
445 }
446
447 return l_inventoryPaths;
448}
449
Patrick Williams43fedab2025-02-03 14:28:05 -0500450std::string Manager::getHwPath(
451 const sdbusplus::message::object_path& i_dbusObjPath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500452{
453 // Dummy code to supress unused variable warning. To be removed.
454 logging::logMessage(std::string(i_dbusObjPath));
455
456 return std::string{};
457}
458
459std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode(
460 const std::string& i_expandedLocationCode)
461{
462 /**
463 * Location code should always start with U and fulfil minimum length
464 * criteria.
465 */
466 if (i_expandedLocationCode[0] != 'U' ||
467 i_expandedLocationCode.length() <
468 constants::EXP_LOCATION_CODE_MIN_LENGTH)
469 {
470 phosphor::logging::elog<types::DbusInvalidArgument>(
471 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
472 types::InvalidArgument::ARGUMENT_VALUE(
473 i_expandedLocationCode.c_str()));
474 }
475
476 std::string l_fcKwd;
477
478 auto l_fcKwdValue = dbusUtility::readDbusProperty(
479 "xyz.openbmc_project.Inventory.Manager",
480 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
481 "com.ibm.ipzvpd.VCEN", "FC");
482
483 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue))
484 {
485 l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
486 }
487
488 // Get the first part of expanded location code to check for FC or TM.
489 std::string l_firstKwd = i_expandedLocationCode.substr(1, 4);
490
491 std::string l_unexpandedLocationCode{};
492 uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER;
493
494 // Check if this value matches the value of FC keyword.
495 if (l_fcKwd.substr(0, 4) == l_firstKwd)
496 {
497 /**
498 * Period(.) should be there in expanded location code to seggregate
499 * FC, node number and SE values.
500 */
501 size_t l_nodeStartPos = i_expandedLocationCode.find('.');
502 if (l_nodeStartPos == std::string::npos)
503 {
504 phosphor::logging::elog<types::DbusInvalidArgument>(
505 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
506 types::InvalidArgument::ARGUMENT_VALUE(
507 i_expandedLocationCode.c_str()));
508 }
509
510 size_t l_nodeEndPos =
511 i_expandedLocationCode.find('.', l_nodeStartPos + 1);
512 if (l_nodeEndPos == std::string::npos)
513 {
514 phosphor::logging::elog<types::DbusInvalidArgument>(
515 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
516 types::InvalidArgument::ARGUMENT_VALUE(
517 i_expandedLocationCode.c_str()));
518 }
519
520 // Skip 3 bytes for '.ND'
521 l_nodeNummber = std::stoi(i_expandedLocationCode.substr(
522 l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3)));
523
524 /**
525 * Confirm if there are other details apart FC, node number and SE
526 * in location code
527 */
528 if (i_expandedLocationCode.length() >
529 constants::EXP_LOCATION_CODE_MIN_LENGTH)
530 {
531 l_unexpandedLocationCode =
532 i_expandedLocationCode[0] + std::string("fcs") +
533 i_expandedLocationCode.substr(
534 l_nodeEndPos + 1 + constants::SE_KWD_LENGTH,
535 std::string::npos);
536 }
537 else
538 {
539 l_unexpandedLocationCode = "Ufcs";
540 }
541 }
542 else
543 {
544 std::string l_tmKwd;
545 // Read TM keyword value.
546 auto l_tmKwdValue = dbusUtility::readDbusProperty(
547 "xyz.openbmc_project.Inventory.Manager",
548 "/xyz/openbmc_project/inventory/system/chassis/motherboard",
549 "com.ibm.ipzvpd.VSYS", "TM");
550
551 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue))
552 {
553 l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
554 }
555
556 // Check if the substr matches to TM keyword value.
557 if (l_tmKwd.substr(0, 4) == l_firstKwd)
558 {
559 /**
560 * System location code will not have node number and any other
561 * details.
562 */
563 l_unexpandedLocationCode = "Umts";
564 }
565 // The given location code is neither "fcs" or "mts".
566 else
567 {
568 phosphor::logging::elog<types::DbusInvalidArgument>(
569 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
570 types::InvalidArgument::ARGUMENT_VALUE(
571 i_expandedLocationCode.c_str()));
572 }
573 }
574
575 return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber);
576}
577
578types::ListOfPaths Manager::getFrusByExpandedLocationCode(
579 const std::string& i_expandedLocationCode)
580{
581 std::tuple<std::string, uint16_t> l_locationAndNodePair =
582 getUnexpandedLocationCode(i_expandedLocationCode);
583
584 return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair),
585 std::get<1>(l_locationAndNodePair));
586}
587
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500588void Manager::performVpdRecollection()
589{
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530590 if (m_worker.get() != nullptr)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500591 {
Sunny Srivastava380efbb2025-04-25 10:28:30 +0530592 m_worker->performVpdRecollection();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500593 }
594}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500595} // namespace vpd