xref: /openbmc/bmcweb/redfish-core/lib/processor_operating_config.hpp (revision 5ecf7b4e1bb6988dd3403fa80b723afbe2be30ae)
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