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 boost::lexical_cast<std::string>(*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." 595 "OperatingConfig", 596 "BaseSpeedPrioritySettings"); 597 } 598 else if (dbusPropName == "BaseSpeedPriorityEnabled") 599 { 600 const bool* state = std::get_if<bool>(&variantVal); 601 if (state != nullptr) 602 { 603 json["BaseSpeedPriorityState"] = 604 *state ? "Enabled" : "Disabled"; 605 } 606 } 607 } 608 }, 609 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 610 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"); 611 } 612 613 /** 614 * @brief Fill out location info of a processor by 615 * requesting data from the given D-Bus object. 616 * 617 * @param[in,out] aResp Async HTTP response. 618 * @param[in] service D-Bus service to query. 619 * @param[in] objPath D-Bus object to query. 620 */ 621 inline void getCpuLocationCode(std::shared_ptr<bmcweb::AsyncResp> aResp, 622 const std::string& service, 623 const std::string& objPath) 624 { 625 BMCWEB_LOG_DEBUG << "Get Cpu Location Data"; 626 crow::connections::systemBus->async_method_call( 627 [objPath, 628 aResp{std::move(aResp)}](const boost::system::error_code ec, 629 const std::variant<std::string>& property) { 630 if (ec) 631 { 632 BMCWEB_LOG_DEBUG << "DBUS response error"; 633 messages::internalError(aResp->res); 634 return; 635 } 636 637 const std::string* value = std::get_if<std::string>(&property); 638 639 if (value == nullptr) 640 { 641 // illegal value 642 BMCWEB_LOG_DEBUG << "Location code value error"; 643 messages::internalError(aResp->res); 644 return; 645 } 646 647 aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 648 *value; 649 }, 650 service, objPath, "org.freedesktop.DBus.Properties", "Get", 651 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode"); 652 } 653 654 /** 655 * Populate the unique identifier in a Processor resource by requesting data 656 * from the given D-Bus object. 657 * 658 * @param[in,out] aResp Async HTTP response. 659 * @param[in] service D-Bus service to query. 660 * @param[in] objPath D-Bus object to query. 661 */ 662 inline void getCpuUniqueId(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 663 const std::string& service, 664 const std::string& objectPath) 665 { 666 BMCWEB_LOG_DEBUG << "Get CPU UniqueIdentifier"; 667 crow::connections::systemBus->async_method_call( 668 [aResp](boost::system::error_code ec, 669 const std::variant<std::string>& property) { 670 const std::string* id = std::get_if<std::string>(&property); 671 if (ec || id == nullptr) 672 { 673 BMCWEB_LOG_ERROR << "Failed to read cpu unique id: " << ec; 674 messages::internalError(aResp->res); 675 return; 676 } 677 aResp->res 678 .jsonValue["ProcessorId"]["ProtectedIdentificationNumber"] = 679 *id; 680 }, 681 service, objectPath, "org.freedesktop.DBus.Properties", "Get", 682 "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier", 683 "UniqueIdentifier"); 684 } 685 686 /** 687 * Find the D-Bus object representing the requested Processor, and call the 688 * handler with the results. If matching object is not found, add 404 error to 689 * response and don't call the handler. 690 * 691 * @param[in,out] resp Async HTTP response. 692 * @param[in] processorId Redfish Processor Id. 693 * @param[in] handler Callback to continue processing request upon 694 * successfully finding object. 695 */ 696 template <typename Handler> 697 inline void getProcessorObject(const std::shared_ptr<bmcweb::AsyncResp>& resp, 698 const std::string& processorId, 699 Handler&& handler) 700 { 701 BMCWEB_LOG_DEBUG << "Get available system processor resources."; 702 703 // GetSubTree on all interfaces which provide info about a Processor 704 crow::connections::systemBus->async_method_call( 705 [resp, processorId, handler = std::forward<Handler>(handler)]( 706 boost::system::error_code ec, 707 const MapperGetSubTreeResponse& subtree) mutable { 708 if (ec) 709 { 710 BMCWEB_LOG_DEBUG << "DBUS response error: " << ec; 711 messages::internalError(resp->res); 712 return; 713 } 714 for (const auto& [objectPath, serviceMap] : subtree) 715 { 716 // Ignore any objects which don't end with our desired cpu name 717 if (!boost::ends_with(objectPath, processorId)) 718 { 719 continue; 720 } 721 722 bool found = false; 723 // Filter out objects that don't have the CPU-specific 724 // interfaces to make sure we can return 404 on non-CPUs 725 // (e.g. /redfish/../Processors/dimm0) 726 for (const auto& [serviceName, interfaceList] : serviceMap) 727 { 728 if (std::find_first_of( 729 interfaceList.begin(), interfaceList.end(), 730 processorInterfaces.begin(), 731 processorInterfaces.end()) != interfaceList.end()) 732 { 733 found = true; 734 break; 735 } 736 } 737 738 if (!found) 739 { 740 continue; 741 } 742 743 // Process the first object which does match our cpu name and 744 // required interfaces, and potentially ignore any other 745 // matching objects. Assume all interfaces we want to process 746 // must be on the same object path. 747 748 handler(resp, processorId, objectPath, serviceMap); 749 return; 750 } 751 messages::resourceNotFound(resp->res, "Processor", processorId); 752 }, 753 "xyz.openbmc_project.ObjectMapper", 754 "/xyz/openbmc_project/object_mapper", 755 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 756 "/xyz/openbmc_project/inventory", 0, 757 std::array<const char*, 8>{ 758 "xyz.openbmc_project.Common.UUID", 759 "xyz.openbmc_project.Inventory.Decorator.Asset", 760 "xyz.openbmc_project.Inventory.Decorator.Revision", 761 "xyz.openbmc_project.Inventory.Item.Cpu", 762 "xyz.openbmc_project.Inventory.Decorator.LocationCode", 763 "xyz.openbmc_project.Inventory.Item.Accelerator", 764 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", 765 "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier"}); 766 } 767 768 inline void getProcessorData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 769 const std::string& processorId, 770 const std::string& objectPath, 771 const MapperServiceMap& serviceMap) 772 { 773 for (const auto& [serviceName, interfaceList] : serviceMap) 774 { 775 for (const auto& interface : interfaceList) 776 { 777 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") 778 { 779 getCpuAssetData(aResp, serviceName, objectPath); 780 } 781 else if (interface == "xyz.openbmc_project.Inventory." 782 "Decorator.Revision") 783 { 784 getCpuRevisionData(aResp, serviceName, objectPath); 785 } 786 else if (interface == "xyz.openbmc_project.Inventory.Item.Cpu") 787 { 788 getCpuDataByService(aResp, processorId, serviceName, 789 objectPath); 790 } 791 else if (interface == "xyz.openbmc_project.Inventory." 792 "Item.Accelerator") 793 { 794 getAcceleratorDataByService(aResp, processorId, serviceName, 795 objectPath); 796 } 797 else if (interface == "xyz.openbmc_project.Control.Processor." 798 "CurrentOperatingConfig") 799 { 800 getCpuConfigData(aResp, processorId, serviceName, objectPath); 801 } 802 else if (interface == "xyz.openbmc_project.Inventory." 803 "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 == "xyz.openbmc_project.Inventory." 812 "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(boost::beast::http::verb::get)( 1072 [](const crow::Request& req, 1073 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1074 const std::string& cpuName) { 1075 asyncResp->res.jsonValue["@odata.type"] = 1076 "#OperatingConfigCollection.OperatingConfigCollection"; 1077 asyncResp->res.jsonValue["@odata.id"] = req.url; 1078 asyncResp->res.jsonValue["Name"] = 1079 "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, 1085 cpuName](const boost::system::error_code ec, 1086 const std::vector<std::string>& objects) { 1087 if (ec) 1088 { 1089 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 1090 << ec.message(); 1091 messages::internalError(asyncResp->res); 1092 return; 1093 } 1094 1095 for (const std::string& object : objects) 1096 { 1097 if (!boost::ends_with(object, cpuName)) 1098 { 1099 continue; 1100 } 1101 1102 // Not expected that there will be multiple matching 1103 // CPU objects, but if there are just use the first 1104 // one. 1105 1106 // Use the common search routine to construct the 1107 // Collection of all Config objects under this CPU. 1108 collection_util::getCollectionMembers( 1109 asyncResp, 1110 "/redfish/v1/Systems/system/Processors/" + 1111 cpuName + "/OperatingConfigs", 1112 {"xyz.openbmc_project.Inventory.Item.Cpu." 1113 "OperatingConfig"}, 1114 object.c_str()); 1115 return; 1116 } 1117 }, 1118 "xyz.openbmc_project.ObjectMapper", 1119 "/xyz/openbmc_project/object_mapper", 1120 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 1121 "/xyz/openbmc_project/inventory", 0, 1122 std::array<const char*, 1>{ 1123 "xyz.openbmc_project.Control.Processor." 1124 "CurrentOperatingConfig"}); 1125 }); 1126 } 1127 1128 inline void requestRoutesOperatingConfig(App& app) 1129 { 1130 BMCWEB_ROUTE( 1131 app, 1132 "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/<str>/") 1133 .privileges(redfish::privileges::getOperatingConfig) 1134 .methods( 1135 boost::beast::http::verb::get)([](const crow::Request& req, 1136 const std::shared_ptr< 1137 bmcweb::AsyncResp>& asyncResp, 1138 const std::string& cpuName, 1139 const std::string& configName) { 1140 // Ask for all objects implementing OperatingConfig so we can search 1141 // for one with a matching name 1142 crow::connections::systemBus->async_method_call( 1143 [asyncResp, cpuName, configName, 1144 reqUrl{req.url}](boost::system::error_code ec, 1145 const MapperGetSubTreeResponse& subtree) { 1146 if (ec) 1147 { 1148 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " 1149 << ec.message(); 1150 messages::internalError(asyncResp->res); 1151 return; 1152 } 1153 const std::string expectedEnding = 1154 cpuName + '/' + configName; 1155 for (const auto& [objectPath, serviceMap] : subtree) 1156 { 1157 // Ignore any configs without matching cpuX/configY 1158 if (!boost::ends_with(objectPath, expectedEnding) || 1159 serviceMap.empty()) 1160 { 1161 continue; 1162 } 1163 1164 nlohmann::json& json = asyncResp->res.jsonValue; 1165 json["@odata.type"] = 1166 "#OperatingConfig.v1_0_0.OperatingConfig"; 1167 json["@odata.id"] = reqUrl; 1168 json["Name"] = "Processor Profile"; 1169 json["Id"] = configName; 1170 1171 // Just use the first implementation of the object - not 1172 // expected that there would be multiple matching 1173 // services 1174 getOperatingConfigData( 1175 asyncResp, serviceMap.begin()->first, objectPath); 1176 return; 1177 } 1178 messages::resourceNotFound(asyncResp->res, 1179 "OperatingConfig", configName); 1180 }, 1181 "xyz.openbmc_project.ObjectMapper", 1182 "/xyz/openbmc_project/object_mapper", 1183 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 1184 "/xyz/openbmc_project/inventory", 0, 1185 std::array<const char*, 1>{ 1186 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}); 1187 }); 1188 } 1189 1190 inline void requestRoutesProcessorCollection(App& app) 1191 { 1192 /** 1193 * Functions triggers appropriate requests on DBus 1194 */ 1195 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Processors/") 1196 .privileges(redfish::privileges::getProcessorCollection) 1197 .methods(boost::beast::http::verb::get)( 1198 [](const crow::Request&, 1199 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1200 asyncResp->res.jsonValue["@odata.type"] = 1201 "#ProcessorCollection.ProcessorCollection"; 1202 asyncResp->res.jsonValue["Name"] = "Processor Collection"; 1203 1204 asyncResp->res.jsonValue["@odata.id"] = 1205 "/redfish/v1/Systems/system/Processors"; 1206 1207 collection_util::getCollectionMembers( 1208 asyncResp, "/redfish/v1/Systems/system/Processors", 1209 std::vector<const char*>(processorInterfaces.begin(), 1210 processorInterfaces.end())); 1211 }); 1212 } 1213 1214 inline void requestRoutesProcessor(App& app) 1215 { 1216 /** 1217 * Functions triggers appropriate requests on DBus 1218 */ 1219 1220 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Processors/<str>/") 1221 .privileges(redfish::privileges::getProcessor) 1222 .methods(boost::beast::http::verb::get)( 1223 [](const crow::Request&, 1224 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1225 const std::string& processorId) { 1226 asyncResp->res.jsonValue["@odata.type"] = 1227 "#Processor.v1_11_0.Processor"; 1228 asyncResp->res.jsonValue["@odata.id"] = 1229 "/redfish/v1/Systems/system/Processors/" + processorId; 1230 1231 getProcessorObject(asyncResp, processorId, getProcessorData); 1232 }); 1233 1234 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Processors/<str>/") 1235 .privileges(redfish::privileges::patchProcessor) 1236 .methods(boost::beast::http::verb::patch)( 1237 [](const crow::Request& req, 1238 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1239 const std::string& processorId) { 1240 std::optional<nlohmann::json> appliedConfigJson; 1241 if (!json_util::readJson(req, asyncResp->res, 1242 "AppliedOperatingConfig", 1243 appliedConfigJson)) 1244 { 1245 return; 1246 } 1247 1248 std::string appliedConfigUri; 1249 if (appliedConfigJson) 1250 { 1251 if (!json_util::readJson(*appliedConfigJson, asyncResp->res, 1252 "@odata.id", appliedConfigUri)) 1253 { 1254 return; 1255 } 1256 // Check for 404 and find matching D-Bus object, then run 1257 // property patch handlers if that all succeeds. 1258 getProcessorObject( 1259 asyncResp, processorId, 1260 [appliedConfigUri = std::move(appliedConfigUri)]( 1261 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1262 const std::string& processorId, 1263 const std::string& objectPath, 1264 const MapperServiceMap& serviceMap) { 1265 patchAppliedOperatingConfig(asyncResp, processorId, 1266 appliedConfigUri, 1267 objectPath, serviceMap); 1268 }); 1269 } 1270 }); 1271 } 1272 1273 } // namespace redfish 1274