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_view> 42 43 namespace redfish 44 { 45 46 // Interfaces which imply a D-Bus object represents a Processor 47 constexpr std::array<std::string_view, 2> processorInterfaces = { 48 "xyz.openbmc_project.Inventory.Item.Cpu", 49 "xyz.openbmc_project.Inventory.Item.Accelerator"}; 50 51 /** 52 * @brief Fill out uuid info of a processor by 53 * requesting data from the given D-Bus object. 54 * 55 * @param[in,out] asyncResp Async HTTP response. 56 * @param[in] service D-Bus service to query. 57 * @param[in] objPath D-Bus object to query. 58 */ 59 inline void getProcessorUUID(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 60 const std::string& service, 61 const std::string& objPath) 62 { 63 BMCWEB_LOG_DEBUG("Get Processor UUID"); 64 sdbusplus::asio::getProperty<std::string>( 65 *crow::connections::systemBus, service, objPath, 66 "xyz.openbmc_project.Common.UUID", "UUID", 67 [objPath, asyncResp{std::move(asyncResp)}]( 68 const boost::system::error_code& ec, const std::string& property) { 69 if (ec) 70 { 71 BMCWEB_LOG_DEBUG("DBUS response error"); 72 messages::internalError(asyncResp->res); 73 return; 74 } 75 asyncResp->res.jsonValue["UUID"] = property; 76 }); 77 } 78 79 inline void getCpuDataByInterface( 80 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 81 const dbus::utility::DBusInterfacesMap& cpuInterfacesProperties) 82 { 83 BMCWEB_LOG_DEBUG("Get CPU resources by interface."); 84 85 // Set the default value of state 86 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 87 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 88 89 for (const auto& interface : cpuInterfacesProperties) 90 { 91 for (const auto& property : interface.second) 92 { 93 if (property.first == "Present") 94 { 95 const bool* cpuPresent = std::get_if<bool>(&property.second); 96 if (cpuPresent == nullptr) 97 { 98 // Important property not in desired type 99 messages::internalError(asyncResp->res); 100 return; 101 } 102 if (!*cpuPresent) 103 { 104 // Slot is not populated 105 asyncResp->res.jsonValue["Status"]["State"] = "Absent"; 106 } 107 } 108 else if (property.first == "Functional") 109 { 110 const bool* cpuFunctional = std::get_if<bool>(&property.second); 111 if (cpuFunctional == nullptr) 112 { 113 messages::internalError(asyncResp->res); 114 return; 115 } 116 if (!*cpuFunctional) 117 { 118 asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; 119 } 120 } 121 else if (property.first == "CoreCount") 122 { 123 const uint16_t* coresCount = 124 std::get_if<uint16_t>(&property.second); 125 if (coresCount == nullptr) 126 { 127 messages::internalError(asyncResp->res); 128 return; 129 } 130 asyncResp->res.jsonValue["TotalCores"] = *coresCount; 131 } 132 else if (property.first == "MaxSpeedInMhz") 133 { 134 const uint32_t* value = std::get_if<uint32_t>(&property.second); 135 if (value != nullptr) 136 { 137 asyncResp->res.jsonValue["MaxSpeedMHz"] = *value; 138 } 139 } 140 else if (property.first == "Socket") 141 { 142 const std::string* value = 143 std::get_if<std::string>(&property.second); 144 if (value != nullptr) 145 { 146 asyncResp->res.jsonValue["Socket"] = *value; 147 } 148 } 149 else if (property.first == "ThreadCount") 150 { 151 const uint16_t* value = std::get_if<uint16_t>(&property.second); 152 if (value != nullptr) 153 { 154 asyncResp->res.jsonValue["TotalThreads"] = *value; 155 } 156 } 157 else if (property.first == "EffectiveFamily") 158 { 159 const uint16_t* value = std::get_if<uint16_t>(&property.second); 160 if (value != nullptr && *value != 2) 161 { 162 asyncResp->res.jsonValue["ProcessorId"]["EffectiveFamily"] = 163 "0x" + intToHexString(*value, 4); 164 } 165 } 166 else if (property.first == "EffectiveModel") 167 { 168 const uint16_t* value = std::get_if<uint16_t>(&property.second); 169 if (value == nullptr) 170 { 171 messages::internalError(asyncResp->res); 172 return; 173 } 174 if (*value != 0) 175 { 176 asyncResp->res.jsonValue["ProcessorId"]["EffectiveModel"] = 177 "0x" + intToHexString(*value, 4); 178 } 179 } 180 else if (property.first == "Id") 181 { 182 const uint64_t* value = std::get_if<uint64_t>(&property.second); 183 if (value != nullptr && *value != 0) 184 { 185 asyncResp->res 186 .jsonValue["ProcessorId"]["IdentificationRegisters"] = 187 "0x" + intToHexString(*value, 16); 188 } 189 } 190 else if (property.first == "Microcode") 191 { 192 const uint32_t* value = std::get_if<uint32_t>(&property.second); 193 if (value == nullptr) 194 { 195 messages::internalError(asyncResp->res); 196 return; 197 } 198 if (*value != 0) 199 { 200 asyncResp->res.jsonValue["ProcessorId"]["MicrocodeInfo"] = 201 "0x" + intToHexString(*value, 8); 202 } 203 } 204 else if (property.first == "Step") 205 { 206 const uint16_t* value = std::get_if<uint16_t>(&property.second); 207 if (value == nullptr) 208 { 209 messages::internalError(asyncResp->res); 210 return; 211 } 212 if (*value != std::numeric_limits<uint16_t>::max()) 213 { 214 asyncResp->res.jsonValue["ProcessorId"]["Step"] = 215 "0x" + intToHexString(*value, 4); 216 } 217 } 218 } 219 } 220 } 221 222 inline void getCpuDataByService(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 223 const std::string& cpuId, 224 const std::string& service, 225 const std::string& objPath) 226 { 227 BMCWEB_LOG_DEBUG("Get available system cpu resources by service."); 228 229 sdbusplus::message::object_path path("/xyz/openbmc_project/inventory"); 230 dbus::utility::getManagedObjects( 231 service, path, 232 [cpuId, service, objPath, asyncResp{std::move(asyncResp)}]( 233 const boost::system::error_code& ec, 234 const dbus::utility::ManagedObjectType& dbusData) { 235 if (ec) 236 { 237 BMCWEB_LOG_DEBUG("DBUS response error"); 238 messages::internalError(asyncResp->res); 239 return; 240 } 241 asyncResp->res.jsonValue["Id"] = cpuId; 242 asyncResp->res.jsonValue["Name"] = "Processor"; 243 asyncResp->res.jsonValue["ProcessorType"] = "CPU"; 244 245 bool slotPresent = false; 246 std::string corePath = objPath + "/core"; 247 size_t totalCores = 0; 248 for (const auto& object : dbusData) 249 { 250 if (object.first.str == objPath) 251 { 252 getCpuDataByInterface(asyncResp, object.second); 253 } 254 else if (object.first.str.starts_with(corePath)) 255 { 256 for (const auto& interface : object.second) 257 { 258 if (interface.first == "xyz.openbmc_project.Inventory.Item") 259 { 260 for (const auto& property : interface.second) 261 { 262 if (property.first == "Present") 263 { 264 const bool* present = 265 std::get_if<bool>(&property.second); 266 if (present != nullptr) 267 { 268 if (*present) 269 { 270 slotPresent = true; 271 totalCores++; 272 } 273 } 274 } 275 } 276 } 277 } 278 } 279 } 280 // In getCpuDataByInterface(), state and health are set 281 // based on the present and functional status. If core 282 // count is zero, then it has a higher precedence. 283 if (slotPresent) 284 { 285 if (totalCores == 0) 286 { 287 // Slot is not populated, set status end return 288 asyncResp->res.jsonValue["Status"]["State"] = "Absent"; 289 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 290 } 291 asyncResp->res.jsonValue["TotalCores"] = totalCores; 292 } 293 return; 294 }); 295 } 296 297 /** 298 * @brief Translates throttle cause DBUS property to redfish. 299 * 300 * @param[in] dbusSource The throttle cause from DBUS 301 * 302 * @return Returns as a string, the throttle cause in Redfish terms. If 303 * translation cannot be done, returns "Unknown" throttle reason. 304 */ 305 inline processor::ThrottleCause 306 dbusToRfThrottleCause(const std::string& dbusSource) 307 { 308 if (dbusSource == 309 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.ClockLimit") 310 { 311 return processor::ThrottleCause::ClockLimit; 312 } 313 if (dbusSource == 314 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.ManagementDetectedFault") 315 { 316 return processor::ThrottleCause::ManagementDetectedFault; 317 } 318 if (dbusSource == 319 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.PowerLimit") 320 { 321 return processor::ThrottleCause::PowerLimit; 322 } 323 if (dbusSource == 324 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.ThermalLimit") 325 { 326 return processor::ThrottleCause::ThermalLimit; 327 } 328 if (dbusSource == 329 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.Unknown") 330 { 331 return processor::ThrottleCause::Unknown; 332 } 333 return processor::ThrottleCause::Invalid; 334 } 335 336 inline void 337 readThrottleProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 338 const boost::system::error_code& ec, 339 const dbus::utility::DBusPropertiesMap& properties) 340 { 341 if (ec) 342 { 343 BMCWEB_LOG_ERROR("Processor Throttle getAllProperties error {}", ec); 344 messages::internalError(asyncResp->res); 345 return; 346 } 347 348 const bool* status = nullptr; 349 const std::vector<std::string>* causes = nullptr; 350 351 if (!sdbusplus::unpackPropertiesNoThrow(dbus_utils::UnpackErrorPrinter(), 352 properties, "Throttled", status, 353 "ThrottleCauses", causes)) 354 { 355 messages::internalError(asyncResp->res); 356 return; 357 } 358 359 asyncResp->res.jsonValue["Throttled"] = *status; 360 nlohmann::json::array_t rCauses; 361 for (const std::string& cause : *causes) 362 { 363 processor::ThrottleCause rfCause = dbusToRfThrottleCause(cause); 364 if (rfCause == processor::ThrottleCause::Invalid) 365 { 366 messages::internalError(asyncResp->res); 367 return; 368 } 369 370 rCauses.emplace_back(rfCause); 371 } 372 asyncResp->res.jsonValue["ThrottleCauses"] = std::move(rCauses); 373 } 374 375 inline void 376 getThrottleProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 377 const std::string& service, 378 const std::string& objectPath) 379 { 380 BMCWEB_LOG_DEBUG("Get processor throttle resources"); 381 382 sdbusplus::asio::getAllProperties( 383 *crow::connections::systemBus, service, objectPath, 384 "xyz.openbmc_project.Control.Power.Throttle", 385 [asyncResp](const boost::system::error_code& ec, 386 const dbus::utility::DBusPropertiesMap& properties) { 387 readThrottleProperties(asyncResp, ec, properties); 388 }); 389 } 390 391 inline void getCpuAssetData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 392 const std::string& service, 393 const std::string& objPath) 394 { 395 BMCWEB_LOG_DEBUG("Get Cpu Asset Data"); 396 sdbusplus::asio::getAllProperties( 397 *crow::connections::systemBus, service, objPath, 398 "xyz.openbmc_project.Inventory.Decorator.Asset", 399 [objPath, asyncResp{std::move(asyncResp)}]( 400 const boost::system::error_code& ec, 401 const dbus::utility::DBusPropertiesMap& properties) { 402 if (ec) 403 { 404 BMCWEB_LOG_DEBUG("DBUS response error"); 405 messages::internalError(asyncResp->res); 406 return; 407 } 408 409 const std::string* serialNumber = nullptr; 410 const std::string* model = nullptr; 411 const std::string* manufacturer = nullptr; 412 const std::string* partNumber = nullptr; 413 const std::string* sparePartNumber = nullptr; 414 415 const bool success = sdbusplus::unpackPropertiesNoThrow( 416 dbus_utils::UnpackErrorPrinter(), properties, "SerialNumber", 417 serialNumber, "Model", model, "Manufacturer", manufacturer, 418 "PartNumber", partNumber, "SparePartNumber", sparePartNumber); 419 420 if (!success) 421 { 422 messages::internalError(asyncResp->res); 423 return; 424 } 425 426 if (serialNumber != nullptr && !serialNumber->empty()) 427 { 428 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 429 } 430 431 if ((model != nullptr) && !model->empty()) 432 { 433 asyncResp->res.jsonValue["Model"] = *model; 434 } 435 436 if (manufacturer != nullptr) 437 { 438 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; 439 440 // Otherwise would be unexpected. 441 if (manufacturer->find("Intel") != std::string::npos) 442 { 443 asyncResp->res.jsonValue["ProcessorArchitecture"] = "x86"; 444 asyncResp->res.jsonValue["InstructionSet"] = "x86-64"; 445 } 446 else if (manufacturer->find("IBM") != std::string::npos) 447 { 448 asyncResp->res.jsonValue["ProcessorArchitecture"] = "Power"; 449 asyncResp->res.jsonValue["InstructionSet"] = "PowerISA"; 450 } 451 } 452 453 if (partNumber != nullptr) 454 { 455 asyncResp->res.jsonValue["PartNumber"] = *partNumber; 456 } 457 458 if (sparePartNumber != nullptr && !sparePartNumber->empty()) 459 { 460 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 461 } 462 }); 463 } 464 465 inline void getCpuRevisionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 466 const std::string& service, 467 const std::string& objPath) 468 { 469 BMCWEB_LOG_DEBUG("Get Cpu Revision Data"); 470 sdbusplus::asio::getAllProperties( 471 *crow::connections::systemBus, service, objPath, 472 "xyz.openbmc_project.Inventory.Decorator.Revision", 473 [objPath, asyncResp{std::move(asyncResp)}]( 474 const boost::system::error_code& ec, 475 const dbus::utility::DBusPropertiesMap& properties) { 476 if (ec) 477 { 478 BMCWEB_LOG_DEBUG("DBUS response error"); 479 messages::internalError(asyncResp->res); 480 return; 481 } 482 483 const std::string* version = nullptr; 484 485 const bool success = sdbusplus::unpackPropertiesNoThrow( 486 dbus_utils::UnpackErrorPrinter(), properties, "Version", version); 487 488 if (!success) 489 { 490 messages::internalError(asyncResp->res); 491 return; 492 } 493 494 if (version != nullptr) 495 { 496 asyncResp->res.jsonValue["Version"] = *version; 497 } 498 }); 499 } 500 501 inline void getAcceleratorDataByService( 502 std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& acclrtrId, 503 const std::string& service, const std::string& objPath) 504 { 505 BMCWEB_LOG_DEBUG("Get available system Accelerator resources by service."); 506 sdbusplus::asio::getAllProperties( 507 *crow::connections::systemBus, service, objPath, "", 508 [acclrtrId, asyncResp{std::move(asyncResp)}]( 509 const boost::system::error_code& ec, 510 const dbus::utility::DBusPropertiesMap& properties) { 511 if (ec) 512 { 513 BMCWEB_LOG_DEBUG("DBUS response error"); 514 messages::internalError(asyncResp->res); 515 return; 516 } 517 518 const bool* functional = nullptr; 519 const bool* present = nullptr; 520 521 const bool success = sdbusplus::unpackPropertiesNoThrow( 522 dbus_utils::UnpackErrorPrinter(), properties, "Functional", 523 functional, "Present", present); 524 525 if (!success) 526 { 527 messages::internalError(asyncResp->res); 528 return; 529 } 530 531 std::string state = "Enabled"; 532 std::string health = "OK"; 533 534 if (present != nullptr && !*present) 535 { 536 state = "Absent"; 537 } 538 539 if (functional != nullptr && !*functional) 540 { 541 if (state == "Enabled") 542 { 543 health = "Critical"; 544 } 545 } 546 547 asyncResp->res.jsonValue["Id"] = acclrtrId; 548 asyncResp->res.jsonValue["Name"] = "Processor"; 549 asyncResp->res.jsonValue["Status"]["State"] = state; 550 asyncResp->res.jsonValue["Status"]["Health"] = health; 551 asyncResp->res.jsonValue["ProcessorType"] = "Accelerator"; 552 }); 553 } 554 555 // OperatingConfig D-Bus Types 556 using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>; 557 using BaseSpeedPrioritySettingsProperty = 558 std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>; 559 // uint32_t and size_t may or may not be the same type, requiring a dedup'd 560 // variant 561 562 /** 563 * Fill out the HighSpeedCoreIDs in a Processor resource from the given 564 * OperatingConfig D-Bus property. 565 * 566 * @param[in,out] asyncResp Async HTTP response. 567 * @param[in] baseSpeedSettings Full list of base speed priority groups, 568 * to use to determine the list of high 569 * speed cores. 570 */ 571 inline void highSpeedCoreIdsHandler( 572 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 573 const BaseSpeedPrioritySettingsProperty& baseSpeedSettings) 574 { 575 // The D-Bus property does not indicate which bucket is the "high 576 // priority" group, so let's discern that by looking for the one with 577 // highest base frequency. 578 auto highPriorityGroup = baseSpeedSettings.cend(); 579 uint32_t highestBaseSpeed = 0; 580 for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend(); 581 ++it) 582 { 583 const uint32_t baseFreq = std::get<uint32_t>(*it); 584 if (baseFreq > highestBaseSpeed) 585 { 586 highestBaseSpeed = baseFreq; 587 highPriorityGroup = it; 588 } 589 } 590 591 nlohmann::json& jsonCoreIds = asyncResp->res.jsonValue["HighSpeedCoreIDs"]; 592 jsonCoreIds = nlohmann::json::array(); 593 594 // There may not be any entries in the D-Bus property, so only populate 595 // if there was actually something there. 596 if (highPriorityGroup != baseSpeedSettings.cend()) 597 { 598 jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup); 599 } 600 } 601 602 /** 603 * Fill out OperatingConfig related items in a Processor resource by requesting 604 * data from the given D-Bus object. 605 * 606 * @param[in,out] asyncResp Async HTTP response. 607 * @param[in] cpuId CPU D-Bus name. 608 * @param[in] service D-Bus service to query. 609 * @param[in] objPath D-Bus object to query. 610 */ 611 inline void 612 getCpuConfigData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 613 const std::string& cpuId, const std::string& service, 614 const std::string& objPath) 615 { 616 BMCWEB_LOG_INFO("Getting CPU operating configs for {}", cpuId); 617 618 // First, GetAll CurrentOperatingConfig properties on the object 619 sdbusplus::asio::getAllProperties( 620 *crow::connections::systemBus, service, objPath, 621 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", 622 [asyncResp, cpuId, 623 service](const boost::system::error_code& ec, 624 const dbus::utility::DBusPropertiesMap& properties) { 625 if (ec) 626 { 627 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); 628 messages::internalError(asyncResp->res); 629 return; 630 } 631 632 nlohmann::json& json = asyncResp->res.jsonValue; 633 634 const sdbusplus::message::object_path* appliedConfig = nullptr; 635 const bool* baseSpeedPriorityEnabled = nullptr; 636 637 const bool success = sdbusplus::unpackPropertiesNoThrow( 638 dbus_utils::UnpackErrorPrinter(), properties, "AppliedConfig", 639 appliedConfig, "BaseSpeedPriorityEnabled", 640 baseSpeedPriorityEnabled); 641 642 if (!success) 643 { 644 messages::internalError(asyncResp->res); 645 return; 646 } 647 648 if (appliedConfig != nullptr) 649 { 650 const std::string& dbusPath = appliedConfig->str; 651 nlohmann::json::object_t operatingConfig; 652 operatingConfig["@odata.id"] = boost::urls::format( 653 "/redfish/v1/Systems/system/Processors/{}/OperatingConfigs", 654 cpuId); 655 json["OperatingConfigs"] = std::move(operatingConfig); 656 657 // Reuse the D-Bus config object name for the Redfish 658 // URI 659 size_t baseNamePos = dbusPath.rfind('/'); 660 if (baseNamePos == std::string::npos || 661 baseNamePos == (dbusPath.size() - 1)) 662 { 663 // If the AppliedConfig was somehow not a valid path, 664 // skip adding any more properties, since everything 665 // else is tied to this applied config. 666 messages::internalError(asyncResp->res); 667 return; 668 } 669 nlohmann::json::object_t appliedOperatingConfig; 670 appliedOperatingConfig["@odata.id"] = boost::urls::format( 671 "/redfish/v1/Systems/system/Processors/{}/OperatingConfigs/{}", 672 cpuId, 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 D-Bus response from attempting to set the CPU's AppliedConfig 1009 * property. Main task is to translate error messages into Redfish errors. 1010 * 1011 * @param[in,out] resp HTTP response. 1012 * @param[in] setPropVal Value which we attempted to set. 1013 * @param[in] ec D-Bus response error code. 1014 * @param[in] msg D-Bus response message. 1015 */ 1016 inline void 1017 handleAppliedConfigResponse(const std::shared_ptr<bmcweb::AsyncResp>& resp, 1018 const std::string& setPropVal, 1019 const boost::system::error_code& ec, 1020 const sdbusplus::message_t& msg) 1021 { 1022 if (!ec) 1023 { 1024 BMCWEB_LOG_DEBUG("Set Property succeeded"); 1025 return; 1026 } 1027 1028 BMCWEB_LOG_DEBUG("Set Property failed: {}", ec); 1029 1030 const sd_bus_error* dbusError = msg.get_error(); 1031 if (dbusError == nullptr) 1032 { 1033 messages::internalError(resp->res); 1034 return; 1035 } 1036 1037 // The asio error code doesn't know about our custom errors, so we have to 1038 // parse the error string. Some of these D-Bus -> Redfish translations are a 1039 // stretch, but it's good to try to communicate something vaguely useful. 1040 if (strcmp(dbusError->name, 1041 "xyz.openbmc_project.Common.Error.InvalidArgument") == 0) 1042 { 1043 // Service did not like the object_path we tried to set. 1044 messages::propertyValueIncorrect( 1045 resp->res, "AppliedOperatingConfig/@odata.id", setPropVal); 1046 } 1047 else if (strcmp(dbusError->name, 1048 "xyz.openbmc_project.Common.Error.NotAllowed") == 0) 1049 { 1050 // Service indicates we can never change the config for this processor. 1051 messages::propertyNotWritable(resp->res, "AppliedOperatingConfig"); 1052 } 1053 else if (strcmp(dbusError->name, 1054 "xyz.openbmc_project.Common.Error.Unavailable") == 0) 1055 { 1056 // Service indicates the config cannot be changed right now, but maybe 1057 // in a different system state. 1058 messages::resourceInStandby(resp->res); 1059 } 1060 else 1061 { 1062 messages::internalError(resp->res); 1063 } 1064 } 1065 1066 /** 1067 * Handle the PATCH operation of the AppliedOperatingConfig property. Do basic 1068 * validation of the input data, and then set the D-Bus property. 1069 * 1070 * @param[in,out] resp Async HTTP response. 1071 * @param[in] processorId Processor's Id. 1072 * @param[in] appliedConfigUri New property value to apply. 1073 * @param[in] cpuObjectPath Path of CPU object to modify. 1074 * @param[in] serviceMap Service map for CPU object. 1075 */ 1076 inline void patchAppliedOperatingConfig( 1077 const std::shared_ptr<bmcweb::AsyncResp>& resp, 1078 const std::string& processorId, const std::string& appliedConfigUri, 1079 const std::string& cpuObjectPath, 1080 const dbus::utility::MapperServiceMap& serviceMap) 1081 { 1082 // Check that the property even exists by checking for the interface 1083 const std::string* controlService = nullptr; 1084 for (const auto& [serviceName, interfaceList] : serviceMap) 1085 { 1086 if (std::ranges::find(interfaceList, 1087 "xyz.openbmc_project.Control.Processor." 1088 "CurrentOperatingConfig") != interfaceList.end()) 1089 { 1090 controlService = &serviceName; 1091 break; 1092 } 1093 } 1094 1095 if (controlService == nullptr) 1096 { 1097 messages::internalError(resp->res); 1098 return; 1099 } 1100 1101 // Check that the config URI is a child of the cpu URI being patched. 1102 std::string expectedPrefix("/redfish/v1/Systems/system/Processors/"); 1103 expectedPrefix += processorId; 1104 expectedPrefix += "/OperatingConfigs/"; 1105 if (!appliedConfigUri.starts_with(expectedPrefix) || 1106 expectedPrefix.size() == appliedConfigUri.size()) 1107 { 1108 messages::propertyValueIncorrect( 1109 resp->res, "AppliedOperatingConfig/@odata.id", appliedConfigUri); 1110 return; 1111 } 1112 1113 // Generate the D-Bus path of the OperatingConfig object, by assuming it's a 1114 // direct child of the CPU object. 1115 // Strip the expectedPrefix from the config URI to get the "filename", and 1116 // append to the CPU's path. 1117 std::string configBaseName = appliedConfigUri.substr(expectedPrefix.size()); 1118 sdbusplus::message::object_path configPath(cpuObjectPath); 1119 configPath /= configBaseName; 1120 1121 BMCWEB_LOG_INFO("Setting config to {}", configPath.str); 1122 1123 // Set the property, with handler to check error responses 1124 sdbusplus::asio::setProperty( 1125 *crow::connections::systemBus, *controlService, cpuObjectPath, 1126 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", 1127 "AppliedConfig", configPath, 1128 [resp, appliedConfigUri](const boost::system::error_code& ec, 1129 const sdbusplus::message_t& msg) { 1130 handleAppliedConfigResponse(resp, appliedConfigUri, ec, msg); 1131 }); 1132 } 1133 1134 inline void 1135 handleProcessorHead(crow::App& app, const crow::Request& req, 1136 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1137 const std::string& /* systemName */, 1138 const std::string& /* processorId */) 1139 { 1140 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1141 { 1142 return; 1143 } 1144 asyncResp->res.addHeader( 1145 boost::beast::http::field::link, 1146 "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); 1147 } 1148 1149 inline void handleProcessorCollectionHead( 1150 crow::App& app, const crow::Request& req, 1151 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1152 const std::string& /* systemName */) 1153 { 1154 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1155 { 1156 return; 1157 } 1158 asyncResp->res.addHeader( 1159 boost::beast::http::field::link, 1160 "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); 1161 } 1162 1163 inline void requestRoutesOperatingConfigCollection(App& app) 1164 { 1165 BMCWEB_ROUTE(app, 1166 "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/") 1167 .privileges(redfish::privileges::getOperatingConfigCollection) 1168 .methods(boost::beast::http::verb::get)( 1169 [&app](const crow::Request& req, 1170 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1171 const std::string& systemName, const std::string& cpuName) { 1172 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1173 { 1174 return; 1175 } 1176 1177 if constexpr (bmcwebEnableMultiHost) 1178 { 1179 // Option currently returns no systems. TBD 1180 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1181 systemName); 1182 return; 1183 } 1184 1185 if (systemName != "system") 1186 { 1187 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1188 systemName); 1189 return; 1190 } 1191 asyncResp->res.jsonValue["@odata.type"] = 1192 "#OperatingConfigCollection.OperatingConfigCollection"; 1193 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1194 "/redfish/v1/Systems/system/Processors/{}/OperatingConfigs", 1195 cpuName); 1196 asyncResp->res.jsonValue["Name"] = "Operating Config Collection"; 1197 1198 // First find the matching CPU object so we know how to 1199 // constrain our search for related Config objects. 1200 const std::array<std::string_view, 1> interfaces = { 1201 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}; 1202 dbus::utility::getSubTreePaths( 1203 "/xyz/openbmc_project/inventory", 0, interfaces, 1204 [asyncResp, cpuName]( 1205 const boost::system::error_code& ec, 1206 const dbus::utility::MapperGetSubTreePathsResponse& objects) { 1207 if (ec) 1208 { 1209 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); 1210 messages::internalError(asyncResp->res); 1211 return; 1212 } 1213 1214 for (const std::string& object : objects) 1215 { 1216 if (!object.ends_with(cpuName)) 1217 { 1218 continue; 1219 } 1220 1221 // Not expected that there will be multiple matching 1222 // CPU objects, but if there are just use the first 1223 // one. 1224 1225 // Use the common search routine to construct the 1226 // Collection of all Config objects under this CPU. 1227 constexpr std::array<std::string_view, 1> interface{ 1228 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; 1229 collection_util::getCollectionMembers( 1230 asyncResp, 1231 boost::urls::format( 1232 "/redfish/v1/Systems/system/Processors/{}/OperatingConfigs", 1233 cpuName), 1234 interface, object); 1235 return; 1236 } 1237 }); 1238 }); 1239 } 1240 1241 inline void requestRoutesOperatingConfig(App& app) 1242 { 1243 BMCWEB_ROUTE( 1244 app, 1245 "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/<str>/") 1246 .privileges(redfish::privileges::getOperatingConfig) 1247 .methods(boost::beast::http::verb::get)( 1248 [&app](const crow::Request& req, 1249 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1250 const std::string& systemName, const std::string& cpuName, 1251 const std::string& configName) { 1252 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1253 { 1254 return; 1255 } 1256 if constexpr (bmcwebEnableMultiHost) 1257 { 1258 // Option currently returns no systems. TBD 1259 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1260 systemName); 1261 return; 1262 } 1263 1264 if (systemName != "system") 1265 { 1266 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1267 systemName); 1268 return; 1269 } 1270 // Ask for all objects implementing OperatingConfig so we can search 1271 // for one with a matching name 1272 constexpr std::array<std::string_view, 1> interfaces = { 1273 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; 1274 dbus::utility::getSubTree( 1275 "/xyz/openbmc_project/inventory", 0, interfaces, 1276 [asyncResp, cpuName, configName]( 1277 const boost::system::error_code& ec, 1278 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1279 if (ec) 1280 { 1281 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); 1282 messages::internalError(asyncResp->res); 1283 return; 1284 } 1285 const std::string expectedEnding = cpuName + '/' + configName; 1286 for (const auto& [objectPath, serviceMap] : subtree) 1287 { 1288 // Ignore any configs without matching cpuX/configY 1289 if (!objectPath.ends_with(expectedEnding) || serviceMap.empty()) 1290 { 1291 continue; 1292 } 1293 1294 nlohmann::json& json = asyncResp->res.jsonValue; 1295 json["@odata.type"] = "#OperatingConfig.v1_0_0.OperatingConfig"; 1296 json["@odata.id"] = boost::urls::format( 1297 "/redfish/v1/Systems/system/Processors/{}/OperatingConfigs/{}", 1298 cpuName, configName); 1299 json["Name"] = "Processor Profile"; 1300 json["Id"] = configName; 1301 1302 // Just use the first implementation of the object - not 1303 // expected that there would be multiple matching 1304 // services 1305 getOperatingConfigData(asyncResp, serviceMap.begin()->first, 1306 objectPath); 1307 return; 1308 } 1309 messages::resourceNotFound(asyncResp->res, "OperatingConfig", 1310 configName); 1311 }); 1312 }); 1313 } 1314 1315 inline void requestRoutesProcessorCollection(App& app) 1316 { 1317 /** 1318 * Functions triggers appropriate requests on DBus 1319 */ 1320 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") 1321 .privileges(redfish::privileges::headProcessorCollection) 1322 .methods(boost::beast::http::verb::head)( 1323 std::bind_front(handleProcessorCollectionHead, std::ref(app))); 1324 1325 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") 1326 .privileges(redfish::privileges::getProcessorCollection) 1327 .methods(boost::beast::http::verb::get)( 1328 [&app](const crow::Request& req, 1329 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1330 const std::string& systemName) { 1331 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1332 { 1333 return; 1334 } 1335 if constexpr (bmcwebEnableMultiHost) 1336 { 1337 // Option currently returns no systems. TBD 1338 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1339 systemName); 1340 return; 1341 } 1342 1343 if (systemName != "system") 1344 { 1345 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1346 systemName); 1347 return; 1348 } 1349 1350 asyncResp->res.addHeader( 1351 boost::beast::http::field::link, 1352 "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); 1353 1354 asyncResp->res.jsonValue["@odata.type"] = 1355 "#ProcessorCollection.ProcessorCollection"; 1356 asyncResp->res.jsonValue["Name"] = "Processor Collection"; 1357 1358 asyncResp->res.jsonValue["@odata.id"] = 1359 "/redfish/v1/Systems/system/Processors"; 1360 1361 collection_util::getCollectionMembers( 1362 asyncResp, 1363 boost::urls::url("/redfish/v1/Systems/system/Processors"), 1364 processorInterfaces, "/xyz/openbmc_project/inventory"); 1365 }); 1366 } 1367 1368 inline void requestRoutesProcessor(App& app) 1369 { 1370 /** 1371 * Functions triggers appropriate requests on DBus 1372 */ 1373 1374 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") 1375 .privileges(redfish::privileges::headProcessor) 1376 .methods(boost::beast::http::verb::head)( 1377 std::bind_front(handleProcessorHead, std::ref(app))); 1378 1379 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") 1380 .privileges(redfish::privileges::getProcessor) 1381 .methods(boost::beast::http::verb::get)( 1382 [&app](const crow::Request& req, 1383 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1384 const std::string& systemName, 1385 const std::string& processorId) { 1386 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1387 { 1388 return; 1389 } 1390 if constexpr (bmcwebEnableMultiHost) 1391 { 1392 // Option currently returns no systems. TBD 1393 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1394 systemName); 1395 return; 1396 } 1397 if (systemName != "system") 1398 { 1399 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1400 systemName); 1401 return; 1402 } 1403 1404 asyncResp->res.addHeader( 1405 boost::beast::http::field::link, 1406 "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); 1407 asyncResp->res.jsonValue["@odata.type"] = 1408 "#Processor.v1_18_0.Processor"; 1409 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1410 "/redfish/v1/Systems/system/Processors/{}", processorId); 1411 1412 getProcessorObject( 1413 asyncResp, processorId, 1414 std::bind_front(getProcessorData, asyncResp, processorId)); 1415 }); 1416 1417 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") 1418 .privileges(redfish::privileges::patchProcessor) 1419 .methods(boost::beast::http::verb::patch)( 1420 [&app](const crow::Request& req, 1421 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1422 const std::string& systemName, 1423 const std::string& processorId) { 1424 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1425 { 1426 return; 1427 } 1428 if constexpr (bmcwebEnableMultiHost) 1429 { 1430 // Option currently returns no systems. TBD 1431 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1432 systemName); 1433 return; 1434 } 1435 if (systemName != "system") 1436 { 1437 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1438 systemName); 1439 return; 1440 } 1441 1442 std::optional<nlohmann::json> appliedConfigJson; 1443 if (!json_util::readJsonPatch(req, asyncResp->res, 1444 "AppliedOperatingConfig", 1445 appliedConfigJson)) 1446 { 1447 return; 1448 } 1449 1450 if (appliedConfigJson) 1451 { 1452 std::string appliedConfigUri; 1453 if (!json_util::readJson(*appliedConfigJson, asyncResp->res, 1454 "@odata.id", appliedConfigUri)) 1455 { 1456 return; 1457 } 1458 // Check for 404 and find matching D-Bus object, then run 1459 // property patch handlers if that all succeeds. 1460 getProcessorObject(asyncResp, processorId, 1461 std::bind_front(patchAppliedOperatingConfig, 1462 asyncResp, processorId, 1463 appliedConfigUri)); 1464 } 1465 }); 1466 } 1467 1468 } // namespace redfish 1469