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