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