1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation 4 5 #pragma once 6 7 #include "bmcweb_config.h" 8 9 #include "app.hpp" 10 #include "async_resp.hpp" 11 #include "dbus_utility.hpp" 12 #include "error_messages.hpp" 13 #include "generated/enums/pcie_device.hpp" 14 #include "generated/enums/pcie_slots.hpp" 15 #include "generated/enums/resource.hpp" 16 #include "http_request.hpp" 17 #include "logging.hpp" 18 #include "query.hpp" 19 #include "registries/privilege_registry.hpp" 20 #include "utils/asset_utils.hpp" 21 #include "utils/dbus_utils.hpp" 22 #include "utils/pcie_util.hpp" 23 24 #include <asm-generic/errno.h> 25 26 #include <boost/beast/http/field.hpp> 27 #include <boost/beast/http/verb.hpp> 28 #include <boost/url/format.hpp> 29 #include <sdbusplus/unpack_properties.hpp> 30 31 #include <array> 32 #include <charconv> 33 #include <cstddef> 34 #include <cstdint> 35 #include <format> 36 #include <functional> 37 #include <limits> 38 #include <memory> 39 #include <optional> 40 #include <string> 41 #include <string_view> 42 #include <system_error> 43 #include <utility> 44 #include <variant> 45 46 namespace redfish 47 { 48 49 static constexpr const char* inventoryPath = "/xyz/openbmc_project/inventory"; 50 static constexpr std::array<std::string_view, 1> pcieDeviceInterface = { 51 "xyz.openbmc_project.Inventory.Item.PCIeDevice"}; 52 static constexpr std::array<std::string_view, 1> pcieSlotInterface = { 53 "xyz.openbmc_project.Inventory.Item.PCIeSlot"}; 54 55 inline void handlePCIeDevicePath( 56 const std::string& pcieDeviceId, 57 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 58 const dbus::utility::MapperGetSubTreePathsResponse& pcieDevicePaths, 59 const std::function<void(const std::string& pcieDevicePath, 60 const std::string& service)>& callback) 61 62 { 63 for (const std::string& pcieDevicePath : pcieDevicePaths) 64 { 65 std::string pciecDeviceName = 66 sdbusplus::message::object_path(pcieDevicePath).filename(); 67 if (pciecDeviceName.empty() || pciecDeviceName != pcieDeviceId) 68 { 69 continue; 70 } 71 72 dbus::utility::getDbusObject( 73 pcieDevicePath, pcieDeviceInterface, 74 [pcieDevicePath, asyncResp, 75 callback](const boost::system::error_code& ec, 76 const dbus::utility::MapperGetObject& object) { 77 if (ec || object.empty()) 78 { 79 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 80 messages::internalError(asyncResp->res); 81 return; 82 } 83 callback(pcieDevicePath, object.begin()->first); 84 }); 85 return; 86 } 87 88 BMCWEB_LOG_WARNING("PCIe Device not found"); 89 messages::resourceNotFound(asyncResp->res, "PCIeDevice", pcieDeviceId); 90 } 91 92 inline void getValidPCIeDevicePath( 93 const std::string& pcieDeviceId, 94 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 95 const std::function<void(const std::string& pcieDevicePath, 96 const std::string& service)>& callback) 97 { 98 dbus::utility::getSubTreePaths( 99 inventoryPath, 0, pcieDeviceInterface, 100 [pcieDeviceId, asyncResp, 101 callback](const boost::system::error_code& ec, 102 const dbus::utility::MapperGetSubTreePathsResponse& 103 pcieDevicePaths) { 104 if (ec) 105 { 106 BMCWEB_LOG_ERROR("D-Bus response error on GetSubTree {}", ec); 107 messages::internalError(asyncResp->res); 108 return; 109 } 110 handlePCIeDevicePath(pcieDeviceId, asyncResp, pcieDevicePaths, 111 callback); 112 return; 113 }); 114 } 115 116 inline void handlePCIeDeviceCollectionGet( 117 crow::App& app, const crow::Request& req, 118 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 119 const std::string& systemName) 120 { 121 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 122 { 123 return; 124 } 125 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 126 { 127 // Option currently returns no systems. TBD 128 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 129 systemName); 130 return; 131 } 132 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 133 { 134 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 135 systemName); 136 return; 137 } 138 139 asyncResp->res.addHeader(boost::beast::http::field::link, 140 "</redfish/v1/JsonSchemas/PCIeDeviceCollection/" 141 "PCIeDeviceCollection.json>; rel=describedby"); 142 asyncResp->res.jsonValue["@odata.type"] = 143 "#PCIeDeviceCollection.PCIeDeviceCollection"; 144 asyncResp->res.jsonValue["@odata.id"] = std::format( 145 "/redfish/v1/Systems/{}/PCIeDevices", BMCWEB_REDFISH_SYSTEM_URI_NAME); 146 asyncResp->res.jsonValue["Name"] = "PCIe Device Collection"; 147 asyncResp->res.jsonValue["Description"] = "Collection of PCIe Devices"; 148 149 pcie_util::getPCIeDeviceList(asyncResp, 150 nlohmann::json::json_pointer("/Members")); 151 } 152 153 inline void requestRoutesSystemPCIeDeviceCollection(App& app) 154 { 155 /** 156 * Functions triggers appropriate requests on DBus 157 */ 158 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/") 159 .privileges(redfish::privileges::getPCIeDeviceCollection) 160 .methods(boost::beast::http::verb::get)( 161 std::bind_front(handlePCIeDeviceCollectionGet, std::ref(app))); 162 } 163 164 inline void addPCIeSlotProperties( 165 crow::Response& res, const boost::system::error_code& ec, 166 const dbus::utility::DBusPropertiesMap& pcieSlotProperties) 167 { 168 if (ec) 169 { 170 BMCWEB_LOG_ERROR("DBUS response error for getAllProperties{}", 171 ec.value()); 172 messages::internalError(res); 173 return; 174 } 175 std::string generation; 176 size_t lanes = 0; 177 std::string slotType; 178 179 bool success = sdbusplus::unpackPropertiesNoThrow( 180 dbus_utils::UnpackErrorPrinter(), pcieSlotProperties, "Generation", 181 generation, "Lanes", lanes, "SlotType", slotType); 182 183 if (!success) 184 { 185 messages::internalError(res); 186 return; 187 } 188 189 std::optional<pcie_device::PCIeTypes> pcieType = 190 pcie_util::redfishPcieGenerationFromDbus(generation); 191 if (!pcieType) 192 { 193 BMCWEB_LOG_WARNING("Unknown PCIeType: {}", generation); 194 } 195 else 196 { 197 if (*pcieType == pcie_device::PCIeTypes::Invalid) 198 { 199 BMCWEB_LOG_ERROR("Invalid PCIeType: {}", generation); 200 messages::internalError(res); 201 return; 202 } 203 res.jsonValue["Slot"]["PCIeType"] = *pcieType; 204 } 205 206 if (lanes != 0) 207 { 208 res.jsonValue["Slot"]["Lanes"] = lanes; 209 } 210 211 std::optional<pcie_slots::SlotTypes> redfishSlotType = 212 pcie_util::dbusSlotTypeToRf(slotType); 213 if (!redfishSlotType) 214 { 215 BMCWEB_LOG_WARNING("Unknown PCIeSlot Type: {}", slotType); 216 } 217 else 218 { 219 if (*redfishSlotType == pcie_slots::SlotTypes::Invalid) 220 { 221 BMCWEB_LOG_ERROR("Invalid PCIeSlot type: {}", slotType); 222 messages::internalError(res); 223 return; 224 } 225 res.jsonValue["Slot"]["SlotType"] = *redfishSlotType; 226 } 227 } 228 229 inline void getPCIeDeviceSlotPath( 230 const std::string& pcieDevicePath, 231 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 232 std::function<void(const std::string& pcieDeviceSlot)>&& callback) 233 { 234 std::string associationPath = pcieDevicePath + "/contained_by"; 235 dbus::utility::getAssociatedSubTreePaths( 236 associationPath, sdbusplus::message::object_path(inventoryPath), 0, 237 pcieSlotInterface, 238 [callback = std::move(callback), asyncResp, pcieDevicePath]( 239 const boost::system::error_code& ec, 240 const dbus::utility::MapperGetSubTreePathsResponse& endpoints) { 241 if (ec) 242 { 243 if (ec.value() == EBADR) 244 { 245 // Missing association is not an error 246 return; 247 } 248 BMCWEB_LOG_ERROR( 249 "DBUS response error for getAssociatedSubTreePaths {}", 250 ec.value()); 251 messages::internalError(asyncResp->res); 252 return; 253 } 254 if (endpoints.size() > 1) 255 { 256 BMCWEB_LOG_ERROR( 257 "PCIeDevice {} is associated with more than one PCIeSlot: {}", 258 pcieDevicePath, endpoints.size()); 259 for (const std::string& slotPath : endpoints) 260 { 261 BMCWEB_LOG_ERROR("Invalid PCIeSlotPath: {}", slotPath); 262 } 263 messages::internalError(asyncResp->res); 264 return; 265 } 266 if (endpoints.empty()) 267 { 268 // If the device doesn't have an association, return without 269 // PCIe Slot properties 270 BMCWEB_LOG_DEBUG("PCIeDevice is not associated with PCIeSlot"); 271 return; 272 } 273 callback(endpoints[0]); 274 }); 275 } 276 277 inline void afterGetDbusObject( 278 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 279 const std::string& pcieDeviceSlot, const boost::system::error_code& ec, 280 const dbus::utility::MapperGetObject& object) 281 { 282 if (ec || object.empty()) 283 { 284 BMCWEB_LOG_ERROR("DBUS response error for getDbusObject {}", 285 ec.value()); 286 messages::internalError(asyncResp->res); 287 return; 288 } 289 dbus::utility::getAllProperties( 290 object.begin()->first, pcieDeviceSlot, 291 "xyz.openbmc_project.Inventory.Item.PCIeSlot", 292 [asyncResp]( 293 const boost::system::error_code& ec2, 294 const dbus::utility::DBusPropertiesMap& pcieSlotProperties) { 295 addPCIeSlotProperties(asyncResp->res, ec2, pcieSlotProperties); 296 }); 297 } 298 299 inline void afterGetPCIeDeviceSlotPath( 300 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 301 const std::string& pcieDeviceSlot) 302 { 303 dbus::utility::getDbusObject( 304 pcieDeviceSlot, pcieSlotInterface, 305 [asyncResp, 306 pcieDeviceSlot](const boost::system::error_code& ec, 307 const dbus::utility::MapperGetObject& object) { 308 afterGetDbusObject(asyncResp, pcieDeviceSlot, ec, object); 309 }); 310 } 311 312 inline void getPCIeDeviceHealth( 313 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 314 const std::string& pcieDevicePath, const std::string& service) 315 { 316 dbus::utility::getProperty<bool>( 317 service, pcieDevicePath, 318 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 319 [asyncResp](const boost::system::error_code& ec, const bool value) { 320 if (ec) 321 { 322 if (ec.value() != EBADR) 323 { 324 BMCWEB_LOG_ERROR("DBUS response error for Health {}", 325 ec.value()); 326 messages::internalError(asyncResp->res); 327 } 328 return; 329 } 330 331 if (!value) 332 { 333 asyncResp->res.jsonValue["Status"]["Health"] = 334 resource::Health::Critical; 335 } 336 }); 337 } 338 339 inline void getPCIeDeviceState( 340 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 341 const std::string& pcieDevicePath, const std::string& service) 342 { 343 dbus::utility::getProperty<bool>( 344 service, pcieDevicePath, "xyz.openbmc_project.Inventory.Item", 345 "Present", 346 [asyncResp](const boost::system::error_code& ec, bool value) { 347 if (ec) 348 { 349 if (ec.value() != EBADR) 350 { 351 BMCWEB_LOG_ERROR("DBUS response error for State"); 352 messages::internalError(asyncResp->res); 353 } 354 return; 355 } 356 357 if (!value) 358 { 359 asyncResp->res.jsonValue["Status"]["State"] = 360 resource::State::Absent; 361 } 362 }); 363 } 364 365 inline void addPCIeDeviceProperties( 366 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 367 const std::string& pcieDeviceId, 368 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 369 { 370 const std::string* generationInUse = nullptr; 371 const std::string* generationSupported = nullptr; 372 const size_t* lanesInUse = nullptr; 373 const size_t* maxLanes = nullptr; 374 375 const bool success = sdbusplus::unpackPropertiesNoThrow( 376 dbus_utils::UnpackErrorPrinter(), pcieDevProperties, "GenerationInUse", 377 generationInUse, "GenerationSupported", generationSupported, 378 "LanesInUse", lanesInUse, "MaxLanes", maxLanes); 379 380 if (!success) 381 { 382 messages::internalError(asyncResp->res); 383 return; 384 } 385 386 if (generationInUse != nullptr) 387 { 388 std::optional<pcie_device::PCIeTypes> redfishGenerationInUse = 389 pcie_util::redfishPcieGenerationFromDbus(*generationInUse); 390 391 if (!redfishGenerationInUse) 392 { 393 BMCWEB_LOG_WARNING("Unknown PCIe Device Generation: {}", 394 *generationInUse); 395 } 396 else 397 { 398 if (*redfishGenerationInUse == pcie_device::PCIeTypes::Invalid) 399 { 400 BMCWEB_LOG_ERROR("Invalid PCIe Device Generation: {}", 401 *generationInUse); 402 messages::internalError(asyncResp->res); 403 return; 404 } 405 asyncResp->res.jsonValue["PCIeInterface"]["PCIeType"] = 406 *redfishGenerationInUse; 407 } 408 } 409 410 if (generationSupported != nullptr) 411 { 412 std::optional<pcie_device::PCIeTypes> redfishGenerationSupported = 413 pcie_util::redfishPcieGenerationFromDbus(*generationSupported); 414 415 if (!redfishGenerationSupported) 416 { 417 BMCWEB_LOG_WARNING("Unknown PCIe Device Generation: {}", 418 *generationSupported); 419 } 420 else 421 { 422 if (*redfishGenerationSupported == pcie_device::PCIeTypes::Invalid) 423 { 424 BMCWEB_LOG_ERROR("Invalid PCIe Device Generation: {}", 425 *generationSupported); 426 messages::internalError(asyncResp->res); 427 return; 428 } 429 asyncResp->res.jsonValue["PCIeInterface"]["MaxPCIeType"] = 430 *redfishGenerationSupported; 431 } 432 } 433 434 if (lanesInUse != nullptr) 435 { 436 if (*lanesInUse == std::numeric_limits<size_t>::max()) 437 { 438 // The default value of LanesInUse is "maxint", and the field will 439 // be null if it is a default value. 440 asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] = nullptr; 441 } 442 else 443 { 444 asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] = 445 *lanesInUse; 446 } 447 } 448 // The default value of MaxLanes is 0, and the field will be 449 // left as off if it is a default value. 450 if (maxLanes != nullptr && *maxLanes != 0) 451 { 452 asyncResp->res.jsonValue["PCIeInterface"]["MaxLanes"] = *maxLanes; 453 } 454 455 asyncResp->res.jsonValue["PCIeFunctions"]["@odata.id"] = 456 boost::urls::format( 457 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions", 458 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); 459 } 460 461 inline void getPCIeDeviceProperties( 462 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 463 const std::string& pcieDevicePath, const std::string& service, 464 const std::function<void( 465 const dbus::utility::DBusPropertiesMap& pcieDevProperties)>&& callback) 466 { 467 dbus::utility::getAllProperties( 468 service, pcieDevicePath, 469 "xyz.openbmc_project.Inventory.Item.PCIeDevice", 470 [asyncResp, 471 callback](const boost::system::error_code& ec, 472 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 473 if (ec) 474 { 475 if (ec.value() != EBADR) 476 { 477 BMCWEB_LOG_ERROR("DBUS response error for Properties"); 478 messages::internalError(asyncResp->res); 479 } 480 return; 481 } 482 callback(pcieDevProperties); 483 }); 484 } 485 486 inline void addPCIeDeviceCommonProperties( 487 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 488 const std::string& pcieDeviceId) 489 { 490 asyncResp->res.addHeader( 491 boost::beast::http::field::link, 492 "</redfish/v1/JsonSchemas/PCIeDevice/PCIeDevice.json>; rel=describedby"); 493 asyncResp->res.jsonValue["@odata.type"] = "#PCIeDevice.v1_9_0.PCIeDevice"; 494 asyncResp->res.jsonValue["@odata.id"] = 495 boost::urls::format("/redfish/v1/Systems/{}/PCIeDevices/{}", 496 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); 497 asyncResp->res.jsonValue["Name"] = "PCIe Device"; 498 asyncResp->res.jsonValue["Id"] = pcieDeviceId; 499 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 500 asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; 501 } 502 503 inline void afterGetValidPcieDevicePath( 504 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 505 const std::string& pcieDeviceId, const std::string& pcieDevicePath, 506 const std::string& service) 507 { 508 addPCIeDeviceCommonProperties(asyncResp, pcieDeviceId); 509 asset_utils::getAssetInfo(asyncResp, service, pcieDevicePath, 510 ""_json_pointer, true); 511 getPCIeDeviceState(asyncResp, pcieDevicePath, service); 512 getPCIeDeviceHealth(asyncResp, pcieDevicePath, service); 513 getPCIeDeviceProperties( 514 asyncResp, pcieDevicePath, service, 515 std::bind_front(addPCIeDeviceProperties, asyncResp, pcieDeviceId)); 516 getPCIeDeviceSlotPath( 517 pcieDevicePath, asyncResp, 518 std::bind_front(afterGetPCIeDeviceSlotPath, asyncResp)); 519 } 520 521 inline void handlePCIeDeviceGet( 522 App& app, const crow::Request& req, 523 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 524 const std::string& systemName, const std::string& pcieDeviceId) 525 { 526 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 527 { 528 return; 529 } 530 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 531 { 532 // Option currently returns no systems. TBD 533 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 534 systemName); 535 return; 536 } 537 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 538 { 539 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 540 systemName); 541 return; 542 } 543 544 getValidPCIeDevicePath( 545 pcieDeviceId, asyncResp, 546 std::bind_front(afterGetValidPcieDevicePath, asyncResp, pcieDeviceId)); 547 } 548 549 inline void requestRoutesSystemPCIeDevice(App& app) 550 { 551 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/") 552 .privileges(redfish::privileges::getPCIeDevice) 553 .methods(boost::beast::http::verb::get)( 554 std::bind_front(handlePCIeDeviceGet, std::ref(app))); 555 } 556 557 inline void addPCIeFunctionList( 558 crow::Response& res, const std::string& pcieDeviceId, 559 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 560 { 561 nlohmann::json& pcieFunctionList = res.jsonValue["Members"]; 562 pcieFunctionList = nlohmann::json::array(); 563 static constexpr const int maxPciFunctionNum = 8; 564 565 for (int functionNum = 0; functionNum < maxPciFunctionNum; functionNum++) 566 { 567 // Check if this function exists by 568 // looking for a device ID 569 std::string devIDProperty = 570 "Function" + std::to_string(functionNum) + "DeviceId"; 571 const std::string* property = nullptr; 572 for (const auto& propEntry : pcieDevProperties) 573 { 574 if (propEntry.first == devIDProperty) 575 { 576 property = std::get_if<std::string>(&propEntry.second); 577 break; 578 } 579 } 580 if (property == nullptr || property->empty()) 581 { 582 continue; 583 } 584 585 nlohmann::json::object_t pcieFunction; 586 pcieFunction["@odata.id"] = boost::urls::format( 587 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions/{}", 588 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId, 589 std::to_string(functionNum)); 590 pcieFunctionList.emplace_back(std::move(pcieFunction)); 591 } 592 res.jsonValue["PCIeFunctions@odata.count"] = pcieFunctionList.size(); 593 } 594 595 inline void handlePCIeFunctionCollectionGet( 596 App& app, const crow::Request& req, 597 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 598 const std::string& systemName, const std::string& pcieDeviceId) 599 { 600 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 601 { 602 return; 603 } 604 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 605 { 606 // Option currently returns no systems. TBD 607 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 608 systemName); 609 return; 610 } 611 612 getValidPCIeDevicePath( 613 pcieDeviceId, asyncResp, 614 [asyncResp, pcieDeviceId](const std::string& pcieDevicePath, 615 const std::string& service) { 616 asyncResp->res.addHeader( 617 boost::beast::http::field::link, 618 "</redfish/v1/JsonSchemas/PCIeFunctionCollection/PCIeFunctionCollection.json>; rel=describedby"); 619 asyncResp->res.jsonValue["@odata.type"] = 620 "#PCIeFunctionCollection.PCIeFunctionCollection"; 621 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 622 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions", 623 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); 624 asyncResp->res.jsonValue["Name"] = "PCIe Function Collection"; 625 asyncResp->res.jsonValue["Description"] = 626 "Collection of PCIe Functions for PCIe Device " + pcieDeviceId; 627 getPCIeDeviceProperties( 628 asyncResp, pcieDevicePath, service, 629 [asyncResp, pcieDeviceId]( 630 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 631 addPCIeFunctionList(asyncResp->res, pcieDeviceId, 632 pcieDevProperties); 633 }); 634 }); 635 } 636 637 inline void requestRoutesSystemPCIeFunctionCollection(App& app) 638 { 639 /** 640 * Functions triggers appropriate requests on DBus 641 */ 642 BMCWEB_ROUTE(app, 643 "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/") 644 .privileges(redfish::privileges::getPCIeFunctionCollection) 645 .methods(boost::beast::http::verb::get)( 646 std::bind_front(handlePCIeFunctionCollectionGet, std::ref(app))); 647 } 648 649 inline void addPCIeFunctionProperties( 650 crow::Response& resp, uint64_t pcieFunctionId, 651 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 652 { 653 std::string functionName = "Function" + std::to_string(pcieFunctionId); 654 for (const auto& property : pcieDevProperties) 655 { 656 const std::string* strProperty = 657 std::get_if<std::string>(&property.second); 658 if (strProperty == nullptr) 659 { 660 continue; 661 } 662 if (property.first == functionName + "DeviceId") 663 { 664 resp.jsonValue["DeviceId"] = *strProperty; 665 } 666 if (property.first == functionName + "VendorId") 667 { 668 resp.jsonValue["VendorId"] = *strProperty; 669 } 670 // TODO: FunctionType and DeviceClass are Redfish enums. The D-Bus 671 // property strings should be mapped correctly to ensure these 672 // strings are Redfish enum values. For now just check for empty. 673 if (property.first == functionName + "FunctionType") 674 { 675 if (!strProperty->empty()) 676 { 677 resp.jsonValue["FunctionType"] = *strProperty; 678 } 679 } 680 if (property.first == functionName + "DeviceClass") 681 { 682 if (!strProperty->empty()) 683 { 684 resp.jsonValue["DeviceClass"] = *strProperty; 685 } 686 } 687 if (property.first == functionName + "ClassCode") 688 { 689 resp.jsonValue["ClassCode"] = *strProperty; 690 } 691 if (property.first == functionName + "RevisionId") 692 { 693 resp.jsonValue["RevisionId"] = *strProperty; 694 } 695 if (property.first == functionName + "SubsystemId") 696 { 697 resp.jsonValue["SubsystemId"] = *strProperty; 698 } 699 if (property.first == functionName + "SubsystemVendorId") 700 { 701 resp.jsonValue["SubsystemVendorId"] = *strProperty; 702 } 703 } 704 } 705 706 inline void addPCIeFunctionCommonProperties(crow::Response& resp, 707 const std::string& pcieDeviceId, 708 uint64_t pcieFunctionId) 709 { 710 resp.addHeader( 711 boost::beast::http::field::link, 712 "</redfish/v1/JsonSchemas/PCIeFunction/PCIeFunction.json>; rel=describedby"); 713 resp.jsonValue["@odata.type"] = "#PCIeFunction.v1_2_3.PCIeFunction"; 714 resp.jsonValue["@odata.id"] = boost::urls::format( 715 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions/{}", 716 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId, 717 std::to_string(pcieFunctionId)); 718 resp.jsonValue["Name"] = "PCIe Function"; 719 resp.jsonValue["Id"] = std::to_string(pcieFunctionId); 720 resp.jsonValue["FunctionId"] = pcieFunctionId; 721 resp.jsonValue["Links"]["PCIeDevice"]["@odata.id"] = 722 boost::urls::format("/redfish/v1/Systems/{}/PCIeDevices/{}", 723 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); 724 } 725 726 inline void handlePCIeFunctionGet( 727 App& app, const crow::Request& req, 728 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 729 const std::string& systemName, const std::string& pcieDeviceId, 730 const std::string& pcieFunctionIdStr) 731 { 732 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 733 { 734 return; 735 } 736 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 737 { 738 // Option currently returns no systems. TBD 739 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 740 systemName); 741 return; 742 } 743 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 744 { 745 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 746 systemName); 747 return; 748 } 749 std::string_view pcieFunctionIdView = pcieFunctionIdStr; 750 751 uint64_t pcieFunctionId = 0; 752 std::from_chars_result result = std::from_chars( 753 pcieFunctionIdView.begin(), pcieFunctionIdView.end(), pcieFunctionId); 754 if (result.ec != std::errc{} || result.ptr != pcieFunctionIdView.end()) 755 { 756 messages::resourceNotFound(asyncResp->res, "PCIeFunction", 757 pcieFunctionIdStr); 758 return; 759 } 760 761 getValidPCIeDevicePath( 762 pcieDeviceId, asyncResp, 763 [asyncResp, pcieDeviceId, pcieFunctionId]( 764 const std::string& pcieDevicePath, const std::string& service) { 765 getPCIeDeviceProperties( 766 asyncResp, pcieDevicePath, service, 767 [asyncResp, pcieDeviceId, pcieFunctionId]( 768 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 769 addPCIeFunctionCommonProperties( 770 asyncResp->res, pcieDeviceId, pcieFunctionId); 771 addPCIeFunctionProperties(asyncResp->res, pcieFunctionId, 772 pcieDevProperties); 773 }); 774 }); 775 } 776 777 inline void requestRoutesSystemPCIeFunction(App& app) 778 { 779 BMCWEB_ROUTE( 780 app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/<str>/") 781 .privileges(redfish::privileges::getPCIeFunction) 782 .methods(boost::beast::http::verb::get)( 783 std::bind_front(handlePCIeFunctionGet, std::ref(app))); 784 } 785 786 } // namespace redfish 787