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