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