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