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