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