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