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