1 /*
2 * SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include "NvidiaPcieInterface.hpp"
7
8 #include "Utils.hpp"
9
10 #include <bits/basic_string.h>
11
12 #include <MctpRequester.hpp>
13 #include <NvidiaGpuMctpVdm.hpp>
14 #include <NvidiaPcieDevice.hpp>
15 #include <OcpMctpVdm.hpp>
16 #include <phosphor-logging/lg2.hpp>
17 #include <sdbusplus/asio/connection.hpp>
18 #include <sdbusplus/asio/object_server.hpp>
19
20 #include <cmath>
21 #include <cstddef>
22 #include <cstdint>
23 #include <functional>
24 #include <limits>
25 #include <memory>
26 #include <span>
27 #include <string>
28 #include <system_error>
29 #include <vector>
30
31 using std::string;
32
33 using namespace std::literals;
34
NvidiaPcieInterface(std::shared_ptr<sdbusplus::asio::connection> & conn,mctp::MctpRequester & mctpRequester,const std::string & name,const std::string & path,uint8_t eid,sdbusplus::asio::object_server & objectServer)35 NvidiaPcieInterface::NvidiaPcieInterface(
36 std::shared_ptr<sdbusplus::asio::connection>& conn,
37 mctp::MctpRequester& mctpRequester, const std::string& name,
38 const std::string& path, uint8_t eid,
39 sdbusplus::asio::object_server& objectServer) :
40 eid(eid), path(path), conn(conn), mctpRequester(mctpRequester)
41 {
42 const std::string dbusPath = pcieDevicePathPrefix + escapeName(name);
43
44 pcieDeviceInterface = objectServer.add_interface(
45 dbusPath, "xyz.openbmc_project.Inventory.Item.PCIeDevice");
46
47 switchInterface = objectServer.add_interface(
48 dbusPath, "xyz.openbmc_project.Inventory.Item.PCIeSwitch");
49
50 pcieDeviceInterface->register_property(
51 "GenerationInUse",
52 std::string(
53 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown"));
54
55 pcieDeviceInterface->register_property("LanesInUse",
56 std::numeric_limits<size_t>::max());
57
58 pcieDeviceInterface->register_property(
59 "GenerationSupported",
60 std::string(
61 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown"));
62
63 pcieDeviceInterface->register_property("MaxLanes", static_cast<size_t>(0));
64
65 if (!pcieDeviceInterface->initialize())
66 {
67 lg2::error("Error initializing PCIe Device Interface for EID={EID}",
68 "EID", eid);
69 }
70
71 if (!switchInterface->initialize())
72 {
73 lg2::error("Error initializing Switch Interface for EID={EID}", "EID",
74 eid);
75 }
76 }
77
mapPcieGeneration(uint32_t value)78 string NvidiaPcieInterface::mapPcieGeneration(uint32_t value)
79 {
80 switch (value)
81 {
82 case 1:
83 return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen1";
84 case 2:
85 return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen2";
86 case 3:
87 return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen3";
88 case 4:
89 return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen4";
90 case 5:
91 return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen5";
92 case 6:
93 return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen6";
94 default:
95 return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown";
96 }
97 }
98
decodeLinkWidth(uint32_t value)99 size_t NvidiaPcieInterface::decodeLinkWidth(uint32_t value)
100 {
101 return (value > 0) ? pow(2, value - 1) : 0;
102 }
103
processResponse(const std::error_code & ec,std::span<const uint8_t> response)104 void NvidiaPcieInterface::processResponse(const std::error_code& ec,
105 std::span<const uint8_t> response)
106 {
107 if (ec)
108 {
109 lg2::error(
110 "Error updating PCIe Interface: sending message over MCTP failed, "
111 "rc={RC}, EID={EID}",
112 "RC", ec.value(), "EID", eid);
113 return;
114 }
115
116 ocp::accelerator_management::CompletionCode cc{};
117 uint16_t reasonCode = 0;
118 size_t numTelemetryValue = 0;
119
120 auto rc = gpu::decodeQueryScalarGroupTelemetryV2Response(
121 response, cc, reasonCode, numTelemetryValue, telemetryValues);
122
123 if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
124 {
125 lg2::error("Error updating PCIe Interface: decode failed, "
126 "rc={RC}, cc={CC}, reasonCode={RESC}, EID={EID}",
127 "RC", rc, "CC", static_cast<uint8_t>(cc), "RESC", reasonCode,
128 "EID", eid);
129 return;
130 }
131
132 if (!telemetryValues.empty())
133 {
134 pcieDeviceInterface->set_property(
135 "GenerationInUse", mapPcieGeneration(telemetryValues[0]));
136 }
137
138 if (telemetryValues.size() > 1)
139 {
140 pcieDeviceInterface->set_property(
141 "LanesInUse",
142 decodeLinkWidth(static_cast<size_t>(telemetryValues[1])));
143 }
144
145 if (telemetryValues.size() > 3)
146 {
147 pcieDeviceInterface->set_property(
148 "GenerationSupported", mapPcieGeneration(telemetryValues[3]));
149 }
150
151 if (telemetryValues.size() > 4)
152 {
153 pcieDeviceInterface->set_property(
154 "MaxLanes",
155 decodeLinkWidth(static_cast<size_t>(telemetryValues[4])));
156 }
157 }
158
update()159 void NvidiaPcieInterface::update()
160 {
161 auto rc =
162 gpu::encodeQueryScalarGroupTelemetryV2Request(0, {}, 0, 0, 1, request);
163
164 if (rc != 0)
165 {
166 lg2::error("Error updating PCIe Interface: failed, rc={RC}, EID={EID}",
167 "RC", rc, "EID", eid);
168 return;
169 }
170
171 mctpRequester.sendRecvMsg(
172 eid, request,
173 [weak{weak_from_this()}](const std::error_code& ec,
174 std::span<const uint8_t> buffer) {
175 std::shared_ptr<NvidiaPcieInterface> self = weak.lock();
176 if (!self)
177 {
178 lg2::error(
179 "Invalid reference to NvidiaPcieInterface for EID {EID}",
180 "EID", self->eid);
181 return;
182 }
183 self->processResponse(ec, buffer);
184 });
185 }
186