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 pdrFetchEvent.reset(); 111 112 std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + 113 PLDM_GET_PDR_REQ_BYTES); 114 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 115 bool merged = false; 116 PDRList stateSensorPDRs{}; 117 TLPDRMap tlpdrInfo{}; 118 119 uint32_t nextRecordHandle{}; 120 uint32_t recordHandle{}; 121 bool isFormatRecHandles = false; 122 if (!pdrRecordHandles.empty()) 123 { 124 recordHandle = pdrRecordHandles.front(); 125 pdrRecordHandles.pop_front(); 126 isFormatRecHandles = true; 127 } 128 129 do 130 { 131 auto instanceId = requester.getInstanceId(mctp_eid); 132 133 auto rc = 134 encode_get_pdr_req(instanceId, recordHandle, 0, PLDM_GET_FIRSTPART, 135 UINT16_MAX, 0, request, PLDM_GET_PDR_REQ_BYTES); 136 if (rc != PLDM_SUCCESS) 137 { 138 requester.markFree(mctp_eid, instanceId); 139 std::cerr << "Failed to encode_get_pdr_req, rc = " << rc 140 << std::endl; 141 return; 142 } 143 if (verbose) 144 { 145 std::cout << "Sending Msg:" << std::endl; 146 printBuffer(requestMsg, verbose); 147 } 148 149 uint8_t* responseMsg = nullptr; 150 size_t responseMsgSize{}; 151 auto requesterRc = 152 pldm_send_recv(mctp_eid, mctp_fd, requestMsg.data(), 153 requestMsg.size(), &responseMsg, &responseMsgSize); 154 std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{ 155 responseMsg, std::free}; 156 requester.markFree(mctp_eid, instanceId); 157 if (requesterRc != PLDM_REQUESTER_SUCCESS) 158 { 159 std::cerr << "Failed to send msg to fetch pdrs, rc = " 160 << requesterRc << std::endl; 161 return; 162 } 163 164 uint8_t completionCode{}; 165 uint32_t nextDataTransferHandle{}; 166 uint8_t transferFlag{}; 167 uint16_t respCount{}; 168 uint8_t transferCRC{}; 169 auto responsePtr = 170 reinterpret_cast<struct pldm_msg*>(responseMsgPtr.get()); 171 rc = decode_get_pdr_resp( 172 responsePtr, responseMsgSize - sizeof(pldm_msg_hdr), 173 &completionCode, &nextRecordHandle, &nextDataTransferHandle, 174 &transferFlag, &respCount, nullptr, 0, &transferCRC); 175 176 std::vector<uint8_t> responsePDRMsg; 177 responsePDRMsg.resize(responseMsgSize); 178 memcpy(responsePDRMsg.data(), responsePtr, responsePDRMsg.size()); 179 if (verbose) 180 { 181 std::cout << "Receiving Msg:" << std::endl; 182 printBuffer(responsePDRMsg, verbose); 183 } 184 if (rc != PLDM_SUCCESS) 185 { 186 std::cerr << "Failed to decode_get_pdr_resp, rc = " << rc 187 << std::endl; 188 } 189 else 190 { 191 std::vector<uint8_t> pdr(respCount, 0); 192 rc = decode_get_pdr_resp( 193 responsePtr, responseMsgSize - sizeof(pldm_msg_hdr), 194 &completionCode, &nextRecordHandle, &nextDataTransferHandle, 195 &transferFlag, &respCount, pdr.data(), respCount, &transferCRC); 196 if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS) 197 { 198 std::cerr << "Failed to decode_get_pdr_resp: " 199 << "rc=" << rc 200 << ", cc=" << static_cast<unsigned>(completionCode) 201 << std::endl; 202 } 203 else 204 { 205 // Process the PDR host firmware sent us. The most common action 206 // is to add the PDR to the the BMC's PDR repo. 207 auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(pdr.data()); 208 if (pdrHdr->type == PLDM_PDR_ENTITY_ASSOCIATION) 209 { 210 mergeEntityAssociations(pdr); 211 merged = true; 212 } 213 else 214 { 215 if (pdrHdr->type == PLDM_TERMINUS_LOCATOR_PDR) 216 { 217 auto tlpdr = 218 reinterpret_cast<const pldm_terminus_locator_pdr*>( 219 pdr.data()); 220 tlpdrInfo.emplace( 221 static_cast<pdr::TerminusHandle>( 222 tlpdr->terminus_handle), 223 static_cast<pdr::TerminusID>(tlpdr->tid)); 224 } 225 else if (pdrHdr->type == PLDM_STATE_SENSOR_PDR) 226 { 227 stateSensorPDRs.emplace_back(pdr); 228 } 229 pldm_pdr_add(repo, pdr.data(), respCount, 0, true); 230 } 231 } 232 233 recordHandle = nextRecordHandle; 234 if (!pdrRecordHandles.empty()) 235 { 236 recordHandle = pdrRecordHandles.front(); 237 pdrRecordHandles.pop_front(); 238 } 239 else if (isFormatRecHandles) 240 { 241 break; 242 } 243 } 244 } while (recordHandle); 245 246 parseStateSensorPDRs(stateSensorPDRs, tlpdrInfo); 247 248 if (merged) 249 { 250 // We have merged host's entity association PDRs with our own. Send an 251 // event to the host firmware to indicate the same. 252 sendPDRRepositoryChgEvent( 253 std::move(std::vector<uint8_t>(1, PLDM_PDR_ENTITY_ASSOCIATION)), 254 FORMAT_IS_PDR_HANDLES); 255 } 256 } 257 258 int HostPDRHandler::handleStateSensorEvent(const StateSensorEntry& entry, 259 pdr::EventState state) 260 { 261 auto rc = stateSensorHandler.eventAction(entry, state); 262 if (rc != PLDM_SUCCESS) 263 { 264 std::cerr << "Failed to fetch and update D-bus property, rc = " << rc 265 << std::endl; 266 return rc; 267 } 268 return PLDM_SUCCESS; 269 } 270 bool HostPDRHandler::getParent(EntityType type, pldm_entity& parent) 271 { 272 auto found = parents.find(type); 273 if (found != parents.end()) 274 { 275 parent.entity_type = found->second.entity_type; 276 parent.entity_instance_num = found->second.entity_instance_num; 277 return true; 278 } 279 280 return false; 281 } 282 283 void HostPDRHandler::mergeEntityAssociations(const std::vector<uint8_t>& pdr) 284 { 285 size_t numEntities{}; 286 pldm_entity* entities = nullptr; 287 bool merged = false; 288 auto entityPdr = reinterpret_cast<pldm_pdr_entity_association*>( 289 const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr)); 290 291 pldm_entity_association_pdr_extract(pdr.data(), pdr.size(), &numEntities, 292 &entities); 293 for (size_t i = 0; i < numEntities; ++i) 294 { 295 pldm_entity parent{}; 296 if (getParent(entities[i].entity_type, parent)) 297 { 298 auto node = pldm_entity_association_tree_find(entityTree, &parent); 299 if (node) 300 { 301 pldm_entity_association_tree_add(entityTree, &entities[i], node, 302 entityPdr->association_type); 303 merged = true; 304 } 305 } 306 } 307 free(entities); 308 309 if (merged) 310 { 311 // Update our PDR repo with the merged entity association PDRs 312 pldm_entity_association_pdr_add(entityTree, repo, true); 313 } 314 } 315 316 void HostPDRHandler::sendPDRRepositoryChgEvent(std::vector<uint8_t>&& pdrTypes, 317 uint8_t eventDataFormat) 318 { 319 assert(eventDataFormat == FORMAT_IS_PDR_HANDLES); 320 321 // Extract from the PDR repo record handles of PDRs we want the host 322 // to pull up. 323 std::vector<uint8_t> eventDataOps{PLDM_RECORDS_ADDED}; 324 std::vector<uint8_t> numsOfChangeEntries(1); 325 std::vector<std::vector<ChangeEntry>> changeEntries( 326 numsOfChangeEntries.size()); 327 for (auto pdrType : pdrTypes) 328 { 329 const pldm_pdr_record* record{}; 330 do 331 { 332 record = pldm_pdr_find_record_by_type(repo, pdrType, record, 333 nullptr, nullptr); 334 if (record && pldm_pdr_record_is_remote(record)) 335 { 336 changeEntries[0].push_back( 337 pldm_pdr_get_record_handle(repo, record)); 338 } 339 } while (record); 340 } 341 if (changeEntries.empty()) 342 { 343 return; 344 } 345 numsOfChangeEntries[0] = changeEntries[0].size(); 346 347 // Encode PLDM platform event msg to indicate a PDR repo change. 348 size_t maxSize = PLDM_PDR_REPOSITORY_CHG_EVENT_MIN_LENGTH + 349 PLDM_PDR_REPOSITORY_CHANGE_RECORD_MIN_LENGTH + 350 changeEntries[0].size() * sizeof(uint32_t); 351 std::vector<uint8_t> eventDataVec{}; 352 eventDataVec.resize(maxSize); 353 auto eventData = 354 reinterpret_cast<struct pldm_pdr_repository_chg_event_data*>( 355 eventDataVec.data()); 356 size_t actualSize{}; 357 auto firstEntry = changeEntries[0].data(); 358 auto rc = encode_pldm_pdr_repository_chg_event_data( 359 eventDataFormat, 1, eventDataOps.data(), numsOfChangeEntries.data(), 360 &firstEntry, eventData, &actualSize, maxSize); 361 if (rc != PLDM_SUCCESS) 362 { 363 std::cerr 364 << "Failed to encode_pldm_pdr_repository_chg_event_data, rc = " 365 << rc << std::endl; 366 return; 367 } 368 auto instanceId = requester.getInstanceId(mctp_eid); 369 std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + 370 PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES + 371 actualSize); 372 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 373 rc = encode_platform_event_message_req( 374 instanceId, 1, 0, PLDM_PDR_REPOSITORY_CHG_EVENT, eventDataVec.data(), 375 actualSize, request, 376 actualSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES); 377 if (rc != PLDM_SUCCESS) 378 { 379 requester.markFree(mctp_eid, instanceId); 380 std::cerr << "Failed to encode_platform_event_message_req, rc = " << rc 381 << std::endl; 382 return; 383 } 384 385 auto platformEventMessageResponseHandler = [](mctp_eid_t /*eid*/, 386 const pldm_msg* response, 387 size_t respMsgLen) { 388 if (response == nullptr || !respMsgLen) 389 { 390 std::cerr << "Failed to receive response for the PDR repository " 391 "changed event" 392 << "\n"; 393 return; 394 } 395 396 uint8_t completionCode{}; 397 uint8_t status{}; 398 auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response); 399 auto rc = decode_platform_event_message_resp( 400 responsePtr, respMsgLen - sizeof(pldm_msg_hdr), &completionCode, 401 &status); 402 if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS) 403 { 404 std::cerr << "Failed to decode_platform_event_message_resp: " 405 << "rc=" << rc 406 << ", cc=" << static_cast<unsigned>(completionCode) 407 << std::endl; 408 } 409 }; 410 411 rc = handler.registerRequest( 412 mctp_eid, instanceId, PLDM_PLATFORM, PLDM_PDR_REPOSITORY_CHG_EVENT, 413 std::move(requestMsg), std::move(platformEventMessageResponseHandler)); 414 if (rc != PLDM_SUCCESS) 415 { 416 std::cerr << "Failed to send the PDR repository changed event request" 417 << "\n"; 418 } 419 } 420 421 void HostPDRHandler::parseStateSensorPDRs(const PDRList& stateSensorPDRs, 422 const TLPDRMap& tlpdrInfo) 423 { 424 for (const auto& pdr : stateSensorPDRs) 425 { 426 SensorEntry sensorEntry{}; 427 const auto& [terminusHandle, sensorID, sensorInfo] = 428 responder::pdr_utils::parseStateSensorPDR(pdr); 429 sensorEntry.sensorID = sensorID; 430 try 431 { 432 sensorEntry.terminusID = tlpdrInfo.at(terminusHandle); 433 } 434 // If there is no mapping for terminusHandle assign the reserved TID 435 // value of 0xFF to indicate that. 436 catch (const std::out_of_range& e) 437 { 438 sensorEntry.terminusID = PLDM_TID_RESERVED; 439 } 440 sensorMap.emplace(sensorEntry, std::move(sensorInfo)); 441 } 442 } 443 444 } // namespace pldm 445