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(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 226 const std::string& cpuId, 227 const std::string& service, 228 const std::string& objPath) 229 { 230 BMCWEB_LOG_DEBUG("Get available system cpu resources by service."); 231 232 sdbusplus::message::object_path path("/xyz/openbmc_project/inventory"); 233 dbus::utility::getManagedObjects( 234 service, path, 235 [cpuId, service, objPath, asyncResp{std::move(asyncResp)}]( 236 const boost::system::error_code& ec, 237 const dbus::utility::ManagedObjectType& dbusData) { 238 if (ec) 239 { 240 BMCWEB_LOG_DEBUG("DBUS response error"); 241 messages::internalError(asyncResp->res); 242 return; 243 } 244 asyncResp->res.jsonValue["Id"] = cpuId; 245 asyncResp->res.jsonValue["Name"] = "Processor"; 246 asyncResp->res.jsonValue["ProcessorType"] = 247 processor::ProcessorType::CPU; 248 249 bool slotPresent = false; 250 std::string corePath = objPath + "/core"; 251 size_t totalCores = 0; 252 for (const auto& object : dbusData) 253 { 254 if (object.first.str == objPath) 255 { 256 getCpuDataByInterface(asyncResp, object.second); 257 } 258 else if (object.first.str.starts_with(corePath)) 259 { 260 for (const auto& interface : object.second) 261 { 262 if (interface.first == "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 374 getThrottleProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 375 const std::string& service, 376 const std::string& objectPath) 377 { 378 BMCWEB_LOG_DEBUG("Get processor throttle resources"); 379 380 sdbusplus::asio::getAllProperties( 381 *crow::connections::systemBus, service, objectPath, 382 "xyz.openbmc_project.Control.Power.Throttle", 383 [asyncResp](const boost::system::error_code& ec, 384 const dbus::utility::DBusPropertiesMap& properties) { 385 readThrottleProperties(asyncResp, ec, properties); 386 }); 387 } 388 389 inline void getCpuAssetData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 390 const std::string& service, 391 const std::string& objPath) 392 { 393 BMCWEB_LOG_DEBUG("Get Cpu Asset Data"); 394 sdbusplus::asio::getAllProperties( 395 *crow::connections::systemBus, service, objPath, 396 "xyz.openbmc_project.Inventory.Decorator.Asset", 397 [objPath, asyncResp{std::move(asyncResp)}]( 398 const boost::system::error_code& ec, 399 const dbus::utility::DBusPropertiesMap& properties) { 400 if (ec) 401 { 402 BMCWEB_LOG_DEBUG("DBUS response error"); 403 messages::internalError(asyncResp->res); 404 return; 405 } 406 407 const std::string* serialNumber = nullptr; 408 const std::string* model = nullptr; 409 const std::string* manufacturer = nullptr; 410 const std::string* partNumber = nullptr; 411 const std::string* sparePartNumber = nullptr; 412 413 const bool success = sdbusplus::unpackPropertiesNoThrow( 414 dbus_utils::UnpackErrorPrinter(), properties, "SerialNumber", 415 serialNumber, "Model", model, "Manufacturer", manufacturer, 416 "PartNumber", partNumber, "SparePartNumber", sparePartNumber); 417 418 if (!success) 419 { 420 messages::internalError(asyncResp->res); 421 return; 422 } 423 424 if (serialNumber != nullptr && !serialNumber->empty()) 425 { 426 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 427 } 428 429 if ((model != nullptr) && !model->empty()) 430 { 431 asyncResp->res.jsonValue["Model"] = *model; 432 } 433 434 if (manufacturer != nullptr) 435 { 436 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; 437 438 // Otherwise would be unexpected. 439 if (manufacturer->find("Intel") != std::string::npos) 440 { 441 asyncResp->res.jsonValue["ProcessorArchitecture"] = "x86"; 442 asyncResp->res.jsonValue["InstructionSet"] = "x86-64"; 443 } 444 else if (manufacturer->find("IBM") != std::string::npos) 445 { 446 asyncResp->res.jsonValue["ProcessorArchitecture"] = "Power"; 447 asyncResp->res.jsonValue["InstructionSet"] = "PowerISA"; 448 } 449 } 450 451 if (partNumber != nullptr) 452 { 453 asyncResp->res.jsonValue["PartNumber"] = *partNumber; 454 } 455 456 if (sparePartNumber != nullptr && !sparePartNumber->empty()) 457 { 458 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 459 } 460 }); 461 } 462 463 inline void getCpuRevisionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 464 const std::string& service, 465 const std::string& objPath) 466 { 467 BMCWEB_LOG_DEBUG("Get Cpu Revision Data"); 468 sdbusplus::asio::getAllProperties( 469 *crow::connections::systemBus, service, objPath, 470 "xyz.openbmc_project.Inventory.Decorator.Revision", 471 [objPath, asyncResp{std::move(asyncResp)}]( 472 const boost::system::error_code& ec, 473 const dbus::utility::DBusPropertiesMap& properties) { 474 if (ec) 475 { 476 BMCWEB_LOG_DEBUG("DBUS response error"); 477 messages::internalError(asyncResp->res); 478 return; 479 } 480 481 const std::string* version = nullptr; 482 483 const bool success = sdbusplus::unpackPropertiesNoThrow( 484 dbus_utils::UnpackErrorPrinter(), properties, "Version", 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"] = std::move(appliedOperatingConfig); 674 675 // Once we found the current applied config, queue another 676 // request to read the base freq core ids out of that 677 // config. 678 sdbusplus::asio::getProperty<BaseSpeedPrioritySettingsProperty>( 679 *crow::connections::systemBus, service, dbusPath, 680 "xyz.openbmc_project.Inventory.Item.Cpu." 681 "OperatingConfig", 682 "BaseSpeedPrioritySettings", 683 [asyncResp]( 684 const boost::system::error_code& ec2, 685 const BaseSpeedPrioritySettingsProperty& baseSpeedList) { 686 if (ec2) 687 { 688 BMCWEB_LOG_WARNING("D-Bus Property Get error: {}", ec2); 689 messages::internalError(asyncResp->res); 690 return; 691 } 692 693 highSpeedCoreIdsHandler(asyncResp, baseSpeedList); 694 }); 695 } 696 697 if (baseSpeedPriorityEnabled != nullptr) 698 { 699 json["BaseSpeedPriorityState"] = 700 *baseSpeedPriorityEnabled ? "Enabled" : "Disabled"; 701 } 702 }); 703 } 704 705 /** 706 * @brief Fill out location info of a processor by 707 * requesting data from the given D-Bus object. 708 * 709 * @param[in,out] asyncResp Async HTTP response. 710 * @param[in] service D-Bus service to query. 711 * @param[in] objPath D-Bus object to query. 712 */ 713 inline void getCpuLocationCode(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 714 const std::string& service, 715 const std::string& objPath) 716 { 717 BMCWEB_LOG_DEBUG("Get Cpu Location Data"); 718 sdbusplus::asio::getProperty<std::string>( 719 *crow::connections::systemBus, service, objPath, 720 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 721 [objPath, asyncResp{std::move(asyncResp)}]( 722 const boost::system::error_code& ec, const std::string& property) { 723 if (ec) 724 { 725 BMCWEB_LOG_DEBUG("DBUS response error"); 726 messages::internalError(asyncResp->res); 727 return; 728 } 729 730 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 731 property; 732 }); 733 } 734 735 /** 736 * Populate the unique identifier in a Processor resource by requesting data 737 * from the given D-Bus object. 738 * 739 * @param[in,out] asyncResp Async HTTP response. 740 * @param[in] service D-Bus service to query. 741 * @param[in] objPath D-Bus object to query. 742 */ 743 inline void getCpuUniqueId(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 744 const std::string& service, 745 const std::string& objectPath) 746 { 747 BMCWEB_LOG_DEBUG("Get CPU UniqueIdentifier"); 748 sdbusplus::asio::getProperty<std::string>( 749 *crow::connections::systemBus, service, objectPath, 750 "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier", 751 "UniqueIdentifier", 752 [asyncResp](const boost::system::error_code& ec, 753 const std::string& id) { 754 if (ec) 755 { 756 BMCWEB_LOG_ERROR("Failed to read cpu unique id: {}", ec); 757 messages::internalError(asyncResp->res); 758 return; 759 } 760 asyncResp->res 761 .jsonValue["ProcessorId"]["ProtectedIdentificationNumber"] = id; 762 }); 763 } 764 765 /** 766 * Find the D-Bus object representing the requested Processor, and call the 767 * handler with the results. If matching object is not found, add 404 error to 768 * response and don't call the handler. 769 * 770 * @param[in,out] resp Async HTTP response. 771 * @param[in] processorId Redfish Processor Id. 772 * @param[in] handler Callback to continue processing request upon 773 * successfully finding object. 774 */ 775 template <typename Handler> 776 inline void getProcessorObject(const std::shared_ptr<bmcweb::AsyncResp>& resp, 777 const std::string& processorId, 778 Handler&& handler) 779 { 780 BMCWEB_LOG_DEBUG("Get available system processor resources."); 781 782 // GetSubTree on all interfaces which provide info about a Processor 783 constexpr std::array<std::string_view, 9> interfaces = { 784 "xyz.openbmc_project.Common.UUID", 785 "xyz.openbmc_project.Inventory.Decorator.Asset", 786 "xyz.openbmc_project.Inventory.Decorator.Revision", 787 "xyz.openbmc_project.Inventory.Item.Cpu", 788 "xyz.openbmc_project.Inventory.Decorator.LocationCode", 789 "xyz.openbmc_project.Inventory.Item.Accelerator", 790 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", 791 "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier", 792 "xyz.openbmc_project.Control.Power.Throttle"}; 793 dbus::utility::getSubTree( 794 "/xyz/openbmc_project/inventory", 0, interfaces, 795 [resp, processorId, handler = std::forward<Handler>(handler)]( 796 const boost::system::error_code& ec, 797 const dbus::utility::MapperGetSubTreeResponse& subtree) { 798 if (ec) 799 { 800 BMCWEB_LOG_DEBUG("DBUS response error: {}", ec); 801 messages::internalError(resp->res); 802 return; 803 } 804 for (const auto& [objectPath, serviceMap] : subtree) 805 { 806 // Ignore any objects which don't end with our desired cpu name 807 if (!objectPath.ends_with(processorId)) 808 { 809 continue; 810 } 811 812 bool found = false; 813 // Filter out objects that don't have the CPU-specific 814 // interfaces to make sure we can return 404 on non-CPUs 815 // (e.g. /redfish/../Processors/dimm0) 816 for (const auto& [serviceName, interfaceList] : serviceMap) 817 { 818 if (std::ranges::find_first_of(interfaceList, 819 processorInterfaces) != 820 std::end(interfaceList)) 821 { 822 found = true; 823 break; 824 } 825 } 826 827 if (!found) 828 { 829 continue; 830 } 831 832 // Process the first object which does match our cpu name and 833 // required interfaces, and potentially ignore any other 834 // matching objects. Assume all interfaces we want to process 835 // must be on the same object path. 836 837 handler(objectPath, serviceMap); 838 return; 839 } 840 messages::resourceNotFound(resp->res, "Processor", processorId); 841 }); 842 } 843 844 inline void 845 getProcessorData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 846 const std::string& processorId, 847 const std::string& objectPath, 848 const dbus::utility::MapperServiceMap& serviceMap) 849 { 850 for (const auto& [serviceName, interfaceList] : serviceMap) 851 { 852 for (const auto& interface : interfaceList) 853 { 854 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") 855 { 856 getCpuAssetData(asyncResp, serviceName, objectPath); 857 } 858 else if (interface == 859 "xyz.openbmc_project.Inventory.Decorator.Revision") 860 { 861 getCpuRevisionData(asyncResp, serviceName, objectPath); 862 } 863 else if (interface == "xyz.openbmc_project.Inventory.Item.Cpu") 864 { 865 getCpuDataByService(asyncResp, processorId, serviceName, 866 objectPath); 867 } 868 else if (interface == 869 "xyz.openbmc_project.Inventory.Item.Accelerator") 870 { 871 getAcceleratorDataByService(asyncResp, processorId, serviceName, 872 objectPath); 873 } 874 else if ( 875 interface == 876 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig") 877 { 878 getCpuConfigData(asyncResp, processorId, serviceName, 879 objectPath); 880 } 881 else if (interface == 882 "xyz.openbmc_project.Inventory.Decorator.LocationCode") 883 { 884 getCpuLocationCode(asyncResp, serviceName, objectPath); 885 } 886 else if (interface == "xyz.openbmc_project.Common.UUID") 887 { 888 getProcessorUUID(asyncResp, serviceName, objectPath); 889 } 890 else if (interface == 891 "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier") 892 { 893 getCpuUniqueId(asyncResp, serviceName, objectPath); 894 } 895 else if (interface == "xyz.openbmc_project.Control.Power.Throttle") 896 { 897 getThrottleProperties(asyncResp, serviceName, objectPath); 898 } 899 } 900 } 901 } 902 903 /** 904 * Request all the properties for the given D-Bus object and fill out the 905 * related entries in the Redfish OperatingConfig response. 906 * 907 * @param[in,out] asyncResp Async HTTP response. 908 * @param[in] service D-Bus service name to query. 909 * @param[in] objPath D-Bus object to query. 910 */ 911 inline void 912 getOperatingConfigData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 913 const std::string& service, 914 const std::string& objPath) 915 { 916 sdbusplus::asio::getAllProperties( 917 *crow::connections::systemBus, service, objPath, 918 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig", 919 [asyncResp](const boost::system::error_code& ec, 920 const dbus::utility::DBusPropertiesMap& properties) { 921 if (ec) 922 { 923 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); 924 messages::internalError(asyncResp->res); 925 return; 926 } 927 928 const size_t* availableCoreCount = nullptr; 929 const uint32_t* baseSpeed = nullptr; 930 const uint32_t* maxJunctionTemperature = nullptr; 931 const uint32_t* maxSpeed = nullptr; 932 const uint32_t* powerLimit = nullptr; 933 const TurboProfileProperty* turboProfile = nullptr; 934 const BaseSpeedPrioritySettingsProperty* baseSpeedPrioritySettings = 935 nullptr; 936 937 const bool success = sdbusplus::unpackPropertiesNoThrow( 938 dbus_utils::UnpackErrorPrinter(), properties, "AvailableCoreCount", 939 availableCoreCount, "BaseSpeed", baseSpeed, 940 "MaxJunctionTemperature", maxJunctionTemperature, "MaxSpeed", 941 maxSpeed, "PowerLimit", powerLimit, "TurboProfile", turboProfile, 942 "BaseSpeedPrioritySettings", baseSpeedPrioritySettings); 943 944 if (!success) 945 { 946 messages::internalError(asyncResp->res); 947 return; 948 } 949 950 nlohmann::json& json = asyncResp->res.jsonValue; 951 952 if (availableCoreCount != nullptr) 953 { 954 json["TotalAvailableCoreCount"] = *availableCoreCount; 955 } 956 957 if (baseSpeed != nullptr) 958 { 959 json["BaseSpeedMHz"] = *baseSpeed; 960 } 961 962 if (maxJunctionTemperature != nullptr) 963 { 964 json["MaxJunctionTemperatureCelsius"] = *maxJunctionTemperature; 965 } 966 967 if (maxSpeed != nullptr) 968 { 969 json["MaxSpeedMHz"] = *maxSpeed; 970 } 971 972 if (powerLimit != nullptr) 973 { 974 json["TDPWatts"] = *powerLimit; 975 } 976 977 if (turboProfile != nullptr) 978 { 979 nlohmann::json& turboArray = json["TurboProfile"]; 980 turboArray = nlohmann::json::array(); 981 for (const auto& [turboSpeed, coreCount] : *turboProfile) 982 { 983 nlohmann::json::object_t turbo; 984 turbo["ActiveCoreCount"] = coreCount; 985 turbo["MaxSpeedMHz"] = turboSpeed; 986 turboArray.emplace_back(std::move(turbo)); 987 } 988 } 989 990 if (baseSpeedPrioritySettings != nullptr) 991 { 992 nlohmann::json& baseSpeedArray = json["BaseSpeedPrioritySettings"]; 993 baseSpeedArray = nlohmann::json::array(); 994 for (const auto& [baseSpeedMhz, coreList] : 995 *baseSpeedPrioritySettings) 996 { 997 nlohmann::json::object_t speed; 998 speed["CoreCount"] = coreList.size(); 999 speed["CoreIDs"] = coreList; 1000 speed["BaseSpeedMHz"] = baseSpeedMhz; 1001 baseSpeedArray.emplace_back(std::move(speed)); 1002 } 1003 } 1004 }); 1005 } 1006 1007 /** 1008 * Handle the PATCH operation of the AppliedOperatingConfig property. Do basic 1009 * validation of the input data, and then set the D-Bus property. 1010 * 1011 * @param[in,out] resp Async HTTP response. 1012 * @param[in] processorId Processor's Id. 1013 * @param[in] appliedConfigUri New property value to apply. 1014 * @param[in] cpuObjectPath Path of CPU object to modify. 1015 * @param[in] serviceMap Service map for CPU object. 1016 */ 1017 inline void patchAppliedOperatingConfig( 1018 const std::shared_ptr<bmcweb::AsyncResp>& resp, 1019 const std::string& processorId, const std::string& appliedConfigUri, 1020 const std::string& cpuObjectPath, 1021 const dbus::utility::MapperServiceMap& serviceMap) 1022 { 1023 // Check that the property even exists by checking for the interface 1024 const std::string* controlService = nullptr; 1025 for (const auto& [serviceName, interfaceList] : serviceMap) 1026 { 1027 if (std::ranges::find(interfaceList, 1028 "xyz.openbmc_project.Control.Processor." 1029 "CurrentOperatingConfig") != interfaceList.end()) 1030 { 1031 controlService = &serviceName; 1032 break; 1033 } 1034 } 1035 1036 if (controlService == nullptr) 1037 { 1038 messages::internalError(resp->res); 1039 return; 1040 } 1041 1042 // Check that the config URI is a child of the cpu URI being patched. 1043 std::string expectedPrefix(std::format("/redfish/v1/Systems/{}/Processors/", 1044 BMCWEB_REDFISH_SYSTEM_URI_NAME)); 1045 expectedPrefix += processorId; 1046 expectedPrefix += "/OperatingConfigs/"; 1047 if (!appliedConfigUri.starts_with(expectedPrefix) || 1048 expectedPrefix.size() == appliedConfigUri.size()) 1049 { 1050 messages::propertyValueIncorrect(resp->res, "AppliedOperatingConfig", 1051 appliedConfigUri); 1052 return; 1053 } 1054 1055 // Generate the D-Bus path of the OperatingConfig object, by assuming it's a 1056 // direct child of the CPU object. 1057 // Strip the expectedPrefix from the config URI to get the "filename", and 1058 // append to the CPU's path. 1059 std::string configBaseName = appliedConfigUri.substr(expectedPrefix.size()); 1060 sdbusplus::message::object_path configPath(cpuObjectPath); 1061 configPath /= configBaseName; 1062 1063 BMCWEB_LOG_INFO("Setting config to {}", configPath.str); 1064 1065 // Set the property, with handler to check error responses 1066 setDbusProperty( 1067 resp, "AppliedOperatingConfig", *controlService, cpuObjectPath, 1068 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", 1069 "AppliedConfig", configPath); 1070 } 1071 1072 inline void 1073 handleProcessorHead(crow::App& app, const crow::Request& req, 1074 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1075 const std::string& /* systemName */, 1076 const std::string& /* processorId */) 1077 { 1078 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1079 { 1080 return; 1081 } 1082 asyncResp->res.addHeader( 1083 boost::beast::http::field::link, 1084 "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); 1085 } 1086 1087 inline void handleProcessorCollectionHead( 1088 crow::App& app, const crow::Request& req, 1089 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1090 const std::string& /* systemName */) 1091 { 1092 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1093 { 1094 return; 1095 } 1096 asyncResp->res.addHeader( 1097 boost::beast::http::field::link, 1098 "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); 1099 } 1100 1101 inline void requestRoutesOperatingConfigCollection(App& app) 1102 { 1103 BMCWEB_ROUTE(app, 1104 "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/") 1105 .privileges(redfish::privileges::getOperatingConfigCollection) 1106 .methods(boost::beast::http::verb::get)( 1107 [&app](const crow::Request& req, 1108 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1109 const std::string& systemName, const std::string& cpuName) { 1110 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1111 { 1112 return; 1113 } 1114 1115 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1116 { 1117 // Option currently returns no systems. TBD 1118 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1119 systemName); 1120 return; 1121 } 1122 1123 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1124 { 1125 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1126 systemName); 1127 return; 1128 } 1129 asyncResp->res.jsonValue["@odata.type"] = 1130 "#OperatingConfigCollection.OperatingConfigCollection"; 1131 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1132 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", 1133 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName); 1134 asyncResp->res.jsonValue["Name"] = "Operating Config Collection"; 1135 1136 // First find the matching CPU object so we know how to 1137 // constrain our search for related Config objects. 1138 const std::array<std::string_view, 1> interfaces = { 1139 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}; 1140 dbus::utility::getSubTreePaths( 1141 "/xyz/openbmc_project/inventory", 0, interfaces, 1142 [asyncResp, cpuName]( 1143 const boost::system::error_code& ec, 1144 const dbus::utility::MapperGetSubTreePathsResponse& objects) { 1145 if (ec) 1146 { 1147 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); 1148 messages::internalError(asyncResp->res); 1149 return; 1150 } 1151 1152 for (const std::string& object : objects) 1153 { 1154 if (!object.ends_with(cpuName)) 1155 { 1156 continue; 1157 } 1158 1159 // Not expected that there will be multiple matching 1160 // CPU objects, but if there are just use the first 1161 // one. 1162 1163 // Use the common search routine to construct the 1164 // Collection of all Config objects under this CPU. 1165 constexpr std::array<std::string_view, 1> interface{ 1166 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; 1167 collection_util::getCollectionMembers( 1168 asyncResp, 1169 boost::urls::format( 1170 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", 1171 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName), 1172 interface, object); 1173 return; 1174 } 1175 }); 1176 }); 1177 } 1178 1179 inline void requestRoutesOperatingConfig(App& app) 1180 { 1181 BMCWEB_ROUTE( 1182 app, 1183 "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/<str>/") 1184 .privileges(redfish::privileges::getOperatingConfig) 1185 .methods(boost::beast::http::verb::get)( 1186 [&app](const crow::Request& req, 1187 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1188 const std::string& systemName, const std::string& cpuName, 1189 const std::string& configName) { 1190 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1191 { 1192 return; 1193 } 1194 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1195 { 1196 // Option currently returns no systems. TBD 1197 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1198 systemName); 1199 return; 1200 } 1201 1202 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1203 { 1204 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1205 systemName); 1206 return; 1207 } 1208 // Ask for all objects implementing OperatingConfig so we can search 1209 // for one with a matching name 1210 constexpr std::array<std::string_view, 1> interfaces = { 1211 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; 1212 dbus::utility::getSubTree( 1213 "/xyz/openbmc_project/inventory", 0, interfaces, 1214 [asyncResp, cpuName, configName]( 1215 const boost::system::error_code& ec, 1216 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1217 if (ec) 1218 { 1219 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); 1220 messages::internalError(asyncResp->res); 1221 return; 1222 } 1223 const std::string expectedEnding = cpuName + '/' + configName; 1224 for (const auto& [objectPath, serviceMap] : subtree) 1225 { 1226 // Ignore any configs without matching cpuX/configY 1227 if (!objectPath.ends_with(expectedEnding) || serviceMap.empty()) 1228 { 1229 continue; 1230 } 1231 1232 nlohmann::json& json = asyncResp->res.jsonValue; 1233 json["@odata.type"] = "#OperatingConfig.v1_0_0.OperatingConfig"; 1234 json["@odata.id"] = boost::urls::format( 1235 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}", 1236 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName, configName); 1237 json["Name"] = "Processor Profile"; 1238 json["Id"] = configName; 1239 1240 // Just use the first implementation of the object - not 1241 // expected that there would be multiple matching 1242 // services 1243 getOperatingConfigData(asyncResp, serviceMap.begin()->first, 1244 objectPath); 1245 return; 1246 } 1247 messages::resourceNotFound(asyncResp->res, "OperatingConfig", 1248 configName); 1249 }); 1250 }); 1251 } 1252 1253 inline void requestRoutesProcessorCollection(App& app) 1254 { 1255 /** 1256 * Functions triggers appropriate requests on DBus 1257 */ 1258 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") 1259 .privileges(redfish::privileges::headProcessorCollection) 1260 .methods(boost::beast::http::verb::head)( 1261 std::bind_front(handleProcessorCollectionHead, std::ref(app))); 1262 1263 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") 1264 .privileges(redfish::privileges::getProcessorCollection) 1265 .methods(boost::beast::http::verb::get)( 1266 [&app](const crow::Request& req, 1267 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1268 const std::string& systemName) { 1269 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1270 { 1271 return; 1272 } 1273 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1274 { 1275 // Option currently returns no systems. TBD 1276 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1277 systemName); 1278 return; 1279 } 1280 1281 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1282 { 1283 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1284 systemName); 1285 return; 1286 } 1287 1288 asyncResp->res.addHeader( 1289 boost::beast::http::field::link, 1290 "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); 1291 1292 asyncResp->res.jsonValue["@odata.type"] = 1293 "#ProcessorCollection.ProcessorCollection"; 1294 asyncResp->res.jsonValue["Name"] = "Processor Collection"; 1295 1296 asyncResp->res.jsonValue["@odata.id"] = 1297 std::format("/redfish/v1/Systems/{}/Processors", 1298 BMCWEB_REDFISH_SYSTEM_URI_NAME); 1299 1300 collection_util::getCollectionMembers( 1301 asyncResp, 1302 boost::urls::format("/redfish/v1/Systems/{}/Processors", 1303 BMCWEB_REDFISH_SYSTEM_URI_NAME), 1304 processorInterfaces, "/xyz/openbmc_project/inventory"); 1305 }); 1306 } 1307 1308 inline void requestRoutesProcessor(App& app) 1309 { 1310 /** 1311 * Functions triggers appropriate requests on DBus 1312 */ 1313 1314 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") 1315 .privileges(redfish::privileges::headProcessor) 1316 .methods(boost::beast::http::verb::head)( 1317 std::bind_front(handleProcessorHead, std::ref(app))); 1318 1319 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") 1320 .privileges(redfish::privileges::getProcessor) 1321 .methods(boost::beast::http::verb::get)( 1322 [&app](const crow::Request& req, 1323 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1324 const std::string& systemName, 1325 const std::string& processorId) { 1326 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1327 { 1328 return; 1329 } 1330 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1331 { 1332 // Option currently returns no systems. TBD 1333 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1334 systemName); 1335 return; 1336 } 1337 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1338 { 1339 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1340 systemName); 1341 return; 1342 } 1343 1344 asyncResp->res.addHeader( 1345 boost::beast::http::field::link, 1346 "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); 1347 asyncResp->res.jsonValue["@odata.type"] = 1348 "#Processor.v1_18_0.Processor"; 1349 asyncResp->res.jsonValue["@odata.id"] = 1350 boost::urls::format("/redfish/v1/Systems/{}/Processors/{}", 1351 BMCWEB_REDFISH_SYSTEM_URI_NAME, processorId); 1352 1353 getProcessorObject( 1354 asyncResp, processorId, 1355 std::bind_front(getProcessorData, asyncResp, processorId)); 1356 }); 1357 1358 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") 1359 .privileges(redfish::privileges::patchProcessor) 1360 .methods(boost::beast::http::verb::patch)( 1361 [&app](const crow::Request& req, 1362 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1363 const std::string& systemName, 1364 const std::string& processorId) { 1365 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1366 { 1367 return; 1368 } 1369 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 1370 { 1371 // Option currently returns no systems. TBD 1372 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1373 systemName); 1374 return; 1375 } 1376 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 1377 { 1378 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1379 systemName); 1380 return; 1381 } 1382 1383 std::optional<std::string> appliedConfigUri; 1384 if (!json_util::readJsonPatch(req, asyncResp->res, 1385 "AppliedOperatingConfig/@odata.id", 1386 appliedConfigUri)) 1387 { 1388 return; 1389 } 1390 1391 if (appliedConfigUri) 1392 { 1393 // Check for 404 and find matching D-Bus object, then run 1394 // property patch handlers if that all succeeds. 1395 getProcessorObject(asyncResp, processorId, 1396 std::bind_front(patchAppliedOperatingConfig, 1397 asyncResp, processorId, 1398 *appliedConfigUri)); 1399 } 1400 }); 1401 } 1402 1403 } // namespace redfish 1404