1 /*
2 * SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include "NvidiaGpuMctpVdm.hpp"
7
8 #include "OcpMctpVdm.hpp"
9
10 #include <endian.h>
11
12 #include <cerrno>
13 #include <cstddef>
14 #include <cstdint>
15 #include <cstring>
16 #include <span>
17 #include <vector>
18
19 namespace gpu
20 {
21 // These functions encode/decode data communicated over the network
22 // The use of reinterpret_cast enables direct memory access to raw byte buffers
23 // without doing unnecessary data copying
24 // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
packHeader(const ocp::accelerator_management::BindingPciVidInfo & hdr,ocp::accelerator_management::BindingPciVid & msg)25 int packHeader(const ocp::accelerator_management::BindingPciVidInfo& hdr,
26 ocp::accelerator_management::BindingPciVid& msg)
27 {
28 return ocp::accelerator_management::packHeader(nvidiaPciVendorId, hdr, msg);
29 }
30
encodeQueryDeviceIdentificationRequest(uint8_t instanceId,const std::span<uint8_t> buf)31 int encodeQueryDeviceIdentificationRequest(uint8_t instanceId,
32 const std::span<uint8_t> buf)
33 {
34 if (buf.size() < sizeof(QueryDeviceIdentificationRequest))
35 {
36 return EINVAL;
37 }
38
39 auto* msg = reinterpret_cast<QueryDeviceIdentificationRequest*>(buf.data());
40
41 ocp::accelerator_management::BindingPciVidInfo header{};
42
43 header.ocp_accelerator_management_msg_type =
44 static_cast<uint8_t>(ocp::accelerator_management::MessageType::REQUEST);
45 header.instance_id = instanceId &
46 ocp::accelerator_management::instanceIdBitMask;
47 header.msg_type =
48 static_cast<uint8_t>(MessageType::DEVICE_CAPABILITY_DISCOVERY);
49
50 auto rc = packHeader(header, msg->hdr.msgHdr.hdr);
51
52 if (rc != 0)
53 {
54 return rc;
55 }
56
57 msg->hdr.command = static_cast<uint8_t>(
58 DeviceCapabilityDiscoveryCommands::QUERY_DEVICE_IDENTIFICATION);
59 msg->hdr.data_size = 0;
60
61 return 0;
62 }
63
decodeQueryDeviceIdentificationResponse(const std::span<const uint8_t> buf,ocp::accelerator_management::CompletionCode & cc,uint16_t & reasonCode,uint8_t & deviceIdentification,uint8_t & deviceInstance)64 int decodeQueryDeviceIdentificationResponse(
65 const std::span<const uint8_t> buf,
66 ocp::accelerator_management::CompletionCode& cc, uint16_t& reasonCode,
67 uint8_t& deviceIdentification, uint8_t& deviceInstance)
68 {
69 auto rc =
70 ocp::accelerator_management::decodeReasonCodeAndCC(buf, cc, reasonCode);
71
72 if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
73 {
74 return rc;
75 }
76
77 if (buf.size() < sizeof(QueryDeviceIdentificationResponse))
78 {
79 return EINVAL;
80 }
81
82 const auto* response =
83 reinterpret_cast<const QueryDeviceIdentificationResponse*>(buf.data());
84
85 deviceIdentification = response->device_identification;
86 deviceInstance = response->instance_id;
87
88 return 0;
89 }
90
encodeGetTemperatureReadingRequest(uint8_t instanceId,uint8_t sensorId,std::span<uint8_t> buf)91 int encodeGetTemperatureReadingRequest(uint8_t instanceId, uint8_t sensorId,
92 std::span<uint8_t> buf)
93 {
94 if (buf.size() < sizeof(GetTemperatureReadingRequest))
95 {
96 return EINVAL;
97 }
98
99 auto* msg = reinterpret_cast<GetTemperatureReadingRequest*>(buf.data());
100
101 ocp::accelerator_management::BindingPciVidInfo header{};
102 header.ocp_accelerator_management_msg_type =
103 static_cast<uint8_t>(ocp::accelerator_management::MessageType::REQUEST);
104 header.instance_id = instanceId &
105 ocp::accelerator_management::instanceIdBitMask;
106 header.msg_type = static_cast<uint8_t>(MessageType::PLATFORM_ENVIRONMENTAL);
107
108 auto rc = packHeader(header, msg->hdr.msgHdr.hdr);
109
110 if (rc != 0)
111 {
112 return rc;
113 }
114
115 msg->hdr.command = static_cast<uint8_t>(
116 PlatformEnvironmentalCommands::GET_TEMPERATURE_READING);
117 msg->hdr.data_size = sizeof(sensorId);
118 msg->sensor_id = sensorId;
119
120 return 0;
121 }
122
decodeGetTemperatureReadingResponse(const std::span<const uint8_t> buf,ocp::accelerator_management::CompletionCode & cc,uint16_t & reasonCode,double & temperatureReading)123 int decodeGetTemperatureReadingResponse(
124 const std::span<const uint8_t> buf,
125 ocp::accelerator_management::CompletionCode& cc, uint16_t& reasonCode,
126 double& temperatureReading)
127 {
128 auto rc =
129 ocp::accelerator_management::decodeReasonCodeAndCC(buf, cc, reasonCode);
130
131 if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
132 {
133 return rc;
134 }
135
136 if (buf.size() < sizeof(GetTemperatureReadingResponse))
137 {
138 return EINVAL;
139 }
140
141 const auto* response =
142 reinterpret_cast<const GetTemperatureReadingResponse*>(buf.data());
143
144 uint16_t dataSize = le16toh(response->hdr.data_size);
145
146 if (dataSize != sizeof(int32_t))
147 {
148 return EINVAL;
149 }
150
151 int32_t reading = le32toh(response->reading);
152 temperatureReading = reading / static_cast<double>(1 << 8);
153
154 return 0;
155 }
156
encodeReadThermalParametersRequest(uint8_t instanceId,uint8_t sensorId,std::span<uint8_t> buf)157 int encodeReadThermalParametersRequest(uint8_t instanceId, uint8_t sensorId,
158 std::span<uint8_t> buf)
159 {
160 if (buf.size() < sizeof(ReadThermalParametersRequest))
161 {
162 return EINVAL;
163 }
164
165 auto* msg = reinterpret_cast<ReadThermalParametersRequest*>(buf.data());
166
167 ocp::accelerator_management::BindingPciVidInfo header{};
168 header.ocp_accelerator_management_msg_type =
169 static_cast<uint8_t>(ocp::accelerator_management::MessageType::REQUEST);
170 header.instance_id = instanceId &
171 ocp::accelerator_management::instanceIdBitMask;
172 header.msg_type = static_cast<uint8_t>(MessageType::PLATFORM_ENVIRONMENTAL);
173
174 auto rc = packHeader(header, msg->hdr.msgHdr.hdr);
175
176 if (rc != 0)
177 {
178 return rc;
179 }
180
181 msg->hdr.command = static_cast<uint8_t>(
182 PlatformEnvironmentalCommands::READ_THERMAL_PARAMETERS);
183 msg->hdr.data_size = sizeof(sensorId);
184 msg->sensor_id = sensorId;
185
186 return 0;
187 }
188
decodeReadThermalParametersResponse(std::span<const uint8_t> buf,ocp::accelerator_management::CompletionCode & cc,uint16_t & reasonCode,int32_t & threshold)189 int decodeReadThermalParametersResponse(
190 std::span<const uint8_t> buf,
191 ocp::accelerator_management::CompletionCode& cc, uint16_t& reasonCode,
192 int32_t& threshold)
193 {
194 auto rc =
195 ocp::accelerator_management::decodeReasonCodeAndCC(buf, cc, reasonCode);
196
197 if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
198 {
199 return rc;
200 }
201
202 if (buf.size() < sizeof(ReadThermalParametersResponse))
203 {
204 return EINVAL;
205 }
206
207 const auto* response =
208 reinterpret_cast<const ReadThermalParametersResponse*>(buf.data());
209
210 uint16_t dataSize = le16toh(response->hdr.data_size);
211
212 if (dataSize != sizeof(int32_t))
213 {
214 return EINVAL;
215 }
216
217 threshold = le32toh(response->threshold);
218
219 return 0;
220 }
221
encodeGetPowerDrawRequest(PlatformEnvironmentalCommands commandCode,uint8_t instanceId,uint8_t sensorId,uint8_t averagingInterval,std::span<uint8_t> buf)222 int encodeGetPowerDrawRequest(PlatformEnvironmentalCommands commandCode,
223 uint8_t instanceId, uint8_t sensorId,
224 uint8_t averagingInterval, std::span<uint8_t> buf)
225 {
226 if (buf.size() < sizeof(GetPowerDrawRequest))
227 {
228 return EINVAL;
229 }
230
231 auto* msg = reinterpret_cast<GetPowerDrawRequest*>(buf.data());
232
233 ocp::accelerator_management::BindingPciVidInfo header{};
234 header.ocp_accelerator_management_msg_type =
235 static_cast<uint8_t>(ocp::accelerator_management::MessageType::REQUEST);
236 header.instance_id = instanceId &
237 ocp::accelerator_management::instanceIdBitMask;
238 header.msg_type = static_cast<uint8_t>(MessageType::PLATFORM_ENVIRONMENTAL);
239
240 auto rc = packHeader(header, msg->hdr.msgHdr.hdr);
241
242 if (rc != 0)
243 {
244 return rc;
245 }
246
247 msg->hdr.command = static_cast<uint8_t>(commandCode);
248 msg->hdr.data_size = sizeof(sensorId) + sizeof(averagingInterval);
249 msg->sensorId = sensorId;
250 msg->averagingInterval = averagingInterval;
251
252 return 0;
253 }
254
decodeGetPowerDrawResponse(std::span<const uint8_t> buf,ocp::accelerator_management::CompletionCode & cc,uint16_t & reasonCode,uint32_t & power)255 int decodeGetPowerDrawResponse(std::span<const uint8_t> buf,
256 ocp::accelerator_management::CompletionCode& cc,
257 uint16_t& reasonCode, uint32_t& power)
258 {
259 auto rc =
260 ocp::accelerator_management::decodeReasonCodeAndCC(buf, cc, reasonCode);
261
262 if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
263 {
264 return rc;
265 }
266
267 if (buf.size() < sizeof(GetPowerDrawResponse))
268 {
269 return EINVAL;
270 }
271
272 const auto* response =
273 reinterpret_cast<const GetPowerDrawResponse*>(buf.data());
274
275 const uint16_t dataSize = le16toh(response->hdr.data_size);
276
277 if (dataSize != sizeof(uint32_t))
278 {
279 return EINVAL;
280 }
281
282 power = le32toh(response->power);
283
284 return 0;
285 }
286
encodeGetCurrentEnergyCounterRequest(uint8_t instanceId,uint8_t sensorId,std::span<uint8_t> buf)287 int encodeGetCurrentEnergyCounterRequest(uint8_t instanceId, uint8_t sensorId,
288 std::span<uint8_t> buf)
289 {
290 if (buf.size() < sizeof(GetTemperatureReadingRequest))
291 {
292 return EINVAL;
293 }
294
295 auto* msg = reinterpret_cast<GetCurrentEnergyCounterRequest*>(buf.data());
296
297 ocp::accelerator_management::BindingPciVidInfo header{};
298 header.ocp_accelerator_management_msg_type =
299 static_cast<uint8_t>(ocp::accelerator_management::MessageType::REQUEST);
300 header.instance_id = instanceId &
301 ocp::accelerator_management::instanceIdBitMask;
302 header.msg_type = static_cast<uint8_t>(MessageType::PLATFORM_ENVIRONMENTAL);
303
304 auto rc = packHeader(header, msg->hdr.msgHdr.hdr);
305
306 if (rc != 0)
307 {
308 return rc;
309 }
310
311 msg->hdr.command = static_cast<uint8_t>(
312 PlatformEnvironmentalCommands::GET_CURRENT_ENERGY_COUNTER);
313 msg->hdr.data_size = sizeof(sensorId);
314 msg->sensor_id = sensorId;
315
316 return 0;
317 }
318
decodeGetCurrentEnergyCounterResponse(std::span<const uint8_t> buf,ocp::accelerator_management::CompletionCode & cc,uint16_t & reasonCode,uint64_t & energy)319 int decodeGetCurrentEnergyCounterResponse(
320 std::span<const uint8_t> buf,
321 ocp::accelerator_management::CompletionCode& cc, uint16_t& reasonCode,
322 uint64_t& energy)
323 {
324 auto rc =
325 ocp::accelerator_management::decodeReasonCodeAndCC(buf, cc, reasonCode);
326
327 if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
328 {
329 return rc;
330 }
331
332 if (buf.size() < sizeof(GetPowerDrawResponse))
333 {
334 return EINVAL;
335 }
336
337 const auto* response =
338 reinterpret_cast<const GetCurrentEnergyCounterResponse*>(buf.data());
339
340 const uint16_t dataSize = le16toh(response->hdr.data_size);
341
342 if (dataSize != sizeof(uint64_t))
343 {
344 return EINVAL;
345 }
346
347 energy = le32toh(response->energy);
348
349 return 0;
350 }
351
encodeGetVoltageRequest(uint8_t instanceId,uint8_t sensorId,std::span<uint8_t> buf)352 int encodeGetVoltageRequest(uint8_t instanceId, uint8_t sensorId,
353 std::span<uint8_t> buf)
354 {
355 if (buf.size() < sizeof(GetVoltageRequest))
356 {
357 return EINVAL;
358 }
359
360 auto* msg = reinterpret_cast<GetVoltageRequest*>(buf.data());
361
362 ocp::accelerator_management::BindingPciVidInfo header{};
363 header.ocp_accelerator_management_msg_type =
364 static_cast<uint8_t>(ocp::accelerator_management::MessageType::REQUEST);
365 header.instance_id = instanceId &
366 ocp::accelerator_management::instanceIdBitMask;
367 header.msg_type = static_cast<uint8_t>(MessageType::PLATFORM_ENVIRONMENTAL);
368
369 auto rc = packHeader(header, msg->hdr.msgHdr.hdr);
370
371 if (rc != 0)
372 {
373 return rc;
374 }
375
376 msg->hdr.command =
377 static_cast<uint8_t>(PlatformEnvironmentalCommands::GET_VOLTAGE);
378 msg->hdr.data_size = sizeof(sensorId);
379 msg->sensor_id = sensorId;
380
381 return 0;
382 }
383
decodeGetVoltageResponse(std::span<const uint8_t> buf,ocp::accelerator_management::CompletionCode & cc,uint16_t & reasonCode,uint32_t & voltage)384 int decodeGetVoltageResponse(std::span<const uint8_t> buf,
385 ocp::accelerator_management::CompletionCode& cc,
386 uint16_t& reasonCode, uint32_t& voltage)
387 {
388 auto rc =
389 ocp::accelerator_management::decodeReasonCodeAndCC(buf, cc, reasonCode);
390
391 if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
392 {
393 return rc;
394 }
395
396 if (buf.size() < sizeof(GetVoltageResponse))
397 {
398 return EINVAL;
399 }
400
401 const auto* response =
402 reinterpret_cast<const GetVoltageResponse*>(buf.data());
403
404 const uint16_t dataSize = le16toh(response->hdr.data_size);
405
406 if (dataSize != sizeof(uint32_t))
407 {
408 return EINVAL;
409 }
410
411 voltage = le32toh(response->voltage);
412
413 return 0;
414 }
415
encodeGetInventoryInformationRequest(uint8_t instanceId,uint8_t propertyId,std::span<uint8_t> buf)416 int encodeGetInventoryInformationRequest(uint8_t instanceId, uint8_t propertyId,
417 std::span<uint8_t> buf)
418 {
419 if (buf.size() < sizeof(GetInventoryInformationRequest))
420 {
421 return EINVAL;
422 }
423
424 auto* msg = reinterpret_cast<GetInventoryInformationRequest*>(buf.data());
425
426 ocp::accelerator_management::BindingPciVidInfo header{};
427 header.ocp_accelerator_management_msg_type =
428 static_cast<uint8_t>(ocp::accelerator_management::MessageType::REQUEST);
429 header.instance_id = instanceId &
430 ocp::accelerator_management::instanceIdBitMask;
431 header.msg_type = static_cast<uint8_t>(MessageType::PLATFORM_ENVIRONMENTAL);
432
433 auto rc = packHeader(header, msg->hdr.msgHdr.hdr);
434
435 if (rc != 0)
436 {
437 return rc;
438 }
439
440 msg->hdr.command = static_cast<uint8_t>(
441 PlatformEnvironmentalCommands::GET_INVENTORY_INFORMATION);
442 msg->hdr.data_size = sizeof(propertyId);
443 msg->property_id = propertyId;
444
445 return 0;
446 }
447
decodeGetInventoryInformationResponse(std::span<const uint8_t> buf,ocp::accelerator_management::CompletionCode & cc,uint16_t & reasonCode,InventoryPropertyId propertyId,InventoryValue & value)448 int decodeGetInventoryInformationResponse(
449 std::span<const uint8_t> buf,
450 ocp::accelerator_management::CompletionCode& cc, uint16_t& reasonCode,
451 InventoryPropertyId propertyId, InventoryValue& value)
452 {
453 auto rc =
454 ocp::accelerator_management::decodeReasonCodeAndCC(buf, cc, reasonCode);
455 if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
456 {
457 return rc;
458 }
459 // Expect at least one byte of inventory response data after common response
460 if (buf.size() < (sizeof(ocp::accelerator_management::CommonResponse) + 1))
461 {
462 return EINVAL;
463 }
464
465 const auto* response =
466 reinterpret_cast<const GetInventoryInformationResponse*>(buf.data());
467 uint16_t dataSize = le16toh(response->hdr.data_size);
468
469 if (dataSize == 0 || dataSize > maxInventoryDataSize)
470 {
471 return EINVAL;
472 }
473
474 const uint8_t* dataPtr = response->data.data();
475
476 switch (propertyId)
477 {
478 case InventoryPropertyId::BOARD_PART_NUMBER:
479 case InventoryPropertyId::SERIAL_NUMBER:
480 case InventoryPropertyId::MARKETING_NAME:
481 case InventoryPropertyId::DEVICE_PART_NUMBER:
482 value =
483 std::string(reinterpret_cast<const char*>(dataPtr), dataSize);
484 break;
485 case InventoryPropertyId::DEVICE_GUID:
486 value = std::vector<uint8_t>(dataPtr, dataPtr + dataSize);
487 break;
488 default:
489 return EINVAL;
490 }
491 return 0;
492 }
493
encodeQueryScalarGroupTelemetryV2Request(uint8_t instanceId,PciePortType portType,uint8_t upstreamPortNumber,uint8_t portNumber,uint8_t groupId,std::span<uint8_t> buf)494 int encodeQueryScalarGroupTelemetryV2Request(
495 uint8_t instanceId, PciePortType portType, uint8_t upstreamPortNumber,
496 uint8_t portNumber, uint8_t groupId, std::span<uint8_t> buf)
497 {
498 if (buf.size() < sizeof(QueryScalarGroupTelemetryV2Request))
499 {
500 return EINVAL;
501 }
502
503 auto* msg =
504 reinterpret_cast<QueryScalarGroupTelemetryV2Request*>(buf.data());
505
506 ocp::accelerator_management::BindingPciVidInfo header{};
507 header.ocp_accelerator_management_msg_type =
508 static_cast<uint8_t>(ocp::accelerator_management::MessageType::REQUEST);
509 header.instance_id = instanceId &
510 ocp::accelerator_management::instanceIdBitMask;
511 header.msg_type = static_cast<uint8_t>(MessageType::PCIE_LINK);
512
513 auto rc = packHeader(header, msg->hdr.msgHdr.hdr);
514
515 if (rc != 0)
516 {
517 return rc;
518 }
519
520 msg->hdr.command =
521 static_cast<uint8_t>(PcieLinkCommands::QueryScalarGroupTelemetryV2);
522 msg->hdr.data_size = 3;
523 msg->upstreamPortNumber =
524 (static_cast<uint8_t>(portType) << 7) | (upstreamPortNumber & 0x7F);
525 msg->portNumber = portNumber;
526 msg->groupId = groupId;
527
528 return 0;
529 }
530
decodeQueryScalarGroupTelemetryV2Response(std::span<const uint8_t> buf,ocp::accelerator_management::CompletionCode & cc,uint16_t & reasonCode,size_t & numTelemetryValues,std::vector<uint32_t> & telemetryValues)531 int decodeQueryScalarGroupTelemetryV2Response(
532 std::span<const uint8_t> buf,
533 ocp::accelerator_management::CompletionCode& cc, uint16_t& reasonCode,
534 size_t& numTelemetryValues, std::vector<uint32_t>& telemetryValues)
535 {
536 auto rc =
537 ocp::accelerator_management::decodeReasonCodeAndCC(buf, cc, reasonCode);
538
539 if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
540 {
541 return rc;
542 }
543
544 if (buf.size() < sizeof(ocp::accelerator_management::CommonResponse))
545 {
546 return EINVAL;
547 }
548
549 const auto* response =
550 reinterpret_cast<const ocp::accelerator_management::CommonResponse*>(
551 buf.data());
552
553 const uint16_t dataSize = le16toh(response->data_size);
554
555 if (buf.size() <
556 dataSize + sizeof(ocp::accelerator_management::CommonResponse))
557 {
558 return EINVAL;
559 }
560
561 numTelemetryValues = dataSize / sizeof(uint32_t);
562
563 if (telemetryValues.size() < numTelemetryValues)
564 {
565 telemetryValues.resize(numTelemetryValues);
566 }
567
568 const auto* telemetryDataPtr =
569 buf.data() + sizeof(ocp::accelerator_management::CommonResponse);
570
571 for (size_t i = 0; i < numTelemetryValues; i++)
572 {
573 std::memcpy(&telemetryValues[i],
574 telemetryDataPtr + i * sizeof(uint32_t), sizeof(uint32_t));
575
576 telemetryValues[i] = le32toh(telemetryValues[i]);
577 }
578
579 return 0;
580 }
581
encodeListPciePortsRequest(uint8_t instanceId,std::span<uint8_t> buf)582 int encodeListPciePortsRequest(uint8_t instanceId, std::span<uint8_t> buf)
583 {
584 if (buf.size() < sizeof(ocp::accelerator_management::CommonRequest))
585 {
586 return EINVAL;
587 }
588
589 auto* msg = reinterpret_cast<ocp::accelerator_management::CommonRequest*>(
590 buf.data());
591
592 ocp::accelerator_management::BindingPciVidInfo header{};
593 header.ocp_accelerator_management_msg_type =
594 static_cast<uint8_t>(ocp::accelerator_management::MessageType::REQUEST);
595 header.instance_id = instanceId &
596 ocp::accelerator_management::instanceIdBitMask;
597 header.msg_type = static_cast<uint8_t>(MessageType::PCIE_LINK);
598
599 auto rc = packHeader(header, msg->msgHdr.hdr);
600
601 if (rc != 0)
602 {
603 return rc;
604 }
605
606 msg->command = static_cast<uint8_t>(PcieLinkCommands::ListPCIePorts);
607 msg->data_size = 0;
608
609 return 0;
610 }
611
decodeListPciePortsResponse(std::span<const uint8_t> buf,ocp::accelerator_management::CompletionCode & cc,uint16_t & reasonCode,uint16_t & numUpstreamPorts,std::vector<uint8_t> & numDownstreamPorts)612 int decodeListPciePortsResponse(
613 std::span<const uint8_t> buf,
614 ocp::accelerator_management::CompletionCode& cc, uint16_t& reasonCode,
615 uint16_t& numUpstreamPorts, std::vector<uint8_t>& numDownstreamPorts)
616 {
617 auto rc =
618 ocp::accelerator_management::decodeReasonCodeAndCC(buf, cc, reasonCode);
619
620 if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
621 {
622 return rc;
623 }
624
625 if (buf.size() < sizeof(ListPCIePortsResponse))
626 {
627 return EINVAL;
628 }
629
630 const auto* response =
631 reinterpret_cast<const ListPCIePortsResponse*>(buf.data());
632
633 const uint16_t dataSize = le16toh(response->hdr.data_size);
634
635 if (dataSize < sizeof(uint16_t))
636 {
637 return EINVAL;
638 }
639
640 uint16_t upstreamPorts = le16toh(response->numUpstreamPorts);
641
642 numUpstreamPorts = 0;
643 numDownstreamPorts.clear();
644 numDownstreamPorts.reserve(upstreamPorts);
645
646 size_t offset = sizeof(ListPCIePortsResponse);
647
648 for (size_t i = 0; i < upstreamPorts; i++)
649 {
650 if (offset + sizeof(ListPCIePortsDownstreamPortsData) > buf.size())
651 {
652 return EINVAL;
653 }
654
655 const auto* downstreamPortData =
656 reinterpret_cast<const ListPCIePortsDownstreamPortsData*>(
657 buf.data() + offset);
658
659 // Count only external upstream ports
660 if (downstreamPortData->isInternal == 0)
661 {
662 ++numUpstreamPorts;
663 numDownstreamPorts.push_back(downstreamPortData->count);
664 }
665
666 offset += sizeof(ListPCIePortsDownstreamPortsData);
667 }
668
669 return 0;
670 }
671 // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
672 } // namespace gpu
673