1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation 4 #pragma once 5 6 #include "bmcweb_config.h" 7 8 #include "app.hpp" 9 #include "async_resp.hpp" 10 #include "dbus_singleton.hpp" 11 #include "dbus_utility.hpp" 12 #include "error_messages.hpp" 13 #include "generated/enums/processor.hpp" 14 #include "generated/enums/resource.hpp" 15 #include "http_request.hpp" 16 #include "led.hpp" 17 #include "logging.hpp" 18 #include "query.hpp" 19 #include "registries/privilege_registry.hpp" 20 #include "utils/collection.hpp" 21 #include "utils/dbus_utils.hpp" 22 #include "utils/hex_utils.hpp" 23 #include "utils/json_utils.hpp" 24 25 #include <boost/beast/http/field.hpp> 26 #include <boost/beast/http/verb.hpp> 27 #include <boost/system/error_code.hpp> 28 #include <boost/url/format.hpp> 29 #include <sdbusplus/message/native_types.hpp> 30 #include <sdbusplus/unpack_properties.hpp> 31 32 #include <algorithm> 33 #include <array> 34 #include <cstddef> 35 #include <cstdint> 36 #include <format> 37 #include <functional> 38 #include <limits> 39 #include <memory> 40 #include <optional> 41 #include <ranges> 42 #include <string> 43 #include <string_view> 44 #include <tuple> 45 #include <utility> 46 #include <variant> 47 #include <vector> 48 49 namespace redfish 50 { 51 52 // Interfaces which imply a D-Bus object represents a Processor 53 constexpr std::array<std::string_view, 2> processorInterfaces = { 54 "xyz.openbmc_project.Inventory.Item.Cpu", 55 "xyz.openbmc_project.Inventory.Item.Accelerator"}; 56 57 /** 58 * @brief Fill out uuid info of a processor by 59 * requesting data from the given D-Bus object. 60 * 61 * @param[in,out] asyncResp Async HTTP response. 62 * @param[in] service D-Bus service to query. 63 * @param[in] objPath D-Bus object to query. 64 */ 65 inline void getProcessorUUID(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 66 const std::string& service, 67 const std::string& objPath) 68 { 69 BMCWEB_LOG_DEBUG("Get Processor UUID"); 70 dbus::utility::getProperty<std::string>( 71 service, objPath, "xyz.openbmc_project.Common.UUID", "UUID", 72 [objPath, asyncResp{std::move(asyncResp)}]( 73 const boost::system::error_code& ec, const std::string& property) { 74 if (ec) 75 { 76 BMCWEB_LOG_DEBUG("DBUS response error"); 77 messages::internalError(asyncResp->res); 78 return; 79 } 80 asyncResp->res.jsonValue["UUID"] = property; 81 }); 82 } 83 84 inline void getCpuDataByInterface( 85 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 86 const dbus::utility::DBusInterfacesMap& cpuInterfacesProperties) 87 { 88 BMCWEB_LOG_DEBUG("Get CPU resources by interface."); 89 90 // Set the default value of state 91 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 92 asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; 93 94 for (const auto& interface : cpuInterfacesProperties) 95 { 96 for (const auto& property : interface.second) 97 { 98 if (property.first == "Present") 99 { 100 const bool* cpuPresent = std::get_if<bool>(&property.second); 101 if (cpuPresent == nullptr) 102 { 103 // Important property not in desired type 104 messages::internalError(asyncResp->res); 105 return; 106 } 107 if (!*cpuPresent) 108 { 109 // Slot is not populated 110 asyncResp->res.jsonValue["Status"]["State"] = 111 resource::State::Absent; 112 } 113 } 114 else if (property.first == "Functional") 115 { 116 const bool* cpuFunctional = std::get_if<bool>(&property.second); 117 if (cpuFunctional == nullptr) 118 { 119 messages::internalError(asyncResp->res); 120 return; 121 } 122 if (!*cpuFunctional) 123 { 124 asyncResp->res.jsonValue["Status"]["Health"] = 125 resource::Health::Critical; 126 } 127 } 128 else if (property.first == "CoreCount") 129 { 130 const uint16_t* coresCount = 131 std::get_if<uint16_t>(&property.second); 132 if (coresCount == nullptr) 133 { 134 messages::internalError(asyncResp->res); 135 return; 136 } 137 asyncResp->res.jsonValue["TotalCores"] = *coresCount; 138 } 139 else if (property.first == "MaxSpeedInMhz") 140 { 141 const uint32_t* value = std::get_if<uint32_t>(&property.second); 142 if (value != nullptr) 143 { 144 asyncResp->res.jsonValue["MaxSpeedMHz"] = *value; 145 } 146 } 147 else if (property.first == "Socket") 148 { 149 const std::string* value = 150 std::get_if<std::string>(&property.second); 151 if (value != nullptr) 152 { 153 asyncResp->res.jsonValue["Socket"] = *value; 154 } 155 } 156 else if (property.first == "ThreadCount") 157 { 158 const uint16_t* value = std::get_if<uint16_t>(&property.second); 159 if (value != nullptr) 160 { 161 asyncResp->res.jsonValue["TotalThreads"] = *value; 162 } 163 } 164 else if (property.first == "EffectiveFamily") 165 { 166 const uint16_t* value = std::get_if<uint16_t>(&property.second); 167 if (value != nullptr && *value != 2) 168 { 169 asyncResp->res.jsonValue["ProcessorId"]["EffectiveFamily"] = 170 "0x" + intToHexString(*value, 4); 171 } 172 } 173 else if (property.first == "EffectiveModel") 174 { 175 const uint16_t* value = std::get_if<uint16_t>(&property.second); 176 if (value == nullptr) 177 { 178 messages::internalError(asyncResp->res); 179 return; 180 } 181 if (*value != 0) 182 { 183 asyncResp->res.jsonValue["ProcessorId"]["EffectiveModel"] = 184 "0x" + intToHexString(*value, 4); 185 } 186 } 187 else if (property.first == "Id") 188 { 189 const uint64_t* value = std::get_if<uint64_t>(&property.second); 190 if (value != nullptr && *value != 0) 191 { 192 asyncResp->res 193 .jsonValue["ProcessorId"]["IdentificationRegisters"] = 194 "0x" + intToHexString(*value, 16); 195 } 196 } 197 else if (property.first == "Microcode") 198 { 199 const uint32_t* value = std::get_if<uint32_t>(&property.second); 200 if (value == nullptr) 201 { 202 messages::internalError(asyncResp->res); 203 return; 204 } 205 if (*value != 0) 206 { 207 asyncResp->res.jsonValue["ProcessorId"]["MicrocodeInfo"] = 208 "0x" + intToHexString(*value, 8); 209 } 210 } 211 else if (property.first == "Step") 212 { 213 const uint16_t* value = std::get_if<uint16_t>(&property.second); 214 if (value == nullptr) 215 { 216 messages::internalError(asyncResp->res); 217 return; 218 } 219 if (*value != std::numeric_limits<uint16_t>::max()) 220 { 221 asyncResp->res.jsonValue["ProcessorId"]["Step"] = 222 "0x" + intToHexString(*value, 4); 223 } 224 } 225 } 226 } 227 } 228 229 inline void getCpuDataByService( 230 std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& cpuId, 231 const std::string& service, const std::string& objPath) 232 { 233 BMCWEB_LOG_DEBUG("Get available system cpu resources by service."); 234 235 sdbusplus::message::object_path path("/xyz/openbmc_project/inventory"); 236 dbus::utility::getManagedObjects( 237 service, path, 238 [cpuId, service, objPath, asyncResp{std::move(asyncResp)}]( 239 const boost::system::error_code& ec, 240 const dbus::utility::ManagedObjectType& dbusData) { 241 if (ec) 242 { 243 BMCWEB_LOG_DEBUG("DBUS response error"); 244 messages::internalError(asyncResp->res); 245 return; 246 } 247 asyncResp->res.jsonValue["Id"] = cpuId; 248 asyncResp->res.jsonValue["Name"] = "Processor"; 249 asyncResp->res.jsonValue["ProcessorType"] = 250 processor::ProcessorType::CPU; 251 252 for (const auto& object : dbusData) 253 { 254 if (object.first.str == objPath) 255 { 256 getCpuDataByInterface(asyncResp, object.second); 257 } 258 } 259 return; 260 }); 261 } 262 263 /** 264 * @brief Translates throttle cause DBUS property to redfish. 265 * 266 * @param[in] dbusSource The throttle cause from DBUS 267 * 268 * @return Returns as a string, the throttle cause in Redfish terms. If 269 * translation cannot be done, returns "Unknown" throttle reason. 270 */ 271 inline processor::ThrottleCause dbusToRfThrottleCause( 272 const std::string& dbusSource) 273 { 274 if (dbusSource == 275 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.ClockLimit") 276 { 277 return processor::ThrottleCause::ClockLimit; 278 } 279 if (dbusSource == 280 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.ManagementDetectedFault") 281 { 282 return processor::ThrottleCause::ManagementDetectedFault; 283 } 284 if (dbusSource == 285 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.PowerLimit") 286 { 287 return processor::ThrottleCause::PowerLimit; 288 } 289 if (dbusSource == 290 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.ThermalLimit") 291 { 292 return processor::ThrottleCause::ThermalLimit; 293 } 294 if (dbusSource == 295 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.Unknown") 296 { 297 return processor::ThrottleCause::Unknown; 298 } 299 return processor::ThrottleCause::Invalid; 300 } 301 302 inline void readThrottleProperties( 303 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 304 const boost::system::error_code& ec, 305 const dbus::utility::DBusPropertiesMap& properties) 306 { 307 if (ec) 308 { 309 BMCWEB_LOG_ERROR("Processor Throttle getAllProperties error {}", ec); 310 messages::internalError(asyncResp->res); 311 return; 312 } 313 314 const bool* status = nullptr; 315 const std::vector<std::string>* causes = nullptr; 316 317 if (!sdbusplus::unpackPropertiesNoThrow(dbus_utils::UnpackErrorPrinter(), 318 properties, "Throttled", status, 319 "ThrottleCauses", causes)) 320 { 321 messages::internalError(asyncResp->res); 322 return; 323 } 324 325 asyncResp->res.jsonValue["Throttled"] = *status; 326 nlohmann::json::array_t rCauses; 327 for (const std::string& cause : *causes) 328 { 329 processor::ThrottleCause rfCause = dbusToRfThrottleCause(cause); 330 if (rfCause == processor::ThrottleCause::Invalid) 331 { 332 messages::internalError(asyncResp->res); 333 return; 334 } 335 336 rCauses.emplace_back(rfCause); 337 } 338 asyncResp->res.jsonValue["ThrottleCauses"] = std::move(rCauses); 339 } 340 341 inline void getThrottleProperties( 342 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 343 const std::string& service, const std::string& objectPath) 344 { 345 BMCWEB_LOG_DEBUG("Get processor throttle resources"); 346 347 dbus::utility::getAllProperties( 348 service, objectPath, "xyz.openbmc_project.Control.Power.Throttle", 349 [asyncResp](const boost::system::error_code& ec, 350 const dbus::utility::DBusPropertiesMap& properties) { 351 readThrottleProperties(asyncResp, ec, properties); 352 }); 353 } 354 355 inline void getCpuAssetData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 356 const std::string& service, 357 const std::string& objPath) 358 { 359 BMCWEB_LOG_DEBUG("Get Cpu Asset Data"); 360 dbus::utility::getAllProperties( 361 service, objPath, "xyz.openbmc_project.Inventory.Decorator.Asset", 362 [objPath, asyncResp{std::move(asyncResp)}]( 363 const boost::system::error_code& ec, 364 const dbus::utility::DBusPropertiesMap& properties) { 365 if (ec) 366 { 367 BMCWEB_LOG_DEBUG("DBUS response error"); 368 messages::internalError(asyncResp->res); 369 return; 370 } 371 372 const std::string* serialNumber = nullptr; 373 const std::string* model = nullptr; 374 const std::string* manufacturer = nullptr; 375 const std::string* partNumber = nullptr; 376 const std::string* sparePartNumber = nullptr; 377 378 const bool success = sdbusplus::unpackPropertiesNoThrow( 379 dbus_utils::UnpackErrorPrinter(), properties, "SerialNumber", 380 serialNumber, "Model", model, "Manufacturer", manufacturer, 381 "PartNumber", partNumber, "SparePartNumber", sparePartNumber); 382 383 if (!success) 384 { 385 messages::internalError(asyncResp->res); 386 return; 387 } 388 389 if (serialNumber != nullptr && !serialNumber->empty()) 390 { 391 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 392 } 393 394 if ((model != nullptr) && !model->empty()) 395 { 396 asyncResp->res.jsonValue["Model"] = *model; 397 } 398 399 if (manufacturer != nullptr) 400 { 401 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; 402 403 // Otherwise would be unexpected. 404 if (manufacturer->find("Intel") != std::string::npos) 405 { 406 asyncResp->res.jsonValue["ProcessorArchitecture"] = "x86"; 407 asyncResp->res.jsonValue["InstructionSet"] = "x86-64"; 408 } 409 else if (manufacturer->find("IBM") != std::string::npos) 410 { 411 asyncResp->res.jsonValue["ProcessorArchitecture"] = "Power"; 412 asyncResp->res.jsonValue["InstructionSet"] = "PowerISA"; 413 } 414 } 415 416 if (partNumber != nullptr) 417 { 418 asyncResp->res.jsonValue["PartNumber"] = *partNumber; 419 } 420 421 if (sparePartNumber != nullptr && !sparePartNumber->empty()) 422 { 423 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 424 } 425 }); 426 } 427 428 inline void getCpuRevisionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 429 const std::string& service, 430 const std::string& objPath) 431 { 432 BMCWEB_LOG_DEBUG("Get Cpu Revision Data"); 433 dbus::utility::getAllProperties( 434 service, objPath, "xyz.openbmc_project.Inventory.Decorator.Revision", 435 [objPath, asyncResp{std::move(asyncResp)}]( 436 const boost::system::error_code& ec, 437 const dbus::utility::DBusPropertiesMap& properties) { 438 if (ec) 439 { 440 BMCWEB_LOG_DEBUG("DBUS response error"); 441 messages::internalError(asyncResp->res); 442 return; 443 } 444 445 const std::string* version = nullptr; 446 447 const bool success = sdbusplus::unpackPropertiesNoThrow( 448 dbus_utils::UnpackErrorPrinter(), properties, "Version", 449 version); 450 451 if (!success) 452 { 453 messages::internalError(asyncResp->res); 454 return; 455 } 456 457 if (version != nullptr) 458 { 459 asyncResp->res.jsonValue["Version"] = *version; 460 } 461 }); 462 } 463 464 inline void getAcceleratorDataByService( 465 std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& acclrtrId, 466 const std::string& service, const std::string& objPath) 467 { 468 BMCWEB_LOG_DEBUG("Get available system Accelerator resources by service."); 469 dbus::utility::getAllProperties( 470 service, objPath, "", 471 [acclrtrId, asyncResp{std::move(asyncResp)}]( 472 const boost::system::error_code& ec, 473 const dbus::utility::DBusPropertiesMap& properties) { 474 if (ec) 475 { 476 BMCWEB_LOG_DEBUG("DBUS response error"); 477 messages::internalError(asyncResp->res); 478 return; 479 } 480 481 const bool* functional = nullptr; 482 const bool* present = nullptr; 483 484 const bool success = sdbusplus::unpackPropertiesNoThrow( 485 dbus_utils::UnpackErrorPrinter(), properties, "Functional", 486 functional, "Present", present); 487 488 if (!success) 489 { 490 messages::internalError(asyncResp->res); 491 return; 492 } 493 494 std::string state = "Enabled"; 495 std::string health = "OK"; 496 497 if (present != nullptr && !*present) 498 { 499 state = "Absent"; 500 } 501 502 if (functional != nullptr && !*functional) 503 { 504 if (state == "Enabled") 505 { 506 health = "Critical"; 507 } 508 } 509 510 asyncResp->res.jsonValue["Id"] = acclrtrId; 511 asyncResp->res.jsonValue["Name"] = "Processor"; 512 asyncResp->res.jsonValue["Status"]["State"] = state; 513 asyncResp->res.jsonValue["Status"]["Health"] = health; 514 asyncResp->res.jsonValue["ProcessorType"] = 515 processor::ProcessorType::Accelerator; 516 }); 517 } 518 519 // OperatingConfig D-Bus Types 520 using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>; 521 using BaseSpeedPrioritySettingsProperty = 522 std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>; 523 // uint32_t and size_t may or may not be the same type, requiring a dedup'd 524 // variant 525 526 /** 527 * Fill out the HighSpeedCoreIDs in a Processor resource from the given 528 * OperatingConfig D-Bus property. 529 * 530 * @param[in,out] asyncResp Async HTTP response. 531 * @param[in] baseSpeedSettings Full list of base speed priority groups, 532 * to use to determine the list of high 533 * speed cores. 534 */ 535 inline void highSpeedCoreIdsHandler( 536 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 537 const BaseSpeedPrioritySettingsProperty& baseSpeedSettings) 538 { 539 // The D-Bus property does not indicate which bucket is the "high 540 // priority" group, so let's discern that by looking for the one with 541 // highest base frequency. 542 auto highPriorityGroup = baseSpeedSettings.cend(); 543 uint32_t highestBaseSpeed = 0; 544 for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend(); 545 ++it) 546 { 547 const uint32_t baseFreq = std::get<uint32_t>(*it); 548 if (baseFreq > highestBaseSpeed) 549 { 550 highestBaseSpeed = baseFreq; 551 highPriorityGroup = it; 552 } 553 } 554 555 nlohmann::json& jsonCoreIds = asyncResp->res.jsonValue["HighSpeedCoreIDs"]; 556 jsonCoreIds = nlohmann::json::array(); 557 558 // There may not be any entries in the D-Bus property, so only populate 559 // if there was actually something there. 560 if (highPriorityGroup != baseSpeedSettings.cend()) 561 { 562 jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup); 563 } 564 } 565 566 /** 567 * Fill out OperatingConfig related items in a Processor resource by requesting 568 * data from the given D-Bus object. 569 * 570 * @param[in,out] asyncResp Async HTTP response. 571 * @param[in] cpuId CPU D-Bus name. 572 * @param[in] service D-Bus service to query. 573 * @param[in] objPath D-Bus object to query. 574 */ 575 inline void getCpuConfigData( 576 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 577 const std::string& cpuId, const std::string& service, 578 const std::string& objPath) 579 { 580 BMCWEB_LOG_INFO("Getting CPU operating configs for {}", cpuId); 581 582 // First, GetAll CurrentOperatingConfig properties on the object 583 dbus::utility::getAllProperties( 584 service, objPath, 585 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", 586 [asyncResp, cpuId, 587 service](const boost::system::error_code& ec, 588 const dbus::utility::DBusPropertiesMap& properties) { 589 if (ec) 590 { 591 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); 592 messages::internalError(asyncResp->res); 593 return; 594 } 595 596 nlohmann::json& json = asyncResp->res.jsonValue; 597 598 const sdbusplus::message::object_path* appliedConfig = nullptr; 599 const bool* baseSpeedPriorityEnabled = nullptr; 600 601 const bool success = sdbusplus::unpackPropertiesNoThrow( 602 dbus_utils::UnpackErrorPrinter(), properties, "AppliedConfig", 603 appliedConfig, "BaseSpeedPriorityEnabled", 604 baseSpeedPriorityEnabled); 605 606 if (!success) 607 { 608 messages::internalError(asyncResp->res); 609 return; 610 } 611 612 if (appliedConfig != nullptr) 613 { 614 const std::string& dbusPath = appliedConfig->str; 615 nlohmann::json::object_t operatingConfig; 616 operatingConfig["@odata.id"] = boost::urls::format( 617 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", 618 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuId); 619 json["OperatingConfigs"] = std::move(operatingConfig); 620 621 // Reuse the D-Bus config object name for the Redfish 622 // URI 623 size_t baseNamePos = dbusPath.rfind('/'); 624 if (baseNamePos == std::string::npos || 625 baseNamePos == (dbusPath.size() - 1)) 626 { 627 // If the AppliedConfig was somehow not a valid path, 628 // skip adding any more properties, since everything 629 // else is tied to this applied config. 630 messages::internalError(asyncResp->res); 631 return; 632 } 633 nlohmann::json::object_t appliedOperatingConfig; 634 appliedOperatingConfig["@odata.id"] = boost::urls::format( 635 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}", 636 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuId, 637 dbusPath.substr(baseNamePos + 1)); 638 json["AppliedOperatingConfig"] = 639 std::move(appliedOperatingConfig); 640 641 // Once we found the current applied config, queue another 642 // request to read the base freq core ids out of that 643 // config. 644 dbus::utility::getProperty<BaseSpeedPrioritySettingsProperty>( 645 service, dbusPath, 646 "xyz.openbmc_project.Inventory.Item.Cpu." 647 "OperatingConfig", 648 "BaseSpeedPrioritySettings", 649 [asyncResp](const boost::system::error_code& ec2, 650 const BaseSpeedPrioritySettingsProperty& 651 baseSpeedList) { 652 if (ec2) 653 { 654 BMCWEB_LOG_WARNING("D-Bus Property Get error: {}", 655 ec2); 656 messages::internalError(asyncResp->res); 657 return; 658 } 659 660 highSpeedCoreIdsHandler(asyncResp, baseSpeedList); 661 }); 662 } 663 664 if (baseSpeedPriorityEnabled != nullptr) 665 { 666 json["BaseSpeedPriorityState"] = 667 *baseSpeedPriorityEnabled ? "Enabled" : "Disabled"; 668 } 669 }); 670 } 671 672 /** 673 * @brief Fill out location info of a processor by 674 * requesting data from the given D-Bus object. 675 * 676 * @param[in,out] asyncResp Async HTTP response. 677 * @param[in] service D-Bus service to query. 678 * @param[in] objPath D-Bus object to query. 679 */ 680 inline void getCpuLocationCode(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 681 const std::string& service, 682 const std::string& objPath) 683 { 684 BMCWEB_LOG_DEBUG("Get Cpu Location Data"); 685 dbus::utility::getProperty<std::string>( 686 service, objPath, 687 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 688 [objPath, asyncResp{std::move(asyncResp)}]( 689 const boost::system::error_code& ec, const std::string& property) { 690 if (ec) 691 { 692 BMCWEB_LOG_DEBUG("DBUS response error"); 693 messages::internalError(asyncResp->res); 694 return; 695 } 696 697 asyncResp->res 698 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 699 property; 700 }); 701 } 702 703 /** 704 * Populate the unique identifier in a Processor resource by requesting data 705 * from the given D-Bus object. 706 * 707 * @param[in,out] asyncResp Async HTTP response. 708 * @param[in] service D-Bus service to query. 709 * @param[in] objPath D-Bus object to query. 710 */ 711 inline void getCpuUniqueId(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 712 const std::string& service, 713 const std::string& objectPath) 714 { 715 BMCWEB_LOG_DEBUG("Get CPU UniqueIdentifier"); 716 dbus::utility::getProperty<std::string>( 717 service, objectPath, 718 "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier", 719 "UniqueIdentifier", 720 [asyncResp](const boost::system::error_code& ec, 721 const std::string& id) { 722 if (ec) 723 { 724 BMCWEB_LOG_ERROR("Failed to read cpu unique id: {}", ec); 725 messages::internalError(asyncResp->res); 726 return; 727 } 728 asyncResp->res 729 .jsonValue["ProcessorId"]["ProtectedIdentificationNumber"] = id; 730 }); 731 } 732 733 inline void handleProcessorSubtree( 734 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 735 const std::string& processorId, 736 const std::function< 737 void(const std::string& objectPath, 738 const dbus::utility::MapperServiceMap& serviceMap)>& callback, 739 const boost::system::error_code& ec, 740 const dbus::utility::MapperGetSubTreeResponse& subtree) 741 { 742 if (ec) 743 { 744 BMCWEB_LOG_ERROR("DBUS response error: {}", ec); 745 messages::internalError(asyncResp->res); 746 return; 747 } 748 for (const auto& [objectPath, serviceMap] : subtree) 749 { 750 // Ignore any objects which don't end with our desired cpu name 751 sdbusplus::message::object_path path(objectPath); 752 if (path.filename() == processorId) 753 { 754 // Filter out objects that don't have the CPU-specific 755 // interfaces to make sure we can return 404 on non-CPUs 756 // (e.g. /redfish/../Processors/dimm0) 757 for (const auto& [serviceName, interfaceList] : serviceMap) 758 { 759 if (std::ranges::find_first_of(interfaceList, 760 processorInterfaces) != 761 interfaceList.end()) 762 { 763 // Process the first object which matches cpu name and 764 // required interfaces, and potentially ignore any other 765 // matching objects. Assume all interfaces we want to 766 // process must be on the same object path. 767 768 callback(objectPath, serviceMap); 769 return; 770 } 771 } 772 } 773 } 774 messages::resourceNotFound(asyncResp->res, "Processor", processorId); 775 } 776 777 /** 778 * Find the D-Bus object representing the requested Processor, and call the 779 * handler with the results. If matching object is not found, add 404 error to 780 * response and don't call the handler. 781 * 782 * @param[in,out] asyncResp Async HTTP response. 783 * @param[in] processorId Redfish Processor Id. 784 * @param[in] callback Callback to continue processing request upon 785 * successfully finding object. 786 */ 787 inline void getProcessorObject( 788 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 789 const std::string& processorId, 790 std::function<void(const std::string& objectPath, 791 const dbus::utility::MapperServiceMap& serviceMap)>&& 792 callback) 793 { 794 BMCWEB_LOG_DEBUG("Get available system processor resources."); 795 796 // GetSubTree on all interfaces which provide info about a Processor 797 constexpr std::array<std::string_view, 9> interfaces = { 798 "xyz.openbmc_project.Common.UUID", 799 "xyz.openbmc_project.Inventory.Decorator.Asset", 800 "xyz.openbmc_project.Inventory.Decorator.Revision", 801 "xyz.openbmc_project.Inventory.Item.Cpu", 802 "xyz.openbmc_project.Inventory.Decorator.LocationCode", 803 "xyz.openbmc_project.Inventory.Item.Accelerator", 804 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", 805 "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier", 806 "xyz.openbmc_project.Control.Power.Throttle"}; 807 dbus::utility::getSubTree( 808 "/xyz/openbmc_project/inventory", 0, interfaces, 809 [asyncResp, processorId, callback{std::move(callback)}]( 810 const boost::system::error_code& ec, 811 const dbus::utility::MapperGetSubTreeResponse& subtree) { 812 handleProcessorSubtree(asyncResp, processorId, callback, ec, 813 subtree); 814 }); 815 } 816 817 inline void getProcessorData( 818 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 819 const std::string& processorId, const std::string& objectPath, 820 const dbus::utility::MapperServiceMap& serviceMap) 821 { 822 asyncResp->res.addHeader( 823 boost::beast::http::field::link, 824 "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); 825 asyncResp->res.jsonValue["@odata.type"] = "#Processor.v1_18_0.Processor"; 826 asyncResp->res.jsonValue["@odata.id"] = 827 boost::urls::format("/redfish/v1/Systems/{}/Processors/{}", 828 BMCWEB_REDFISH_SYSTEM_URI_NAME, processorId); 829 830 for (const auto& [serviceName, interfaceList] : serviceMap) 831 { 832 for (const auto& interface : interfaceList) 833 { 834 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") 835 { 836 getCpuAssetData(asyncResp, serviceName, objectPath); 837 } 838 else if (interface == 839 "xyz.openbmc_project.Inventory.Decorator.Revision") 840 { 841 getCpuRevisionData(asyncResp, serviceName, objectPath); 842 } 843 else if (interface == "xyz.openbmc_project.Inventory.Item.Cpu") 844 { 845 getCpuDataByService(asyncResp, processorId, serviceName, 846 objectPath); 847 } 848 else if (interface == 849 "xyz.openbmc_project.Inventory.Item.Accelerator") 850 { 851 getAcceleratorDataByService(asyncResp, processorId, serviceName, 852 objectPath); 853 } 854 else if ( 855 interface == 856 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig") 857 { 858 getCpuConfigData(asyncResp, processorId, serviceName, 859 objectPath); 860 } 861 else if (interface == 862 "xyz.openbmc_project.Inventory.Decorator.LocationCode") 863 { 864 getCpuLocationCode(asyncResp, serviceName, objectPath); 865 } 866 else if (interface == "xyz.openbmc_project.Common.UUID") 867 { 868 getProcessorUUID(asyncResp, serviceName, objectPath); 869 } 870 else if (interface == 871 "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier") 872 { 873 getCpuUniqueId(asyncResp, serviceName, objectPath); 874 } 875 else if (interface == "xyz.openbmc_project.Control.Power.Throttle") 876 { 877 getThrottleProperties(asyncResp, serviceName, objectPath); 878 } 879 else if (interface == "xyz.openbmc_project.Association.Definitions") 880 { 881 getLocationIndicatorActive(asyncResp, objectPath); 882 } 883 } 884 } 885 } 886 887 /** 888 * Request all the properties for the given D-Bus object and fill out the 889 * related entries in the Redfish OperatingConfig response. 890 * 891 * @param[in,out] asyncResp Async HTTP response. 892 * @param[in] service D-Bus service name to query. 893 * @param[in] objPath D-Bus object to query. 894 */ 895 inline void getOperatingConfigData( 896 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 897 const std::string& service, const std::string& objPath) 898 { 899 dbus::utility::getAllProperties( 900 service, objPath, 901 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig", 902 [asyncResp](const boost::system::error_code& ec, 903 const dbus::utility::DBusPropertiesMap& properties) { 904 if (ec) 905 { 906 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); 907 messages::internalError(asyncResp->res); 908 return; 909 } 910 911 const size_t* availableCoreCount = nullptr; 912 const uint32_t* baseSpeed = nullptr; 913 const uint32_t* maxJunctionTemperature = nullptr; 914 const uint32_t* maxSpeed = nullptr; 915 const uint32_t* powerLimit = nullptr; 916 const TurboProfileProperty* turboProfile = nullptr; 917 const BaseSpeedPrioritySettingsProperty* baseSpeedPrioritySettings = 918 nullptr; 919 920 const bool success = sdbusplus::unpackPropertiesNoThrow( 921 dbus_utils::UnpackErrorPrinter(), properties, 922 "AvailableCoreCount", availableCoreCount, "BaseSpeed", 923 baseSpeed, "MaxJunctionTemperature", maxJunctionTemperature, 924 "MaxSpeed", maxSpeed, "PowerLimit", powerLimit, "TurboProfile", 925 turboProfile, "BaseSpeedPrioritySettings", 926 baseSpeedPrioritySettings); 927 928 if (!success) 929 { 930 messages::internalError(asyncResp->res); 931 return; 932 } 933 934 nlohmann::json& json = asyncResp->res.jsonValue; 935 936 if (availableCoreCount != nullptr) 937 { 938 json["TotalAvailableCoreCount"] = *availableCoreCount; 939 } 940 941 if (baseSpeed != nullptr) 942 { 943 json["BaseSpeedMHz"] = *baseSpeed; 944 } 945 946 if (maxJunctionTemperature != nullptr) 947 { 948 json["MaxJunctionTemperatureCelsius"] = *maxJunctionTemperature; 949 } 950 951 if (maxSpeed != nullptr) 952 { 953 json["MaxSpeedMHz"] = *maxSpeed; 954 } 955 956 if (powerLimit != nullptr) 957 { 958 json["TDPWatts"] = *powerLimit; 959 } 960 961 if (turboProfile != nullptr) 962 { 963 nlohmann::json& turboArray = json["TurboProfile"]; 964 turboArray = nlohmann::json::array(); 965 for (const auto& [turboSpeed, coreCount] : *turboProfile) 966 { 967 nlohmann::json::object_t turbo; 968 turbo["ActiveCoreCount"] = coreCount; 969 turbo["MaxSpeedMHz"] = turboSpeed; 970 turboArray.emplace_back(std::move(turbo)); 971 } 972 } 973 974 if (baseSpeedPrioritySettings != nullptr) 975 { 976 nlohmann::json& baseSpeedArray = 977 json["BaseSpeedPrioritySettings"]; 978 baseSpeedArray = nlohmann::json::array(); 979 for (const auto& [baseSpeedMhz, coreList] : 980 *baseSpeedPrioritySettings) 981 { 982 nlohmann::json::object_t speed; 983 speed["CoreCount"] = coreList.size(); 984 speed["CoreIDs"] = coreList; 985 speed["BaseSpeedMHz"] = baseSpeedMhz; 986 baseSpeedArray.emplace_back(std::move(speed)); 987 } 988 } 989 }); 990 } 991 992 /** 993 * Handle the PATCH operation of the AppliedOperatingConfig property. Do basic 994 * validation of the input data, and then set the D-Bus property. 995 * 996 * @param[in,out] resp Async HTTP response. 997 * @param[in] processorId Processor's Id. 998 * @param[in] appliedConfigUri New property value to apply. 999 * @param[in] cpuObjectPath Path of CPU object to modify. 1000 * @param[in] serviceMap Service map for CPU object. 1001 */ 1002 inline void patchAppliedOperatingConfig( 1003 const std::shared_ptr<bmcweb::AsyncResp>& resp, 1004 const std::string& processorId, const std::string& appliedConfigUri, 1005 const std::string& cpuObjectPath, 1006 const dbus::utility::MapperServiceMap& serviceMap) 1007 { 1008 // Check that the property even exists by checking for the interface 1009 const std::string* controlService = nullptr; 1010 for (const auto& [serviceName, interfaceList] : serviceMap) 1011 { 1012 if (std::ranges::find(interfaceList, 1013 "xyz.openbmc_project.Control.Processor." 1014 "CurrentOperatingConfig") != interfaceList.end()) 1015 { 1016 controlService = &serviceName; 1017 break; 1018 } 1019 } 1020 1021 if (controlService == nullptr) 1022 { 1023 messages::internalError(resp->res); 1024 return; 1025 } 1026 1027 // Check that the config URI is a child of the cpu URI being patched. 1028 std::string expectedPrefix(std::format("/redfish/v1/Systems/{}/Processors/", 1029 BMCWEB_REDFISH_SYSTEM_URI_NAME)); 1030 expectedPrefix += processorId; 1031 expectedPrefix += "/OperatingConfigs/"; 1032 if (!appliedConfigUri.starts_with(expectedPrefix) || 1033 expectedPrefix.size() == appliedConfigUri.size()) 1034 { 1035 messages::propertyValueIncorrect(resp->res, "AppliedOperatingConfig", 1036 appliedConfigUri); 1037 return; 1038 } 1039 1040 // Generate the D-Bus path of the OperatingConfig object, by assuming it's a 1041 // direct child of the CPU object. 1042 // Strip the expectedPrefix from the config URI to get the "filename", and 1043 // append to the CPU's path. 1044 std::string configBaseName = appliedConfigUri.substr(expectedPrefix.size()); 1045 sdbusplus::message::object_path configPath(cpuObjectPath); 1046 configPath /= configBaseName; 1047 1048 BMCWEB_LOG_INFO("Setting config to {}", configPath.str); 1049 1050 // Set the property, with handler to check error responses 1051 setDbusProperty( 1052 resp, "AppliedOperatingConfig", *controlService, cpuObjectPath, 1053 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", 1054 "AppliedConfig", configPath); 1055 } 1056 1057 inline void handleProcessorHead( 1058 crow::App& app, const crow::Request& req, 1059 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1060 const std::string& /* systemName */, const std::string& /* processorId */) 1061 { 1062 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1063 { 1064 return; 1065 } 1066 asyncResp->res.addHeader( 1067 boost::beast::http::field::link, 1068 "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); 1069 } 1070 1071 inline void handleProcessorCollectionHead( 1072 crow::App& app, const crow::Request& req, 1073 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1074 const std::string& /* systemName */) 1075 { 1076 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1077 { 1078 return; 1079 } 1080 asyncResp->res.addHeader( 1081 boost::beast::http::field::link, 1082 "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); 1083 } 1084 1085 inline void handleProcessorGet( 1086 App& app, const crow::Request& req, 1087 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1088 const std::string& systemName, const std::string& processorId) 1089 { 1090 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1091 { 1092 return; 1093 } 1094 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1095 { 1096 // Option currently returns no systems. TBD 1097 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1098 systemName); 1099 return; 1100 } 1101 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1102 { 1103 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1104 systemName); 1105 return; 1106 } 1107 1108 getProcessorObject( 1109 asyncResp, processorId, 1110 std::bind_front(getProcessorData, asyncResp, processorId)); 1111 } 1112 1113 inline void doPatchProcessor( 1114 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1115 const std::string& processorId, 1116 const std::optional<std::string>& appliedConfigUri, 1117 std::optional<bool> locationIndicatorActive, const std::string& objectPath, 1118 const dbus::utility::MapperServiceMap& serviceMap) 1119 { 1120 if (appliedConfigUri) 1121 { 1122 patchAppliedOperatingConfig(asyncResp, processorId, *appliedConfigUri, 1123 objectPath, serviceMap); 1124 } 1125 1126 if (locationIndicatorActive) 1127 { 1128 // Utility function handles reporting errors 1129 setLocationIndicatorActive(asyncResp, objectPath, 1130 *locationIndicatorActive); 1131 } 1132 } 1133 1134 inline void handleProcessorPatch( 1135 App& app, const crow::Request& req, 1136 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1137 const std::string& systemName, const std::string& processorId) 1138 { 1139 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1140 { 1141 return; 1142 } 1143 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1144 { 1145 // Option currently returns no systems. TBD 1146 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1147 systemName); 1148 return; 1149 } 1150 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1151 { 1152 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1153 systemName); 1154 return; 1155 } 1156 1157 std::optional<std::string> appliedConfigUri; 1158 std::optional<bool> locationIndicatorActive; 1159 if (!json_util::readJsonPatch( 1160 req, asyncResp->res, // 1161 "AppliedOperatingConfig/@odata.id", appliedConfigUri, // 1162 "LocationIndicatorActive", locationIndicatorActive // 1163 )) 1164 { 1165 return; 1166 } 1167 1168 // Check for 404 and find matching D-Bus object, then run 1169 // property patch handlers if that all succeeds. 1170 getProcessorObject( 1171 asyncResp, processorId, 1172 std::bind_front(doPatchProcessor, asyncResp, processorId, 1173 appliedConfigUri, locationIndicatorActive)); 1174 } 1175 1176 inline void requestRoutesOperatingConfigCollection(App& app) 1177 { 1178 BMCWEB_ROUTE(app, 1179 "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/") 1180 .privileges(redfish::privileges::getOperatingConfigCollection) 1181 .methods( 1182 boost::beast::http::verb:: 1183 get)([&app](const crow::Request& req, 1184 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1185 const std::string& systemName, 1186 const std::string& cpuName) { 1187 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1188 { 1189 return; 1190 } 1191 1192 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1193 { 1194 // Option currently returns no systems. TBD 1195 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1196 systemName); 1197 return; 1198 } 1199 1200 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1201 { 1202 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1203 systemName); 1204 return; 1205 } 1206 asyncResp->res.jsonValue["@odata.type"] = 1207 "#OperatingConfigCollection.OperatingConfigCollection"; 1208 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1209 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", 1210 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName); 1211 asyncResp->res.jsonValue["Name"] = "Operating Config Collection"; 1212 1213 // First find the matching CPU object so we know how to 1214 // constrain our search for related Config objects. 1215 const std::array<std::string_view, 1> interfaces = { 1216 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}; 1217 dbus::utility::getSubTreePaths( 1218 "/xyz/openbmc_project/inventory", 0, interfaces, 1219 [asyncResp, 1220 cpuName](const boost::system::error_code& ec, 1221 const dbus::utility::MapperGetSubTreePathsResponse& 1222 objects) { 1223 if (ec) 1224 { 1225 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, 1226 ec.message()); 1227 messages::internalError(asyncResp->res); 1228 return; 1229 } 1230 1231 for (const std::string& object : objects) 1232 { 1233 if (!object.ends_with(cpuName)) 1234 { 1235 continue; 1236 } 1237 1238 // Not expected that there will be multiple matching 1239 // CPU objects, but if there are just use the first 1240 // one. 1241 1242 // Use the common search routine to construct the 1243 // Collection of all Config objects under this CPU. 1244 constexpr std::array<std::string_view, 1> interface{ 1245 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; 1246 collection_util::getCollectionMembers( 1247 asyncResp, 1248 boost::urls::format( 1249 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", 1250 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName), 1251 interface, object); 1252 return; 1253 } 1254 }); 1255 }); 1256 } 1257 1258 inline void requestRoutesOperatingConfig(App& app) 1259 { 1260 BMCWEB_ROUTE( 1261 app, 1262 "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/<str>/") 1263 .privileges(redfish::privileges::getOperatingConfig) 1264 .methods( 1265 boost::beast::http::verb:: 1266 get)([&app](const crow::Request& req, 1267 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1268 const std::string& systemName, 1269 const std::string& cpuName, 1270 const std::string& configName) { 1271 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1272 { 1273 return; 1274 } 1275 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1276 { 1277 // Option currently returns no systems. TBD 1278 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1279 systemName); 1280 return; 1281 } 1282 1283 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1284 { 1285 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1286 systemName); 1287 return; 1288 } 1289 // Ask for all objects implementing OperatingConfig so we can search 1290 // for one with a matching name 1291 constexpr std::array<std::string_view, 1> interfaces = { 1292 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; 1293 dbus::utility::getSubTree( 1294 "/xyz/openbmc_project/inventory", 0, interfaces, 1295 [asyncResp, cpuName, configName]( 1296 const boost::system::error_code& ec, 1297 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1298 if (ec) 1299 { 1300 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, 1301 ec.message()); 1302 messages::internalError(asyncResp->res); 1303 return; 1304 } 1305 const std::string expectedEnding = 1306 cpuName + '/' + configName; 1307 for (const auto& [objectPath, serviceMap] : subtree) 1308 { 1309 // Ignore any configs without matching cpuX/configY 1310 if (!objectPath.ends_with(expectedEnding) || 1311 serviceMap.empty()) 1312 { 1313 continue; 1314 } 1315 1316 nlohmann::json& json = asyncResp->res.jsonValue; 1317 json["@odata.type"] = 1318 "#OperatingConfig.v1_0_0.OperatingConfig"; 1319 json["@odata.id"] = boost::urls::format( 1320 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}", 1321 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName, 1322 configName); 1323 json["Name"] = "Processor Profile"; 1324 json["Id"] = configName; 1325 1326 // Just use the first implementation of the object - not 1327 // expected that there would be multiple matching 1328 // services 1329 getOperatingConfigData( 1330 asyncResp, serviceMap.begin()->first, objectPath); 1331 return; 1332 } 1333 messages::resourceNotFound(asyncResp->res, 1334 "OperatingConfig", configName); 1335 }); 1336 }); 1337 } 1338 1339 inline void requestRoutesProcessorCollection(App& app) 1340 { 1341 /** 1342 * Functions triggers appropriate requests on DBus 1343 */ 1344 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") 1345 .privileges(redfish::privileges::headProcessorCollection) 1346 .methods(boost::beast::http::verb::head)( 1347 std::bind_front(handleProcessorCollectionHead, std::ref(app))); 1348 1349 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") 1350 .privileges(redfish::privileges::getProcessorCollection) 1351 .methods( 1352 boost::beast::http::verb:: 1353 get)([&app](const crow::Request& req, 1354 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1355 const std::string& systemName) { 1356 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1357 { 1358 return; 1359 } 1360 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1361 { 1362 // Option currently returns no systems. TBD 1363 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1364 systemName); 1365 return; 1366 } 1367 1368 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1369 { 1370 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1371 systemName); 1372 return; 1373 } 1374 1375 asyncResp->res.addHeader( 1376 boost::beast::http::field::link, 1377 "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); 1378 1379 asyncResp->res.jsonValue["@odata.type"] = 1380 "#ProcessorCollection.ProcessorCollection"; 1381 asyncResp->res.jsonValue["Name"] = "Processor Collection"; 1382 1383 asyncResp->res.jsonValue["@odata.id"] = 1384 std::format("/redfish/v1/Systems/{}/Processors", 1385 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1386 1387 collection_util::getCollectionMembers( 1388 asyncResp, 1389 boost::urls::format("/redfish/v1/Systems/{}/Processors", 1390 BMCWEB_REDFISH_SYSTEM_URI_NAME), 1391 processorInterfaces, "/xyz/openbmc_project/inventory"); 1392 }); 1393 } 1394 1395 inline void requestRoutesProcessor(App& app) 1396 { 1397 /** 1398 * Functions triggers appropriate requests on DBus 1399 */ 1400 1401 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") 1402 .privileges(redfish::privileges::headProcessor) 1403 .methods(boost::beast::http::verb::head)( 1404 std::bind_front(handleProcessorHead, std::ref(app))); 1405 1406 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") 1407 .privileges(redfish::privileges::getProcessor) 1408 .methods(boost::beast::http::verb::get)( 1409 std::bind_front(handleProcessorGet, std::ref(app))); 1410 1411 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") 1412 .privileges(redfish::privileges::patchProcessor) 1413 .methods(boost::beast::http::verb::patch)( 1414 std::bind_front(handleProcessorPatch, std::ref(app))); 1415 } 1416 1417 } // namespace redfish 1418