blob: 419f780ba25efa63c0b73fae6899e79f9d429d05 [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
Manojkiran Edacc5f1582021-09-29 17:03:06 +05305#include <config.h>
George Liuc453e162022-12-21 17:16:23 +08006#include <libpldm/entity.h>
7#include <libpldm/utils.h>
Deepak Kodihalli70e8db02019-10-21 00:59:46 -05008#include <systemd/sd-journal.h>
9
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>
13#include <set>
George Liu077fea22020-04-08 16:47:14 +080014
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050015namespace pldm
16{
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050017namespace responder
18{
Tom Joseph33e9c7e2020-06-11 22:09:52 +053019void FruImpl::buildFRUTable()
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050020{
Tom Joseph33e9c7e2020-06-11 22:09:52 +053021 if (isBuilt)
22 {
23 return;
24 }
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050025
Tom Josephf0076332020-02-06 10:18:50 +053026 fru_parser::DBusLookupInfo dbusInfo;
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050027 // Read the all the inventory D-Bus objects
28 auto& bus = pldm::utils::DBusHandler::getBus();
29 dbus::ObjectValueTree objects;
30
31 try
32 {
Tom Joseph33e9c7e2020-06-11 22:09:52 +053033 dbusInfo = parser.inventoryLookup();
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050034 auto method = bus.new_method_call(
35 std::get<0>(dbusInfo).c_str(), std::get<1>(dbusInfo).c_str(),
36 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
37 auto reply = bus.call(method);
38 reply.read(objects);
39 }
40 catch (const std::exception& e)
41 {
42 std::cerr << "Look up of inventory objects failed and PLDM FRU table "
Brad Bishop0b540732021-08-18 09:42:41 -040043 "creation failed\n";
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050044 return;
45 }
46
Deepak Kodihalli3cd61812020-03-10 06:38:45 -050047 auto itemIntfsLookup = std::get<2>(dbusInfo);
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050048
49 for (const auto& object : objects)
50 {
51 const auto& interfaces = object.second;
Sridevi Ramesheefe49b2022-06-27 11:51:02 -050052 bool isPresent = pldm::utils::checkForFruPresence(object.first.str);
53 // Do not create fru record if fru is not present.
54 // Pick up the next available fru.
55 if (!isPresent)
56 {
57 continue;
58 }
Deepak Kodihalli70e8db02019-10-21 00:59:46 -050059 for (const auto& interface : interfaces)
60 {
61 if (itemIntfsLookup.find(interface.first) != itemIntfsLookup.end())
62 {
63 // An exception will be thrown by getRecordInfo, if the item
64 // D-Bus interface name specified in FRU_Master.json does
65 // not have corresponding config jsons
66 try
67 {
Deepak Kodihalli3cd61812020-03-10 06:38:45 -050068 pldm_entity entity{};
Tom Joseph33e9c7e2020-06-11 22:09:52 +053069 entity.entity_type = parser.getEntityType(interface.first);
Deepak Kodihalli3cd61812020-03-10 06:38:45 -050070 pldm_entity_node* parent = nullptr;
71 auto parentObj = pldm::utils::findParent(object.first.str);
72 // To add a FRU to the entity association tree, we need to
73 // determine if the FRU has a parent (D-Bus object). For eg
74 // /system/backplane's parent is /system. /system has no
75 // parent. Some D-Bus pathnames might just be namespaces
76 // (not D-Bus objects), so we need to iterate upwards until
77 // a parent is found, or we reach the root ("/").
78 // Parents are always added first before children in the
79 // entity association tree. We're relying on the fact that
80 // the std::map containing object paths from the
81 // GetManagedObjects call will have a sorted pathname list.
82 do
83 {
84 auto iter = objToEntityNode.find(parentObj);
85 if (iter != objToEntityNode.end())
86 {
87 parent = iter->second;
88 break;
89 }
90 parentObj = pldm::utils::findParent(parentObj);
91 } while (parentObj != "/");
92
93 auto node = pldm_entity_association_tree_add(
George Liu64a8f0f2021-06-12 10:56:11 +080094 entityTree, &entity, 0xFFFF, parent,
Deepak Kodihalli3cd61812020-03-10 06:38:45 -050095 PLDM_ENTITY_ASSOCIAION_PHYSICAL);
96 objToEntityNode[object.first.str] = node;
97
Tom Joseph33e9c7e2020-06-11 22:09:52 +053098 auto recordInfos = parser.getRecordInfo(interface.first);
Deepak Kodihalli3cd61812020-03-10 06:38:45 -050099 populateRecords(interfaces, recordInfos, entity);
George Liuc4ea6a92020-07-14 15:48:44 +0800100
101 associatedEntityMap.emplace(object.first, entity);
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500102 break;
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500103 }
104 catch (const std::exception& e)
105 {
106 std::cout << "Config JSONs missing for the item "
107 "interface type, interface = "
108 << interface.first << "\n";
109 break;
110 }
111 }
112 }
113 }
114
Manojkiran Eda3ca40452021-10-04 22:51:37 +0530115 pldm_entity_association_pdr_add(entityTree, pdrRepo, false,
116 TERMINUS_HANDLE);
Sampa Misrac073a202021-05-08 10:56:05 -0500117 // save a copy of bmc's entity association tree
118 pldm_entity_association_tree_copy_root(entityTree, bmcEntityTree);
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500119
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500120 if (table.size())
121 {
Sridevi Ramesheefe49b2022-06-27 11:51:02 -0500122 padBytes = pldm::utils::getNumPadBytes(table.size());
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500123 table.resize(table.size() + padBytes, 0);
124
125 // Calculate the checksum
George Liu077fea22020-04-08 16:47:14 +0800126 checksum = crc32(table.data(), table.size());
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500127 }
Tom Joseph33e9c7e2020-06-11 22:09:52 +0530128 isBuilt = true;
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500129}
Pavithra Barithaya47180ac2020-10-28 02:12:05 -0500130std::string FruImpl::populatefwVersion()
131{
132 static constexpr auto fwFunctionalObjPath =
133 "/xyz/openbmc_project/software/functional";
134 auto& bus = pldm::utils::DBusHandler::getBus();
135 std::string currentBmcVersion;
136 try
137 {
138 auto method =
139 bus.new_method_call(pldm::utils::mapperService, fwFunctionalObjPath,
140 pldm::utils::dbusProperties, "Get");
141 method.append("xyz.openbmc_project.Association", "endpoints");
142 std::variant<std::vector<std::string>> paths;
143 auto reply = bus.call(method);
144 reply.read(paths);
145 auto fwRunningVersion = std::get<std::vector<std::string>>(paths)[0];
146 constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
147 auto version = pldm::utils::DBusHandler().getDbusPropertyVariant(
148 fwRunningVersion.c_str(), "Version", versionIntf);
149 currentBmcVersion = std::get<std::string>(version);
150 }
151 catch (const std::exception& e)
152 {
153 std::cerr << "failed to make a d-bus call "
154 "Asociation, ERROR= "
155 << e.what() << "\n";
156 return {};
157 }
158 return currentBmcVersion;
159}
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500160void FruImpl::populateRecords(
161 const pldm::responder::dbus::InterfaceMap& interfaces,
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500162 const fru_parser::FruRecordInfos& recordInfos, const pldm_entity& entity)
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500163{
164 // recordSetIdentifier for the FRU will be set when the first record gets
165 // added for the FRU
166 uint16_t recordSetIdentifier = 0;
167 auto numRecsCount = numRecs;
Pavithra Barithaya4f2538a2021-03-05 07:32:15 -0600168 static uint32_t bmc_record_handle = 0;
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500169
170 for (auto const& [recType, encType, fieldInfos] : recordInfos)
171 {
172 std::vector<uint8_t> tlvs;
173 uint8_t numFRUFields = 0;
174 for (auto const& [intf, prop, propType, fieldTypeNum] : fieldInfos)
175 {
176 try
177 {
Pavithra Barithaya47180ac2020-10-28 02:12:05 -0500178 pldm::responder::dbus::Value propValue;
Manojkiran Eda2b7c1bf2021-09-09 12:26:00 +0530179
180 // Assuming that 0 container Id is assigned to the System (as
181 // that should be the top most container as per dbus hierarchy)
182 if (entity.entity_container_id == 0 && prop == "Version")
Pavithra Barithaya47180ac2020-10-28 02:12:05 -0500183 {
184 propValue = populatefwVersion();
185 }
186 else
187 {
188 propValue = interfaces.at(intf).at(prop);
189 }
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500190 if (propType == "bytearray")
191 {
192 auto byteArray = std::get<std::vector<uint8_t>>(propValue);
193 if (!byteArray.size())
194 {
195 continue;
196 }
197
198 numFRUFields++;
199 tlvs.emplace_back(fieldTypeNum);
200 tlvs.emplace_back(byteArray.size());
201 std::move(std::begin(byteArray), std::end(byteArray),
202 std::back_inserter(tlvs));
203 }
204 else if (propType == "string")
205 {
206 auto str = std::get<std::string>(propValue);
207 if (!str.size())
208 {
209 continue;
210 }
211
212 numFRUFields++;
213 tlvs.emplace_back(fieldTypeNum);
214 tlvs.emplace_back(str.size());
215 std::move(std::begin(str), std::end(str),
216 std::back_inserter(tlvs));
217 }
218 }
219 catch (const std::out_of_range& e)
220 {
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500221 continue;
222 }
223 }
224
225 if (tlvs.size())
226 {
227 if (numRecs == numRecsCount)
228 {
229 recordSetIdentifier = nextRSI();
Pavithra Barithaya4f2538a2021-03-05 07:32:15 -0600230 bmc_record_handle = nextRecordHandle();
Deepak Kodihalli3cd61812020-03-10 06:38:45 -0500231 pldm_pdr_add_fru_record_set(
Manojkiran Edacc5f1582021-09-29 17:03:06 +0530232 pdrRepo, TERMINUS_HANDLE, recordSetIdentifier,
233 entity.entity_type, entity.entity_instance_num,
234 entity.entity_container_id, bmc_record_handle);
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500235 }
236 auto curSize = table.size();
237 table.resize(curSize + recHeaderSize + tlvs.size());
238 encode_fru_record(table.data(), table.size(), &curSize,
239 recordSetIdentifier, recType, numFRUFields,
240 encType, tlvs.data(), tlvs.size());
241 numRecs++;
242 }
243 }
244}
245
246void FruImpl::getFRUTable(Response& response)
247{
248 auto hdrSize = response.size();
249
250 response.resize(hdrSize + table.size() + sizeof(checksum), 0);
251 std::copy(table.begin(), table.end(), response.begin() + hdrSize);
252
253 // Copy the checksum to response data
254 auto iter = response.begin() + hdrSize + table.size();
255 std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum),
256 iter);
257}
258
John Wang9e82ad12020-06-12 10:53:32 +0800259int FruImpl::getFRURecordByOption(std::vector<uint8_t>& fruData,
260 uint16_t /* fruTableHandle */,
261 uint16_t recordSetIdentifer,
262 uint8_t recordType, uint8_t fieldType)
263{
Manojkiran Eda31a78442021-09-12 15:18:25 +0530264 using sum = uint32_t;
265
John Wang9e82ad12020-06-12 10:53:32 +0800266 // FRU table is built lazily, build if not done.
267 buildFRUTable();
268
269 /* 7 is sizeof(checksum,4) + padBytesMax(3)
270 * We can not know size of the record table got by options in advance, but
271 * it must be less than the source table. So it's safe to use sizeof the
272 * source table + 7 as the buffer length
273 */
274 size_t recordTableSize = table.size() - padBytes + 7;
275 fruData.resize(recordTableSize, 0);
276
277 get_fru_record_by_option(table.data(), table.size() - padBytes,
278 fruData.data(), &recordTableSize,
279 recordSetIdentifer, recordType, fieldType);
280
281 if (recordTableSize == 0)
282 {
283 return PLDM_FRU_DATA_STRUCTURE_TABLE_UNAVAILABLE;
284 }
285
Sridevi Ramesheefe49b2022-06-27 11:51:02 -0500286 auto pads = pldm::utils::getNumPadBytes(recordTableSize);
Manojkiran Eda31a78442021-09-12 15:18:25 +0530287 crc32(fruData.data(), recordTableSize + pads);
John Wang9e82ad12020-06-12 10:53:32 +0800288
289 auto iter = fruData.begin() + recordTableSize + pads;
290 std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum),
291 iter);
292 fruData.resize(recordTableSize + pads + sizeof(sum));
293
294 return PLDM_SUCCESS;
295}
296
Deepak Kodihallie60c5822019-10-23 03:26:15 -0500297namespace fru
298{
Deepak Kodihallie60c5822019-10-23 03:26:15 -0500299Response Handler::getFRURecordTableMetadata(const pldm_msg* request,
300 size_t /*payloadLength*/)
301{
Tom Joseph33e9c7e2020-06-11 22:09:52 +0530302 // FRU table is built lazily, build if not done.
303 buildFRUTable();
304
Deepak Kodihallie60c5822019-10-23 03:26:15 -0500305 constexpr uint8_t major = 0x01;
306 constexpr uint8_t minor = 0x00;
307 constexpr uint32_t maxSize = 0xFFFFFFFF;
308
309 Response response(sizeof(pldm_msg_hdr) +
310 PLDM_GET_FRU_RECORD_TABLE_METADATA_RESP_BYTES,
311 0);
312 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
313
314 auto rc = encode_get_fru_record_table_metadata_resp(
315 request->hdr.instance_id, PLDM_SUCCESS, major, minor, maxSize,
316 impl.size(), impl.numRSI(), impl.numRecords(), impl.checkSum(),
317 responsePtr);
318 if (rc != PLDM_SUCCESS)
319 {
320 return ccOnlyResponse(request, rc);
321 }
322
323 return response;
324}
325
326Response Handler::getFRURecordTable(const pldm_msg* request,
327 size_t payloadLength)
328{
Tom Joseph33e9c7e2020-06-11 22:09:52 +0530329 // FRU table is built lazily, build if not done.
330 buildFRUTable();
331
Deepak Kodihallie60c5822019-10-23 03:26:15 -0500332 if (payloadLength != PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES)
333 {
334 return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
335 }
336
337 Response response(
338 sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES, 0);
339 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
340
341 auto rc =
342 encode_get_fru_record_table_resp(request->hdr.instance_id, PLDM_SUCCESS,
343 0, PLDM_START_AND_END, responsePtr);
344 if (rc != PLDM_SUCCESS)
345 {
346 return ccOnlyResponse(request, rc);
347 }
348
349 impl.getFRUTable(response);
350
351 return response;
352}
353
John Wang9e82ad12020-06-12 10:53:32 +0800354Response Handler::getFRURecordByOption(const pldm_msg* request,
355 size_t payloadLength)
356{
357 if (payloadLength != sizeof(pldm_get_fru_record_by_option_req))
358 {
359 return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
360 }
361
362 uint32_t retDataTransferHandle{};
363 uint16_t retFruTableHandle{};
364 uint16_t retRecordSetIdentifier{};
365 uint8_t retRecordType{};
366 uint8_t retFieldType{};
367 uint8_t retTransferOpFlag{};
368
369 auto rc = decode_get_fru_record_by_option_req(
370 request, payloadLength, &retDataTransferHandle, &retFruTableHandle,
371 &retRecordSetIdentifier, &retRecordType, &retFieldType,
372 &retTransferOpFlag);
373
374 if (rc != PLDM_SUCCESS)
375 {
376 return ccOnlyResponse(request, rc);
377 }
378
379 std::vector<uint8_t> fruData;
380 rc = impl.getFRURecordByOption(fruData, retFruTableHandle,
381 retRecordSetIdentifier, retRecordType,
382 retFieldType);
383 if (rc != PLDM_SUCCESS)
384 {
385 return ccOnlyResponse(request, rc);
386 }
387
388 auto respPayloadLength =
389 PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES + fruData.size();
390 Response response(sizeof(pldm_msg_hdr) + respPayloadLength, 0);
391 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
392
393 rc = encode_get_fru_record_by_option_resp(
394 request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END,
395 fruData.data(), fruData.size(), responsePtr, respPayloadLength);
396
397 if (rc != PLDM_SUCCESS)
398 {
399 return ccOnlyResponse(request, rc);
400 }
401
402 return response;
403}
404
Deepak Kodihallie60c5822019-10-23 03:26:15 -0500405} // namespace fru
406
Deepak Kodihalli70e8db02019-10-21 00:59:46 -0500407} // namespace responder
408
409} // namespace pldm