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 "health.hpp" 19 20 #include <boost/container/flat_map.hpp> 21 #include <node.hpp> 22 #include <sdbusplus/message/native_types.hpp> 23 #include <sdbusplus/utility/dedup_variant.hpp> 24 #include <utils/collection.hpp> 25 #include <utils/json_utils.hpp> 26 27 namespace redfish 28 { 29 30 using InterfacesProperties = boost::container::flat_map< 31 std::string, 32 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>>; 33 34 using MapperGetSubTreeResponse = std::vector< 35 std::pair<std::string, 36 std::vector<std::pair<std::string, std::vector<std::string>>>>>; 37 38 inline void 39 getCpuDataByInterface(const std::shared_ptr<AsyncResp>& aResp, 40 const InterfacesProperties& cpuInterfacesProperties) 41 { 42 BMCWEB_LOG_DEBUG << "Get CPU resources by interface."; 43 44 // Added for future purpose. Once present and functional attributes added 45 // in busctl call, need to add actual logic to fetch original values. 46 bool present = false; 47 const bool functional = true; 48 auto health = std::make_shared<HealthPopulate>(aResp); 49 health->populate(); 50 51 for (const auto& interface : cpuInterfacesProperties) 52 { 53 for (const auto& property : interface.second) 54 { 55 if (property.first == "CoreCount") 56 { 57 const uint16_t* coresCount = 58 std::get_if<uint16_t>(&property.second); 59 if (coresCount == nullptr) 60 { 61 // Important property not in desired type 62 messages::internalError(aResp->res); 63 return; 64 } 65 if (*coresCount == 0) 66 { 67 // Slot is not populated, set status end return 68 aResp->res.jsonValue["Status"]["State"] = "Absent"; 69 // HTTP Code will be set up automatically, just return 70 return; 71 } 72 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 73 present = true; 74 aResp->res.jsonValue["TotalCores"] = *coresCount; 75 } 76 else if (property.first == "MaxSpeedInMhz") 77 { 78 const uint32_t* value = std::get_if<uint32_t>(&property.second); 79 if (value != nullptr) 80 { 81 aResp->res.jsonValue["MaxSpeedMHz"] = *value; 82 } 83 } 84 else if (property.first == "Socket") 85 { 86 const std::string* value = 87 std::get_if<std::string>(&property.second); 88 if (value != nullptr) 89 { 90 aResp->res.jsonValue["Socket"] = *value; 91 } 92 } 93 else if (property.first == "ThreadCount") 94 { 95 const uint16_t* value = std::get_if<uint16_t>(&property.second); 96 if (value != nullptr) 97 { 98 aResp->res.jsonValue["TotalThreads"] = *value; 99 } 100 } 101 else if (property.first == "Family") 102 { 103 const std::string* value = 104 std::get_if<std::string>(&property.second); 105 if (value != nullptr) 106 { 107 aResp->res.jsonValue["ProcessorId"]["EffectiveFamily"] = 108 *value; 109 } 110 } 111 else if (property.first == "Id") 112 { 113 const uint64_t* value = std::get_if<uint64_t>(&property.second); 114 if (value != nullptr && *value != 0) 115 { 116 present = true; 117 aResp->res 118 .jsonValue["ProcessorId"]["IdentificationRegisters"] = 119 boost::lexical_cast<std::string>(*value); 120 } 121 } 122 } 123 } 124 125 if (present == false) 126 { 127 aResp->res.jsonValue["Status"]["State"] = "Absent"; 128 aResp->res.jsonValue["Status"]["Health"] = "OK"; 129 } 130 else 131 { 132 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 133 if (functional) 134 { 135 aResp->res.jsonValue["Status"]["Health"] = "OK"; 136 } 137 else 138 { 139 aResp->res.jsonValue["Status"]["Health"] = "Critical"; 140 } 141 } 142 143 return; 144 } 145 146 inline void getCpuDataByService(std::shared_ptr<AsyncResp> aResp, 147 const std::string& cpuId, 148 const std::string& service, 149 const std::string& objPath) 150 { 151 BMCWEB_LOG_DEBUG << "Get available system cpu resources by service."; 152 153 crow::connections::systemBus->async_method_call( 154 [cpuId, service, objPath, aResp{std::move(aResp)}]( 155 const boost::system::error_code ec, 156 const dbus::utility::ManagedObjectType& dbusData) { 157 if (ec) 158 { 159 BMCWEB_LOG_DEBUG << "DBUS response error"; 160 messages::internalError(aResp->res); 161 return; 162 } 163 aResp->res.jsonValue["Id"] = cpuId; 164 aResp->res.jsonValue["Name"] = "Processor"; 165 aResp->res.jsonValue["ProcessorType"] = "CPU"; 166 167 bool slotPresent = false; 168 std::string corePath = objPath + "/core"; 169 size_t totalCores = 0; 170 for (const auto& object : dbusData) 171 { 172 if (object.first.str == objPath) 173 { 174 getCpuDataByInterface(aResp, object.second); 175 } 176 else if (boost::starts_with(object.first.str, corePath)) 177 { 178 for (const auto& interface : object.second) 179 { 180 if (interface.first == 181 "xyz.openbmc_project.Inventory.Item") 182 { 183 for (const auto& property : interface.second) 184 { 185 if (property.first == "Present") 186 { 187 const bool* present = 188 std::get_if<bool>(&property.second); 189 if (present != nullptr) 190 { 191 if (*present == true) 192 { 193 slotPresent = true; 194 totalCores++; 195 } 196 } 197 } 198 } 199 } 200 } 201 } 202 } 203 // In getCpuDataByInterface(), state and health are set 204 // based on the present and functional status. If core 205 // count is zero, then it has a higher precedence. 206 if (slotPresent) 207 { 208 if (totalCores == 0) 209 { 210 // Slot is not populated, set status end return 211 aResp->res.jsonValue["Status"]["State"] = "Absent"; 212 aResp->res.jsonValue["Status"]["Health"] = "OK"; 213 } 214 aResp->res.jsonValue["TotalCores"] = totalCores; 215 } 216 return; 217 }, 218 service, "/xyz/openbmc_project/inventory", 219 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 220 } 221 222 inline void getCpuAssetData(std::shared_ptr<AsyncResp> aResp, 223 const std::string& service, 224 const std::string& objPath) 225 { 226 BMCWEB_LOG_DEBUG << "Get Cpu Asset Data"; 227 crow::connections::systemBus->async_method_call( 228 [objPath, aResp{std::move(aResp)}]( 229 const boost::system::error_code ec, 230 const boost::container::flat_map< 231 std::string, std::variant<std::string, uint32_t, uint16_t, 232 bool>>& properties) { 233 if (ec) 234 { 235 BMCWEB_LOG_DEBUG << "DBUS response error"; 236 messages::internalError(aResp->res); 237 return; 238 } 239 240 for (const auto& property : properties) 241 { 242 if (property.first == "SerialNumber") 243 { 244 const std::string* sn = 245 std::get_if<std::string>(&property.second); 246 if (sn != nullptr && !sn->empty()) 247 { 248 aResp->res.jsonValue["SerialNumber"] = *sn; 249 } 250 } 251 else if (property.first == "Model") 252 { 253 const std::string* model = 254 std::get_if<std::string>(&property.second); 255 if (model != nullptr && !model->empty()) 256 { 257 aResp->res.jsonValue["Model"] = *model; 258 } 259 } 260 else if (property.first == "Manufacturer") 261 { 262 263 const std::string* mfg = 264 std::get_if<std::string>(&property.second); 265 if (mfg != nullptr) 266 { 267 aResp->res.jsonValue["Manufacturer"] = *mfg; 268 269 // Otherwise would be unexpected. 270 if (mfg->find("Intel") != std::string::npos) 271 { 272 aResp->res.jsonValue["ProcessorArchitecture"] = 273 "x86"; 274 aResp->res.jsonValue["InstructionSet"] = "x86-64"; 275 } 276 else if (mfg->find("IBM") != std::string::npos) 277 { 278 aResp->res.jsonValue["ProcessorArchitecture"] = 279 "Power"; 280 aResp->res.jsonValue["InstructionSet"] = "PowerISA"; 281 } 282 } 283 } 284 else if (property.first == "PartNumber") 285 { 286 const std::string* partNumber = 287 std::get_if<std::string>(&property.second); 288 289 if (partNumber == nullptr) 290 { 291 messages::internalError(aResp->res); 292 return; 293 } 294 aResp->res.jsonValue["PartNumber"] = *partNumber; 295 } 296 else if (property.first == "SparePartNumber") 297 { 298 const std::string* sparePartNumber = 299 std::get_if<std::string>(&property.second); 300 301 if (sparePartNumber == nullptr) 302 { 303 messages::internalError(aResp->res); 304 return; 305 } 306 aResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 307 } 308 } 309 }, 310 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 311 "xyz.openbmc_project.Inventory.Decorator.Asset"); 312 } 313 314 inline void getCpuRevisionData(std::shared_ptr<AsyncResp> aResp, 315 const std::string& service, 316 const std::string& objPath) 317 { 318 BMCWEB_LOG_DEBUG << "Get Cpu Revision Data"; 319 crow::connections::systemBus->async_method_call( 320 [objPath, aResp{std::move(aResp)}]( 321 const boost::system::error_code ec, 322 const boost::container::flat_map< 323 std::string, std::variant<std::string, uint32_t, uint16_t, 324 bool>>& properties) { 325 if (ec) 326 { 327 BMCWEB_LOG_DEBUG << "DBUS response error"; 328 messages::internalError(aResp->res); 329 return; 330 } 331 332 for (const auto& property : properties) 333 { 334 if (property.first == "Version") 335 { 336 const std::string* ver = 337 std::get_if<std::string>(&property.second); 338 if (ver != nullptr) 339 { 340 aResp->res.jsonValue["Version"] = *ver; 341 } 342 break; 343 } 344 } 345 }, 346 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 347 "xyz.openbmc_project.Inventory.Decorator.Revision"); 348 } 349 350 inline void getAcceleratorDataByService(std::shared_ptr<AsyncResp> aResp, 351 const std::string& acclrtrId, 352 const std::string& service, 353 const std::string& objPath) 354 { 355 BMCWEB_LOG_DEBUG 356 << "Get available system Accelerator resources by service."; 357 crow::connections::systemBus->async_method_call( 358 [acclrtrId, aResp{std::move(aResp)}]( 359 const boost::system::error_code ec, 360 const boost::container::flat_map< 361 std::string, std::variant<std::string, uint32_t, uint16_t, 362 bool>>& properties) { 363 if (ec) 364 { 365 BMCWEB_LOG_DEBUG << "DBUS response error"; 366 messages::internalError(aResp->res); 367 return; 368 } 369 aResp->res.jsonValue["Id"] = acclrtrId; 370 aResp->res.jsonValue["Name"] = "Processor"; 371 const bool* accPresent = nullptr; 372 const bool* accFunctional = nullptr; 373 374 for (const auto& property : properties) 375 { 376 if (property.first == "Functional") 377 { 378 accFunctional = std::get_if<bool>(&property.second); 379 } 380 else if (property.first == "Present") 381 { 382 accPresent = std::get_if<bool>(&property.second); 383 } 384 } 385 386 std::string state = "Enabled"; 387 std::string health = "OK"; 388 389 if (accPresent != nullptr && *accPresent == false) 390 { 391 state = "Absent"; 392 } 393 394 if ((accFunctional != nullptr) && (*accFunctional == false)) 395 { 396 if (state == "Enabled") 397 { 398 health = "Critical"; 399 } 400 } 401 402 aResp->res.jsonValue["Status"]["State"] = state; 403 aResp->res.jsonValue["Status"]["Health"] = health; 404 aResp->res.jsonValue["ProcessorType"] = "Accelerator"; 405 }, 406 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", ""); 407 } 408 409 // OperatingConfig D-Bus Types 410 using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>; 411 using BaseSpeedPrioritySettingsProperty = 412 std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>; 413 // uint32_t and size_t may or may not be the same type, requiring a dedup'd 414 // variant 415 using OperatingConfigProperties = std::vector<std::pair< 416 std::string, 417 sdbusplus::utility::dedup_variant<uint32_t, size_t, TurboProfileProperty, 418 BaseSpeedPrioritySettingsProperty>>>; 419 420 /** 421 * Fill out the HighSpeedCoreIDs in a Processor resource from the given 422 * OperatingConfig D-Bus property. 423 * 424 * @param[in,out] aResp Async HTTP response. 425 * @param[in] baseSpeedSettings Full list of base speed priority groups, 426 * to use to determine the list of high 427 * speed cores. 428 */ 429 inline void highSpeedCoreIdsHandler( 430 const std::shared_ptr<AsyncResp>& aResp, 431 const BaseSpeedPrioritySettingsProperty& baseSpeedSettings) 432 { 433 // The D-Bus property does not indicate which bucket is the "high 434 // priority" group, so let's discern that by looking for the one with 435 // highest base frequency. 436 auto highPriorityGroup = baseSpeedSettings.cend(); 437 uint32_t highestBaseSpeed = 0; 438 for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend(); 439 ++it) 440 { 441 const uint32_t baseFreq = std::get<uint32_t>(*it); 442 if (baseFreq > highestBaseSpeed) 443 { 444 highestBaseSpeed = baseFreq; 445 highPriorityGroup = it; 446 } 447 } 448 449 nlohmann::json& jsonCoreIds = aResp->res.jsonValue["HighSpeedCoreIDs"]; 450 jsonCoreIds = nlohmann::json::array(); 451 452 // There may not be any entries in the D-Bus property, so only populate 453 // if there was actually something there. 454 if (highPriorityGroup != baseSpeedSettings.cend()) 455 { 456 jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup); 457 } 458 } 459 460 /** 461 * Fill out OperatingConfig related items in a Processor resource by requesting 462 * data from the given D-Bus object. 463 * 464 * @param[in,out] aResp Async HTTP response. 465 * @param[in] cpuId CPU D-Bus name. 466 * @param[in] service D-Bus service to query. 467 * @param[in] objPath D-Bus object to query. 468 */ 469 inline void getCpuConfigData(const std::shared_ptr<AsyncResp>& aResp, 470 const std::string& cpuId, 471 const std::string& service, 472 const std::string& objPath) 473 { 474 BMCWEB_LOG_INFO << "Getting CPU operating configs for " << cpuId; 475 476 // First, GetAll CurrentOperatingConfig properties on the object 477 crow::connections::systemBus->async_method_call( 478 [aResp, cpuId, service]( 479 const boost::system::error_code ec, 480 const std::vector< 481 std::pair<std::string, 482 std::variant<sdbusplus::message::object_path, bool>>>& 483 properties) { 484 if (ec) 485 { 486 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 487 << ec.message(); 488 messages::internalError(aResp->res); 489 return; 490 } 491 492 nlohmann::json& json = aResp->res.jsonValue; 493 494 for (const auto& [dbusPropName, variantVal] : properties) 495 { 496 if (dbusPropName == "AppliedConfig") 497 { 498 const sdbusplus::message::object_path* dbusPathWrapper = 499 std::get_if<sdbusplus::message::object_path>( 500 &variantVal); 501 if (dbusPathWrapper == nullptr) 502 { 503 continue; 504 } 505 506 const std::string& dbusPath = dbusPathWrapper->str; 507 std::string uri = "/redfish/v1/Systems/system/Processors/" + 508 cpuId + "/OperatingConfigs"; 509 json["OperatingConfigs"] = {{"@odata.id", uri}}; 510 511 // Reuse the D-Bus config object name for the Redfish 512 // URI 513 size_t baseNamePos = dbusPath.rfind('/'); 514 if (baseNamePos == std::string::npos || 515 baseNamePos == (dbusPath.size() - 1)) 516 { 517 // If the AppliedConfig was somehow not a valid path, 518 // skip adding any more properties, since everything 519 // else is tied to this applied config. 520 messages::internalError(aResp->res); 521 break; 522 } 523 uri += '/'; 524 uri += dbusPath.substr(baseNamePos + 1); 525 json["AppliedOperatingConfig"] = {{"@odata.id", uri}}; 526 527 // Once we found the current applied config, queue another 528 // request to read the base freq core ids out of that 529 // config. 530 crow::connections::systemBus->async_method_call( 531 [aResp]( 532 const boost::system::error_code ec, 533 const std::variant< 534 BaseSpeedPrioritySettingsProperty>& property) { 535 if (ec) 536 { 537 BMCWEB_LOG_WARNING 538 << "D-Bus Property Get error: " << ec; 539 messages::internalError(aResp->res); 540 return; 541 } 542 auto baseSpeedList = 543 std::get_if<BaseSpeedPrioritySettingsProperty>( 544 &property); 545 if (baseSpeedList != nullptr) 546 { 547 highSpeedCoreIdsHandler(aResp, *baseSpeedList); 548 } 549 }, 550 service, dbusPath, "org.freedesktop.DBus.Properties", 551 "Get", 552 "xyz.openbmc_project.Inventory.Item.Cpu." 553 "OperatingConfig", 554 "BaseSpeedPrioritySettings"); 555 } 556 else if (dbusPropName == "BaseSpeedPriorityEnabled") 557 { 558 const bool* state = std::get_if<bool>(&variantVal); 559 if (state != nullptr) 560 { 561 json["BaseSpeedPriorityState"] = 562 *state ? "Enabled" : "Disabled"; 563 } 564 } 565 } 566 }, 567 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 568 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"); 569 } 570 571 /** 572 * @brief Fill out location info of a processor by 573 * requesting data from the given D-Bus object. 574 * 575 * @param[in,out] aResp Async HTTP response. 576 * @param[in] service D-Bus service to query. 577 * @param[in] objPath D-Bus object to query. 578 */ 579 inline void getCpuLocationCode(std::shared_ptr<AsyncResp> aResp, 580 const std::string& service, 581 const std::string& objPath) 582 { 583 BMCWEB_LOG_DEBUG << "Get Cpu Location Data"; 584 crow::connections::systemBus->async_method_call( 585 [objPath, 586 aResp{std::move(aResp)}](const boost::system::error_code ec, 587 const std::variant<std::string>& property) { 588 if (ec) 589 { 590 BMCWEB_LOG_DEBUG << "DBUS response error"; 591 messages::internalError(aResp->res); 592 return; 593 } 594 595 const std::string* value = std::get_if<std::string>(&property); 596 597 if (value == nullptr) 598 { 599 // illegal value 600 BMCWEB_LOG_DEBUG << "Location code value error"; 601 messages::internalError(aResp->res); 602 return; 603 } 604 605 aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 606 *value; 607 }, 608 service, objPath, "org.freedesktop.DBus.Properties", "Get", 609 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode"); 610 } 611 612 inline void getProcessorData(std::shared_ptr<AsyncResp> aResp, 613 const std::string& processorId) 614 { 615 BMCWEB_LOG_DEBUG << "Get available system processor resources."; 616 617 crow::connections::systemBus->async_method_call( 618 [processorId, 619 aResp{std::move(aResp)}](const boost::system::error_code ec, 620 const MapperGetSubTreeResponse& subtree) { 621 if (ec) 622 { 623 BMCWEB_LOG_DEBUG << "DBUS response error"; 624 messages::internalError(aResp->res); 625 return; 626 } 627 for (const auto& [objectPath, serviceMap] : subtree) 628 { 629 // Ignore any objects which don't end with our desired cpu name 630 if (!boost::ends_with(objectPath, processorId)) 631 { 632 continue; 633 } 634 635 // Process the first object which does match our cpu name 636 // suffix, and potentially ignore any other matching objects. 637 // Assume all interfaces we want to process must be on the same 638 // object. 639 640 for (const auto& [serviceName, interfaceList] : serviceMap) 641 { 642 for (const auto& interface : interfaceList) 643 { 644 if (interface == 645 "xyz.openbmc_project.Inventory.Decorator.Asset") 646 { 647 getCpuAssetData(aResp, serviceName, objectPath); 648 } 649 else if (interface == "xyz.openbmc_project.Inventory." 650 "Decorator.Revision") 651 { 652 getCpuRevisionData(aResp, serviceName, objectPath); 653 } 654 else if (interface == 655 "xyz.openbmc_project.Inventory.Item.Cpu") 656 { 657 getCpuDataByService(aResp, processorId, serviceName, 658 objectPath); 659 } 660 else if (interface == "xyz.openbmc_project.Inventory." 661 "Item.Accelerator") 662 { 663 getAcceleratorDataByService( 664 aResp, processorId, serviceName, objectPath); 665 } 666 else if (interface == 667 "xyz.openbmc_project.Control.Processor." 668 "CurrentOperatingConfig") 669 { 670 getCpuConfigData(aResp, processorId, serviceName, 671 objectPath); 672 } 673 else if (interface == "xyz.openbmc_project.Inventory." 674 "Decorator.LocationCode") 675 { 676 getCpuLocationCode(aResp, serviceName, objectPath); 677 } 678 } 679 } 680 return; 681 } 682 // Object not found 683 messages::resourceNotFound(aResp->res, "Processor", processorId); 684 return; 685 }, 686 "xyz.openbmc_project.ObjectMapper", 687 "/xyz/openbmc_project/object_mapper", 688 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 689 "/xyz/openbmc_project/inventory", 0, 690 std::array<const char*, 6>{ 691 "xyz.openbmc_project.Inventory.Decorator.Asset", 692 "xyz.openbmc_project.Inventory.Decorator.Revision", 693 "xyz.openbmc_project.Inventory.Item.Cpu", 694 "xyz.openbmc_project.Inventory.Decorator.LocationCode", 695 "xyz.openbmc_project.Inventory.Item.Accelerator", 696 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}); 697 } 698 699 /** 700 * Request all the properties for the given D-Bus object and fill out the 701 * related entries in the Redfish OperatingConfig response. 702 * 703 * @param[in,out] aResp Async HTTP response. 704 * @param[in] service D-Bus service name to query. 705 * @param[in] objPath D-Bus object to query. 706 */ 707 inline void getOperatingConfigData(const std::shared_ptr<AsyncResp>& aResp, 708 const std::string& service, 709 const std::string& objPath) 710 { 711 crow::connections::systemBus->async_method_call( 712 [aResp](boost::system::error_code ec, 713 const OperatingConfigProperties& properties) { 714 if (ec) 715 { 716 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 717 << ec.message(); 718 messages::internalError(aResp->res); 719 return; 720 } 721 722 nlohmann::json& json = aResp->res.jsonValue; 723 for (const auto& [key, variant] : properties) 724 { 725 if (key == "AvailableCoreCount") 726 { 727 const size_t* cores = std::get_if<size_t>(&variant); 728 if (cores != nullptr) 729 { 730 json["TotalAvailableCoreCount"] = *cores; 731 } 732 } 733 else if (key == "BaseSpeed") 734 { 735 const uint32_t* speed = std::get_if<uint32_t>(&variant); 736 if (speed != nullptr) 737 { 738 json["BaseSpeedMHz"] = *speed; 739 } 740 } 741 else if (key == "MaxJunctionTemperature") 742 { 743 const uint32_t* temp = std::get_if<uint32_t>(&variant); 744 if (temp != nullptr) 745 { 746 json["MaxJunctionTemperatureCelsius"] = *temp; 747 } 748 } 749 else if (key == "MaxSpeed") 750 { 751 const uint32_t* speed = std::get_if<uint32_t>(&variant); 752 if (speed != nullptr) 753 { 754 json["MaxSpeedMHz"] = *speed; 755 } 756 } 757 else if (key == "PowerLimit") 758 { 759 const uint32_t* tdp = std::get_if<uint32_t>(&variant); 760 if (tdp != nullptr) 761 { 762 json["TDPWatts"] = *tdp; 763 } 764 } 765 else if (key == "TurboProfile") 766 { 767 const auto* turboList = 768 std::get_if<TurboProfileProperty>(&variant); 769 if (turboList == nullptr) 770 { 771 continue; 772 } 773 774 nlohmann::json& turboArray = json["TurboProfile"]; 775 turboArray = nlohmann::json::array(); 776 for (const auto& [turboSpeed, coreCount] : *turboList) 777 { 778 turboArray.push_back({{"ActiveCoreCount", coreCount}, 779 {"MaxSpeedMHz", turboSpeed}}); 780 } 781 } 782 else if (key == "BaseSpeedPrioritySettings") 783 { 784 const auto* baseSpeedList = 785 std::get_if<BaseSpeedPrioritySettingsProperty>( 786 &variant); 787 if (baseSpeedList == nullptr) 788 { 789 continue; 790 } 791 792 nlohmann::json& baseSpeedArray = 793 json["BaseSpeedPrioritySettings"]; 794 baseSpeedArray = nlohmann::json::array(); 795 for (const auto& [baseSpeed, coreList] : *baseSpeedList) 796 { 797 baseSpeedArray.push_back( 798 {{"CoreCount", coreList.size()}, 799 {"CoreIDs", coreList}, 800 {"BaseSpeedMHz", baseSpeed}}); 801 } 802 } 803 } 804 }, 805 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 806 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"); 807 } 808 809 class OperatingConfigCollection : public Node 810 { 811 public: 812 OperatingConfigCollection(App& app) : 813 Node(app, 814 "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/", 815 std::string()) 816 { 817 // Defined by Redfish spec privilege registry 818 entityPrivileges = { 819 {boost::beast::http::verb::get, {{"Login"}}}, 820 {boost::beast::http::verb::head, {{"Login"}}}, 821 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 822 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 823 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 824 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 825 } 826 827 private: 828 void doGet(crow::Response& res, const crow::Request& req, 829 const std::vector<std::string>& params) override 830 { 831 if (params.size() != 1) 832 { 833 messages::internalError(res); 834 res.end(); 835 return; 836 } 837 838 const std::string& cpuName = params[0]; 839 res.jsonValue["@odata.type"] = 840 "#OperatingConfigCollection.OperatingConfigCollection"; 841 res.jsonValue["@odata.id"] = req.url; 842 res.jsonValue["Name"] = "Operating Config Collection"; 843 844 auto asyncResp = std::make_shared<AsyncResp>(res); 845 846 // First find the matching CPU object so we know how to constrain our 847 // search for related Config objects. 848 crow::connections::systemBus->async_method_call( 849 [asyncResp, cpuName](const boost::system::error_code ec, 850 const std::vector<std::string>& objects) { 851 if (ec) 852 { 853 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 854 << ec.message(); 855 messages::internalError(asyncResp->res); 856 return; 857 } 858 859 for (const std::string& object : objects) 860 { 861 if (!boost::ends_with(object, cpuName)) 862 { 863 continue; 864 } 865 866 // Not expected that there will be multiple matching CPU 867 // objects, but if there are just use the first one. 868 869 // Use the common search routine to construct the Collection 870 // of all Config objects under this CPU. 871 collection_util::getCollectionMembers( 872 asyncResp, 873 "/redfish/v1/Systems/system/Processors/" + cpuName + 874 "/OperatingConfigs", 875 {"xyz.openbmc_project.Inventory.Item.Cpu." 876 "OperatingConfig"}, 877 object.c_str()); 878 return; 879 } 880 }, 881 "xyz.openbmc_project.ObjectMapper", 882 "/xyz/openbmc_project/object_mapper", 883 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 884 "/xyz/openbmc_project/inventory", 0, 885 std::array<const char*, 1>{"xyz.openbmc_project.Control.Processor." 886 "CurrentOperatingConfig"}); 887 } 888 }; 889 890 class OperatingConfig : public Node 891 { 892 public: 893 OperatingConfig(App& app) : 894 Node(app, 895 "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/" 896 "<str>/", 897 std::string(), std::string()) 898 { 899 // Defined by Redfish spec privilege registry 900 entityPrivileges = { 901 {boost::beast::http::verb::get, {{"Login"}}}, 902 {boost::beast::http::verb::head, {{"Login"}}}, 903 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 904 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 905 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 906 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 907 } 908 909 private: 910 void doGet(crow::Response& res, const crow::Request& req, 911 const std::vector<std::string>& params) override 912 { 913 if (params.size() != 2) 914 { 915 messages::internalError(res); 916 res.end(); 917 return; 918 } 919 920 const std::string& cpuName = params[0]; 921 const std::string& configName = params[1]; 922 923 auto asyncResp = std::make_shared<AsyncResp>(res); 924 925 // Ask for all objects implementing OperatingConfig so we can search for 926 // one with a matching name 927 crow::connections::systemBus->async_method_call( 928 [asyncResp, cpuName, configName, 929 reqUrl{req.url}](boost::system::error_code ec, 930 const MapperGetSubTreeResponse& subtree) { 931 if (ec) 932 { 933 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 934 << ec.message(); 935 messages::internalError(asyncResp->res); 936 return; 937 } 938 const std::string expectedEnding = cpuName + '/' + configName; 939 for (const auto& [objectPath, serviceMap] : subtree) 940 { 941 // Ignore any configs without matching cpuX/configY 942 if (!boost::ends_with(objectPath, expectedEnding) || 943 serviceMap.empty()) 944 { 945 continue; 946 } 947 948 nlohmann::json& json = asyncResp->res.jsonValue; 949 json["@odata.type"] = 950 "#OperatingConfig.v1_0_0.OperatingConfig"; 951 json["@odata.id"] = reqUrl; 952 json["Name"] = "Processor Profile"; 953 json["Id"] = configName; 954 955 // Just use the first implementation of the object - not 956 // expected that there would be multiple matching services 957 getOperatingConfigData(asyncResp, serviceMap.begin()->first, 958 objectPath); 959 return; 960 } 961 messages::resourceNotFound(asyncResp->res, "OperatingConfig", 962 configName); 963 }, 964 "xyz.openbmc_project.ObjectMapper", 965 "/xyz/openbmc_project/object_mapper", 966 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 967 "/xyz/openbmc_project/inventory", 0, 968 std::array<const char*, 1>{ 969 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}); 970 } 971 }; 972 973 class ProcessorCollection : public Node 974 { 975 public: 976 /* 977 * Default Constructor 978 */ 979 ProcessorCollection(App& app) : 980 Node(app, "/redfish/v1/Systems/system/Processors/") 981 { 982 entityPrivileges = { 983 {boost::beast::http::verb::get, {{"Login"}}}, 984 {boost::beast::http::verb::head, {{"Login"}}}, 985 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 986 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 987 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 988 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 989 } 990 991 private: 992 /** 993 * Functions triggers appropriate requests on DBus 994 */ 995 void doGet(crow::Response& res, const crow::Request&, 996 const std::vector<std::string>&) override 997 { 998 res.jsonValue["@odata.type"] = 999 "#ProcessorCollection.ProcessorCollection"; 1000 res.jsonValue["Name"] = "Processor Collection"; 1001 1002 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Processors"; 1003 auto asyncResp = std::make_shared<AsyncResp>(res); 1004 1005 collection_util::getCollectionMembers( 1006 asyncResp, "/redfish/v1/Systems/system/Processors", 1007 {"xyz.openbmc_project.Inventory.Item.Cpu", 1008 "xyz.openbmc_project.Inventory.Item.Accelerator"}); 1009 } 1010 }; 1011 1012 class Processor : public Node 1013 { 1014 public: 1015 /* 1016 * Default Constructor 1017 */ 1018 Processor(App& app) : 1019 Node(app, "/redfish/v1/Systems/system/Processors/<str>/", std::string()) 1020 { 1021 entityPrivileges = { 1022 {boost::beast::http::verb::get, {{"Login"}}}, 1023 {boost::beast::http::verb::head, {{"Login"}}}, 1024 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1025 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1026 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1027 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1028 } 1029 1030 private: 1031 /** 1032 * Functions triggers appropriate requests on DBus 1033 */ 1034 void doGet(crow::Response& res, const crow::Request&, 1035 const std::vector<std::string>& params) override 1036 { 1037 // Check if there is required param, truly entering this shall be 1038 // impossible 1039 if (params.size() != 1) 1040 { 1041 messages::internalError(res); 1042 1043 res.end(); 1044 return; 1045 } 1046 const std::string& processorId = params[0]; 1047 res.jsonValue["@odata.type"] = "#Processor.v1_11_0.Processor"; 1048 res.jsonValue["@odata.id"] = 1049 "/redfish/v1/Systems/system/Processors/" + processorId; 1050 1051 auto asyncResp = std::make_shared<AsyncResp>(res); 1052 1053 getProcessorData(asyncResp, processorId); 1054 } 1055 }; 1056 1057 } // namespace redfish 1058