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