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