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 sdbusplus::asio::getAllProperties( 152 *crow::connections::systemBus, 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 161 onMapperSubtreeDone(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 162 const std::string& chassisID, 163 const boost::system::error_code& ec, 164 const dbus::utility::MapperGetSubTreeResponse& subtree) 165 { 166 if (ec) 167 { 168 BMCWEB_LOG_ERROR("D-Bus response error on GetSubTree {}", ec); 169 messages::internalError(asyncResp->res); 170 return; 171 } 172 if (subtree.empty()) 173 { 174 messages::resourceNotFound(asyncResp->res, "Chassis", chassisID); 175 return; 176 } 177 178 BMCWEB_LOG_DEBUG("Get properties for PCIeSlots associated to chassis = {}", 179 chassisID); 180 181 asyncResp->res.jsonValue["@odata.type"] = "#PCIeSlots.v1_4_1.PCIeSlots"; 182 asyncResp->res.jsonValue["Name"] = "PCIe Slot Information"; 183 asyncResp->res.jsonValue["@odata.id"] = 184 boost::urls::format("/redfish/v1/Chassis/{}/PCIeSlots", chassisID); 185 asyncResp->res.jsonValue["Id"] = "1"; 186 asyncResp->res.jsonValue["Slots"] = nlohmann::json::array(); 187 188 for (const auto& pathServicePair : subtree) 189 { 190 const std::string& pcieSlotPath = pathServicePair.first; 191 for (const auto& connectionInterfacePair : pathServicePair.second) 192 { 193 const std::string& connectionName = connectionInterfacePair.first; 194 sdbusplus::message::object_path pcieSlotAssociationPath( 195 pcieSlotPath); 196 pcieSlotAssociationPath /= "chassis"; 197 198 // The association of this PCIeSlot is used to determine whether 199 // it belongs to this ChassisID 200 dbus::utility::getAssociationEndPoints( 201 std::string{pcieSlotAssociationPath}, 202 [asyncResp, chassisID, pcieSlotPath, connectionName]( 203 const boost::system::error_code& ec2, 204 const dbus::utility::MapperEndPoints& endpoints) { 205 onMapperAssociationDone(asyncResp, chassisID, pcieSlotPath, 206 connectionName, ec2, endpoints); 207 }); 208 } 209 } 210 } 211 212 inline void handlePCIeSlotCollectionGet( 213 crow::App& app, const crow::Request& req, 214 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 215 const std::string& chassisID) 216 { 217 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 218 { 219 return; 220 } 221 222 constexpr std::array<std::string_view, 1> interfaces = { 223 "xyz.openbmc_project.Inventory.Item.PCIeSlot"}; 224 dbus::utility::getSubTree( 225 "/xyz/openbmc_project/inventory", 0, interfaces, 226 [asyncResp, 227 chassisID](const boost::system::error_code& ec, 228 const dbus::utility::MapperGetSubTreeResponse& subtree) { 229 onMapperSubtreeDone(asyncResp, chassisID, ec, subtree); 230 }); 231 } 232 233 inline void requestRoutesPCIeSlots(App& app) 234 { 235 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PCIeSlots/") 236 .privileges(redfish::privileges::getPCIeSlots) 237 .methods(boost::beast::http::verb::get)( 238 std::bind_front(handlePCIeSlotCollectionGet, std::ref(app))); 239 } 240 241 } // namespace redfish 242