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