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 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 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 dbus::utility::getAllProperties( 152 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 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 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 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