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