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