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