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