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