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