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