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