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 
PCIeInfoHandler(uint32_t fileHandle,uint16_t fileType)85 PCIeInfoHandler::PCIeInfoHandler(uint32_t fileHandle, uint16_t fileType) :
86     FileHandler(fileHandle), infoType(fileType)
87 {
88     receivedFiles.emplace(infoType, false);
89 }
90 
writeFromMemory(uint32_t offset,uint32_t length,uint64_t address,oem_platform::Handler *)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 
write(const char * buffer,uint32_t,uint32_t & length,oem_platform::Handler *)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 
fileAck(uint8_t)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 
parseTopologyData()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 
parseCableInfo()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