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