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