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