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