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