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"] = crow::utility::urlFromPieces( 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 crow::utility::urlFromPieces("redfish", "v1", "Systems", 246 "system", "PCIeDevices", device); 247 asyncResp->res.jsonValue["Name"] = "PCIe Device"; 248 asyncResp->res.jsonValue["Id"] = device; 249 250 asyncResp->res.jsonValue["PCIeFunctions"]["@odata.id"] = 251 crow::utility::urlFromPieces("redfish", "v1", "Systems", 252 "system", "PCIeDevices", device, 253 "PCIeFunctions"); 254 }; 255 std::string escapedPath = std::string(pciePath) + "/" + device; 256 dbus::utility::escapePathForDbus(escapedPath); 257 sdbusplus::asio::getAllProperties( 258 *crow::connections::systemBus, pcieService, escapedPath, 259 pcieDeviceInterface, std::move(getPCIeDeviceCallback)); 260 }); 261 } 262 263 inline void requestRoutesSystemPCIeFunctionCollection(App& app) 264 { 265 /** 266 * Functions triggers appropriate requests on DBus 267 */ 268 BMCWEB_ROUTE(app, 269 "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/") 270 .privileges(redfish::privileges::getPCIeFunctionCollection) 271 .methods(boost::beast::http::verb::get)( 272 [&app](const crow::Request& req, 273 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 274 const std::string& device) { 275 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 276 { 277 return; 278 } 279 280 asyncResp->res.jsonValue["@odata.type"] = 281 "#PCIeFunctionCollection.PCIeFunctionCollection"; 282 asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( 283 "redfish", "v1", "Systems", "system", "PCIeDevices", device, 284 "PCIeFunctions"); 285 asyncResp->res.jsonValue["Name"] = "PCIe Function Collection"; 286 asyncResp->res.jsonValue["Description"] = 287 "Collection of PCIe Functions for PCIe Device " + device; 288 289 auto getPCIeDeviceCallback = 290 [asyncResp, device]( 291 const boost::system::error_code ec, 292 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 293 if (ec) 294 { 295 BMCWEB_LOG_DEBUG 296 << "failed to get PCIe Device properties ec: " << ec.value() 297 << ": " << ec.message(); 298 if (ec.value() == 299 boost::system::linux_error::bad_request_descriptor) 300 { 301 messages::resourceNotFound(asyncResp->res, "PCIeDevice", 302 device); 303 } 304 else 305 { 306 messages::internalError(asyncResp->res); 307 } 308 return; 309 } 310 311 nlohmann::json& pcieFunctionList = 312 asyncResp->res.jsonValue["Members"]; 313 pcieFunctionList = nlohmann::json::array(); 314 static constexpr const int maxPciFunctionNum = 8; 315 for (int functionNum = 0; functionNum < maxPciFunctionNum; 316 functionNum++) 317 { 318 // Check if this function exists by looking for a 319 // device ID 320 std::string devIDProperty = 321 "Function" + std::to_string(functionNum) + "DeviceId"; 322 const std::string* property = nullptr; 323 for (const auto& propEntry : pcieDevProperties) 324 { 325 if (propEntry.first == devIDProperty) 326 { 327 property = std::get_if<std::string>(&propEntry.second); 328 } 329 } 330 if (property == nullptr || property->empty()) 331 { 332 continue; 333 } 334 nlohmann::json::object_t pcieFunction; 335 pcieFunction["@odata.id"] = crow::utility::urlFromPieces( 336 "redfish", "v1", "Systems", "system", "PCIeDevices", device, 337 "PCIeFunctions", std::to_string(functionNum)); 338 pcieFunctionList.push_back(std::move(pcieFunction)); 339 } 340 asyncResp->res.jsonValue["Members@odata.count"] = 341 pcieFunctionList.size(); 342 }; 343 std::string escapedPath = std::string(pciePath) + "/" + device; 344 dbus::utility::escapePathForDbus(escapedPath); 345 sdbusplus::asio::getAllProperties( 346 *crow::connections::systemBus, pcieService, escapedPath, 347 pcieDeviceInterface, std::move(getPCIeDeviceCallback)); 348 }); 349 } 350 351 inline void requestRoutesSystemPCIeFunction(App& app) 352 { 353 BMCWEB_ROUTE( 354 app, 355 "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/<str>/") 356 .privileges(redfish::privileges::getPCIeFunction) 357 .methods(boost::beast::http::verb::get)( 358 [&app](const crow::Request& req, 359 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 360 const std::string& device, const std::string& function) { 361 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 362 { 363 return; 364 } 365 auto getPCIeDeviceCallback = 366 [asyncResp, device, function]( 367 const boost::system::error_code ec, 368 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 369 if (ec) 370 { 371 BMCWEB_LOG_DEBUG 372 << "failed to get PCIe Device properties ec: " << ec.value() 373 << ": " << ec.message(); 374 if (ec.value() == 375 boost::system::linux_error::bad_request_descriptor) 376 { 377 messages::resourceNotFound(asyncResp->res, "PCIeDevice", 378 device); 379 } 380 else 381 { 382 messages::internalError(asyncResp->res); 383 } 384 return; 385 } 386 387 // Check if this function exists by looking for a device 388 // ID 389 std::string functionName = "Function" + function; 390 std::string devIDProperty = functionName + "DeviceId"; 391 392 const std::string* devIdProperty = nullptr; 393 for (const auto& property : pcieDevProperties) 394 { 395 if (property.first == devIDProperty) 396 { 397 devIdProperty = std::get_if<std::string>(&property.second); 398 continue; 399 } 400 } 401 if (devIdProperty == nullptr || devIdProperty->empty()) 402 { 403 messages::resourceNotFound(asyncResp->res, "PCIeFunction", 404 function); 405 return; 406 } 407 408 asyncResp->res.jsonValue["@odata.type"] = 409 "#PCIeFunction.v1_2_0.PCIeFunction"; 410 asyncResp->res.jsonValue["@odata.id"] = 411 crow::utility::urlFromPieces("redfish", "v1", "Systems", 412 "system", "PCIeDevices", device, 413 "PCIeFunctions", function); 414 asyncResp->res.jsonValue["Name"] = "PCIe Function"; 415 asyncResp->res.jsonValue["Id"] = function; 416 asyncResp->res.jsonValue["FunctionId"] = std::stoi(function); 417 asyncResp->res.jsonValue["Links"]["PCIeDevice"]["@odata.id"] = 418 crow::utility::urlFromPieces("redfish", "v1", "Systems", 419 "system", "PCIeDevices", device); 420 421 for (const auto& property : pcieDevProperties) 422 { 423 const std::string* strProperty = 424 std::get_if<std::string>(&property.second); 425 if (property.first == functionName + "DeviceId") 426 { 427 asyncResp->res.jsonValue["DeviceId"] = *strProperty; 428 } 429 if (property.first == functionName + "VendorId") 430 { 431 asyncResp->res.jsonValue["VendorId"] = *strProperty; 432 } 433 if (property.first == functionName + "FunctionType") 434 { 435 asyncResp->res.jsonValue["FunctionType"] = *strProperty; 436 } 437 if (property.first == functionName + "DeviceClass") 438 { 439 asyncResp->res.jsonValue["DeviceClass"] = *strProperty; 440 } 441 if (property.first == functionName + "ClassCode") 442 { 443 asyncResp->res.jsonValue["ClassCode"] = *strProperty; 444 } 445 if (property.first == functionName + "RevisionId") 446 { 447 asyncResp->res.jsonValue["RevisionId"] = *strProperty; 448 } 449 if (property.first == functionName + "SubsystemId") 450 { 451 asyncResp->res.jsonValue["SubsystemId"] = *strProperty; 452 } 453 if (property.first == functionName + "SubsystemVendorId") 454 { 455 asyncResp->res.jsonValue["SubsystemVendorId"] = 456 *strProperty; 457 } 458 } 459 }; 460 std::string escapedPath = std::string(pciePath) + "/" + device; 461 dbus::utility::escapePathForDbus(escapedPath); 462 sdbusplus::asio::getAllProperties( 463 *crow::connections::systemBus, pcieService, escapedPath, 464 pcieDeviceInterface, std::move(getPCIeDeviceCallback)); 465 }); 466 } 467 468 } // namespace redfish 469