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 <string_view> 41 42 namespace redfish 43 { 44 45 // Interfaces which imply a D-Bus object represents a Processor 46 constexpr std::array<std::string_view, 2> processorInterfaces = { 47 "xyz.openbmc_project.Inventory.Item.Cpu", 48 "xyz.openbmc_project.Inventory.Item.Accelerator"}; 49 50 /** 51 * @brief Fill out uuid info of a processor by 52 * requesting data from the given D-Bus object. 53 * 54 * @param[in,out] asyncResp Async HTTP response. 55 * @param[in] service D-Bus service to query. 56 * @param[in] objPath D-Bus object to query. 57 */ 58 inline void getProcessorUUID(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 59 const std::string& service, 60 const std::string& objPath) 61 { 62 BMCWEB_LOG_DEBUG << "Get Processor UUID"; 63 sdbusplus::asio::getProperty<std::string>( 64 *crow::connections::systemBus, service, objPath, 65 "xyz.openbmc_project.Common.UUID", "UUID", 66 [objPath, asyncResp{std::move(asyncResp)}]( 67 const boost::system::error_code& ec, const std::string& property) { 68 if (ec) 69 { 70 BMCWEB_LOG_DEBUG << "DBUS response error"; 71 messages::internalError(asyncResp->res); 72 return; 73 } 74 asyncResp->res.jsonValue["UUID"] = property; 75 }); 76 } 77 78 inline void getCpuDataByInterface( 79 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 80 const dbus::utility::DBusInteracesMap& cpuInterfacesProperties) 81 { 82 BMCWEB_LOG_DEBUG << "Get CPU resources by interface."; 83 84 // Set the default value of state 85 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 86 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 87 88 for (const auto& interface : cpuInterfacesProperties) 89 { 90 for (const auto& property : interface.second) 91 { 92 if (property.first == "Present") 93 { 94 const bool* cpuPresent = std::get_if<bool>(&property.second); 95 if (cpuPresent == nullptr) 96 { 97 // Important property not in desired type 98 messages::internalError(asyncResp->res); 99 return; 100 } 101 if (!*cpuPresent) 102 { 103 // Slot is not populated 104 asyncResp->res.jsonValue["Status"]["State"] = "Absent"; 105 } 106 } 107 else if (property.first == "Functional") 108 { 109 const bool* cpuFunctional = std::get_if<bool>(&property.second); 110 if (cpuFunctional == nullptr) 111 { 112 messages::internalError(asyncResp->res); 113 return; 114 } 115 if (!*cpuFunctional) 116 { 117 asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; 118 } 119 } 120 else if (property.first == "CoreCount") 121 { 122 const uint16_t* coresCount = 123 std::get_if<uint16_t>(&property.second); 124 if (coresCount == nullptr) 125 { 126 messages::internalError(asyncResp->res); 127 return; 128 } 129 asyncResp->res.jsonValue["TotalCores"] = *coresCount; 130 } 131 else if (property.first == "MaxSpeedInMhz") 132 { 133 const uint32_t* value = std::get_if<uint32_t>(&property.second); 134 if (value != nullptr) 135 { 136 asyncResp->res.jsonValue["MaxSpeedMHz"] = *value; 137 } 138 } 139 else if (property.first == "Socket") 140 { 141 const std::string* value = 142 std::get_if<std::string>(&property.second); 143 if (value != nullptr) 144 { 145 asyncResp->res.jsonValue["Socket"] = *value; 146 } 147 } 148 else if (property.first == "ThreadCount") 149 { 150 const uint16_t* value = std::get_if<uint16_t>(&property.second); 151 if (value != nullptr) 152 { 153 asyncResp->res.jsonValue["TotalThreads"] = *value; 154 } 155 } 156 else if (property.first == "EffectiveFamily") 157 { 158 const uint16_t* value = std::get_if<uint16_t>(&property.second); 159 if (value != nullptr && *value != 2) 160 { 161 asyncResp->res.jsonValue["ProcessorId"]["EffectiveFamily"] = 162 "0x" + intToHexString(*value, 4); 163 } 164 } 165 else if (property.first == "EffectiveModel") 166 { 167 const uint16_t* value = std::get_if<uint16_t>(&property.second); 168 if (value == nullptr) 169 { 170 messages::internalError(asyncResp->res); 171 return; 172 } 173 if (*value != 0) 174 { 175 asyncResp->res.jsonValue["ProcessorId"]["EffectiveModel"] = 176 "0x" + intToHexString(*value, 4); 177 } 178 } 179 else if (property.first == "Id") 180 { 181 const uint64_t* value = std::get_if<uint64_t>(&property.second); 182 if (value != nullptr && *value != 0) 183 { 184 asyncResp->res 185 .jsonValue["ProcessorId"]["IdentificationRegisters"] = 186 "0x" + intToHexString(*value, 16); 187 } 188 } 189 else if (property.first == "Microcode") 190 { 191 const uint32_t* value = std::get_if<uint32_t>(&property.second); 192 if (value == nullptr) 193 { 194 messages::internalError(asyncResp->res); 195 return; 196 } 197 if (*value != 0) 198 { 199 asyncResp->res.jsonValue["ProcessorId"]["MicrocodeInfo"] = 200 "0x" + intToHexString(*value, 8); 201 } 202 } 203 else if (property.first == "Step") 204 { 205 const uint16_t* value = std::get_if<uint16_t>(&property.second); 206 if (value == nullptr) 207 { 208 messages::internalError(asyncResp->res); 209 return; 210 } 211 if (*value != std::numeric_limits<uint16_t>::max()) 212 { 213 asyncResp->res.jsonValue["ProcessorId"]["Step"] = 214 "0x" + intToHexString(*value, 4); 215 } 216 } 217 } 218 } 219 } 220 221 inline void getCpuDataByService(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 222 const std::string& cpuId, 223 const std::string& service, 224 const std::string& objPath) 225 { 226 BMCWEB_LOG_DEBUG << "Get available system cpu resources by service."; 227 228 crow::connections::systemBus->async_method_call( 229 [cpuId, service, objPath, asyncResp{std::move(asyncResp)}]( 230 const boost::system::error_code& ec, 231 const dbus::utility::ManagedObjectType& dbusData) { 232 if (ec) 233 { 234 BMCWEB_LOG_DEBUG << "DBUS response error"; 235 messages::internalError(asyncResp->res); 236 return; 237 } 238 asyncResp->res.jsonValue["Id"] = cpuId; 239 asyncResp->res.jsonValue["Name"] = "Processor"; 240 asyncResp->res.jsonValue["ProcessorType"] = "CPU"; 241 242 bool slotPresent = false; 243 std::string corePath = objPath + "/core"; 244 size_t totalCores = 0; 245 for (const auto& object : dbusData) 246 { 247 if (object.first.str == objPath) 248 { 249 getCpuDataByInterface(asyncResp, object.second); 250 } 251 else if (object.first.str.starts_with(corePath)) 252 { 253 for (const auto& interface : object.second) 254 { 255 if (interface.first == "xyz.openbmc_project.Inventory.Item") 256 { 257 for (const auto& property : interface.second) 258 { 259 if (property.first == "Present") 260 { 261 const bool* present = 262 std::get_if<bool>(&property.second); 263 if (present != nullptr) 264 { 265 if (*present) 266 { 267 slotPresent = true; 268 totalCores++; 269 } 270 } 271 } 272 } 273 } 274 } 275 } 276 } 277 // In getCpuDataByInterface(), state and health are set 278 // based on the present and functional status. If core 279 // count is zero, then it has a higher precedence. 280 if (slotPresent) 281 { 282 if (totalCores == 0) 283 { 284 // Slot is not populated, set status end return 285 asyncResp->res.jsonValue["Status"]["State"] = "Absent"; 286 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 287 } 288 asyncResp->res.jsonValue["TotalCores"] = totalCores; 289 } 290 return; 291 }, 292 service, "/xyz/openbmc_project/inventory", 293 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 294 } 295 296 /** 297 * @brief Translates throttle cause DBUS property to redfish. 298 * 299 * @param[in] dbusSource The throttle cause from DBUS 300 * 301 * @return Returns as a string, the throttle cause in Redfish terms. If 302 * translation cannot be done, returns "Unknown" throttle reason. 303 */ 304 inline processor::ThrottleCause 305 dbusToRfThrottleCause(const std::string& dbusSource) 306 { 307 if (dbusSource == 308 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.ClockLimit") 309 { 310 return processor::ThrottleCause::ClockLimit; 311 } 312 if (dbusSource == 313 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.ManagementDetectedFault") 314 { 315 return processor::ThrottleCause::ManagementDetectedFault; 316 } 317 if (dbusSource == 318 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.PowerLimit") 319 { 320 return processor::ThrottleCause::PowerLimit; 321 } 322 if (dbusSource == 323 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.ThermalLimit") 324 { 325 return processor::ThrottleCause::ThermalLimit; 326 } 327 if (dbusSource == 328 "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.Unknown") 329 { 330 return processor::ThrottleCause::Unknown; 331 } 332 return processor::ThrottleCause::Invalid; 333 } 334 335 inline void 336 readThrottleProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 337 const boost::system::error_code& ec, 338 const dbus::utility::DBusPropertiesMap& properties) 339 { 340 if (ec) 341 { 342 BMCWEB_LOG_ERROR << "Processor Throttle getAllProperties error " << ec; 343 messages::internalError(asyncResp->res); 344 return; 345 } 346 347 const bool* status = nullptr; 348 const std::vector<std::string>* causes = nullptr; 349 350 if (!sdbusplus::unpackPropertiesNoThrow(dbus_utils::UnpackErrorPrinter(), 351 properties, "Throttled", status, 352 "ThrottleCauses", causes)) 353 { 354 messages::internalError(asyncResp->res); 355 return; 356 } 357 358 asyncResp->res.jsonValue["Throttled"] = *status; 359 nlohmann::json::array_t rCauses; 360 for (const std::string& cause : *causes) 361 { 362 processor::ThrottleCause rfCause = dbusToRfThrottleCause(cause); 363 if (rfCause == processor::ThrottleCause::Invalid) 364 { 365 messages::internalError(asyncResp->res); 366 return; 367 } 368 369 rCauses.emplace_back(rfCause); 370 } 371 asyncResp->res.jsonValue["ThrottleCauses"] = std::move(rCauses); 372 } 373 374 inline void 375 getThrottleProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 376 const std::string& service, 377 const std::string& objectPath) 378 { 379 BMCWEB_LOG_DEBUG << "Get processor throttle resources"; 380 381 sdbusplus::asio::getAllProperties( 382 *crow::connections::systemBus, service, objectPath, 383 "xyz.openbmc_project.Control.Power.Throttle", 384 [asyncResp](const boost::system::error_code& ec, 385 const dbus::utility::DBusPropertiesMap& properties) { 386 readThrottleProperties(asyncResp, ec, properties); 387 }); 388 } 389 390 inline void getCpuAssetData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 391 const std::string& service, 392 const std::string& objPath) 393 { 394 BMCWEB_LOG_DEBUG << "Get Cpu Asset Data"; 395 sdbusplus::asio::getAllProperties( 396 *crow::connections::systemBus, service, objPath, 397 "xyz.openbmc_project.Inventory.Decorator.Asset", 398 [objPath, asyncResp{std::move(asyncResp)}]( 399 const boost::system::error_code& ec, 400 const dbus::utility::DBusPropertiesMap& properties) { 401 if (ec) 402 { 403 BMCWEB_LOG_DEBUG << "DBUS response error"; 404 messages::internalError(asyncResp->res); 405 return; 406 } 407 408 const std::string* serialNumber = nullptr; 409 const std::string* model = nullptr; 410 const std::string* manufacturer = nullptr; 411 const std::string* partNumber = nullptr; 412 const std::string* sparePartNumber = nullptr; 413 414 const bool success = sdbusplus::unpackPropertiesNoThrow( 415 dbus_utils::UnpackErrorPrinter(), properties, "SerialNumber", 416 serialNumber, "Model", model, "Manufacturer", manufacturer, 417 "PartNumber", partNumber, "SparePartNumber", sparePartNumber); 418 419 if (!success) 420 { 421 messages::internalError(asyncResp->res); 422 return; 423 } 424 425 if (serialNumber != nullptr && !serialNumber->empty()) 426 { 427 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 428 } 429 430 if ((model != nullptr) && !model->empty()) 431 { 432 asyncResp->res.jsonValue["Model"] = *model; 433 } 434 435 if (manufacturer != nullptr) 436 { 437 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; 438 439 // Otherwise would be unexpected. 440 if (manufacturer->find("Intel") != std::string::npos) 441 { 442 asyncResp->res.jsonValue["ProcessorArchitecture"] = "x86"; 443 asyncResp->res.jsonValue["InstructionSet"] = "x86-64"; 444 } 445 else if (manufacturer->find("IBM") != std::string::npos) 446 { 447 asyncResp->res.jsonValue["ProcessorArchitecture"] = "Power"; 448 asyncResp->res.jsonValue["InstructionSet"] = "PowerISA"; 449 } 450 } 451 452 if (partNumber != nullptr) 453 { 454 asyncResp->res.jsonValue["PartNumber"] = *partNumber; 455 } 456 457 if (sparePartNumber != nullptr && !sparePartNumber->empty()) 458 { 459 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 460 } 461 }); 462 } 463 464 inline void getCpuRevisionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 465 const std::string& service, 466 const std::string& objPath) 467 { 468 BMCWEB_LOG_DEBUG << "Get Cpu Revision Data"; 469 sdbusplus::asio::getAllProperties( 470 *crow::connections::systemBus, service, objPath, 471 "xyz.openbmc_project.Inventory.Decorator.Revision", 472 [objPath, asyncResp{std::move(asyncResp)}]( 473 const boost::system::error_code& ec, 474 const dbus::utility::DBusPropertiesMap& properties) { 475 if (ec) 476 { 477 BMCWEB_LOG_DEBUG << "DBUS response error"; 478 messages::internalError(asyncResp->res); 479 return; 480 } 481 482 const std::string* version = nullptr; 483 484 const bool success = sdbusplus::unpackPropertiesNoThrow( 485 dbus_utils::UnpackErrorPrinter(), properties, "Version", version); 486 487 if (!success) 488 { 489 messages::internalError(asyncResp->res); 490 return; 491 } 492 493 if (version != nullptr) 494 { 495 asyncResp->res.jsonValue["Version"] = *version; 496 } 497 }); 498 } 499 500 inline void getAcceleratorDataByService( 501 std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& acclrtrId, 502 const std::string& service, const std::string& objPath) 503 { 504 BMCWEB_LOG_DEBUG 505 << "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::find_first_of( 819 interfaceList.begin(), interfaceList.end(), 820 processorInterfaces.begin(), 821 processorInterfaces.end()) != interfaceList.end()) 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::find(interfaceList.begin(), interfaceList.end(), 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 crow::connections::systemBus->async_method_call( 1126 [resp, appliedConfigUri](const boost::system::error_code& ec, 1127 const sdbusplus::message_t& msg) { 1128 handleAppliedConfigResponse(resp, appliedConfigUri, ec, msg); 1129 }, 1130 *controlService, cpuObjectPath, "org.freedesktop.DBus.Properties", 1131 "Set", "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", 1132 "AppliedConfig", dbus::utility::DbusVariantType(std::move(configPath))); 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( 1167 app, "/redfish/v1/Systems/system/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& cpuName) { 1173 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1174 { 1175 return; 1176 } 1177 asyncResp->res.jsonValue["@odata.type"] = 1178 "#OperatingConfigCollection.OperatingConfigCollection"; 1179 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1180 "/redfish/v1/Systems/system/Processors/{}/OperatingConfigs", 1181 cpuName); 1182 asyncResp->res.jsonValue["Name"] = "Operating Config Collection"; 1183 1184 // First find the matching CPU object so we know how to 1185 // constrain our search for related Config objects. 1186 const std::array<std::string_view, 1> interfaces = { 1187 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}; 1188 dbus::utility::getSubTreePaths( 1189 "/xyz/openbmc_project/inventory", 0, interfaces, 1190 [asyncResp, cpuName]( 1191 const boost::system::error_code& ec, 1192 const dbus::utility::MapperGetSubTreePathsResponse& objects) { 1193 if (ec) 1194 { 1195 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 1196 << ec.message(); 1197 messages::internalError(asyncResp->res); 1198 return; 1199 } 1200 1201 for (const std::string& object : objects) 1202 { 1203 if (!object.ends_with(cpuName)) 1204 { 1205 continue; 1206 } 1207 1208 // Not expected that there will be multiple matching 1209 // CPU objects, but if there are just use the first 1210 // one. 1211 1212 // Use the common search routine to construct the 1213 // Collection of all Config objects under this CPU. 1214 constexpr std::array<std::string_view, 1> interface { 1215 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig" 1216 }; 1217 collection_util::getCollectionMembers( 1218 asyncResp, 1219 boost::urls::format( 1220 "/redfish/v1/Systems/system/Processors/{}/OperatingConfigs", 1221 cpuName), 1222 interface, object.c_str()); 1223 return; 1224 } 1225 }); 1226 }); 1227 } 1228 1229 inline void requestRoutesOperatingConfig(App& app) 1230 { 1231 BMCWEB_ROUTE( 1232 app, 1233 "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/<str>/") 1234 .privileges(redfish::privileges::getOperatingConfig) 1235 .methods(boost::beast::http::verb::get)( 1236 [&app](const crow::Request& req, 1237 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1238 const std::string& cpuName, const std::string& configName) { 1239 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1240 { 1241 return; 1242 } 1243 // Ask for all objects implementing OperatingConfig so we can search 1244 // for one with a matching name 1245 constexpr std::array<std::string_view, 1> interfaces = { 1246 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; 1247 dbus::utility::getSubTree( 1248 "/xyz/openbmc_project/inventory", 0, interfaces, 1249 [asyncResp, cpuName, configName]( 1250 const boost::system::error_code& ec, 1251 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1252 if (ec) 1253 { 1254 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 1255 << ec.message(); 1256 messages::internalError(asyncResp->res); 1257 return; 1258 } 1259 const std::string expectedEnding = cpuName + '/' + configName; 1260 for (const auto& [objectPath, serviceMap] : subtree) 1261 { 1262 // Ignore any configs without matching cpuX/configY 1263 if (!objectPath.ends_with(expectedEnding) || serviceMap.empty()) 1264 { 1265 continue; 1266 } 1267 1268 nlohmann::json& json = asyncResp->res.jsonValue; 1269 json["@odata.type"] = "#OperatingConfig.v1_0_0.OperatingConfig"; 1270 json["@odata.id"] = boost::urls::format( 1271 "/redfish/v1/Systems/system/Processors/{}/OperatingConfigs/{}", 1272 cpuName, configName); 1273 json["Name"] = "Processor Profile"; 1274 json["Id"] = configName; 1275 1276 // Just use the first implementation of the object - not 1277 // expected that there would be multiple matching 1278 // services 1279 getOperatingConfigData(asyncResp, serviceMap.begin()->first, 1280 objectPath); 1281 return; 1282 } 1283 messages::resourceNotFound(asyncResp->res, "OperatingConfig", 1284 configName); 1285 }); 1286 }); 1287 } 1288 1289 inline void requestRoutesProcessorCollection(App& app) 1290 { 1291 /** 1292 * Functions triggers appropriate requests on DBus 1293 */ 1294 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") 1295 .privileges(redfish::privileges::headProcessorCollection) 1296 .methods(boost::beast::http::verb::head)( 1297 std::bind_front(handleProcessorCollectionHead, std::ref(app))); 1298 1299 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") 1300 .privileges(redfish::privileges::getProcessorCollection) 1301 .methods(boost::beast::http::verb::get)( 1302 [&app](const crow::Request& req, 1303 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1304 const std::string& systemName) { 1305 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1306 { 1307 return; 1308 } 1309 if (systemName != "system") 1310 { 1311 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1312 systemName); 1313 return; 1314 } 1315 1316 asyncResp->res.addHeader( 1317 boost::beast::http::field::link, 1318 "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); 1319 1320 asyncResp->res.jsonValue["@odata.type"] = 1321 "#ProcessorCollection.ProcessorCollection"; 1322 asyncResp->res.jsonValue["Name"] = "Processor Collection"; 1323 1324 asyncResp->res.jsonValue["@odata.id"] = 1325 "/redfish/v1/Systems/system/Processors"; 1326 1327 collection_util::getCollectionMembers( 1328 asyncResp, 1329 boost::urls::url("/redfish/v1/Systems/system/Processors"), 1330 processorInterfaces); 1331 }); 1332 } 1333 1334 inline void requestRoutesProcessor(App& app) 1335 { 1336 /** 1337 * Functions triggers appropriate requests on DBus 1338 */ 1339 1340 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") 1341 .privileges(redfish::privileges::headProcessor) 1342 .methods(boost::beast::http::verb::head)( 1343 std::bind_front(handleProcessorHead, std::ref(app))); 1344 1345 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") 1346 .privileges(redfish::privileges::getProcessor) 1347 .methods(boost::beast::http::verb::get)( 1348 [&app](const crow::Request& req, 1349 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1350 const std::string& systemName, 1351 const std::string& processorId) { 1352 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1353 { 1354 return; 1355 } 1356 if (systemName != "system") 1357 { 1358 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1359 systemName); 1360 return; 1361 } 1362 1363 asyncResp->res.addHeader( 1364 boost::beast::http::field::link, 1365 "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); 1366 asyncResp->res.jsonValue["@odata.type"] = 1367 "#Processor.v1_18_0.Processor"; 1368 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 1369 "/redfish/v1/Systems/system/Processors/{}", processorId); 1370 1371 getProcessorObject( 1372 asyncResp, processorId, 1373 std::bind_front(getProcessorData, asyncResp, processorId)); 1374 }); 1375 1376 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") 1377 .privileges(redfish::privileges::patchProcessor) 1378 .methods(boost::beast::http::verb::patch)( 1379 [&app](const crow::Request& req, 1380 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1381 const std::string& systemName, 1382 const std::string& processorId) { 1383 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1384 { 1385 return; 1386 } 1387 if (systemName != "system") 1388 { 1389 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 1390 systemName); 1391 return; 1392 } 1393 1394 std::optional<nlohmann::json> appliedConfigJson; 1395 if (!json_util::readJsonPatch(req, asyncResp->res, 1396 "AppliedOperatingConfig", 1397 appliedConfigJson)) 1398 { 1399 return; 1400 } 1401 1402 if (appliedConfigJson) 1403 { 1404 std::string appliedConfigUri; 1405 if (!json_util::readJson(*appliedConfigJson, asyncResp->res, 1406 "@odata.id", appliedConfigUri)) 1407 { 1408 return; 1409 } 1410 // Check for 404 and find matching D-Bus object, then run 1411 // property patch handlers if that all succeeds. 1412 getProcessorObject(asyncResp, processorId, 1413 std::bind_front(patchAppliedOperatingConfig, 1414 asyncResp, processorId, 1415 appliedConfigUri)); 1416 } 1417 }); 1418 } 1419 1420 } // namespace redfish 1421