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