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<bmcweb::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<bmcweb::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<bmcweb::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<bmcweb::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( 360 std::shared_ptr<bmcweb::AsyncResp> aResp, const std::string& acclrtrId, 361 const std::string& service, const std::string& objPath) 362 { 363 BMCWEB_LOG_DEBUG 364 << "Get available system Accelerator resources by service."; 365 crow::connections::systemBus->async_method_call( 366 [acclrtrId, aResp{std::move(aResp)}]( 367 const boost::system::error_code ec, 368 const boost::container::flat_map< 369 std::string, std::variant<std::string, uint32_t, uint16_t, 370 bool>>& properties) { 371 if (ec) 372 { 373 BMCWEB_LOG_DEBUG << "DBUS response error"; 374 messages::internalError(aResp->res); 375 return; 376 } 377 aResp->res.jsonValue["Id"] = acclrtrId; 378 aResp->res.jsonValue["Name"] = "Processor"; 379 const bool* accPresent = nullptr; 380 const bool* accFunctional = nullptr; 381 382 for (const auto& property : properties) 383 { 384 if (property.first == "Functional") 385 { 386 accFunctional = std::get_if<bool>(&property.second); 387 } 388 else if (property.first == "Present") 389 { 390 accPresent = std::get_if<bool>(&property.second); 391 } 392 } 393 394 std::string state = "Enabled"; 395 std::string health = "OK"; 396 397 if (accPresent != nullptr && *accPresent == false) 398 { 399 state = "Absent"; 400 } 401 402 if ((accFunctional != nullptr) && (*accFunctional == false)) 403 { 404 if (state == "Enabled") 405 { 406 health = "Critical"; 407 } 408 } 409 410 aResp->res.jsonValue["Status"]["State"] = state; 411 aResp->res.jsonValue["Status"]["Health"] = health; 412 aResp->res.jsonValue["ProcessorType"] = "Accelerator"; 413 }, 414 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", ""); 415 } 416 417 // OperatingConfig D-Bus Types 418 using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>; 419 using BaseSpeedPrioritySettingsProperty = 420 std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>; 421 // uint32_t and size_t may or may not be the same type, requiring a dedup'd 422 // variant 423 using OperatingConfigProperties = std::vector<std::pair< 424 std::string, 425 sdbusplus::utility::dedup_variant<uint32_t, size_t, TurboProfileProperty, 426 BaseSpeedPrioritySettingsProperty>>>; 427 428 /** 429 * Fill out the HighSpeedCoreIDs in a Processor resource from the given 430 * OperatingConfig D-Bus property. 431 * 432 * @param[in,out] aResp Async HTTP response. 433 * @param[in] baseSpeedSettings Full list of base speed priority groups, 434 * to use to determine the list of high 435 * speed cores. 436 */ 437 inline void highSpeedCoreIdsHandler( 438 const std::shared_ptr<bmcweb::AsyncResp>& aResp, 439 const BaseSpeedPrioritySettingsProperty& baseSpeedSettings) 440 { 441 // The D-Bus property does not indicate which bucket is the "high 442 // priority" group, so let's discern that by looking for the one with 443 // highest base frequency. 444 auto highPriorityGroup = baseSpeedSettings.cend(); 445 uint32_t highestBaseSpeed = 0; 446 for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend(); 447 ++it) 448 { 449 const uint32_t baseFreq = std::get<uint32_t>(*it); 450 if (baseFreq > highestBaseSpeed) 451 { 452 highestBaseSpeed = baseFreq; 453 highPriorityGroup = it; 454 } 455 } 456 457 nlohmann::json& jsonCoreIds = aResp->res.jsonValue["HighSpeedCoreIDs"]; 458 jsonCoreIds = nlohmann::json::array(); 459 460 // There may not be any entries in the D-Bus property, so only populate 461 // if there was actually something there. 462 if (highPriorityGroup != baseSpeedSettings.cend()) 463 { 464 jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup); 465 } 466 } 467 468 /** 469 * Fill out OperatingConfig related items in a Processor resource by requesting 470 * data from the given D-Bus object. 471 * 472 * @param[in,out] aResp Async HTTP response. 473 * @param[in] cpuId CPU D-Bus name. 474 * @param[in] service D-Bus service to query. 475 * @param[in] objPath D-Bus object to query. 476 */ 477 inline void getCpuConfigData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 478 const std::string& cpuId, 479 const std::string& service, 480 const std::string& objPath) 481 { 482 BMCWEB_LOG_INFO << "Getting CPU operating configs for " << cpuId; 483 484 // First, GetAll CurrentOperatingConfig properties on the object 485 crow::connections::systemBus->async_method_call( 486 [aResp, cpuId, service]( 487 const boost::system::error_code ec, 488 const std::vector< 489 std::pair<std::string, 490 std::variant<sdbusplus::message::object_path, bool>>>& 491 properties) { 492 if (ec) 493 { 494 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 495 << ec.message(); 496 messages::internalError(aResp->res); 497 return; 498 } 499 500 nlohmann::json& json = aResp->res.jsonValue; 501 502 for (const auto& [dbusPropName, variantVal] : properties) 503 { 504 if (dbusPropName == "AppliedConfig") 505 { 506 const sdbusplus::message::object_path* dbusPathWrapper = 507 std::get_if<sdbusplus::message::object_path>( 508 &variantVal); 509 if (dbusPathWrapper == nullptr) 510 { 511 continue; 512 } 513 514 const std::string& dbusPath = dbusPathWrapper->str; 515 std::string uri = "/redfish/v1/Systems/system/Processors/" + 516 cpuId + "/OperatingConfigs"; 517 json["OperatingConfigs"] = {{"@odata.id", uri}}; 518 519 // Reuse the D-Bus config object name for the Redfish 520 // URI 521 size_t baseNamePos = dbusPath.rfind('/'); 522 if (baseNamePos == std::string::npos || 523 baseNamePos == (dbusPath.size() - 1)) 524 { 525 // If the AppliedConfig was somehow not a valid path, 526 // skip adding any more properties, since everything 527 // else is tied to this applied config. 528 messages::internalError(aResp->res); 529 break; 530 } 531 uri += '/'; 532 uri += dbusPath.substr(baseNamePos + 1); 533 json["AppliedOperatingConfig"] = {{"@odata.id", uri}}; 534 535 // Once we found the current applied config, queue another 536 // request to read the base freq core ids out of that 537 // config. 538 crow::connections::systemBus->async_method_call( 539 [aResp]( 540 const boost::system::error_code ec, 541 const std::variant< 542 BaseSpeedPrioritySettingsProperty>& property) { 543 if (ec) 544 { 545 BMCWEB_LOG_WARNING 546 << "D-Bus Property Get error: " << ec; 547 messages::internalError(aResp->res); 548 return; 549 } 550 auto baseSpeedList = 551 std::get_if<BaseSpeedPrioritySettingsProperty>( 552 &property); 553 if (baseSpeedList != nullptr) 554 { 555 highSpeedCoreIdsHandler(aResp, *baseSpeedList); 556 } 557 }, 558 service, dbusPath, "org.freedesktop.DBus.Properties", 559 "Get", 560 "xyz.openbmc_project.Inventory.Item.Cpu." 561 "OperatingConfig", 562 "BaseSpeedPrioritySettings"); 563 } 564 else if (dbusPropName == "BaseSpeedPriorityEnabled") 565 { 566 const bool* state = std::get_if<bool>(&variantVal); 567 if (state != nullptr) 568 { 569 json["BaseSpeedPriorityState"] = 570 *state ? "Enabled" : "Disabled"; 571 } 572 } 573 } 574 }, 575 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 576 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"); 577 } 578 579 /** 580 * @brief Fill out location info of a processor by 581 * requesting data from the given D-Bus object. 582 * 583 * @param[in,out] aResp Async HTTP response. 584 * @param[in] service D-Bus service to query. 585 * @param[in] objPath D-Bus object to query. 586 */ 587 inline void getCpuLocationCode(std::shared_ptr<bmcweb::AsyncResp> aResp, 588 const std::string& service, 589 const std::string& objPath) 590 { 591 BMCWEB_LOG_DEBUG << "Get Cpu Location Data"; 592 crow::connections::systemBus->async_method_call( 593 [objPath, 594 aResp{std::move(aResp)}](const boost::system::error_code ec, 595 const std::variant<std::string>& property) { 596 if (ec) 597 { 598 BMCWEB_LOG_DEBUG << "DBUS response error"; 599 messages::internalError(aResp->res); 600 return; 601 } 602 603 const std::string* value = std::get_if<std::string>(&property); 604 605 if (value == nullptr) 606 { 607 // illegal value 608 BMCWEB_LOG_DEBUG << "Location code value error"; 609 messages::internalError(aResp->res); 610 return; 611 } 612 613 aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 614 *value; 615 }, 616 service, objPath, "org.freedesktop.DBus.Properties", "Get", 617 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode"); 618 } 619 620 /** 621 * Find the D-Bus object representing the requested Processor, and call the 622 * handler with the results. If matching object is not found, add 404 error to 623 * response and don't call the handler. 624 * 625 * @param[in,out] resp Async HTTP response. 626 * @param[in] processorId Redfish Processor Id. 627 * @param[in] handler Callback to continue processing request upon 628 * successfully finding object. 629 */ 630 template <typename Handler> 631 inline void getProcessorObject(const std::shared_ptr<bmcweb::AsyncResp>& resp, 632 const std::string& processorId, 633 Handler&& handler) 634 { 635 BMCWEB_LOG_DEBUG << "Get available system processor resources."; 636 637 // GetSubTree on all interfaces which provide info about a Processor 638 crow::connections::systemBus->async_method_call( 639 [resp, processorId, handler = std::forward<Handler>(handler)]( 640 boost::system::error_code ec, 641 const MapperGetSubTreeResponse& subtree) mutable { 642 if (ec) 643 { 644 BMCWEB_LOG_DEBUG << "DBUS response error: " << ec; 645 messages::internalError(resp->res); 646 return; 647 } 648 for (const auto& [objectPath, serviceMap] : subtree) 649 { 650 // Ignore any objects which don't end with our desired cpu name 651 if (!boost::ends_with(objectPath, processorId)) 652 { 653 continue; 654 } 655 656 bool found = false; 657 // Filter out objects that don't have the CPU-specific 658 // interfaces to make sure we can return 404 on non-CPUs 659 // (e.g. /redfish/../Processors/dimm0) 660 for (const auto& [serviceName, interfaceList] : serviceMap) 661 { 662 if (std::find_first_of( 663 interfaceList.begin(), interfaceList.end(), 664 processorInterfaces.begin(), 665 processorInterfaces.end()) != interfaceList.end()) 666 { 667 found = true; 668 break; 669 } 670 } 671 672 if (!found) 673 { 674 continue; 675 } 676 677 // Process the first object which does match our cpu name and 678 // required interfaces, and potentially ignore any other 679 // matching objects. Assume all interfaces we want to process 680 // must be on the same object path. 681 682 handler(resp, processorId, objectPath, serviceMap); 683 return; 684 } 685 messages::resourceNotFound(resp->res, "Processor", processorId); 686 }, 687 "xyz.openbmc_project.ObjectMapper", 688 "/xyz/openbmc_project/object_mapper", 689 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 690 "/xyz/openbmc_project/inventory", 0, 691 std::array<const char*, 6>{ 692 "xyz.openbmc_project.Inventory.Decorator.Asset", 693 "xyz.openbmc_project.Inventory.Decorator.Revision", 694 "xyz.openbmc_project.Inventory.Item.Cpu", 695 "xyz.openbmc_project.Inventory.Decorator.LocationCode", 696 "xyz.openbmc_project.Inventory.Item.Accelerator", 697 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}); 698 } 699 700 inline void getProcessorData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 701 const std::string& processorId, 702 const std::string& objectPath, 703 const MapperServiceMap& serviceMap) 704 { 705 for (const auto& [serviceName, interfaceList] : serviceMap) 706 { 707 for (const auto& interface : interfaceList) 708 { 709 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") 710 { 711 getCpuAssetData(aResp, serviceName, objectPath); 712 } 713 else if (interface == "xyz.openbmc_project.Inventory." 714 "Decorator.Revision") 715 { 716 getCpuRevisionData(aResp, serviceName, objectPath); 717 } 718 else if (interface == "xyz.openbmc_project.Inventory.Item.Cpu") 719 { 720 getCpuDataByService(aResp, processorId, serviceName, 721 objectPath); 722 } 723 else if (interface == "xyz.openbmc_project.Inventory." 724 "Item.Accelerator") 725 { 726 getAcceleratorDataByService(aResp, processorId, serviceName, 727 objectPath); 728 } 729 else if (interface == "xyz.openbmc_project.Control.Processor." 730 "CurrentOperatingConfig") 731 { 732 getCpuConfigData(aResp, processorId, serviceName, objectPath); 733 } 734 else if (interface == "xyz.openbmc_project.Inventory." 735 "Decorator.LocationCode") 736 { 737 getCpuLocationCode(aResp, serviceName, objectPath); 738 } 739 } 740 } 741 } 742 743 /** 744 * Request all the properties for the given D-Bus object and fill out the 745 * related entries in the Redfish OperatingConfig response. 746 * 747 * @param[in,out] aResp Async HTTP response. 748 * @param[in] service D-Bus service name to query. 749 * @param[in] objPath D-Bus object to query. 750 */ 751 inline void 752 getOperatingConfigData(const std::shared_ptr<bmcweb::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(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 874 const crow::Request& req, 875 const std::vector<std::string>& params) override 876 { 877 if (params.size() != 1) 878 { 879 messages::internalError(asyncResp->res); 880 return; 881 } 882 883 const std::string& cpuName = params[0]; 884 asyncResp->res.jsonValue["@odata.type"] = 885 "#OperatingConfigCollection.OperatingConfigCollection"; 886 asyncResp->res.jsonValue["@odata.id"] = req.url; 887 asyncResp->res.jsonValue["Name"] = "Operating Config Collection"; 888 889 // First find the matching CPU object so we know how to constrain our 890 // search for related Config objects. 891 crow::connections::systemBus->async_method_call( 892 [asyncResp, cpuName](const boost::system::error_code ec, 893 const std::vector<std::string>& objects) { 894 if (ec) 895 { 896 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 897 << ec.message(); 898 messages::internalError(asyncResp->res); 899 return; 900 } 901 902 for (const std::string& object : objects) 903 { 904 if (!boost::ends_with(object, cpuName)) 905 { 906 continue; 907 } 908 909 // Not expected that there will be multiple matching CPU 910 // objects, but if there are just use the first one. 911 912 // Use the common search routine to construct the Collection 913 // of all Config objects under this CPU. 914 collection_util::getCollectionMembers( 915 asyncResp, 916 "/redfish/v1/Systems/system/Processors/" + cpuName + 917 "/OperatingConfigs", 918 {"xyz.openbmc_project.Inventory.Item.Cpu." 919 "OperatingConfig"}, 920 object.c_str()); 921 return; 922 } 923 }, 924 "xyz.openbmc_project.ObjectMapper", 925 "/xyz/openbmc_project/object_mapper", 926 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 927 "/xyz/openbmc_project/inventory", 0, 928 std::array<const char*, 1>{"xyz.openbmc_project.Control.Processor." 929 "CurrentOperatingConfig"}); 930 } 931 }; 932 933 class OperatingConfig : public Node 934 { 935 public: 936 OperatingConfig(App& app) : 937 Node(app, 938 "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/" 939 "<str>/", 940 std::string(), std::string()) 941 { 942 // Defined by Redfish spec privilege registry 943 entityPrivileges = { 944 {boost::beast::http::verb::get, {{"Login"}}}, 945 {boost::beast::http::verb::head, {{"Login"}}}, 946 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 947 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 948 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 949 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 950 } 951 952 private: 953 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 954 const crow::Request& req, 955 const std::vector<std::string>& params) override 956 { 957 if (params.size() != 2) 958 { 959 messages::internalError(asyncResp->res); 960 return; 961 } 962 963 const std::string& cpuName = params[0]; 964 const std::string& configName = params[1]; 965 966 // Ask for all objects implementing OperatingConfig so we can search for 967 // one with a matching name 968 crow::connections::systemBus->async_method_call( 969 [asyncResp, cpuName, configName, 970 reqUrl{req.url}](boost::system::error_code ec, 971 const MapperGetSubTreeResponse& subtree) { 972 if (ec) 973 { 974 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 975 << ec.message(); 976 messages::internalError(asyncResp->res); 977 return; 978 } 979 const std::string expectedEnding = cpuName + '/' + configName; 980 for (const auto& [objectPath, serviceMap] : subtree) 981 { 982 // Ignore any configs without matching cpuX/configY 983 if (!boost::ends_with(objectPath, expectedEnding) || 984 serviceMap.empty()) 985 { 986 continue; 987 } 988 989 nlohmann::json& json = asyncResp->res.jsonValue; 990 json["@odata.type"] = 991 "#OperatingConfig.v1_0_0.OperatingConfig"; 992 json["@odata.id"] = reqUrl; 993 json["Name"] = "Processor Profile"; 994 json["Id"] = configName; 995 996 // Just use the first implementation of the object - not 997 // expected that there would be multiple matching services 998 getOperatingConfigData(asyncResp, serviceMap.begin()->first, 999 objectPath); 1000 return; 1001 } 1002 messages::resourceNotFound(asyncResp->res, "OperatingConfig", 1003 configName); 1004 }, 1005 "xyz.openbmc_project.ObjectMapper", 1006 "/xyz/openbmc_project/object_mapper", 1007 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 1008 "/xyz/openbmc_project/inventory", 0, 1009 std::array<const char*, 1>{ 1010 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}); 1011 } 1012 }; 1013 1014 class ProcessorCollection : public Node 1015 { 1016 public: 1017 /* 1018 * Default Constructor 1019 */ 1020 ProcessorCollection(App& app) : 1021 Node(app, "/redfish/v1/Systems/system/Processors/") 1022 { 1023 entityPrivileges = { 1024 {boost::beast::http::verb::get, {{"Login"}}}, 1025 {boost::beast::http::verb::head, {{"Login"}}}, 1026 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1027 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1028 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1029 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1030 } 1031 1032 private: 1033 /** 1034 * Functions triggers appropriate requests on DBus 1035 */ 1036 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1037 const crow::Request&, const std::vector<std::string>&) override 1038 { 1039 asyncResp->res.jsonValue["@odata.type"] = 1040 "#ProcessorCollection.ProcessorCollection"; 1041 asyncResp->res.jsonValue["Name"] = "Processor Collection"; 1042 1043 asyncResp->res.jsonValue["@odata.id"] = 1044 "/redfish/v1/Systems/system/Processors"; 1045 1046 collection_util::getCollectionMembers( 1047 asyncResp, "/redfish/v1/Systems/system/Processors", 1048 std::vector<const char*>(processorInterfaces.begin(), 1049 processorInterfaces.end())); 1050 } 1051 }; 1052 1053 class Processor : public Node 1054 { 1055 public: 1056 /* 1057 * Default Constructor 1058 */ 1059 Processor(App& app) : 1060 Node(app, "/redfish/v1/Systems/system/Processors/<str>/", std::string()) 1061 { 1062 entityPrivileges = { 1063 {boost::beast::http::verb::get, {{"Login"}}}, 1064 {boost::beast::http::verb::head, {{"Login"}}}, 1065 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1066 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1067 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1068 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1069 } 1070 1071 private: 1072 /** 1073 * Functions triggers appropriate requests on DBus 1074 */ 1075 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1076 const crow::Request&, 1077 const std::vector<std::string>& params) override 1078 { 1079 // Check if there is required param, truly entering this shall be 1080 // impossible 1081 if (params.size() != 1) 1082 { 1083 messages::internalError(asyncResp->res); 1084 return; 1085 } 1086 const std::string& processorId = params[0]; 1087 asyncResp->res.jsonValue["@odata.type"] = 1088 "#Processor.v1_11_0.Processor"; 1089 asyncResp->res.jsonValue["@odata.id"] = 1090 "/redfish/v1/Systems/system/Processors/" + processorId; 1091 1092 getProcessorObject(asyncResp, processorId, getProcessorData); 1093 } 1094 }; 1095 1096 } // namespace redfish 1097