xref: /openbmc/bmcweb/redfish-core/lib/processor.hpp (revision 98eafce542480892f19862182008f62014f5afd1)
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_singleton.hpp"
11 #include "dbus_utility.hpp"
12 #include "error_messages.hpp"
13 #include "generated/enums/processor.hpp"
14 #include "generated/enums/resource.hpp"
15 #include "http_request.hpp"
16 #include "led.hpp"
17 #include "logging.hpp"
18 #include "query.hpp"
19 #include "registries/privilege_registry.hpp"
20 #include "utils/collection.hpp"
21 #include "utils/dbus_utils.hpp"
22 #include "utils/hex_utils.hpp"
23 #include "utils/json_utils.hpp"
24 
25 #include <boost/beast/http/field.hpp>
26 #include <boost/beast/http/verb.hpp>
27 #include <boost/system/error_code.hpp>
28 #include <boost/url/format.hpp>
29 #include <sdbusplus/message/native_types.hpp>
30 #include <sdbusplus/unpack_properties.hpp>
31 
32 #include <algorithm>
33 #include <array>
34 #include <cstddef>
35 #include <cstdint>
36 #include <format>
37 #include <functional>
38 #include <limits>
39 #include <memory>
40 #include <optional>
41 #include <ranges>
42 #include <string>
43 #include <string_view>
44 #include <tuple>
45 #include <utility>
46 #include <variant>
47 #include <vector>
48 
49 namespace redfish
50 {
51 
52 // Interfaces which imply a D-Bus object represents a Processor
53 constexpr std::array<std::string_view, 2> processorInterfaces = {
54     "xyz.openbmc_project.Inventory.Item.Cpu",
55     "xyz.openbmc_project.Inventory.Item.Accelerator"};
56 
57 /**
58  * @brief Fill out uuid info of a processor by
59  * requesting data from the given D-Bus object.
60  *
61  * @param[in,out]   asyncResp       Async HTTP response.
62  * @param[in]       service     D-Bus service to query.
63  * @param[in]       objPath     D-Bus object to query.
64  */
getProcessorUUID(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & service,const std::string & objPath)65 inline void getProcessorUUID(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
66                              const std::string& service,
67                              const std::string& objPath)
68 {
69     BMCWEB_LOG_DEBUG("Get Processor UUID");
70     dbus::utility::getProperty<std::string>(
71         service, objPath, "xyz.openbmc_project.Common.UUID", "UUID",
72         [objPath, asyncResp{std::move(asyncResp)}](
73             const boost::system::error_code& ec, const std::string& property) {
74             if (ec)
75             {
76                 BMCWEB_LOG_DEBUG("DBUS response error");
77                 messages::internalError(asyncResp->res);
78                 return;
79             }
80             asyncResp->res.jsonValue["UUID"] = property;
81         });
82 }
83 
getCpuDataByInterface(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const dbus::utility::DBusInterfacesMap & cpuInterfacesProperties)84 inline void getCpuDataByInterface(
85     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
86     const dbus::utility::DBusInterfacesMap& cpuInterfacesProperties)
87 {
88     BMCWEB_LOG_DEBUG("Get CPU resources by interface.");
89 
90     // Set the default value of state
91     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
92     asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK;
93 
94     for (const auto& interface : cpuInterfacesProperties)
95     {
96         for (const auto& property : interface.second)
97         {
98             if (property.first == "Present")
99             {
100                 const bool* cpuPresent = std::get_if<bool>(&property.second);
101                 if (cpuPresent == nullptr)
102                 {
103                     // Important property not in desired type
104                     messages::internalError(asyncResp->res);
105                     return;
106                 }
107                 if (!*cpuPresent)
108                 {
109                     // Slot is not populated
110                     asyncResp->res.jsonValue["Status"]["State"] =
111                         resource::State::Absent;
112                 }
113             }
114             else if (property.first == "Functional")
115             {
116                 const bool* cpuFunctional = std::get_if<bool>(&property.second);
117                 if (cpuFunctional == nullptr)
118                 {
119                     messages::internalError(asyncResp->res);
120                     return;
121                 }
122                 if (!*cpuFunctional)
123                 {
124                     asyncResp->res.jsonValue["Status"]["Health"] =
125                         resource::Health::Critical;
126                 }
127             }
128             else if (property.first == "CoreCount")
129             {
130                 const uint16_t* coresCount =
131                     std::get_if<uint16_t>(&property.second);
132                 if (coresCount == nullptr)
133                 {
134                     messages::internalError(asyncResp->res);
135                     return;
136                 }
137                 asyncResp->res.jsonValue["TotalCores"] = *coresCount;
138             }
139             else if (property.first == "MaxSpeedInMhz")
140             {
141                 const uint32_t* value = std::get_if<uint32_t>(&property.second);
142                 if (value != nullptr)
143                 {
144                     asyncResp->res.jsonValue["MaxSpeedMHz"] = *value;
145                 }
146             }
147             else if (property.first == "Socket")
148             {
149                 const std::string* value =
150                     std::get_if<std::string>(&property.second);
151                 if (value != nullptr)
152                 {
153                     asyncResp->res.jsonValue["Socket"] = *value;
154                 }
155             }
156             else if (property.first == "ThreadCount")
157             {
158                 const uint16_t* value = std::get_if<uint16_t>(&property.second);
159                 if (value != nullptr)
160                 {
161                     asyncResp->res.jsonValue["TotalThreads"] = *value;
162                 }
163             }
164             else if (property.first == "EffectiveFamily")
165             {
166                 const uint16_t* value = std::get_if<uint16_t>(&property.second);
167                 if (value != nullptr && *value != 2)
168                 {
169                     asyncResp->res.jsonValue["ProcessorId"]["EffectiveFamily"] =
170                         "0x" + intToHexString(*value, 4);
171                 }
172             }
173             else if (property.first == "EffectiveModel")
174             {
175                 const uint16_t* value = std::get_if<uint16_t>(&property.second);
176                 if (value == nullptr)
177                 {
178                     messages::internalError(asyncResp->res);
179                     return;
180                 }
181                 if (*value != 0)
182                 {
183                     asyncResp->res.jsonValue["ProcessorId"]["EffectiveModel"] =
184                         "0x" + intToHexString(*value, 4);
185                 }
186             }
187             else if (property.first == "Id")
188             {
189                 const uint64_t* value = std::get_if<uint64_t>(&property.second);
190                 if (value != nullptr && *value != 0)
191                 {
192                     asyncResp->res
193                         .jsonValue["ProcessorId"]["IdentificationRegisters"] =
194                         "0x" + intToHexString(*value, 16);
195                 }
196             }
197             else if (property.first == "Microcode")
198             {
199                 const uint32_t* value = std::get_if<uint32_t>(&property.second);
200                 if (value == nullptr)
201                 {
202                     messages::internalError(asyncResp->res);
203                     return;
204                 }
205                 if (*value != 0)
206                 {
207                     asyncResp->res.jsonValue["ProcessorId"]["MicrocodeInfo"] =
208                         "0x" + intToHexString(*value, 8);
209                 }
210             }
211             else if (property.first == "Step")
212             {
213                 const uint16_t* value = std::get_if<uint16_t>(&property.second);
214                 if (value == nullptr)
215                 {
216                     messages::internalError(asyncResp->res);
217                     return;
218                 }
219                 if (*value != std::numeric_limits<uint16_t>::max())
220                 {
221                     asyncResp->res.jsonValue["ProcessorId"]["Step"] =
222                         "0x" + intToHexString(*value, 4);
223                 }
224             }
225         }
226     }
227 }
228 
getCpuDataByService(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & cpuId,const std::string & service,const std::string & objPath)229 inline void getCpuDataByService(
230     std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& cpuId,
231     const std::string& service, const std::string& objPath)
232 {
233     BMCWEB_LOG_DEBUG("Get available system cpu resources by service.");
234 
235     sdbusplus::message::object_path path("/xyz/openbmc_project/inventory");
236     dbus::utility::getManagedObjects(
237         service, path,
238         [cpuId, service, objPath, asyncResp{std::move(asyncResp)}](
239             const boost::system::error_code& ec,
240             const dbus::utility::ManagedObjectType& dbusData) {
241             if (ec)
242             {
243                 BMCWEB_LOG_DEBUG("DBUS response error");
244                 messages::internalError(asyncResp->res);
245                 return;
246             }
247             asyncResp->res.jsonValue["Id"] = cpuId;
248             asyncResp->res.jsonValue["Name"] = "Processor";
249             asyncResp->res.jsonValue["ProcessorType"] =
250                 processor::ProcessorType::CPU;
251 
252             for (const auto& object : dbusData)
253             {
254                 if (object.first.str == objPath)
255                 {
256                     getCpuDataByInterface(asyncResp, object.second);
257                 }
258             }
259             return;
260         });
261 }
262 
263 /**
264  * @brief Translates throttle cause DBUS property to redfish.
265  *
266  * @param[in] dbusSource    The throttle cause from DBUS
267  *
268  * @return Returns as a string, the throttle cause in Redfish terms. If
269  * translation cannot be done, returns "Unknown" throttle reason.
270  */
dbusToRfThrottleCause(const std::string & dbusSource)271 inline processor::ThrottleCause dbusToRfThrottleCause(
272     const std::string& dbusSource)
273 {
274     if (dbusSource ==
275         "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.ClockLimit")
276     {
277         return processor::ThrottleCause::ClockLimit;
278     }
279     if (dbusSource ==
280         "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.ManagementDetectedFault")
281     {
282         return processor::ThrottleCause::ManagementDetectedFault;
283     }
284     if (dbusSource ==
285         "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.PowerLimit")
286     {
287         return processor::ThrottleCause::PowerLimit;
288     }
289     if (dbusSource ==
290         "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.ThermalLimit")
291     {
292         return processor::ThrottleCause::ThermalLimit;
293     }
294     if (dbusSource ==
295         "xyz.openbmc_project.Control.Power.Throttle.ThrottleReasons.Unknown")
296     {
297         return processor::ThrottleCause::Unknown;
298     }
299     return processor::ThrottleCause::Invalid;
300 }
301 
readThrottleProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::DBusPropertiesMap & properties)302 inline void readThrottleProperties(
303     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
304     const boost::system::error_code& ec,
305     const dbus::utility::DBusPropertiesMap& properties)
306 {
307     if (ec)
308     {
309         BMCWEB_LOG_ERROR("Processor Throttle getAllProperties error {}", ec);
310         messages::internalError(asyncResp->res);
311         return;
312     }
313 
314     const bool* status = nullptr;
315     const std::vector<std::string>* causes = nullptr;
316 
317     if (!sdbusplus::unpackPropertiesNoThrow(dbus_utils::UnpackErrorPrinter(),
318                                             properties, "Throttled", status,
319                                             "ThrottleCauses", causes))
320     {
321         messages::internalError(asyncResp->res);
322         return;
323     }
324 
325     asyncResp->res.jsonValue["Throttled"] = *status;
326     nlohmann::json::array_t rCauses;
327     for (const std::string& cause : *causes)
328     {
329         processor::ThrottleCause rfCause = dbusToRfThrottleCause(cause);
330         if (rfCause == processor::ThrottleCause::Invalid)
331         {
332             messages::internalError(asyncResp->res);
333             return;
334         }
335 
336         rCauses.emplace_back(rfCause);
337     }
338     asyncResp->res.jsonValue["ThrottleCauses"] = std::move(rCauses);
339 }
340 
getThrottleProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & objectPath)341 inline void getThrottleProperties(
342     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
343     const std::string& service, const std::string& objectPath)
344 {
345     BMCWEB_LOG_DEBUG("Get processor throttle resources");
346 
347     dbus::utility::getAllProperties(
348         service, objectPath, "xyz.openbmc_project.Control.Power.Throttle",
349         [asyncResp](const boost::system::error_code& ec,
350                     const dbus::utility::DBusPropertiesMap& properties) {
351             readThrottleProperties(asyncResp, ec, properties);
352         });
353 }
354 
getCpuAssetData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & service,const std::string & objPath)355 inline void getCpuAssetData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
356                             const std::string& service,
357                             const std::string& objPath)
358 {
359     BMCWEB_LOG_DEBUG("Get Cpu Asset Data");
360     dbus::utility::getAllProperties(
361         service, objPath, "xyz.openbmc_project.Inventory.Decorator.Asset",
362         [objPath, asyncResp{std::move(asyncResp)}](
363             const boost::system::error_code& ec,
364             const dbus::utility::DBusPropertiesMap& properties) {
365             if (ec)
366             {
367                 BMCWEB_LOG_DEBUG("DBUS response error");
368                 messages::internalError(asyncResp->res);
369                 return;
370             }
371 
372             const std::string* serialNumber = nullptr;
373             const std::string* model = nullptr;
374             const std::string* manufacturer = nullptr;
375             const std::string* partNumber = nullptr;
376             const std::string* sparePartNumber = nullptr;
377 
378             const bool success = sdbusplus::unpackPropertiesNoThrow(
379                 dbus_utils::UnpackErrorPrinter(), properties, "SerialNumber",
380                 serialNumber, "Model", model, "Manufacturer", manufacturer,
381                 "PartNumber", partNumber, "SparePartNumber", sparePartNumber);
382 
383             if (!success)
384             {
385                 messages::internalError(asyncResp->res);
386                 return;
387             }
388 
389             if (serialNumber != nullptr && !serialNumber->empty())
390             {
391                 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
392             }
393 
394             if ((model != nullptr) && !model->empty())
395             {
396                 asyncResp->res.jsonValue["Model"] = *model;
397             }
398 
399             if (manufacturer != nullptr)
400             {
401                 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
402 
403                 // Otherwise would be unexpected.
404                 if (manufacturer->find("Intel") != std::string::npos)
405                 {
406                     asyncResp->res.jsonValue["ProcessorArchitecture"] = "x86";
407                     asyncResp->res.jsonValue["InstructionSet"] = "x86-64";
408                 }
409                 else if (manufacturer->find("IBM") != std::string::npos)
410                 {
411                     asyncResp->res.jsonValue["ProcessorArchitecture"] = "Power";
412                     asyncResp->res.jsonValue["InstructionSet"] = "PowerISA";
413                 }
414             }
415 
416             if (partNumber != nullptr)
417             {
418                 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
419             }
420 
421             if (sparePartNumber != nullptr && !sparePartNumber->empty())
422             {
423                 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
424             }
425         });
426 }
427 
getCpuRevisionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & service,const std::string & objPath)428 inline void getCpuRevisionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
429                                const std::string& service,
430                                const std::string& objPath)
431 {
432     BMCWEB_LOG_DEBUG("Get Cpu Revision Data");
433     dbus::utility::getAllProperties(
434         service, objPath, "xyz.openbmc_project.Inventory.Decorator.Revision",
435         [objPath, asyncResp{std::move(asyncResp)}](
436             const boost::system::error_code& ec,
437             const dbus::utility::DBusPropertiesMap& properties) {
438             if (ec)
439             {
440                 BMCWEB_LOG_DEBUG("DBUS response error");
441                 messages::internalError(asyncResp->res);
442                 return;
443             }
444 
445             const std::string* version = nullptr;
446 
447             const bool success = sdbusplus::unpackPropertiesNoThrow(
448                 dbus_utils::UnpackErrorPrinter(), properties, "Version",
449                 version);
450 
451             if (!success)
452             {
453                 messages::internalError(asyncResp->res);
454                 return;
455             }
456 
457             if (version != nullptr)
458             {
459                 asyncResp->res.jsonValue["Version"] = *version;
460             }
461         });
462 }
463 
getAcceleratorDataByService(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & acclrtrId,const std::string & service,const std::string & objPath)464 inline void getAcceleratorDataByService(
465     std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& acclrtrId,
466     const std::string& service, const std::string& objPath)
467 {
468     BMCWEB_LOG_DEBUG("Get available system Accelerator resources by service.");
469     dbus::utility::getAllProperties(
470         service, objPath, "",
471         [acclrtrId, asyncResp{std::move(asyncResp)}](
472             const boost::system::error_code& ec,
473             const dbus::utility::DBusPropertiesMap& properties) {
474             if (ec)
475             {
476                 BMCWEB_LOG_DEBUG("DBUS response error");
477                 messages::internalError(asyncResp->res);
478                 return;
479             }
480 
481             const bool* functional = nullptr;
482             const bool* present = nullptr;
483 
484             const bool success = sdbusplus::unpackPropertiesNoThrow(
485                 dbus_utils::UnpackErrorPrinter(), properties, "Functional",
486                 functional, "Present", present);
487 
488             if (!success)
489             {
490                 messages::internalError(asyncResp->res);
491                 return;
492             }
493 
494             std::string state = "Enabled";
495             std::string health = "OK";
496 
497             if (present != nullptr && !*present)
498             {
499                 state = "Absent";
500             }
501 
502             if (functional != nullptr && !*functional)
503             {
504                 if (state == "Enabled")
505                 {
506                     health = "Critical";
507                 }
508             }
509 
510             asyncResp->res.jsonValue["Id"] = acclrtrId;
511             asyncResp->res.jsonValue["Name"] = "Processor";
512             asyncResp->res.jsonValue["Status"]["State"] = state;
513             asyncResp->res.jsonValue["Status"]["Health"] = health;
514             asyncResp->res.jsonValue["ProcessorType"] =
515                 processor::ProcessorType::Accelerator;
516         });
517 }
518 
519 // OperatingConfig D-Bus Types
520 using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>;
521 using BaseSpeedPrioritySettingsProperty =
522     std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>;
523 // uint32_t and size_t may or may not be the same type, requiring a dedup'd
524 // variant
525 
526 /**
527  * Fill out the HighSpeedCoreIDs in a Processor resource from the given
528  * OperatingConfig D-Bus property.
529  *
530  * @param[in,out]   asyncResp           Async HTTP response.
531  * @param[in]       baseSpeedSettings   Full list of base speed priority groups,
532  *                                      to use to determine the list of high
533  *                                      speed cores.
534  */
highSpeedCoreIdsHandler(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const BaseSpeedPrioritySettingsProperty & baseSpeedSettings)535 inline void highSpeedCoreIdsHandler(
536     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
537     const BaseSpeedPrioritySettingsProperty& baseSpeedSettings)
538 {
539     // The D-Bus property does not indicate which bucket is the "high
540     // priority" group, so let's discern that by looking for the one with
541     // highest base frequency.
542     auto highPriorityGroup = baseSpeedSettings.cend();
543     uint32_t highestBaseSpeed = 0;
544     for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend();
545          ++it)
546     {
547         const uint32_t baseFreq = std::get<uint32_t>(*it);
548         if (baseFreq > highestBaseSpeed)
549         {
550             highestBaseSpeed = baseFreq;
551             highPriorityGroup = it;
552         }
553     }
554 
555     nlohmann::json& jsonCoreIds = asyncResp->res.jsonValue["HighSpeedCoreIDs"];
556     jsonCoreIds = nlohmann::json::array();
557 
558     // There may not be any entries in the D-Bus property, so only populate
559     // if there was actually something there.
560     if (highPriorityGroup != baseSpeedSettings.cend())
561     {
562         jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup);
563     }
564 }
565 
566 /**
567  * Fill out OperatingConfig related items in a Processor resource by requesting
568  * data from the given D-Bus object.
569  *
570  * @param[in,out]   asyncResp       Async HTTP response.
571  * @param[in]       cpuId       CPU D-Bus name.
572  * @param[in]       service     D-Bus service to query.
573  * @param[in]       objPath     D-Bus object to query.
574  */
getCpuConfigData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & cpuId,const std::string & service,const std::string & objPath)575 inline void getCpuConfigData(
576     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
577     const std::string& cpuId, const std::string& service,
578     const std::string& objPath)
579 {
580     BMCWEB_LOG_INFO("Getting CPU operating configs for {}", cpuId);
581 
582     // First, GetAll CurrentOperatingConfig properties on the object
583     dbus::utility::getAllProperties(
584         service, objPath,
585         "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig",
586         [asyncResp, cpuId,
587          service](const boost::system::error_code& ec,
588                   const dbus::utility::DBusPropertiesMap& properties) {
589             if (ec)
590             {
591                 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message());
592                 messages::internalError(asyncResp->res);
593                 return;
594             }
595 
596             nlohmann::json& json = asyncResp->res.jsonValue;
597 
598             const sdbusplus::message::object_path* appliedConfig = nullptr;
599             const bool* baseSpeedPriorityEnabled = nullptr;
600 
601             const bool success = sdbusplus::unpackPropertiesNoThrow(
602                 dbus_utils::UnpackErrorPrinter(), properties, "AppliedConfig",
603                 appliedConfig, "BaseSpeedPriorityEnabled",
604                 baseSpeedPriorityEnabled);
605 
606             if (!success)
607             {
608                 messages::internalError(asyncResp->res);
609                 return;
610             }
611 
612             if (appliedConfig != nullptr)
613             {
614                 const std::string& dbusPath = appliedConfig->str;
615                 nlohmann::json::object_t operatingConfig;
616                 operatingConfig["@odata.id"] = boost::urls::format(
617                     "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs",
618                     BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuId);
619                 json["OperatingConfigs"] = std::move(operatingConfig);
620 
621                 // Reuse the D-Bus config object name for the Redfish
622                 // URI
623                 size_t baseNamePos = dbusPath.rfind('/');
624                 if (baseNamePos == std::string::npos ||
625                     baseNamePos == (dbusPath.size() - 1))
626                 {
627                     // If the AppliedConfig was somehow not a valid path,
628                     // skip adding any more properties, since everything
629                     // else is tied to this applied config.
630                     messages::internalError(asyncResp->res);
631                     return;
632                 }
633                 nlohmann::json::object_t appliedOperatingConfig;
634                 appliedOperatingConfig["@odata.id"] = boost::urls::format(
635                     "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}",
636                     BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuId,
637                     dbusPath.substr(baseNamePos + 1));
638                 json["AppliedOperatingConfig"] =
639                     std::move(appliedOperatingConfig);
640 
641                 // Once we found the current applied config, queue another
642                 // request to read the base freq core ids out of that
643                 // config.
644                 dbus::utility::getProperty<BaseSpeedPrioritySettingsProperty>(
645                     service, dbusPath,
646                     "xyz.openbmc_project.Inventory.Item.Cpu."
647                     "OperatingConfig",
648                     "BaseSpeedPrioritySettings",
649                     [asyncResp](const boost::system::error_code& ec2,
650                                 const BaseSpeedPrioritySettingsProperty&
651                                     baseSpeedList) {
652                         if (ec2)
653                         {
654                             BMCWEB_LOG_WARNING("D-Bus Property Get error: {}",
655                                                ec2);
656                             messages::internalError(asyncResp->res);
657                             return;
658                         }
659 
660                         highSpeedCoreIdsHandler(asyncResp, baseSpeedList);
661                     });
662             }
663 
664             if (baseSpeedPriorityEnabled != nullptr)
665             {
666                 json["BaseSpeedPriorityState"] =
667                     *baseSpeedPriorityEnabled ? "Enabled" : "Disabled";
668             }
669         });
670 }
671 
672 /**
673  * @brief Fill out location info of a processor by
674  * requesting data from the given D-Bus object.
675  *
676  * @param[in,out]   asyncResp       Async HTTP response.
677  * @param[in]       service     D-Bus service to query.
678  * @param[in]       objPath     D-Bus object to query.
679  */
getCpuLocationCode(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & service,const std::string & objPath)680 inline void getCpuLocationCode(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
681                                const std::string& service,
682                                const std::string& objPath)
683 {
684     BMCWEB_LOG_DEBUG("Get Cpu Location Data");
685     dbus::utility::getProperty<std::string>(
686         service, objPath,
687         "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
688         [objPath, asyncResp{std::move(asyncResp)}](
689             const boost::system::error_code& ec, const std::string& property) {
690             if (ec)
691             {
692                 BMCWEB_LOG_DEBUG("DBUS response error");
693                 messages::internalError(asyncResp->res);
694                 return;
695             }
696 
697             asyncResp->res
698                 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
699                 property;
700         });
701 }
702 
703 /**
704  * Populate the unique identifier in a Processor resource by requesting data
705  * from the given D-Bus object.
706  *
707  * @param[in,out]   asyncResp   Async HTTP response.
708  * @param[in]       service     D-Bus service to query.
709  * @param[in]       objPath     D-Bus object to query.
710  */
getCpuUniqueId(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & objectPath)711 inline void getCpuUniqueId(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
712                            const std::string& service,
713                            const std::string& objectPath)
714 {
715     BMCWEB_LOG_DEBUG("Get CPU UniqueIdentifier");
716     dbus::utility::getProperty<std::string>(
717         service, objectPath,
718         "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier",
719         "UniqueIdentifier",
720         [asyncResp](const boost::system::error_code& ec,
721                     const std::string& id) {
722             if (ec)
723             {
724                 BMCWEB_LOG_ERROR("Failed to read cpu unique id: {}", ec);
725                 messages::internalError(asyncResp->res);
726                 return;
727             }
728             asyncResp->res
729                 .jsonValue["ProcessorId"]["ProtectedIdentificationNumber"] = id;
730         });
731 }
732 
handleProcessorSubtree(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & processorId,const std::function<void (const std::string & objectPath,const dbus::utility::MapperServiceMap & serviceMap)> & callback,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)733 inline void handleProcessorSubtree(
734     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
735     const std::string& processorId,
736     const std::function<
737         void(const std::string& objectPath,
738              const dbus::utility::MapperServiceMap& serviceMap)>& callback,
739     const boost::system::error_code& ec,
740     const dbus::utility::MapperGetSubTreeResponse& subtree)
741 {
742     if (ec)
743     {
744         BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
745         messages::internalError(asyncResp->res);
746         return;
747     }
748     for (const auto& [objectPath, serviceMap] : subtree)
749     {
750         // Ignore any objects which don't end with our desired cpu name
751         sdbusplus::message::object_path path(objectPath);
752         if (path.filename() == processorId)
753         {
754             // Filter out objects that don't have the CPU-specific
755             // interfaces to make sure we can return 404 on non-CPUs
756             // (e.g. /redfish/../Processors/dimm0)
757             for (const auto& [serviceName, interfaceList] : serviceMap)
758             {
759                 if (std::ranges::find_first_of(interfaceList,
760                                                processorInterfaces) !=
761                     interfaceList.end())
762                 {
763                     // Process the first object which matches cpu name and
764                     // required interfaces, and potentially ignore any other
765                     // matching objects. Assume all interfaces we want to
766                     // process must be on the same object path.
767 
768                     callback(objectPath, serviceMap);
769                     return;
770                 }
771             }
772         }
773     }
774     messages::resourceNotFound(asyncResp->res, "Processor", processorId);
775 }
776 
777 /**
778  * Find the D-Bus object representing the requested Processor, and call the
779  * handler with the results. If matching object is not found, add 404 error to
780  * response and don't call the handler.
781  *
782  * @param[in,out]   asyncResp       Async HTTP response.
783  * @param[in]       processorId     Redfish Processor Id.
784  * @param[in]       callback        Callback to continue processing request upon
785  *                                  successfully finding object.
786  */
getProcessorObject(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & processorId,std::function<void (const std::string & objectPath,const dbus::utility::MapperServiceMap & serviceMap)> && callback)787 inline void getProcessorObject(
788     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
789     const std::string& processorId,
790     std::function<void(const std::string& objectPath,
791                        const dbus::utility::MapperServiceMap& serviceMap)>&&
792         callback)
793 {
794     BMCWEB_LOG_DEBUG("Get available system processor resources.");
795 
796     // GetSubTree on all interfaces which provide info about a Processor
797     constexpr std::array<std::string_view, 9> interfaces = {
798         "xyz.openbmc_project.Common.UUID",
799         "xyz.openbmc_project.Inventory.Decorator.Asset",
800         "xyz.openbmc_project.Inventory.Decorator.Revision",
801         "xyz.openbmc_project.Inventory.Item.Cpu",
802         "xyz.openbmc_project.Inventory.Decorator.LocationCode",
803         "xyz.openbmc_project.Inventory.Item.Accelerator",
804         "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig",
805         "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier",
806         "xyz.openbmc_project.Control.Power.Throttle"};
807     dbus::utility::getSubTree(
808         "/xyz/openbmc_project/inventory", 0, interfaces,
809         [asyncResp, processorId, callback{std::move(callback)}](
810             const boost::system::error_code& ec,
811             const dbus::utility::MapperGetSubTreeResponse& subtree) {
812             handleProcessorSubtree(asyncResp, processorId, callback, ec,
813                                    subtree);
814         });
815 }
816 
getProcessorData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & processorId,const std::string & objectPath,const dbus::utility::MapperServiceMap & serviceMap)817 inline void getProcessorData(
818     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
819     const std::string& processorId, const std::string& objectPath,
820     const dbus::utility::MapperServiceMap& serviceMap)
821 {
822     asyncResp->res.addHeader(
823         boost::beast::http::field::link,
824         "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby");
825     asyncResp->res.jsonValue["@odata.type"] = "#Processor.v1_18_0.Processor";
826     asyncResp->res.jsonValue["@odata.id"] =
827         boost::urls::format("/redfish/v1/Systems/{}/Processors/{}",
828                             BMCWEB_REDFISH_SYSTEM_URI_NAME, processorId);
829 
830     for (const auto& [serviceName, interfaceList] : serviceMap)
831     {
832         for (const auto& interface : interfaceList)
833         {
834             if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
835             {
836                 getCpuAssetData(asyncResp, serviceName, objectPath);
837             }
838             else if (interface ==
839                      "xyz.openbmc_project.Inventory.Decorator.Revision")
840             {
841                 getCpuRevisionData(asyncResp, serviceName, objectPath);
842             }
843             else if (interface == "xyz.openbmc_project.Inventory.Item.Cpu")
844             {
845                 getCpuDataByService(asyncResp, processorId, serviceName,
846                                     objectPath);
847             }
848             else if (interface ==
849                      "xyz.openbmc_project.Inventory.Item.Accelerator")
850             {
851                 getAcceleratorDataByService(asyncResp, processorId, serviceName,
852                                             objectPath);
853             }
854             else if (
855                 interface ==
856                 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig")
857             {
858                 getCpuConfigData(asyncResp, processorId, serviceName,
859                                  objectPath);
860             }
861             else if (interface ==
862                      "xyz.openbmc_project.Inventory.Decorator.LocationCode")
863             {
864                 getCpuLocationCode(asyncResp, serviceName, objectPath);
865             }
866             else if (interface == "xyz.openbmc_project.Common.UUID")
867             {
868                 getProcessorUUID(asyncResp, serviceName, objectPath);
869             }
870             else if (interface ==
871                      "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier")
872             {
873                 getCpuUniqueId(asyncResp, serviceName, objectPath);
874             }
875             else if (interface == "xyz.openbmc_project.Control.Power.Throttle")
876             {
877                 getThrottleProperties(asyncResp, serviceName, objectPath);
878             }
879             else if (interface == "xyz.openbmc_project.Association.Definitions")
880             {
881                 getLocationIndicatorActive(asyncResp, objectPath);
882             }
883         }
884     }
885 }
886 
887 /**
888  * Request all the properties for the given D-Bus object and fill out the
889  * related entries in the Redfish OperatingConfig response.
890  *
891  * @param[in,out]   asyncResp       Async HTTP response.
892  * @param[in]       service     D-Bus service name to query.
893  * @param[in]       objPath     D-Bus object to query.
894  */
getOperatingConfigData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & objPath)895 inline void getOperatingConfigData(
896     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
897     const std::string& service, const std::string& objPath)
898 {
899     dbus::utility::getAllProperties(
900         service, objPath,
901         "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig",
902         [asyncResp](const boost::system::error_code& ec,
903                     const dbus::utility::DBusPropertiesMap& properties) {
904             if (ec)
905             {
906                 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message());
907                 messages::internalError(asyncResp->res);
908                 return;
909             }
910 
911             const size_t* availableCoreCount = nullptr;
912             const uint32_t* baseSpeed = nullptr;
913             const uint32_t* maxJunctionTemperature = nullptr;
914             const uint32_t* maxSpeed = nullptr;
915             const uint32_t* powerLimit = nullptr;
916             const TurboProfileProperty* turboProfile = nullptr;
917             const BaseSpeedPrioritySettingsProperty* baseSpeedPrioritySettings =
918                 nullptr;
919 
920             const bool success = sdbusplus::unpackPropertiesNoThrow(
921                 dbus_utils::UnpackErrorPrinter(), properties,
922                 "AvailableCoreCount", availableCoreCount, "BaseSpeed",
923                 baseSpeed, "MaxJunctionTemperature", maxJunctionTemperature,
924                 "MaxSpeed", maxSpeed, "PowerLimit", powerLimit, "TurboProfile",
925                 turboProfile, "BaseSpeedPrioritySettings",
926                 baseSpeedPrioritySettings);
927 
928             if (!success)
929             {
930                 messages::internalError(asyncResp->res);
931                 return;
932             }
933 
934             nlohmann::json& json = asyncResp->res.jsonValue;
935 
936             if (availableCoreCount != nullptr)
937             {
938                 json["TotalAvailableCoreCount"] = *availableCoreCount;
939             }
940 
941             if (baseSpeed != nullptr)
942             {
943                 json["BaseSpeedMHz"] = *baseSpeed;
944             }
945 
946             if (maxJunctionTemperature != nullptr)
947             {
948                 json["MaxJunctionTemperatureCelsius"] = *maxJunctionTemperature;
949             }
950 
951             if (maxSpeed != nullptr)
952             {
953                 json["MaxSpeedMHz"] = *maxSpeed;
954             }
955 
956             if (powerLimit != nullptr)
957             {
958                 json["TDPWatts"] = *powerLimit;
959             }
960 
961             if (turboProfile != nullptr)
962             {
963                 nlohmann::json& turboArray = json["TurboProfile"];
964                 turboArray = nlohmann::json::array();
965                 for (const auto& [turboSpeed, coreCount] : *turboProfile)
966                 {
967                     nlohmann::json::object_t turbo;
968                     turbo["ActiveCoreCount"] = coreCount;
969                     turbo["MaxSpeedMHz"] = turboSpeed;
970                     turboArray.emplace_back(std::move(turbo));
971                 }
972             }
973 
974             if (baseSpeedPrioritySettings != nullptr)
975             {
976                 nlohmann::json& baseSpeedArray =
977                     json["BaseSpeedPrioritySettings"];
978                 baseSpeedArray = nlohmann::json::array();
979                 for (const auto& [baseSpeedMhz, coreList] :
980                      *baseSpeedPrioritySettings)
981                 {
982                     nlohmann::json::object_t speed;
983                     speed["CoreCount"] = coreList.size();
984                     speed["CoreIDs"] = coreList;
985                     speed["BaseSpeedMHz"] = baseSpeedMhz;
986                     baseSpeedArray.emplace_back(std::move(speed));
987                 }
988             }
989         });
990 }
991 
992 /**
993  * Handle the PATCH operation of the AppliedOperatingConfig property. Do basic
994  * validation of the input data, and then set the D-Bus property.
995  *
996  * @param[in,out]   resp            Async HTTP response.
997  * @param[in]       processorId     Processor's Id.
998  * @param[in]       appliedConfigUri    New property value to apply.
999  * @param[in]       cpuObjectPath   Path of CPU object to modify.
1000  * @param[in]       serviceMap      Service map for CPU object.
1001  */
patchAppliedOperatingConfig(const std::shared_ptr<bmcweb::AsyncResp> & resp,const std::string & processorId,const std::string & appliedConfigUri,const std::string & cpuObjectPath,const dbus::utility::MapperServiceMap & serviceMap)1002 inline void patchAppliedOperatingConfig(
1003     const std::shared_ptr<bmcweb::AsyncResp>& resp,
1004     const std::string& processorId, const std::string& appliedConfigUri,
1005     const std::string& cpuObjectPath,
1006     const dbus::utility::MapperServiceMap& serviceMap)
1007 {
1008     // Check that the property even exists by checking for the interface
1009     const std::string* controlService = nullptr;
1010     for (const auto& [serviceName, interfaceList] : serviceMap)
1011     {
1012         if (std::ranges::find(interfaceList,
1013                               "xyz.openbmc_project.Control.Processor."
1014                               "CurrentOperatingConfig") != interfaceList.end())
1015         {
1016             controlService = &serviceName;
1017             break;
1018         }
1019     }
1020 
1021     if (controlService == nullptr)
1022     {
1023         messages::internalError(resp->res);
1024         return;
1025     }
1026 
1027     // Check that the config URI is a child of the cpu URI being patched.
1028     std::string expectedPrefix(std::format("/redfish/v1/Systems/{}/Processors/",
1029                                            BMCWEB_REDFISH_SYSTEM_URI_NAME));
1030     expectedPrefix += processorId;
1031     expectedPrefix += "/OperatingConfigs/";
1032     if (!appliedConfigUri.starts_with(expectedPrefix) ||
1033         expectedPrefix.size() == appliedConfigUri.size())
1034     {
1035         messages::propertyValueIncorrect(resp->res, "AppliedOperatingConfig",
1036                                          appliedConfigUri);
1037         return;
1038     }
1039 
1040     // Generate the D-Bus path of the OperatingConfig object, by assuming it's a
1041     // direct child of the CPU object.
1042     // Strip the expectedPrefix from the config URI to get the "filename", and
1043     // append to the CPU's path.
1044     std::string configBaseName = appliedConfigUri.substr(expectedPrefix.size());
1045     sdbusplus::message::object_path configPath(cpuObjectPath);
1046     configPath /= configBaseName;
1047 
1048     BMCWEB_LOG_INFO("Setting config to {}", configPath.str);
1049 
1050     // Set the property, with handler to check error responses
1051     setDbusProperty(
1052         resp, "AppliedOperatingConfig", *controlService, cpuObjectPath,
1053         "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig",
1054         "AppliedConfig", configPath);
1055 }
1056 
handleProcessorHead(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string &,const std::string &)1057 inline void handleProcessorHead(
1058     crow::App& app, const crow::Request& req,
1059     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1060     const std::string& /* systemName */, const std::string& /* processorId */)
1061 {
1062     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1063     {
1064         return;
1065     }
1066     asyncResp->res.addHeader(
1067         boost::beast::http::field::link,
1068         "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby");
1069 }
1070 
handleProcessorCollectionHead(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string &)1071 inline void handleProcessorCollectionHead(
1072     crow::App& app, const crow::Request& req,
1073     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1074     const std::string& /* systemName */)
1075 {
1076     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1077     {
1078         return;
1079     }
1080     asyncResp->res.addHeader(
1081         boost::beast::http::field::link,
1082         "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby");
1083 }
1084 
handleProcessorGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & processorId)1085 inline void handleProcessorGet(
1086     App& app, const crow::Request& req,
1087     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1088     const std::string& systemName, const std::string& processorId)
1089 {
1090     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1091     {
1092         return;
1093     }
1094     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1095     {
1096         // Option currently returns no systems.  TBD
1097         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1098                                    systemName);
1099         return;
1100     }
1101     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1102     {
1103         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1104                                    systemName);
1105         return;
1106     }
1107 
1108     getProcessorObject(
1109         asyncResp, processorId,
1110         std::bind_front(getProcessorData, asyncResp, processorId));
1111 }
1112 
doPatchProcessor(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & processorId,const std::optional<std::string> & appliedConfigUri,std::optional<bool> locationIndicatorActive,const std::string & objectPath,const dbus::utility::MapperServiceMap & serviceMap)1113 inline void doPatchProcessor(
1114     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1115     const std::string& processorId,
1116     const std::optional<std::string>& appliedConfigUri,
1117     std::optional<bool> locationIndicatorActive, const std::string& objectPath,
1118     const dbus::utility::MapperServiceMap& serviceMap)
1119 {
1120     if (appliedConfigUri)
1121     {
1122         patchAppliedOperatingConfig(asyncResp, processorId, *appliedConfigUri,
1123                                     objectPath, serviceMap);
1124     }
1125 
1126     if (locationIndicatorActive)
1127     {
1128         // Utility function handles reporting errors
1129         setLocationIndicatorActive(asyncResp, objectPath,
1130                                    *locationIndicatorActive);
1131     }
1132 }
1133 
handleProcessorPatch(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & processorId)1134 inline void handleProcessorPatch(
1135     App& app, const crow::Request& req,
1136     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1137     const std::string& systemName, const std::string& processorId)
1138 {
1139     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1140     {
1141         return;
1142     }
1143     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1144     {
1145         // Option currently returns no systems.  TBD
1146         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1147                                    systemName);
1148         return;
1149     }
1150     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1151     {
1152         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1153                                    systemName);
1154         return;
1155     }
1156 
1157     std::optional<std::string> appliedConfigUri;
1158     std::optional<bool> locationIndicatorActive;
1159     if (!json_util::readJsonPatch(
1160             req, asyncResp->res,                                  //
1161             "AppliedOperatingConfig/@odata.id", appliedConfigUri, //
1162             "LocationIndicatorActive", locationIndicatorActive    //
1163             ))
1164     {
1165         return;
1166     }
1167 
1168     // Check for 404 and find matching D-Bus object, then run
1169     // property patch handlers if that all succeeds.
1170     getProcessorObject(
1171         asyncResp, processorId,
1172         std::bind_front(doPatchProcessor, asyncResp, processorId,
1173                         appliedConfigUri, locationIndicatorActive));
1174 }
1175 
requestRoutesOperatingConfigCollection(App & app)1176 inline void requestRoutesOperatingConfigCollection(App& app)
1177 {
1178     BMCWEB_ROUTE(app,
1179                  "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/")
1180         .privileges(redfish::privileges::getOperatingConfigCollection)
1181         .methods(
1182             boost::beast::http::verb::
1183                 get)([&app](const crow::Request& req,
1184                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1185                             const std::string& systemName,
1186                             const std::string& cpuName) {
1187             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1188             {
1189                 return;
1190             }
1191 
1192             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1193             {
1194                 // Option currently returns no systems.  TBD
1195                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1196                                            systemName);
1197                 return;
1198             }
1199 
1200             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1201             {
1202                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1203                                            systemName);
1204                 return;
1205             }
1206             asyncResp->res.jsonValue["@odata.type"] =
1207                 "#OperatingConfigCollection.OperatingConfigCollection";
1208             asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1209                 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs",
1210                 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName);
1211             asyncResp->res.jsonValue["Name"] = "Operating Config Collection";
1212 
1213             // First find the matching CPU object so we know how to
1214             // constrain our search for related Config objects.
1215             const std::array<std::string_view, 1> interfaces = {
1216                 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"};
1217             dbus::utility::getSubTreePaths(
1218                 "/xyz/openbmc_project/inventory", 0, interfaces,
1219                 [asyncResp,
1220                  cpuName](const boost::system::error_code& ec,
1221                           const dbus::utility::MapperGetSubTreePathsResponse&
1222                               objects) {
1223                     if (ec)
1224                     {
1225                         BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec,
1226                                            ec.message());
1227                         messages::internalError(asyncResp->res);
1228                         return;
1229                     }
1230 
1231                     for (const std::string& object : objects)
1232                     {
1233                         if (!object.ends_with(cpuName))
1234                         {
1235                             continue;
1236                         }
1237 
1238                         // Not expected that there will be multiple matching
1239                         // CPU objects, but if there are just use the first
1240                         // one.
1241 
1242                         // Use the common search routine to construct the
1243                         // Collection of all Config objects under this CPU.
1244                         constexpr std::array<std::string_view, 1> interface{
1245                             "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"};
1246                         collection_util::getCollectionMembers(
1247                             asyncResp,
1248                             boost::urls::format(
1249                                 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs",
1250                                 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName),
1251                             interface, object);
1252                         return;
1253                     }
1254                 });
1255         });
1256 }
1257 
requestRoutesOperatingConfig(App & app)1258 inline void requestRoutesOperatingConfig(App& app)
1259 {
1260     BMCWEB_ROUTE(
1261         app,
1262         "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/<str>/")
1263         .privileges(redfish::privileges::getOperatingConfig)
1264         .methods(
1265             boost::beast::http::verb::
1266                 get)([&app](const crow::Request& req,
1267                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1268                             const std::string& systemName,
1269                             const std::string& cpuName,
1270                             const std::string& configName) {
1271             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1272             {
1273                 return;
1274             }
1275             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1276             {
1277                 // Option currently returns no systems.  TBD
1278                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1279                                            systemName);
1280                 return;
1281             }
1282 
1283             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1284             {
1285                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1286                                            systemName);
1287                 return;
1288             }
1289             // Ask for all objects implementing OperatingConfig so we can search
1290             // for one with a matching name
1291             constexpr std::array<std::string_view, 1> interfaces = {
1292                 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"};
1293             dbus::utility::getSubTree(
1294                 "/xyz/openbmc_project/inventory", 0, interfaces,
1295                 [asyncResp, cpuName, configName](
1296                     const boost::system::error_code& ec,
1297                     const dbus::utility::MapperGetSubTreeResponse& subtree) {
1298                     if (ec)
1299                     {
1300                         BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec,
1301                                            ec.message());
1302                         messages::internalError(asyncResp->res);
1303                         return;
1304                     }
1305                     const std::string expectedEnding =
1306                         cpuName + '/' + configName;
1307                     for (const auto& [objectPath, serviceMap] : subtree)
1308                     {
1309                         // Ignore any configs without matching cpuX/configY
1310                         if (!objectPath.ends_with(expectedEnding) ||
1311                             serviceMap.empty())
1312                         {
1313                             continue;
1314                         }
1315 
1316                         nlohmann::json& json = asyncResp->res.jsonValue;
1317                         json["@odata.type"] =
1318                             "#OperatingConfig.v1_0_0.OperatingConfig";
1319                         json["@odata.id"] = boost::urls::format(
1320                             "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}",
1321                             BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName,
1322                             configName);
1323                         json["Name"] = "Processor Profile";
1324                         json["Id"] = configName;
1325 
1326                         // Just use the first implementation of the object - not
1327                         // expected that there would be multiple matching
1328                         // services
1329                         getOperatingConfigData(
1330                             asyncResp, serviceMap.begin()->first, objectPath);
1331                         return;
1332                     }
1333                     messages::resourceNotFound(asyncResp->res,
1334                                                "OperatingConfig", configName);
1335                 });
1336         });
1337 }
1338 
requestRoutesProcessorCollection(App & app)1339 inline void requestRoutesProcessorCollection(App& app)
1340 {
1341     /**
1342      * Functions triggers appropriate requests on DBus
1343      */
1344     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/")
1345         .privileges(redfish::privileges::headProcessorCollection)
1346         .methods(boost::beast::http::verb::head)(
1347             std::bind_front(handleProcessorCollectionHead, std::ref(app)));
1348 
1349     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/")
1350         .privileges(redfish::privileges::getProcessorCollection)
1351         .methods(
1352             boost::beast::http::verb::
1353                 get)([&app](const crow::Request& req,
1354                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1355                             const std::string& systemName) {
1356             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1357             {
1358                 return;
1359             }
1360             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1361             {
1362                 // Option currently returns no systems.  TBD
1363                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1364                                            systemName);
1365                 return;
1366             }
1367 
1368             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1369             {
1370                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1371                                            systemName);
1372                 return;
1373             }
1374 
1375             asyncResp->res.addHeader(
1376                 boost::beast::http::field::link,
1377                 "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby");
1378 
1379             asyncResp->res.jsonValue["@odata.type"] =
1380                 "#ProcessorCollection.ProcessorCollection";
1381             asyncResp->res.jsonValue["Name"] = "Processor Collection";
1382 
1383             asyncResp->res.jsonValue["@odata.id"] =
1384                 std::format("/redfish/v1/Systems/{}/Processors",
1385                             BMCWEB_REDFISH_SYSTEM_URI_NAME);
1386 
1387             collection_util::getCollectionMembers(
1388                 asyncResp,
1389                 boost::urls::format("/redfish/v1/Systems/{}/Processors",
1390                                     BMCWEB_REDFISH_SYSTEM_URI_NAME),
1391                 processorInterfaces, "/xyz/openbmc_project/inventory");
1392         });
1393 }
1394 
requestRoutesProcessor(App & app)1395 inline void requestRoutesProcessor(App& app)
1396 {
1397     /**
1398      * Functions triggers appropriate requests on DBus
1399      */
1400 
1401     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/")
1402         .privileges(redfish::privileges::headProcessor)
1403         .methods(boost::beast::http::verb::head)(
1404             std::bind_front(handleProcessorHead, std::ref(app)));
1405 
1406     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/")
1407         .privileges(redfish::privileges::getProcessor)
1408         .methods(boost::beast::http::verb::get)(
1409             std::bind_front(handleProcessorGet, std::ref(app)));
1410 
1411     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/")
1412         .privileges(redfish::privileges::patchProcessor)
1413         .methods(boost::beast::http::verb::patch)(
1414             std::bind_front(handleProcessorPatch, std::ref(app)));
1415 }
1416 
1417 } // namespace redfish
1418