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