1 #include "config.h" 2 3 #include "host_pdr_handler.hpp" 4 5 #include "libpldm/requester/pldm.h" 6 7 #include <assert.h> 8 9 #include <nlohmann/json.hpp> 10 11 #include <fstream> 12 13 namespace pldm 14 { 15 16 using namespace pldm::utils; 17 using namespace sdbusplus::bus::match::rules; 18 using Json = nlohmann::json; 19 namespace fs = std::filesystem; 20 constexpr auto fruJson = "host_frus.json"; 21 const Json emptyJson{}; 22 const std::vector<Json> emptyJsonList{}; 23 24 HostPDRHandler::HostPDRHandler( 25 int mctp_fd, uint8_t mctp_eid, sdeventplus::Event& event, pldm_pdr* repo, 26 const std::string& eventsJsonsDir, pldm_entity_association_tree* entityTree, 27 pldm_entity_association_tree* bmcEntityTree, Requester& requester, 28 pldm::requester::Handler<pldm::requester::Request>* handler, bool verbose) : 29 mctp_fd(mctp_fd), 30 mctp_eid(mctp_eid), event(event), repo(repo), 31 stateSensorHandler(eventsJsonsDir), entityTree(entityTree), 32 bmcEntityTree(bmcEntityTree), requester(requester), handler(handler), 33 verbose(verbose) 34 { 35 fs::path hostFruJson(fs::path(HOST_JSONS_DIR) / fruJson); 36 if (fs::exists(hostFruJson)) 37 { 38 // Note parent entities for entities sent down by the host firmware. 39 // This will enable a merge of entity associations. 40 try 41 { 42 std::ifstream jsonFile(hostFruJson); 43 auto data = Json::parse(jsonFile, nullptr, false); 44 if (data.is_discarded()) 45 { 46 std::cerr << "Parsing Host FRU json file failed" << std::endl; 47 } 48 else 49 { 50 auto entities = data.value("entities", emptyJsonList); 51 for (auto& entity : entities) 52 { 53 EntityType entityType = entity.value("entity_type", 0); 54 auto parent = entity.value("parent", emptyJson); 55 pldm_entity p{}; 56 p.entity_type = parent.value("entity_type", 0); 57 p.entity_instance_num = parent.value("entity_instance", 0); 58 parents.emplace(entityType, std::move(p)); 59 } 60 } 61 } 62 catch (const std::exception& e) 63 { 64 std::cerr << "Parsing Host FRU json file failed, exception = " 65 << e.what() << std::endl; 66 } 67 } 68 69 hostOffMatch = std::make_unique<sdbusplus::bus::match::match>( 70 pldm::utils::DBusHandler::getBus(), 71 propertiesChanged("/xyz/openbmc_project/state/host0", 72 "xyz.openbmc_project.State.Host"), 73 [this, repo, entityTree, 74 bmcEntityTree](sdbusplus::message::message& msg) { 75 DbusChangedProps props{}; 76 std::string intf; 77 msg.read(intf, props); 78 const auto itr = props.find("CurrentHostState"); 79 if (itr != props.end()) 80 { 81 PropertyValue value = itr->second; 82 auto propVal = std::get<std::string>(value); 83 if (propVal == "xyz.openbmc_project.State.Host.HostState.Off") 84 { 85 pldm_pdr_remove_remote_pdrs(repo); 86 pldm_entity_association_tree_destroy_root(entityTree); 87 pldm_entity_association_tree_copy_root(bmcEntityTree, 88 entityTree); 89 this->sensorMap.clear(); 90 } 91 } 92 }); 93 } 94 95 void HostPDRHandler::fetchPDR(PDRRecordHandles&& recordHandles) 96 { 97 pdrRecordHandles.clear(); 98 pdrRecordHandles = std::move(recordHandles); 99 100 // Defer the actual fetch of PDRs from the host (by queuing the call on the 101 // main event loop). That way, we can respond to the platform event msg from 102 // the host firmware. 103 pdrFetchEvent = std::make_unique<sdeventplus::source::Defer>( 104 event, std::bind(std::mem_fn(&HostPDRHandler::_fetchPDR), this, 105 std::placeholders::_1)); 106 } 107 108 void HostPDRHandler::_fetchPDR(sdeventplus::source::EventBase& /*source*/) 109 { 110 getHostPDR(); 111 } 112 113 void HostPDRHandler::getHostPDR(uint32_t nextRecordHandle) 114 { 115 pdrFetchEvent.reset(); 116 117 std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + 118 PLDM_GET_PDR_REQ_BYTES); 119 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 120 uint32_t recordHandle{}; 121 if (!nextRecordHandle) 122 { 123 if (!pdrRecordHandles.empty()) 124 { 125 recordHandle = pdrRecordHandles.front(); 126 pdrRecordHandles.pop_front(); 127 } 128 } 129 else 130 { 131 recordHandle = nextRecordHandle; 132 } 133 auto instanceId = requester.getInstanceId(mctp_eid); 134 135 auto rc = 136 encode_get_pdr_req(instanceId, recordHandle, 0, PLDM_GET_FIRSTPART, 137 UINT16_MAX, 0, request, PLDM_GET_PDR_REQ_BYTES); 138 if (rc != PLDM_SUCCESS) 139 { 140 requester.markFree(mctp_eid, instanceId); 141 std::cerr << "Failed to encode_get_pdr_req, rc = " << rc << std::endl; 142 return; 143 } 144 if (verbose) 145 { 146 std::cout << "Sending Msg:" << std::endl; 147 printBuffer(requestMsg, verbose); 148 } 149 150 rc = handler->registerRequest( 151 mctp_eid, instanceId, PLDM_PLATFORM, PLDM_GET_PDR, 152 std::move(requestMsg), 153 std::move(std::bind_front(&HostPDRHandler::processHostPDRs, this))); 154 if (rc) 155 { 156 std::cerr << "Failed to send the GetPDR request to Host \n"; 157 } 158 } 159 160 int HostPDRHandler::handleStateSensorEvent(const StateSensorEntry& entry, 161 pdr::EventState state) 162 { 163 auto rc = stateSensorHandler.eventAction(entry, state); 164 if (rc != PLDM_SUCCESS) 165 { 166 std::cerr << "Failed to fetch and update D-bus property, rc = " << rc 167 << std::endl; 168 return rc; 169 } 170 return PLDM_SUCCESS; 171 } 172 bool HostPDRHandler::getParent(EntityType type, pldm_entity& parent) 173 { 174 auto found = parents.find(type); 175 if (found != parents.end()) 176 { 177 parent.entity_type = found->second.entity_type; 178 parent.entity_instance_num = found->second.entity_instance_num; 179 return true; 180 } 181 182 return false; 183 } 184 185 void HostPDRHandler::mergeEntityAssociations(const std::vector<uint8_t>& pdr) 186 { 187 size_t numEntities{}; 188 pldm_entity* entities = nullptr; 189 bool merged = false; 190 auto entityPdr = reinterpret_cast<pldm_pdr_entity_association*>( 191 const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr)); 192 193 pldm_entity_association_pdr_extract(pdr.data(), pdr.size(), &numEntities, 194 &entities); 195 for (size_t i = 0; i < numEntities; ++i) 196 { 197 pldm_entity parent{}; 198 if (getParent(entities[i].entity_type, parent)) 199 { 200 auto node = pldm_entity_association_tree_find(entityTree, &parent); 201 if (node) 202 { 203 pldm_entity_association_tree_add(entityTree, &entities[i], 204 0xFFFF, node, 205 entityPdr->association_type); 206 merged = true; 207 } 208 } 209 } 210 free(entities); 211 212 if (merged) 213 { 214 // Update our PDR repo with the merged entity association PDRs 215 pldm_entity_association_pdr_add(entityTree, repo, true); 216 } 217 } 218 219 void HostPDRHandler::sendPDRRepositoryChgEvent(std::vector<uint8_t>&& pdrTypes, 220 uint8_t eventDataFormat) 221 { 222 assert(eventDataFormat == FORMAT_IS_PDR_HANDLES); 223 224 // Extract from the PDR repo record handles of PDRs we want the host 225 // to pull up. 226 std::vector<uint8_t> eventDataOps{PLDM_RECORDS_ADDED}; 227 std::vector<uint8_t> numsOfChangeEntries(1); 228 std::vector<std::vector<ChangeEntry>> changeEntries( 229 numsOfChangeEntries.size()); 230 for (auto pdrType : pdrTypes) 231 { 232 const pldm_pdr_record* record{}; 233 do 234 { 235 record = pldm_pdr_find_record_by_type(repo, pdrType, record, 236 nullptr, nullptr); 237 if (record && pldm_pdr_record_is_remote(record)) 238 { 239 changeEntries[0].push_back( 240 pldm_pdr_get_record_handle(repo, record)); 241 } 242 } while (record); 243 } 244 if (changeEntries.empty()) 245 { 246 return; 247 } 248 numsOfChangeEntries[0] = changeEntries[0].size(); 249 250 // Encode PLDM platform event msg to indicate a PDR repo change. 251 size_t maxSize = PLDM_PDR_REPOSITORY_CHG_EVENT_MIN_LENGTH + 252 PLDM_PDR_REPOSITORY_CHANGE_RECORD_MIN_LENGTH + 253 changeEntries[0].size() * sizeof(uint32_t); 254 std::vector<uint8_t> eventDataVec{}; 255 eventDataVec.resize(maxSize); 256 auto eventData = 257 reinterpret_cast<struct pldm_pdr_repository_chg_event_data*>( 258 eventDataVec.data()); 259 size_t actualSize{}; 260 auto firstEntry = changeEntries[0].data(); 261 auto rc = encode_pldm_pdr_repository_chg_event_data( 262 eventDataFormat, 1, eventDataOps.data(), numsOfChangeEntries.data(), 263 &firstEntry, eventData, &actualSize, maxSize); 264 if (rc != PLDM_SUCCESS) 265 { 266 std::cerr 267 << "Failed to encode_pldm_pdr_repository_chg_event_data, rc = " 268 << rc << std::endl; 269 return; 270 } 271 auto instanceId = requester.getInstanceId(mctp_eid); 272 std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + 273 PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES + 274 actualSize); 275 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 276 rc = encode_platform_event_message_req( 277 instanceId, 1, 0, PLDM_PDR_REPOSITORY_CHG_EVENT, eventDataVec.data(), 278 actualSize, request, 279 actualSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES); 280 if (rc != PLDM_SUCCESS) 281 { 282 requester.markFree(mctp_eid, instanceId); 283 std::cerr << "Failed to encode_platform_event_message_req, rc = " << rc 284 << std::endl; 285 return; 286 } 287 288 auto platformEventMessageResponseHandler = [](mctp_eid_t /*eid*/, 289 const pldm_msg* response, 290 size_t respMsgLen) { 291 if (response == nullptr || !respMsgLen) 292 { 293 std::cerr << "Failed to receive response for the PDR repository " 294 "changed event" 295 << "\n"; 296 return; 297 } 298 299 uint8_t completionCode{}; 300 uint8_t status{}; 301 auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response); 302 auto rc = decode_platform_event_message_resp( 303 responsePtr, respMsgLen - sizeof(pldm_msg_hdr), &completionCode, 304 &status); 305 if (rc || completionCode) 306 { 307 std::cerr << "Failed to decode_platform_event_message_resp: " 308 << "rc=" << rc 309 << ", cc=" << static_cast<unsigned>(completionCode) 310 << std::endl; 311 } 312 }; 313 314 rc = handler->registerRequest( 315 mctp_eid, instanceId, PLDM_PLATFORM, PLDM_PDR_REPOSITORY_CHG_EVENT, 316 std::move(requestMsg), std::move(platformEventMessageResponseHandler)); 317 if (rc) 318 { 319 std::cerr << "Failed to send the PDR repository changed event request" 320 << "\n"; 321 } 322 } 323 324 void HostPDRHandler::parseStateSensorPDRs(const PDRList& stateSensorPDRs, 325 const TLPDRMap& tlpdrInfo) 326 { 327 for (const auto& pdr : stateSensorPDRs) 328 { 329 SensorEntry sensorEntry{}; 330 const auto& [terminusHandle, sensorID, sensorInfo] = 331 responder::pdr_utils::parseStateSensorPDR(pdr); 332 sensorEntry.sensorID = sensorID; 333 try 334 { 335 sensorEntry.terminusID = tlpdrInfo.at(terminusHandle); 336 } 337 // If there is no mapping for terminusHandle assign the reserved TID 338 // value of 0xFF to indicate that. 339 catch (const std::out_of_range& e) 340 { 341 sensorEntry.terminusID = PLDM_TID_RESERVED; 342 } 343 sensorMap.emplace(sensorEntry, std::move(sensorInfo)); 344 } 345 } 346 347 void HostPDRHandler::processHostPDRs(mctp_eid_t /*eid*/, 348 const pldm_msg* response, 349 size_t respMsgLen) 350 { 351 static bool merged = false; 352 static PDRList stateSensorPDRs{}; 353 static TLPDRMap tlpdrInfo{}; 354 uint32_t nextRecordHandle{}; 355 uint8_t completionCode{}; 356 uint32_t nextDataTransferHandle{}; 357 uint8_t transferFlag{}; 358 uint16_t respCount{}; 359 uint8_t transferCRC{}; 360 if (response == nullptr || !respMsgLen) 361 { 362 std::cerr << "Failed to receive response for the GetPDR" 363 " command \n"; 364 return; 365 } 366 367 auto rc = decode_get_pdr_resp( 368 response, respMsgLen /*- sizeof(pldm_msg_hdr)*/, &completionCode, 369 &nextRecordHandle, &nextDataTransferHandle, &transferFlag, &respCount, 370 nullptr, 0, &transferCRC); 371 std::vector<uint8_t> responsePDRMsg; 372 responsePDRMsg.resize(respMsgLen + sizeof(pldm_msg_hdr)); 373 memcpy(responsePDRMsg.data(), response, respMsgLen + sizeof(pldm_msg_hdr)); 374 if (verbose) 375 { 376 std::cout << "Receiving Msg:" << std::endl; 377 printBuffer(responsePDRMsg, verbose); 378 } 379 if (rc != PLDM_SUCCESS) 380 { 381 std::cerr << "Failed to decode_get_pdr_resp, rc = " << rc << std::endl; 382 return; 383 } 384 else 385 { 386 std::vector<uint8_t> pdr(respCount, 0); 387 rc = decode_get_pdr_resp(response, respMsgLen, &completionCode, 388 &nextRecordHandle, &nextDataTransferHandle, 389 &transferFlag, &respCount, pdr.data(), 390 respCount, &transferCRC); 391 if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS) 392 { 393 std::cerr << "Failed to decode_get_pdr_resp: " 394 << "rc=" << rc 395 << ", cc=" << static_cast<unsigned>(completionCode) 396 << std::endl; 397 return; 398 } 399 else 400 { 401 auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(pdr.data()); 402 if (pdrHdr->type == PLDM_PDR_ENTITY_ASSOCIATION) 403 { 404 this->mergeEntityAssociations(pdr); 405 merged = true; 406 } 407 else 408 { 409 if (pdrHdr->type == PLDM_TERMINUS_LOCATOR_PDR) 410 { 411 auto tlpdr = 412 reinterpret_cast<const pldm_terminus_locator_pdr*>( 413 pdr.data()); 414 tlpdrInfo.emplace( 415 static_cast<pldm::pdr::TerminusHandle>( 416 tlpdr->terminus_handle), 417 static_cast<pldm::pdr::TerminusID>(tlpdr->tid)); 418 } 419 else if (pdrHdr->type == PLDM_STATE_SENSOR_PDR) 420 { 421 stateSensorPDRs.emplace_back(pdr); 422 } 423 pldm_pdr_add(repo, pdr.data(), respCount, 0, true); 424 } 425 } 426 } 427 if (!nextRecordHandle) 428 { 429 /*received last record*/ 430 this->parseStateSensorPDRs(stateSensorPDRs, tlpdrInfo); 431 stateSensorPDRs.clear(); 432 tlpdrInfo.clear(); 433 if (merged) 434 { 435 merged = false; 436 deferredPDRRepoChgEvent = 437 std::make_unique<sdeventplus::source::Defer>( 438 event, 439 std::bind( 440 std::mem_fn((&HostPDRHandler::_processPDRRepoChgEvent)), 441 this, std::placeholders::_1)); 442 } 443 } 444 else 445 { 446 deferredFetchPDREvent = std::make_unique<sdeventplus::source::Defer>( 447 event, 448 std::bind(std::mem_fn((&HostPDRHandler::_processFetchPDREvent)), 449 this, nextRecordHandle, std::placeholders::_1)); 450 } 451 } 452 453 void HostPDRHandler::_processPDRRepoChgEvent( 454 sdeventplus::source::EventBase& /*source */) 455 { 456 deferredPDRRepoChgEvent.reset(); 457 this->sendPDRRepositoryChgEvent( 458 std::move(std::vector<uint8_t>(1, PLDM_PDR_ENTITY_ASSOCIATION)), 459 FORMAT_IS_PDR_HANDLES); 460 } 461 462 void HostPDRHandler::_processFetchPDREvent( 463 uint32_t nextRecordHandle, sdeventplus::source::EventBase& /*source */) 464 { 465 deferredFetchPDREvent.reset(); 466 if (!this->pdrRecordHandles.empty()) 467 { 468 nextRecordHandle = this->pdrRecordHandles.front(); 469 this->pdrRecordHandles.pop_front(); 470 } 471 this->getHostPDR(nextRecordHandle); 472 } 473 474 } // namespace pldm 475