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