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 "query.hpp" 22 #include "registries/privilege_registry.hpp" 23 #include "utils/collection.hpp" 24 #include "utils/dbus_utils.hpp" 25 #include "utils/pcie_util.hpp" 26 27 #include <boost/system/linux_error.hpp> 28 #include <boost/url/format.hpp> 29 #include <sdbusplus/asio/property.hpp> 30 #include <sdbusplus/unpack_properties.hpp> 31 32 #include <limits> 33 34 namespace redfish 35 { 36 37 static constexpr const char* inventoryPath = "/xyz/openbmc_project/inventory"; 38 static constexpr std::array<std::string_view, 1> pcieDeviceInterface = { 39 "xyz.openbmc_project.Inventory.Item.PCIeDevice"}; 40 static constexpr std::array<std::string_view, 1> pcieSlotInterface = { 41 "xyz.openbmc_project.Inventory.Item.PCIeSlot"}; 42 43 static inline void handlePCIeDevicePath( 44 const std::string& pcieDeviceId, 45 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 46 const dbus::utility::MapperGetSubTreePathsResponse& pcieDevicePaths, 47 const std::function<void(const std::string& pcieDevicePath, 48 const std::string& service)>& callback) 49 50 { 51 for (const std::string& pcieDevicePath : pcieDevicePaths) 52 { 53 std::string pciecDeviceName = 54 sdbusplus::message::object_path(pcieDevicePath).filename(); 55 if (pciecDeviceName.empty() || pciecDeviceName != pcieDeviceId) 56 { 57 continue; 58 } 59 60 dbus::utility::getDbusObject( 61 pcieDevicePath, {}, 62 [pcieDevicePath, asyncResp, 63 callback](const boost::system::error_code& ec, 64 const dbus::utility::MapperGetObject& object) { 65 if (ec || object.empty()) 66 { 67 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 68 messages::internalError(asyncResp->res); 69 return; 70 } 71 callback(pcieDevicePath, object.begin()->first); 72 }); 73 return; 74 } 75 76 BMCWEB_LOG_WARNING("PCIe Device not found"); 77 messages::resourceNotFound(asyncResp->res, "PCIeDevice", pcieDeviceId); 78 } 79 80 static inline void getValidPCIeDevicePath( 81 const std::string& pcieDeviceId, 82 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 83 const std::function<void(const std::string& pcieDevicePath, 84 const std::string& service)>& callback) 85 { 86 dbus::utility::getSubTreePaths( 87 inventoryPath, 0, pcieDeviceInterface, 88 [pcieDeviceId, asyncResp, 89 callback](const boost::system::error_code& ec, 90 const dbus::utility::MapperGetSubTreePathsResponse& 91 pcieDevicePaths) { 92 if (ec) 93 { 94 BMCWEB_LOG_ERROR("D-Bus response error on GetSubTree {}", ec); 95 messages::internalError(asyncResp->res); 96 return; 97 } 98 handlePCIeDevicePath(pcieDeviceId, asyncResp, pcieDevicePaths, 99 callback); 100 return; 101 }); 102 } 103 104 static inline void handlePCIeDeviceCollectionGet( 105 crow::App& app, const crow::Request& req, 106 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 107 const std::string& systemName) 108 { 109 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 110 { 111 return; 112 } 113 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 114 { 115 // Option currently returns no systems. TBD 116 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 117 systemName); 118 return; 119 } 120 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 121 { 122 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 123 systemName); 124 return; 125 } 126 127 asyncResp->res.addHeader(boost::beast::http::field::link, 128 "</redfish/v1/JsonSchemas/PCIeDeviceCollection/" 129 "PCIeDeviceCollection.json>; rel=describedby"); 130 asyncResp->res.jsonValue["@odata.type"] = 131 "#PCIeDeviceCollection.PCIeDeviceCollection"; 132 asyncResp->res.jsonValue["@odata.id"] = std::format( 133 "/redfish/v1/Systems/{}/PCIeDevices", BMCWEB_REDFISH_SYSTEM_URI_NAME); 134 asyncResp->res.jsonValue["Name"] = "PCIe Device Collection"; 135 asyncResp->res.jsonValue["Description"] = "Collection of PCIe Devices"; 136 137 pcie_util::getPCIeDeviceList(asyncResp, 138 nlohmann::json::json_pointer("/Members")); 139 } 140 141 inline void requestRoutesSystemPCIeDeviceCollection(App& app) 142 { 143 /** 144 * Functions triggers appropriate requests on DBus 145 */ 146 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/") 147 .privileges(redfish::privileges::getPCIeDeviceCollection) 148 .methods(boost::beast::http::verb::get)( 149 std::bind_front(handlePCIeDeviceCollectionGet, std::ref(app))); 150 } 151 152 inline void addPCIeSlotProperties( 153 crow::Response& res, const boost::system::error_code& ec, 154 const dbus::utility::DBusPropertiesMap& pcieSlotProperties) 155 { 156 if (ec) 157 { 158 BMCWEB_LOG_ERROR("DBUS response error for getAllProperties{}", 159 ec.value()); 160 messages::internalError(res); 161 return; 162 } 163 std::string generation; 164 size_t lanes = 0; 165 std::string slotType; 166 167 bool success = sdbusplus::unpackPropertiesNoThrow( 168 dbus_utils::UnpackErrorPrinter(), pcieSlotProperties, "Generation", 169 generation, "Lanes", lanes, "SlotType", slotType); 170 171 if (!success) 172 { 173 messages::internalError(res); 174 return; 175 } 176 177 std::optional<pcie_device::PCIeTypes> pcieType = 178 pcie_util::redfishPcieGenerationFromDbus(generation); 179 if (!pcieType) 180 { 181 BMCWEB_LOG_WARNING("Unknown PCIeType: {}", generation); 182 } 183 else 184 { 185 if (*pcieType == pcie_device::PCIeTypes::Invalid) 186 { 187 BMCWEB_LOG_ERROR("Invalid PCIeType: {}", generation); 188 messages::internalError(res); 189 return; 190 } 191 res.jsonValue["Slot"]["PCIeType"] = *pcieType; 192 } 193 194 if (lanes != 0) 195 { 196 res.jsonValue["Slot"]["Lanes"] = lanes; 197 } 198 199 std::optional<pcie_slots::SlotTypes> redfishSlotType = 200 pcie_util::dbusSlotTypeToRf(slotType); 201 if (!redfishSlotType) 202 { 203 BMCWEB_LOG_WARNING("Unknown PCIeSlot Type: {}", slotType); 204 } 205 else 206 { 207 if (*redfishSlotType == pcie_slots::SlotTypes::Invalid) 208 { 209 BMCWEB_LOG_ERROR("Invalid PCIeSlot type: {}", slotType); 210 messages::internalError(res); 211 return; 212 } 213 res.jsonValue["Slot"]["SlotType"] = *redfishSlotType; 214 } 215 } 216 217 inline void getPCIeDeviceSlotPath( 218 const std::string& pcieDevicePath, 219 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 220 std::function<void(const std::string& pcieDeviceSlot)>&& callback) 221 { 222 std::string associationPath = pcieDevicePath + "/contained_by"; 223 dbus::utility::getAssociatedSubTreePaths( 224 associationPath, sdbusplus::message::object_path(inventoryPath), 0, 225 pcieSlotInterface, 226 [callback = std::move(callback), asyncResp, pcieDevicePath]( 227 const boost::system::error_code& ec, 228 const dbus::utility::MapperGetSubTreePathsResponse& endpoints) { 229 if (ec) 230 { 231 if (ec.value() == EBADR) 232 { 233 // Missing association is not an error 234 return; 235 } 236 BMCWEB_LOG_ERROR( 237 "DBUS response error for getAssociatedSubTreePaths {}", 238 ec.value()); 239 messages::internalError(asyncResp->res); 240 return; 241 } 242 if (endpoints.size() > 1) 243 { 244 BMCWEB_LOG_ERROR( 245 "PCIeDevice is associated with more than one PCIeSlot: {}", 246 endpoints.size()); 247 messages::internalError(asyncResp->res); 248 return; 249 } 250 if (endpoints.empty()) 251 { 252 // If the device doesn't have an association, return without PCIe 253 // Slot properties 254 BMCWEB_LOG_DEBUG("PCIeDevice is not associated with PCIeSlot"); 255 return; 256 } 257 callback(endpoints[0]); 258 }); 259 } 260 261 inline void 262 afterGetDbusObject(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 263 const std::string& pcieDeviceSlot, 264 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 298 getPCIeDeviceHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 299 const std::string& pcieDevicePath, 300 const std::string& service) 301 { 302 sdbusplus::asio::getProperty<bool>( 303 *crow::connections::systemBus, service, pcieDevicePath, 304 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 305 [asyncResp](const boost::system::error_code& ec, const bool value) { 306 if (ec) 307 { 308 if (ec.value() != EBADR) 309 { 310 BMCWEB_LOG_ERROR("DBUS response error for Health {}", 311 ec.value()); 312 messages::internalError(asyncResp->res); 313 } 314 return; 315 } 316 317 if (!value) 318 { 319 asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; 320 } 321 }); 322 } 323 324 inline void 325 getPCIeDeviceState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 326 const std::string& pcieDevicePath, 327 const std::string& service) 328 { 329 sdbusplus::asio::getProperty<bool>( 330 *crow::connections::systemBus, service, pcieDevicePath, 331 "xyz.openbmc_project.Inventory.Item", "Present", 332 [asyncResp](const boost::system::error_code& ec, bool value) { 333 if (ec) 334 { 335 if (ec.value() != EBADR) 336 { 337 BMCWEB_LOG_ERROR("DBUS response error for State"); 338 messages::internalError(asyncResp->res); 339 } 340 return; 341 } 342 343 if (!value) 344 { 345 asyncResp->res.jsonValue["Status"]["State"] = "Absent"; 346 } 347 }); 348 } 349 350 inline void 351 getPCIeDeviceAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 352 const std::string& pcieDevicePath, 353 const std::string& service) 354 { 355 sdbusplus::asio::getAllProperties( 356 *crow::connections::systemBus, service, pcieDevicePath, 357 "xyz.openbmc_project.Inventory.Decorator.Asset", 358 [pcieDevicePath, asyncResp{asyncResp}]( 359 const boost::system::error_code& ec, 360 const dbus::utility::DBusPropertiesMap& assetList) { 361 if (ec) 362 { 363 if (ec.value() != EBADR) 364 { 365 BMCWEB_LOG_ERROR("DBUS response error for Properties{}", 366 ec.value()); 367 messages::internalError(asyncResp->res); 368 } 369 return; 370 } 371 372 const std::string* manufacturer = nullptr; 373 const std::string* model = nullptr; 374 const std::string* partNumber = nullptr; 375 const std::string* serialNumber = nullptr; 376 const std::string* sparePartNumber = nullptr; 377 378 const bool success = sdbusplus::unpackPropertiesNoThrow( 379 dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer", 380 manufacturer, "Model", model, "PartNumber", partNumber, 381 "SerialNumber", serialNumber, "SparePartNumber", 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"] = "Enabled"; 550 asyncResp->res.jsonValue["Status"]["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 571 handlePCIeDeviceGet(App& app, const crow::Request& req, 572 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 573 const std::string& systemName, 574 const std::string& pcieDeviceId) 575 { 576 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 577 { 578 return; 579 } 580 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 581 { 582 // Option currently returns no systems. TBD 583 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 584 systemName); 585 return; 586 } 587 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 588 { 589 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 590 systemName); 591 return; 592 } 593 594 getValidPCIeDevicePath( 595 pcieDeviceId, asyncResp, 596 std::bind_front(afterGetValidPcieDevicePath, asyncResp, pcieDeviceId)); 597 } 598 599 inline void requestRoutesSystemPCIeDevice(App& app) 600 { 601 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/") 602 .privileges(redfish::privileges::getPCIeDevice) 603 .methods(boost::beast::http::verb::get)( 604 std::bind_front(handlePCIeDeviceGet, std::ref(app))); 605 } 606 607 inline void addPCIeFunctionList( 608 crow::Response& res, const std::string& pcieDeviceId, 609 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 610 { 611 nlohmann::json& pcieFunctionList = res.jsonValue["Members"]; 612 pcieFunctionList = nlohmann::json::array(); 613 static constexpr const int maxPciFunctionNum = 8; 614 615 for (int functionNum = 0; functionNum < maxPciFunctionNum; functionNum++) 616 { 617 // Check if this function exists by 618 // looking for a device ID 619 std::string devIDProperty = "Function" + std::to_string(functionNum) + 620 "DeviceId"; 621 const std::string* property = nullptr; 622 for (const auto& propEntry : pcieDevProperties) 623 { 624 if (propEntry.first == devIDProperty) 625 { 626 property = std::get_if<std::string>(&propEntry.second); 627 break; 628 } 629 } 630 if (property == nullptr || property->empty()) 631 { 632 continue; 633 } 634 635 nlohmann::json::object_t pcieFunction; 636 pcieFunction["@odata.id"] = boost::urls::format( 637 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions/{}", 638 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId, 639 std::to_string(functionNum)); 640 pcieFunctionList.emplace_back(std::move(pcieFunction)); 641 } 642 res.jsonValue["PCIeFunctions@odata.count"] = pcieFunctionList.size(); 643 } 644 645 inline void handlePCIeFunctionCollectionGet( 646 App& app, const crow::Request& req, 647 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 648 const std::string& systemName, const std::string& pcieDeviceId) 649 { 650 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 651 { 652 return; 653 } 654 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 655 { 656 // Option currently returns no systems. TBD 657 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 658 systemName); 659 return; 660 } 661 662 getValidPCIeDevicePath( 663 pcieDeviceId, asyncResp, 664 [asyncResp, pcieDeviceId](const std::string& pcieDevicePath, 665 const std::string& service) { 666 asyncResp->res.addHeader( 667 boost::beast::http::field::link, 668 "</redfish/v1/JsonSchemas/PCIeFunctionCollection/PCIeFunctionCollection.json>; rel=describedby"); 669 asyncResp->res.jsonValue["@odata.type"] = 670 "#PCIeFunctionCollection.PCIeFunctionCollection"; 671 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 672 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions", 673 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); 674 asyncResp->res.jsonValue["Name"] = "PCIe Function Collection"; 675 asyncResp->res.jsonValue["Description"] = 676 "Collection of PCIe Functions for PCIe Device " + pcieDeviceId; 677 getPCIeDeviceProperties( 678 asyncResp, pcieDevicePath, service, 679 [asyncResp, pcieDeviceId]( 680 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 681 addPCIeFunctionList(asyncResp->res, pcieDeviceId, 682 pcieDevProperties); 683 }); 684 }); 685 } 686 687 inline void requestRoutesSystemPCIeFunctionCollection(App& app) 688 { 689 /** 690 * Functions triggers appropriate requests on DBus 691 */ 692 BMCWEB_ROUTE(app, 693 "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/") 694 .privileges(redfish::privileges::getPCIeFunctionCollection) 695 .methods(boost::beast::http::verb::get)( 696 std::bind_front(handlePCIeFunctionCollectionGet, std::ref(app))); 697 } 698 699 inline bool validatePCIeFunctionId( 700 uint64_t pcieFunctionId, 701 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 702 { 703 std::string functionName = "Function" + std::to_string(pcieFunctionId); 704 std::string devIDProperty = functionName + "DeviceId"; 705 706 const std::string* devIdProperty = nullptr; 707 for (const auto& property : pcieDevProperties) 708 { 709 if (property.first == devIDProperty) 710 { 711 devIdProperty = std::get_if<std::string>(&property.second); 712 break; 713 } 714 } 715 return (devIdProperty != nullptr && !devIdProperty->empty()); 716 } 717 718 inline void addPCIeFunctionProperties( 719 crow::Response& resp, uint64_t pcieFunctionId, 720 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 721 { 722 std::string functionName = "Function" + std::to_string(pcieFunctionId); 723 for (const auto& property : pcieDevProperties) 724 { 725 const std::string* strProperty = 726 std::get_if<std::string>(&property.second); 727 if (strProperty == nullptr) 728 { 729 BMCWEB_LOG_ERROR("Function wasn't a string?"); 730 continue; 731 } 732 if (property.first == functionName + "DeviceId") 733 { 734 resp.jsonValue["DeviceId"] = *strProperty; 735 } 736 if (property.first == functionName + "VendorId") 737 { 738 resp.jsonValue["VendorId"] = *strProperty; 739 } 740 // TODO: FunctionType and DeviceClass are Redfish enums. The D-Bus 741 // property strings should be mapped correctly to ensure these 742 // strings are Redfish enum values. For now just check for empty. 743 if (property.first == functionName + "FunctionType") 744 { 745 if (!strProperty->empty()) 746 { 747 resp.jsonValue["FunctionType"] = *strProperty; 748 } 749 } 750 if (property.first == functionName + "DeviceClass") 751 { 752 if (!strProperty->empty()) 753 { 754 resp.jsonValue["DeviceClass"] = *strProperty; 755 } 756 } 757 if (property.first == functionName + "ClassCode") 758 { 759 resp.jsonValue["ClassCode"] = *strProperty; 760 } 761 if (property.first == functionName + "RevisionId") 762 { 763 resp.jsonValue["RevisionId"] = *strProperty; 764 } 765 if (property.first == functionName + "SubsystemId") 766 { 767 resp.jsonValue["SubsystemId"] = *strProperty; 768 } 769 if (property.first == functionName + "SubsystemVendorId") 770 { 771 resp.jsonValue["SubsystemVendorId"] = *strProperty; 772 } 773 } 774 } 775 776 inline void addPCIeFunctionCommonProperties(crow::Response& resp, 777 const std::string& pcieDeviceId, 778 uint64_t pcieFunctionId) 779 { 780 resp.addHeader( 781 boost::beast::http::field::link, 782 "</redfish/v1/JsonSchemas/PCIeFunction/PCIeFunction.json>; rel=describedby"); 783 resp.jsonValue["@odata.type"] = "#PCIeFunction.v1_2_3.PCIeFunction"; 784 resp.jsonValue["@odata.id"] = boost::urls::format( 785 "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions/{}", 786 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId, 787 std::to_string(pcieFunctionId)); 788 resp.jsonValue["Name"] = "PCIe Function"; 789 resp.jsonValue["Id"] = std::to_string(pcieFunctionId); 790 resp.jsonValue["FunctionId"] = pcieFunctionId; 791 resp.jsonValue["Links"]["PCIeDevice"]["@odata.id"] = 792 boost::urls::format("/redfish/v1/Systems/{}/PCIeDevices/{}", 793 BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); 794 } 795 796 inline void 797 handlePCIeFunctionGet(App& app, const crow::Request& req, 798 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 799 const std::string& systemName, 800 const std::string& pcieDeviceId, 801 const std::string& pcieFunctionIdStr) 802 { 803 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 804 { 805 return; 806 } 807 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 808 { 809 // Option currently returns no systems. TBD 810 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 811 systemName); 812 return; 813 } 814 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 815 { 816 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 817 systemName); 818 return; 819 } 820 std::string_view pcieFunctionIdView = pcieFunctionIdStr; 821 822 uint64_t pcieFunctionId = 0; 823 std::from_chars_result result = std::from_chars( 824 pcieFunctionIdView.begin(), pcieFunctionIdView.end(), pcieFunctionId); 825 if (result.ec != std::errc{} || result.ptr != pcieFunctionIdView.end()) 826 { 827 messages::resourceNotFound(asyncResp->res, "PCIeFunction", 828 pcieFunctionIdStr); 829 return; 830 } 831 832 getValidPCIeDevicePath(pcieDeviceId, asyncResp, 833 [asyncResp, pcieDeviceId, 834 pcieFunctionId](const std::string& pcieDevicePath, 835 const std::string& service) { 836 getPCIeDeviceProperties( 837 asyncResp, pcieDevicePath, service, 838 [asyncResp, pcieDeviceId, pcieFunctionId]( 839 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 840 addPCIeFunctionCommonProperties(asyncResp->res, pcieDeviceId, 841 pcieFunctionId); 842 addPCIeFunctionProperties(asyncResp->res, pcieFunctionId, 843 pcieDevProperties); 844 }); 845 }); 846 } 847 848 inline void requestRoutesSystemPCIeFunction(App& app) 849 { 850 BMCWEB_ROUTE( 851 app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/<str>/") 852 .privileges(redfish::privileges::getPCIeFunction) 853 .methods(boost::beast::http::verb::get)( 854 std::bind_front(handlePCIeFunctionGet, std::ref(app))); 855 } 856 857 } // namespace redfish 858