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