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