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