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