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