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 json["OperatingConfigs"] = {{"@odata.id", uri}}; 559 560 // Reuse the D-Bus config object name for the Redfish 561 // URI 562 size_t baseNamePos = dbusPath.rfind('/'); 563 if (baseNamePos == std::string::npos || 564 baseNamePos == (dbusPath.size() - 1)) 565 { 566 // If the AppliedConfig was somehow not a valid path, 567 // skip adding any more properties, since everything 568 // else is tied to this applied config. 569 messages::internalError(aResp->res); 570 break; 571 } 572 uri += '/'; 573 uri += dbusPath.substr(baseNamePos + 1); 574 json["AppliedOperatingConfig"] = {{"@odata.id", uri}}; 575 576 // Once we found the current applied config, queue another 577 // request to read the base freq core ids out of that 578 // config. 579 sdbusplus::asio::getProperty< 580 BaseSpeedPrioritySettingsProperty>( 581 *crow::connections::systemBus, service, dbusPath, 582 "xyz.openbmc_project.Inventory.Item.Cpu." 583 "OperatingConfig", 584 "BaseSpeedPrioritySettings", 585 [aResp](const boost::system::error_code ec, 586 const BaseSpeedPrioritySettingsProperty& 587 baseSpeedList) { 588 if (ec) 589 { 590 BMCWEB_LOG_WARNING 591 << "D-Bus Property Get error: " << ec; 592 messages::internalError(aResp->res); 593 return; 594 } 595 596 highSpeedCoreIdsHandler(aResp, baseSpeedList); 597 }); 598 } 599 else if (dbusPropName == "BaseSpeedPriorityEnabled") 600 { 601 const bool* state = std::get_if<bool>(&variantVal); 602 if (state != nullptr) 603 { 604 json["BaseSpeedPriorityState"] = 605 *state ? "Enabled" : "Disabled"; 606 } 607 } 608 } 609 }, 610 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 611 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"); 612 } 613 614 /** 615 * @brief Fill out location info of a processor by 616 * requesting data from the given D-Bus object. 617 * 618 * @param[in,out] aResp Async HTTP response. 619 * @param[in] service D-Bus service to query. 620 * @param[in] objPath D-Bus object to query. 621 */ 622 inline void getCpuLocationCode(std::shared_ptr<bmcweb::AsyncResp> aResp, 623 const std::string& service, 624 const std::string& objPath) 625 { 626 BMCWEB_LOG_DEBUG << "Get Cpu Location Data"; 627 sdbusplus::asio::getProperty<std::string>( 628 *crow::connections::systemBus, service, objPath, 629 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 630 [objPath, aResp{std::move(aResp)}](const boost::system::error_code ec, 631 const std::string& property) { 632 if (ec) 633 { 634 BMCWEB_LOG_DEBUG << "DBUS response error"; 635 messages::internalError(aResp->res); 636 return; 637 } 638 639 aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 640 property; 641 }); 642 } 643 644 /** 645 * Populate the unique identifier in a Processor resource by requesting data 646 * from the given D-Bus object. 647 * 648 * @param[in,out] aResp Async HTTP response. 649 * @param[in] service D-Bus service to query. 650 * @param[in] objPath D-Bus object to query. 651 */ 652 inline void getCpuUniqueId(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 653 const std::string& service, 654 const std::string& objectPath) 655 { 656 BMCWEB_LOG_DEBUG << "Get CPU UniqueIdentifier"; 657 sdbusplus::asio::getProperty<std::string>( 658 *crow::connections::systemBus, service, objectPath, 659 "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier", 660 "UniqueIdentifier", 661 [aResp](boost::system::error_code ec, const std::string& id) { 662 if (ec) 663 { 664 BMCWEB_LOG_ERROR << "Failed to read cpu unique id: " << ec; 665 messages::internalError(aResp->res); 666 return; 667 } 668 aResp->res 669 .jsonValue["ProcessorId"]["ProtectedIdentificationNumber"] = id; 670 }); 671 } 672 673 /** 674 * Find the D-Bus object representing the requested Processor, and call the 675 * handler with the results. If matching object is not found, add 404 error to 676 * response and don't call the handler. 677 * 678 * @param[in,out] resp Async HTTP response. 679 * @param[in] processorId Redfish Processor Id. 680 * @param[in] handler Callback to continue processing request upon 681 * successfully finding object. 682 */ 683 template <typename Handler> 684 inline void getProcessorObject(const std::shared_ptr<bmcweb::AsyncResp>& resp, 685 const std::string& processorId, 686 Handler&& handler) 687 { 688 BMCWEB_LOG_DEBUG << "Get available system processor resources."; 689 690 // GetSubTree on all interfaces which provide info about a Processor 691 crow::connections::systemBus->async_method_call( 692 [resp, processorId, handler = std::forward<Handler>(handler)]( 693 boost::system::error_code ec, 694 const dbus::utility::MapperGetSubTreeResponse& subtree) mutable { 695 if (ec) 696 { 697 BMCWEB_LOG_DEBUG << "DBUS response error: " << ec; 698 messages::internalError(resp->res); 699 return; 700 } 701 for (const auto& [objectPath, serviceMap] : subtree) 702 { 703 // Ignore any objects which don't end with our desired cpu name 704 if (!boost::ends_with(objectPath, processorId)) 705 { 706 continue; 707 } 708 709 bool found = false; 710 // Filter out objects that don't have the CPU-specific 711 // interfaces to make sure we can return 404 on non-CPUs 712 // (e.g. /redfish/../Processors/dimm0) 713 for (const auto& [serviceName, interfaceList] : serviceMap) 714 { 715 if (std::find_first_of( 716 interfaceList.begin(), interfaceList.end(), 717 processorInterfaces.begin(), 718 processorInterfaces.end()) != interfaceList.end()) 719 { 720 found = true; 721 break; 722 } 723 } 724 725 if (!found) 726 { 727 continue; 728 } 729 730 // Process the first object which does match our cpu name and 731 // required interfaces, and potentially ignore any other 732 // matching objects. Assume all interfaces we want to process 733 // must be on the same object path. 734 735 handler(resp, processorId, objectPath, serviceMap); 736 return; 737 } 738 messages::resourceNotFound(resp->res, "Processor", processorId); 739 }, 740 "xyz.openbmc_project.ObjectMapper", 741 "/xyz/openbmc_project/object_mapper", 742 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 743 "/xyz/openbmc_project/inventory", 0, 744 std::array<const char*, 8>{ 745 "xyz.openbmc_project.Common.UUID", 746 "xyz.openbmc_project.Inventory.Decorator.Asset", 747 "xyz.openbmc_project.Inventory.Decorator.Revision", 748 "xyz.openbmc_project.Inventory.Item.Cpu", 749 "xyz.openbmc_project.Inventory.Decorator.LocationCode", 750 "xyz.openbmc_project.Inventory.Item.Accelerator", 751 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", 752 "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier"}); 753 } 754 755 inline void getProcessorData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 756 const std::string& processorId, 757 const std::string& objectPath, 758 const dbus::utility::MapperServiceMap& serviceMap) 759 { 760 for (const auto& [serviceName, interfaceList] : serviceMap) 761 { 762 for (const auto& interface : interfaceList) 763 { 764 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") 765 { 766 getCpuAssetData(aResp, serviceName, objectPath); 767 } 768 else if (interface == 769 "xyz.openbmc_project.Inventory.Decorator.Revision") 770 { 771 getCpuRevisionData(aResp, serviceName, objectPath); 772 } 773 else if (interface == "xyz.openbmc_project.Inventory.Item.Cpu") 774 { 775 getCpuDataByService(aResp, processorId, serviceName, 776 objectPath); 777 } 778 else if (interface == 779 "xyz.openbmc_project.Inventory.Item.Accelerator") 780 { 781 getAcceleratorDataByService(aResp, processorId, serviceName, 782 objectPath); 783 } 784 else if ( 785 interface == 786 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig") 787 { 788 getCpuConfigData(aResp, processorId, serviceName, objectPath); 789 } 790 else if (interface == 791 "xyz.openbmc_project.Inventory.Decorator.LocationCode") 792 { 793 getCpuLocationCode(aResp, serviceName, objectPath); 794 } 795 else if (interface == "xyz.openbmc_project.Common.UUID") 796 { 797 getProcessorUUID(aResp, serviceName, objectPath); 798 } 799 else if (interface == 800 "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier") 801 { 802 getCpuUniqueId(aResp, serviceName, objectPath); 803 } 804 } 805 } 806 } 807 808 /** 809 * Request all the properties for the given D-Bus object and fill out the 810 * related entries in the Redfish OperatingConfig response. 811 * 812 * @param[in,out] aResp Async HTTP response. 813 * @param[in] service D-Bus service name to query. 814 * @param[in] objPath D-Bus object to query. 815 */ 816 inline void 817 getOperatingConfigData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 818 const std::string& service, 819 const std::string& objPath) 820 { 821 crow::connections::systemBus->async_method_call( 822 [aResp](const boost::system::error_code ec, 823 const OperatingConfigProperties& properties) { 824 if (ec) 825 { 826 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 827 << ec.message(); 828 messages::internalError(aResp->res); 829 return; 830 } 831 832 nlohmann::json& json = aResp->res.jsonValue; 833 for (const auto& [key, variant] : properties) 834 { 835 if (key == "AvailableCoreCount") 836 { 837 const size_t* cores = std::get_if<size_t>(&variant); 838 if (cores != nullptr) 839 { 840 json["TotalAvailableCoreCount"] = *cores; 841 } 842 } 843 else if (key == "BaseSpeed") 844 { 845 const uint32_t* speed = std::get_if<uint32_t>(&variant); 846 if (speed != nullptr) 847 { 848 json["BaseSpeedMHz"] = *speed; 849 } 850 } 851 else if (key == "MaxJunctionTemperature") 852 { 853 const uint32_t* temp = std::get_if<uint32_t>(&variant); 854 if (temp != nullptr) 855 { 856 json["MaxJunctionTemperatureCelsius"] = *temp; 857 } 858 } 859 else if (key == "MaxSpeed") 860 { 861 const uint32_t* speed = std::get_if<uint32_t>(&variant); 862 if (speed != nullptr) 863 { 864 json["MaxSpeedMHz"] = *speed; 865 } 866 } 867 else if (key == "PowerLimit") 868 { 869 const uint32_t* tdp = std::get_if<uint32_t>(&variant); 870 if (tdp != nullptr) 871 { 872 json["TDPWatts"] = *tdp; 873 } 874 } 875 else if (key == "TurboProfile") 876 { 877 const auto* turboList = 878 std::get_if<TurboProfileProperty>(&variant); 879 if (turboList == nullptr) 880 { 881 continue; 882 } 883 884 nlohmann::json& turboArray = json["TurboProfile"]; 885 turboArray = nlohmann::json::array(); 886 for (const auto& [turboSpeed, coreCount] : *turboList) 887 { 888 turboArray.push_back({{"ActiveCoreCount", coreCount}, 889 {"MaxSpeedMHz", turboSpeed}}); 890 } 891 } 892 else if (key == "BaseSpeedPrioritySettings") 893 { 894 const auto* baseSpeedList = 895 std::get_if<BaseSpeedPrioritySettingsProperty>( 896 &variant); 897 if (baseSpeedList == nullptr) 898 { 899 continue; 900 } 901 902 nlohmann::json& baseSpeedArray = 903 json["BaseSpeedPrioritySettings"]; 904 baseSpeedArray = nlohmann::json::array(); 905 for (const auto& [baseSpeed, coreList] : *baseSpeedList) 906 { 907 baseSpeedArray.push_back( 908 {{"CoreCount", coreList.size()}, 909 {"CoreIDs", coreList}, 910 {"BaseSpeedMHz", baseSpeed}}); 911 } 912 } 913 } 914 }, 915 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 916 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"); 917 } 918 919 /** 920 * Handle the D-Bus response from attempting to set the CPU's AppliedConfig 921 * property. Main task is to translate error messages into Redfish errors. 922 * 923 * @param[in,out] resp HTTP response. 924 * @param[in] setPropVal Value which we attempted to set. 925 * @param[in] ec D-Bus response error code. 926 * @param[in] msg D-Bus response message. 927 */ 928 inline void 929 handleAppliedConfigResponse(const std::shared_ptr<bmcweb::AsyncResp>& resp, 930 const std::string& setPropVal, 931 boost::system::error_code ec, 932 const sdbusplus::message::message& msg) 933 { 934 if (!ec) 935 { 936 BMCWEB_LOG_DEBUG << "Set Property succeeded"; 937 return; 938 } 939 940 BMCWEB_LOG_DEBUG << "Set Property failed: " << ec; 941 942 const sd_bus_error* dbusError = msg.get_error(); 943 if (dbusError == nullptr) 944 { 945 messages::internalError(resp->res); 946 return; 947 } 948 949 // The asio error code doesn't know about our custom errors, so we have to 950 // parse the error string. Some of these D-Bus -> Redfish translations are a 951 // stretch, but it's good to try to communicate something vaguely useful. 952 if (strcmp(dbusError->name, 953 "xyz.openbmc_project.Common.Error.InvalidArgument") == 0) 954 { 955 // Service did not like the object_path we tried to set. 956 messages::propertyValueIncorrect( 957 resp->res, "AppliedOperatingConfig/@odata.id", setPropVal); 958 } 959 else if (strcmp(dbusError->name, 960 "xyz.openbmc_project.Common.Error.NotAllowed") == 0) 961 { 962 // Service indicates we can never change the config for this processor. 963 messages::propertyNotWritable(resp->res, "AppliedOperatingConfig"); 964 } 965 else if (strcmp(dbusError->name, 966 "xyz.openbmc_project.Common.Error.Unavailable") == 0) 967 { 968 // Service indicates the config cannot be changed right now, but maybe 969 // in a different system state. 970 messages::resourceInStandby(resp->res); 971 } 972 else if (strcmp(dbusError->name, 973 "xyz.openbmc_project.Common.Device.Error.WriteFailure") == 974 0) 975 { 976 // Service tried to change the config, but it failed. 977 messages::operationFailed(resp->res); 978 } 979 else 980 { 981 messages::internalError(resp->res); 982 } 983 } 984 985 /** 986 * Handle the PATCH operation of the AppliedOperatingConfig property. Do basic 987 * validation of the input data, and then set the D-Bus property. 988 * 989 * @param[in,out] resp Async HTTP response. 990 * @param[in] processorId Processor's Id. 991 * @param[in] appliedConfigUri New property value to apply. 992 * @param[in] cpuObjectPath Path of CPU object to modify. 993 * @param[in] serviceMap Service map for CPU object. 994 */ 995 inline void patchAppliedOperatingConfig( 996 const std::shared_ptr<bmcweb::AsyncResp>& resp, 997 const std::string& processorId, const std::string& appliedConfigUri, 998 const std::string& cpuObjectPath, 999 const dbus::utility::MapperServiceMap& serviceMap) 1000 { 1001 // Check that the property even exists by checking for the interface 1002 const std::string* controlService = nullptr; 1003 for (const auto& [serviceName, interfaceList] : serviceMap) 1004 { 1005 if (std::find(interfaceList.begin(), interfaceList.end(), 1006 "xyz.openbmc_project.Control.Processor." 1007 "CurrentOperatingConfig") != interfaceList.end()) 1008 { 1009 controlService = &serviceName; 1010 break; 1011 } 1012 } 1013 1014 if (controlService == nullptr) 1015 { 1016 messages::internalError(resp->res); 1017 return; 1018 } 1019 1020 // Check that the config URI is a child of the cpu URI being patched. 1021 std::string expectedPrefix("/redfish/v1/Systems/system/Processors/"); 1022 expectedPrefix += processorId; 1023 expectedPrefix += "/OperatingConfigs/"; 1024 if (!boost::starts_with(appliedConfigUri, expectedPrefix) || 1025 expectedPrefix.size() == appliedConfigUri.size()) 1026 { 1027 messages::propertyValueIncorrect( 1028 resp->res, "AppliedOperatingConfig/@odata.id", appliedConfigUri); 1029 return; 1030 } 1031 1032 // Generate the D-Bus path of the OperatingConfig object, by assuming it's a 1033 // direct child of the CPU object. 1034 // Strip the expectedPrefix from the config URI to get the "filename", and 1035 // append to the CPU's path. 1036 std::string configBaseName = appliedConfigUri.substr(expectedPrefix.size()); 1037 sdbusplus::message::object_path configPath(cpuObjectPath); 1038 configPath /= configBaseName; 1039 1040 BMCWEB_LOG_INFO << "Setting config to " << configPath.str; 1041 1042 // Set the property, with handler to check error responses 1043 crow::connections::systemBus->async_method_call( 1044 [resp, appliedConfigUri](const boost::system::error_code ec, 1045 const sdbusplus::message::message& msg) { 1046 handleAppliedConfigResponse(resp, appliedConfigUri, ec, msg); 1047 }, 1048 *controlService, cpuObjectPath, "org.freedesktop.DBus.Properties", 1049 "Set", "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", 1050 "AppliedConfig", dbus::utility::DbusVariantType(std::move(configPath))); 1051 } 1052 1053 inline void requestRoutesOperatingConfigCollection(App& app) 1054 { 1055 1056 BMCWEB_ROUTE( 1057 app, "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/") 1058 .privileges(redfish::privileges::getOperatingConfigCollection) 1059 .methods( 1060 boost::beast::http::verb:: 1061 get)([&app](const crow::Request& req, 1062 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1063 const std::string& cpuName) { 1064 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1065 { 1066 return; 1067 } 1068 asyncResp->res.jsonValue["@odata.type"] = 1069 "#OperatingConfigCollection.OperatingConfigCollection"; 1070 asyncResp->res.jsonValue["@odata.id"] = req.url; 1071 asyncResp->res.jsonValue["Name"] = "Operating Config Collection"; 1072 1073 // First find the matching CPU object so we know how to 1074 // constrain our search for related Config objects. 1075 crow::connections::systemBus->async_method_call( 1076 [asyncResp, 1077 cpuName](const boost::system::error_code ec, 1078 const dbus::utility::MapperGetSubTreePathsResponse& 1079 objects) { 1080 if (ec) 1081 { 1082 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 1083 << ec.message(); 1084 messages::internalError(asyncResp->res); 1085 return; 1086 } 1087 1088 for (const std::string& object : objects) 1089 { 1090 if (!boost::ends_with(object, cpuName)) 1091 { 1092 continue; 1093 } 1094 1095 // Not expected that there will be multiple matching 1096 // CPU objects, but if there are just use the first 1097 // one. 1098 1099 // Use the common search routine to construct the 1100 // Collection of all Config objects under this CPU. 1101 collection_util::getCollectionMembers( 1102 asyncResp, 1103 "/redfish/v1/Systems/system/Processors/" + cpuName + 1104 "/OperatingConfigs", 1105 {"xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}, 1106 object.c_str()); 1107 return; 1108 } 1109 }, 1110 "xyz.openbmc_project.ObjectMapper", 1111 "/xyz/openbmc_project/object_mapper", 1112 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 1113 "/xyz/openbmc_project/inventory", 0, 1114 std::array<const char*, 1>{ 1115 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}); 1116 }); 1117 } 1118 1119 inline void requestRoutesOperatingConfig(App& app) 1120 { 1121 BMCWEB_ROUTE( 1122 app, 1123 "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/<str>/") 1124 .privileges(redfish::privileges::getOperatingConfig) 1125 .methods( 1126 boost::beast::http::verb:: 1127 get)([&app](const crow::Request& req, 1128 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1129 const std::string& cpuName, 1130 const std::string& configName) { 1131 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1132 { 1133 return; 1134 } 1135 // Ask for all objects implementing OperatingConfig so we can search 1136 // for one with a matching name 1137 crow::connections::systemBus->async_method_call( 1138 [asyncResp, cpuName, configName, reqUrl{req.url}]( 1139 boost::system::error_code ec, 1140 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1141 if (ec) 1142 { 1143 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 1144 << ec.message(); 1145 messages::internalError(asyncResp->res); 1146 return; 1147 } 1148 const std::string expectedEnding = 1149 cpuName + '/' + configName; 1150 for (const auto& [objectPath, serviceMap] : subtree) 1151 { 1152 // Ignore any configs without matching cpuX/configY 1153 if (!boost::ends_with(objectPath, expectedEnding) || 1154 serviceMap.empty()) 1155 { 1156 continue; 1157 } 1158 1159 nlohmann::json& json = asyncResp->res.jsonValue; 1160 json["@odata.type"] = 1161 "#OperatingConfig.v1_0_0.OperatingConfig"; 1162 json["@odata.id"] = reqUrl; 1163 json["Name"] = "Processor Profile"; 1164 json["Id"] = configName; 1165 1166 // Just use the first implementation of the object - not 1167 // expected that there would be multiple matching 1168 // services 1169 getOperatingConfigData( 1170 asyncResp, serviceMap.begin()->first, objectPath); 1171 return; 1172 } 1173 messages::resourceNotFound(asyncResp->res, 1174 "OperatingConfig", configName); 1175 }, 1176 "xyz.openbmc_project.ObjectMapper", 1177 "/xyz/openbmc_project/object_mapper", 1178 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 1179 "/xyz/openbmc_project/inventory", 0, 1180 std::array<const char*, 1>{ 1181 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}); 1182 }); 1183 } 1184 1185 inline void requestRoutesProcessorCollection(App& app) 1186 { 1187 /** 1188 * Functions triggers appropriate requests on DBus 1189 */ 1190 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Processors/") 1191 .privileges(redfish::privileges::getProcessorCollection) 1192 .methods(boost::beast::http::verb::get)( 1193 [&app](const crow::Request& req, 1194 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1195 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1196 { 1197 return; 1198 } 1199 asyncResp->res.jsonValue["@odata.type"] = 1200 "#ProcessorCollection.ProcessorCollection"; 1201 asyncResp->res.jsonValue["Name"] = "Processor Collection"; 1202 1203 asyncResp->res.jsonValue["@odata.id"] = 1204 "/redfish/v1/Systems/system/Processors"; 1205 1206 collection_util::getCollectionMembers( 1207 asyncResp, "/redfish/v1/Systems/system/Processors", 1208 std::vector<const char*>(processorInterfaces.begin(), 1209 processorInterfaces.end())); 1210 }); 1211 } 1212 1213 inline void requestRoutesProcessor(App& app) 1214 { 1215 /** 1216 * Functions triggers appropriate requests on DBus 1217 */ 1218 1219 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Processors/<str>/") 1220 .privileges(redfish::privileges::getProcessor) 1221 .methods(boost::beast::http::verb::get)( 1222 [&app](const crow::Request& req, 1223 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1224 const std::string& processorId) { 1225 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1226 { 1227 return; 1228 } 1229 asyncResp->res.jsonValue["@odata.type"] = 1230 "#Processor.v1_11_0.Processor"; 1231 asyncResp->res.jsonValue["@odata.id"] = 1232 "/redfish/v1/Systems/system/Processors/" + processorId; 1233 1234 getProcessorObject(asyncResp, processorId, getProcessorData); 1235 }); 1236 1237 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Processors/<str>/") 1238 .privileges(redfish::privileges::patchProcessor) 1239 .methods(boost::beast::http::verb::patch)( 1240 [&app](const crow::Request& req, 1241 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1242 const std::string& processorId) { 1243 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1244 { 1245 return; 1246 } 1247 std::optional<nlohmann::json> appliedConfigJson; 1248 if (!json_util::readJsonPatch(req, asyncResp->res, 1249 "AppliedOperatingConfig", 1250 appliedConfigJson)) 1251 { 1252 return; 1253 } 1254 1255 std::string appliedConfigUri; 1256 if (appliedConfigJson) 1257 { 1258 if (!json_util::readJson(*appliedConfigJson, asyncResp->res, 1259 "@odata.id", appliedConfigUri)) 1260 { 1261 return; 1262 } 1263 // Check for 404 and find matching D-Bus object, then run 1264 // property patch handlers if that all succeeds. 1265 getProcessorObject( 1266 asyncResp, processorId, 1267 [appliedConfigUri = std::move(appliedConfigUri)]( 1268 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1269 const std::string& processorId, 1270 const std::string& objectPath, 1271 const dbus::utility::MapperServiceMap& serviceMap) { 1272 patchAppliedOperatingConfig(asyncResp, processorId, 1273 appliedConfigUri, 1274 objectPath, serviceMap); 1275 }); 1276 } 1277 }); 1278 } 1279 1280 } // namespace redfish 1281