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