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, "AppliedOperatingConfig/@odata.id", 1401 appliedConfigUri)) 1402 { 1403 return; 1404 } 1405 1406 if (appliedConfigUri) 1407 { 1408 // Check for 404 and find matching D-Bus object, then run 1409 // property patch handlers if that all succeeds. 1410 getProcessorObject( 1411 asyncResp, processorId, 1412 std::bind_front(patchAppliedOperatingConfig, asyncResp, 1413 processorId, *appliedConfigUri)); 1414 } 1415 }); 1416 } 1417 1418 } // namespace redfish 1419