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