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