xref: /openbmc/bmcweb/redfish-core/lib/pcie.hpp (revision ae9031f0)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #pragma once
18 
19 #include <app.hpp>
20 #include <boost/system/linux_error.hpp>
21 #include <dbus_utility.hpp>
22 #include <query.hpp>
23 #include <registries/privilege_registry.hpp>
24 #include <sdbusplus/asio/property.hpp>
25 #include <sdbusplus/unpack_properties.hpp>
26 #include <utils/dbus_utils.hpp>
27 
28 namespace redfish
29 {
30 
31 static constexpr char const* pcieService = "xyz.openbmc_project.PCIe";
32 static constexpr char const* pciePath = "/xyz/openbmc_project/PCIe";
33 static constexpr char const* pcieDeviceInterface =
34     "xyz.openbmc_project.PCIe.Device";
35 
36 static inline void
37     getPCIeDeviceList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
38                       const std::string& name)
39 {
40     auto getPCIeMapCallback =
41         [asyncResp, name](const boost::system::error_code ec,
42                           const dbus::utility::MapperGetSubTreePathsResponse&
43                               pcieDevicePaths) {
44         if (ec)
45         {
46             BMCWEB_LOG_DEBUG << "no PCIe device paths found ec: "
47                              << ec.message();
48             // Not an error, system just doesn't have PCIe info
49             return;
50         }
51         nlohmann::json& pcieDeviceList = asyncResp->res.jsonValue[name];
52         pcieDeviceList = nlohmann::json::array();
53         for (const std::string& pcieDevicePath : pcieDevicePaths)
54         {
55             size_t devStart = pcieDevicePath.rfind('/');
56             if (devStart == std::string::npos)
57             {
58                 continue;
59             }
60 
61             std::string devName = pcieDevicePath.substr(devStart + 1);
62             if (devName.empty())
63             {
64                 continue;
65             }
66             nlohmann::json::object_t pcieDevice;
67             pcieDevice["@odata.id"] =
68                 "/redfish/v1/Systems/system/PCIeDevices/" + devName;
69             pcieDeviceList.push_back(std::move(pcieDevice));
70         }
71         asyncResp->res.jsonValue[name + "@odata.count"] = pcieDeviceList.size();
72     };
73     crow::connections::systemBus->async_method_call(
74         std::move(getPCIeMapCallback), "xyz.openbmc_project.ObjectMapper",
75         "/xyz/openbmc_project/object_mapper",
76         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
77         std::string(pciePath) + "/", 1, std::array<std::string, 0>());
78 }
79 
80 inline void requestRoutesSystemPCIeDeviceCollection(App& app)
81 {
82     /**
83      * Functions triggers appropriate requests on DBus
84      */
85     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/")
86         .privileges(redfish::privileges::getPCIeDeviceCollection)
87         .methods(boost::beast::http::verb::get)(
88             [&app](const crow::Request& req,
89                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
90                    const std::string& systemName) {
91         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
92         {
93             return;
94         }
95         if (systemName != "system")
96         {
97             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
98                                        systemName);
99             return;
100         }
101 
102         asyncResp->res.jsonValue["@odata.type"] =
103             "#PCIeDeviceCollection.PCIeDeviceCollection";
104         asyncResp->res.jsonValue["@odata.id"] =
105             "/redfish/v1/Systems/system/PCIeDevices";
106         asyncResp->res.jsonValue["Name"] = "PCIe Device Collection";
107         asyncResp->res.jsonValue["Description"] = "Collection of PCIe Devices";
108         asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
109         asyncResp->res.jsonValue["Members@odata.count"] = 0;
110         getPCIeDeviceList(asyncResp, "Members");
111         });
112 }
113 
114 inline std::optional<std::string>
115     redfishPcieGenerationFromDbus(const std::string& generationInUse)
116 {
117     if (generationInUse ==
118         "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen1")
119     {
120         return "Gen1";
121     }
122     if (generationInUse ==
123         "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen2")
124     {
125         return "Gen2";
126     }
127     if (generationInUse ==
128         "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen3")
129     {
130         return "Gen3";
131     }
132     if (generationInUse ==
133         "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen4")
134     {
135         return "Gen4";
136     }
137     if (generationInUse ==
138         "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen5")
139     {
140         return "Gen5";
141     }
142     if (generationInUse.empty() ||
143         generationInUse ==
144             "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown")
145     {
146         return "";
147     }
148 
149     // The value is not unknown or Gen1-5, need return an internal error.
150     return std::nullopt;
151 }
152 
153 inline void requestRoutesSystemPCIeDevice(App& app)
154 {
155     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/")
156         .privileges(redfish::privileges::getPCIeDevice)
157         .methods(boost::beast::http::verb::get)(
158             [&app](const crow::Request& req,
159                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
160                    const std::string& systemName, const std::string& device) {
161         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
162         {
163             return;
164         }
165         if (systemName != "system")
166         {
167             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
168                                        systemName);
169             return;
170         }
171 
172         auto getPCIeDeviceCallback =
173             [asyncResp, device](
174                 const boost::system::error_code ec,
175                 const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
176             if (ec)
177             {
178                 BMCWEB_LOG_DEBUG
179                     << "failed to get PCIe Device properties ec: " << ec.value()
180                     << ": " << ec.message();
181                 if (ec.value() ==
182                     boost::system::linux_error::bad_request_descriptor)
183                 {
184                     messages::resourceNotFound(asyncResp->res, "PCIeDevice",
185                                                device);
186                 }
187                 else
188                 {
189                     messages::internalError(asyncResp->res);
190                 }
191                 return;
192             }
193 
194             const std::string* manufacturer = nullptr;
195             const std::string* deviceType = nullptr;
196             const std::string* generationInUse = nullptr;
197 
198             const bool success = sdbusplus::unpackPropertiesNoThrow(
199                 dbus_utils::UnpackErrorPrinter(), pcieDevProperties,
200                 "Manufacturer", manufacturer, "DeviceType", deviceType,
201                 "GenerationInUse", generationInUse);
202 
203             if (!success)
204             {
205                 messages::internalError(asyncResp->res);
206                 return;
207             }
208 
209             if (generationInUse != nullptr)
210             {
211                 std::optional<std::string> redfishGenerationInUse =
212                     redfishPcieGenerationFromDbus(*generationInUse);
213                 if (!redfishGenerationInUse)
214                 {
215                     messages::internalError(asyncResp->res);
216                     return;
217                 }
218                 if (!redfishGenerationInUse->empty())
219                 {
220                     asyncResp->res.jsonValue["PCIeInterface"]["PCIeType"] =
221                         *redfishGenerationInUse;
222                 }
223             }
224 
225             if (manufacturer != nullptr)
226             {
227                 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
228             }
229 
230             if (deviceType != nullptr)
231             {
232                 asyncResp->res.jsonValue["DeviceType"] = *deviceType;
233             }
234 
235             asyncResp->res.jsonValue["@odata.type"] =
236                 "#PCIeDevice.v1_4_0.PCIeDevice";
237             asyncResp->res.jsonValue["@odata.id"] =
238                 "/redfish/v1/Systems/system/PCIeDevices/" + device;
239             asyncResp->res.jsonValue["Name"] = "PCIe Device";
240             asyncResp->res.jsonValue["Id"] = device;
241 
242             asyncResp->res.jsonValue["PCIeFunctions"]["@odata.id"] =
243                 "/redfish/v1/Systems/system/PCIeDevices/" + device +
244                 "/PCIeFunctions";
245         };
246         std::string escapedPath = std::string(pciePath) + "/" + device;
247         dbus::utility::escapePathForDbus(escapedPath);
248         sdbusplus::asio::getAllProperties(
249             *crow::connections::systemBus, pcieService, escapedPath,
250             pcieDeviceInterface, std::move(getPCIeDeviceCallback));
251         });
252 }
253 
254 inline void requestRoutesSystemPCIeFunctionCollection(App& app)
255 {
256     /**
257      * Functions triggers appropriate requests on DBus
258      */
259     BMCWEB_ROUTE(app,
260                  "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/")
261         .privileges(redfish::privileges::getPCIeFunctionCollection)
262         .methods(boost::beast::http::verb::get)(
263             [&app](const crow::Request& req,
264                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
265                    const std::string& device) {
266         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
267         {
268             return;
269         }
270 
271         asyncResp->res.jsonValue["@odata.type"] =
272             "#PCIeFunctionCollection.PCIeFunctionCollection";
273         asyncResp->res.jsonValue["@odata.id"] =
274             "/redfish/v1/Systems/system/PCIeDevices/" + device +
275             "/PCIeFunctions";
276         asyncResp->res.jsonValue["Name"] = "PCIe Function Collection";
277         asyncResp->res.jsonValue["Description"] =
278             "Collection of PCIe Functions for PCIe Device " + device;
279 
280         auto getPCIeDeviceCallback =
281             [asyncResp, device](
282                 const boost::system::error_code ec,
283                 const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
284             if (ec)
285             {
286                 BMCWEB_LOG_DEBUG
287                     << "failed to get PCIe Device properties ec: " << ec.value()
288                     << ": " << ec.message();
289                 if (ec.value() ==
290                     boost::system::linux_error::bad_request_descriptor)
291                 {
292                     messages::resourceNotFound(asyncResp->res, "PCIeDevice",
293                                                device);
294                 }
295                 else
296                 {
297                     messages::internalError(asyncResp->res);
298                 }
299                 return;
300             }
301 
302             nlohmann::json& pcieFunctionList =
303                 asyncResp->res.jsonValue["Members"];
304             pcieFunctionList = nlohmann::json::array();
305             static constexpr const int maxPciFunctionNum = 8;
306             for (int functionNum = 0; functionNum < maxPciFunctionNum;
307                  functionNum++)
308             {
309                 // Check if this function exists by looking for a
310                 // device ID
311                 std::string devIDProperty =
312                     "Function" + std::to_string(functionNum) + "DeviceId";
313                 const std::string* property = nullptr;
314                 for (const auto& propEntry : pcieDevProperties)
315                 {
316                     if (propEntry.first == devIDProperty)
317                     {
318                         property = std::get_if<std::string>(&propEntry.second);
319                     }
320                 }
321                 if (property == nullptr || property->empty())
322                 {
323                     continue;
324                 }
325                 nlohmann::json::object_t pcieFunction;
326                 pcieFunction["@odata.id"] =
327                     "/redfish/v1/Systems/system/PCIeDevices/" + device +
328                     "/PCIeFunctions/" + std::to_string(functionNum);
329                 pcieFunctionList.push_back(std::move(pcieFunction));
330             }
331             asyncResp->res.jsonValue["Members@odata.count"] =
332                 pcieFunctionList.size();
333         };
334         std::string escapedPath = std::string(pciePath) + "/" + device;
335         dbus::utility::escapePathForDbus(escapedPath);
336         sdbusplus::asio::getAllProperties(
337             *crow::connections::systemBus, pcieService, escapedPath,
338             pcieDeviceInterface, std::move(getPCIeDeviceCallback));
339         });
340 }
341 
342 inline void requestRoutesSystemPCIeFunction(App& app)
343 {
344     BMCWEB_ROUTE(
345         app,
346         "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/<str>/")
347         .privileges(redfish::privileges::getPCIeFunction)
348         .methods(boost::beast::http::verb::get)(
349             [&app](const crow::Request& req,
350                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
351                    const std::string& device, const std::string& function) {
352         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
353         {
354             return;
355         }
356         auto getPCIeDeviceCallback =
357             [asyncResp, device, function](
358                 const boost::system::error_code ec,
359                 const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
360             if (ec)
361             {
362                 BMCWEB_LOG_DEBUG
363                     << "failed to get PCIe Device properties ec: " << ec.value()
364                     << ": " << ec.message();
365                 if (ec.value() ==
366                     boost::system::linux_error::bad_request_descriptor)
367                 {
368                     messages::resourceNotFound(asyncResp->res, "PCIeDevice",
369                                                device);
370                 }
371                 else
372                 {
373                     messages::internalError(asyncResp->res);
374                 }
375                 return;
376             }
377 
378             // Check if this function exists by looking for a device
379             // ID
380             std::string functionName = "Function" + function;
381             std::string devIDProperty = functionName + "DeviceId";
382 
383             const std::string* devIdProperty = nullptr;
384             for (const auto& property : pcieDevProperties)
385             {
386                 if (property.first == devIDProperty)
387                 {
388                     devIdProperty = std::get_if<std::string>(&property.second);
389                     continue;
390                 }
391             }
392             if (devIdProperty == nullptr || !devIdProperty->empty())
393             {
394                 messages::resourceNotFound(asyncResp->res, "PCIeFunction",
395                                            function);
396                 return;
397             }
398 
399             asyncResp->res.jsonValue["@odata.type"] =
400                 "#PCIeFunction.v1_2_0.PCIeFunction";
401             asyncResp->res.jsonValue["@odata.id"] =
402                 "/redfish/v1/Systems/system/PCIeDevices/" + device +
403                 "/PCIeFunctions/" + function;
404             asyncResp->res.jsonValue["Name"] = "PCIe Function";
405             asyncResp->res.jsonValue["Id"] = function;
406             asyncResp->res.jsonValue["FunctionId"] = std::stoi(function);
407             asyncResp->res.jsonValue["Links"]["PCIeDevice"]["@odata.id"] =
408                 "/redfish/v1/Systems/system/PCIeDevices/" + device;
409 
410             for (const auto& property : pcieDevProperties)
411             {
412                 const std::string* strProperty =
413                     std::get_if<std::string>(&property.second);
414                 if (property.first == functionName + "DeviceId")
415                 {
416                     asyncResp->res.jsonValue["DeviceId"] = *strProperty;
417                 }
418                 if (property.first == functionName + "VendorId")
419                 {
420                     asyncResp->res.jsonValue["VendorId"] = *strProperty;
421                 }
422                 if (property.first == functionName + "FunctionType")
423                 {
424                     asyncResp->res.jsonValue["FunctionType"] = *strProperty;
425                 }
426                 if (property.first == functionName + "DeviceClass")
427                 {
428                     asyncResp->res.jsonValue["DeviceClass"] = *strProperty;
429                 }
430                 if (property.first == functionName + "ClassCode")
431                 {
432                     asyncResp->res.jsonValue["ClassCode"] = *strProperty;
433                 }
434                 if (property.first == functionName + "RevisionId")
435                 {
436                     asyncResp->res.jsonValue["RevisionId"] = *strProperty;
437                 }
438                 if (property.first == functionName + "SubsystemId")
439                 {
440                     asyncResp->res.jsonValue["SubsystemId"] = *strProperty;
441                 }
442                 if (property.first == functionName + "SubsystemVendorId")
443                 {
444                     asyncResp->res.jsonValue["SubsystemVendorId"] =
445                         *strProperty;
446                 }
447             }
448         };
449         std::string escapedPath = std::string(pciePath) + "/" + device;
450         dbus::utility::escapePathForDbus(escapedPath);
451         sdbusplus::asio::getAllProperties(
452             *crow::connections::systemBus, pcieService, escapedPath,
453             pcieDeviceInterface, std::move(getPCIeDeviceCallback));
454         });
455 }
456 
457 } // namespace redfish
458