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