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