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