xref: /openbmc/bmcweb/features/redfish/lib/processor_operating_config.hpp (revision 3b37e835b378975aea49089d72173530765db262)
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