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