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