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