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 messages::internalError(asyncResp->res); 259 return; 260 } 261 if (endpoints.empty()) 262 { 263 // If the device doesn't have an association, return without 264 // PCIe Slot properties 265 BMCWEB_LOG_DEBUG("PCIeDevice is not associated with PCIeSlot"); 266 return; 267 } 268 callback(endpoints[0]); 269 }); 270 } 271 272 inline void afterGetDbusObject( 273 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 274 const std::string& pcieDeviceSlot, const boost::system::error_code& ec, 275 const dbus::utility::MapperGetObject& object) 276 { 277 if (ec || object.empty()) 278 { 279 BMCWEB_LOG_ERROR("DBUS response error for getDbusObject {}", 280 ec.value()); 281 messages::internalError(asyncResp->res); 282 return; 283 } 284 dbus::utility::getAllProperties( 285 object.begin()->first, pcieDeviceSlot, 286 "xyz.openbmc_project.Inventory.Item.PCIeSlot", 287 [asyncResp]( 288 const boost::system::error_code& ec2, 289 const dbus::utility::DBusPropertiesMap& pcieSlotProperties) { 290 addPCIeSlotProperties(asyncResp->res, ec2, pcieSlotProperties); 291 }); 292 } 293 294 inline void afterGetPCIeDeviceSlotPath( 295 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 296 const std::string& pcieDeviceSlot) 297 { 298 dbus::utility::getDbusObject( 299 pcieDeviceSlot, pcieSlotInterface, 300 [asyncResp, 301 pcieDeviceSlot](const boost::system::error_code& ec, 302 const dbus::utility::MapperGetObject& object) { 303 afterGetDbusObject(asyncResp, pcieDeviceSlot, ec, object); 304 }); 305 } 306 307 inline void getPCIeDeviceHealth( 308 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 309 const std::string& pcieDevicePath, const std::string& service) 310 { 311 dbus::utility::getProperty<bool>( 312 service, pcieDevicePath, 313 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 314 [asyncResp](const boost::system::error_code& ec, const bool value) { 315 if (ec) 316 { 317 if (ec.value() != EBADR) 318 { 319 BMCWEB_LOG_ERROR("DBUS response error for Health {}", 320 ec.value()); 321 messages::internalError(asyncResp->res); 322 } 323 return; 324 } 325 326 if (!value) 327 { 328 asyncResp->res.jsonValue["Status"]["Health"] = 329 resource::Health::Critical; 330 } 331 }); 332 } 333 334 inline void getPCIeDeviceState( 335 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 336 const std::string& pcieDevicePath, const std::string& service) 337 { 338 dbus::utility::getProperty<bool>( 339 service, pcieDevicePath, "xyz.openbmc_project.Inventory.Item", 340 "Present", 341 [asyncResp](const boost::system::error_code& ec, bool value) { 342 if (ec) 343 { 344 if (ec.value() != EBADR) 345 { 346 BMCWEB_LOG_ERROR("DBUS response error for State"); 347 messages::internalError(asyncResp->res); 348 } 349 return; 350 } 351 352 if (!value) 353 { 354 asyncResp->res.jsonValue["Status"]["State"] = 355 resource::State::Absent; 356 } 357 }); 358 } 359 360 inline void getPCIeDeviceAsset( 361 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 362 const std::string& pcieDevicePath, const std::string& service) 363 { 364 dbus::utility::getAllProperties( 365 service, pcieDevicePath, 366 "xyz.openbmc_project.Inventory.Decorator.Asset", 367 [pcieDevicePath, asyncResp{asyncResp}]( 368 const boost::system::error_code& ec, 369 const dbus::utility::DBusPropertiesMap& assetList) { 370 if (ec) 371 { 372 if (ec.value() != EBADR) 373 { 374 BMCWEB_LOG_ERROR("DBUS response error for Properties{}", 375 ec.value()); 376 messages::internalError(asyncResp->res); 377 } 378 return; 379 } 380 381 const std::string* manufacturer = nullptr; 382 const std::string* model = nullptr; 383 const std::string* partNumber = nullptr; 384 const std::string* serialNumber = nullptr; 385 const std::string* sparePartNumber = nullptr; 386 387 const bool success = sdbusplus::unpackPropertiesNoThrow( 388 dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer", 389 manufacturer, "Model", model, "PartNumber", partNumber, 390 "SerialNumber", serialNumber, "SparePartNumber", 391 sparePartNumber); 392 393 if (!success) 394 { 395 messages::internalError(asyncResp->res); 396 return; 397 } 398 399 if (manufacturer != nullptr) 400 { 401 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; 402 } 403 if (model != nullptr) 404 { 405 asyncResp->res.jsonValue["Model"] = *model; 406 } 407 408 if (partNumber != nullptr) 409 { 410 asyncResp->res.jsonValue["PartNumber"] = *partNumber; 411 } 412 413 if (serialNumber != nullptr) 414 { 415 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 416 } 417 418 if (sparePartNumber != nullptr && !sparePartNumber->empty()) 419 { 420 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 421 } 422 }); 423 } 424 425 inline void addPCIeDeviceProperties( 426 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 427 const std::string& pcieDeviceId, 428 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 429 { 430 const std::string* generationInUse = nullptr; 431 const std::string* generationSupported = nullptr; 432 const size_t* lanesInUse = nullptr; 433 const size_t* maxLanes = nullptr; 434 435 const bool success = sdbusplus::unpackPropertiesNoThrow( 436 dbus_utils::UnpackErrorPrinter(), pcieDevProperties, "GenerationInUse", 437 generationInUse, "GenerationSupported", generationSupported, 438 "LanesInUse", lanesInUse, "MaxLanes", maxLanes); 439 440 if (!success) 441 { 442 messages::internalError(asyncResp->res); 443 return; 444 } 445 446 if (generationInUse != nullptr) 447 { 448 std::optional<pcie_device::PCIeTypes> redfishGenerationInUse = 449 pcie_util::redfishPcieGenerationFromDbus(*generationInUse); 450 451 if (!redfishGenerationInUse) 452 { 453 BMCWEB_LOG_WARNING("Unknown PCIe Device Generation: {}", 454 *generationInUse); 455 } 456 else 457 { 458 if (*redfishGenerationInUse == pcie_device::PCIeTypes::Invalid) 459 { 460 BMCWEB_LOG_ERROR("Invalid PCIe Device Generation: {}", 461 *generationInUse); 462 messages::internalError(asyncResp->res); 463 return; 464 } 465 asyncResp->res.jsonValue["PCIeInterface"]["PCIeType"] = 466 *redfishGenerationInUse; 467 } 468 } 469 470 if (generationSupported != nullptr) 471 { 472 std::optional<pcie_device::PCIeTypes> redfishGenerationSupported = 473 pcie_util::redfishPcieGenerationFromDbus(*generationSupported); 474 475 if (!redfishGenerationSupported) 476 { 477 BMCWEB_LOG_WARNING("Unknown PCIe Device Generation: {}", 478 *generationSupported); 479 } 480 else 481 { 482 if (*redfishGenerationSupported == pcie_device::PCIeTypes::Invalid) 483 { 484 BMCWEB_LOG_ERROR("Invalid PCIe Device Generation: {}", 485 *generationSupported); 486 messages::internalError(asyncResp->res); 487 return; 488 } 489 asyncResp->res.jsonValue["PCIeInterface"]["MaxPCIeType"] = 490 *redfishGenerationSupported; 491 } 492 } 493 494 if (lanesInUse != nullptr) 495 { 496 if (*lanesInUse == std::numeric_limits<size_t>::max()) 497 { 498 // The default value of LanesInUse is "maxint", and the field will 499 // be null if it is a default value. 500 asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] = nullptr; 501 } 502 else 503 { 504 asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] = 505 *lanesInUse; 506 } 507 } 508 // The default value of MaxLanes is 0, and the field will be 509 // left as off if it is a default value. 510 if (maxLanes != nullptr && *maxLanes != 0) 511 { 512 asyncResp->res.jsonValue["PCIeInterface"]["MaxLanes"] = *maxLanes; 513 } 514 515 asyncResp->res.jsonValue["PCIeFunctions"]["@odata.id"] = 516 boost::urls::format( 517 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions", 518 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); 519 } 520 521 inline void getPCIeDeviceProperties( 522 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 523 const std::string& pcieDevicePath, const std::string& service, 524 const std::function<void( 525 const dbus::utility::DBusPropertiesMap& pcieDevProperties)>&& callback) 526 { 527 dbus::utility::getAllProperties( 528 service, pcieDevicePath, 529 "xyz.openbmc_project.Inventory.Item.PCIeDevice", 530 [asyncResp, 531 callback](const boost::system::error_code& ec, 532 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 533 if (ec) 534 { 535 if (ec.value() != EBADR) 536 { 537 BMCWEB_LOG_ERROR("DBUS response error for Properties"); 538 messages::internalError(asyncResp->res); 539 } 540 return; 541 } 542 callback(pcieDevProperties); 543 }); 544 } 545 546 inline void addPCIeDeviceCommonProperties( 547 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 548 const std::string& pcieDeviceId) 549 { 550 asyncResp->res.addHeader( 551 boost::beast::http::field::link, 552 "</redfish/v1/JsonSchemas/PCIeDevice/PCIeDevice.json>; rel=describedby"); 553 asyncResp->res.jsonValue["@odata.type"] = "#PCIeDevice.v1_9_0.PCIeDevice"; 554 asyncResp->res.jsonValue["@odata.id"] = 555 boost::urls::format("/redfish/v1/Systems/{}/PCIeDevices/{}", 556 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); 557 asyncResp->res.jsonValue["Name"] = "PCIe Device"; 558 asyncResp->res.jsonValue["Id"] = pcieDeviceId; 559 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 560 asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; 561 } 562 563 inline void afterGetValidPcieDevicePath( 564 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 565 const std::string& pcieDeviceId, const std::string& pcieDevicePath, 566 const std::string& service) 567 { 568 addPCIeDeviceCommonProperties(asyncResp, pcieDeviceId); 569 getPCIeDeviceAsset(asyncResp, pcieDevicePath, service); 570 getPCIeDeviceState(asyncResp, pcieDevicePath, service); 571 getPCIeDeviceHealth(asyncResp, pcieDevicePath, service); 572 getPCIeDeviceProperties( 573 asyncResp, pcieDevicePath, service, 574 std::bind_front(addPCIeDeviceProperties, asyncResp, pcieDeviceId)); 575 getPCIeDeviceSlotPath( 576 pcieDevicePath, asyncResp, 577 std::bind_front(afterGetPCIeDeviceSlotPath, asyncResp)); 578 } 579 580 inline void handlePCIeDeviceGet( 581 App& app, const crow::Request& req, 582 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 583 const std::string& systemName, const std::string& pcieDeviceId) 584 { 585 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 586 { 587 return; 588 } 589 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 590 { 591 // Option currently returns no systems. TBD 592 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 593 systemName); 594 return; 595 } 596 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 597 { 598 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 599 systemName); 600 return; 601 } 602 603 getValidPCIeDevicePath( 604 pcieDeviceId, asyncResp, 605 std::bind_front(afterGetValidPcieDevicePath, asyncResp, pcieDeviceId)); 606 } 607 608 inline void requestRoutesSystemPCIeDevice(App& app) 609 { 610 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/") 611 .privileges(redfish::privileges::getPCIeDevice) 612 .methods(boost::beast::http::verb::get)( 613 std::bind_front(handlePCIeDeviceGet, std::ref(app))); 614 } 615 616 inline void addPCIeFunctionList( 617 crow::Response& res, const std::string& pcieDeviceId, 618 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 619 { 620 nlohmann::json& pcieFunctionList = res.jsonValue["Members"]; 621 pcieFunctionList = nlohmann::json::array(); 622 static constexpr const int maxPciFunctionNum = 8; 623 624 for (int functionNum = 0; functionNum < maxPciFunctionNum; functionNum++) 625 { 626 // Check if this function exists by 627 // looking for a device ID 628 std::string devIDProperty = 629 "Function" + std::to_string(functionNum) + "DeviceId"; 630 const std::string* property = nullptr; 631 for (const auto& propEntry : pcieDevProperties) 632 { 633 if (propEntry.first == devIDProperty) 634 { 635 property = std::get_if<std::string>(&propEntry.second); 636 break; 637 } 638 } 639 if (property == nullptr || property->empty()) 640 { 641 continue; 642 } 643 644 nlohmann::json::object_t pcieFunction; 645 pcieFunction["@odata.id"] = boost::urls::format( 646 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions/{}", 647 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId, 648 std::to_string(functionNum)); 649 pcieFunctionList.emplace_back(std::move(pcieFunction)); 650 } 651 res.jsonValue["PCIeFunctions@odata.count"] = pcieFunctionList.size(); 652 } 653 654 inline void handlePCIeFunctionCollectionGet( 655 App& app, const crow::Request& req, 656 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 657 const std::string& systemName, const std::string& pcieDeviceId) 658 { 659 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 660 { 661 return; 662 } 663 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 664 { 665 // Option currently returns no systems. TBD 666 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 667 systemName); 668 return; 669 } 670 671 getValidPCIeDevicePath( 672 pcieDeviceId, asyncResp, 673 [asyncResp, pcieDeviceId](const std::string& pcieDevicePath, 674 const std::string& service) { 675 asyncResp->res.addHeader( 676 boost::beast::http::field::link, 677 "</redfish/v1/JsonSchemas/PCIeFunctionCollection/PCIeFunctionCollection.json>; rel=describedby"); 678 asyncResp->res.jsonValue["@odata.type"] = 679 "#PCIeFunctionCollection.PCIeFunctionCollection"; 680 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 681 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions", 682 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); 683 asyncResp->res.jsonValue["Name"] = "PCIe Function Collection"; 684 asyncResp->res.jsonValue["Description"] = 685 "Collection of PCIe Functions for PCIe Device " + pcieDeviceId; 686 getPCIeDeviceProperties( 687 asyncResp, pcieDevicePath, service, 688 [asyncResp, pcieDeviceId]( 689 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 690 addPCIeFunctionList(asyncResp->res, pcieDeviceId, 691 pcieDevProperties); 692 }); 693 }); 694 } 695 696 inline void requestRoutesSystemPCIeFunctionCollection(App& app) 697 { 698 /** 699 * Functions triggers appropriate requests on DBus 700 */ 701 BMCWEB_ROUTE(app, 702 "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/") 703 .privileges(redfish::privileges::getPCIeFunctionCollection) 704 .methods(boost::beast::http::verb::get)( 705 std::bind_front(handlePCIeFunctionCollectionGet, std::ref(app))); 706 } 707 708 inline bool validatePCIeFunctionId( 709 uint64_t pcieFunctionId, 710 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 711 { 712 std::string functionName = "Function" + std::to_string(pcieFunctionId); 713 std::string devIDProperty = functionName + "DeviceId"; 714 715 const std::string* devIdProperty = nullptr; 716 for (const auto& property : pcieDevProperties) 717 { 718 if (property.first == devIDProperty) 719 { 720 devIdProperty = std::get_if<std::string>(&property.second); 721 break; 722 } 723 } 724 return (devIdProperty != nullptr && !devIdProperty->empty()); 725 } 726 727 inline void addPCIeFunctionProperties( 728 crow::Response& resp, uint64_t pcieFunctionId, 729 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 730 { 731 std::string functionName = "Function" + std::to_string(pcieFunctionId); 732 for (const auto& property : pcieDevProperties) 733 { 734 const std::string* strProperty = 735 std::get_if<std::string>(&property.second); 736 if (strProperty == nullptr) 737 { 738 continue; 739 } 740 if (property.first == functionName + "DeviceId") 741 { 742 resp.jsonValue["DeviceId"] = *strProperty; 743 } 744 if (property.first == functionName + "VendorId") 745 { 746 resp.jsonValue["VendorId"] = *strProperty; 747 } 748 // TODO: FunctionType and DeviceClass are Redfish enums. The D-Bus 749 // property strings should be mapped correctly to ensure these 750 // strings are Redfish enum values. For now just check for empty. 751 if (property.first == functionName + "FunctionType") 752 { 753 if (!strProperty->empty()) 754 { 755 resp.jsonValue["FunctionType"] = *strProperty; 756 } 757 } 758 if (property.first == functionName + "DeviceClass") 759 { 760 if (!strProperty->empty()) 761 { 762 resp.jsonValue["DeviceClass"] = *strProperty; 763 } 764 } 765 if (property.first == functionName + "ClassCode") 766 { 767 resp.jsonValue["ClassCode"] = *strProperty; 768 } 769 if (property.first == functionName + "RevisionId") 770 { 771 resp.jsonValue["RevisionId"] = *strProperty; 772 } 773 if (property.first == functionName + "SubsystemId") 774 { 775 resp.jsonValue["SubsystemId"] = *strProperty; 776 } 777 if (property.first == functionName + "SubsystemVendorId") 778 { 779 resp.jsonValue["SubsystemVendorId"] = *strProperty; 780 } 781 } 782 } 783 784 inline void addPCIeFunctionCommonProperties(crow::Response& resp, 785 const std::string& pcieDeviceId, 786 uint64_t pcieFunctionId) 787 { 788 resp.addHeader( 789 boost::beast::http::field::link, 790 "</redfish/v1/JsonSchemas/PCIeFunction/PCIeFunction.json>; rel=describedby"); 791 resp.jsonValue["@odata.type"] = "#PCIeFunction.v1_2_3.PCIeFunction"; 792 resp.jsonValue["@odata.id"] = boost::urls::format( 793 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions/{}", 794 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId, 795 std::to_string(pcieFunctionId)); 796 resp.jsonValue["Name"] = "PCIe Function"; 797 resp.jsonValue["Id"] = std::to_string(pcieFunctionId); 798 resp.jsonValue["FunctionId"] = pcieFunctionId; 799 resp.jsonValue["Links"]["PCIeDevice"]["@odata.id"] = 800 boost::urls::format("/redfish/v1/Systems/{}/PCIeDevices/{}", 801 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); 802 } 803 804 inline void handlePCIeFunctionGet( 805 App& app, const crow::Request& req, 806 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 807 const std::string& systemName, const std::string& pcieDeviceId, 808 const std::string& pcieFunctionIdStr) 809 { 810 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 811 { 812 return; 813 } 814 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 815 { 816 // Option currently returns no systems. TBD 817 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 818 systemName); 819 return; 820 } 821 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 822 { 823 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 824 systemName); 825 return; 826 } 827 std::string_view pcieFunctionIdView = pcieFunctionIdStr; 828 829 uint64_t pcieFunctionId = 0; 830 std::from_chars_result result = std::from_chars( 831 pcieFunctionIdView.begin(), pcieFunctionIdView.end(), pcieFunctionId); 832 if (result.ec != std::errc{} || result.ptr != pcieFunctionIdView.end()) 833 { 834 messages::resourceNotFound(asyncResp->res, "PCIeFunction", 835 pcieFunctionIdStr); 836 return; 837 } 838 839 getValidPCIeDevicePath( 840 pcieDeviceId, asyncResp, 841 [asyncResp, pcieDeviceId, pcieFunctionId]( 842 const std::string& pcieDevicePath, const std::string& service) { 843 getPCIeDeviceProperties( 844 asyncResp, pcieDevicePath, service, 845 [asyncResp, pcieDeviceId, pcieFunctionId]( 846 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 847 addPCIeFunctionCommonProperties( 848 asyncResp->res, pcieDeviceId, pcieFunctionId); 849 addPCIeFunctionProperties(asyncResp->res, pcieFunctionId, 850 pcieDevProperties); 851 }); 852 }); 853 } 854 855 inline void requestRoutesSystemPCIeFunction(App& app) 856 { 857 BMCWEB_ROUTE( 858 app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/<str>/") 859 .privileges(redfish::privileges::getPCIeFunction) 860 .methods(boost::beast::http::verb::get)( 861 std::bind_front(handlePCIeFunctionGet, std::ref(app))); 862 } 863 864 } // namespace redfish 865