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 */
getOperatingConfigData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & objPath)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
handleOperatingConfigCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & cpuName)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
handleOperationConfigGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & cpuName,const std::string & configName)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
requestRoutesOperatingConfig(App & app)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