blob: b13509359d4abd11b99b27a8c57873b57aa93740 [file] [log] [blame]
Deepak Kodihalli70e8db02019-10-21 00:59:46 -05001#include "fru.hpp"
2
Deepak Kodihallid130e1a2020-06-17 05:55:32 -05003#include "common/utils.hpp"
Deepak Kodihalli70e8db02019-10-21 00:59:46 -05004
George Liuc453e162022-12-21 17:16:23 +08005#include <libpldm/entity.h>
6#include <libpldm/utils.h>
Deepak Kodihalli70e8db02019-10-21 00:59:46 -05007#include <systemd/sd-journal.h>
8
Riya Dixit49cfb132023-03-02 04:26:53 -06009#include <phosphor-logging/lg2.hpp>
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050010#include <sdbusplus/bus.hpp>
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050011
George Liu6492f522020-06-16 10:34:05 +080012#include <iostream>
George Liu5bfb0dc2021-05-01 14:28:41 +080013#include <optional>
George Liu6492f522020-06-16 10:34:05 +080014#include <set>
George Liu5bfb0dc2021-05-01 14:28:41 +080015#include <stack>
George Liu077fea22020-04-08 16:47:14 +080016
Riya Dixit49cfb132023-03-02 04:26:53 -060017PHOSPHOR_LOG2_USING;
18
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050019namespace pldm
20{
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050021namespace responder
22{
George Liu5bfb0dc2021-05-01 14:28:41 +080023
24constexpr auto root = "/xyz/openbmc_project/inventory/";
25
26std::optional<pldm_entity>
27 FruImpl::getEntityByObjectPath(const dbus::InterfaceMap& intfMaps)
28{
29 for (const auto& intfMap : intfMaps)
30 {
31 try
32 {
33 pldm_entity entity{};
34 entity.entity_type = parser.getEntityType(intfMap.first);
35 return entity;
36 }
37 catch (const std::exception& e)
38 {
39 continue;
40 }
41 }
42
43 return std::nullopt;
44}
45
46void FruImpl::updateAssociationTree(const dbus::ObjectValueTree& objects,
47 const std::string& path)
48{
49 if (path.find(root) == std::string::npos)
50 {
51 return;
52 }
53
54 std::stack<std::string> tmpObjPaths{};
55 tmpObjPaths.emplace(path);
56
57 auto obj = pldm::utils::findParent(path);
58 while ((obj + '/') != root)
59 {
60 tmpObjPaths.emplace(obj);
61 obj = pldm::utils::findParent(obj);
62 }
63
64 std::stack<std::string> tmpObj = tmpObjPaths;
65 while (!tmpObj.empty())
66 {
67 std::string s = tmpObj.top();
68 std::cout << s << std::endl;
69 tmpObj.pop();
70 }
71 // Update pldm entity to assocition tree
72 std::string prePath = tmpObjPaths.top();
73 while (!tmpObjPaths.empty())
74 {
75 std::string currPath = tmpObjPaths.top();
76 tmpObjPaths.pop();
77
78 do
79 {
80 if (objToEntityNode.contains(currPath))
81 {
82 pldm_entity node =
83 pldm_entity_extract(objToEntityNode.at(currPath));
84 if (pldm_entity_association_tree_find(entityTree, &node))
85 {
86 break;
87 }
88 }
89 else
90 {
91 if (!objects.contains(currPath))
92 {
93 break;
94 }
95
96 auto entityPtr = getEntityByObjectPath(objects.at(currPath));
97 if (!entityPtr)
98 {
99 break;
100 }
101
102 pldm_entity entity = *entityPtr;
103
104 for (auto& it : objToEntityNode)
105 {
106 pldm_entity node = pldm_entity_extract(it.second);
107 if (node.entity_type == entity.entity_type)
108 {
109 entity.entity_instance_num = node.entity_instance_num +
110 1;
111 break;
112 }
113 }
114
115 if (currPath == prePath)
116 {
117 auto node = pldm_entity_association_tree_add(
118 entityTree, &entity, 0xFFFF, nullptr,
119 PLDM_ENTITY_ASSOCIAION_PHYSICAL);
120 objToEntityNode[currPath] = node;
121 }
122 else
123 {
124 if (objToEntityNode.contains(prePath))
125 {
126 auto node = pldm_entity_association_tree_add(
127 entityTree, &entity, 0xFFFF,
128 objToEntityNode[prePath],
129 PLDM_ENTITY_ASSOCIAION_PHYSICAL);
130 objToEntityNode[currPath] = node;
131 }
132 }
133 }
134 } while (0);
135
136 prePath = currPath;
137 }
138}
139
Tom Joseph33e9c7e2020-06-11 22:09:52 +0530140void FruImpl::buildFRUTable()
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500141{
Tom Joseph33e9c7e2020-06-11 22:09:52 +0530142 if (isBuilt)
143 {
144 return;
145 }
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500146
Tom Josephf0076332020-02-06 10:18:50 +0530147 fru_parser::DBusLookupInfo dbusInfo;
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500148 // Read the all the inventory D-Bus objects
149 auto& bus = pldm::utils::DBusHandler::getBus();
150 dbus::ObjectValueTree objects;
151
152 try
153 {
Tom Joseph33e9c7e2020-06-11 22:09:52 +0530154 dbusInfo = parser.inventoryLookup();
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500155 auto method = bus.new_method_call(
156 std::get<0>(dbusInfo).c_str(), std::get<1>(dbusInfo).c_str(),
157 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
vkaverap@in.ibm.com9138c202023-05-19 07:50:47 -0500158 auto reply = bus.call(
159 method,
160 std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT)).count());
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500161 reply.read(objects);
162 }
163 catch (const std::exception& e)
164 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600165 error(
166 "Look up of inventory objects failed and PLDM FRU table creation failed");
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500167 return;
168 }
169
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500170 auto itemIntfsLookup = std::get<2>(dbusInfo);
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500171
172 for (const auto& object : objects)
173 {
174 const auto& interfaces = object.second;
Sridevi Ramesheefe49b2022-06-27 11:51:02 -0500175 bool isPresent = pldm::utils::checkForFruPresence(object.first.str);
176 // Do not create fru record if fru is not present.
177 // Pick up the next available fru.
178 if (!isPresent)
179 {
180 continue;
181 }
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500182 for (const auto& interface : interfaces)
183 {
184 if (itemIntfsLookup.find(interface.first) != itemIntfsLookup.end())
185 {
186 // An exception will be thrown by getRecordInfo, if the item
187 // D-Bus interface name specified in FRU_Master.json does
188 // not have corresponding config jsons
189 try
190 {
George Liu5bfb0dc2021-05-01 14:28:41 +0800191 updateAssociationTree(objects, object.first.str);
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500192 pldm_entity entity{};
George Liu5bfb0dc2021-05-01 14:28:41 +0800193 if (objToEntityNode.contains(object.first.str))
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500194 {
George Liu5bfb0dc2021-05-01 14:28:41 +0800195 pldm_entity_node* node =
196 objToEntityNode.at(object.first.str);
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500197
George Liu5bfb0dc2021-05-01 14:28:41 +0800198 entity = pldm_entity_extract(node);
199 }
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500200
Tom Joseph33e9c7e2020-06-11 22:09:52 +0530201 auto recordInfos = parser.getRecordInfo(interface.first);
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500202 populateRecords(interfaces, recordInfos, entity);
George Liuc4ea6a92020-07-14 15:48:44 +0800203
204 associatedEntityMap.emplace(object.first, entity);
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500205 break;
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500206 }
207 catch (const std::exception& e)
208 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600209 info(
210 "Config JSONs missing for the item interface type, interface = {INTF}",
211 "INTF", interface.first);
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500212 break;
213 }
214 }
215 }
216 }
217
Andrew Jefferyb0ba34d2023-07-17 17:47:16 +0930218 int rc = pldm_entity_association_pdr_add_check(entityTree, pdrRepo, false,
219 TERMINUS_HANDLE);
220 if (rc < 0)
221 {
222 // pldm_entity_assocation_pdr_add() assert()ed on failure
223 error("Failed to add PLDM entity association PDR: {LIBPLDM_ERROR}",
224 "LIBPLDM_ERROR", rc);
225 throw std::runtime_error("Failed to add PLDM entity association PDR");
226 }
227
Sampa Misrac073a202021-05-08 10:56:05 -0500228 // save a copy of bmc's entity association tree
229 pldm_entity_association_tree_copy_root(entityTree, bmcEntityTree);
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500230
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500231 if (table.size())
232 {
Sridevi Ramesheefe49b2022-06-27 11:51:02 -0500233 padBytes = pldm::utils::getNumPadBytes(table.size());
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500234 table.resize(table.size() + padBytes, 0);
235
236 // Calculate the checksum
George Liu077fea22020-04-08 16:47:14 +0800237 checksum = crc32(table.data(), table.size());
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500238 }
Tom Joseph33e9c7e2020-06-11 22:09:52 +0530239 isBuilt = true;
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500240}
Pavithra Barithaya47180ac2020-10-28 02:12:05 -0500241std::string FruImpl::populatefwVersion()
242{
243 static constexpr auto fwFunctionalObjPath =
244 "/xyz/openbmc_project/software/functional";
245 auto& bus = pldm::utils::DBusHandler::getBus();
246 std::string currentBmcVersion;
247 try
248 {
Patrick Williams6da4f912023-05-10 07:50:53 -0500249 auto method = bus.new_method_call(pldm::utils::mapperService,
250 fwFunctionalObjPath,
251 pldm::utils::dbusProperties, "Get");
Pavithra Barithaya47180ac2020-10-28 02:12:05 -0500252 method.append("xyz.openbmc_project.Association", "endpoints");
253 std::variant<std::vector<std::string>> paths;
vkaverap@in.ibm.com9138c202023-05-19 07:50:47 -0500254 auto reply = bus.call(
255 method,
256 std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT)).count());
Pavithra Barithaya47180ac2020-10-28 02:12:05 -0500257 reply.read(paths);
258 auto fwRunningVersion = std::get<std::vector<std::string>>(paths)[0];
259 constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
260 auto version = pldm::utils::DBusHandler().getDbusPropertyVariant(
261 fwRunningVersion.c_str(), "Version", versionIntf);
262 currentBmcVersion = std::get<std::string>(version);
263 }
264 catch (const std::exception& e)
265 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600266 error("failed to make a d-bus call Asociation, ERROR= {ERR_EXCEP}",
267 "ERR_EXCEP", e.what());
Pavithra Barithaya47180ac2020-10-28 02:12:05 -0500268 return {};
269 }
270 return currentBmcVersion;
271}
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500272void FruImpl::populateRecords(
273 const pldm::responder::dbus::InterfaceMap& interfaces,
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500274 const fru_parser::FruRecordInfos& recordInfos, const pldm_entity& entity)
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500275{
276 // recordSetIdentifier for the FRU will be set when the first record gets
277 // added for the FRU
278 uint16_t recordSetIdentifier = 0;
279 auto numRecsCount = numRecs;
Pavithra Barithaya4f2538a2021-03-05 07:32:15 -0600280 static uint32_t bmc_record_handle = 0;
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500281
Patrick Williams6da4f912023-05-10 07:50:53 -0500282 for (const auto& [recType, encType, fieldInfos] : recordInfos)
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500283 {
284 std::vector<uint8_t> tlvs;
285 uint8_t numFRUFields = 0;
Patrick Williams6da4f912023-05-10 07:50:53 -0500286 for (const auto& [intf, prop, propType, fieldTypeNum] : fieldInfos)
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500287 {
288 try
289 {
Pavithra Barithaya47180ac2020-10-28 02:12:05 -0500290 pldm::responder::dbus::Value propValue;
Manojkiran Eda2b7c1bf2021-09-09 12:26:00 +0530291
292 // Assuming that 0 container Id is assigned to the System (as
293 // that should be the top most container as per dbus hierarchy)
294 if (entity.entity_container_id == 0 && prop == "Version")
Pavithra Barithaya47180ac2020-10-28 02:12:05 -0500295 {
296 propValue = populatefwVersion();
297 }
298 else
299 {
300 propValue = interfaces.at(intf).at(prop);
301 }
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500302 if (propType == "bytearray")
303 {
304 auto byteArray = std::get<std::vector<uint8_t>>(propValue);
305 if (!byteArray.size())
306 {
307 continue;
308 }
309
310 numFRUFields++;
311 tlvs.emplace_back(fieldTypeNum);
312 tlvs.emplace_back(byteArray.size());
313 std::move(std::begin(byteArray), std::end(byteArray),
314 std::back_inserter(tlvs));
315 }
316 else if (propType == "string")
317 {
318 auto str = std::get<std::string>(propValue);
319 if (!str.size())
320 {
321 continue;
322 }
323
324 numFRUFields++;
325 tlvs.emplace_back(fieldTypeNum);
326 tlvs.emplace_back(str.size());
327 std::move(std::begin(str), std::end(str),
328 std::back_inserter(tlvs));
329 }
330 }
331 catch (const std::out_of_range& e)
332 {
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500333 continue;
334 }
335 }
336
337 if (tlvs.size())
338 {
339 if (numRecs == numRecsCount)
340 {
341 recordSetIdentifier = nextRSI();
Pavithra Barithaya4f2538a2021-03-05 07:32:15 -0600342 bmc_record_handle = nextRecordHandle();
Andrew Jeffery2e248e82023-07-03 17:23:24 +0930343 int rc = pldm_pdr_add_fru_record_set_check(
Manojkiran Edacc5f1582021-09-29 17:03:06 +0530344 pdrRepo, TERMINUS_HANDLE, recordSetIdentifier,
345 entity.entity_type, entity.entity_instance_num,
Andrew Jeffery2e248e82023-07-03 17:23:24 +0930346 entity.entity_container_id, &bmc_record_handle);
347 if (rc)
348 {
349 // pldm_pdr_add_fru_record_set() assert()ed on failure
350 throw std::runtime_error(
351 "Failed to add PDR FRU record set");
352 }
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500353 }
354 auto curSize = table.size();
355 table.resize(curSize + recHeaderSize + tlvs.size());
356 encode_fru_record(table.data(), table.size(), &curSize,
357 recordSetIdentifier, recType, numFRUFields,
358 encType, tlvs.data(), tlvs.size());
359 numRecs++;
360 }
361 }
362}
363
364void FruImpl::getFRUTable(Response& response)
365{
366 auto hdrSize = response.size();
367
368 response.resize(hdrSize + table.size() + sizeof(checksum), 0);
369 std::copy(table.begin(), table.end(), response.begin() + hdrSize);
370
371 // Copy the checksum to response data
372 auto iter = response.begin() + hdrSize + table.size();
373 std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum),
374 iter);
375}
376
John Wang9e82ad12020-06-12 10:53:32 +0800377int FruImpl::getFRURecordByOption(std::vector<uint8_t>& fruData,
378 uint16_t /* fruTableHandle */,
379 uint16_t recordSetIdentifer,
380 uint8_t recordType, uint8_t fieldType)
381{
Manojkiran Eda31a78442021-09-12 15:18:25 +0530382 using sum = uint32_t;
383
John Wang9e82ad12020-06-12 10:53:32 +0800384 // FRU table is built lazily, build if not done.
385 buildFRUTable();
386
387 /* 7 is sizeof(checksum,4) + padBytesMax(3)
388 * We can not know size of the record table got by options in advance, but
389 * it must be less than the source table. So it's safe to use sizeof the
390 * source table + 7 as the buffer length
391 */
392 size_t recordTableSize = table.size() - padBytes + 7;
393 fruData.resize(recordTableSize, 0);
394
Andrew Jeffery663783b2023-07-03 12:58:14 +0930395 int rc = get_fru_record_by_option_check(
396 table.data(), table.size() - padBytes, fruData.data(), &recordTableSize,
397 recordSetIdentifer, recordType, fieldType);
John Wang9e82ad12020-06-12 10:53:32 +0800398
Andrew Jeffery663783b2023-07-03 12:58:14 +0930399 if (rc != PLDM_SUCCESS || recordTableSize == 0)
John Wang9e82ad12020-06-12 10:53:32 +0800400 {
401 return PLDM_FRU_DATA_STRUCTURE_TABLE_UNAVAILABLE;
402 }
403
Sridevi Ramesheefe49b2022-06-27 11:51:02 -0500404 auto pads = pldm::utils::getNumPadBytes(recordTableSize);
Manojkiran Eda31a78442021-09-12 15:18:25 +0530405 crc32(fruData.data(), recordTableSize + pads);
John Wang9e82ad12020-06-12 10:53:32 +0800406
407 auto iter = fruData.begin() + recordTableSize + pads;
408 std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum),
409 iter);
410 fruData.resize(recordTableSize + pads + sizeof(sum));
411
412 return PLDM_SUCCESS;
413}
414
Deepak Kodihallie60c5822019-10-23 03:26:15 -0500415namespace fru
416{
Deepak Kodihallie60c5822019-10-23 03:26:15 -0500417Response Handler::getFRURecordTableMetadata(const pldm_msg* request,
418 size_t /*payloadLength*/)
419{
Tom Joseph33e9c7e2020-06-11 22:09:52 +0530420 // FRU table is built lazily, build if not done.
421 buildFRUTable();
422
Deepak Kodihallie60c5822019-10-23 03:26:15 -0500423 constexpr uint8_t major = 0x01;
424 constexpr uint8_t minor = 0x00;
425 constexpr uint32_t maxSize = 0xFFFFFFFF;
426
427 Response response(sizeof(pldm_msg_hdr) +
428 PLDM_GET_FRU_RECORD_TABLE_METADATA_RESP_BYTES,
429 0);
430 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
431
432 auto rc = encode_get_fru_record_table_metadata_resp(
433 request->hdr.instance_id, PLDM_SUCCESS, major, minor, maxSize,
434 impl.size(), impl.numRSI(), impl.numRecords(), impl.checkSum(),
435 responsePtr);
436 if (rc != PLDM_SUCCESS)
437 {
438 return ccOnlyResponse(request, rc);
439 }
440
441 return response;
442}
443
444Response Handler::getFRURecordTable(const pldm_msg* request,
445 size_t payloadLength)
446{
Tom Joseph33e9c7e2020-06-11 22:09:52 +0530447 // FRU table is built lazily, build if not done.
448 buildFRUTable();
449
Deepak Kodihallie60c5822019-10-23 03:26:15 -0500450 if (payloadLength != PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES)
451 {
452 return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
453 }
454
455 Response response(
456 sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES, 0);
457 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
458
Patrick Williams6da4f912023-05-10 07:50:53 -0500459 auto rc = encode_get_fru_record_table_resp(request->hdr.instance_id,
460 PLDM_SUCCESS, 0,
461 PLDM_START_AND_END, responsePtr);
Deepak Kodihallie60c5822019-10-23 03:26:15 -0500462 if (rc != PLDM_SUCCESS)
463 {
464 return ccOnlyResponse(request, rc);
465 }
466
467 impl.getFRUTable(response);
468
469 return response;
470}
471
John Wang9e82ad12020-06-12 10:53:32 +0800472Response Handler::getFRURecordByOption(const pldm_msg* request,
473 size_t payloadLength)
474{
475 if (payloadLength != sizeof(pldm_get_fru_record_by_option_req))
476 {
477 return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
478 }
479
480 uint32_t retDataTransferHandle{};
481 uint16_t retFruTableHandle{};
482 uint16_t retRecordSetIdentifier{};
483 uint8_t retRecordType{};
484 uint8_t retFieldType{};
485 uint8_t retTransferOpFlag{};
486
487 auto rc = decode_get_fru_record_by_option_req(
488 request, payloadLength, &retDataTransferHandle, &retFruTableHandle,
489 &retRecordSetIdentifier, &retRecordType, &retFieldType,
490 &retTransferOpFlag);
491
492 if (rc != PLDM_SUCCESS)
493 {
494 return ccOnlyResponse(request, rc);
495 }
496
497 std::vector<uint8_t> fruData;
498 rc = impl.getFRURecordByOption(fruData, retFruTableHandle,
499 retRecordSetIdentifier, retRecordType,
500 retFieldType);
501 if (rc != PLDM_SUCCESS)
502 {
503 return ccOnlyResponse(request, rc);
504 }
505
Patrick Williams6da4f912023-05-10 07:50:53 -0500506 auto respPayloadLength = PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES +
507 fruData.size();
John Wang9e82ad12020-06-12 10:53:32 +0800508 Response response(sizeof(pldm_msg_hdr) + respPayloadLength, 0);
509 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
510
511 rc = encode_get_fru_record_by_option_resp(
512 request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END,
513 fruData.data(), fruData.size(), responsePtr, respPayloadLength);
514
515 if (rc != PLDM_SUCCESS)
516 {
517 return ccOnlyResponse(request, rc);
518 }
519
520 return response;
521}
522
Deepak Kodihallie60c5822019-10-23 03:26:15 -0500523} // namespace fru
524
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500525} // namespace responder
526
527} // namespace pldm