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