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