xref: /openbmc/bmcweb/redfish-core/lib/pcie.hpp (revision 3e8c7dfc)
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::string analysisGeneration(const std::string& pcieType)
98 {
99     if (pcieType ==
100         "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen1")
101     {
102         return "Gen1";
103     }
104     if (pcieType ==
105         "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen2")
106     {
107         return "Gen2";
108     }
109     if (pcieType ==
110         "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen3")
111     {
112         return "Gen3";
113     }
114     if (pcieType ==
115         "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen4")
116     {
117         return "Gen4";
118     }
119     if (pcieType ==
120         "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen5")
121     {
122         return "Gen5";
123     }
124 
125     // Unknown or others
126     return "";
127 }
128 
129 inline void requestRoutesSystemPCIeDevice(App& app)
130 {
131     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/PCIeDevices/<str>/")
132         .privileges(redfish::privileges::getPCIeDevice)
133         .methods(boost::beast::http::verb::get)(
134             [](const crow::Request&,
135                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
136                const std::string& device)
137 
138             {
139                 auto getPCIeDeviceCallback = [asyncResp, device](
140                                                  const boost::system::error_code
141                                                      ec,
142                                                  boost::container::flat_map<
143                                                      std::string,
144                                                      std::variant<std::string>>&
145                                                      pcieDevProperties) {
146                     if (ec)
147                     {
148                         BMCWEB_LOG_DEBUG
149                             << "failed to get PCIe Device properties ec: "
150                             << ec.value() << ": " << ec.message();
151                         if (ec.value() ==
152                             boost::system::linux_error::bad_request_descriptor)
153                         {
154                             messages::resourceNotFound(asyncResp->res,
155                                                        "PCIeDevice", device);
156                         }
157                         else
158                         {
159                             messages::internalError(asyncResp->res);
160                         }
161                         return;
162                     }
163 
164                     asyncResp->res.jsonValue = {
165                         {"@odata.type", "#PCIeDevice.v1_4_0.PCIeDevice"},
166                         {"@odata.id",
167                          "/redfish/v1/Systems/system/PCIeDevices/" + device},
168                         {"Name", "PCIe Device"},
169                         {"Id", device}};
170 
171                     if (std::string* property = std::get_if<std::string>(
172                             &pcieDevProperties["Manufacturer"]);
173                         property)
174                     {
175                         asyncResp->res.jsonValue["Manufacturer"] = *property;
176                     }
177 
178                     if (std::string* property = std::get_if<std::string>(
179                             &pcieDevProperties["DeviceType"]);
180                         property)
181                     {
182                         asyncResp->res.jsonValue["DeviceType"] = *property;
183                     }
184 
185                     if (std::string* property = std::get_if<std::string>(
186                             &pcieDevProperties["PcieType"]);
187                         property)
188                     {
189                         std::string pcieType = analysisGeneration(*property);
190                         if (!pcieType.empty())
191                         {
192                             asyncResp->res
193                                 .jsonValue["PCIeInterface"]["PcieType"] =
194                                 pcieType;
195                         }
196                     }
197 
198                     asyncResp->res.jsonValue["PCIeFunctions"] = {
199                         {"@odata.id",
200                          "/redfish/v1/Systems/system/PCIeDevices/" + device +
201                              "/PCIeFunctions"}};
202                 };
203                 std::string escapedPath = std::string(pciePath) + "/" + device;
204                 dbus::utility::escapePathForDbus(escapedPath);
205                 crow::connections::systemBus->async_method_call(
206                     std::move(getPCIeDeviceCallback), pcieService, escapedPath,
207                     "org.freedesktop.DBus.Properties", "GetAll",
208                     pcieDeviceInterface);
209             });
210 }
211 
212 inline void requestRoutesSystemPCIeFunctionCollection(App& app)
213 {
214     /**
215      * Functions triggers appropriate requests on DBus
216      */
217     BMCWEB_ROUTE(app,
218                  "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/")
219         .privileges(redfish::privileges::getPCIeFunctionCollection)
220         .methods(boost::beast::http::verb::get)(
221             [](const crow::Request&,
222                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
223                const std::string& device)
224 
225             {
226                 asyncResp->res.jsonValue = {
227                     {"@odata.type",
228                      "#PCIeFunctionCollection.PCIeFunctionCollection"},
229                     {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" +
230                                       device + "/PCIeFunctions"},
231                     {"Name", "PCIe Function Collection"},
232                     {"Description",
233                      "Collection of PCIe Functions for PCIe Device " + device}};
234 
235                 auto getPCIeDeviceCallback = [asyncResp, device](
236                                                  const boost::system::error_code
237                                                      ec,
238                                                  boost::container::flat_map<
239                                                      std::string,
240                                                      std::variant<std::string>>&
241                                                      pcieDevProperties) {
242                     if (ec)
243                     {
244                         BMCWEB_LOG_DEBUG
245                             << "failed to get PCIe Device properties ec: "
246                             << ec.value() << ": " << ec.message();
247                         if (ec.value() ==
248                             boost::system::linux_error::bad_request_descriptor)
249                         {
250                             messages::resourceNotFound(asyncResp->res,
251                                                        "PCIeDevice", device);
252                         }
253                         else
254                         {
255                             messages::internalError(asyncResp->res);
256                         }
257                         return;
258                     }
259 
260                     nlohmann::json& pcieFunctionList =
261                         asyncResp->res.jsonValue["Members"];
262                     pcieFunctionList = nlohmann::json::array();
263                     static constexpr const int maxPciFunctionNum = 8;
264                     for (int functionNum = 0; functionNum < maxPciFunctionNum;
265                          functionNum++)
266                     {
267                         // Check if this function exists by looking for a device
268                         // ID
269                         std::string devIDProperty =
270                             "Function" + std::to_string(functionNum) +
271                             "DeviceId";
272                         std::string* property = std::get_if<std::string>(
273                             &pcieDevProperties[devIDProperty]);
274                         if (property && !property->empty())
275                         {
276                             pcieFunctionList.push_back(
277                                 {{"@odata.id",
278                                   "/redfish/v1/Systems/system/PCIeDevices/" +
279                                       device + "/PCIeFunctions/" +
280                                       std::to_string(functionNum)}});
281                         }
282                     }
283                     asyncResp->res.jsonValue["PCIeFunctions@odata.count"] =
284                         pcieFunctionList.size();
285                 };
286                 std::string escapedPath = std::string(pciePath) + "/" + device;
287                 dbus::utility::escapePathForDbus(escapedPath);
288                 crow::connections::systemBus->async_method_call(
289                     std::move(getPCIeDeviceCallback), pcieService, escapedPath,
290                     "org.freedesktop.DBus.Properties", "GetAll",
291                     pcieDeviceInterface);
292             });
293 }
294 
295 inline void requestRoutesSystemPCIeFunction(App& app)
296 {
297     BMCWEB_ROUTE(
298         app,
299         "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/<str>/")
300         .privileges(redfish::privileges::getPCIeFunction)
301         .methods(
302             boost::beast::http::verb::get)([](const crow::Request&,
303                                               const std::shared_ptr<
304                                                   bmcweb::AsyncResp>& asyncResp,
305                                               const std::string& device,
306                                               const std::string& function) {
307             auto getPCIeDeviceCallback = [asyncResp, device, function](
308                                              const boost::system::error_code ec,
309                                              boost::container::flat_map<
310                                                  std::string,
311                                                  std::variant<std::string>>&
312                                                  pcieDevProperties) {
313                 if (ec)
314                 {
315                     BMCWEB_LOG_DEBUG
316                         << "failed to get PCIe Device properties ec: "
317                         << ec.value() << ": " << ec.message();
318                     if (ec.value() ==
319                         boost::system::linux_error::bad_request_descriptor)
320                     {
321                         messages::resourceNotFound(asyncResp->res, "PCIeDevice",
322                                                    device);
323                     }
324                     else
325                     {
326                         messages::internalError(asyncResp->res);
327                     }
328                     return;
329                 }
330 
331                 // Check if this function exists by looking for a device ID
332                 std::string devIDProperty = "Function" + function + "DeviceId";
333                 if (std::string* property = std::get_if<std::string>(
334                         &pcieDevProperties[devIDProperty]);
335                     property && property->empty())
336                 {
337                     messages::resourceNotFound(asyncResp->res, "PCIeFunction",
338                                                function);
339                     return;
340                 }
341 
342                 asyncResp->res.jsonValue = {
343                     {"@odata.type", "#PCIeFunction.v1_2_0.PCIeFunction"},
344                     {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices/" +
345                                       device + "/PCIeFunctions/" + function},
346                     {"Name", "PCIe Function"},
347                     {"Id", function},
348                     {"FunctionId", std::stoi(function)},
349                     {"Links",
350                      {{"PCIeDevice",
351                        {{"@odata.id",
352                          "/redfish/v1/Systems/system/PCIeDevices/" +
353                              device}}}}}};
354 
355                 if (std::string* property = std::get_if<std::string>(
356                         &pcieDevProperties["Function" + function + "DeviceId"]);
357                     property)
358                 {
359                     asyncResp->res.jsonValue["DeviceId"] = *property;
360                 }
361 
362                 if (std::string* property = std::get_if<std::string>(
363                         &pcieDevProperties["Function" + function + "VendorId"]);
364                     property)
365                 {
366                     asyncResp->res.jsonValue["VendorId"] = *property;
367                 }
368 
369                 if (std::string* property = std::get_if<std::string>(
370                         &pcieDevProperties["Function" + function +
371                                            "FunctionType"]);
372                     property)
373                 {
374                     asyncResp->res.jsonValue["FunctionType"] = *property;
375                 }
376 
377                 if (std::string* property = std::get_if<std::string>(
378                         &pcieDevProperties["Function" + function +
379                                            "DeviceClass"]);
380                     property)
381                 {
382                     asyncResp->res.jsonValue["DeviceClass"] = *property;
383                 }
384 
385                 if (std::string* property = std::get_if<std::string>(
386                         &pcieDevProperties["Function" + function +
387                                            "ClassCode"]);
388                     property)
389                 {
390                     asyncResp->res.jsonValue["ClassCode"] = *property;
391                 }
392 
393                 if (std::string* property = std::get_if<std::string>(
394                         &pcieDevProperties["Function" + function +
395                                            "RevisionId"]);
396                     property)
397                 {
398                     asyncResp->res.jsonValue["RevisionId"] = *property;
399                 }
400 
401                 if (std::string* property = std::get_if<std::string>(
402                         &pcieDevProperties["Function" + function +
403                                            "SubsystemId"]);
404                     property)
405                 {
406                     asyncResp->res.jsonValue["SubsystemId"] = *property;
407                 }
408 
409                 if (std::string* property = std::get_if<std::string>(
410                         &pcieDevProperties["Function" + function +
411                                            "SubsystemVendorId"]);
412                     property)
413                 {
414                     asyncResp->res.jsonValue["SubsystemVendorId"] = *property;
415                 }
416             };
417             std::string escapedPath = std::string(pciePath) + "/" + device;
418             dbus::utility::escapePathForDbus(escapedPath);
419             crow::connections::systemBus->async_method_call(
420                 std::move(getPCIeDeviceCallback), pcieService, escapedPath,
421                 "org.freedesktop.DBus.Properties", "GetAll",
422                 pcieDeviceInterface);
423         });
424 }
425 
426 } // namespace redfish
427