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