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