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