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