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