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