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