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