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