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