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