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