xref: /openbmc/bmcweb/redfish-core/lib/pcie_slots.hpp (revision 504af5a0568171b72caf13234cc81380b261fa21)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #pragma once
4 
5 #include "app.hpp"
6 #include "async_resp.hpp"
7 #include "dbus_utility.hpp"
8 #include "error_messages.hpp"
9 #include "generated/enums/pcie_device.hpp"
10 #include "generated/enums/pcie_slots.hpp"
11 #include "http_request.hpp"
12 #include "logging.hpp"
13 #include "query.hpp"
14 #include "registries/privilege_registry.hpp"
15 #include "utils/dbus_utils.hpp"
16 #include "utils/pcie_util.hpp"
17 
18 #include <asm-generic/errno.h>
19 
20 #include <boost/beast/http/verb.hpp>
21 #include <boost/system/error_code.hpp>
22 #include <boost/url/format.hpp>
23 #include <nlohmann/json.hpp>
24 #include <sdbusplus/message/native_types.hpp>
25 #include <sdbusplus/unpack_properties.hpp>
26 
27 #include <array>
28 #include <cstddef>
29 #include <functional>
30 #include <memory>
31 #include <optional>
32 #include <string>
33 #include <string_view>
34 #include <utility>
35 
36 namespace redfish
37 {
38 
onPcieSlotGetAllDone(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::DBusPropertiesMap & propertiesList)39 inline void onPcieSlotGetAllDone(
40     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
41     const boost::system::error_code& ec,
42     const dbus::utility::DBusPropertiesMap& propertiesList)
43 {
44     if (ec)
45     {
46         BMCWEB_LOG_ERROR("Can't get PCIeSlot properties!");
47         messages::internalError(asyncResp->res);
48         return;
49     }
50 
51     nlohmann::json& slots = asyncResp->res.jsonValue["Slots"];
52 
53     nlohmann::json::array_t* slotsPtr =
54         slots.get_ptr<nlohmann::json::array_t*>();
55     if (slotsPtr == nullptr)
56     {
57         BMCWEB_LOG_ERROR("Slots key isn't an array???");
58         messages::internalError(asyncResp->res);
59         return;
60     }
61 
62     nlohmann::json::object_t slot;
63 
64     const std::string* generation = nullptr;
65     const size_t* lanes = nullptr;
66     const std::string* slotType = nullptr;
67     const bool* hotPluggable = nullptr;
68 
69     const bool success = sdbusplus::unpackPropertiesNoThrow(
70         dbus_utils::UnpackErrorPrinter(), propertiesList, "Generation",
71         generation, "Lanes", lanes, "SlotType", slotType, "HotPluggable",
72         hotPluggable);
73 
74     if (!success)
75     {
76         messages::internalError(asyncResp->res);
77         return;
78     }
79 
80     if (generation != nullptr)
81     {
82         std::optional<pcie_device::PCIeTypes> pcieType =
83             pcie_util::redfishPcieGenerationFromDbus(*generation);
84         if (!pcieType)
85         {
86             BMCWEB_LOG_WARNING("Unknown PCIe Slot Generation: {}", *generation);
87         }
88         else
89         {
90             if (*pcieType == pcie_device::PCIeTypes::Invalid)
91             {
92                 messages::internalError(asyncResp->res);
93                 return;
94             }
95             slot["PCIeType"] = *pcieType;
96         }
97     }
98 
99     if (lanes != nullptr && *lanes != 0)
100     {
101         slot["Lanes"] = *lanes;
102     }
103 
104     if (slotType != nullptr)
105     {
106         std::optional<pcie_slots::SlotTypes> redfishSlotType =
107             pcie_util::dbusSlotTypeToRf(*slotType);
108         if (!redfishSlotType)
109         {
110             BMCWEB_LOG_WARNING("Unknown PCIe Slot Type: {}", *slotType);
111         }
112         else
113         {
114             if (*redfishSlotType == pcie_slots::SlotTypes::Invalid)
115             {
116                 BMCWEB_LOG_ERROR("Unknown PCIe Slot Type: {}", *slotType);
117                 messages::internalError(asyncResp->res);
118                 return;
119             }
120             slot["SlotType"] = *redfishSlotType;
121         }
122     }
123 
124     if (hotPluggable != nullptr)
125     {
126         slot["HotPluggable"] = *hotPluggable;
127     }
128 
129     slots.emplace_back(std::move(slot));
130 }
131 
onMapperAssociationDone(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisID,const std::string & pcieSlotPath,const std::string & connectionName,const boost::system::error_code & ec,const dbus::utility::MapperEndPoints & pcieSlotChassis)132 inline void onMapperAssociationDone(
133     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
134     const std::string& chassisID, const std::string& pcieSlotPath,
135     const std::string& connectionName, const boost::system::error_code& ec,
136     const dbus::utility::MapperEndPoints& pcieSlotChassis)
137 {
138     if (ec)
139     {
140         if (ec.value() == EBADR)
141         {
142             // This PCIeSlot have no chassis association.
143             return;
144         }
145         BMCWEB_LOG_ERROR("DBUS response error");
146         messages::internalError(asyncResp->res);
147         return;
148     }
149 
150     if (pcieSlotChassis.size() != 1)
151     {
152         BMCWEB_LOG_ERROR("PCIe Slot association error! ");
153         messages::internalError(asyncResp->res);
154         return;
155     }
156 
157     sdbusplus::message::object_path path(pcieSlotChassis[0]);
158     std::string chassisName = path.filename();
159     if (chassisName != chassisID)
160     {
161         // The pcie slot doesn't belong to the chassisID
162         return;
163     }
164 
165     dbus::utility::getAllProperties(
166         connectionName, pcieSlotPath,
167         "xyz.openbmc_project.Inventory.Item.PCIeSlot",
168         [asyncResp](const boost::system::error_code& ec2,
169                     const dbus::utility::DBusPropertiesMap& propertiesList) {
170             onPcieSlotGetAllDone(asyncResp, ec2, propertiesList);
171         });
172 }
173 
onMapperSubtreeDone(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisID,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)174 inline void onMapperSubtreeDone(
175     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
176     const std::string& chassisID, const boost::system::error_code& ec,
177     const dbus::utility::MapperGetSubTreeResponse& subtree)
178 {
179     if (ec)
180     {
181         BMCWEB_LOG_ERROR("D-Bus response error on GetSubTree {}", ec);
182         messages::internalError(asyncResp->res);
183         return;
184     }
185     if (subtree.empty())
186     {
187         messages::resourceNotFound(asyncResp->res, "Chassis", chassisID);
188         return;
189     }
190 
191     BMCWEB_LOG_DEBUG("Get properties for PCIeSlots associated to chassis = {}",
192                      chassisID);
193 
194     asyncResp->res.jsonValue["@odata.type"] = "#PCIeSlots.v1_4_1.PCIeSlots";
195     asyncResp->res.jsonValue["Name"] = "PCIe Slot Information";
196     asyncResp->res.jsonValue["@odata.id"] =
197         boost::urls::format("/redfish/v1/Chassis/{}/PCIeSlots", chassisID);
198     asyncResp->res.jsonValue["Id"] = "1";
199     asyncResp->res.jsonValue["Slots"] = nlohmann::json::array();
200 
201     for (const auto& pathServicePair : subtree)
202     {
203         const std::string& pcieSlotPath = pathServicePair.first;
204         for (const auto& connectionInterfacePair : pathServicePair.second)
205         {
206             const std::string& connectionName = connectionInterfacePair.first;
207             sdbusplus::message::object_path pcieSlotAssociationPath(
208                 pcieSlotPath);
209             pcieSlotAssociationPath /= "chassis";
210 
211             // The association of this PCIeSlot is used to determine whether
212             // it belongs to this ChassisID
213             dbus::utility::getAssociationEndPoints(
214                 std::string{pcieSlotAssociationPath},
215                 [asyncResp, chassisID, pcieSlotPath, connectionName](
216                     const boost::system::error_code& ec2,
217                     const dbus::utility::MapperEndPoints& endpoints) {
218                     onMapperAssociationDone(asyncResp, chassisID, pcieSlotPath,
219                                             connectionName, ec2, endpoints);
220                 });
221         }
222     }
223 }
224 
handlePCIeSlotCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisID)225 inline void handlePCIeSlotCollectionGet(
226     crow::App& app, const crow::Request& req,
227     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
228     const std::string& chassisID)
229 {
230     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
231     {
232         return;
233     }
234 
235     constexpr std::array<std::string_view, 1> interfaces = {
236         "xyz.openbmc_project.Inventory.Item.PCIeSlot"};
237     dbus::utility::getSubTree(
238         "/xyz/openbmc_project/inventory", 0, interfaces,
239         [asyncResp,
240          chassisID](const boost::system::error_code& ec,
241                     const dbus::utility::MapperGetSubTreeResponse& subtree) {
242             onMapperSubtreeDone(asyncResp, chassisID, ec, subtree);
243         });
244 }
245 
requestRoutesPCIeSlots(App & app)246 inline void requestRoutesPCIeSlots(App& app)
247 {
248     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PCIeSlots/")
249         .privileges(redfish::privileges::getPCIeSlots)
250         .methods(boost::beast::http::verb::get)(
251             std::bind_front(handlePCIeSlotCollectionGet, std::ref(app)));
252 }
253 
254 } // namespace redfish
255