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