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