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 static 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, {}, 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 static 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 static 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