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