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