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