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