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