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 <query.hpp> 26 #include <registries/privilege_registry.hpp> 27 #include <sdbusplus/asio/property.hpp> 28 #include <sdbusplus/message/native_types.hpp> 29 #include <sdbusplus/utility/dedup_variant.hpp> 30 #include <utils/collection.hpp> 31 #include <utils/json_utils.hpp> 32 33 namespace redfish 34 { 35 36 // Interfaces which imply a D-Bus object represents a Processor 37 constexpr std::array<const char*, 2> processorInterfaces = { 38 "xyz.openbmc_project.Inventory.Item.Cpu", 39 "xyz.openbmc_project.Inventory.Item.Accelerator"}; 40 41 /** 42 * @brief Fill out uuid info of a processor by 43 * requesting data from the given D-Bus object. 44 * 45 * @param[in,out] aResp Async HTTP response. 46 * @param[in] service D-Bus service to query. 47 * @param[in] objPath D-Bus object to query. 48 */ 49 inline void getProcessorUUID(std::shared_ptr<bmcweb::AsyncResp> aResp, 50 const std::string& service, 51 const std::string& objPath) 52 { 53 BMCWEB_LOG_DEBUG << "Get Processor UUID"; 54 sdbusplus::asio::getProperty<std::string>( 55 *crow::connections::systemBus, service, objPath, 56 "xyz.openbmc_project.Common.UUID", "UUID", 57 [objPath, aResp{std::move(aResp)}](const boost::system::error_code ec, 58 const std::string& property) { 59 if (ec) 60 { 61 BMCWEB_LOG_DEBUG << "DBUS response error"; 62 messages::internalError(aResp->res); 63 return; 64 } 65 aResp->res.jsonValue["UUID"] = property; 66 }); 67 } 68 69 inline void getCpuDataByInterface( 70 const std::shared_ptr<bmcweb::AsyncResp>& aResp, 71 const dbus::utility::DBusInteracesMap& cpuInterfacesProperties) 72 { 73 BMCWEB_LOG_DEBUG << "Get CPU resources by interface."; 74 75 // Set the default value of state 76 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 77 aResp->res.jsonValue["Status"]["Health"] = "OK"; 78 79 for (const auto& interface : cpuInterfacesProperties) 80 { 81 for (const auto& property : interface.second) 82 { 83 if (property.first == "Present") 84 { 85 const bool* cpuPresent = std::get_if<bool>(&property.second); 86 if (cpuPresent == nullptr) 87 { 88 // Important property not in desired type 89 messages::internalError(aResp->res); 90 return; 91 } 92 if (!*cpuPresent) 93 { 94 // Slot is not populated 95 aResp->res.jsonValue["Status"]["State"] = "Absent"; 96 } 97 } 98 else if (property.first == "Functional") 99 { 100 const bool* cpuFunctional = std::get_if<bool>(&property.second); 101 if (cpuFunctional == nullptr) 102 { 103 messages::internalError(aResp->res); 104 return; 105 } 106 if (!*cpuFunctional) 107 { 108 aResp->res.jsonValue["Status"]["Health"] = "Critical"; 109 } 110 } 111 else if (property.first == "CoreCount") 112 { 113 const uint16_t* coresCount = 114 std::get_if<uint16_t>(&property.second); 115 if (coresCount == nullptr) 116 { 117 messages::internalError(aResp->res); 118 return; 119 } 120 aResp->res.jsonValue["TotalCores"] = *coresCount; 121 } 122 else if (property.first == "MaxSpeedInMhz") 123 { 124 const uint32_t* value = std::get_if<uint32_t>(&property.second); 125 if (value != nullptr) 126 { 127 aResp->res.jsonValue["MaxSpeedMHz"] = *value; 128 } 129 } 130 else if (property.first == "Socket") 131 { 132 const std::string* value = 133 std::get_if<std::string>(&property.second); 134 if (value != nullptr) 135 { 136 aResp->res.jsonValue["Socket"] = *value; 137 } 138 } 139 else if (property.first == "ThreadCount") 140 { 141 const uint16_t* value = std::get_if<uint16_t>(&property.second); 142 if (value != nullptr) 143 { 144 aResp->res.jsonValue["TotalThreads"] = *value; 145 } 146 } 147 else if (property.first == "EffectiveFamily") 148 { 149 const uint16_t* value = std::get_if<uint16_t>(&property.second); 150 if (value != nullptr) 151 { 152 aResp->res.jsonValue["ProcessorId"]["EffectiveFamily"] = 153 "0x" + intToHexString(*value, 4); 154 } 155 } 156 else if (property.first == "EffectiveModel") 157 { 158 const uint16_t* value = std::get_if<uint16_t>(&property.second); 159 if (value == nullptr) 160 { 161 messages::internalError(aResp->res); 162 return; 163 } 164 aResp->res.jsonValue["ProcessorId"]["EffectiveModel"] = 165 "0x" + intToHexString(*value, 4); 166 } 167 else if (property.first == "Id") 168 { 169 const uint64_t* value = std::get_if<uint64_t>(&property.second); 170 if (value != nullptr && *value != 0) 171 { 172 aResp->res 173 .jsonValue["ProcessorId"]["IdentificationRegisters"] = 174 "0x" + intToHexString(*value, 16); 175 } 176 } 177 else if (property.first == "Microcode") 178 { 179 const uint32_t* value = std::get_if<uint32_t>(&property.second); 180 if (value == nullptr) 181 { 182 messages::internalError(aResp->res); 183 return; 184 } 185 aResp->res.jsonValue["ProcessorId"]["MicrocodeInfo"] = 186 "0x" + intToHexString(*value, 8); 187 } 188 else if (property.first == "Step") 189 { 190 const uint16_t* value = std::get_if<uint16_t>(&property.second); 191 if (value == nullptr) 192 { 193 messages::internalError(aResp->res); 194 return; 195 } 196 aResp->res.jsonValue["ProcessorId"]["Step"] = 197 "0x" + intToHexString(*value, 4); 198 } 199 } 200 } 201 } 202 203 inline void getCpuDataByService(std::shared_ptr<bmcweb::AsyncResp> aResp, 204 const std::string& cpuId, 205 const std::string& service, 206 const std::string& objPath) 207 { 208 BMCWEB_LOG_DEBUG << "Get available system cpu resources by service."; 209 210 crow::connections::systemBus->async_method_call( 211 [cpuId, service, objPath, aResp{std::move(aResp)}]( 212 const boost::system::error_code ec, 213 const dbus::utility::ManagedObjectType& dbusData) { 214 if (ec) 215 { 216 BMCWEB_LOG_DEBUG << "DBUS response error"; 217 messages::internalError(aResp->res); 218 return; 219 } 220 aResp->res.jsonValue["Id"] = cpuId; 221 aResp->res.jsonValue["Name"] = "Processor"; 222 aResp->res.jsonValue["ProcessorType"] = "CPU"; 223 224 bool slotPresent = false; 225 std::string corePath = objPath + "/core"; 226 size_t totalCores = 0; 227 for (const auto& object : dbusData) 228 { 229 if (object.first.str == objPath) 230 { 231 getCpuDataByInterface(aResp, object.second); 232 } 233 else if (boost::starts_with(object.first.str, corePath)) 234 { 235 for (const auto& interface : object.second) 236 { 237 if (interface.first == 238 "xyz.openbmc_project.Inventory.Item") 239 { 240 for (const auto& property : interface.second) 241 { 242 if (property.first == "Present") 243 { 244 const bool* present = 245 std::get_if<bool>(&property.second); 246 if (present != nullptr) 247 { 248 if (*present) 249 { 250 slotPresent = true; 251 totalCores++; 252 } 253 } 254 } 255 } 256 } 257 } 258 } 259 } 260 // In getCpuDataByInterface(), state and health are set 261 // based on the present and functional status. If core 262 // count is zero, then it has a higher precedence. 263 if (slotPresent) 264 { 265 if (totalCores == 0) 266 { 267 // Slot is not populated, set status end return 268 aResp->res.jsonValue["Status"]["State"] = "Absent"; 269 aResp->res.jsonValue["Status"]["Health"] = "OK"; 270 } 271 aResp->res.jsonValue["TotalCores"] = totalCores; 272 } 273 return; 274 }, 275 service, "/xyz/openbmc_project/inventory", 276 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 277 } 278 279 inline void getCpuAssetData(std::shared_ptr<bmcweb::AsyncResp> aResp, 280 const std::string& service, 281 const std::string& objPath) 282 { 283 BMCWEB_LOG_DEBUG << "Get Cpu Asset Data"; 284 crow::connections::systemBus->async_method_call( 285 [objPath, aResp{std::move(aResp)}]( 286 const boost::system::error_code ec, 287 const boost::container::flat_map< 288 std::string, dbus::utility::DbusVariantType>& properties) { 289 if (ec) 290 { 291 BMCWEB_LOG_DEBUG << "DBUS response error"; 292 messages::internalError(aResp->res); 293 return; 294 } 295 296 for (const auto& property : properties) 297 { 298 if (property.first == "SerialNumber") 299 { 300 const std::string* sn = 301 std::get_if<std::string>(&property.second); 302 if (sn != nullptr && !sn->empty()) 303 { 304 aResp->res.jsonValue["SerialNumber"] = *sn; 305 } 306 } 307 else if (property.first == "Model") 308 { 309 const std::string* model = 310 std::get_if<std::string>(&property.second); 311 if (model != nullptr && !model->empty()) 312 { 313 aResp->res.jsonValue["Model"] = *model; 314 } 315 } 316 else if (property.first == "Manufacturer") 317 { 318 319 const std::string* mfg = 320 std::get_if<std::string>(&property.second); 321 if (mfg != nullptr) 322 { 323 aResp->res.jsonValue["Manufacturer"] = *mfg; 324 325 // Otherwise would be unexpected. 326 if (mfg->find("Intel") != std::string::npos) 327 { 328 aResp->res.jsonValue["ProcessorArchitecture"] = 329 "x86"; 330 aResp->res.jsonValue["InstructionSet"] = "x86-64"; 331 } 332 else if (mfg->find("IBM") != std::string::npos) 333 { 334 aResp->res.jsonValue["ProcessorArchitecture"] = 335 "Power"; 336 aResp->res.jsonValue["InstructionSet"] = "PowerISA"; 337 } 338 } 339 } 340 else if (property.first == "PartNumber") 341 { 342 const std::string* partNumber = 343 std::get_if<std::string>(&property.second); 344 345 if (partNumber == nullptr) 346 { 347 messages::internalError(aResp->res); 348 return; 349 } 350 aResp->res.jsonValue["PartNumber"] = *partNumber; 351 } 352 else if (property.first == "SparePartNumber") 353 { 354 const std::string* sparePartNumber = 355 std::get_if<std::string>(&property.second); 356 357 if (sparePartNumber == nullptr) 358 { 359 messages::internalError(aResp->res); 360 return; 361 } 362 aResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 363 } 364 } 365 }, 366 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 367 "xyz.openbmc_project.Inventory.Decorator.Asset"); 368 } 369 370 inline void getCpuRevisionData(std::shared_ptr<bmcweb::AsyncResp> aResp, 371 const std::string& service, 372 const std::string& objPath) 373 { 374 BMCWEB_LOG_DEBUG << "Get Cpu Revision Data"; 375 crow::connections::systemBus->async_method_call( 376 [objPath, aResp{std::move(aResp)}]( 377 const boost::system::error_code ec, 378 const boost::container::flat_map< 379 std::string, dbus::utility::DbusVariantType>& properties) { 380 if (ec) 381 { 382 BMCWEB_LOG_DEBUG << "DBUS response error"; 383 messages::internalError(aResp->res); 384 return; 385 } 386 387 for (const auto& property : properties) 388 { 389 if (property.first == "Version") 390 { 391 const std::string* ver = 392 std::get_if<std::string>(&property.second); 393 if (ver != nullptr) 394 { 395 aResp->res.jsonValue["Version"] = *ver; 396 } 397 break; 398 } 399 } 400 }, 401 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 402 "xyz.openbmc_project.Inventory.Decorator.Revision"); 403 } 404 405 inline void getAcceleratorDataByService( 406 std::shared_ptr<bmcweb::AsyncResp> aResp, const std::string& acclrtrId, 407 const std::string& service, const std::string& objPath) 408 { 409 BMCWEB_LOG_DEBUG 410 << "Get available system Accelerator resources by service."; 411 crow::connections::systemBus->async_method_call( 412 [acclrtrId, aResp{std::move(aResp)}]( 413 const boost::system::error_code ec, 414 const boost::container::flat_map< 415 std::string, dbus::utility::DbusVariantType>& properties) { 416 if (ec) 417 { 418 BMCWEB_LOG_DEBUG << "DBUS response error"; 419 messages::internalError(aResp->res); 420 return; 421 } 422 aResp->res.jsonValue["Id"] = acclrtrId; 423 aResp->res.jsonValue["Name"] = "Processor"; 424 const bool* accPresent = nullptr; 425 const bool* accFunctional = nullptr; 426 427 for (const auto& property : properties) 428 { 429 if (property.first == "Functional") 430 { 431 accFunctional = std::get_if<bool>(&property.second); 432 } 433 else if (property.first == "Present") 434 { 435 accPresent = std::get_if<bool>(&property.second); 436 } 437 } 438 439 std::string state = "Enabled"; 440 std::string health = "OK"; 441 442 if (accPresent != nullptr && !*accPresent) 443 { 444 state = "Absent"; 445 } 446 447 if ((accFunctional != nullptr) && !*accFunctional) 448 { 449 if (state == "Enabled") 450 { 451 health = "Critical"; 452 } 453 } 454 455 aResp->res.jsonValue["Status"]["State"] = state; 456 aResp->res.jsonValue["Status"]["Health"] = health; 457 aResp->res.jsonValue["ProcessorType"] = "Accelerator"; 458 }, 459 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", ""); 460 } 461 462 // OperatingConfig D-Bus Types 463 using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>; 464 using BaseSpeedPrioritySettingsProperty = 465 std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>; 466 // uint32_t and size_t may or may not be the same type, requiring a dedup'd 467 // variant 468 using OperatingConfigProperties = 469 std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>; 470 471 /** 472 * Fill out the HighSpeedCoreIDs in a Processor resource from the given 473 * OperatingConfig D-Bus property. 474 * 475 * @param[in,out] aResp Async HTTP response. 476 * @param[in] baseSpeedSettings Full list of base speed priority groups, 477 * to use to determine the list of high 478 * speed cores. 479 */ 480 inline void highSpeedCoreIdsHandler( 481 const std::shared_ptr<bmcweb::AsyncResp>& aResp, 482 const BaseSpeedPrioritySettingsProperty& baseSpeedSettings) 483 { 484 // The D-Bus property does not indicate which bucket is the "high 485 // priority" group, so let's discern that by looking for the one with 486 // highest base frequency. 487 auto highPriorityGroup = baseSpeedSettings.cend(); 488 uint32_t highestBaseSpeed = 0; 489 for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend(); 490 ++it) 491 { 492 const uint32_t baseFreq = std::get<uint32_t>(*it); 493 if (baseFreq > highestBaseSpeed) 494 { 495 highestBaseSpeed = baseFreq; 496 highPriorityGroup = it; 497 } 498 } 499 500 nlohmann::json& jsonCoreIds = aResp->res.jsonValue["HighSpeedCoreIDs"]; 501 jsonCoreIds = nlohmann::json::array(); 502 503 // There may not be any entries in the D-Bus property, so only populate 504 // if there was actually something there. 505 if (highPriorityGroup != baseSpeedSettings.cend()) 506 { 507 jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup); 508 } 509 } 510 511 /** 512 * Fill out OperatingConfig related items in a Processor resource by requesting 513 * data from the given D-Bus object. 514 * 515 * @param[in,out] aResp Async HTTP response. 516 * @param[in] cpuId CPU D-Bus name. 517 * @param[in] service D-Bus service to query. 518 * @param[in] objPath D-Bus object to query. 519 */ 520 inline void getCpuConfigData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 521 const std::string& cpuId, 522 const std::string& service, 523 const std::string& objPath) 524 { 525 BMCWEB_LOG_INFO << "Getting CPU operating configs for " << cpuId; 526 527 // First, GetAll CurrentOperatingConfig properties on the object 528 crow::connections::systemBus->async_method_call( 529 [aResp, cpuId, service]( 530 const boost::system::error_code ec, 531 const std::vector<std::pair< 532 std::string, dbus::utility::DbusVariantType>>& properties) { 533 if (ec) 534 { 535 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 536 << ec.message(); 537 messages::internalError(aResp->res); 538 return; 539 } 540 541 nlohmann::json& json = aResp->res.jsonValue; 542 543 for (const auto& [dbusPropName, variantVal] : properties) 544 { 545 if (dbusPropName == "AppliedConfig") 546 { 547 const sdbusplus::message::object_path* dbusPathWrapper = 548 std::get_if<sdbusplus::message::object_path>( 549 &variantVal); 550 if (dbusPathWrapper == nullptr) 551 { 552 continue; 553 } 554 555 const std::string& dbusPath = dbusPathWrapper->str; 556 std::string uri = "/redfish/v1/Systems/system/Processors/" + 557 cpuId + "/OperatingConfigs"; 558 nlohmann::json::object_t operatingConfig; 559 operatingConfig["@odata.id"] = uri; 560 json["OperatingConfigs"] = std::move(operatingConfig); 561 562 // Reuse the D-Bus config object name for the Redfish 563 // URI 564 size_t baseNamePos = dbusPath.rfind('/'); 565 if (baseNamePos == std::string::npos || 566 baseNamePos == (dbusPath.size() - 1)) 567 { 568 // If the AppliedConfig was somehow not a valid path, 569 // skip adding any more properties, since everything 570 // else is tied to this applied config. 571 messages::internalError(aResp->res); 572 break; 573 } 574 uri += '/'; 575 uri += dbusPath.substr(baseNamePos + 1); 576 nlohmann::json::object_t appliedOperatingConfig; 577 appliedOperatingConfig["@odata.id"] = uri; 578 json["AppliedOperatingConfig"] = 579 std::move(appliedOperatingConfig); 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 nlohmann::json::object_t turbo; 894 turbo["ActiveCoreCount"] = coreCount; 895 turbo["MaxSpeedMHz"] = turboSpeed; 896 turboArray.push_back(std::move(turbo)); 897 } 898 } 899 else if (key == "BaseSpeedPrioritySettings") 900 { 901 const auto* baseSpeedList = 902 std::get_if<BaseSpeedPrioritySettingsProperty>( 903 &variant); 904 if (baseSpeedList == nullptr) 905 { 906 continue; 907 } 908 909 nlohmann::json& baseSpeedArray = 910 json["BaseSpeedPrioritySettings"]; 911 baseSpeedArray = nlohmann::json::array(); 912 for (const auto& [baseSpeed, coreList] : *baseSpeedList) 913 { 914 nlohmann::json::object_t speed; 915 speed["CoreCount"] = coreList.size(); 916 speed["CoreIDs"] = coreList; 917 speed["BaseSpeedMHz"] = baseSpeed; 918 baseSpeedArray.push_back(std::move(speed)); 919 } 920 } 921 } 922 }, 923 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 924 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"); 925 } 926 927 /** 928 * Handle the D-Bus response from attempting to set the CPU's AppliedConfig 929 * property. Main task is to translate error messages into Redfish errors. 930 * 931 * @param[in,out] resp HTTP response. 932 * @param[in] setPropVal Value which we attempted to set. 933 * @param[in] ec D-Bus response error code. 934 * @param[in] msg D-Bus response message. 935 */ 936 inline void 937 handleAppliedConfigResponse(const std::shared_ptr<bmcweb::AsyncResp>& resp, 938 const std::string& setPropVal, 939 boost::system::error_code ec, 940 const sdbusplus::message::message& msg) 941 { 942 if (!ec) 943 { 944 BMCWEB_LOG_DEBUG << "Set Property succeeded"; 945 return; 946 } 947 948 BMCWEB_LOG_DEBUG << "Set Property failed: " << ec; 949 950 const sd_bus_error* dbusError = msg.get_error(); 951 if (dbusError == nullptr) 952 { 953 messages::internalError(resp->res); 954 return; 955 } 956 957 // The asio error code doesn't know about our custom errors, so we have to 958 // parse the error string. Some of these D-Bus -> Redfish translations are a 959 // stretch, but it's good to try to communicate something vaguely useful. 960 if (strcmp(dbusError->name, 961 "xyz.openbmc_project.Common.Error.InvalidArgument") == 0) 962 { 963 // Service did not like the object_path we tried to set. 964 messages::propertyValueIncorrect( 965 resp->res, "AppliedOperatingConfig/@odata.id", setPropVal); 966 } 967 else if (strcmp(dbusError->name, 968 "xyz.openbmc_project.Common.Error.NotAllowed") == 0) 969 { 970 // Service indicates we can never change the config for this processor. 971 messages::propertyNotWritable(resp->res, "AppliedOperatingConfig"); 972 } 973 else if (strcmp(dbusError->name, 974 "xyz.openbmc_project.Common.Error.Unavailable") == 0) 975 { 976 // Service indicates the config cannot be changed right now, but maybe 977 // in a different system state. 978 messages::resourceInStandby(resp->res); 979 } 980 else if (strcmp(dbusError->name, 981 "xyz.openbmc_project.Common.Device.Error.WriteFailure") == 982 0) 983 { 984 // Service tried to change the config, but it failed. 985 messages::operationFailed(resp->res); 986 } 987 else 988 { 989 messages::internalError(resp->res); 990 } 991 } 992 993 /** 994 * Handle the PATCH operation of the AppliedOperatingConfig property. Do basic 995 * validation of the input data, and then set the D-Bus property. 996 * 997 * @param[in,out] resp Async HTTP response. 998 * @param[in] processorId Processor's Id. 999 * @param[in] appliedConfigUri New property value to apply. 1000 * @param[in] cpuObjectPath Path of CPU object to modify. 1001 * @param[in] serviceMap Service map for CPU object. 1002 */ 1003 inline void patchAppliedOperatingConfig( 1004 const std::shared_ptr<bmcweb::AsyncResp>& resp, 1005 const std::string& processorId, const std::string& appliedConfigUri, 1006 const std::string& cpuObjectPath, 1007 const dbus::utility::MapperServiceMap& serviceMap) 1008 { 1009 // Check that the property even exists by checking for the interface 1010 const std::string* controlService = nullptr; 1011 for (const auto& [serviceName, interfaceList] : serviceMap) 1012 { 1013 if (std::find(interfaceList.begin(), interfaceList.end(), 1014 "xyz.openbmc_project.Control.Processor." 1015 "CurrentOperatingConfig") != interfaceList.end()) 1016 { 1017 controlService = &serviceName; 1018 break; 1019 } 1020 } 1021 1022 if (controlService == nullptr) 1023 { 1024 messages::internalError(resp->res); 1025 return; 1026 } 1027 1028 // Check that the config URI is a child of the cpu URI being patched. 1029 std::string expectedPrefix("/redfish/v1/Systems/system/Processors/"); 1030 expectedPrefix += processorId; 1031 expectedPrefix += "/OperatingConfigs/"; 1032 if (!boost::starts_with(appliedConfigUri, expectedPrefix) || 1033 expectedPrefix.size() == appliedConfigUri.size()) 1034 { 1035 messages::propertyValueIncorrect( 1036 resp->res, "AppliedOperatingConfig/@odata.id", appliedConfigUri); 1037 return; 1038 } 1039 1040 // Generate the D-Bus path of the OperatingConfig object, by assuming it's a 1041 // direct child of the CPU object. 1042 // Strip the expectedPrefix from the config URI to get the "filename", and 1043 // append to the CPU's path. 1044 std::string configBaseName = appliedConfigUri.substr(expectedPrefix.size()); 1045 sdbusplus::message::object_path configPath(cpuObjectPath); 1046 configPath /= configBaseName; 1047 1048 BMCWEB_LOG_INFO << "Setting config to " << configPath.str; 1049 1050 // Set the property, with handler to check error responses 1051 crow::connections::systemBus->async_method_call( 1052 [resp, appliedConfigUri](const boost::system::error_code ec, 1053 const sdbusplus::message::message& msg) { 1054 handleAppliedConfigResponse(resp, appliedConfigUri, ec, msg); 1055 }, 1056 *controlService, cpuObjectPath, "org.freedesktop.DBus.Properties", 1057 "Set", "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", 1058 "AppliedConfig", dbus::utility::DbusVariantType(std::move(configPath))); 1059 } 1060 1061 inline void requestRoutesOperatingConfigCollection(App& app) 1062 { 1063 1064 BMCWEB_ROUTE( 1065 app, "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/") 1066 .privileges(redfish::privileges::getOperatingConfigCollection) 1067 .methods( 1068 boost::beast::http::verb:: 1069 get)([&app](const crow::Request& req, 1070 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1071 const std::string& cpuName) { 1072 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1073 { 1074 return; 1075 } 1076 asyncResp->res.jsonValue["@odata.type"] = 1077 "#OperatingConfigCollection.OperatingConfigCollection"; 1078 asyncResp->res.jsonValue["@odata.id"] = req.url; 1079 asyncResp->res.jsonValue["Name"] = "Operating Config Collection"; 1080 1081 // First find the matching CPU object so we know how to 1082 // constrain our search for related Config objects. 1083 crow::connections::systemBus->async_method_call( 1084 [asyncResp, 1085 cpuName](const boost::system::error_code ec, 1086 const dbus::utility::MapperGetSubTreePathsResponse& 1087 objects) { 1088 if (ec) 1089 { 1090 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 1091 << ec.message(); 1092 messages::internalError(asyncResp->res); 1093 return; 1094 } 1095 1096 for (const std::string& object : objects) 1097 { 1098 if (!boost::ends_with(object, cpuName)) 1099 { 1100 continue; 1101 } 1102 1103 // Not expected that there will be multiple matching 1104 // CPU objects, but if there are just use the first 1105 // one. 1106 1107 // Use the common search routine to construct the 1108 // Collection of all Config objects under this CPU. 1109 collection_util::getCollectionMembers( 1110 asyncResp, 1111 "/redfish/v1/Systems/system/Processors/" + cpuName + 1112 "/OperatingConfigs", 1113 {"xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}, 1114 object.c_str()); 1115 return; 1116 } 1117 }, 1118 "xyz.openbmc_project.ObjectMapper", 1119 "/xyz/openbmc_project/object_mapper", 1120 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 1121 "/xyz/openbmc_project/inventory", 0, 1122 std::array<const char*, 1>{ 1123 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}); 1124 }); 1125 } 1126 1127 inline void requestRoutesOperatingConfig(App& app) 1128 { 1129 BMCWEB_ROUTE( 1130 app, 1131 "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/<str>/") 1132 .privileges(redfish::privileges::getOperatingConfig) 1133 .methods( 1134 boost::beast::http::verb:: 1135 get)([&app](const crow::Request& req, 1136 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1137 const std::string& cpuName, 1138 const std::string& configName) { 1139 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1140 { 1141 return; 1142 } 1143 // Ask for all objects implementing OperatingConfig so we can search 1144 // for one with a matching name 1145 crow::connections::systemBus->async_method_call( 1146 [asyncResp, cpuName, configName, reqUrl{req.url}]( 1147 boost::system::error_code ec, 1148 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1149 if (ec) 1150 { 1151 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 1152 << ec.message(); 1153 messages::internalError(asyncResp->res); 1154 return; 1155 } 1156 const std::string expectedEnding = 1157 cpuName + '/' + configName; 1158 for (const auto& [objectPath, serviceMap] : subtree) 1159 { 1160 // Ignore any configs without matching cpuX/configY 1161 if (!boost::ends_with(objectPath, expectedEnding) || 1162 serviceMap.empty()) 1163 { 1164 continue; 1165 } 1166 1167 nlohmann::json& json = asyncResp->res.jsonValue; 1168 json["@odata.type"] = 1169 "#OperatingConfig.v1_0_0.OperatingConfig"; 1170 json["@odata.id"] = reqUrl; 1171 json["Name"] = "Processor Profile"; 1172 json["Id"] = configName; 1173 1174 // Just use the first implementation of the object - not 1175 // expected that there would be multiple matching 1176 // services 1177 getOperatingConfigData( 1178 asyncResp, serviceMap.begin()->first, objectPath); 1179 return; 1180 } 1181 messages::resourceNotFound(asyncResp->res, 1182 "OperatingConfig", configName); 1183 }, 1184 "xyz.openbmc_project.ObjectMapper", 1185 "/xyz/openbmc_project/object_mapper", 1186 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 1187 "/xyz/openbmc_project/inventory", 0, 1188 std::array<const char*, 1>{ 1189 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}); 1190 }); 1191 } 1192 1193 inline void requestRoutesProcessorCollection(App& app) 1194 { 1195 /** 1196 * Functions triggers appropriate requests on DBus 1197 */ 1198 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Processors/") 1199 .privileges(redfish::privileges::getProcessorCollection) 1200 .methods(boost::beast::http::verb::get)( 1201 [&app](const crow::Request& req, 1202 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1203 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1204 { 1205 return; 1206 } 1207 asyncResp->res.jsonValue["@odata.type"] = 1208 "#ProcessorCollection.ProcessorCollection"; 1209 asyncResp->res.jsonValue["Name"] = "Processor Collection"; 1210 1211 asyncResp->res.jsonValue["@odata.id"] = 1212 "/redfish/v1/Systems/system/Processors"; 1213 1214 collection_util::getCollectionMembers( 1215 asyncResp, "/redfish/v1/Systems/system/Processors", 1216 std::vector<const char*>(processorInterfaces.begin(), 1217 processorInterfaces.end())); 1218 }); 1219 } 1220 1221 inline void requestRoutesProcessor(App& app) 1222 { 1223 /** 1224 * Functions triggers appropriate requests on DBus 1225 */ 1226 1227 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Processors/<str>/") 1228 .privileges(redfish::privileges::getProcessor) 1229 .methods(boost::beast::http::verb::get)( 1230 [&app](const crow::Request& req, 1231 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1232 const std::string& processorId) { 1233 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1234 { 1235 return; 1236 } 1237 asyncResp->res.jsonValue["@odata.type"] = 1238 "#Processor.v1_11_0.Processor"; 1239 asyncResp->res.jsonValue["@odata.id"] = 1240 "/redfish/v1/Systems/system/Processors/" + processorId; 1241 1242 getProcessorObject(asyncResp, processorId, getProcessorData); 1243 }); 1244 1245 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Processors/<str>/") 1246 .privileges(redfish::privileges::patchProcessor) 1247 .methods(boost::beast::http::verb::patch)( 1248 [&app](const crow::Request& req, 1249 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1250 const std::string& processorId) { 1251 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1252 { 1253 return; 1254 } 1255 std::optional<nlohmann::json> appliedConfigJson; 1256 if (!json_util::readJsonPatch(req, asyncResp->res, 1257 "AppliedOperatingConfig", 1258 appliedConfigJson)) 1259 { 1260 return; 1261 } 1262 1263 std::string appliedConfigUri; 1264 if (appliedConfigJson) 1265 { 1266 if (!json_util::readJson(*appliedConfigJson, asyncResp->res, 1267 "@odata.id", appliedConfigUri)) 1268 { 1269 return; 1270 } 1271 // Check for 404 and find matching D-Bus object, then run 1272 // property patch handlers if that all succeeds. 1273 getProcessorObject( 1274 asyncResp, processorId, 1275 [appliedConfigUri = std::move(appliedConfigUri)]( 1276 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1277 const std::string& processorId, 1278 const std::string& objectPath, 1279 const dbus::utility::MapperServiceMap& serviceMap) { 1280 patchAppliedOperatingConfig(asyncResp, processorId, 1281 appliedConfigUri, 1282 objectPath, serviceMap); 1283 }); 1284 } 1285 }); 1286 } 1287 1288 } // namespace redfish 1289