1 #include "host_pdr_handler.hpp"
2
3 #include <libpldm/fru.h>
4 #ifdef OEM_IBM
5 #include <libpldm/oem/ibm/fru.h>
6 #endif
7 #include "dbus/custom_dbus.hpp"
8
9 #include <nlohmann/json.hpp>
10 #include <phosphor-logging/lg2.hpp>
11 #include <sdeventplus/clock.hpp>
12 #include <sdeventplus/exception.hpp>
13 #include <sdeventplus/source/io.hpp>
14 #include <sdeventplus/source/time.hpp>
15
16 #include <cassert>
17 #include <fstream>
18 #include <type_traits>
19
20 PHOSPHOR_LOG2_USING;
21
22 namespace pldm
23 {
24 using namespace pldm::responder::events;
25 using namespace pldm::utils;
26 using namespace sdbusplus::bus::match::rules;
27 using namespace pldm::responder::pdr_utils;
28 using namespace pldm::hostbmc::utils;
29 using Json = nlohmann::json;
30 namespace fs = std::filesystem;
31 using namespace pldm::dbus;
32 const Json emptyJson{};
33
34 template <typename T>
extractTerminusHandle(std::vector<uint8_t> & pdr)35 uint16_t extractTerminusHandle(std::vector<uint8_t>& pdr)
36 {
37 T* var = nullptr;
38 if (std::is_same<T, pldm_pdr_fru_record_set>::value)
39 {
40 var = (T*)(pdr.data() + sizeof(pldm_pdr_hdr));
41 }
42 else
43 {
44 var = (T*)(pdr.data());
45 }
46 if (var != nullptr)
47 {
48 return var->terminus_handle;
49 }
50 return TERMINUS_HANDLE;
51 }
52
53 template <typename T>
updateContainerId(pldm_entity_association_tree * entityTree,std::vector<uint8_t> & pdr)54 void updateContainerId(pldm_entity_association_tree* entityTree,
55 std::vector<uint8_t>& pdr)
56 {
57 T* t = nullptr;
58 if (entityTree == nullptr)
59 {
60 return;
61 }
62 if (std::is_same<T, pldm_pdr_fru_record_set>::value)
63 {
64 t = (T*)(pdr.data() + sizeof(pldm_pdr_hdr));
65 }
66 else
67 {
68 t = (T*)(pdr.data());
69 }
70 if (t == nullptr)
71 {
72 return;
73 }
74
75 pldm_entity entity{t->entity_type, t->entity_instance, t->container_id};
76 auto node = pldm_entity_association_tree_find_with_locality(
77 entityTree, &entity, true);
78 if (node)
79 {
80 pldm_entity e = pldm_entity_extract(node);
81 t->container_id = e.entity_container_id;
82 }
83 }
84
HostPDRHandler(int,uint8_t mctp_eid,sdeventplus::Event & event,pldm_pdr * repo,const std::string & eventsJsonsDir,pldm_entity_association_tree * entityTree,pldm_entity_association_tree * bmcEntityTree,pldm::InstanceIdDb & instanceIdDb,pldm::requester::Handler<pldm::requester::Request> * handler)85 HostPDRHandler::HostPDRHandler(
86 int /* mctp_fd */, uint8_t mctp_eid, sdeventplus::Event& event,
87 pldm_pdr* repo, const std::string& eventsJsonsDir,
88 pldm_entity_association_tree* entityTree,
89 pldm_entity_association_tree* bmcEntityTree,
90 pldm::InstanceIdDb& instanceIdDb,
91 pldm::requester::Handler<pldm::requester::Request>* handler) :
92 mctp_eid(mctp_eid), event(event), repo(repo),
93 stateSensorHandler(eventsJsonsDir), entityTree(entityTree),
94 instanceIdDb(instanceIdDb), handler(handler),
95 entityMaps(parseEntityMap(ENTITY_MAP_JSON)), oemUtilsHandler(nullptr)
96 {
97 mergedHostParents = false;
98 hostOffMatch = std::make_unique<sdbusplus::bus::match_t>(
99 pldm::utils::DBusHandler::getBus(),
100 propertiesChanged("/xyz/openbmc_project/state/host0",
101 "xyz.openbmc_project.State.Host"),
102 [this, repo, entityTree, bmcEntityTree](sdbusplus::message_t& msg) {
103 DbusChangedProps props{};
104 std::string intf;
105 msg.read(intf, props);
106 const auto itr = props.find("CurrentHostState");
107 if (itr != props.end())
108 {
109 PropertyValue value = itr->second;
110 auto propVal = std::get<std::string>(value);
111 if (propVal == "xyz.openbmc_project.State.Host.HostState.Off")
112 {
113 // Delete all the remote terminus information
114 std::erase_if(tlPDRInfo, [](const auto& item) {
115 const auto& [key, value] = item;
116 return key != TERMINUS_HANDLE;
117 });
118 // when the host is powered off, set the availability
119 // state of all the dbus objects to false
120 this->setPresenceFrus();
121 pldm_pdr_remove_remote_pdrs(repo);
122 pldm_entity_association_tree_destroy_root(entityTree);
123 pldm_entity_association_tree_copy_root(bmcEntityTree,
124 entityTree);
125 this->sensorMap.clear();
126 this->responseReceived = false;
127 this->mergedHostParents = false;
128 }
129 }
130 });
131 }
132
setPresenceFrus()133 void HostPDRHandler::setPresenceFrus()
134 {
135 // iterate over all dbus objects
136 for (const auto& [path, entityId] : objPathMap)
137 {
138 CustomDBus::getCustomDBus().setAvailabilityState(path, false);
139 }
140 }
141
fetchPDR(PDRRecordHandles && recordHandles)142 void HostPDRHandler::fetchPDR(PDRRecordHandles&& recordHandles)
143 {
144 pdrRecordHandles.clear();
145 modifiedPDRRecordHandles.clear();
146
147 if (isHostPdrModified)
148 {
149 modifiedPDRRecordHandles = std::move(recordHandles);
150 }
151 else
152 {
153 pdrRecordHandles = std::move(recordHandles);
154 }
155
156 // Defer the actual fetch of PDRs from the host (by queuing the call on the
157 // main event loop). That way, we can respond to the platform event msg from
158 // the host firmware.
159 pdrFetchEvent = std::make_unique<sdeventplus::source::Defer>(
160 event, std::bind(std::mem_fn(&HostPDRHandler::_fetchPDR), this,
161 std::placeholders::_1));
162 }
163
_fetchPDR(sdeventplus::source::EventBase &)164 void HostPDRHandler::_fetchPDR(sdeventplus::source::EventBase& /*source*/)
165 {
166 getHostPDR();
167 }
168
getHostPDR(uint32_t nextRecordHandle)169 void HostPDRHandler::getHostPDR(uint32_t nextRecordHandle)
170 {
171 pdrFetchEvent.reset();
172
173 std::vector<uint8_t> requestMsg(
174 sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES);
175 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
176 uint32_t recordHandle{};
177 if (!nextRecordHandle && (!modifiedPDRRecordHandles.empty()) &&
178 isHostPdrModified)
179 {
180 recordHandle = modifiedPDRRecordHandles.front();
181 modifiedPDRRecordHandles.pop_front();
182 }
183 else if (!nextRecordHandle && (!pdrRecordHandles.empty()))
184 {
185 recordHandle = pdrRecordHandles.front();
186 pdrRecordHandles.pop_front();
187 }
188 else
189 {
190 recordHandle = nextRecordHandle;
191 }
192 auto instanceId = instanceIdDb.next(mctp_eid);
193
194 auto rc =
195 encode_get_pdr_req(instanceId, recordHandle, 0, PLDM_GET_FIRSTPART,
196 UINT16_MAX, 0, request, PLDM_GET_PDR_REQ_BYTES);
197 if (rc != PLDM_SUCCESS)
198 {
199 instanceIdDb.free(mctp_eid, instanceId);
200 error("Failed to encode get pdr request, response code '{RC}'", "RC",
201 rc);
202 return;
203 }
204
205 rc = handler->registerRequest(
206 mctp_eid, instanceId, PLDM_PLATFORM, PLDM_GET_PDR,
207 std::move(requestMsg),
208 std::bind_front(&HostPDRHandler::processHostPDRs, this));
209 if (rc)
210 {
211 error(
212 "Failed to send the getPDR request to remote terminus, response code '{RC}'",
213 "RC", rc);
214 }
215 }
216
handleStateSensorEvent(const StateSensorEntry & entry,pdr::EventState state)217 int HostPDRHandler::handleStateSensorEvent(const StateSensorEntry& entry,
218 pdr::EventState state)
219 {
220 auto rc = stateSensorHandler.eventAction(entry, state);
221 if (rc != PLDM_SUCCESS)
222 {
223 error("Failed to fetch and update D-bus property, response code '{RC}'",
224 "RC", rc);
225 return rc;
226 }
227 return PLDM_SUCCESS;
228 }
229
mergeEntityAssociations(const std::vector<uint8_t> & pdr,const uint32_t & size,const uint32_t & record_handle)230 void HostPDRHandler::mergeEntityAssociations(
231 const std::vector<uint8_t>& pdr, [[maybe_unused]] const uint32_t& size,
232 [[maybe_unused]] const uint32_t& record_handle)
233 {
234 size_t numEntities{};
235 pldm_entity* entities = nullptr;
236 bool merged = false;
237 auto entityPdr = reinterpret_cast<pldm_pdr_entity_association*>(
238 const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
239
240 if (oemPlatformHandler &&
241 oemPlatformHandler->checkRecordHandleInRange(record_handle))
242 {
243 // Adding the remote range PDRs to the repo before merging it
244 uint32_t handle = record_handle;
245 pldm_pdr_add(repo, pdr.data(), size, true, 0xFFFF, &handle);
246 }
247
248 pldm_entity_association_pdr_extract(pdr.data(), pdr.size(), &numEntities,
249 &entities);
250 if (numEntities > 0)
251 {
252 pldm_entity_node* pNode = nullptr;
253 if (!mergedHostParents)
254 {
255 pNode = pldm_entity_association_tree_find_with_locality(
256 entityTree, &entities[0], false);
257 }
258 else
259 {
260 pNode = pldm_entity_association_tree_find_with_locality(
261 entityTree, &entities[0], true);
262 }
263 if (!pNode)
264 {
265 return;
266 }
267
268 Entities entityAssoc;
269 entityAssoc.push_back(pNode);
270 for (size_t i = 1; i < numEntities; ++i)
271 {
272 bool isUpdateContainerId = true;
273 if (oemPlatformHandler)
274 {
275 isUpdateContainerId =
276 checkIfLogicalBitSet(entities[i].entity_container_id);
277 }
278 auto node = pldm_entity_association_tree_add_entity(
279 entityTree, &entities[i], entities[i].entity_instance_num,
280 pNode, entityPdr->association_type, true, isUpdateContainerId,
281 0xFFFF);
282 if (!node)
283 {
284 continue;
285 }
286 merged = true;
287 entityAssoc.push_back(node);
288 }
289
290 mergedHostParents = true;
291 if (merged)
292 {
293 entityAssociations.push_back(entityAssoc);
294 }
295 }
296
297 if (merged)
298 {
299 // Update our PDR repo with the merged entity association PDRs
300 pldm_entity_node* node = nullptr;
301 pldm_find_entity_ref_in_tree(entityTree, entities[0], &node);
302 if (node == nullptr)
303 {
304 error("Failed to find reference of the entity in the tree");
305 }
306 else
307 {
308 int rc = 0;
309 if (oemPlatformHandler)
310 {
311 auto record = oemPlatformHandler->fetchLastBMCRecord(repo);
312
313 uint32_t record_handle =
314 pldm_pdr_get_record_handle(repo, record);
315
316 rc =
317 pldm_entity_association_pdr_add_from_node_with_record_handle(
318 node, repo, &entities, numEntities, true,
319 TERMINUS_HANDLE, (record_handle + 1));
320 }
321 else
322 {
323 rc = pldm_entity_association_pdr_add_from_node(
324 node, repo, &entities, numEntities, true, TERMINUS_HANDLE);
325 }
326
327 if (rc)
328 {
329 error(
330 "Failed to add entity association PDR from node, response code '{RC}'",
331 "RC", rc);
332 }
333 }
334 }
335 free(entities);
336 }
337
sendPDRRepositoryChgEvent(std::vector<uint8_t> && pdrTypes,uint8_t eventDataFormat)338 void HostPDRHandler::sendPDRRepositoryChgEvent(std::vector<uint8_t>&& pdrTypes,
339 uint8_t eventDataFormat)
340 {
341 assert(eventDataFormat == FORMAT_IS_PDR_HANDLES);
342
343 // Extract from the PDR repo record handles of PDRs we want the host
344 // to pull up.
345 std::vector<uint8_t> eventDataOps{PLDM_RECORDS_ADDED};
346 std::vector<uint8_t> numsOfChangeEntries(1);
347 std::vector<std::vector<ChangeEntry>> changeEntries(
348 numsOfChangeEntries.size());
349 for (auto pdrType : pdrTypes)
350 {
351 const pldm_pdr_record* record{};
352 do
353 {
354 record = pldm_pdr_find_record_by_type(repo, pdrType, record,
355 nullptr, nullptr);
356 if (record && pldm_pdr_record_is_remote(record))
357 {
358 changeEntries[0].push_back(
359 pldm_pdr_get_record_handle(repo, record));
360 }
361 } while (record);
362 }
363 if (changeEntries.empty())
364 {
365 return;
366 }
367 numsOfChangeEntries[0] = changeEntries[0].size();
368
369 // Encode PLDM platform event msg to indicate a PDR repo change.
370 size_t maxSize = PLDM_PDR_REPOSITORY_CHG_EVENT_MIN_LENGTH +
371 PLDM_PDR_REPOSITORY_CHANGE_RECORD_MIN_LENGTH +
372 changeEntries[0].size() * sizeof(uint32_t);
373 std::vector<uint8_t> eventDataVec{};
374 eventDataVec.resize(maxSize);
375 auto eventData =
376 reinterpret_cast<struct pldm_pdr_repository_chg_event_data*>(
377 eventDataVec.data());
378 size_t actualSize{};
379 auto firstEntry = changeEntries[0].data();
380 auto rc = encode_pldm_pdr_repository_chg_event_data(
381 eventDataFormat, 1, eventDataOps.data(), numsOfChangeEntries.data(),
382 &firstEntry, eventData, &actualSize, maxSize);
383 if (rc != PLDM_SUCCESS)
384 {
385 error(
386 "Failed to encode pldm pdr repository change event data, response code '{RC}'",
387 "RC", rc);
388 return;
389 }
390 auto instanceId = instanceIdDb.next(mctp_eid);
391 std::vector<uint8_t> requestMsg(
392 sizeof(pldm_msg_hdr) + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES +
393 actualSize);
394 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
395 rc = encode_platform_event_message_req(
396 instanceId, 1, TERMINUS_ID, PLDM_PDR_REPOSITORY_CHG_EVENT,
397 eventDataVec.data(), actualSize, request,
398 actualSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES);
399 if (rc != PLDM_SUCCESS)
400 {
401 instanceIdDb.free(mctp_eid, instanceId);
402 error(
403 "Failed to encode platform event message request, response code '{RC}'",
404 "RC", rc);
405 return;
406 }
407
408 auto platformEventMessageResponseHandler = [](mctp_eid_t /*eid*/,
409 const pldm_msg* response,
410 size_t respMsgLen) {
411 if (response == nullptr || !respMsgLen)
412 {
413 error(
414 "Failed to receive response for the PDR repository changed event");
415 return;
416 }
417
418 uint8_t completionCode{};
419 uint8_t status{};
420 auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response);
421 auto rc = decode_platform_event_message_resp(responsePtr, respMsgLen,
422 &completionCode, &status);
423 if (rc || completionCode)
424 {
425 error(
426 "Failed to decode platform event message response, response code '{RC}' and completion code '{CC}'",
427 "RC", rc, "CC", completionCode);
428 }
429 };
430
431 rc = handler->registerRequest(
432 mctp_eid, instanceId, PLDM_PLATFORM, PLDM_PLATFORM_EVENT_MESSAGE,
433 std::move(requestMsg), std::move(platformEventMessageResponseHandler));
434 if (rc)
435 {
436 error(
437 "Failed to send the PDR repository changed event request, response code '{RC}'",
438 "RC", rc);
439 }
440 }
441
parseStateSensorPDRs(const PDRList & stateSensorPDRs)442 void HostPDRHandler::parseStateSensorPDRs(const PDRList& stateSensorPDRs)
443 {
444 for (const auto& pdr : stateSensorPDRs)
445 {
446 SensorEntry sensorEntry{};
447 const auto& [terminusHandle, sensorID, sensorInfo] =
448 responder::pdr_utils::parseStateSensorPDR(pdr);
449 sensorEntry.sensorID = sensorID;
450 try
451 {
452 sensorEntry.terminusID = std::get<0>(tlPDRInfo.at(terminusHandle));
453 }
454 // If there is no mapping for terminusHandle assign the reserved TID
455 // value of 0xFF to indicate that.
456 catch (const std::out_of_range&)
457 {
458 sensorEntry.terminusID = PLDM_TID_RESERVED;
459 }
460 sensorMap.emplace(sensorEntry, std::move(sensorInfo));
461 }
462 }
463
processHostPDRs(mctp_eid_t,const pldm_msg * response,size_t respMsgLen)464 void HostPDRHandler::processHostPDRs(
465 mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen)
466 {
467 static bool merged = false;
468 static PDRList stateSensorPDRs{};
469 static PDRList fruRecordSetPDRs{};
470 uint32_t nextRecordHandle{};
471 uint8_t tlEid = 0;
472 bool tlValid = true;
473 uint32_t rh = 0;
474 uint16_t terminusHandle = 0;
475 uint16_t pdrTerminusHandle = 0;
476 uint8_t tid = 0;
477
478 uint8_t completionCode{};
479 uint32_t nextDataTransferHandle{};
480 uint8_t transferFlag{};
481 uint16_t respCount{};
482 uint8_t transferCRC{};
483 if (response == nullptr || !respMsgLen)
484 {
485 error("Failed to receive response for the GetPDR command");
486 pldm::utils::reportError(
487 "xyz.openbmc_project.PLDM.Error.GetPDR.PDRExchangeFailure");
488 return;
489 }
490
491 auto rc = decode_get_pdr_resp(
492 response, respMsgLen /*- sizeof(pldm_msg_hdr)*/, &completionCode,
493 &nextRecordHandle, &nextDataTransferHandle, &transferFlag, &respCount,
494 nullptr, 0, &transferCRC);
495 std::vector<uint8_t> responsePDRMsg;
496 responsePDRMsg.resize(respMsgLen + sizeof(pldm_msg_hdr));
497 memcpy(responsePDRMsg.data(), response, respMsgLen + sizeof(pldm_msg_hdr));
498 if (rc != PLDM_SUCCESS)
499 {
500 error(
501 "Failed to decode getPDR response for next record handle '{NEXT_RECORD_HANDLE}', response code '{RC}'",
502 "NEXT_RECORD_HANDLE", nextRecordHandle, "RC", rc);
503 return;
504 }
505 else
506 {
507 std::vector<uint8_t> pdr(respCount, 0);
508 rc = decode_get_pdr_resp(response, respMsgLen, &completionCode,
509 &nextRecordHandle, &nextDataTransferHandle,
510 &transferFlag, &respCount, pdr.data(),
511 respCount, &transferCRC);
512 if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
513 {
514 error(
515 "Failed to decode getPDR response for next record handle '{NEXT_RECORD_HANDLE}', next data transfer handle '{DATA_TRANSFER_HANDLE}' and transfer flag '{FLAG}', response code '{RC}' and completion code '{CC}'",
516 "NEXT_RECORD_HANDLE", nextRecordHandle, "DATA_TRANSFER_HANDLE",
517 nextDataTransferHandle, "FLAG", transferFlag, "RC", rc, "CC",
518 completionCode);
519 return;
520 }
521 else
522 {
523 // when nextRecordHandle is 0, we need the recordHandle of the last
524 // PDR and not 0-1.
525 if (!nextRecordHandle)
526 {
527 rh = nextRecordHandle;
528 }
529 else
530 {
531 rh = nextRecordHandle - 1;
532 }
533
534 auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(pdr.data());
535 if (!rh)
536 {
537 rh = pdrHdr->record_handle;
538 }
539
540 if (pdrHdr->type == PLDM_PDR_ENTITY_ASSOCIATION)
541 {
542 this->mergeEntityAssociations(pdr, respCount, rh);
543 merged = true;
544 }
545 else
546 {
547 if (pdrHdr->type == PLDM_TERMINUS_LOCATOR_PDR)
548 {
549 pdrTerminusHandle =
550 extractTerminusHandle<pldm_terminus_locator_pdr>(pdr);
551 auto tlpdr =
552 reinterpret_cast<const pldm_terminus_locator_pdr*>(
553 pdr.data());
554
555 terminusHandle = tlpdr->terminus_handle;
556 tid = tlpdr->tid;
557 auto terminus_locator_type = tlpdr->terminus_locator_type;
558 if (terminus_locator_type ==
559 PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID)
560 {
561 auto locatorValue = reinterpret_cast<
562 const pldm_terminus_locator_type_mctp_eid*>(
563 tlpdr->terminus_locator_value);
564 tlEid = static_cast<uint8_t>(locatorValue->eid);
565 }
566 if (tlpdr->validity == 0)
567 {
568 tlValid = false;
569 }
570 for (const auto& terminusMap : tlPDRInfo)
571 {
572 if ((terminusHandle == (terminusMap.first)) &&
573 (get<1>(terminusMap.second) == tlEid) &&
574 (get<2>(terminusMap.second) == tlpdr->validity))
575 {
576 // TL PDR already present with same validity don't
577 // add the PDR to the repo just return
578 return;
579 }
580 }
581 tlPDRInfo.insert_or_assign(
582 tlpdr->terminus_handle,
583 std::make_tuple(tlpdr->tid, tlEid, tlpdr->validity));
584 }
585 else if (pdrHdr->type == PLDM_STATE_SENSOR_PDR)
586 {
587 pdrTerminusHandle =
588 extractTerminusHandle<pldm_state_sensor_pdr>(pdr);
589 updateContainerId<pldm_state_sensor_pdr>(entityTree, pdr);
590 stateSensorPDRs.emplace_back(pdr);
591 }
592 else if (pdrHdr->type == PLDM_PDR_FRU_RECORD_SET)
593 {
594 pdrTerminusHandle =
595 extractTerminusHandle<pldm_pdr_fru_record_set>(pdr);
596 updateContainerId<pldm_pdr_fru_record_set>(entityTree, pdr);
597 fruRecordSetPDRs.emplace_back(pdr);
598 }
599 else if (pdrHdr->type == PLDM_STATE_EFFECTER_PDR)
600 {
601 pdrTerminusHandle =
602 extractTerminusHandle<pldm_state_effecter_pdr>(pdr);
603 updateContainerId<pldm_state_effecter_pdr>(entityTree, pdr);
604 }
605 else if (pdrHdr->type == PLDM_NUMERIC_EFFECTER_PDR)
606 {
607 pdrTerminusHandle =
608 extractTerminusHandle<pldm_numeric_effecter_value_pdr>(
609 pdr);
610 updateContainerId<pldm_numeric_effecter_value_pdr>(
611 entityTree, pdr);
612 }
613 // if the TLPDR is invalid update the repo accordingly
614 if (!tlValid)
615 {
616 pldm_pdr_update_TL_pdr(repo, terminusHandle, tid, tlEid,
617 tlValid);
618
619 if (!isHostUp())
620 {
621 // The terminus PDR becomes invalid when the terminus
622 // itself is down. We don't need to do PDR exchange in
623 // that case, so setting the next record handle to 0.
624 nextRecordHandle = 0;
625 }
626 }
627 else
628 {
629 rc = pldm_pdr_add(repo, pdr.data(), respCount, true,
630 pdrTerminusHandle, &rh);
631 if (rc)
632 {
633 // pldm_pdr_add() assert()ed on failure to add a PDR.
634 throw std::runtime_error("Failed to add PDR");
635 }
636 }
637 }
638 }
639 }
640 if (!nextRecordHandle)
641 {
642 updateEntityAssociation(entityAssociations, entityTree, objPathMap,
643 entityMaps, oemPlatformHandler);
644 if (oemUtilsHandler)
645 {
646 oemUtilsHandler->setCoreCount(entityAssociations, entityMaps);
647 }
648 /*received last record*/
649 this->parseStateSensorPDRs(stateSensorPDRs);
650 this->createDbusObjects(fruRecordSetPDRs);
651 if (isHostUp())
652 {
653 this->setHostSensorState(stateSensorPDRs);
654 }
655 stateSensorPDRs.clear();
656 fruRecordSetPDRs.clear();
657 entityAssociations.clear();
658
659 if (merged)
660 {
661 merged = false;
662 deferredPDRRepoChgEvent =
663 std::make_unique<sdeventplus::source::Defer>(
664 event,
665 std::bind(
666 std::mem_fn((&HostPDRHandler::_processPDRRepoChgEvent)),
667 this, std::placeholders::_1));
668 }
669 }
670 else
671 {
672 if (modifiedPDRRecordHandles.empty() && isHostPdrModified)
673 {
674 isHostPdrModified = false;
675 }
676 else
677 {
678 deferredFetchPDREvent =
679 std::make_unique<sdeventplus::source::Defer>(
680 event,
681 std::bind(
682 std::mem_fn((&HostPDRHandler::_processFetchPDREvent)),
683 this, nextRecordHandle, std::placeholders::_1));
684 }
685 }
686 }
687
_processPDRRepoChgEvent(sdeventplus::source::EventBase &)688 void HostPDRHandler::_processPDRRepoChgEvent(
689 sdeventplus::source::EventBase& /*source */)
690 {
691 deferredPDRRepoChgEvent.reset();
692 this->sendPDRRepositoryChgEvent(
693 std::vector<uint8_t>(1, PLDM_PDR_ENTITY_ASSOCIATION),
694 FORMAT_IS_PDR_HANDLES);
695 }
696
_processFetchPDREvent(uint32_t nextRecordHandle,sdeventplus::source::EventBase &)697 void HostPDRHandler::_processFetchPDREvent(
698 uint32_t nextRecordHandle, sdeventplus::source::EventBase& /*source */)
699 {
700 deferredFetchPDREvent.reset();
701 if (!this->pdrRecordHandles.empty())
702 {
703 nextRecordHandle = this->pdrRecordHandles.front();
704 this->pdrRecordHandles.pop_front();
705 }
706 if (isHostPdrModified && (!this->modifiedPDRRecordHandles.empty()))
707 {
708 nextRecordHandle = this->modifiedPDRRecordHandles.front();
709 this->modifiedPDRRecordHandles.pop_front();
710 }
711 this->getHostPDR(nextRecordHandle);
712 }
713
setHostFirmwareCondition()714 void HostPDRHandler::setHostFirmwareCondition()
715 {
716 responseReceived = false;
717 auto instanceId = instanceIdDb.next(mctp_eid);
718 std::vector<uint8_t> requestMsg(
719 sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_REQ_BYTES);
720 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
721 auto rc = encode_get_version_req(instanceId, 0, PLDM_GET_FIRSTPART,
722 PLDM_BASE, request);
723 if (rc != PLDM_SUCCESS)
724 {
725 error("Failed to encode GetPLDMVersion, response code {RC}", "RC",
726 lg2::hex, rc);
727 instanceIdDb.free(mctp_eid, instanceId);
728 return;
729 }
730
731 auto getPLDMVersionHandler = [this](mctp_eid_t /*eid*/,
732 const pldm_msg* response,
733 size_t respMsgLen) {
734 if (response == nullptr || !respMsgLen)
735 {
736 error(
737 "Failed to receive response for getPLDMVersion command, Host seems to be off");
738 return;
739 }
740 info("Getting the response code '{RC}'", "RC", lg2::hex,
741 response->payload[0]);
742 this->responseReceived = true;
743 };
744 rc = handler->registerRequest(mctp_eid, instanceId, PLDM_BASE,
745 PLDM_GET_PLDM_VERSION, std::move(requestMsg),
746 std::move(getPLDMVersionHandler));
747 if (rc)
748 {
749 error(
750 "Failed to discover remote terminus state. Assuming remote terminus as off");
751 }
752 }
753
isHostUp()754 bool HostPDRHandler::isHostUp()
755 {
756 return responseReceived;
757 }
758
setHostSensorState(const PDRList & stateSensorPDRs)759 void HostPDRHandler::setHostSensorState(const PDRList& stateSensorPDRs)
760 {
761 for (const auto& stateSensorPDR : stateSensorPDRs)
762 {
763 auto pdr = reinterpret_cast<const pldm_state_sensor_pdr*>(
764 stateSensorPDR.data());
765
766 if (!pdr)
767 {
768 error("Failed to get state sensor PDR");
769 pldm::utils::reportError(
770 "xyz.openbmc_project.bmc.pldm.InternalFailure");
771 return;
772 }
773
774 uint16_t sensorId = pdr->sensor_id;
775
776 for (const auto& [terminusHandle, terminusInfo] : tlPDRInfo)
777 {
778 if (terminusHandle == pdr->terminus_handle)
779 {
780 if (std::get<2>(terminusInfo) == PLDM_TL_PDR_VALID)
781 {
782 mctp_eid = std::get<1>(terminusInfo);
783 }
784
785 bitfield8_t sensorRearm;
786 sensorRearm.byte = 0;
787 uint8_t tid = std::get<0>(terminusInfo);
788
789 auto instanceId = instanceIdDb.next(mctp_eid);
790 std::vector<uint8_t> requestMsg(
791 sizeof(pldm_msg_hdr) +
792 PLDM_GET_STATE_SENSOR_READINGS_REQ_BYTES);
793 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
794 auto rc = encode_get_state_sensor_readings_req(
795 instanceId, sensorId, sensorRearm, 0, request);
796
797 if (rc != PLDM_SUCCESS)
798 {
799 instanceIdDb.free(mctp_eid, instanceId);
800 error(
801 "Failed to encode get state sensor readings request for sensorID '{SENSOR_ID}' and instanceID '{INSTANCE}', response code '{RC}'",
802 "SENSOR_ID", sensorId, "INSTANCE", instanceId, "RC",
803 rc);
804 pldm::utils::reportError(
805 "xyz.openbmc_project.bmc.pldm.InternalFailure");
806 return;
807 }
808
809 auto getStateSensorReadingRespHandler = [=, this](
810 mctp_eid_t /*eid*/,
811 const pldm_msg*
812 response,
813 size_t respMsgLen) {
814 if (response == nullptr || !respMsgLen)
815 {
816 error(
817 "Failed to receive response for get state sensor reading command for sensorID '{SENSOR_ID}' and instanceID '{INSTANCE}'",
818 "SENSOR_ID", sensorId, "INSTANCE", instanceId);
819 return;
820 }
821 std::array<get_sensor_state_field, 8> stateField{};
822 uint8_t completionCode = 0;
823 uint8_t comp_sensor_count = 0;
824
825 auto rc = decode_get_state_sensor_readings_resp(
826 response, respMsgLen, &completionCode,
827 &comp_sensor_count, stateField.data());
828
829 if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
830 {
831 error(
832 "Failed to decode get state sensor readings response for sensorID '{SENSOR_ID}' and instanceID '{INSTANCE}', response code'{RC}' and completion code '{CC}'",
833 "SENSOR_ID", sensorId, "INSTANCE", instanceId, "RC",
834 rc, "CC", completionCode);
835 pldm::utils::reportError(
836 "xyz.openbmc_project.bmc.pldm.InternalFailure");
837 }
838
839 uint8_t eventState;
840 uint8_t previousEventState;
841
842 for (uint8_t sensorOffset = 0;
843 sensorOffset < comp_sensor_count; sensorOffset++)
844 {
845 eventState = stateField[sensorOffset].present_state;
846 previousEventState =
847 stateField[sensorOffset].previous_state;
848
849 emitStateSensorEventSignal(tid, sensorId, sensorOffset,
850 eventState,
851 previousEventState);
852
853 SensorEntry sensorEntry{tid, sensorId};
854
855 pldm::pdr::EntityInfo entityInfo{};
856 pldm::pdr::CompositeSensorStates
857 compositeSensorStates{};
858 std::vector<pldm::pdr::StateSetId> stateSetIds{};
859
860 try
861 {
862 std::tie(entityInfo, compositeSensorStates,
863 stateSetIds) =
864 lookupSensorInfo(sensorEntry);
865 }
866 catch (const std::out_of_range&)
867 {
868 try
869 {
870 sensorEntry.terminusID = PLDM_TID_RESERVED;
871 std::tie(entityInfo, compositeSensorStates,
872 stateSetIds) =
873 lookupSensorInfo(sensorEntry);
874 }
875 catch (const std::out_of_range&)
876 {
877 error("No mapping for the events");
878 }
879 }
880
881 if ((compositeSensorStates.size() > 1) &&
882 (sensorOffset > (compositeSensorStates.size() - 1)))
883 {
884 error(
885 "Error Invalid data, Invalid sensor offset '{SENSOR_OFFSET}'",
886 "SENSOR_OFFSET", sensorOffset);
887 return;
888 }
889
890 const auto& possibleStates =
891 compositeSensorStates[sensorOffset];
892 if (possibleStates.find(eventState) ==
893 possibleStates.end())
894 {
895 error(
896 "Error invalid_data, Invalid event state '{STATE}'",
897 "STATE", eventState);
898 return;
899 }
900 const auto& [containerId, entityType, entityInstance] =
901 entityInfo;
902 auto stateSetId = stateSetIds[sensorOffset];
903 pldm::responder::events::StateSensorEntry
904 stateSensorEntry{containerId, entityType,
905 entityInstance, sensorOffset,
906 stateSetId, false};
907 handleStateSensorEvent(stateSensorEntry, eventState);
908 }
909 };
910
911 rc = handler->registerRequest(
912 mctp_eid, instanceId, PLDM_PLATFORM,
913 PLDM_GET_STATE_SENSOR_READINGS, std::move(requestMsg),
914 std::move(getStateSensorReadingRespHandler));
915
916 if (rc != PLDM_SUCCESS)
917 {
918 error(
919 "Failed to send request to get state sensor reading on remote terminus for sensorID '{SENSOR_ID}' and instanceID '{INSTANCE}', response code '{RC}'",
920 "SENSOR_ID", sensorId, "INSTANCE", instanceId, "RC",
921 rc);
922 }
923 }
924 }
925 }
926 }
927
getFRURecordTableMetadataByRemote(const PDRList & fruRecordSetPDRs)928 void HostPDRHandler::getFRURecordTableMetadataByRemote(
929 const PDRList& fruRecordSetPDRs)
930 {
931 auto instanceId = instanceIdDb.next(mctp_eid);
932 std::vector<uint8_t> requestMsg(
933 sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES);
934
935 // GetFruRecordTableMetadata
936 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
937 auto rc = encode_get_fru_record_table_metadata_req(
938 instanceId, request, requestMsg.size() - sizeof(pldm_msg_hdr));
939 if (rc != PLDM_SUCCESS)
940 {
941 instanceIdDb.free(mctp_eid, instanceId);
942 error(
943 "Failed to encode get fru record table metadata request, response code '{RC}'",
944 "RC", lg2::hex, rc);
945 return;
946 }
947
948 auto getFruRecordTableMetadataResponseHandler = [this, fruRecordSetPDRs](
949 mctp_eid_t /*eid*/,
950 const pldm_msg*
951 response,
952 size_t respMsgLen) {
953 if (response == nullptr || !respMsgLen)
954 {
955 error(
956 "Failed to receive response for the get fru record table metadata");
957 return;
958 }
959
960 uint8_t cc = 0;
961 uint8_t fru_data_major_version, fru_data_minor_version;
962 uint32_t fru_table_maximum_size, fru_table_length;
963 uint16_t total_record_set_identifiers;
964 uint16_t total;
965 uint32_t checksum;
966
967 auto rc = decode_get_fru_record_table_metadata_resp(
968 response, respMsgLen, &cc, &fru_data_major_version,
969 &fru_data_minor_version, &fru_table_maximum_size, &fru_table_length,
970 &total_record_set_identifiers, &total, &checksum);
971
972 if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
973 {
974 error(
975 "Failed to decode get fru record table metadata response, response code '{RC}' and completion code '{CC}'",
976 "RC", lg2::hex, rc, "CC", cc);
977 return;
978 }
979
980 // pass total to getFRURecordTableByRemote
981 this->getFRURecordTableByRemote(fruRecordSetPDRs, total);
982 };
983
984 rc = handler->registerRequest(
985 mctp_eid, instanceId, PLDM_FRU, PLDM_GET_FRU_RECORD_TABLE_METADATA,
986 std::move(requestMsg),
987 std::move(getFruRecordTableMetadataResponseHandler));
988 if (rc != PLDM_SUCCESS)
989 {
990 error(
991 "Failed to send the the set state effecter states request, response code '{RC}'",
992 "RC", rc);
993 }
994
995 return;
996 }
997
getFRURecordTableByRemote(const PDRList & fruRecordSetPDRs,uint16_t totalTableRecords)998 void HostPDRHandler::getFRURecordTableByRemote(const PDRList& fruRecordSetPDRs,
999 uint16_t totalTableRecords)
1000 {
1001 fruRecordData.clear();
1002
1003 if (!totalTableRecords)
1004 {
1005 error("Failed to get fru record table");
1006 return;
1007 }
1008
1009 auto instanceId = instanceIdDb.next(mctp_eid);
1010 std::vector<uint8_t> requestMsg(
1011 sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES);
1012
1013 // send the getFruRecordTable command
1014 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
1015 auto rc = encode_get_fru_record_table_req(
1016 instanceId, 0, PLDM_GET_FIRSTPART, request,
1017 requestMsg.size() - sizeof(pldm_msg_hdr));
1018 if (rc != PLDM_SUCCESS)
1019 {
1020 instanceIdDb.free(mctp_eid, instanceId);
1021 error(
1022 "Failed to encode get fru record table request, response code '{RC}'",
1023 "RC", lg2::hex, rc);
1024 return;
1025 }
1026
1027 auto getFruRecordTableResponseHandler = [totalTableRecords, this,
1028 fruRecordSetPDRs](
1029 mctp_eid_t /*eid*/,
1030 const pldm_msg* response,
1031 size_t respMsgLen) {
1032 if (response == nullptr || !respMsgLen)
1033 {
1034 error("Failed to receive response for the get fru record table");
1035 return;
1036 }
1037
1038 uint8_t cc = 0;
1039 uint32_t next_data_transfer_handle = 0;
1040 uint8_t transfer_flag = 0;
1041 size_t fru_record_table_length = 0;
1042 std::vector<uint8_t> fru_record_table_data(
1043 respMsgLen - sizeof(pldm_msg_hdr));
1044 auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response);
1045 auto rc = decode_get_fru_record_table_resp(
1046 responsePtr, respMsgLen, &cc, &next_data_transfer_handle,
1047 &transfer_flag, fru_record_table_data.data(),
1048 &fru_record_table_length);
1049
1050 if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
1051 {
1052 error(
1053 "Failed to decode get fru record table resp, response code '{RC}' and completion code '{CC}'",
1054 "RC", lg2::hex, rc, "CC", cc);
1055 return;
1056 }
1057
1058 fruRecordData = responder::pdr_utils::parseFruRecordTable(
1059 fru_record_table_data.data(), fru_record_table_length);
1060
1061 if (totalTableRecords != fruRecordData.size())
1062 {
1063 fruRecordData.clear();
1064
1065 error("Failed to parse fru record data format.");
1066 return;
1067 }
1068
1069 this->setFRUDataOnDBus(fruRecordSetPDRs, fruRecordData);
1070 };
1071
1072 rc = handler->registerRequest(
1073 mctp_eid, instanceId, PLDM_FRU, PLDM_GET_FRU_RECORD_TABLE,
1074 std::move(requestMsg), std::move(getFruRecordTableResponseHandler));
1075 if (rc != PLDM_SUCCESS)
1076 {
1077 error("Failed to send the the set state effecter states request");
1078 }
1079 }
1080
getRSI(const PDRList & fruRecordSetPDRs,const pldm_entity & entity)1081 std::optional<uint16_t> HostPDRHandler::getRSI(const PDRList& fruRecordSetPDRs,
1082 const pldm_entity& entity)
1083 {
1084 for (const auto& pdr : fruRecordSetPDRs)
1085 {
1086 auto fruPdr = reinterpret_cast<const pldm_pdr_fru_record_set*>(
1087 const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
1088
1089 if (fruPdr->entity_type == entity.entity_type &&
1090 fruPdr->entity_instance == entity.entity_instance_num &&
1091 fruPdr->container_id == entity.entity_container_id)
1092 {
1093 return fruPdr->fru_rsi;
1094 }
1095 }
1096
1097 return std::nullopt;
1098 }
1099
setFRUDataOnDBus(const PDRList & fruRecordSetPDRs,const std::vector<responder::pdr_utils::FruRecordDataFormat> & fruRecordData)1100 void HostPDRHandler::setFRUDataOnDBus(
1101 [[maybe_unused]] const PDRList& fruRecordSetPDRs,
1102 [[maybe_unused]] const std::vector<
1103 responder::pdr_utils::FruRecordDataFormat>& fruRecordData)
1104 {
1105 #ifdef OEM_IBM
1106 for (const auto& entity : objPathMap)
1107 {
1108 pldm_entity node = pldm_entity_extract(entity.second);
1109 auto fruRSI = getRSI(fruRecordSetPDRs, node);
1110
1111 for (const auto& data : fruRecordData)
1112 {
1113 if (!fruRSI || *fruRSI != data.fruRSI)
1114 {
1115 continue;
1116 }
1117
1118 if (data.fruRecType == PLDM_FRU_RECORD_TYPE_OEM)
1119 {
1120 for (const auto& tlv : data.fruTLV)
1121 {
1122 if (tlv.fruFieldType ==
1123 PLDM_OEM_FRU_FIELD_TYPE_LOCATION_CODE)
1124 {
1125 CustomDBus::getCustomDBus().setLocationCode(
1126 entity.first,
1127 std::string(reinterpret_cast<const char*>(
1128 tlv.fruFieldValue.data()),
1129 tlv.fruFieldLen));
1130 }
1131 }
1132 }
1133 }
1134 }
1135 #endif
1136 }
1137
setPresentPropertyStatus(const std::string & path)1138 void HostPDRHandler::setPresentPropertyStatus(const std::string& path)
1139 {
1140 CustomDBus::getCustomDBus().updateItemPresentStatus(path, true);
1141 }
1142
setAvailabilityState(const std::string & path)1143 void HostPDRHandler::setAvailabilityState(const std::string& path)
1144 {
1145 CustomDBus::getCustomDBus().setAvailabilityState(path, true);
1146 }
1147
createDbusObjects(const PDRList & fruRecordSetPDRs)1148 void HostPDRHandler::createDbusObjects(const PDRList& fruRecordSetPDRs)
1149 {
1150 // Creating and Refreshing dbus hosted by remote PLDM entity Fru PDRs
1151
1152 for (const auto& entity : objPathMap)
1153 {
1154 // update the Present Property
1155 setPresentPropertyStatus(entity.first);
1156 // Implement & update the Availability to true
1157 setAvailabilityState(entity.first);
1158
1159 pldm_entity node = pldm_entity_extract(entity.second);
1160 switch (node.entity_type)
1161 {
1162 case PLDM_ENTITY_PROC | 0x8000:
1163 CustomDBus::getCustomDBus().implementCpuCoreInterface(
1164 entity.first);
1165 break;
1166 case PLDM_ENTITY_SYSTEM_CHASSIS:
1167 CustomDBus::getCustomDBus().implementChassisInterface(
1168 entity.first);
1169 break;
1170 case PLDM_ENTITY_POWER_SUPPLY:
1171 CustomDBus::getCustomDBus().implementPowerSupplyInterface(
1172 entity.first);
1173 break;
1174 case PLDM_ENTITY_CHASSIS_FRONT_PANEL_BOARD:
1175 CustomDBus::getCustomDBus().implementPanelInterface(
1176 entity.first);
1177 break;
1178 case PLDM_ENTITY_POWER_CONVERTER:
1179 CustomDBus::getCustomDBus().implementVRMInterface(entity.first);
1180 break;
1181 case PLDM_ENTITY_SLOT:
1182 CustomDBus::getCustomDBus().implementPCIeSlotInterface(
1183 entity.first);
1184 break;
1185 case PLDM_ENTITY_CONNECTOR:
1186 CustomDBus::getCustomDBus().implementConnecterInterface(
1187 entity.first);
1188 break;
1189 case PLDM_ENTITY_BOARD:
1190 CustomDBus::getCustomDBus().implementBoard(entity.first);
1191 break;
1192 case PLDM_ENTITY_CARD:
1193 CustomDBus::getCustomDBus().implementPCIeDeviceInterface(
1194 entity.first);
1195 break;
1196 case PLDM_ENTITY_SYS_BOARD:
1197 CustomDBus::getCustomDBus().implementMotherboardInterface(
1198 entity.first);
1199 break;
1200 case PLDM_ENTITY_FAN:
1201 CustomDBus::getCustomDBus().implementFanInterface(entity.first);
1202 break;
1203 case PLDM_ENTITY_IO_MODULE:
1204 CustomDBus::getCustomDBus().implementFabricAdapter(
1205 entity.first);
1206 break;
1207 default:
1208 break;
1209 }
1210 }
1211 getFRURecordTableMetadataByRemote(fruRecordSetPDRs);
1212 }
1213
1214 } // namespace pldm
1215