xref: /openbmc/bmcweb/redfish-core/lib/pcie.hpp (revision 6f581ec3)
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 == "GenerationInUse")
212                         {
213                             if (propertyString == nullptr)
214                             {
215                                 messages::internalError(asyncResp->res);
216                                 return;
217                             }
218                             std::optional<std::string> generationInUse =
219                                 redfishPcieGenerationFromDbus(*propertyString);
220                             if (!generationInUse)
221                             {
222                                 messages::internalError(asyncResp->res);
223                                 return;
224                             }
225                             if (generationInUse->empty())
226                             {
227                                 // unknown, no need to handle
228                                 return;
229                             }
230                             asyncResp->res
231                                 .jsonValue["PCIeInterface"]["PCIeType"] =
232                                 *generationInUse;
233                         }
234                     }
235                 };
236             std::string escapedPath = std::string(pciePath) + "/" + device;
237             dbus::utility::escapePathForDbus(escapedPath);
238             crow::connections::systemBus->async_method_call(
239                 std::move(getPCIeDeviceCallback), pcieService, escapedPath,
240                 "org.freedesktop.DBus.Properties", "GetAll",
241                 pcieDeviceInterface);
242         });
243 }
244 
245 inline void requestRoutesSystemPCIeFunctionCollection(App& app)
246 {
247     /**
248      * Functions triggers appropriate requests on DBus
249      */
250     BMCWEB_ROUTE(app,
251                  "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/")
252         .privileges(redfish::privileges::getPCIeFunctionCollection)
253         .methods(
254             boost::beast::http::verb::
255                 get)([&app](const crow::Request& req,
256                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
257                             const std::string& device) {
258             if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
259             {
260                 return;
261             }
262             asyncResp->res.jsonValue = {
263                 {"@odata.type",
264                  "#PCIeFunctionCollection.PCIeFunctionCollection"},
265                 {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" +
266                                   device + "/PCIeFunctions"},
267                 {"Name", "PCIe Function Collection"},
268                 {"Description",
269                  "Collection of PCIe Functions for PCIe Device " + device}};
270 
271             auto getPCIeDeviceCallback =
272                 [asyncResp, device](
273                     const boost::system::error_code ec,
274                     const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
275                     if (ec)
276                     {
277                         BMCWEB_LOG_DEBUG
278                             << "failed to get PCIe Device properties ec: "
279                             << ec.value() << ": " << ec.message();
280                         if (ec.value() ==
281                             boost::system::linux_error::bad_request_descriptor)
282                         {
283                             messages::resourceNotFound(asyncResp->res,
284                                                        "PCIeDevice", device);
285                         }
286                         else
287                         {
288                             messages::internalError(asyncResp->res);
289                         }
290                         return;
291                     }
292 
293                     nlohmann::json& pcieFunctionList =
294                         asyncResp->res.jsonValue["Members"];
295                     pcieFunctionList = nlohmann::json::array();
296                     static constexpr const int maxPciFunctionNum = 8;
297                     for (int functionNum = 0; functionNum < maxPciFunctionNum;
298                          functionNum++)
299                     {
300                         // Check if this function exists by looking for a
301                         // device ID
302                         std::string devIDProperty =
303                             "Function" + std::to_string(functionNum) +
304                             "DeviceId";
305                         const std::string* property = nullptr;
306                         for (const auto& propEntry : pcieDevProperties)
307                         {
308                             if (propEntry.first == devIDProperty)
309                             {
310                                 property =
311                                     std::get_if<std::string>(&propEntry.second);
312                             }
313                         }
314                         if (property == nullptr || !property->empty())
315                         {
316                             return;
317                         }
318                         pcieFunctionList.push_back(
319                             {{"@odata.id",
320                               "/redfish/v1/Systems/system/PCIeDevices/" +
321                                   device + "/PCIeFunctions/" +
322                                   std::to_string(functionNum)}});
323                     }
324                     asyncResp->res.jsonValue["Members@odata.count"] =
325                         pcieFunctionList.size();
326                 };
327             std::string escapedPath = std::string(pciePath) + "/" + device;
328             dbus::utility::escapePathForDbus(escapedPath);
329             crow::connections::systemBus->async_method_call(
330                 std::move(getPCIeDeviceCallback), pcieService, escapedPath,
331                 "org.freedesktop.DBus.Properties", "GetAll",
332                 pcieDeviceInterface);
333         });
334 }
335 
336 inline void requestRoutesSystemPCIeFunction(App& app)
337 {
338     BMCWEB_ROUTE(
339         app,
340         "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/<str>/")
341         .privileges(redfish::privileges::getPCIeFunction)
342         .methods(
343             boost::beast::http::verb::
344                 get)([&app](const crow::Request& req,
345                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
346                             const std::string& device,
347                             const std::string& function) {
348             if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
349             {
350                 return;
351             }
352             auto getPCIeDeviceCallback =
353                 [asyncResp, device, function](
354                     const boost::system::error_code ec,
355                     const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
356                     if (ec)
357                     {
358                         BMCWEB_LOG_DEBUG
359                             << "failed to get PCIe Device properties ec: "
360                             << ec.value() << ": " << ec.message();
361                         if (ec.value() ==
362                             boost::system::linux_error::bad_request_descriptor)
363                         {
364                             messages::resourceNotFound(asyncResp->res,
365                                                        "PCIeDevice", device);
366                         }
367                         else
368                         {
369                             messages::internalError(asyncResp->res);
370                         }
371                         return;
372                     }
373 
374                     // Check if this function exists by looking for a device ID
375                     std::string functionName = "Function" + function;
376                     std::string devIDProperty = functionName + "DeviceId";
377 
378                     const std::string* devIdProperty = nullptr;
379                     for (const auto& property : pcieDevProperties)
380                     {
381                         if (property.first == devIDProperty)
382                         {
383                             devIdProperty =
384                                 std::get_if<std::string>(&property.second);
385                             continue;
386                         }
387                     }
388                     if (devIdProperty == nullptr || !devIdProperty->empty())
389                     {
390                         messages::resourceNotFound(asyncResp->res,
391                                                    "PCIeFunction", function);
392                         return;
393                     }
394 
395                     asyncResp->res.jsonValue = {
396                         {"@odata.type", "#PCIeFunction.v1_2_0.PCIeFunction"},
397                         {"@odata.id",
398                          "/redfish/v1/Systems/system/PCIeDevices/" + device +
399                              "/PCIeFunctions/" + function},
400                         {"Name", "PCIe Function"},
401                         {"Id", function},
402                         {"FunctionId", std::stoi(function)},
403                         {"Links",
404                          {{"PCIeDevice",
405                            {{"@odata.id",
406                              "/redfish/v1/Systems/system/PCIeDevices/" +
407                                  device}}}}}};
408 
409                     for (const auto& property : pcieDevProperties)
410                     {
411                         const std::string* strProperty =
412                             std::get_if<std::string>(&property.second);
413                         if (property.first == functionName + "DeviceId")
414                         {
415                             asyncResp->res.jsonValue["DeviceId"] = *strProperty;
416                         }
417                         if (property.first == functionName + "VendorId")
418                         {
419                             asyncResp->res.jsonValue["VendorId"] = *strProperty;
420                         }
421                         if (property.first == functionName + "FunctionType")
422                         {
423                             asyncResp->res.jsonValue["FunctionType"] =
424                                 *strProperty;
425                         }
426                         if (property.first == functionName + "DeviceClass")
427                         {
428                             asyncResp->res.jsonValue["DeviceClass"] =
429                                 *strProperty;
430                         }
431                         if (property.first == functionName + "ClassCode")
432                         {
433                             asyncResp->res.jsonValue["ClassCode"] =
434                                 *strProperty;
435                         }
436                         if (property.first == functionName + "RevisionId")
437                         {
438                             asyncResp->res.jsonValue["RevisionId"] =
439                                 *strProperty;
440                         }
441                         if (property.first == functionName + "SubsystemId")
442                         {
443                             asyncResp->res.jsonValue["SubsystemId"] =
444                                 *strProperty;
445                         }
446                         if (property.first ==
447                             functionName + "SubsystemVendorId")
448                         {
449                             asyncResp->res.jsonValue["SubsystemVendorId"] =
450                                 *strProperty;
451                         }
452                     }
453                 };
454             std::string escapedPath = std::string(pciePath) + "/" + device;
455             dbus::utility::escapePathForDbus(escapedPath);
456             crow::connections::systemBus->async_method_call(
457                 std::move(getPCIeDeviceCallback), pcieService, escapedPath,
458                 "org.freedesktop.DBus.Properties", "GetAll",
459                 pcieDeviceInterface);
460         });
461 }
462 
463 } // namespace redfish
464