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 size_t* lanesInUse = nullptr; 422 423 const bool success = sdbusplus::unpackPropertiesNoThrow( 424 dbus_utils::UnpackErrorPrinter(), pcieDevProperties, "DeviceType", 425 deviceType, "GenerationInUse", generationInUse, "LanesInUse", 426 lanesInUse); 427 428 if (!success) 429 { 430 messages::internalError(asyncResp->res); 431 return; 432 } 433 434 if (deviceType != nullptr && !deviceType->empty()) 435 { 436 asyncResp->res.jsonValue["PCIeInterface"]["DeviceType"] = *deviceType; 437 } 438 439 if (generationInUse != nullptr) 440 { 441 std::optional<pcie_device::PCIeTypes> redfishGenerationInUse = 442 pcie_util::redfishPcieGenerationFromDbus(*generationInUse); 443 444 if (!redfishGenerationInUse) 445 { 446 BMCWEB_LOG_WARNING << "Unknown PCIe Device Generation: " 447 << *generationInUse; 448 } 449 else 450 { 451 if (*redfishGenerationInUse == pcie_device::PCIeTypes::Invalid) 452 { 453 BMCWEB_LOG_ERROR << "Invalid PCIe Device Generation: " 454 << *generationInUse; 455 messages::internalError(asyncResp->res); 456 return; 457 } 458 asyncResp->res.jsonValue["PCIeInterface"]["PCIeType"] = 459 *redfishGenerationInUse; 460 } 461 } 462 463 // The default value of LanesInUse is 0, and the field will be 464 // left as off if it is a default value. 465 if (lanesInUse != nullptr && *lanesInUse != 0) 466 { 467 asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] = *lanesInUse; 468 } 469 470 asyncResp->res.jsonValue["PCIeFunctions"]["@odata.id"] = 471 boost::urls::format( 472 "/redfish/v1/Systems/system/PCIeDevices/{}/PCIeFunctions", 473 pcieDeviceId); 474 } 475 476 inline void getPCIeDeviceProperties( 477 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 478 const std::string& pcieDevicePath, const std::string& service, 479 const std::function<void( 480 const dbus::utility::DBusPropertiesMap& pcieDevProperties)>&& callback) 481 { 482 sdbusplus::asio::getAllProperties( 483 *crow::connections::systemBus, service, pcieDevicePath, 484 "xyz.openbmc_project.Inventory.Item.PCIeDevice", 485 [asyncResp, 486 callback](const boost::system::error_code& ec, 487 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 488 if (ec) 489 { 490 if (ec.value() != EBADR) 491 { 492 BMCWEB_LOG_ERROR << "DBUS response error for Properties"; 493 messages::internalError(asyncResp->res); 494 } 495 return; 496 } 497 callback(pcieDevProperties); 498 }); 499 } 500 501 inline void addPCIeDeviceCommonProperties( 502 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 503 const std::string& pcieDeviceId) 504 { 505 asyncResp->res.addHeader( 506 boost::beast::http::field::link, 507 "</redfish/v1/JsonSchemas/PCIeDevice/PCIeDevice.json>; rel=describedby"); 508 asyncResp->res.jsonValue["@odata.type"] = "#PCIeDevice.v1_9_0.PCIeDevice"; 509 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 510 "/redfish/v1/Systems/system/PCIeDevices/{}", pcieDeviceId); 511 asyncResp->res.jsonValue["Name"] = "PCIe Device"; 512 asyncResp->res.jsonValue["Id"] = pcieDeviceId; 513 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 514 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 515 } 516 517 inline void afterGetValidPcieDevicePath( 518 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 519 const std::string& pcieDeviceId, const std::string& pcieDevicePath, 520 const std::string& service) 521 { 522 addPCIeDeviceCommonProperties(asyncResp, pcieDeviceId); 523 getPCIeDeviceAsset(asyncResp, pcieDevicePath, service); 524 getPCIeDeviceState(asyncResp, pcieDevicePath, service); 525 getPCIeDeviceHealth(asyncResp, pcieDevicePath, service); 526 getPCIeDeviceProperties( 527 asyncResp, pcieDevicePath, service, 528 std::bind_front(addPCIeDeviceProperties, asyncResp, pcieDeviceId)); 529 getPCIeDeviceSlotPath( 530 pcieDevicePath, asyncResp, 531 std::bind_front(afterGetPCIeDeviceSlotPath, asyncResp)); 532 } 533 534 inline void 535 handlePCIeDeviceGet(App& app, const crow::Request& req, 536 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 537 const std::string& systemName, 538 const std::string& pcieDeviceId) 539 { 540 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 541 { 542 return; 543 } 544 if constexpr (bmcwebEnableMultiHost) 545 { 546 // Option currently returns no systems. TBD 547 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 548 systemName); 549 return; 550 } 551 if (systemName != "system") 552 { 553 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 554 systemName); 555 return; 556 } 557 558 getValidPCIeDevicePath( 559 pcieDeviceId, asyncResp, 560 std::bind_front(afterGetValidPcieDevicePath, asyncResp, pcieDeviceId)); 561 } 562 563 inline void requestRoutesSystemPCIeDevice(App& app) 564 { 565 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/") 566 .privileges(redfish::privileges::getPCIeDevice) 567 .methods(boost::beast::http::verb::get)( 568 std::bind_front(handlePCIeDeviceGet, std::ref(app))); 569 } 570 571 inline void addPCIeFunctionList( 572 crow::Response& res, const std::string& pcieDeviceId, 573 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 574 { 575 nlohmann::json& pcieFunctionList = res.jsonValue["Members"]; 576 pcieFunctionList = nlohmann::json::array(); 577 static constexpr const int maxPciFunctionNum = 8; 578 579 for (int functionNum = 0; functionNum < maxPciFunctionNum; functionNum++) 580 { 581 // Check if this function exists by 582 // looking for a device ID 583 std::string devIDProperty = "Function" + std::to_string(functionNum) + 584 "DeviceId"; 585 const std::string* property = nullptr; 586 for (const auto& propEntry : pcieDevProperties) 587 { 588 if (propEntry.first == devIDProperty) 589 { 590 property = std::get_if<std::string>(&propEntry.second); 591 break; 592 } 593 } 594 if (property == nullptr || property->empty()) 595 { 596 continue; 597 } 598 599 nlohmann::json::object_t pcieFunction; 600 pcieFunction["@odata.id"] = boost::urls::format( 601 "/redfish/v1/Systems/system/PCIeDevices/{}/PCIeFunctions/{}", 602 pcieDeviceId, std::to_string(functionNum)); 603 pcieFunctionList.emplace_back(std::move(pcieFunction)); 604 } 605 res.jsonValue["PCIeFunctions@odata.count"] = pcieFunctionList.size(); 606 } 607 608 inline void handlePCIeFunctionCollectionGet( 609 App& app, const crow::Request& req, 610 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 611 const std::string& systemName, const std::string& pcieDeviceId) 612 { 613 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 614 { 615 return; 616 } 617 if constexpr (bmcwebEnableMultiHost) 618 { 619 // Option currently returns no systems. TBD 620 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 621 systemName); 622 return; 623 } 624 625 getValidPCIeDevicePath( 626 pcieDeviceId, asyncResp, 627 [asyncResp, pcieDeviceId](const std::string& pcieDevicePath, 628 const std::string& service) { 629 asyncResp->res.addHeader( 630 boost::beast::http::field::link, 631 "</redfish/v1/JsonSchemas/PCIeFunctionCollection/PCIeFunctionCollection.json>; rel=describedby"); 632 asyncResp->res.jsonValue["@odata.type"] = 633 "#PCIeFunctionCollection.PCIeFunctionCollection"; 634 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 635 "/redfish/v1/Systems/system/PCIeDevices/{}/PCIeFunctions", 636 pcieDeviceId); 637 asyncResp->res.jsonValue["Name"] = "PCIe Function Collection"; 638 asyncResp->res.jsonValue["Description"] = 639 "Collection of PCIe Functions for PCIe Device " + pcieDeviceId; 640 getPCIeDeviceProperties( 641 asyncResp, pcieDevicePath, service, 642 [asyncResp, pcieDeviceId]( 643 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 644 addPCIeFunctionList(asyncResp->res, pcieDeviceId, 645 pcieDevProperties); 646 }); 647 }); 648 } 649 650 inline void requestRoutesSystemPCIeFunctionCollection(App& app) 651 { 652 /** 653 * Functions triggers appropriate requests on DBus 654 */ 655 BMCWEB_ROUTE(app, 656 "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/") 657 .privileges(redfish::privileges::getPCIeFunctionCollection) 658 .methods(boost::beast::http::verb::get)( 659 std::bind_front(handlePCIeFunctionCollectionGet, std::ref(app))); 660 } 661 662 inline bool validatePCIeFunctionId( 663 uint64_t pcieFunctionId, 664 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 665 { 666 std::string functionName = "Function" + std::to_string(pcieFunctionId); 667 std::string devIDProperty = functionName + "DeviceId"; 668 669 const std::string* devIdProperty = nullptr; 670 for (const auto& property : pcieDevProperties) 671 { 672 if (property.first == devIDProperty) 673 { 674 devIdProperty = std::get_if<std::string>(&property.second); 675 break; 676 } 677 } 678 return (devIdProperty != nullptr && !devIdProperty->empty()); 679 } 680 681 inline void addPCIeFunctionProperties( 682 crow::Response& resp, uint64_t pcieFunctionId, 683 const dbus::utility::DBusPropertiesMap& pcieDevProperties) 684 { 685 std::string functionName = "Function" + std::to_string(pcieFunctionId); 686 for (const auto& property : pcieDevProperties) 687 { 688 const std::string* strProperty = 689 std::get_if<std::string>(&property.second); 690 691 if (property.first == functionName + "DeviceId") 692 { 693 resp.jsonValue["DeviceId"] = *strProperty; 694 } 695 if (property.first == functionName + "VendorId") 696 { 697 resp.jsonValue["VendorId"] = *strProperty; 698 } 699 // TODO: FunctionType and DeviceClass are Redfish enums. The D-Bus 700 // property strings should be mapped correctly to ensure these 701 // strings are Redfish enum values. For now just check for empty. 702 if (property.first == functionName + "FunctionType") 703 { 704 if (!strProperty->empty()) 705 { 706 resp.jsonValue["FunctionType"] = *strProperty; 707 } 708 } 709 if (property.first == functionName + "DeviceClass") 710 { 711 if (!strProperty->empty()) 712 { 713 resp.jsonValue["DeviceClass"] = *strProperty; 714 } 715 } 716 if (property.first == functionName + "ClassCode") 717 { 718 resp.jsonValue["ClassCode"] = *strProperty; 719 } 720 if (property.first == functionName + "RevisionId") 721 { 722 resp.jsonValue["RevisionId"] = *strProperty; 723 } 724 if (property.first == functionName + "SubsystemId") 725 { 726 resp.jsonValue["SubsystemId"] = *strProperty; 727 } 728 if (property.first == functionName + "SubsystemVendorId") 729 { 730 resp.jsonValue["SubsystemVendorId"] = *strProperty; 731 } 732 } 733 } 734 735 inline void addPCIeFunctionCommonProperties(crow::Response& resp, 736 const std::string& pcieDeviceId, 737 uint64_t pcieFunctionId) 738 { 739 resp.addHeader( 740 boost::beast::http::field::link, 741 "</redfish/v1/JsonSchemas/PCIeFunction/PCIeFunction.json>; rel=describedby"); 742 resp.jsonValue["@odata.type"] = "#PCIeFunction.v1_2_3.PCIeFunction"; 743 resp.jsonValue["@odata.id"] = boost::urls::format( 744 "/redfish/v1/Systems/system/PCIeDevices/{}/PCIeFunctions/{}", 745 pcieDeviceId, std::to_string(pcieFunctionId)); 746 resp.jsonValue["Name"] = "PCIe Function"; 747 resp.jsonValue["Id"] = std::to_string(pcieFunctionId); 748 resp.jsonValue["FunctionId"] = pcieFunctionId; 749 resp.jsonValue["Links"]["PCIeDevice"]["@odata.id"] = boost::urls::format( 750 "/redfish/v1/Systems/system/PCIeDevices/{}", pcieDeviceId); 751 } 752 753 inline void 754 handlePCIeFunctionGet(App& app, const crow::Request& req, 755 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 756 const std::string& systemName, 757 const std::string& pcieDeviceId, 758 const std::string& pcieFunctionIdStr) 759 { 760 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 761 { 762 return; 763 } 764 if constexpr (bmcwebEnableMultiHost) 765 { 766 // Option currently returns no systems. TBD 767 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 768 systemName); 769 return; 770 } 771 if (systemName != "system") 772 { 773 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 774 systemName); 775 return; 776 } 777 778 uint64_t pcieFunctionId = 0; 779 std::from_chars_result result = std::from_chars( 780 &*pcieFunctionIdStr.begin(), &*pcieFunctionIdStr.end(), pcieFunctionId); 781 if (result.ec != std::errc{} || result.ptr != &*pcieFunctionIdStr.end()) 782 { 783 messages::resourceNotFound(asyncResp->res, "PCIeFunction", 784 pcieFunctionIdStr); 785 return; 786 } 787 788 getValidPCIeDevicePath(pcieDeviceId, asyncResp, 789 [asyncResp, pcieDeviceId, 790 pcieFunctionId](const std::string& pcieDevicePath, 791 const std::string& service) { 792 getPCIeDeviceProperties( 793 asyncResp, pcieDevicePath, service, 794 [asyncResp, pcieDeviceId, pcieFunctionId]( 795 const dbus::utility::DBusPropertiesMap& pcieDevProperties) { 796 addPCIeFunctionCommonProperties(asyncResp->res, pcieDeviceId, 797 pcieFunctionId); 798 addPCIeFunctionProperties(asyncResp->res, pcieFunctionId, 799 pcieDevProperties); 800 }); 801 }); 802 } 803 804 inline void requestRoutesSystemPCIeFunction(App& app) 805 { 806 BMCWEB_ROUTE( 807 app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/PCIeFunctions/<str>/") 808 .privileges(redfish::privileges::getPCIeFunction) 809 .methods(boost::beast::http::verb::get)( 810 std::bind_front(handlePCIeFunctionGet, std::ref(app))); 811 } 812 813 } // namespace redfish 814