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 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 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 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 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 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