1 #include "Inventory.hpp"
2
3 #include "Utils.hpp"
4
5 #include <MctpRequester.hpp>
6 #include <NvidiaGpuMctpVdm.hpp>
7 #include <OcpMctpVdm.hpp>
8 #include <boost/asio/io_context.hpp>
9 #include <boost/uuid/uuid.hpp>
10 #include <boost/uuid/uuid_io.hpp>
11 #include <phosphor-logging/lg2.hpp>
12 #include <sdbusplus/asio/connection.hpp>
13 #include <sdbusplus/asio/object_server.hpp>
14
15 #include <algorithm>
16 #include <cstdint>
17 #include <memory>
18 #include <optional>
19 #include <span>
20 #include <string>
21 #include <system_error>
22 #include <unordered_map>
23 #include <variant>
24 #include <vector>
25
26 constexpr const char* inventoryPrefix = "/xyz/openbmc_project/inventory/";
27 constexpr const char* acceleratorIfaceName =
28 "xyz.openbmc_project.Inventory.Item.Accelerator";
29 static constexpr const char* assetIfaceName =
30 "xyz.openbmc_project.Inventory.Decorator.Asset";
31 static constexpr const char* uuidIfaceName = "xyz.openbmc_project.Common.UUID";
32 static constexpr const char* revisionIfaceName =
33 "xyz.openbmc_project.Inventory.Decorator.Revision";
34
Inventory(const std::shared_ptr<sdbusplus::asio::connection> &,sdbusplus::asio::object_server & objectServer,const std::string & inventoryName,mctp::MctpRequester & mctpRequester,const gpu::DeviceIdentification deviceTypeIn,const uint8_t eid,boost::asio::io_context & io)35 Inventory::Inventory(
36 const std::shared_ptr<sdbusplus::asio::connection>& /*conn*/,
37 sdbusplus::asio::object_server& objectServer,
38 const std::string& inventoryName, mctp::MctpRequester& mctpRequester,
39 const gpu::DeviceIdentification deviceTypeIn, const uint8_t eid,
40 boost::asio::io_context& io) :
41 name(escapeName(inventoryName)), mctpRequester(mctpRequester),
42 deviceType(deviceTypeIn), eid(eid), retryTimer(io)
43 {
44 std::string path = inventoryPrefix + name;
45
46 assetIface = objectServer.add_interface(path, assetIfaceName);
47 assetIface->register_property("Manufacturer", std::string("NVIDIA"));
48 // Register properties which need to be fetched from the device
49 registerProperty(gpu::InventoryPropertyId::SERIAL_NUMBER, assetIface,
50 "SerialNumber");
51 registerProperty(gpu::InventoryPropertyId::BOARD_PART_NUMBER, assetIface,
52 "PartNumber");
53 registerProperty(gpu::InventoryPropertyId::MARKETING_NAME, assetIface,
54 "Model");
55 assetIface->initialize();
56
57 uuidInterface = objectServer.add_interface(path, uuidIfaceName);
58 registerProperty(gpu::InventoryPropertyId::DEVICE_GUID, uuidInterface,
59 "UUID");
60 uuidInterface->initialize();
61
62 revisionIface = objectServer.add_interface(path, revisionIfaceName);
63 registerProperty(gpu::InventoryPropertyId::DEVICE_PART_NUMBER,
64 revisionIface, "Version");
65 revisionIface->initialize();
66
67 // Static properties
68 if (deviceType == gpu::DeviceIdentification::DEVICE_GPU)
69 {
70 acceleratorInterface =
71 objectServer.add_interface(path, acceleratorIfaceName);
72 acceleratorInterface->register_property("Type", std::string("GPU"));
73 acceleratorInterface->initialize();
74 }
75 }
76
init()77 void Inventory::init()
78 {
79 processNextProperty();
80 }
81
registerProperty(gpu::InventoryPropertyId propertyId,const std::shared_ptr<sdbusplus::asio::dbus_interface> & interface,const std::string & propertyName)82 void Inventory::registerProperty(
83 gpu::InventoryPropertyId propertyId,
84 const std::shared_ptr<sdbusplus::asio::dbus_interface>& interface,
85 const std::string& propertyName)
86 {
87 if (interface)
88 {
89 interface->register_property(propertyName, std::string{});
90 properties[propertyId] = {interface, propertyName, 0, true};
91 }
92 }
93
processInventoryProperty(gpu::InventoryPropertyId propertyId)94 void Inventory::processInventoryProperty(gpu::InventoryPropertyId propertyId)
95 {
96 auto it = properties.find(propertyId);
97 if (it != properties.end())
98 {
99 markPropertyPending(it);
100 std::optional<gpu::InventoryPropertyId> nextProperty =
101 getNextPendingProperty();
102 if (nextProperty && *nextProperty == propertyId)
103 {
104 processNextProperty();
105 }
106 }
107 }
108
markPropertyPending(std::unordered_map<gpu::InventoryPropertyId,PropertyInfo>::iterator it)109 void Inventory::markPropertyPending(
110 std::unordered_map<gpu::InventoryPropertyId, PropertyInfo>::iterator it)
111 {
112 it->second.isPending = true;
113 it->second.retryCount = 0;
114 }
115
markPropertyProcessed(std::unordered_map<gpu::InventoryPropertyId,PropertyInfo>::iterator it)116 void Inventory::markPropertyProcessed(
117 std::unordered_map<gpu::InventoryPropertyId, PropertyInfo>::iterator it)
118 {
119 it->second.isPending = false;
120 }
121
getNextPendingProperty() const122 std::optional<gpu::InventoryPropertyId> Inventory::getNextPendingProperty()
123 const
124 {
125 for (const auto& [propertyId, info] : properties)
126 {
127 if (info.isPending)
128 {
129 return propertyId;
130 }
131 }
132 return std::nullopt;
133 }
134
sendInventoryPropertyRequest(gpu::InventoryPropertyId propertyId)135 void Inventory::sendInventoryPropertyRequest(
136 gpu::InventoryPropertyId propertyId)
137 {
138 int rc = gpu::encodeGetInventoryInformationRequest(
139 0, static_cast<uint8_t>(propertyId), requestBuffer);
140 if (rc != 0)
141 {
142 lg2::error(
143 "Failed to encode property ID {PROP_ID} request for {NAME}: rc={RC}",
144 "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", name, "RC",
145 rc);
146 return;
147 }
148
149 lg2::info(
150 "Sending inventory request for property ID {PROP_ID} to EID {EID} for {NAME}",
151 "PROP_ID", static_cast<uint8_t>(propertyId), "EID", eid, "NAME", name);
152
153 mctpRequester.sendRecvMsg(
154 eid, requestBuffer,
155 [weak{weak_from_this()}, propertyId](const std::error_code& ec,
156 std::span<const uint8_t> buffer) {
157 std::shared_ptr<Inventory> self = weak.lock();
158 if (!self)
159 {
160 lg2::error("Invalid Inventory reference");
161 return;
162 }
163 self->handleInventoryPropertyResponse(propertyId, ec, buffer);
164 });
165 }
166
handleInventoryPropertyResponse(gpu::InventoryPropertyId propertyId,const std::error_code & ec,std::span<const uint8_t> buffer)167 void Inventory::handleInventoryPropertyResponse(
168 gpu::InventoryPropertyId propertyId, const std::error_code& ec,
169 std::span<const uint8_t> buffer)
170 {
171 auto it = properties.find(propertyId);
172 if (it == properties.end())
173 {
174 lg2::error("Property ID {PROP_ID} for {NAME} not found", "PROP_ID",
175 static_cast<uint8_t>(propertyId), "NAME", name);
176 processNextProperty();
177 return;
178 }
179
180 bool success = false;
181 if (!ec)
182 {
183 ocp::accelerator_management::CompletionCode cc{};
184 uint16_t reasonCode = 0;
185 gpu::InventoryValue info;
186 int rc = gpu::decodeGetInventoryInformationResponse(
187 buffer, cc, reasonCode, propertyId, info);
188
189 lg2::info(
190 "Response for property ID {PROP_ID} from {NAME}, sendRecvMsgResult: {RESULT}, decode_rc: {RC}, completion_code: {CC}, reason_code: {REASON}",
191 "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", name, "RESULT",
192 ec.message(), "RC", rc, "CC", static_cast<uint8_t>(cc), "REASON",
193 reasonCode);
194
195 if (rc == 0 &&
196 cc == ocp::accelerator_management::CompletionCode::SUCCESS)
197 {
198 std::string value;
199
200 // Handle different property types based on property ID
201 switch (propertyId)
202 {
203 case gpu::InventoryPropertyId::BOARD_PART_NUMBER:
204 case gpu::InventoryPropertyId::SERIAL_NUMBER:
205 case gpu::InventoryPropertyId::MARKETING_NAME:
206 case gpu::InventoryPropertyId::DEVICE_PART_NUMBER:
207 if (std::holds_alternative<std::string>(info))
208 {
209 value = std::get<std::string>(info);
210 }
211 else
212 {
213 lg2::error(
214 "Property ID {PROP_ID} for {NAME} expected string but got different type",
215 "PROP_ID", static_cast<uint8_t>(propertyId), "NAME",
216 name);
217 break;
218 }
219 break;
220
221 case gpu::InventoryPropertyId::DEVICE_GUID:
222 if (std::holds_alternative<std::vector<uint8_t>>(info))
223 {
224 const auto& guidBytes =
225 std::get<std::vector<uint8_t>>(info);
226 if (guidBytes.size() >= 16)
227 {
228 boost::uuids::uuid uuid;
229 std::copy(guidBytes.begin(), guidBytes.begin() + 16,
230 uuid.begin());
231 value = boost::uuids::to_string(uuid);
232 }
233 else
234 {
235 lg2::error(
236 "Property ID {PROP_ID} for {NAME} GUID size {SIZE} is less than 16 bytes",
237 "PROP_ID", static_cast<uint8_t>(propertyId),
238 "NAME", name, "SIZE", guidBytes.size());
239 break;
240 }
241 }
242 else
243 {
244 lg2::error(
245 "Property ID {PROP_ID} for {NAME} expected vector<uint8_t> but got different type",
246 "PROP_ID", static_cast<uint8_t>(propertyId), "NAME",
247 name);
248 break;
249 }
250 break;
251
252 default:
253 lg2::error("Unsupported property ID {PROP_ID} for {NAME}",
254 "PROP_ID", static_cast<uint8_t>(propertyId),
255 "NAME", name);
256 break;
257 }
258
259 if (!value.empty())
260 {
261 it->second.interface->set_property(it->second.propertyName,
262 value);
263 lg2::info(
264 "Successfully received property ID {PROP_ID} for {NAME} with value: {VALUE}",
265 "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", name,
266 "VALUE", value);
267 success = true;
268 }
269 }
270 }
271
272 if (!success)
273 {
274 it->second.retryCount++;
275 if (it->second.retryCount >= maxRetryAttempts)
276 {
277 lg2::error(
278 "Property ID {PROP_ID} for {NAME} failed after {ATTEMPTS} attempts",
279 "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", name,
280 "ATTEMPTS", maxRetryAttempts);
281 markPropertyProcessed(it);
282 }
283 else
284 {
285 retryTimer.expires_after(retryDelay);
286 retryTimer.async_wait(
287 [weak{weak_from_this()}](const boost::system::error_code& ec) {
288 std::shared_ptr<Inventory> self = weak.lock();
289 if (!self)
290 {
291 lg2::error("Invalid reference to Inventory");
292 return;
293 }
294 if (ec)
295 {
296 lg2::error("Retry timer error for {NAME}: {ERROR}",
297 "NAME", self->name, "ERROR", ec.message());
298 return;
299 }
300 self->processNextProperty();
301 });
302 return;
303 }
304 }
305 else
306 {
307 markPropertyProcessed(it);
308 }
309
310 processNextProperty();
311 }
312
processNextProperty()313 void Inventory::processNextProperty()
314 {
315 std::optional<gpu::InventoryPropertyId> nextProperty =
316 getNextPendingProperty();
317 if (nextProperty)
318 {
319 sendInventoryPropertyRequest(*nextProperty);
320 }
321 else
322 {
323 lg2::info("No pending properties found to process for {NAME}", "NAME",
324 name);
325 }
326 }
327