1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 17 #pragma once 18 19 #include "generated/enums/pcie_device.hpp" 20 21 #include <app.hpp> 22 #include <boost/system/linux_error.hpp> 23 #include <dbus_utility.hpp> 24 #include <query.hpp> 25 #include <registries/privilege_registry.hpp> 26 #include <sdbusplus/asio/property.hpp> 27 #include <sdbusplus/unpack_properties.hpp> 28 #include <utils/dbus_utils.hpp> 29 30 namespace redfish 31 { 32 33 static constexpr char const* pcieService = "xyz.openbmc_project.PCIe"; 34 static constexpr char const* pciePath = "/xyz/openbmc_project/PCIe"; 35 static constexpr char const* pcieDeviceInterface = 36 "xyz.openbmc_project.PCIe.Device"; 37 38 static inline void 39 getPCIeDeviceList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 40 const std::string& name) 41 { 42 auto getPCIeMapCallback = 43 [asyncResp, name](const boost::system::error_code ec, 44 const dbus::utility::MapperGetSubTreePathsResponse& 45 pcieDevicePaths) { 46 if (ec) 47 { 48 BMCWEB_LOG_DEBUG << "no PCIe device paths found ec: " 49 << ec.message(); 50 // Not an error, system just doesn't have PCIe info 51 return; 52 } 53 nlohmann::json& pcieDeviceList = asyncResp->res.jsonValue[name]; 54 pcieDeviceList = nlohmann::json::array(); 55 for (const std::string& pcieDevicePath : pcieDevicePaths) 56 { 57 size_t devStart = pcieDevicePath.rfind('/'); 58 if (devStart == std::string::npos) 59 { 60 continue; 61 } 62 63 std::string devName = pcieDevicePath.substr(devStart + 1); 64 if (devName.empty()) 65 { 66 continue; 67 } 68 nlohmann::json::object_t pcieDevice; 69 pcieDevice["@odata.id"] = 70 "/redfish/v1/Systems/system/PCIeDevices/" + devName; 71 pcieDeviceList.push_back(std::move(pcieDevice)); 72 } 73 asyncResp->res.jsonValue[name + "@odata.count"] = pcieDeviceList.size(); 74 }; 75 crow::connections::systemBus->async_method_call( 76 std::move(getPCIeMapCallback), "xyz.openbmc_project.ObjectMapper", 77 "/xyz/openbmc_project/object_mapper", 78 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 79 std::string(pciePath) + "/", 1, std::array<std::string, 0>()); 80 } 81 82 inline void requestRoutesSystemPCIeDeviceCollection(App& app) 83 { 84 /** 85 * Functions triggers appropriate requests on DBus 86 */ 87 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/") 88 .privileges(redfish::privileges::getPCIeDeviceCollection) 89 .methods(boost::beast::http::verb::get)( 90 [&app](const crow::Request& req, 91 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 92 const std::string& systemName) { 93 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 94 { 95 return; 96 } 97 if (systemName != "system") 98 { 99 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 100 systemName); 101 return; 102 } 103 104 asyncResp->res.jsonValue["@odata.type"] = 105 "#PCIeDeviceCollection.PCIeDeviceCollection"; 106 asyncResp->res.jsonValue["@odata.id"] = 107 "/redfish/v1/Systems/system/PCIeDevices"; 108 asyncResp->res.jsonValue["Name"] = "PCIe Device Collection"; 109 asyncResp->res.jsonValue["Description"] = "Collection of PCIe Devices"; 110 asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 111 asyncResp->res.jsonValue["Members@odata.count"] = 0; 112 getPCIeDeviceList(asyncResp, "Members"); 113 }); 114 } 115 116 inline std::optional<pcie_device::PCIeTypes> 117 redfishPcieGenerationFromDbus(const std::string& generationInUse) 118 { 119 if (generationInUse == 120 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen1") 121 { 122 return pcie_device::PCIeTypes::Gen1; 123 } 124 if (generationInUse == 125 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen2") 126 { 127 return pcie_device::PCIeTypes::Gen2; 128 } 129 if (generationInUse == 130 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen3") 131 { 132 return pcie_device::PCIeTypes::Gen3; 133 } 134 if (generationInUse == 135 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen4") 136 { 137 return pcie_device::PCIeTypes::Gen4; 138 } 139 if (generationInUse == 140 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen5") 141 { 142 return pcie_device::PCIeTypes::Gen5; 143 } 144 if (generationInUse.empty() || 145 generationInUse == 146 "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown") 147 { 148 return pcie_device::PCIeTypes::Invalid; 149 } 150 151 // The value is not unknown or Gen1-5, need return an internal error. 152 return std::nullopt; 153 } 154 155 inline void requestRoutesSystemPCIeDevice(App& app) 156 { 157 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/") 158 .privileges(redfish::privileges::getPCIeDevice) 159 .methods(boost::beast::http::verb::get)( 160 [&app](const crow::Request& req, 161 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 162 const std::string& systemName, const std::string& device) { 163 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 164 { 165 return; 166 } 167 if (systemName != "system") 168 { 169 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 170 systemName); 171 return; 172 } 173 174 auto getPCIeDeviceCallback = 175 [asyncResp, device]( 176 const boost::system::error_code ec, 177 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 178 if (ec) 179 { 180 BMCWEB_LOG_DEBUG 181 << "failed to get PCIe Device properties ec: " << ec.value() 182 << ": " << ec.message(); 183 if (ec.value() == 184 boost::system::linux_error::bad_request_descriptor) 185 { 186 messages::resourceNotFound(asyncResp->res, "PCIeDevice", 187 device); 188 } 189 else 190 { 191 messages::internalError(asyncResp->res); 192 } 193 return; 194 } 195 196 const std::string* manufacturer = nullptr; 197 const std::string* deviceType = nullptr; 198 const std::string* generationInUse = nullptr; 199 200 const bool success = sdbusplus::unpackPropertiesNoThrow( 201 dbus_utils::UnpackErrorPrinter(), pcieDevProperties, 202 "Manufacturer", manufacturer, "DeviceType", deviceType, 203 "GenerationInUse", generationInUse); 204 205 if (!success) 206 { 207 messages::internalError(asyncResp->res); 208 return; 209 } 210 211 if (generationInUse != nullptr) 212 { 213 std::optional<pcie_device::PCIeTypes> redfishGenerationInUse = 214 redfishPcieGenerationFromDbus(*generationInUse); 215 if (!redfishGenerationInUse) 216 { 217 messages::internalError(asyncResp->res); 218 return; 219 } 220 if (*redfishGenerationInUse != pcie_device::PCIeTypes::Invalid) 221 { 222 asyncResp->res.jsonValue["PCIeInterface"]["PCIeType"] = 223 *redfishGenerationInUse; 224 } 225 } 226 227 if (manufacturer != nullptr) 228 { 229 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; 230 } 231 232 if (deviceType != nullptr) 233 { 234 asyncResp->res.jsonValue["DeviceType"] = *deviceType; 235 } 236 237 asyncResp->res.jsonValue["@odata.type"] = 238 "#PCIeDevice.v1_4_0.PCIeDevice"; 239 asyncResp->res.jsonValue["@odata.id"] = 240 "/redfish/v1/Systems/system/PCIeDevices/" + device; 241 asyncResp->res.jsonValue["Name"] = "PCIe Device"; 242 asyncResp->res.jsonValue["Id"] = device; 243 244 asyncResp->res.jsonValue["PCIeFunctions"]["@odata.id"] = 245 "/redfish/v1/Systems/system/PCIeDevices/" + device + 246 "/PCIeFunctions"; 247 }; 248 std::string escapedPath = std::string(pciePath) + "/" + device; 249 dbus::utility::escapePathForDbus(escapedPath); 250 sdbusplus::asio::getAllProperties( 251 *crow::connections::systemBus, pcieService, escapedPath, 252 pcieDeviceInterface, std::move(getPCIeDeviceCallback)); 253 }); 254 } 255 256 inline void requestRoutesSystemPCIeFunctionCollection(App& app) 257 { 258 /** 259 * Functions triggers appropriate requests on DBus 260 */ 261 BMCWEB_ROUTE(app, 262 "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/") 263 .privileges(redfish::privileges::getPCIeFunctionCollection) 264 .methods(boost::beast::http::verb::get)( 265 [&app](const crow::Request& req, 266 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 267 const std::string& device) { 268 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 269 { 270 return; 271 } 272 273 asyncResp->res.jsonValue["@odata.type"] = 274 "#PCIeFunctionCollection.PCIeFunctionCollection"; 275 asyncResp->res.jsonValue["@odata.id"] = 276 "/redfish/v1/Systems/system/PCIeDevices/" + device + 277 "/PCIeFunctions"; 278 asyncResp->res.jsonValue["Name"] = "PCIe Function Collection"; 279 asyncResp->res.jsonValue["Description"] = 280 "Collection of PCIe Functions for PCIe Device " + device; 281 282 auto getPCIeDeviceCallback = 283 [asyncResp, device]( 284 const boost::system::error_code ec, 285 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 286 if (ec) 287 { 288 BMCWEB_LOG_DEBUG 289 << "failed to get PCIe Device properties ec: " << ec.value() 290 << ": " << ec.message(); 291 if (ec.value() == 292 boost::system::linux_error::bad_request_descriptor) 293 { 294 messages::resourceNotFound(asyncResp->res, "PCIeDevice", 295 device); 296 } 297 else 298 { 299 messages::internalError(asyncResp->res); 300 } 301 return; 302 } 303 304 nlohmann::json& pcieFunctionList = 305 asyncResp->res.jsonValue["Members"]; 306 pcieFunctionList = nlohmann::json::array(); 307 static constexpr const int maxPciFunctionNum = 8; 308 for (int functionNum = 0; functionNum < maxPciFunctionNum; 309 functionNum++) 310 { 311 // Check if this function exists by looking for a 312 // device ID 313 std::string devIDProperty = 314 "Function" + std::to_string(functionNum) + "DeviceId"; 315 const std::string* property = nullptr; 316 for (const auto& propEntry : pcieDevProperties) 317 { 318 if (propEntry.first == devIDProperty) 319 { 320 property = std::get_if<std::string>(&propEntry.second); 321 } 322 } 323 if (property == nullptr || property->empty()) 324 { 325 continue; 326 } 327 nlohmann::json::object_t pcieFunction; 328 pcieFunction["@odata.id"] = 329 "/redfish/v1/Systems/system/PCIeDevices/" + device + 330 "/PCIeFunctions/" + std::to_string(functionNum); 331 pcieFunctionList.push_back(std::move(pcieFunction)); 332 } 333 asyncResp->res.jsonValue["Members@odata.count"] = 334 pcieFunctionList.size(); 335 }; 336 std::string escapedPath = std::string(pciePath) + "/" + device; 337 dbus::utility::escapePathForDbus(escapedPath); 338 sdbusplus::asio::getAllProperties( 339 *crow::connections::systemBus, pcieService, escapedPath, 340 pcieDeviceInterface, std::move(getPCIeDeviceCallback)); 341 }); 342 } 343 344 inline void requestRoutesSystemPCIeFunction(App& app) 345 { 346 BMCWEB_ROUTE( 347 app, 348 "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/<str>/") 349 .privileges(redfish::privileges::getPCIeFunction) 350 .methods(boost::beast::http::verb::get)( 351 [&app](const crow::Request& req, 352 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 353 const std::string& device, const std::string& function) { 354 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 355 { 356 return; 357 } 358 auto getPCIeDeviceCallback = 359 [asyncResp, device, function]( 360 const boost::system::error_code ec, 361 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 362 if (ec) 363 { 364 BMCWEB_LOG_DEBUG 365 << "failed to get PCIe Device properties ec: " << ec.value() 366 << ": " << ec.message(); 367 if (ec.value() == 368 boost::system::linux_error::bad_request_descriptor) 369 { 370 messages::resourceNotFound(asyncResp->res, "PCIeDevice", 371 device); 372 } 373 else 374 { 375 messages::internalError(asyncResp->res); 376 } 377 return; 378 } 379 380 // Check if this function exists by looking for a device 381 // ID 382 std::string functionName = "Function" + function; 383 std::string devIDProperty = functionName + "DeviceId"; 384 385 const std::string* devIdProperty = nullptr; 386 for (const auto& property : pcieDevProperties) 387 { 388 if (property.first == devIDProperty) 389 { 390 devIdProperty = std::get_if<std::string>(&property.second); 391 continue; 392 } 393 } 394 if (devIdProperty == nullptr || devIdProperty->empty()) 395 { 396 messages::resourceNotFound(asyncResp->res, "PCIeFunction", 397 function); 398 return; 399 } 400 401 asyncResp->res.jsonValue["@odata.type"] = 402 "#PCIeFunction.v1_2_0.PCIeFunction"; 403 asyncResp->res.jsonValue["@odata.id"] = 404 "/redfish/v1/Systems/system/PCIeDevices/" + device + 405 "/PCIeFunctions/" + function; 406 asyncResp->res.jsonValue["Name"] = "PCIe Function"; 407 asyncResp->res.jsonValue["Id"] = function; 408 asyncResp->res.jsonValue["FunctionId"] = std::stoi(function); 409 asyncResp->res.jsonValue["Links"]["PCIeDevice"]["@odata.id"] = 410 "/redfish/v1/Systems/system/PCIeDevices/" + device; 411 412 for (const auto& property : pcieDevProperties) 413 { 414 const std::string* strProperty = 415 std::get_if<std::string>(&property.second); 416 if (property.first == functionName + "DeviceId") 417 { 418 asyncResp->res.jsonValue["DeviceId"] = *strProperty; 419 } 420 if (property.first == functionName + "VendorId") 421 { 422 asyncResp->res.jsonValue["VendorId"] = *strProperty; 423 } 424 if (property.first == functionName + "FunctionType") 425 { 426 asyncResp->res.jsonValue["FunctionType"] = *strProperty; 427 } 428 if (property.first == functionName + "DeviceClass") 429 { 430 asyncResp->res.jsonValue["DeviceClass"] = *strProperty; 431 } 432 if (property.first == functionName + "ClassCode") 433 { 434 asyncResp->res.jsonValue["ClassCode"] = *strProperty; 435 } 436 if (property.first == functionName + "RevisionId") 437 { 438 asyncResp->res.jsonValue["RevisionId"] = *strProperty; 439 } 440 if (property.first == functionName + "SubsystemId") 441 { 442 asyncResp->res.jsonValue["SubsystemId"] = *strProperty; 443 } 444 if (property.first == functionName + "SubsystemVendorId") 445 { 446 asyncResp->res.jsonValue["SubsystemVendorId"] = 447 *strProperty; 448 } 449 } 450 }; 451 std::string escapedPath = std::string(pciePath) + "/" + device; 452 dbus::utility::escapePathForDbus(escapedPath); 453 sdbusplus::asio::getAllProperties( 454 *crow::connections::systemBus, pcieService, escapedPath, 455 pcieDeviceInterface, std::move(getPCIeDeviceCallback)); 456 }); 457 } 458 459 } // namespace redfish 460