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