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