1*3b37e835SChristopher Meis // SPDX-License-Identifier: Apache-2.0 2*3b37e835SChristopher Meis // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3*3b37e835SChristopher Meis // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation 4*3b37e835SChristopher Meis #pragma once 5*3b37e835SChristopher Meis 6*3b37e835SChristopher Meis #include "bmcweb_config.h" 7*3b37e835SChristopher Meis 8*3b37e835SChristopher Meis #include "app.hpp" 9*3b37e835SChristopher Meis #include "async_resp.hpp" 10*3b37e835SChristopher Meis #include "dbus_utility.hpp" 11*3b37e835SChristopher Meis #include "error_messages.hpp" 12*3b37e835SChristopher Meis #include "http_request.hpp" 13*3b37e835SChristopher Meis #include "logging.hpp" 14*3b37e835SChristopher Meis #include "query.hpp" 15*3b37e835SChristopher Meis #include "registries/privilege_registry.hpp" 16*3b37e835SChristopher Meis #include "utils/collection.hpp" 17*3b37e835SChristopher Meis #include "utils/dbus_utils.hpp" 18*3b37e835SChristopher Meis 19*3b37e835SChristopher Meis #include <boost/beast/http/field.hpp> 20*3b37e835SChristopher Meis #include <boost/beast/http/verb.hpp> 21*3b37e835SChristopher Meis #include <boost/system/error_code.hpp> 22*3b37e835SChristopher Meis #include <boost/url/format.hpp> 23*3b37e835SChristopher Meis #include <sdbusplus/message/native_types.hpp> 24*3b37e835SChristopher Meis #include <sdbusplus/unpack_properties.hpp> 25*3b37e835SChristopher Meis 26*3b37e835SChristopher Meis #include <array> 27*3b37e835SChristopher Meis #include <cstddef> 28*3b37e835SChristopher Meis #include <cstdint> 29*3b37e835SChristopher Meis #include <format> 30*3b37e835SChristopher Meis #include <functional> 31*3b37e835SChristopher Meis #include <memory> 32*3b37e835SChristopher Meis #include <ranges> 33*3b37e835SChristopher Meis #include <string> 34*3b37e835SChristopher Meis #include <string_view> 35*3b37e835SChristopher Meis #include <tuple> 36*3b37e835SChristopher Meis #include <utility> 37*3b37e835SChristopher Meis #include <vector> 38*3b37e835SChristopher Meis 39*3b37e835SChristopher Meis namespace redfish 40*3b37e835SChristopher Meis { 41*3b37e835SChristopher Meis 42*3b37e835SChristopher Meis // OperatingConfig D-Bus Types 43*3b37e835SChristopher Meis using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>; 44*3b37e835SChristopher Meis using BaseSpeedPrioritySettingsProperty = 45*3b37e835SChristopher Meis std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>; 46*3b37e835SChristopher Meis // uint32_t and size_t may or may not be the same type, requiring a dedup'd 47*3b37e835SChristopher Meis // variant 48*3b37e835SChristopher Meis 49*3b37e835SChristopher Meis /** 50*3b37e835SChristopher Meis * Request all the properties for the given D-Bus object and fill out the 51*3b37e835SChristopher Meis * related entries in the Redfish OperatingConfig response. 52*3b37e835SChristopher Meis * 53*3b37e835SChristopher Meis * @param[in,out] asyncResp Async HTTP response. 54*3b37e835SChristopher Meis * @param[in] service D-Bus service name to query. 55*3b37e835SChristopher Meis * @param[in] objPath D-Bus object to query. 56*3b37e835SChristopher Meis */ 57*3b37e835SChristopher Meis inline void getOperatingConfigData( 58*3b37e835SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 59*3b37e835SChristopher Meis const std::string& service, const std::string& objPath) 60*3b37e835SChristopher Meis { 61*3b37e835SChristopher Meis dbus::utility::getAllProperties( 62*3b37e835SChristopher Meis service, objPath, 63*3b37e835SChristopher Meis "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig", 64*3b37e835SChristopher Meis [asyncResp](const boost::system::error_code& ec, 65*3b37e835SChristopher Meis const dbus::utility::DBusPropertiesMap& properties) { 66*3b37e835SChristopher Meis if (ec) 67*3b37e835SChristopher Meis { 68*3b37e835SChristopher Meis BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); 69*3b37e835SChristopher Meis messages::internalError(asyncResp->res); 70*3b37e835SChristopher Meis return; 71*3b37e835SChristopher Meis } 72*3b37e835SChristopher Meis 73*3b37e835SChristopher Meis const size_t* availableCoreCount = nullptr; 74*3b37e835SChristopher Meis const uint32_t* baseSpeed = nullptr; 75*3b37e835SChristopher Meis const uint32_t* maxJunctionTemperature = nullptr; 76*3b37e835SChristopher Meis const uint32_t* maxSpeed = nullptr; 77*3b37e835SChristopher Meis const uint32_t* powerLimit = nullptr; 78*3b37e835SChristopher Meis const TurboProfileProperty* turboProfile = nullptr; 79*3b37e835SChristopher Meis const BaseSpeedPrioritySettingsProperty* baseSpeedPrioritySettings = 80*3b37e835SChristopher Meis nullptr; 81*3b37e835SChristopher Meis 82*3b37e835SChristopher Meis const bool success = sdbusplus::unpackPropertiesNoThrow( 83*3b37e835SChristopher Meis dbus_utils::UnpackErrorPrinter(), properties, 84*3b37e835SChristopher Meis "AvailableCoreCount", availableCoreCount, "BaseSpeed", 85*3b37e835SChristopher Meis baseSpeed, "MaxJunctionTemperature", maxJunctionTemperature, 86*3b37e835SChristopher Meis "MaxSpeed", maxSpeed, "PowerLimit", powerLimit, "TurboProfile", 87*3b37e835SChristopher Meis turboProfile, "BaseSpeedPrioritySettings", 88*3b37e835SChristopher Meis baseSpeedPrioritySettings); 89*3b37e835SChristopher Meis 90*3b37e835SChristopher Meis if (!success) 91*3b37e835SChristopher Meis { 92*3b37e835SChristopher Meis messages::internalError(asyncResp->res); 93*3b37e835SChristopher Meis return; 94*3b37e835SChristopher Meis } 95*3b37e835SChristopher Meis 96*3b37e835SChristopher Meis nlohmann::json& json = asyncResp->res.jsonValue; 97*3b37e835SChristopher Meis 98*3b37e835SChristopher Meis if (availableCoreCount != nullptr) 99*3b37e835SChristopher Meis { 100*3b37e835SChristopher Meis json["TotalAvailableCoreCount"] = *availableCoreCount; 101*3b37e835SChristopher Meis } 102*3b37e835SChristopher Meis 103*3b37e835SChristopher Meis if (baseSpeed != nullptr) 104*3b37e835SChristopher Meis { 105*3b37e835SChristopher Meis json["BaseSpeedMHz"] = *baseSpeed; 106*3b37e835SChristopher Meis } 107*3b37e835SChristopher Meis 108*3b37e835SChristopher Meis if (maxJunctionTemperature != nullptr) 109*3b37e835SChristopher Meis { 110*3b37e835SChristopher Meis json["MaxJunctionTemperatureCelsius"] = *maxJunctionTemperature; 111*3b37e835SChristopher Meis } 112*3b37e835SChristopher Meis 113*3b37e835SChristopher Meis if (maxSpeed != nullptr) 114*3b37e835SChristopher Meis { 115*3b37e835SChristopher Meis json["MaxSpeedMHz"] = *maxSpeed; 116*3b37e835SChristopher Meis } 117*3b37e835SChristopher Meis 118*3b37e835SChristopher Meis if (powerLimit != nullptr) 119*3b37e835SChristopher Meis { 120*3b37e835SChristopher Meis json["TDPWatts"] = *powerLimit; 121*3b37e835SChristopher Meis } 122*3b37e835SChristopher Meis 123*3b37e835SChristopher Meis if (turboProfile != nullptr) 124*3b37e835SChristopher Meis { 125*3b37e835SChristopher Meis nlohmann::json& turboArray = json["TurboProfile"]; 126*3b37e835SChristopher Meis turboArray = nlohmann::json::array(); 127*3b37e835SChristopher Meis for (const auto& [turboSpeed, coreCount] : *turboProfile) 128*3b37e835SChristopher Meis { 129*3b37e835SChristopher Meis nlohmann::json::object_t turbo; 130*3b37e835SChristopher Meis turbo["ActiveCoreCount"] = coreCount; 131*3b37e835SChristopher Meis turbo["MaxSpeedMHz"] = turboSpeed; 132*3b37e835SChristopher Meis turboArray.emplace_back(std::move(turbo)); 133*3b37e835SChristopher Meis } 134*3b37e835SChristopher Meis } 135*3b37e835SChristopher Meis 136*3b37e835SChristopher Meis if (baseSpeedPrioritySettings != nullptr) 137*3b37e835SChristopher Meis { 138*3b37e835SChristopher Meis nlohmann::json& baseSpeedArray = 139*3b37e835SChristopher Meis json["BaseSpeedPrioritySettings"]; 140*3b37e835SChristopher Meis baseSpeedArray = nlohmann::json::array(); 141*3b37e835SChristopher Meis for (const auto& [baseSpeedMhz, coreList] : 142*3b37e835SChristopher Meis *baseSpeedPrioritySettings) 143*3b37e835SChristopher Meis { 144*3b37e835SChristopher Meis nlohmann::json::object_t speed; 145*3b37e835SChristopher Meis speed["CoreCount"] = coreList.size(); 146*3b37e835SChristopher Meis speed["CoreIDs"] = coreList; 147*3b37e835SChristopher Meis speed["BaseSpeedMHz"] = baseSpeedMhz; 148*3b37e835SChristopher Meis baseSpeedArray.emplace_back(std::move(speed)); 149*3b37e835SChristopher Meis } 150*3b37e835SChristopher Meis } 151*3b37e835SChristopher Meis }); 152*3b37e835SChristopher Meis } 153*3b37e835SChristopher Meis 154*3b37e835SChristopher Meis inline void handleOperatingConfigCollectionGet( 155*3b37e835SChristopher Meis App& app, const crow::Request& req, 156*3b37e835SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 157*3b37e835SChristopher Meis const std::string& systemName, const std::string& cpuName) 158*3b37e835SChristopher Meis { 159*3b37e835SChristopher Meis if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 160*3b37e835SChristopher Meis { 161*3b37e835SChristopher Meis return; 162*3b37e835SChristopher Meis } 163*3b37e835SChristopher Meis 164*3b37e835SChristopher Meis if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 165*3b37e835SChristopher Meis { 166*3b37e835SChristopher Meis // Option currently returns no systems. TBD 167*3b37e835SChristopher Meis messages::resourceNotFound(asyncResp->res, "ComputerSystem", 168*3b37e835SChristopher Meis systemName); 169*3b37e835SChristopher Meis return; 170*3b37e835SChristopher Meis } 171*3b37e835SChristopher Meis 172*3b37e835SChristopher Meis if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 173*3b37e835SChristopher Meis { 174*3b37e835SChristopher Meis messages::resourceNotFound(asyncResp->res, "ComputerSystem", 175*3b37e835SChristopher Meis systemName); 176*3b37e835SChristopher Meis return; 177*3b37e835SChristopher Meis } 178*3b37e835SChristopher Meis asyncResp->res.jsonValue["@odata.type"] = 179*3b37e835SChristopher Meis "#OperatingConfigCollection.OperatingConfigCollection"; 180*3b37e835SChristopher Meis asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 181*3b37e835SChristopher Meis "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", 182*3b37e835SChristopher Meis BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName); 183*3b37e835SChristopher Meis asyncResp->res.jsonValue["Name"] = "Operating Config Collection"; 184*3b37e835SChristopher Meis 185*3b37e835SChristopher Meis // First find the matching CPU object so we know how to 186*3b37e835SChristopher Meis // constrain our search for related Config objects. 187*3b37e835SChristopher Meis const std::array<std::string_view, 1> interfaces = { 188*3b37e835SChristopher Meis "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}; 189*3b37e835SChristopher Meis dbus::utility::getSubTreePaths( 190*3b37e835SChristopher Meis "/xyz/openbmc_project/inventory", 0, interfaces, 191*3b37e835SChristopher Meis [asyncResp, 192*3b37e835SChristopher Meis cpuName](const boost::system::error_code& ec, 193*3b37e835SChristopher Meis const dbus::utility::MapperGetSubTreePathsResponse& objects) { 194*3b37e835SChristopher Meis if (ec) 195*3b37e835SChristopher Meis { 196*3b37e835SChristopher Meis BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); 197*3b37e835SChristopher Meis messages::internalError(asyncResp->res); 198*3b37e835SChristopher Meis return; 199*3b37e835SChristopher Meis } 200*3b37e835SChristopher Meis 201*3b37e835SChristopher Meis for (const std::string& object : objects) 202*3b37e835SChristopher Meis { 203*3b37e835SChristopher Meis if (!object.ends_with(cpuName)) 204*3b37e835SChristopher Meis { 205*3b37e835SChristopher Meis continue; 206*3b37e835SChristopher Meis } 207*3b37e835SChristopher Meis 208*3b37e835SChristopher Meis // Not expected that there will be multiple matching 209*3b37e835SChristopher Meis // CPU objects, but if there are just use the first 210*3b37e835SChristopher Meis // one. 211*3b37e835SChristopher Meis 212*3b37e835SChristopher Meis // Use the common search routine to construct the 213*3b37e835SChristopher Meis // Collection of all Config objects under this CPU. 214*3b37e835SChristopher Meis constexpr std::array<std::string_view, 1> interface{ 215*3b37e835SChristopher Meis "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; 216*3b37e835SChristopher Meis collection_util::getCollectionMembers( 217*3b37e835SChristopher Meis asyncResp, 218*3b37e835SChristopher Meis boost::urls::format( 219*3b37e835SChristopher Meis "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", 220*3b37e835SChristopher Meis BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName), 221*3b37e835SChristopher Meis interface, object); 222*3b37e835SChristopher Meis return; 223*3b37e835SChristopher Meis } 224*3b37e835SChristopher Meis }); 225*3b37e835SChristopher Meis } 226*3b37e835SChristopher Meis 227*3b37e835SChristopher Meis inline void handleOperationConfigGet( 228*3b37e835SChristopher Meis App& app, const crow::Request& req, 229*3b37e835SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 230*3b37e835SChristopher Meis const std::string& systemName, const std::string& cpuName, 231*3b37e835SChristopher Meis const std::string& configName) 232*3b37e835SChristopher Meis { 233*3b37e835SChristopher Meis if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 234*3b37e835SChristopher Meis { 235*3b37e835SChristopher Meis return; 236*3b37e835SChristopher Meis } 237*3b37e835SChristopher Meis if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 238*3b37e835SChristopher Meis { 239*3b37e835SChristopher Meis // Option currently returns no systems. TBD 240*3b37e835SChristopher Meis messages::resourceNotFound(asyncResp->res, "ComputerSystem", 241*3b37e835SChristopher Meis systemName); 242*3b37e835SChristopher Meis return; 243*3b37e835SChristopher Meis } 244*3b37e835SChristopher Meis 245*3b37e835SChristopher Meis if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 246*3b37e835SChristopher Meis { 247*3b37e835SChristopher Meis messages::resourceNotFound(asyncResp->res, "ComputerSystem", 248*3b37e835SChristopher Meis systemName); 249*3b37e835SChristopher Meis return; 250*3b37e835SChristopher Meis } 251*3b37e835SChristopher Meis // Ask for all objects implementing OperatingConfig so we can search 252*3b37e835SChristopher Meis // for one with a matching name 253*3b37e835SChristopher Meis constexpr std::array<std::string_view, 1> interfaces = { 254*3b37e835SChristopher Meis "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; 255*3b37e835SChristopher Meis dbus::utility::getSubTree( 256*3b37e835SChristopher Meis "/xyz/openbmc_project/inventory", 0, interfaces, 257*3b37e835SChristopher Meis [asyncResp, cpuName, 258*3b37e835SChristopher Meis configName](const boost::system::error_code& ec, 259*3b37e835SChristopher Meis const dbus::utility::MapperGetSubTreeResponse& subtree) { 260*3b37e835SChristopher Meis if (ec) 261*3b37e835SChristopher Meis { 262*3b37e835SChristopher Meis BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); 263*3b37e835SChristopher Meis messages::internalError(asyncResp->res); 264*3b37e835SChristopher Meis return; 265*3b37e835SChristopher Meis } 266*3b37e835SChristopher Meis const std::string expectedEnding = cpuName + '/' + configName; 267*3b37e835SChristopher Meis for (const auto& [objectPath, serviceMap] : subtree) 268*3b37e835SChristopher Meis { 269*3b37e835SChristopher Meis // Ignore any configs without matching cpuX/configY 270*3b37e835SChristopher Meis if (!objectPath.ends_with(expectedEnding) || serviceMap.empty()) 271*3b37e835SChristopher Meis { 272*3b37e835SChristopher Meis continue; 273*3b37e835SChristopher Meis } 274*3b37e835SChristopher Meis 275*3b37e835SChristopher Meis nlohmann::json& json = asyncResp->res.jsonValue; 276*3b37e835SChristopher Meis json["@odata.type"] = "#OperatingConfig.v1_0_0.OperatingConfig"; 277*3b37e835SChristopher Meis json["@odata.id"] = boost::urls::format( 278*3b37e835SChristopher Meis "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}", 279*3b37e835SChristopher Meis BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName, configName); 280*3b37e835SChristopher Meis json["Name"] = "Processor Profile"; 281*3b37e835SChristopher Meis json["Id"] = configName; 282*3b37e835SChristopher Meis 283*3b37e835SChristopher Meis // Just use the first implementation of the object - not 284*3b37e835SChristopher Meis // expected that there would be multiple matching 285*3b37e835SChristopher Meis // services 286*3b37e835SChristopher Meis getOperatingConfigData(asyncResp, serviceMap.begin()->first, 287*3b37e835SChristopher Meis objectPath); 288*3b37e835SChristopher Meis return; 289*3b37e835SChristopher Meis } 290*3b37e835SChristopher Meis messages::resourceNotFound(asyncResp->res, "OperatingConfig", 291*3b37e835SChristopher Meis configName); 292*3b37e835SChristopher Meis }); 293*3b37e835SChristopher Meis } 294*3b37e835SChristopher Meis 295*3b37e835SChristopher Meis inline void requestRoutesOperatingConfig(App& app) 296*3b37e835SChristopher Meis { 297*3b37e835SChristopher Meis BMCWEB_ROUTE(app, 298*3b37e835SChristopher Meis "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/") 299*3b37e835SChristopher Meis .privileges(redfish::privileges::getOperatingConfigCollection) 300*3b37e835SChristopher Meis .methods(boost::beast::http::verb::get)( 301*3b37e835SChristopher Meis std::bind_front(handleOperatingConfigCollectionGet, std::ref(app))); 302*3b37e835SChristopher Meis 303*3b37e835SChristopher Meis BMCWEB_ROUTE( 304*3b37e835SChristopher Meis app, 305*3b37e835SChristopher Meis "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/<str>/") 306*3b37e835SChristopher Meis .privileges(redfish::privileges::getOperatingConfig) 307*3b37e835SChristopher Meis .methods(boost::beast::http::verb::get)( 308*3b37e835SChristopher Meis std::bind_front(handleOperationConfigGet, std::ref(app))); 309*3b37e835SChristopher Meis } 310*3b37e835SChristopher Meis } // namespace redfish 311