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             messages::internalError(asyncResp->res);
73             return;
74         }
75         if (*pcieType != pcie_device::PCIeTypes::Invalid)
76         {
77             slot["PCIeType"] = *pcieType;
78         }
79     }
80 
81     if (lanes != nullptr)
82     {
83         slot["Lanes"] = *lanes;
84     }
85 
86     if (slotType != nullptr)
87     {
88         std::optional<pcie_slots::SlotTypes> redfishSlotType =
89             pcie_util::dbusSlotTypeToRf(*slotType);
90         if (!redfishSlotType)
91         {
92             messages::internalError(asyncResp->res);
93             return;
94         }
95         if (*redfishSlotType != pcie_slots::SlotTypes::Invalid)
96         {
97             slot["SlotType"] = *redfishSlotType;
98         }
99     }
100 
101     if (hotPluggable != nullptr)
102     {
103         slot["HotPluggable"] = *hotPluggable;
104     }
105 
106     slots.emplace_back(std::move(slot));
107 }
108 
109 inline void onMapperAssociationDone(
110     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
111     const std::string& chassisID, const std::string& pcieSlotPath,
112     const std::string& connectionName, const boost::system::error_code& ec,
113     const dbus::utility::MapperEndPoints& pcieSlotChassis)
114 {
115     if (ec)
116     {
117         if (ec.value() == EBADR)
118         {
119             // This PCIeSlot have no chassis association.
120             return;
121         }
122         BMCWEB_LOG_ERROR << "DBUS response error";
123         messages::internalError(asyncResp->res);
124         return;
125     }
126 
127     if (pcieSlotChassis.size() != 1)
128     {
129         BMCWEB_LOG_ERROR << "PCIe Slot association error! ";
130         messages::internalError(asyncResp->res);
131         return;
132     }
133 
134     sdbusplus::message::object_path path(pcieSlotChassis[0]);
135     std::string chassisName = path.filename();
136     if (chassisName != chassisID)
137     {
138         // The pcie slot doesn't belong to the chassisID
139         return;
140     }
141 
142     sdbusplus::asio::getAllProperties(
143         *crow::connections::systemBus, connectionName, pcieSlotPath,
144         "xyz.openbmc_project.Inventory.Item.PCIeSlot",
145         [asyncResp](const boost::system::error_code& ec2,
146                     const dbus::utility::DBusPropertiesMap& propertiesList) {
147         onPcieSlotGetAllDone(asyncResp, ec2, propertiesList);
148         });
149 }
150 
151 inline void
152     onMapperSubtreeDone(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
153                         const std::string& chassisID,
154                         const boost::system::error_code& ec,
155                         const dbus::utility::MapperGetSubTreeResponse& subtree)
156 {
157     if (ec)
158     {
159         BMCWEB_LOG_ERROR << "D-Bus response error on GetSubTree " << ec;
160         messages::internalError(asyncResp->res);
161         return;
162     }
163     if (subtree.empty())
164     {
165         messages::resourceNotFound(asyncResp->res, "Chassis", chassisID);
166         return;
167     }
168 
169     BMCWEB_LOG_DEBUG << "Get properties for PCIeSlots associated to chassis = "
170                      << chassisID;
171 
172     asyncResp->res.jsonValue["@odata.type"] = "#PCIeSlots.v1_4_1.PCIeSlots";
173     asyncResp->res.jsonValue["Name"] = "PCIe Slot Information";
174     asyncResp->res.jsonValue["@odata.id"] =
175         boost::urls::format("/redfish/v1/Chassis/{}/PCIeSlots", chassisID);
176     asyncResp->res.jsonValue["Id"] = "1";
177     asyncResp->res.jsonValue["Slots"] = nlohmann::json::array();
178 
179     for (const auto& pathServicePair : subtree)
180     {
181         const std::string& pcieSlotPath = pathServicePair.first;
182         for (const auto& connectionInterfacePair : pathServicePair.second)
183         {
184             const std::string& connectionName = connectionInterfacePair.first;
185             sdbusplus::message::object_path pcieSlotAssociationPath(
186                 pcieSlotPath);
187             pcieSlotAssociationPath /= "chassis";
188 
189             // The association of this PCIeSlot is used to determine whether
190             // it belongs to this ChassisID
191             dbus::utility::getAssociationEndPoints(
192                 std::string{pcieSlotAssociationPath},
193                 [asyncResp, chassisID, pcieSlotPath, connectionName](
194                     const boost::system::error_code& ec2,
195                     const dbus::utility::MapperEndPoints& endpoints) {
196                 onMapperAssociationDone(asyncResp, chassisID, pcieSlotPath,
197                                         connectionName, ec2, endpoints);
198                 });
199         }
200     }
201 }
202 
203 inline void handlePCIeSlotCollectionGet(
204     crow::App& app, const crow::Request& req,
205     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
206     const std::string& chassisID)
207 {
208     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
209     {
210         return;
211     }
212 
213     constexpr std::array<std::string_view, 1> interfaces = {
214         "xyz.openbmc_project.Inventory.Item.PCIeSlot"};
215     dbus::utility::getSubTree(
216         "/xyz/openbmc_project/inventory", 0, interfaces,
217         [asyncResp,
218          chassisID](const boost::system::error_code& ec,
219                     const dbus::utility::MapperGetSubTreeResponse& subtree) {
220         onMapperSubtreeDone(asyncResp, chassisID, ec, subtree);
221         });
222 }
223 
224 inline void requestRoutesPCIeSlots(App& app)
225 {
226     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PCIeSlots/")
227         .privileges(redfish::privileges::getPCIeSlots)
228         .methods(boost::beast::http::verb::get)(
229             std::bind_front(handlePCIeSlotCollectionGet, std::ref(app)));
230 }
231 
232 } // namespace redfish
233