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 } 285 }, 286 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 287 "xyz.openbmc_project.Inventory.Decorator.Asset"); 288 } 289 290 inline void getCpuRevisionData(std::shared_ptr<AsyncResp> aResp, 291 const std::string& service, 292 const std::string& objPath) 293 { 294 BMCWEB_LOG_DEBUG << "Get Cpu Revision Data"; 295 crow::connections::systemBus->async_method_call( 296 [objPath, aResp{std::move(aResp)}]( 297 const boost::system::error_code ec, 298 const boost::container::flat_map< 299 std::string, std::variant<std::string, uint32_t, uint16_t, 300 bool>>& properties) { 301 if (ec) 302 { 303 BMCWEB_LOG_DEBUG << "DBUS response error"; 304 messages::internalError(aResp->res); 305 return; 306 } 307 308 for (const auto& property : properties) 309 { 310 if (property.first == "Version") 311 { 312 const std::string* ver = 313 std::get_if<std::string>(&property.second); 314 if (ver != nullptr) 315 { 316 aResp->res.jsonValue["Version"] = *ver; 317 } 318 break; 319 } 320 } 321 }, 322 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 323 "xyz.openbmc_project.Inventory.Decorator.Revision"); 324 } 325 326 inline void getAcceleratorDataByService(std::shared_ptr<AsyncResp> aResp, 327 const std::string& acclrtrId, 328 const std::string& service, 329 const std::string& objPath) 330 { 331 BMCWEB_LOG_DEBUG 332 << "Get available system Accelerator resources by service."; 333 crow::connections::systemBus->async_method_call( 334 [acclrtrId, aResp{std::move(aResp)}]( 335 const boost::system::error_code ec, 336 const boost::container::flat_map< 337 std::string, std::variant<std::string, uint32_t, uint16_t, 338 bool>>& properties) { 339 if (ec) 340 { 341 BMCWEB_LOG_DEBUG << "DBUS response error"; 342 messages::internalError(aResp->res); 343 return; 344 } 345 aResp->res.jsonValue["Id"] = acclrtrId; 346 aResp->res.jsonValue["Name"] = "Processor"; 347 const bool* accPresent = nullptr; 348 const bool* accFunctional = nullptr; 349 350 for (const auto& property : properties) 351 { 352 if (property.first == "Functional") 353 { 354 accFunctional = std::get_if<bool>(&property.second); 355 } 356 else if (property.first == "Present") 357 { 358 accPresent = std::get_if<bool>(&property.second); 359 } 360 } 361 362 std::string state = "Enabled"; 363 std::string health = "OK"; 364 365 if (accPresent != nullptr && *accPresent == false) 366 { 367 state = "Absent"; 368 } 369 370 if ((accFunctional != nullptr) && (*accFunctional == false)) 371 { 372 if (state == "Enabled") 373 { 374 health = "Critical"; 375 } 376 } 377 378 aResp->res.jsonValue["Status"]["State"] = state; 379 aResp->res.jsonValue["Status"]["Health"] = health; 380 aResp->res.jsonValue["ProcessorType"] = "Accelerator"; 381 }, 382 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", ""); 383 } 384 385 // OperatingConfig D-Bus Types 386 using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>; 387 using BaseSpeedPrioritySettingsProperty = 388 std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>; 389 // uint32_t and size_t may or may not be the same type, requiring a dedup'd 390 // variant 391 using OperatingConfigProperties = std::vector<std::pair< 392 std::string, 393 sdbusplus::utility::dedup_variant<uint32_t, size_t, TurboProfileProperty, 394 BaseSpeedPrioritySettingsProperty>>>; 395 396 /** 397 * Fill out the HighSpeedCoreIDs in a Processor resource from the given 398 * OperatingConfig D-Bus property. 399 * 400 * @param[in,out] aResp Async HTTP response. 401 * @param[in] baseSpeedSettings Full list of base speed priority groups, 402 * to use to determine the list of high 403 * speed cores. 404 */ 405 inline void highSpeedCoreIdsHandler( 406 const std::shared_ptr<AsyncResp>& aResp, 407 const BaseSpeedPrioritySettingsProperty& baseSpeedSettings) 408 { 409 // The D-Bus property does not indicate which bucket is the "high 410 // priority" group, so let's discern that by looking for the one with 411 // highest base frequency. 412 auto highPriorityGroup = baseSpeedSettings.cend(); 413 uint32_t highestBaseSpeed = 0; 414 for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend(); 415 ++it) 416 { 417 const uint32_t baseFreq = std::get<uint32_t>(*it); 418 if (baseFreq > highestBaseSpeed) 419 { 420 highestBaseSpeed = baseFreq; 421 highPriorityGroup = it; 422 } 423 } 424 425 nlohmann::json& jsonCoreIds = aResp->res.jsonValue["HighSpeedCoreIDs"]; 426 jsonCoreIds = nlohmann::json::array(); 427 428 // There may not be any entries in the D-Bus property, so only populate 429 // if there was actually something there. 430 if (highPriorityGroup != baseSpeedSettings.cend()) 431 { 432 jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup); 433 } 434 } 435 436 /** 437 * Fill out OperatingConfig related items in a Processor resource by requesting 438 * data from the given D-Bus object. 439 * 440 * @param[in,out] aResp Async HTTP response. 441 * @param[in] cpuId CPU D-Bus name. 442 * @param[in] service D-Bus service to query. 443 * @param[in] objPath D-Bus object to query. 444 */ 445 inline void getCpuConfigData(const std::shared_ptr<AsyncResp>& aResp, 446 const std::string& cpuId, 447 const std::string& service, 448 const std::string& objPath) 449 { 450 BMCWEB_LOG_INFO << "Getting CPU operating configs for " << cpuId; 451 452 // First, GetAll CurrentOperatingConfig properties on the object 453 crow::connections::systemBus->async_method_call( 454 [aResp, cpuId, service]( 455 const boost::system::error_code ec, 456 const std::vector< 457 std::pair<std::string, 458 std::variant<sdbusplus::message::object_path, bool>>>& 459 properties) { 460 if (ec) 461 { 462 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 463 << ec.message(); 464 messages::internalError(aResp->res); 465 return; 466 } 467 468 nlohmann::json& json = aResp->res.jsonValue; 469 470 for (const auto& [dbusPropName, variantVal] : properties) 471 { 472 if (dbusPropName == "AppliedConfig") 473 { 474 const sdbusplus::message::object_path* dbusPathWrapper = 475 std::get_if<sdbusplus::message::object_path>( 476 &variantVal); 477 if (dbusPathWrapper == nullptr) 478 { 479 continue; 480 } 481 482 const std::string& dbusPath = dbusPathWrapper->str; 483 std::string uri = "/redfish/v1/Systems/system/Processors/" + 484 cpuId + "/OperatingConfigs"; 485 json["OperatingConfigs"] = {{"@odata.id", uri}}; 486 487 // Reuse the D-Bus config object name for the Redfish 488 // URI 489 size_t baseNamePos = dbusPath.rfind('/'); 490 if (baseNamePos == std::string::npos || 491 baseNamePos == (dbusPath.size() - 1)) 492 { 493 // If the AppliedConfig was somehow not a valid path, 494 // skip adding any more properties, since everything 495 // else is tied to this applied config. 496 messages::internalError(aResp->res); 497 break; 498 } 499 uri += '/'; 500 uri += dbusPath.substr(baseNamePos + 1); 501 json["AppliedOperatingConfig"] = {{"@odata.id", uri}}; 502 503 // Once we found the current applied config, queue another 504 // request to read the base freq core ids out of that 505 // config. 506 crow::connections::systemBus->async_method_call( 507 [aResp]( 508 const boost::system::error_code ec, 509 const std::variant< 510 BaseSpeedPrioritySettingsProperty>& property) { 511 if (ec) 512 { 513 BMCWEB_LOG_WARNING 514 << "D-Bus Property Get error: " << ec; 515 messages::internalError(aResp->res); 516 return; 517 } 518 auto baseSpeedList = 519 std::get_if<BaseSpeedPrioritySettingsProperty>( 520 &property); 521 if (baseSpeedList != nullptr) 522 { 523 highSpeedCoreIdsHandler(aResp, *baseSpeedList); 524 } 525 }, 526 service, dbusPath, "org.freedesktop.DBus.Properties", 527 "Get", 528 "xyz.openbmc_project.Inventory.Item.Cpu." 529 "OperatingConfig", 530 "BaseSpeedPrioritySettings"); 531 } 532 else if (dbusPropName == "BaseSpeedPriorityEnabled") 533 { 534 const bool* state = std::get_if<bool>(&variantVal); 535 if (state != nullptr) 536 { 537 json["BaseSpeedPriorityState"] = 538 *state ? "Enabled" : "Disabled"; 539 } 540 } 541 } 542 }, 543 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 544 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"); 545 } 546 547 inline void getProcessorData(std::shared_ptr<AsyncResp> aResp, 548 const std::string& processorId) 549 { 550 BMCWEB_LOG_DEBUG << "Get available system processor resources."; 551 552 crow::connections::systemBus->async_method_call( 553 [processorId, 554 aResp{std::move(aResp)}](const boost::system::error_code ec, 555 const MapperGetSubTreeResponse& subtree) { 556 if (ec) 557 { 558 BMCWEB_LOG_DEBUG << "DBUS response error"; 559 messages::internalError(aResp->res); 560 return; 561 } 562 for (const auto& [objectPath, serviceMap] : subtree) 563 { 564 // Ignore any objects which don't end with our desired cpu name 565 if (!boost::ends_with(objectPath, processorId)) 566 { 567 continue; 568 } 569 570 // Process the first object which does match our cpu name 571 // suffix, and potentially ignore any other matching objects. 572 // Assume all interfaces we want to process must be on the same 573 // object. 574 575 for (const auto& [serviceName, interfaceList] : serviceMap) 576 { 577 for (const auto& interface : interfaceList) 578 { 579 if (interface == 580 "xyz.openbmc_project.Inventory.Decorator.Asset") 581 { 582 getCpuAssetData(aResp, serviceName, objectPath); 583 } 584 else if (interface == "xyz.openbmc_project.Inventory." 585 "Decorator.Revision") 586 { 587 getCpuRevisionData(aResp, serviceName, objectPath); 588 } 589 else if (interface == 590 "xyz.openbmc_project.Inventory.Item.Cpu") 591 { 592 getCpuDataByService(aResp, processorId, serviceName, 593 objectPath); 594 } 595 else if (interface == "xyz.openbmc_project.Inventory." 596 "Item.Accelerator") 597 { 598 getAcceleratorDataByService( 599 aResp, processorId, serviceName, objectPath); 600 } 601 else if (interface == 602 "xyz.openbmc_project.Control.Processor." 603 "CurrentOperatingConfig") 604 { 605 getCpuConfigData(aResp, processorId, serviceName, 606 objectPath); 607 } 608 } 609 } 610 return; 611 } 612 // Object not found 613 messages::resourceNotFound(aResp->res, "Processor", processorId); 614 return; 615 }, 616 "xyz.openbmc_project.ObjectMapper", 617 "/xyz/openbmc_project/object_mapper", 618 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 619 "/xyz/openbmc_project/inventory", 0, 620 std::array<const char*, 5>{ 621 "xyz.openbmc_project.Inventory.Decorator.Asset", 622 "xyz.openbmc_project.Inventory.Decorator.Revision", 623 "xyz.openbmc_project.Inventory.Item.Cpu", 624 "xyz.openbmc_project.Inventory.Item.Accelerator", 625 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}); 626 } 627 628 /** 629 * Request all the properties for the given D-Bus object and fill out the 630 * related entries in the Redfish OperatingConfig response. 631 * 632 * @param[in,out] aResp Async HTTP response. 633 * @param[in] service D-Bus service name to query. 634 * @param[in] objPath D-Bus object to query. 635 */ 636 inline void getOperatingConfigData(const std::shared_ptr<AsyncResp>& aResp, 637 const std::string& service, 638 const std::string& objPath) 639 { 640 crow::connections::systemBus->async_method_call( 641 [aResp](boost::system::error_code ec, 642 const OperatingConfigProperties& properties) { 643 if (ec) 644 { 645 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 646 << ec.message(); 647 messages::internalError(aResp->res); 648 return; 649 } 650 651 nlohmann::json& json = aResp->res.jsonValue; 652 for (const auto& [key, variant] : properties) 653 { 654 if (key == "AvailableCoreCount") 655 { 656 const size_t* cores = std::get_if<size_t>(&variant); 657 if (cores != nullptr) 658 { 659 json["TotalAvailableCoreCount"] = *cores; 660 } 661 } 662 else if (key == "BaseSpeed") 663 { 664 const uint32_t* speed = std::get_if<uint32_t>(&variant); 665 if (speed != nullptr) 666 { 667 json["BaseSpeedMHz"] = *speed; 668 } 669 } 670 else if (key == "MaxJunctionTemperature") 671 { 672 const uint32_t* temp = std::get_if<uint32_t>(&variant); 673 if (temp != nullptr) 674 { 675 json["MaxJunctionTemperatureCelsius"] = *temp; 676 } 677 } 678 else if (key == "MaxSpeed") 679 { 680 const uint32_t* speed = std::get_if<uint32_t>(&variant); 681 if (speed != nullptr) 682 { 683 json["MaxSpeedMHz"] = *speed; 684 } 685 } 686 else if (key == "PowerLimit") 687 { 688 const uint32_t* tdp = std::get_if<uint32_t>(&variant); 689 if (tdp != nullptr) 690 { 691 json["TDPWatts"] = *tdp; 692 } 693 } 694 else if (key == "TurboProfile") 695 { 696 const auto* turboList = 697 std::get_if<TurboProfileProperty>(&variant); 698 if (turboList == nullptr) 699 { 700 continue; 701 } 702 703 nlohmann::json& turboArray = json["TurboProfile"]; 704 turboArray = nlohmann::json::array(); 705 for (const auto& [turboSpeed, coreCount] : *turboList) 706 { 707 turboArray.push_back({{"ActiveCoreCount", coreCount}, 708 {"MaxSpeedMHz", turboSpeed}}); 709 } 710 } 711 else if (key == "BaseSpeedPrioritySettings") 712 { 713 const auto* baseSpeedList = 714 std::get_if<BaseSpeedPrioritySettingsProperty>( 715 &variant); 716 if (baseSpeedList == nullptr) 717 { 718 continue; 719 } 720 721 nlohmann::json& baseSpeedArray = 722 json["BaseSpeedPrioritySettings"]; 723 baseSpeedArray = nlohmann::json::array(); 724 for (const auto& [baseSpeed, coreList] : *baseSpeedList) 725 { 726 baseSpeedArray.push_back( 727 {{"CoreCount", coreList.size()}, 728 {"CoreIDs", coreList}, 729 {"BaseSpeedMHz", baseSpeed}}); 730 } 731 } 732 } 733 }, 734 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 735 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"); 736 } 737 738 class OperatingConfigCollection : public Node 739 { 740 public: 741 OperatingConfigCollection(App& app) : 742 Node(app, 743 "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/", 744 std::string()) 745 { 746 // Defined by Redfish spec privilege registry 747 entityPrivileges = { 748 {boost::beast::http::verb::get, {{"Login"}}}, 749 {boost::beast::http::verb::head, {{"Login"}}}, 750 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 751 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 752 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 753 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 754 } 755 756 private: 757 void doGet(crow::Response& res, const crow::Request& req, 758 const std::vector<std::string>& params) override 759 { 760 if (params.size() != 1) 761 { 762 messages::internalError(res); 763 res.end(); 764 return; 765 } 766 767 const std::string& cpuName = params[0]; 768 res.jsonValue["@odata.type"] = 769 "#OperatingConfigCollection.OperatingConfigCollection"; 770 res.jsonValue["@odata.id"] = req.url; 771 res.jsonValue["Name"] = "Operating Config Collection"; 772 773 auto asyncResp = std::make_shared<AsyncResp>(res); 774 775 // First find the matching CPU object so we know how to constrain our 776 // search for related Config objects. 777 crow::connections::systemBus->async_method_call( 778 [asyncResp, cpuName](const boost::system::error_code ec, 779 const std::vector<std::string>& objects) { 780 if (ec) 781 { 782 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 783 << ec.message(); 784 messages::internalError(asyncResp->res); 785 return; 786 } 787 788 for (const std::string& object : objects) 789 { 790 if (!boost::ends_with(object, cpuName)) 791 { 792 continue; 793 } 794 795 // Not expected that there will be multiple matching CPU 796 // objects, but if there are just use the first one. 797 798 // Use the common search routine to construct the Collection 799 // of all Config objects under this CPU. 800 collection_util::getCollectionMembers( 801 asyncResp, 802 "/redfish/v1/Systems/system/Processors/" + cpuName + 803 "/OperatingConfigs", 804 {"xyz.openbmc_project.Inventory.Item.Cpu." 805 "OperatingConfig"}, 806 object.c_str()); 807 return; 808 } 809 }, 810 "xyz.openbmc_project.ObjectMapper", 811 "/xyz/openbmc_project/object_mapper", 812 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 813 "/xyz/openbmc_project/inventory", 0, 814 std::array<const char*, 1>{"xyz.openbmc_project.Control.Processor." 815 "CurrentOperatingConfig"}); 816 } 817 }; 818 819 class OperatingConfig : public Node 820 { 821 public: 822 OperatingConfig(App& app) : 823 Node(app, 824 "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/" 825 "<str>/", 826 std::string(), std::string()) 827 { 828 // Defined by Redfish spec privilege registry 829 entityPrivileges = { 830 {boost::beast::http::verb::get, {{"Login"}}}, 831 {boost::beast::http::verb::head, {{"Login"}}}, 832 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 833 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 834 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 835 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 836 } 837 838 private: 839 void doGet(crow::Response& res, const crow::Request& req, 840 const std::vector<std::string>& params) override 841 { 842 if (params.size() != 2) 843 { 844 messages::internalError(res); 845 res.end(); 846 return; 847 } 848 849 const std::string& cpuName = params[0]; 850 const std::string& configName = params[1]; 851 852 auto asyncResp = std::make_shared<AsyncResp>(res); 853 854 // Ask for all objects implementing OperatingConfig so we can search for 855 // one with a matching name 856 crow::connections::systemBus->async_method_call( 857 [asyncResp, cpuName, configName, 858 reqUrl{req.url}](boost::system::error_code ec, 859 const MapperGetSubTreeResponse& subtree) { 860 if (ec) 861 { 862 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 863 << ec.message(); 864 messages::internalError(asyncResp->res); 865 return; 866 } 867 const std::string expectedEnding = cpuName + '/' + configName; 868 for (const auto& [objectPath, serviceMap] : subtree) 869 { 870 // Ignore any configs without matching cpuX/configY 871 if (!boost::ends_with(objectPath, expectedEnding) || 872 serviceMap.empty()) 873 { 874 continue; 875 } 876 877 nlohmann::json& json = asyncResp->res.jsonValue; 878 json["@odata.type"] = 879 "#OperatingConfig.v1_0_0.OperatingConfig"; 880 json["@odata.id"] = reqUrl; 881 json["Name"] = "Processor Profile"; 882 json["Id"] = configName; 883 884 // Just use the first implementation of the object - not 885 // expected that there would be multiple matching services 886 getOperatingConfigData(asyncResp, serviceMap.begin()->first, 887 objectPath); 888 return; 889 } 890 messages::resourceNotFound(asyncResp->res, "OperatingConfig", 891 configName); 892 }, 893 "xyz.openbmc_project.ObjectMapper", 894 "/xyz/openbmc_project/object_mapper", 895 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 896 "/xyz/openbmc_project/inventory", 0, 897 std::array<const char*, 1>{ 898 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}); 899 } 900 }; 901 902 class ProcessorCollection : public Node 903 { 904 public: 905 /* 906 * Default Constructor 907 */ 908 ProcessorCollection(App& app) : 909 Node(app, "/redfish/v1/Systems/system/Processors/") 910 { 911 entityPrivileges = { 912 {boost::beast::http::verb::get, {{"Login"}}}, 913 {boost::beast::http::verb::head, {{"Login"}}}, 914 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 915 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 916 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 917 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 918 } 919 920 private: 921 /** 922 * Functions triggers appropriate requests on DBus 923 */ 924 void doGet(crow::Response& res, const crow::Request&, 925 const std::vector<std::string>&) override 926 { 927 res.jsonValue["@odata.type"] = 928 "#ProcessorCollection.ProcessorCollection"; 929 res.jsonValue["Name"] = "Processor Collection"; 930 931 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Processors"; 932 auto asyncResp = std::make_shared<AsyncResp>(res); 933 934 collection_util::getCollectionMembers( 935 asyncResp, "/redfish/v1/Systems/system/Processors", 936 {"xyz.openbmc_project.Inventory.Item.Cpu", 937 "xyz.openbmc_project.Inventory.Item.Accelerator"}); 938 } 939 }; 940 941 class Processor : public Node 942 { 943 public: 944 /* 945 * Default Constructor 946 */ 947 Processor(App& app) : 948 Node(app, "/redfish/v1/Systems/system/Processors/<str>/", std::string()) 949 { 950 entityPrivileges = { 951 {boost::beast::http::verb::get, {{"Login"}}}, 952 {boost::beast::http::verb::head, {{"Login"}}}, 953 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 954 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 955 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 956 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 957 } 958 959 private: 960 /** 961 * Functions triggers appropriate requests on DBus 962 */ 963 void doGet(crow::Response& res, const crow::Request&, 964 const std::vector<std::string>& params) override 965 { 966 // Check if there is required param, truly entering this shall be 967 // impossible 968 if (params.size() != 1) 969 { 970 messages::internalError(res); 971 972 res.end(); 973 return; 974 } 975 const std::string& processorId = params[0]; 976 res.jsonValue["@odata.type"] = "#Processor.v1_9_0.Processor"; 977 res.jsonValue["@odata.id"] = 978 "/redfish/v1/Systems/system/Processors/" + processorId; 979 980 auto asyncResp = std::make_shared<AsyncResp>(res); 981 982 getProcessorData(asyncResp, processorId); 983 } 984 }; 985 986 } // namespace redfish 987