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 (bmcwebEnableMultiHost) 112 { 113 // Option currently returns no systems. TBD 114 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 115 systemName); 116 return; 117 } 118 if (systemName != "system") 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"] = 131 "/redfish/v1/Systems/system/PCIeDevices"; 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/system/PCIeDevices/{}/PCIeFunctions", 498 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"] = boost::urls::format( 535 "/redfish/v1/Systems/system/PCIeDevices/{}", pcieDeviceId); 536 asyncResp->res.jsonValue["Name"] = "PCIe Device"; 537 asyncResp->res.jsonValue["Id"] = pcieDeviceId; 538 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 539 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 540 } 541 542 inline void afterGetValidPcieDevicePath( 543 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 544 const std::string& pcieDeviceId, const std::string& pcieDevicePath, 545 const std::string& service) 546 { 547 addPCIeDeviceCommonProperties(asyncResp, pcieDeviceId); 548 getPCIeDeviceAsset(asyncResp, pcieDevicePath, service); 549 getPCIeDeviceState(asyncResp, pcieDevicePath, service); 550 getPCIeDeviceHealth(asyncResp, pcieDevicePath, service); 551 getPCIeDeviceProperties( 552 asyncResp, pcieDevicePath, service, 553 std::bind_front(addPCIeDeviceProperties, asyncResp, pcieDeviceId)); 554 getPCIeDeviceSlotPath( 555 pcieDevicePath, asyncResp, 556 std::bind_front(afterGetPCIeDeviceSlotPath, asyncResp)); 557 } 558 559 inline void 560 handlePCIeDeviceGet(App& app, const crow::Request& req, 561 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 562 const std::string& systemName, 563 const std::string& pcieDeviceId) 564 { 565 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 566 { 567 return; 568 } 569 if constexpr (bmcwebEnableMultiHost) 570 { 571 // Option currently returns no systems. TBD 572 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 573 systemName); 574 return; 575 } 576 if (systemName != "system") 577 { 578 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 579 systemName); 580 return; 581 } 582 583 getValidPCIeDevicePath( 584 pcieDeviceId, asyncResp, 585 std::bind_front(afterGetValidPcieDevicePath, asyncResp, pcieDeviceId)); 586 } 587 588 inline void requestRoutesSystemPCIeDevice(App& app) 589 { 590 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/") 591 .privileges(redfish::privileges::getPCIeDevice) 592 .methods(boost::beast::http::verb::get)( 593 std::bind_front(handlePCIeDeviceGet, std::ref(app))); 594 } 595 596 inline void addPCIeFunctionList( 597 crow::Response& res, const std::string& pcieDeviceId, 598 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 599 { 600 nlohmann::json& pcieFunctionList = res.jsonValue["Members"]; 601 pcieFunctionList = nlohmann::json::array(); 602 static constexpr const int maxPciFunctionNum = 8; 603 604 for (int functionNum = 0; functionNum < maxPciFunctionNum; functionNum++) 605 { 606 // Check if this function exists by 607 // looking for a device ID 608 std::string devIDProperty = "Function" + std::to_string(functionNum) + 609 "DeviceId"; 610 const std::string* property = nullptr; 611 for (const auto& propEntry : pcieDevProperties) 612 { 613 if (propEntry.first == devIDProperty) 614 { 615 property = std::get_if<std::string>(&propEntry.second); 616 break; 617 } 618 } 619 if (property == nullptr || property->empty()) 620 { 621 continue; 622 } 623 624 nlohmann::json::object_t pcieFunction; 625 pcieFunction["@odata.id"] = boost::urls::format( 626 "/redfish/v1/Systems/system/PCIeDevices/{}/PCIeFunctions/{}", 627 pcieDeviceId, std::to_string(functionNum)); 628 pcieFunctionList.emplace_back(std::move(pcieFunction)); 629 } 630 res.jsonValue["PCIeFunctions@odata.count"] = pcieFunctionList.size(); 631 } 632 633 inline void handlePCIeFunctionCollectionGet( 634 App& app, const crow::Request& req, 635 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 636 const std::string& systemName, const std::string& pcieDeviceId) 637 { 638 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 639 { 640 return; 641 } 642 if constexpr (bmcwebEnableMultiHost) 643 { 644 // Option currently returns no systems. TBD 645 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 646 systemName); 647 return; 648 } 649 650 getValidPCIeDevicePath( 651 pcieDeviceId, asyncResp, 652 [asyncResp, pcieDeviceId](const std::string& pcieDevicePath, 653 const std::string& service) { 654 asyncResp->res.addHeader( 655 boost::beast::http::field::link, 656 "</redfish/v1/JsonSchemas/PCIeFunctionCollection/PCIeFunctionCollection.json>; rel=describedby"); 657 asyncResp->res.jsonValue["@odata.type"] = 658 "#PCIeFunctionCollection.PCIeFunctionCollection"; 659 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 660 "/redfish/v1/Systems/system/PCIeDevices/{}/PCIeFunctions", 661 pcieDeviceId); 662 asyncResp->res.jsonValue["Name"] = "PCIe Function Collection"; 663 asyncResp->res.jsonValue["Description"] = 664 "Collection of PCIe Functions for PCIe Device " + pcieDeviceId; 665 getPCIeDeviceProperties( 666 asyncResp, pcieDevicePath, service, 667 [asyncResp, pcieDeviceId]( 668 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 669 addPCIeFunctionList(asyncResp->res, pcieDeviceId, 670 pcieDevProperties); 671 }); 672 }); 673 } 674 675 inline void requestRoutesSystemPCIeFunctionCollection(App& app) 676 { 677 /** 678 * Functions triggers appropriate requests on DBus 679 */ 680 BMCWEB_ROUTE(app, 681 "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/") 682 .privileges(redfish::privileges::getPCIeFunctionCollection) 683 .methods(boost::beast::http::verb::get)( 684 std::bind_front(handlePCIeFunctionCollectionGet, std::ref(app))); 685 } 686 687 inline bool validatePCIeFunctionId( 688 uint64_t pcieFunctionId, 689 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 690 { 691 std::string functionName = "Function" + std::to_string(pcieFunctionId); 692 std::string devIDProperty = functionName + "DeviceId"; 693 694 const std::string* devIdProperty = nullptr; 695 for (const auto& property : pcieDevProperties) 696 { 697 if (property.first == devIDProperty) 698 { 699 devIdProperty = std::get_if<std::string>(&property.second); 700 break; 701 } 702 } 703 return (devIdProperty != nullptr && !devIdProperty->empty()); 704 } 705 706 inline void addPCIeFunctionProperties( 707 crow::Response& resp, uint64_t pcieFunctionId, 708 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 709 { 710 std::string functionName = "Function" + std::to_string(pcieFunctionId); 711 for (const auto& property : pcieDevProperties) 712 { 713 const std::string* strProperty = 714 std::get_if<std::string>(&property.second); 715 716 if (property.first == functionName + "DeviceId") 717 { 718 resp.jsonValue["DeviceId"] = *strProperty; 719 } 720 if (property.first == functionName + "VendorId") 721 { 722 resp.jsonValue["VendorId"] = *strProperty; 723 } 724 // TODO: FunctionType and DeviceClass are Redfish enums. The D-Bus 725 // property strings should be mapped correctly to ensure these 726 // strings are Redfish enum values. For now just check for empty. 727 if (property.first == functionName + "FunctionType") 728 { 729 if (!strProperty->empty()) 730 { 731 resp.jsonValue["FunctionType"] = *strProperty; 732 } 733 } 734 if (property.first == functionName + "DeviceClass") 735 { 736 if (!strProperty->empty()) 737 { 738 resp.jsonValue["DeviceClass"] = *strProperty; 739 } 740 } 741 if (property.first == functionName + "ClassCode") 742 { 743 resp.jsonValue["ClassCode"] = *strProperty; 744 } 745 if (property.first == functionName + "RevisionId") 746 { 747 resp.jsonValue["RevisionId"] = *strProperty; 748 } 749 if (property.first == functionName + "SubsystemId") 750 { 751 resp.jsonValue["SubsystemId"] = *strProperty; 752 } 753 if (property.first == functionName + "SubsystemVendorId") 754 { 755 resp.jsonValue["SubsystemVendorId"] = *strProperty; 756 } 757 } 758 } 759 760 inline void addPCIeFunctionCommonProperties(crow::Response& resp, 761 const std::string& pcieDeviceId, 762 uint64_t pcieFunctionId) 763 { 764 resp.addHeader( 765 boost::beast::http::field::link, 766 "</redfish/v1/JsonSchemas/PCIeFunction/PCIeFunction.json>; rel=describedby"); 767 resp.jsonValue["@odata.type"] = "#PCIeFunction.v1_2_3.PCIeFunction"; 768 resp.jsonValue["@odata.id"] = boost::urls::format( 769 "/redfish/v1/Systems/system/PCIeDevices/{}/PCIeFunctions/{}", 770 pcieDeviceId, std::to_string(pcieFunctionId)); 771 resp.jsonValue["Name"] = "PCIe Function"; 772 resp.jsonValue["Id"] = std::to_string(pcieFunctionId); 773 resp.jsonValue["FunctionId"] = pcieFunctionId; 774 resp.jsonValue["Links"]["PCIeDevice"]["@odata.id"] = boost::urls::format( 775 "/redfish/v1/Systems/system/PCIeDevices/{}", pcieDeviceId); 776 } 777 778 inline void 779 handlePCIeFunctionGet(App& app, const crow::Request& req, 780 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 781 const std::string& systemName, 782 const std::string& pcieDeviceId, 783 const std::string& pcieFunctionIdStr) 784 { 785 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 786 { 787 return; 788 } 789 if constexpr (bmcwebEnableMultiHost) 790 { 791 // Option currently returns no systems. TBD 792 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 793 systemName); 794 return; 795 } 796 if (systemName != "system") 797 { 798 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 799 systemName); 800 return; 801 } 802 803 uint64_t pcieFunctionId = 0; 804 std::from_chars_result result = std::from_chars( 805 &*pcieFunctionIdStr.begin(), &*pcieFunctionIdStr.end(), pcieFunctionId); 806 if (result.ec != std::errc{} || result.ptr != &*pcieFunctionIdStr.end()) 807 { 808 messages::resourceNotFound(asyncResp->res, "PCIeFunction", 809 pcieFunctionIdStr); 810 return; 811 } 812 813 getValidPCIeDevicePath(pcieDeviceId, asyncResp, 814 [asyncResp, pcieDeviceId, 815 pcieFunctionId](const std::string& pcieDevicePath, 816 const std::string& service) { 817 getPCIeDeviceProperties( 818 asyncResp, pcieDevicePath, service, 819 [asyncResp, pcieDeviceId, pcieFunctionId]( 820 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 821 addPCIeFunctionCommonProperties(asyncResp->res, pcieDeviceId, 822 pcieFunctionId); 823 addPCIeFunctionProperties(asyncResp->res, pcieFunctionId, 824 pcieDevProperties); 825 }); 826 }); 827 } 828 829 inline void requestRoutesSystemPCIeFunction(App& app) 830 { 831 BMCWEB_ROUTE( 832 app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/<str>/") 833 .privileges(redfish::privileges::getPCIeFunction) 834 .methods(boost::beast::http::verb::get)( 835 std::bind_front(handlePCIeFunctionGet, std::ref(app))); 836 } 837 838 } // namespace redfish 839