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