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