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