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