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