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