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 <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_slots::SlotTypes dbusSlotTypeToRf(const std::string& slotType)
24 {
25     if (slotType ==
26         "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.FullLength")
27     {
28         return pcie_slots::SlotTypes::FullLength;
29     }
30     if (slotType ==
31         "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.HalfLength")
32     {
33         return pcie_slots::SlotTypes::HalfLength;
34     }
35     if (slotType ==
36         "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.LowProfile")
37     {
38         return pcie_slots::SlotTypes::LowProfile;
39     }
40     if (slotType ==
41         "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.Mini")
42     {
43         return pcie_slots::SlotTypes::Mini;
44     }
45     if (slotType == "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.M_2")
46     {
47         return pcie_slots::SlotTypes::M2;
48     }
49     if (slotType == "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.OEM")
50     {
51         return pcie_slots::SlotTypes::OEM;
52     }
53     if (slotType ==
54         "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.OCP3Small")
55     {
56         return pcie_slots::SlotTypes::OCP3Small;
57     }
58     if (slotType ==
59         "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.OCP3Large")
60     {
61         return pcie_slots::SlotTypes::OCP3Large;
62     }
63     if (slotType == "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.U_2")
64     {
65         return pcie_slots::SlotTypes::U2;
66     }
67 
68     // Unknown or others
69     return pcie_slots::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         slot["Lanes"] = *lanes;
128     }
129 
130     if (slotType != nullptr)
131     {
132         pcie_slots::SlotTypes redfishSlotType = dbusSlotTypeToRf(*slotType);
133         if (redfishSlotType == pcie_slots::SlotTypes::Invalid)
134         {
135             messages::internalError(asyncResp->res);
136             return;
137         }
138         slot["SlotType"] = redfishSlotType;
139     }
140 
141     if (hotPluggable != nullptr)
142     {
143         slot["HotPluggable"] = *hotPluggable;
144     }
145 
146     slots.emplace_back(std::move(slot));
147 }
148 
149 inline void onMapperAssociationDone(
150     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
151     const std::string& chassisID, const std::string& pcieSlotPath,
152     const std::string& connectionName, const boost::system::error_code& ec,
153     const dbus::utility::MapperEndPoints& pcieSlotChassis)
154 {
155     if (ec)
156     {
157         if (ec.value() == EBADR)
158         {
159             // This PCIeSlot have no chassis association.
160             return;
161         }
162         BMCWEB_LOG_ERROR << "DBUS response error";
163         messages::internalError(asyncResp->res);
164         return;
165     }
166 
167     if (pcieSlotChassis.size() != 1)
168     {
169         BMCWEB_LOG_ERROR << "PCIe Slot association error! ";
170         messages::internalError(asyncResp->res);
171         return;
172     }
173 
174     sdbusplus::message::object_path path(pcieSlotChassis[0]);
175     std::string chassisName = path.filename();
176     if (chassisName != chassisID)
177     {
178         // The pcie slot doesn't belong to the chassisID
179         return;
180     }
181 
182     sdbusplus::asio::getAllProperties(
183         *crow::connections::systemBus, connectionName, pcieSlotPath,
184         "xyz.openbmc_project.Inventory.Item.PCIeSlot",
185         [asyncResp](const boost::system::error_code& ec2,
186                     const dbus::utility::DBusPropertiesMap& propertiesList) {
187         onPcieSlotGetAllDone(asyncResp, ec2, propertiesList);
188         });
189 }
190 
191 inline void
192     onMapperSubtreeDone(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
193                         const std::string& chassisID,
194                         const boost::system::error_code& ec,
195                         const dbus::utility::MapperGetSubTreeResponse& subtree)
196 {
197     if (ec)
198     {
199         BMCWEB_LOG_ERROR << "D-Bus response error on GetSubTree " << ec;
200         messages::internalError(asyncResp->res);
201         return;
202     }
203     if (subtree.empty())
204     {
205         messages::resourceNotFound(asyncResp->res, "Chassis", chassisID);
206         return;
207     }
208 
209     BMCWEB_LOG_DEBUG << "Get properties for PCIeSlots associated to chassis = "
210                      << chassisID;
211 
212     asyncResp->res.jsonValue["@odata.type"] = "#PCIeSlots.v1_4_1.PCIeSlots";
213     asyncResp->res.jsonValue["Name"] = "PCIe Slot Information";
214     asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
215         "redfish", "v1", "Chassis", chassisID, "PCIeSlots");
216     asyncResp->res.jsonValue["Id"] = "1";
217     asyncResp->res.jsonValue["Slots"] = nlohmann::json::array();
218 
219     for (const auto& pathServicePair : subtree)
220     {
221         const std::string& pcieSlotPath = pathServicePair.first;
222         for (const auto& connectionInterfacePair : pathServicePair.second)
223         {
224             const std::string& connectionName = connectionInterfacePair.first;
225             sdbusplus::message::object_path pcieSlotAssociationPath(
226                 pcieSlotPath);
227             pcieSlotAssociationPath /= "chassis";
228 
229             // The association of this PCIeSlot is used to determine whether
230             // it belongs to this ChassisID
231             dbus::utility::getAssociationEndPoints(
232                 std::string{pcieSlotAssociationPath},
233                 [asyncResp, chassisID, pcieSlotPath, connectionName](
234                     const boost::system::error_code& ec2,
235                     const dbus::utility::MapperEndPoints& endpoints) {
236                 onMapperAssociationDone(asyncResp, chassisID, pcieSlotPath,
237                                         connectionName, ec2, endpoints);
238                 });
239         }
240     }
241 }
242 
243 inline void handlePCIeSlotCollectionGet(
244     crow::App& app, const crow::Request& req,
245     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
246     const std::string& chassisID)
247 {
248     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
249     {
250         return;
251     }
252 
253     constexpr std::array<std::string_view, 1> interfaces = {
254         "xyz.openbmc_project.Inventory.Item.PCIeSlot"};
255     dbus::utility::getSubTree(
256         "/xyz/openbmc_project/inventory", 0, interfaces,
257         [asyncResp,
258          chassisID](const boost::system::error_code& ec,
259                     const dbus::utility::MapperGetSubTreeResponse& subtree) {
260         onMapperSubtreeDone(asyncResp, chassisID, ec, subtree);
261         });
262 }
263 
264 inline void requestRoutesPCIeSlots(App& app)
265 {
266     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PCIeSlots/")
267         .privileges(redfish::privileges::getPCIeSlots)
268         .methods(boost::beast::http::verb::get)(
269             std::bind_front(handlePCIeSlotCollectionGet, std::ref(app)));
270 }
271 
272 } // namespace redfish
273