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