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