1 #include "file_io_type_pcie.hpp" 2 3 #include <libpldm/base.h> 4 #include <libpldm/oem/ibm/file_io.h> 5 #include <stdint.h> 6 7 #include <phosphor-logging/lg2.hpp> 8 9 PHOSPHOR_LOG2_USING; 10 11 namespace pldm 12 { 13 namespace responder 14 { 15 16 std::unordered_map<uint8_t, std::string> linkStateMap{ 17 {0x00, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Status.Operational"}, 18 {0x01, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Status.Degraded"}, 19 {0x02, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Status.Unused"}, 20 {0x03, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Status.Unused"}, 21 {0x04, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Status.Failed"}, 22 {0x05, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Status.Open"}, 23 {0x06, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Status.Inactive"}, 24 {0x07, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Status.Unused"}, 25 {0xFF, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Status.Unknown"}}; 26 27 std::unordered_map<uint8_t, std::string> linkSpeed{ 28 {0x00, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen1"}, 29 {0x01, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen2"}, 30 {0x02, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen3"}, 31 {0x03, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen4"}, 32 {0x04, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen5"}, 33 {0x10, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown"}, 34 {0xFF, "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown"}}; 35 36 std::unordered_map<uint8_t, size_t> linkWidth{ 37 {0x01, 1}, {0x02, 2}, {0x04, 4}, {0x08, 8}, 38 {0x10, 16}, {0xFF, UINT_MAX}, {0x00, 0}}; 39 40 std::unordered_map<uint8_t, double> cableLengthMap{ 41 {0x00, 0}, {0x01, 2}, {0x02, 3}, 42 {0x03, 10}, {0x04, 20}, {0xFF, std::numeric_limits<double>::quiet_NaN()}}; 43 44 std::unordered_map<uint8_t, std::string> cableTypeMap{ 45 {0x00, "optical"}, {0x01, "copper"}, {0xFF, "Unknown"}}; 46 47 std::unordered_map<uint8_t, std::string> cableStatusMap{ 48 {0x00, "xyz.openbmc_project.Inventory.Item.Cable.Status.Inactive"}, 49 {0x01, "xyz.openbmc_project.Inventory.Item.Cable.Status.Running"}, 50 {0x02, "xyz.openbmc_project.Inventory.Item.Cable.Status.PoweredOff"}, 51 {0xFF, "xyz.openbmc_project.Inventory.Item.Cable.Status.Unknown"}}; 52 53 static constexpr auto pciePath = "/var/lib/pldm/pcie-topology/"; 54 constexpr auto topologyFile = "topology"; 55 constexpr auto cableInfoFile = "cableinfo"; 56 57 // Slot location code structure contains multiple slot location code 58 // suffix structures. 59 // Each slot location code suffix structure is as follows 60 // {Slot location code suffix size(uint8_t), 61 // Slot location code suffix(variable size)} 62 constexpr auto sizeOfSuffixSizeDataMember = 1; 63 64 // Each slot location structure contains 65 // { 66 // Number of slot location codes (1byte), 67 // Slot location code Common part size (1byte) 68 // Slot location common part (Var) 69 // } 70 constexpr auto slotLocationDataMemberSize = 2; 71 72 namespace fs = std::filesystem; 73 74 std::unordered_map<uint16_t, bool> PCIeInfoHandler::receivedFiles; 75 std::unordered_map<LinkId, std::tuple<LinkStatus, linkTypeData, LinkSpeed, 76 LinkWidth, PcieHostBridgeLoc, LocalPort, 77 RemotePort, IoSlotLocation, LinkId>> 78 PCIeInfoHandler::topologyInformation; 79 std::unordered_map< 80 CableLinkNum, std::tuple<LinkId, LocalPortLocCode, IoSlotLocationCode, 81 CablePartNum, CableLength, CableType, CableStatus>> 82 PCIeInfoHandler::cableInformation; 83 std::unordered_map<LinkId, linkTypeData> PCIeInfoHandler::linkTypeInfo; 84 85 PCIeInfoHandler::PCIeInfoHandler(uint32_t fileHandle, uint16_t fileType) : 86 FileHandler(fileHandle), infoType(fileType) 87 { 88 receivedFiles.emplace(infoType, false); 89 } 90 91 int PCIeInfoHandler::writeFromMemory( 92 uint32_t offset, uint32_t length, uint64_t address, 93 oem_platform::Handler* /*oemPlatformHandler*/) 94 { 95 if (!fs::exists(pciePath)) 96 { 97 fs::create_directories(pciePath); 98 fs::permissions(pciePath, 99 fs::perms::others_read | fs::perms::owner_write); 100 } 101 102 fs::path infoFile(fs::path(pciePath) / topologyFile); 103 if (infoType == PLDM_FILE_TYPE_CABLE_INFO) 104 { 105 infoFile = (fs::path(pciePath) / cableInfoFile); 106 } 107 108 try 109 { 110 std::ofstream pcieData(infoFile, std::ios::out | std::ios::binary); 111 auto rc = transferFileData(infoFile, false, offset, length, address); 112 if (rc != PLDM_SUCCESS) 113 { 114 error("TransferFileData failed in PCIeTopology with error {ERROR}", 115 "ERROR", rc); 116 return rc; 117 } 118 return PLDM_SUCCESS; 119 } 120 catch (const std::exception& e) 121 { 122 error("Create/Write data to the File type {TYPE}, failed {ERROR}", 123 "TYPE", infoType, "ERROR", e); 124 return PLDM_ERROR; 125 } 126 } 127 128 int PCIeInfoHandler::write(const char* buffer, uint32_t, uint32_t& length, 129 oem_platform::Handler* /*oemPlatformHandler*/) 130 { 131 fs::path infoFile(fs::path(pciePath) / topologyFile); 132 if (infoType == PLDM_FILE_TYPE_CABLE_INFO) 133 { 134 infoFile = (fs::path(pciePath) / cableInfoFile); 135 } 136 137 try 138 { 139 std::ofstream pcieData(infoFile, std::ios::out | std::ios::binary | 140 std::ios::app); 141 142 if (!buffer) 143 { 144 pcieData.write(buffer, length); 145 } 146 pcieData.close(); 147 } 148 catch (const std::exception& e) 149 { 150 error("Create/Write data to the File type {TYPE}, failed {ERROR}", 151 "TYPE", infoType, "ERROR", e); 152 return PLDM_ERROR; 153 } 154 155 return PLDM_SUCCESS; 156 } 157 158 int PCIeInfoHandler::fileAck(uint8_t /*fileStatus*/) 159 { 160 receivedFiles[infoType] = true; 161 try 162 { 163 if (receivedFiles.at(PLDM_FILE_TYPE_CABLE_INFO) && 164 receivedFiles.at(PLDM_FILE_TYPE_PCIE_TOPOLOGY)) 165 { 166 receivedFiles.clear(); 167 // parse the topology data and cache the information 168 // for further processing 169 parseTopologyData(); 170 } 171 } 172 catch (const std::out_of_range& e) 173 { 174 info("Received only one of the topology file"); 175 } 176 return PLDM_SUCCESS; 177 } 178 179 void PCIeInfoHandler::parseTopologyData() 180 { 181 int fd = open((fs::path(pciePath) / topologyFile).string().c_str(), 182 O_RDONLY, S_IRUSR | S_IWUSR); 183 if (fd == -1) 184 { 185 perror("Topology file not present"); 186 return; 187 } 188 pldm::utils::CustomFD topologyFd(fd); 189 // Reading the statistics of the topology file, to get the size. 190 // stat sb is the out parameter provided to fstat 191 struct stat sb; 192 if (fstat(fd, &sb) == -1) 193 { 194 perror("Could not get topology file size"); 195 return; 196 } 197 198 if (sb.st_size == 0) 199 { 200 error("Topology file Size is 0"); 201 return; 202 } 203 204 auto topologyCleanup = [sb](void* fileInMemory) { 205 munmap(fileInMemory, sb.st_size); 206 }; 207 208 // memory map the topology file into pldm memory 209 void* fileInMemory = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, 210 topologyFd(), 0); 211 if (MAP_FAILED == fileInMemory) 212 { 213 error("mmap on topology file failed with error {RC}", "RC", -errno); 214 return; 215 } 216 217 std::unique_ptr<void, decltype(topologyCleanup)> topologyPtr( 218 fileInMemory, topologyCleanup); 219 220 auto pcieLinkList = reinterpret_cast<struct topologyBlob*>(fileInMemory); 221 uint16_t numOfLinks = 0; 222 if (!pcieLinkList) 223 { 224 error("Parsing of topology file failed : pcieLinkList is null"); 225 return; 226 } 227 228 // The elements in the structure that were being parsed from the obtained 229 // files via write() and writeFromMemory() were written by IBM enterprise 230 // host firmware which runs on big-endian format. The DMA engine in BMC 231 // (aspeed-xdma device driver) which is responsible for exposing the data 232 // shared by the host in the shared VGA memory has no idea of the structure 233 // of the data it's copying from host. So it's up to the consumers of the 234 // data to deal with the endianness once it has been copied to user space & 235 // and as pldm is the first application layer that interprets the data 236 // provided by host reading from the sysfs interface of xdma-engine & also 237 // since pldm application runs on BMC which could be little/big endian, its 238 // essential to swap the endianness to agreed big-endian format (htobe) 239 // before using the data. 240 241 numOfLinks = htobe16(pcieLinkList->numPcieLinkEntries); 242 243 // To fetch the PCIe link from the topology data, moving the pointer 244 // by 8 bytes based on the size of other elements of the structure 245 struct pcieLinkEntry* singleEntryData = 246 reinterpret_cast<struct pcieLinkEntry*>( 247 reinterpret_cast<uint8_t*>(pcieLinkList) + 8); 248 249 if (!singleEntryData) 250 { 251 error("Parsing of topology file failed : single_link is null"); 252 return; 253 } 254 255 auto singleEntryDataCharStream = reinterpret_cast<char*>(singleEntryData); 256 257 // iterate over every pcie link and get the link specific attributes 258 for ([[maybe_unused]] const auto& link : 259 std::views::iota(0) | std::views::take(numOfLinks)) 260 { 261 // get the link id 262 auto linkId = htobe16(singleEntryData->linkId); 263 264 // get parent link id 265 auto parentLinkId = htobe16(singleEntryData->parentLinkId); 266 267 // get link status 268 auto linkStatus = singleEntryData->linkStatus; 269 270 // get link type 271 auto type = singleEntryData->linkType; 272 if (type != pldm::responder::linkTypeData::Unknown) 273 { 274 linkTypeInfo[linkId] = type; 275 } 276 277 // get link speed 278 auto linkSpeed = singleEntryData->linkSpeed; 279 280 // get link width 281 auto width = singleEntryData->linkWidth; 282 283 // get the PCIe Host Bridge Location 284 size_t pcieLocCodeSize = singleEntryData->pcieHostBridgeLocCodeSize; 285 std::vector<char> pcieHostBridgeLocation( 286 singleEntryDataCharStream + 287 htobe16(singleEntryData->pcieHostBridgeLocCodeOff), 288 singleEntryDataCharStream + 289 htobe16(singleEntryData->pcieHostBridgeLocCodeOff) + 290 static_cast<int>(pcieLocCodeSize)); 291 std::string pcieHostBridgeLocationCode(pcieHostBridgeLocation.begin(), 292 pcieHostBridgeLocation.end()); 293 294 // get the local port - top location 295 size_t localTopPortLocSize = singleEntryData->topLocalPortLocCodeSize; 296 std::vector<char> localTopPortLocation( 297 singleEntryDataCharStream + 298 htobe16(singleEntryData->topLocalPortLocCodeOff), 299 singleEntryDataCharStream + 300 htobe16(singleEntryData->topLocalPortLocCodeOff) + 301 static_cast<int>(localTopPortLocSize)); 302 std::string localTopPortLocationCode(localTopPortLocation.begin(), 303 localTopPortLocation.end()); 304 305 // get the local port - bottom location 306 size_t localBottomPortLocSize = 307 singleEntryData->bottomLocalPortLocCodeSize; 308 std::vector<char> localBottomPortLocation( 309 singleEntryDataCharStream + 310 htobe16(singleEntryData->bottomLocalPortLocCodeOff), 311 singleEntryDataCharStream + 312 htobe16(singleEntryData->bottomLocalPortLocCodeOff) + 313 static_cast<int>(localBottomPortLocSize)); 314 std::string localBottomPortLocationCode(localBottomPortLocation.begin(), 315 localBottomPortLocation.end()); 316 317 // get the remote port - top location 318 size_t remoteTopPortLocSize = singleEntryData->topRemotePortLocCodeSize; 319 std::vector<char> remoteTopPortLocation( 320 singleEntryDataCharStream + 321 htobe16(singleEntryData->topRemotePortLocCodeOff), 322 singleEntryDataCharStream + 323 htobe16(singleEntryData->topRemotePortLocCodeOff) + 324 static_cast<int>(remoteTopPortLocSize)); 325 std::string remoteTopPortLocationCode(remoteTopPortLocation.begin(), 326 remoteTopPortLocation.end()); 327 328 // get the remote port - bottom location 329 size_t remoteBottomLocSize = 330 singleEntryData->bottomRemotePortLocCodeSize; 331 std::vector<char> remoteBottomPortLocation( 332 singleEntryDataCharStream + 333 htobe16(singleEntryData->bottomRemotePortLocCodeOff), 334 singleEntryDataCharStream + 335 htobe16(singleEntryData->bottomRemotePortLocCodeOff) + 336 static_cast<int>(remoteBottomLocSize)); 337 std::string remoteBottomPortLocationCode( 338 remoteBottomPortLocation.begin(), remoteBottomPortLocation.end()); 339 340 struct slotLocCode* slotData = reinterpret_cast<struct slotLocCode*>( 341 (reinterpret_cast<uint8_t*>(singleEntryData)) + 342 htobe16(singleEntryData->slotLocCodesOffset)); 343 if (!slotData) 344 { 345 error("Parsing the topology file failed : slotData is null"); 346 return; 347 } 348 // get the Slot location code common part 349 size_t numOfSlots = slotData->numSlotLocCodes; 350 size_t slotLocCodeCompartSize = slotData->slotLocCodesCmnPrtSize; 351 std::vector<char> slotLocation( 352 reinterpret_cast<char*>(slotData->slotLocCodesCmnPrt), 353 (reinterpret_cast<char*>(slotData->slotLocCodesCmnPrt) + 354 static_cast<int>(slotLocCodeCompartSize))); 355 std::string slotLocationCode(slotLocation.begin(), slotLocation.end()); 356 357 uint8_t* suffixData = reinterpret_cast<uint8_t*>(slotData) + 358 slotLocationDataMemberSize + 359 slotData->slotLocCodesCmnPrtSize; 360 if (!suffixData) 361 { 362 error("slot location suffix data is nullptr"); 363 return; 364 } 365 366 // create the full slot location code by combining common part and 367 // suffix part 368 std::string slotSuffixLocationCode; 369 std::vector<std::string> slotFinaLocationCode{}; 370 for ([[maybe_unused]] const auto& slot : 371 std::views::iota(0) | std::views::take(numOfSlots)) 372 { 373 struct slotLocCodeSuf* slotLocSufData = 374 reinterpret_cast<struct slotLocCodeSuf*>(suffixData); 375 if (!slotLocSufData) 376 { 377 error("slot location suffix data is nullptr"); 378 break; 379 } 380 381 size_t slotLocCodeSuffixSize = slotLocSufData->slotLocCodeSz; 382 if (slotLocCodeSuffixSize > 0) 383 { 384 std::vector<char> slotSuffixLocation( 385 reinterpret_cast<char*>(slotLocSufData) + 1, 386 reinterpret_cast<char*>(slotLocSufData) + 1 + 387 static_cast<int>(slotLocCodeSuffixSize)); 388 std::string slotSuffLocationCode(slotSuffixLocation.begin(), 389 slotSuffixLocation.end()); 390 391 slotSuffixLocationCode = slotSuffLocationCode; 392 } 393 std::string slotFullLocationCode = slotLocationCode + 394 slotSuffixLocationCode; 395 slotFinaLocationCode.push_back(slotFullLocationCode); 396 397 // move the pointer to next slot 398 suffixData += sizeOfSuffixSizeDataMember + slotLocCodeSuffixSize; 399 } 400 401 // store the information into a map 402 topologyInformation[linkId] = 403 std::make_tuple(linkStateMap[linkStatus], type, linkSpeed, 404 linkWidth[width], pcieHostBridgeLocationCode, 405 std::make_pair(localTopPortLocationCode, 406 localBottomPortLocationCode), 407 std::make_pair(remoteTopPortLocationCode, 408 remoteBottomPortLocationCode), 409 slotFinaLocationCode, parentLinkId); 410 411 // move the pointer to next link 412 singleEntryData = reinterpret_cast<struct pcieLinkEntry*>( 413 reinterpret_cast<uint8_t*>(singleEntryData) + 414 htobe16(singleEntryData->entryLength)); 415 } 416 // Need to call cable info at the end , because we dont want to parse 417 // cable info without parsing the successful topology successfully 418 // Having partial information is of no use. 419 parseCableInfo(); 420 } 421 422 void PCIeInfoHandler::parseCableInfo() 423 { 424 int fd = open((fs::path(pciePath) / cableInfoFile).string().c_str(), 425 O_RDONLY, S_IRUSR | S_IWUSR); 426 if (fd == -1) 427 { 428 perror("CableInfo file not present"); 429 return; 430 } 431 pldm::utils::CustomFD cableInfoFd(fd); 432 struct stat sb; 433 434 if (fstat(fd, &sb) == -1) 435 { 436 perror("Could not get cableinfo file size"); 437 return; 438 } 439 440 if (sb.st_size == 0) 441 { 442 error("Topology file Size is 0"); 443 return; 444 } 445 446 auto cableInfoCleanup = [sb](void* fileInMemory) { 447 munmap(fileInMemory, sb.st_size); 448 }; 449 450 void* fileInMemory = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, 451 cableInfoFd(), 0); 452 453 if (MAP_FAILED == fileInMemory) 454 { 455 int rc = -errno; 456 error("mmap on cable ifno file failed, RC={RC}", "RC", rc); 457 return; 458 } 459 460 std::unique_ptr<void, decltype(cableInfoCleanup)> cablePtr( 461 fileInMemory, cableInfoCleanup); 462 463 auto cableList = 464 reinterpret_cast<struct cableAttributesList*>(fileInMemory); 465 466 // get number of cable links 467 auto numOfCableLinks = htobe16(cableList->numOfCables); 468 469 struct pcieLinkCableAttr* cableData = 470 reinterpret_cast<struct pcieLinkCableAttr*>( 471 (reinterpret_cast<uint8_t*>(cableList)) + 472 (sizeof(struct cableAttributesList) - 1)); 473 474 if (!cableData) 475 { 476 error("Cable info parsing failed , cableData = nullptr"); 477 return; 478 } 479 480 // iterate over each pci cable link 481 for (const auto& cable : 482 std::views::iota(0) | std::views::take(numOfCableLinks)) 483 { 484 // get the link id 485 auto linkId = htobe16(cableData->linkId); 486 char* cableDataPtr = reinterpret_cast<char*>(cableData); 487 488 std::string localPortLocCode( 489 cableDataPtr + htobe16(cableData->hostPortLocationCodeOffset), 490 cableData->hostPortLocationCodeSize); 491 492 std::string ioSlotLocationCode( 493 cableDataPtr + 494 htobe16(cableData->ioEnclosurePortLocationCodeOffset), 495 cableData->ioEnclosurePortLocationCodeSize); 496 497 std::string cablePartNum(cableDataPtr + 498 htobe16(cableData->cablePartNumberOffset), 499 cableData->cablePartNumberSize); 500 501 // cache the data into a map 502 cableInformation[cable] = std::make_tuple( 503 linkId, localPortLocCode, ioSlotLocationCode, cablePartNum, 504 cableLengthMap[cableData->cableLength], 505 cableTypeMap[cableData->cableType], 506 cableStatusMap[cableData->cableStatus]); 507 // move the cable data pointer 508 509 cableData = reinterpret_cast<struct pcieLinkCableAttr*>( 510 (reinterpret_cast<uint8_t*>(cableData)) + 511 htobe16(cableData->entryLength)); 512 } 513 } 514 515 } // namespace responder 516 } // namespace pldm 517