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