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