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