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