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