xref: /openbmc/bmcweb/redfish-core/lib/pcie.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4 
5 #pragma once
6 
7 #include "app.hpp"
8 #include "dbus_utility.hpp"
9 #include "generated/enums/resource.hpp"
10 #include "query.hpp"
11 #include "registries/privilege_registry.hpp"
12 #include "utils/collection.hpp"
13 #include "utils/dbus_utils.hpp"
14 #include "utils/pcie_util.hpp"
15 
16 #include <boost/system/linux_error.hpp>
17 #include <boost/url/format.hpp>
18 #include <sdbusplus/asio/property.hpp>
19 #include <sdbusplus/unpack_properties.hpp>
20 
21 #include <limits>
22 
23 namespace redfish
24 {
25 
26 static constexpr const char* inventoryPath = "/xyz/openbmc_project/inventory";
27 static constexpr std::array<std::string_view, 1> pcieDeviceInterface = {
28     "xyz.openbmc_project.Inventory.Item.PCIeDevice"};
29 static constexpr std::array<std::string_view, 1> pcieSlotInterface = {
30     "xyz.openbmc_project.Inventory.Item.PCIeSlot"};
31 
handlePCIeDevicePath(const std::string & pcieDeviceId,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const dbus::utility::MapperGetSubTreePathsResponse & pcieDevicePaths,const std::function<void (const std::string & pcieDevicePath,const std::string & service)> & callback)32 inline void handlePCIeDevicePath(
33     const std::string& pcieDeviceId,
34     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
35     const dbus::utility::MapperGetSubTreePathsResponse& pcieDevicePaths,
36     const std::function<void(const std::string& pcieDevicePath,
37                              const std::string& service)>& callback)
38 
39 {
40     for (const std::string& pcieDevicePath : pcieDevicePaths)
41     {
42         std::string pciecDeviceName =
43             sdbusplus::message::object_path(pcieDevicePath).filename();
44         if (pciecDeviceName.empty() || pciecDeviceName != pcieDeviceId)
45         {
46             continue;
47         }
48 
49         dbus::utility::getDbusObject(
50             pcieDevicePath, pcieDeviceInterface,
51             [pcieDevicePath, asyncResp,
52              callback](const boost::system::error_code& ec,
53                        const dbus::utility::MapperGetObject& object) {
54                 if (ec || object.empty())
55                 {
56                     BMCWEB_LOG_ERROR("DBUS response error {}", ec);
57                     messages::internalError(asyncResp->res);
58                     return;
59                 }
60                 callback(pcieDevicePath, object.begin()->first);
61             });
62         return;
63     }
64 
65     BMCWEB_LOG_WARNING("PCIe Device not found");
66     messages::resourceNotFound(asyncResp->res, "PCIeDevice", pcieDeviceId);
67 }
68 
getValidPCIeDevicePath(const std::string & pcieDeviceId,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::function<void (const std::string & pcieDevicePath,const std::string & service)> & callback)69 inline void getValidPCIeDevicePath(
70     const std::string& pcieDeviceId,
71     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
72     const std::function<void(const std::string& pcieDevicePath,
73                              const std::string& service)>& callback)
74 {
75     dbus::utility::getSubTreePaths(
76         inventoryPath, 0, pcieDeviceInterface,
77         [pcieDeviceId, asyncResp,
78          callback](const boost::system::error_code& ec,
79                    const dbus::utility::MapperGetSubTreePathsResponse&
80                        pcieDevicePaths) {
81             if (ec)
82             {
83                 BMCWEB_LOG_ERROR("D-Bus response error on GetSubTree {}", ec);
84                 messages::internalError(asyncResp->res);
85                 return;
86             }
87             handlePCIeDevicePath(pcieDeviceId, asyncResp, pcieDevicePaths,
88                                  callback);
89             return;
90         });
91 }
92 
handlePCIeDeviceCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)93 inline void handlePCIeDeviceCollectionGet(
94     crow::App& app, const crow::Request& req,
95     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
96     const std::string& systemName)
97 {
98     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
99     {
100         return;
101     }
102     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
103     {
104         // Option currently returns no systems.  TBD
105         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
106                                    systemName);
107         return;
108     }
109     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
110     {
111         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
112                                    systemName);
113         return;
114     }
115 
116     asyncResp->res.addHeader(boost::beast::http::field::link,
117                              "</redfish/v1/JsonSchemas/PCIeDeviceCollection/"
118                              "PCIeDeviceCollection.json>; rel=describedby");
119     asyncResp->res.jsonValue["@odata.type"] =
120         "#PCIeDeviceCollection.PCIeDeviceCollection";
121     asyncResp->res.jsonValue["@odata.id"] = std::format(
122         "/redfish/v1/Systems/{}/PCIeDevices", BMCWEB_REDFISH_SYSTEM_URI_NAME);
123     asyncResp->res.jsonValue["Name"] = "PCIe Device Collection";
124     asyncResp->res.jsonValue["Description"] = "Collection of PCIe Devices";
125 
126     pcie_util::getPCIeDeviceList(asyncResp,
127                                  nlohmann::json::json_pointer("/Members"));
128 }
129 
requestRoutesSystemPCIeDeviceCollection(App & app)130 inline void requestRoutesSystemPCIeDeviceCollection(App& app)
131 {
132     /**
133      * Functions triggers appropriate requests on DBus
134      */
135     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/")
136         .privileges(redfish::privileges::getPCIeDeviceCollection)
137         .methods(boost::beast::http::verb::get)(
138             std::bind_front(handlePCIeDeviceCollectionGet, std::ref(app)));
139 }
140 
addPCIeSlotProperties(crow::Response & res,const boost::system::error_code & ec,const dbus::utility::DBusPropertiesMap & pcieSlotProperties)141 inline void addPCIeSlotProperties(
142     crow::Response& res, const boost::system::error_code& ec,
143     const dbus::utility::DBusPropertiesMap& pcieSlotProperties)
144 {
145     if (ec)
146     {
147         BMCWEB_LOG_ERROR("DBUS response error for getAllProperties{}",
148                          ec.value());
149         messages::internalError(res);
150         return;
151     }
152     std::string generation;
153     size_t lanes = 0;
154     std::string slotType;
155 
156     bool success = sdbusplus::unpackPropertiesNoThrow(
157         dbus_utils::UnpackErrorPrinter(), pcieSlotProperties, "Generation",
158         generation, "Lanes", lanes, "SlotType", slotType);
159 
160     if (!success)
161     {
162         messages::internalError(res);
163         return;
164     }
165 
166     std::optional<pcie_device::PCIeTypes> pcieType =
167         pcie_util::redfishPcieGenerationFromDbus(generation);
168     if (!pcieType)
169     {
170         BMCWEB_LOG_WARNING("Unknown PCIeType: {}", generation);
171     }
172     else
173     {
174         if (*pcieType == pcie_device::PCIeTypes::Invalid)
175         {
176             BMCWEB_LOG_ERROR("Invalid PCIeType: {}", generation);
177             messages::internalError(res);
178             return;
179         }
180         res.jsonValue["Slot"]["PCIeType"] = *pcieType;
181     }
182 
183     if (lanes != 0)
184     {
185         res.jsonValue["Slot"]["Lanes"] = lanes;
186     }
187 
188     std::optional<pcie_slots::SlotTypes> redfishSlotType =
189         pcie_util::dbusSlotTypeToRf(slotType);
190     if (!redfishSlotType)
191     {
192         BMCWEB_LOG_WARNING("Unknown PCIeSlot Type: {}", slotType);
193     }
194     else
195     {
196         if (*redfishSlotType == pcie_slots::SlotTypes::Invalid)
197         {
198             BMCWEB_LOG_ERROR("Invalid PCIeSlot type: {}", slotType);
199             messages::internalError(res);
200             return;
201         }
202         res.jsonValue["Slot"]["SlotType"] = *redfishSlotType;
203     }
204 }
205 
getPCIeDeviceSlotPath(const std::string & pcieDevicePath,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::function<void (const std::string & pcieDeviceSlot)> && callback)206 inline void getPCIeDeviceSlotPath(
207     const std::string& pcieDevicePath,
208     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
209     std::function<void(const std::string& pcieDeviceSlot)>&& callback)
210 {
211     std::string associationPath = pcieDevicePath + "/contained_by";
212     dbus::utility::getAssociatedSubTreePaths(
213         associationPath, sdbusplus::message::object_path(inventoryPath), 0,
214         pcieSlotInterface,
215         [callback = std::move(callback), asyncResp, pcieDevicePath](
216             const boost::system::error_code& ec,
217             const dbus::utility::MapperGetSubTreePathsResponse& endpoints) {
218             if (ec)
219             {
220                 if (ec.value() == EBADR)
221                 {
222                     // Missing association is not an error
223                     return;
224                 }
225                 BMCWEB_LOG_ERROR(
226                     "DBUS response error for getAssociatedSubTreePaths {}",
227                     ec.value());
228                 messages::internalError(asyncResp->res);
229                 return;
230             }
231             if (endpoints.size() > 1)
232             {
233                 BMCWEB_LOG_ERROR(
234                     "PCIeDevice {} is associated with more than one PCIeSlot: {}",
235                     pcieDevicePath, endpoints.size());
236                 messages::internalError(asyncResp->res);
237                 return;
238             }
239             if (endpoints.empty())
240             {
241                 // If the device doesn't have an association, return without
242                 // PCIe Slot properties
243                 BMCWEB_LOG_DEBUG("PCIeDevice is not associated with PCIeSlot");
244                 return;
245             }
246             callback(endpoints[0]);
247         });
248 }
249 
afterGetDbusObject(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceSlot,const boost::system::error_code & ec,const dbus::utility::MapperGetObject & object)250 inline void afterGetDbusObject(
251     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
252     const std::string& pcieDeviceSlot, const boost::system::error_code& ec,
253     const dbus::utility::MapperGetObject& object)
254 {
255     if (ec || object.empty())
256     {
257         BMCWEB_LOG_ERROR("DBUS response error for getDbusObject {}",
258                          ec.value());
259         messages::internalError(asyncResp->res);
260         return;
261     }
262     dbus::utility::getAllProperties(
263         object.begin()->first, pcieDeviceSlot,
264         "xyz.openbmc_project.Inventory.Item.PCIeSlot",
265         [asyncResp](
266             const boost::system::error_code& ec2,
267             const dbus::utility::DBusPropertiesMap& pcieSlotProperties) {
268             addPCIeSlotProperties(asyncResp->res, ec2, pcieSlotProperties);
269         });
270 }
271 
afterGetPCIeDeviceSlotPath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceSlot)272 inline void afterGetPCIeDeviceSlotPath(
273     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
274     const std::string& pcieDeviceSlot)
275 {
276     dbus::utility::getDbusObject(
277         pcieDeviceSlot, pcieSlotInterface,
278         [asyncResp,
279          pcieDeviceSlot](const boost::system::error_code& ec,
280                          const dbus::utility::MapperGetObject& object) {
281             afterGetDbusObject(asyncResp, pcieDeviceSlot, ec, object);
282         });
283 }
284 
getPCIeDeviceHealth(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDevicePath,const std::string & service)285 inline void getPCIeDeviceHealth(
286     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
287     const std::string& pcieDevicePath, const std::string& service)
288 {
289     dbus::utility::getProperty<bool>(
290         service, pcieDevicePath,
291         "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
292         [asyncResp](const boost::system::error_code& ec, const bool value) {
293             if (ec)
294             {
295                 if (ec.value() != EBADR)
296                 {
297                     BMCWEB_LOG_ERROR("DBUS response error for Health {}",
298                                      ec.value());
299                     messages::internalError(asyncResp->res);
300                 }
301                 return;
302             }
303 
304             if (!value)
305             {
306                 asyncResp->res.jsonValue["Status"]["Health"] =
307                     resource::Health::Critical;
308             }
309         });
310 }
311 
getPCIeDeviceState(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDevicePath,const std::string & service)312 inline void getPCIeDeviceState(
313     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
314     const std::string& pcieDevicePath, const std::string& service)
315 {
316     dbus::utility::getProperty<bool>(
317         service, pcieDevicePath, "xyz.openbmc_project.Inventory.Item",
318         "Present",
319         [asyncResp](const boost::system::error_code& ec, bool value) {
320             if (ec)
321             {
322                 if (ec.value() != EBADR)
323                 {
324                     BMCWEB_LOG_ERROR("DBUS response error for State");
325                     messages::internalError(asyncResp->res);
326                 }
327                 return;
328             }
329 
330             if (!value)
331             {
332                 asyncResp->res.jsonValue["Status"]["State"] =
333                     resource::State::Absent;
334             }
335         });
336 }
337 
getPCIeDeviceAsset(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDevicePath,const std::string & service)338 inline void getPCIeDeviceAsset(
339     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
340     const std::string& pcieDevicePath, const std::string& service)
341 {
342     dbus::utility::getAllProperties(
343         service, pcieDevicePath,
344         "xyz.openbmc_project.Inventory.Decorator.Asset",
345         [pcieDevicePath, asyncResp{asyncResp}](
346             const boost::system::error_code& ec,
347             const dbus::utility::DBusPropertiesMap& assetList) {
348             if (ec)
349             {
350                 if (ec.value() != EBADR)
351                 {
352                     BMCWEB_LOG_ERROR("DBUS response error for Properties{}",
353                                      ec.value());
354                     messages::internalError(asyncResp->res);
355                 }
356                 return;
357             }
358 
359             const std::string* manufacturer = nullptr;
360             const std::string* model = nullptr;
361             const std::string* partNumber = nullptr;
362             const std::string* serialNumber = nullptr;
363             const std::string* sparePartNumber = nullptr;
364 
365             const bool success = sdbusplus::unpackPropertiesNoThrow(
366                 dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer",
367                 manufacturer, "Model", model, "PartNumber", partNumber,
368                 "SerialNumber", serialNumber, "SparePartNumber",
369                 sparePartNumber);
370 
371             if (!success)
372             {
373                 messages::internalError(asyncResp->res);
374                 return;
375             }
376 
377             if (manufacturer != nullptr)
378             {
379                 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
380             }
381             if (model != nullptr)
382             {
383                 asyncResp->res.jsonValue["Model"] = *model;
384             }
385 
386             if (partNumber != nullptr)
387             {
388                 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
389             }
390 
391             if (serialNumber != nullptr)
392             {
393                 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
394             }
395 
396             if (sparePartNumber != nullptr && !sparePartNumber->empty())
397             {
398                 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
399             }
400         });
401 }
402 
addPCIeDeviceProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceId,const dbus::utility::DBusPropertiesMap & pcieDevProperties)403 inline void addPCIeDeviceProperties(
404     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
405     const std::string& pcieDeviceId,
406     const dbus::utility::DBusPropertiesMap& pcieDevProperties)
407 {
408     const std::string* generationInUse = nullptr;
409     const std::string* generationSupported = nullptr;
410     const size_t* lanesInUse = nullptr;
411     const size_t* maxLanes = nullptr;
412 
413     const bool success = sdbusplus::unpackPropertiesNoThrow(
414         dbus_utils::UnpackErrorPrinter(), pcieDevProperties, "GenerationInUse",
415         generationInUse, "GenerationSupported", generationSupported,
416         "LanesInUse", lanesInUse, "MaxLanes", maxLanes);
417 
418     if (!success)
419     {
420         messages::internalError(asyncResp->res);
421         return;
422     }
423 
424     if (generationInUse != nullptr)
425     {
426         std::optional<pcie_device::PCIeTypes> redfishGenerationInUse =
427             pcie_util::redfishPcieGenerationFromDbus(*generationInUse);
428 
429         if (!redfishGenerationInUse)
430         {
431             BMCWEB_LOG_WARNING("Unknown PCIe Device Generation: {}",
432                                *generationInUse);
433         }
434         else
435         {
436             if (*redfishGenerationInUse == pcie_device::PCIeTypes::Invalid)
437             {
438                 BMCWEB_LOG_ERROR("Invalid PCIe Device Generation: {}",
439                                  *generationInUse);
440                 messages::internalError(asyncResp->res);
441                 return;
442             }
443             asyncResp->res.jsonValue["PCIeInterface"]["PCIeType"] =
444                 *redfishGenerationInUse;
445         }
446     }
447 
448     if (generationSupported != nullptr)
449     {
450         std::optional<pcie_device::PCIeTypes> redfishGenerationSupported =
451             pcie_util::redfishPcieGenerationFromDbus(*generationSupported);
452 
453         if (!redfishGenerationSupported)
454         {
455             BMCWEB_LOG_WARNING("Unknown PCIe Device Generation: {}",
456                                *generationSupported);
457         }
458         else
459         {
460             if (*redfishGenerationSupported == pcie_device::PCIeTypes::Invalid)
461             {
462                 BMCWEB_LOG_ERROR("Invalid PCIe Device Generation: {}",
463                                  *generationSupported);
464                 messages::internalError(asyncResp->res);
465                 return;
466             }
467             asyncResp->res.jsonValue["PCIeInterface"]["MaxPCIeType"] =
468                 *redfishGenerationSupported;
469         }
470     }
471 
472     if (lanesInUse != nullptr)
473     {
474         if (*lanesInUse == std::numeric_limits<size_t>::max())
475         {
476             // The default value of LanesInUse is "maxint", and the field will
477             // be null if it is a default value.
478             asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] = nullptr;
479         }
480         else
481         {
482             asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] =
483                 *lanesInUse;
484         }
485     }
486     // The default value of MaxLanes is 0, and the field will be
487     // left as off if it is a default value.
488     if (maxLanes != nullptr && *maxLanes != 0)
489     {
490         asyncResp->res.jsonValue["PCIeInterface"]["MaxLanes"] = *maxLanes;
491     }
492 
493     asyncResp->res.jsonValue["PCIeFunctions"]["@odata.id"] =
494         boost::urls::format(
495             "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions",
496             BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
497 }
498 
getPCIeDeviceProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDevicePath,const std::string & service,const std::function<void (const dbus::utility::DBusPropertiesMap & pcieDevProperties)> && callback)499 inline void getPCIeDeviceProperties(
500     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
501     const std::string& pcieDevicePath, const std::string& service,
502     const std::function<void(
503         const dbus::utility::DBusPropertiesMap& pcieDevProperties)>&& callback)
504 {
505     dbus::utility::getAllProperties(
506         service, pcieDevicePath,
507         "xyz.openbmc_project.Inventory.Item.PCIeDevice",
508         [asyncResp,
509          callback](const boost::system::error_code& ec,
510                    const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
511             if (ec)
512             {
513                 if (ec.value() != EBADR)
514                 {
515                     BMCWEB_LOG_ERROR("DBUS response error for Properties");
516                     messages::internalError(asyncResp->res);
517                 }
518                 return;
519             }
520             callback(pcieDevProperties);
521         });
522 }
523 
addPCIeDeviceCommonProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceId)524 inline void addPCIeDeviceCommonProperties(
525     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
526     const std::string& pcieDeviceId)
527 {
528     asyncResp->res.addHeader(
529         boost::beast::http::field::link,
530         "</redfish/v1/JsonSchemas/PCIeDevice/PCIeDevice.json>; rel=describedby");
531     asyncResp->res.jsonValue["@odata.type"] = "#PCIeDevice.v1_9_0.PCIeDevice";
532     asyncResp->res.jsonValue["@odata.id"] =
533         boost::urls::format("/redfish/v1/Systems/{}/PCIeDevices/{}",
534                             BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
535     asyncResp->res.jsonValue["Name"] = "PCIe Device";
536     asyncResp->res.jsonValue["Id"] = pcieDeviceId;
537     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
538     asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK;
539 }
540 
afterGetValidPcieDevicePath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & pcieDeviceId,const std::string & pcieDevicePath,const std::string & service)541 inline void afterGetValidPcieDevicePath(
542     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
543     const std::string& pcieDeviceId, const std::string& pcieDevicePath,
544     const std::string& service)
545 {
546     addPCIeDeviceCommonProperties(asyncResp, pcieDeviceId);
547     getPCIeDeviceAsset(asyncResp, pcieDevicePath, service);
548     getPCIeDeviceState(asyncResp, pcieDevicePath, service);
549     getPCIeDeviceHealth(asyncResp, pcieDevicePath, service);
550     getPCIeDeviceProperties(
551         asyncResp, pcieDevicePath, service,
552         std::bind_front(addPCIeDeviceProperties, asyncResp, pcieDeviceId));
553     getPCIeDeviceSlotPath(
554         pcieDevicePath, asyncResp,
555         std::bind_front(afterGetPCIeDeviceSlotPath, asyncResp));
556 }
557 
handlePCIeDeviceGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & pcieDeviceId)558 inline void handlePCIeDeviceGet(
559     App& app, const crow::Request& req,
560     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
561     const std::string& systemName, const std::string& pcieDeviceId)
562 {
563     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
564     {
565         return;
566     }
567     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
568     {
569         // Option currently returns no systems.  TBD
570         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
571                                    systemName);
572         return;
573     }
574     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
575     {
576         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
577                                    systemName);
578         return;
579     }
580 
581     getValidPCIeDevicePath(
582         pcieDeviceId, asyncResp,
583         std::bind_front(afterGetValidPcieDevicePath, asyncResp, pcieDeviceId));
584 }
585 
requestRoutesSystemPCIeDevice(App & app)586 inline void requestRoutesSystemPCIeDevice(App& app)
587 {
588     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/")
589         .privileges(redfish::privileges::getPCIeDevice)
590         .methods(boost::beast::http::verb::get)(
591             std::bind_front(handlePCIeDeviceGet, std::ref(app)));
592 }
593 
addPCIeFunctionList(crow::Response & res,const std::string & pcieDeviceId,const dbus::utility::DBusPropertiesMap & pcieDevProperties)594 inline void addPCIeFunctionList(
595     crow::Response& res, const std::string& pcieDeviceId,
596     const dbus::utility::DBusPropertiesMap& pcieDevProperties)
597 {
598     nlohmann::json& pcieFunctionList = res.jsonValue["Members"];
599     pcieFunctionList = nlohmann::json::array();
600     static constexpr const int maxPciFunctionNum = 8;
601 
602     for (int functionNum = 0; functionNum < maxPciFunctionNum; functionNum++)
603     {
604         // Check if this function exists by
605         // looking for a device ID
606         std::string devIDProperty =
607             "Function" + std::to_string(functionNum) + "DeviceId";
608         const std::string* property = nullptr;
609         for (const auto& propEntry : pcieDevProperties)
610         {
611             if (propEntry.first == devIDProperty)
612             {
613                 property = std::get_if<std::string>(&propEntry.second);
614                 break;
615             }
616         }
617         if (property == nullptr || property->empty())
618         {
619             continue;
620         }
621 
622         nlohmann::json::object_t pcieFunction;
623         pcieFunction["@odata.id"] = boost::urls::format(
624             "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions/{}",
625             BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId,
626             std::to_string(functionNum));
627         pcieFunctionList.emplace_back(std::move(pcieFunction));
628     }
629     res.jsonValue["PCIeFunctions@odata.count"] = pcieFunctionList.size();
630 }
631 
handlePCIeFunctionCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & pcieDeviceId)632 inline void handlePCIeFunctionCollectionGet(
633     App& app, const crow::Request& req,
634     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
635     const std::string& systemName, const std::string& pcieDeviceId)
636 {
637     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
638     {
639         return;
640     }
641     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
642     {
643         // Option currently returns no systems.  TBD
644         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
645                                    systemName);
646         return;
647     }
648 
649     getValidPCIeDevicePath(
650         pcieDeviceId, asyncResp,
651         [asyncResp, pcieDeviceId](const std::string& pcieDevicePath,
652                                   const std::string& service) {
653             asyncResp->res.addHeader(
654                 boost::beast::http::field::link,
655                 "</redfish/v1/JsonSchemas/PCIeFunctionCollection/PCIeFunctionCollection.json>; rel=describedby");
656             asyncResp->res.jsonValue["@odata.type"] =
657                 "#PCIeFunctionCollection.PCIeFunctionCollection";
658             asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
659                 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions",
660                 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
661             asyncResp->res.jsonValue["Name"] = "PCIe Function Collection";
662             asyncResp->res.jsonValue["Description"] =
663                 "Collection of PCIe Functions for PCIe Device " + pcieDeviceId;
664             getPCIeDeviceProperties(
665                 asyncResp, pcieDevicePath, service,
666                 [asyncResp, pcieDeviceId](
667                     const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
668                     addPCIeFunctionList(asyncResp->res, pcieDeviceId,
669                                         pcieDevProperties);
670                 });
671         });
672 }
673 
requestRoutesSystemPCIeFunctionCollection(App & app)674 inline void requestRoutesSystemPCIeFunctionCollection(App& app)
675 {
676     /**
677      * Functions triggers appropriate requests on DBus
678      */
679     BMCWEB_ROUTE(app,
680                  "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/")
681         .privileges(redfish::privileges::getPCIeFunctionCollection)
682         .methods(boost::beast::http::verb::get)(
683             std::bind_front(handlePCIeFunctionCollectionGet, std::ref(app)));
684 }
685 
validatePCIeFunctionId(uint64_t pcieFunctionId,const dbus::utility::DBusPropertiesMap & pcieDevProperties)686 inline bool validatePCIeFunctionId(
687     uint64_t pcieFunctionId,
688     const dbus::utility::DBusPropertiesMap& pcieDevProperties)
689 {
690     std::string functionName = "Function" + std::to_string(pcieFunctionId);
691     std::string devIDProperty = functionName + "DeviceId";
692 
693     const std::string* devIdProperty = nullptr;
694     for (const auto& property : pcieDevProperties)
695     {
696         if (property.first == devIDProperty)
697         {
698             devIdProperty = std::get_if<std::string>(&property.second);
699             break;
700         }
701     }
702     return (devIdProperty != nullptr && !devIdProperty->empty());
703 }
704 
addPCIeFunctionProperties(crow::Response & resp,uint64_t pcieFunctionId,const dbus::utility::DBusPropertiesMap & pcieDevProperties)705 inline void addPCIeFunctionProperties(
706     crow::Response& resp, uint64_t pcieFunctionId,
707     const dbus::utility::DBusPropertiesMap& pcieDevProperties)
708 {
709     std::string functionName = "Function" + std::to_string(pcieFunctionId);
710     for (const auto& property : pcieDevProperties)
711     {
712         const std::string* strProperty =
713             std::get_if<std::string>(&property.second);
714         if (strProperty == nullptr)
715         {
716             continue;
717         }
718         if (property.first == functionName + "DeviceId")
719         {
720             resp.jsonValue["DeviceId"] = *strProperty;
721         }
722         if (property.first == functionName + "VendorId")
723         {
724             resp.jsonValue["VendorId"] = *strProperty;
725         }
726         // TODO: FunctionType and DeviceClass are Redfish enums. The D-Bus
727         // property strings should be mapped correctly to ensure these
728         // strings are Redfish enum values. For now just check for empty.
729         if (property.first == functionName + "FunctionType")
730         {
731             if (!strProperty->empty())
732             {
733                 resp.jsonValue["FunctionType"] = *strProperty;
734             }
735         }
736         if (property.first == functionName + "DeviceClass")
737         {
738             if (!strProperty->empty())
739             {
740                 resp.jsonValue["DeviceClass"] = *strProperty;
741             }
742         }
743         if (property.first == functionName + "ClassCode")
744         {
745             resp.jsonValue["ClassCode"] = *strProperty;
746         }
747         if (property.first == functionName + "RevisionId")
748         {
749             resp.jsonValue["RevisionId"] = *strProperty;
750         }
751         if (property.first == functionName + "SubsystemId")
752         {
753             resp.jsonValue["SubsystemId"] = *strProperty;
754         }
755         if (property.first == functionName + "SubsystemVendorId")
756         {
757             resp.jsonValue["SubsystemVendorId"] = *strProperty;
758         }
759     }
760 }
761 
addPCIeFunctionCommonProperties(crow::Response & resp,const std::string & pcieDeviceId,uint64_t pcieFunctionId)762 inline void addPCIeFunctionCommonProperties(crow::Response& resp,
763                                             const std::string& pcieDeviceId,
764                                             uint64_t pcieFunctionId)
765 {
766     resp.addHeader(
767         boost::beast::http::field::link,
768         "</redfish/v1/JsonSchemas/PCIeFunction/PCIeFunction.json>; rel=describedby");
769     resp.jsonValue["@odata.type"] = "#PCIeFunction.v1_2_3.PCIeFunction";
770     resp.jsonValue["@odata.id"] = boost::urls::format(
771         "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions/{}",
772         BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId,
773         std::to_string(pcieFunctionId));
774     resp.jsonValue["Name"] = "PCIe Function";
775     resp.jsonValue["Id"] = std::to_string(pcieFunctionId);
776     resp.jsonValue["FunctionId"] = pcieFunctionId;
777     resp.jsonValue["Links"]["PCIeDevice"]["@odata.id"] =
778         boost::urls::format("/redfish/v1/Systems/{}/PCIeDevices/{}",
779                             BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId);
780 }
781 
handlePCIeFunctionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & pcieDeviceId,const std::string & pcieFunctionIdStr)782 inline void handlePCIeFunctionGet(
783     App& app, const crow::Request& req,
784     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
785     const std::string& systemName, const std::string& pcieDeviceId,
786     const std::string& pcieFunctionIdStr)
787 {
788     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
789     {
790         return;
791     }
792     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
793     {
794         // Option currently returns no systems.  TBD
795         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
796                                    systemName);
797         return;
798     }
799     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
800     {
801         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
802                                    systemName);
803         return;
804     }
805     std::string_view pcieFunctionIdView = pcieFunctionIdStr;
806 
807     uint64_t pcieFunctionId = 0;
808     std::from_chars_result result = std::from_chars(
809         pcieFunctionIdView.begin(), pcieFunctionIdView.end(), pcieFunctionId);
810     if (result.ec != std::errc{} || result.ptr != pcieFunctionIdView.end())
811     {
812         messages::resourceNotFound(asyncResp->res, "PCIeFunction",
813                                    pcieFunctionIdStr);
814         return;
815     }
816 
817     getValidPCIeDevicePath(
818         pcieDeviceId, asyncResp,
819         [asyncResp, pcieDeviceId, pcieFunctionId](
820             const std::string& pcieDevicePath, const std::string& service) {
821             getPCIeDeviceProperties(
822                 asyncResp, pcieDevicePath, service,
823                 [asyncResp, pcieDeviceId, pcieFunctionId](
824                     const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
825                     addPCIeFunctionCommonProperties(
826                         asyncResp->res, pcieDeviceId, pcieFunctionId);
827                     addPCIeFunctionProperties(asyncResp->res, pcieFunctionId,
828                                               pcieDevProperties);
829                 });
830         });
831 }
832 
requestRoutesSystemPCIeFunction(App & app)833 inline void requestRoutesSystemPCIeFunction(App& app)
834 {
835     BMCWEB_ROUTE(
836         app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/<str>/")
837         .privileges(redfish::privileges::getPCIeFunction)
838         .methods(boost::beast::http::verb::get)(
839             std::bind_front(handlePCIeFunctionGet, std::ref(app)));
840 }
841 
842 } // namespace redfish
843