xref: /openbmc/bmcweb/features/redfish/lib/processor.hpp (revision 4d0c0c422007722f94c7cd3a2c66cec4ab2a03e3)
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  */
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 
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 
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  */
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 
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 
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 
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                 else if (manufacturer->find("Ampere") != std::string::npos)
415                 {
416                     asyncResp->res.jsonValue["ProcessorArchitecture"] = "ARM";
417                     asyncResp->res.jsonValue["InstructionSet"] = "ARM-A64";
418                 }
419             }
420 
421             if (partNumber != nullptr)
422             {
423                 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
424             }
425 
426             if (sparePartNumber != nullptr && !sparePartNumber->empty())
427             {
428                 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
429             }
430         });
431 }
432 
433 inline void getCpuRevisionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
434                                const std::string& service,
435                                const std::string& objPath)
436 {
437     BMCWEB_LOG_DEBUG("Get Cpu Revision Data");
438     dbus::utility::getAllProperties(
439         service, objPath, "xyz.openbmc_project.Inventory.Decorator.Revision",
440         [objPath, asyncResp{std::move(asyncResp)}](
441             const boost::system::error_code& ec,
442             const dbus::utility::DBusPropertiesMap& properties) {
443             if (ec)
444             {
445                 BMCWEB_LOG_DEBUG("DBUS response error");
446                 messages::internalError(asyncResp->res);
447                 return;
448             }
449 
450             const std::string* version = nullptr;
451 
452             const bool success = sdbusplus::unpackPropertiesNoThrow(
453                 dbus_utils::UnpackErrorPrinter(), properties, "Version",
454                 version);
455 
456             if (!success)
457             {
458                 messages::internalError(asyncResp->res);
459                 return;
460             }
461 
462             if (version != nullptr)
463             {
464                 asyncResp->res.jsonValue["Version"] = *version;
465             }
466         });
467 }
468 
469 inline void getAcceleratorDataByService(
470     std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& acclrtrId,
471     const std::string& service, const std::string& objPath)
472 {
473     BMCWEB_LOG_DEBUG("Get available system Accelerator resources by service.");
474     dbus::utility::getAllProperties(
475         service, objPath, "",
476         [acclrtrId, asyncResp{std::move(asyncResp)}](
477             const boost::system::error_code& ec,
478             const dbus::utility::DBusPropertiesMap& properties) {
479             if (ec)
480             {
481                 BMCWEB_LOG_DEBUG("DBUS response error");
482                 messages::internalError(asyncResp->res);
483                 return;
484             }
485 
486             const bool* functional = nullptr;
487             const bool* present = nullptr;
488 
489             const bool success = sdbusplus::unpackPropertiesNoThrow(
490                 dbus_utils::UnpackErrorPrinter(), properties, "Functional",
491                 functional, "Present", present);
492 
493             if (!success)
494             {
495                 messages::internalError(asyncResp->res);
496                 return;
497             }
498 
499             std::string state = "Enabled";
500             std::string health = "OK";
501 
502             if (present != nullptr && !*present)
503             {
504                 state = "Absent";
505             }
506 
507             if (functional != nullptr && !*functional)
508             {
509                 if (state == "Enabled")
510                 {
511                     health = "Critical";
512                 }
513             }
514 
515             asyncResp->res.jsonValue["Id"] = acclrtrId;
516             asyncResp->res.jsonValue["Name"] = "Processor";
517             asyncResp->res.jsonValue["Status"]["State"] = state;
518             asyncResp->res.jsonValue["Status"]["Health"] = health;
519             asyncResp->res.jsonValue["ProcessorType"] =
520                 processor::ProcessorType::Accelerator;
521         });
522 }
523 
524 // OperatingConfig D-Bus Types
525 using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>;
526 using BaseSpeedPrioritySettingsProperty =
527     std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>;
528 // uint32_t and size_t may or may not be the same type, requiring a dedup'd
529 // variant
530 
531 /**
532  * Fill out the HighSpeedCoreIDs in a Processor resource from the given
533  * OperatingConfig D-Bus property.
534  *
535  * @param[in,out]   asyncResp           Async HTTP response.
536  * @param[in]       baseSpeedSettings   Full list of base speed priority groups,
537  *                                      to use to determine the list of high
538  *                                      speed cores.
539  */
540 inline void highSpeedCoreIdsHandler(
541     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
542     const BaseSpeedPrioritySettingsProperty& baseSpeedSettings)
543 {
544     // The D-Bus property does not indicate which bucket is the "high
545     // priority" group, so let's discern that by looking for the one with
546     // highest base frequency.
547     auto highPriorityGroup = baseSpeedSettings.cend();
548     uint32_t highestBaseSpeed = 0;
549     for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend();
550          ++it)
551     {
552         const uint32_t baseFreq = std::get<uint32_t>(*it);
553         if (baseFreq > highestBaseSpeed)
554         {
555             highestBaseSpeed = baseFreq;
556             highPriorityGroup = it;
557         }
558     }
559 
560     nlohmann::json& jsonCoreIds = asyncResp->res.jsonValue["HighSpeedCoreIDs"];
561     jsonCoreIds = nlohmann::json::array();
562 
563     // There may not be any entries in the D-Bus property, so only populate
564     // if there was actually something there.
565     if (highPriorityGroup != baseSpeedSettings.cend())
566     {
567         jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup);
568     }
569 }
570 
571 /**
572  * Fill out OperatingConfig related items in a Processor resource by requesting
573  * data from the given D-Bus object.
574  *
575  * @param[in,out]   asyncResp       Async HTTP response.
576  * @param[in]       cpuId       CPU D-Bus name.
577  * @param[in]       service     D-Bus service to query.
578  * @param[in]       objPath     D-Bus object to query.
579  */
580 inline void getCpuConfigData(
581     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
582     const std::string& cpuId, const std::string& service,
583     const std::string& objPath)
584 {
585     BMCWEB_LOG_INFO("Getting CPU operating configs for {}", cpuId);
586 
587     // First, GetAll CurrentOperatingConfig properties on the object
588     dbus::utility::getAllProperties(
589         service, objPath,
590         "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig",
591         [asyncResp, cpuId,
592          service](const boost::system::error_code& ec,
593                   const dbus::utility::DBusPropertiesMap& properties) {
594             if (ec)
595             {
596                 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message());
597                 messages::internalError(asyncResp->res);
598                 return;
599             }
600 
601             nlohmann::json& json = asyncResp->res.jsonValue;
602 
603             const sdbusplus::message::object_path* appliedConfig = nullptr;
604             const bool* baseSpeedPriorityEnabled = nullptr;
605 
606             const bool success = sdbusplus::unpackPropertiesNoThrow(
607                 dbus_utils::UnpackErrorPrinter(), properties, "AppliedConfig",
608                 appliedConfig, "BaseSpeedPriorityEnabled",
609                 baseSpeedPriorityEnabled);
610 
611             if (!success)
612             {
613                 messages::internalError(asyncResp->res);
614                 return;
615             }
616 
617             if (appliedConfig != nullptr)
618             {
619                 const std::string& dbusPath = appliedConfig->str;
620                 nlohmann::json::object_t operatingConfig;
621                 operatingConfig["@odata.id"] = boost::urls::format(
622                     "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs",
623                     BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuId);
624                 json["OperatingConfigs"] = std::move(operatingConfig);
625 
626                 // Reuse the D-Bus config object name for the Redfish
627                 // URI
628                 size_t baseNamePos = dbusPath.rfind('/');
629                 if (baseNamePos == std::string::npos ||
630                     baseNamePos == (dbusPath.size() - 1))
631                 {
632                     // If the AppliedConfig was somehow not a valid path,
633                     // skip adding any more properties, since everything
634                     // else is tied to this applied config.
635                     messages::internalError(asyncResp->res);
636                     return;
637                 }
638                 nlohmann::json::object_t appliedOperatingConfig;
639                 appliedOperatingConfig["@odata.id"] = boost::urls::format(
640                     "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}",
641                     BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuId,
642                     dbusPath.substr(baseNamePos + 1));
643                 json["AppliedOperatingConfig"] =
644                     std::move(appliedOperatingConfig);
645 
646                 // Once we found the current applied config, queue another
647                 // request to read the base freq core ids out of that
648                 // config.
649                 dbus::utility::getProperty<BaseSpeedPrioritySettingsProperty>(
650                     service, dbusPath,
651                     "xyz.openbmc_project.Inventory.Item.Cpu."
652                     "OperatingConfig",
653                     "BaseSpeedPrioritySettings",
654                     [asyncResp](const boost::system::error_code& ec2,
655                                 const BaseSpeedPrioritySettingsProperty&
656                                     baseSpeedList) {
657                         if (ec2)
658                         {
659                             BMCWEB_LOG_WARNING("D-Bus Property Get error: {}",
660                                                ec2);
661                             messages::internalError(asyncResp->res);
662                             return;
663                         }
664 
665                         highSpeedCoreIdsHandler(asyncResp, baseSpeedList);
666                     });
667             }
668 
669             if (baseSpeedPriorityEnabled != nullptr)
670             {
671                 json["BaseSpeedPriorityState"] =
672                     *baseSpeedPriorityEnabled ? "Enabled" : "Disabled";
673             }
674         });
675 }
676 
677 /**
678  * @brief Fill out location info of a processor by
679  * requesting data from the given D-Bus object.
680  *
681  * @param[in,out]   asyncResp       Async HTTP response.
682  * @param[in]       service     D-Bus service to query.
683  * @param[in]       objPath     D-Bus object to query.
684  */
685 inline void getCpuLocationCode(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
686                                const std::string& service,
687                                const std::string& objPath)
688 {
689     BMCWEB_LOG_DEBUG("Get Cpu Location Data");
690     dbus::utility::getProperty<std::string>(
691         service, objPath,
692         "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
693         [objPath, asyncResp{std::move(asyncResp)}](
694             const boost::system::error_code& ec, const std::string& property) {
695             if (ec)
696             {
697                 BMCWEB_LOG_DEBUG("DBUS response error");
698                 messages::internalError(asyncResp->res);
699                 return;
700             }
701 
702             asyncResp->res
703                 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
704                 property;
705         });
706 }
707 
708 /**
709  * Populate the unique identifier in a Processor resource by requesting data
710  * from the given D-Bus object.
711  *
712  * @param[in,out]   asyncResp   Async HTTP response.
713  * @param[in]       service     D-Bus service to query.
714  * @param[in]       objPath     D-Bus object to query.
715  */
716 inline void getCpuUniqueId(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
717                            const std::string& service,
718                            const std::string& objectPath)
719 {
720     BMCWEB_LOG_DEBUG("Get CPU UniqueIdentifier");
721     dbus::utility::getProperty<std::string>(
722         service, objectPath,
723         "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier",
724         "UniqueIdentifier",
725         [asyncResp](const boost::system::error_code& ec,
726                     const std::string& id) {
727             if (ec)
728             {
729                 BMCWEB_LOG_ERROR("Failed to read cpu unique id: {}", ec);
730                 messages::internalError(asyncResp->res);
731                 return;
732             }
733             asyncResp->res
734                 .jsonValue["ProcessorId"]["ProtectedIdentificationNumber"] = id;
735         });
736 }
737 
738 inline void handleProcessorSubtree(
739     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
740     const std::string& processorId,
741     const std::function<
742         void(const std::string& objectPath,
743              const dbus::utility::MapperServiceMap& serviceMap)>& callback,
744     const boost::system::error_code& ec,
745     const dbus::utility::MapperGetSubTreeResponse& subtree)
746 {
747     if (ec)
748     {
749         BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
750         messages::internalError(asyncResp->res);
751         return;
752     }
753     for (const auto& [objectPath, serviceMap] : subtree)
754     {
755         // Ignore any objects which don't end with our desired cpu name
756         sdbusplus::message::object_path path(objectPath);
757         if (path.filename() == processorId)
758         {
759             // Filter out objects that don't have the CPU-specific
760             // interfaces to make sure we can return 404 on non-CPUs
761             // (e.g. /redfish/../Processors/dimm0)
762             for (const auto& [serviceName, interfaceList] : serviceMap)
763             {
764                 if (std::ranges::find_first_of(interfaceList,
765                                                processorInterfaces) !=
766                     interfaceList.end())
767                 {
768                     // Process the first object which matches cpu name and
769                     // required interfaces, and potentially ignore any other
770                     // matching objects. Assume all interfaces we want to
771                     // process must be on the same object path.
772 
773                     callback(objectPath, serviceMap);
774                     return;
775                 }
776             }
777         }
778     }
779     messages::resourceNotFound(asyncResp->res, "Processor", processorId);
780 }
781 
782 /**
783  * Find the D-Bus object representing the requested Processor, and call the
784  * handler with the results. If matching object is not found, add 404 error to
785  * response and don't call the handler.
786  *
787  * @param[in,out]   asyncResp       Async HTTP response.
788  * @param[in]       processorId     Redfish Processor Id.
789  * @param[in]       callback        Callback to continue processing request upon
790  *                                  successfully finding object.
791  */
792 inline void getProcessorObject(
793     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
794     const std::string& processorId,
795     std::function<void(const std::string& objectPath,
796                        const dbus::utility::MapperServiceMap& serviceMap)>&&
797         callback)
798 {
799     BMCWEB_LOG_DEBUG("Get available system processor resources.");
800 
801     // GetSubTree on all interfaces which provide info about a Processor
802     constexpr std::array<std::string_view, 9> interfaces = {
803         "xyz.openbmc_project.Common.UUID",
804         "xyz.openbmc_project.Inventory.Decorator.Asset",
805         "xyz.openbmc_project.Inventory.Decorator.Revision",
806         "xyz.openbmc_project.Inventory.Item.Cpu",
807         "xyz.openbmc_project.Inventory.Decorator.LocationCode",
808         "xyz.openbmc_project.Inventory.Item.Accelerator",
809         "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig",
810         "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier",
811         "xyz.openbmc_project.Control.Power.Throttle"};
812     dbus::utility::getSubTree(
813         "/xyz/openbmc_project/inventory", 0, interfaces,
814         [asyncResp, processorId, callback{std::move(callback)}](
815             const boost::system::error_code& ec,
816             const dbus::utility::MapperGetSubTreeResponse& subtree) {
817             handleProcessorSubtree(asyncResp, processorId, callback, ec,
818                                    subtree);
819         });
820 }
821 
822 inline void getProcessorData(
823     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
824     const std::string& processorId, const std::string& objectPath,
825     const dbus::utility::MapperServiceMap& serviceMap)
826 {
827     asyncResp->res.addHeader(
828         boost::beast::http::field::link,
829         "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby");
830     asyncResp->res.jsonValue["@odata.type"] = "#Processor.v1_18_0.Processor";
831     asyncResp->res.jsonValue["@odata.id"] =
832         boost::urls::format("/redfish/v1/Systems/{}/Processors/{}",
833                             BMCWEB_REDFISH_SYSTEM_URI_NAME, processorId);
834 
835     for (const auto& [serviceName, interfaceList] : serviceMap)
836     {
837         for (const auto& interface : interfaceList)
838         {
839             if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
840             {
841                 getCpuAssetData(asyncResp, serviceName, objectPath);
842             }
843             else if (interface ==
844                      "xyz.openbmc_project.Inventory.Decorator.Revision")
845             {
846                 getCpuRevisionData(asyncResp, serviceName, objectPath);
847             }
848             else if (interface == "xyz.openbmc_project.Inventory.Item.Cpu")
849             {
850                 getCpuDataByService(asyncResp, processorId, serviceName,
851                                     objectPath);
852             }
853             else if (interface ==
854                      "xyz.openbmc_project.Inventory.Item.Accelerator")
855             {
856                 getAcceleratorDataByService(asyncResp, processorId, serviceName,
857                                             objectPath);
858             }
859             else if (
860                 interface ==
861                 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig")
862             {
863                 getCpuConfigData(asyncResp, processorId, serviceName,
864                                  objectPath);
865             }
866             else if (interface ==
867                      "xyz.openbmc_project.Inventory.Decorator.LocationCode")
868             {
869                 getCpuLocationCode(asyncResp, serviceName, objectPath);
870             }
871             else if (interface == "xyz.openbmc_project.Common.UUID")
872             {
873                 getProcessorUUID(asyncResp, serviceName, objectPath);
874             }
875             else if (interface ==
876                      "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier")
877             {
878                 getCpuUniqueId(asyncResp, serviceName, objectPath);
879             }
880             else if (interface == "xyz.openbmc_project.Control.Power.Throttle")
881             {
882                 getThrottleProperties(asyncResp, serviceName, objectPath);
883             }
884             else if (interface == "xyz.openbmc_project.Association.Definitions")
885             {
886                 getLocationIndicatorActive(asyncResp, objectPath);
887             }
888         }
889     }
890 }
891 
892 /**
893  * Request all the properties for the given D-Bus object and fill out the
894  * related entries in the Redfish OperatingConfig response.
895  *
896  * @param[in,out]   asyncResp       Async HTTP response.
897  * @param[in]       service     D-Bus service name to query.
898  * @param[in]       objPath     D-Bus object to query.
899  */
900 inline void getOperatingConfigData(
901     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
902     const std::string& service, const std::string& objPath)
903 {
904     dbus::utility::getAllProperties(
905         service, objPath,
906         "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig",
907         [asyncResp](const boost::system::error_code& ec,
908                     const dbus::utility::DBusPropertiesMap& properties) {
909             if (ec)
910             {
911                 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message());
912                 messages::internalError(asyncResp->res);
913                 return;
914             }
915 
916             const size_t* availableCoreCount = nullptr;
917             const uint32_t* baseSpeed = nullptr;
918             const uint32_t* maxJunctionTemperature = nullptr;
919             const uint32_t* maxSpeed = nullptr;
920             const uint32_t* powerLimit = nullptr;
921             const TurboProfileProperty* turboProfile = nullptr;
922             const BaseSpeedPrioritySettingsProperty* baseSpeedPrioritySettings =
923                 nullptr;
924 
925             const bool success = sdbusplus::unpackPropertiesNoThrow(
926                 dbus_utils::UnpackErrorPrinter(), properties,
927                 "AvailableCoreCount", availableCoreCount, "BaseSpeed",
928                 baseSpeed, "MaxJunctionTemperature", maxJunctionTemperature,
929                 "MaxSpeed", maxSpeed, "PowerLimit", powerLimit, "TurboProfile",
930                 turboProfile, "BaseSpeedPrioritySettings",
931                 baseSpeedPrioritySettings);
932 
933             if (!success)
934             {
935                 messages::internalError(asyncResp->res);
936                 return;
937             }
938 
939             nlohmann::json& json = asyncResp->res.jsonValue;
940 
941             if (availableCoreCount != nullptr)
942             {
943                 json["TotalAvailableCoreCount"] = *availableCoreCount;
944             }
945 
946             if (baseSpeed != nullptr)
947             {
948                 json["BaseSpeedMHz"] = *baseSpeed;
949             }
950 
951             if (maxJunctionTemperature != nullptr)
952             {
953                 json["MaxJunctionTemperatureCelsius"] = *maxJunctionTemperature;
954             }
955 
956             if (maxSpeed != nullptr)
957             {
958                 json["MaxSpeedMHz"] = *maxSpeed;
959             }
960 
961             if (powerLimit != nullptr)
962             {
963                 json["TDPWatts"] = *powerLimit;
964             }
965 
966             if (turboProfile != nullptr)
967             {
968                 nlohmann::json& turboArray = json["TurboProfile"];
969                 turboArray = nlohmann::json::array();
970                 for (const auto& [turboSpeed, coreCount] : *turboProfile)
971                 {
972                     nlohmann::json::object_t turbo;
973                     turbo["ActiveCoreCount"] = coreCount;
974                     turbo["MaxSpeedMHz"] = turboSpeed;
975                     turboArray.emplace_back(std::move(turbo));
976                 }
977             }
978 
979             if (baseSpeedPrioritySettings != nullptr)
980             {
981                 nlohmann::json& baseSpeedArray =
982                     json["BaseSpeedPrioritySettings"];
983                 baseSpeedArray = nlohmann::json::array();
984                 for (const auto& [baseSpeedMhz, coreList] :
985                      *baseSpeedPrioritySettings)
986                 {
987                     nlohmann::json::object_t speed;
988                     speed["CoreCount"] = coreList.size();
989                     speed["CoreIDs"] = coreList;
990                     speed["BaseSpeedMHz"] = baseSpeedMhz;
991                     baseSpeedArray.emplace_back(std::move(speed));
992                 }
993             }
994         });
995 }
996 
997 /**
998  * Handle the PATCH operation of the AppliedOperatingConfig property. Do basic
999  * validation of the input data, and then set the D-Bus property.
1000  *
1001  * @param[in,out]   resp            Async HTTP response.
1002  * @param[in]       processorId     Processor's Id.
1003  * @param[in]       appliedConfigUri    New property value to apply.
1004  * @param[in]       cpuObjectPath   Path of CPU object to modify.
1005  * @param[in]       serviceMap      Service map for CPU object.
1006  */
1007 inline void patchAppliedOperatingConfig(
1008     const std::shared_ptr<bmcweb::AsyncResp>& resp,
1009     const std::string& processorId, const std::string& appliedConfigUri,
1010     const std::string& cpuObjectPath,
1011     const dbus::utility::MapperServiceMap& serviceMap)
1012 {
1013     // Check that the property even exists by checking for the interface
1014     const std::string* controlService = nullptr;
1015     for (const auto& [serviceName, interfaceList] : serviceMap)
1016     {
1017         if (std::ranges::find(interfaceList,
1018                               "xyz.openbmc_project.Control.Processor."
1019                               "CurrentOperatingConfig") != interfaceList.end())
1020         {
1021             controlService = &serviceName;
1022             break;
1023         }
1024     }
1025 
1026     if (controlService == nullptr)
1027     {
1028         messages::internalError(resp->res);
1029         return;
1030     }
1031 
1032     // Check that the config URI is a child of the cpu URI being patched.
1033     std::string expectedPrefix(std::format("/redfish/v1/Systems/{}/Processors/",
1034                                            BMCWEB_REDFISH_SYSTEM_URI_NAME));
1035     expectedPrefix += processorId;
1036     expectedPrefix += "/OperatingConfigs/";
1037     if (!appliedConfigUri.starts_with(expectedPrefix) ||
1038         expectedPrefix.size() == appliedConfigUri.size())
1039     {
1040         messages::propertyValueIncorrect(resp->res, "AppliedOperatingConfig",
1041                                          appliedConfigUri);
1042         return;
1043     }
1044 
1045     // Generate the D-Bus path of the OperatingConfig object, by assuming it's a
1046     // direct child of the CPU object.
1047     // Strip the expectedPrefix from the config URI to get the "filename", and
1048     // append to the CPU's path.
1049     std::string configBaseName = appliedConfigUri.substr(expectedPrefix.size());
1050     sdbusplus::message::object_path configPath(cpuObjectPath);
1051     configPath /= configBaseName;
1052 
1053     BMCWEB_LOG_INFO("Setting config to {}", configPath.str);
1054 
1055     // Set the property, with handler to check error responses
1056     setDbusProperty(
1057         resp, "AppliedOperatingConfig", *controlService, cpuObjectPath,
1058         "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig",
1059         "AppliedConfig", configPath);
1060 }
1061 
1062 inline void handleProcessorHead(
1063     crow::App& app, const crow::Request& req,
1064     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1065     const std::string& /* systemName */, const std::string& /* processorId */)
1066 {
1067     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1068     {
1069         return;
1070     }
1071     asyncResp->res.addHeader(
1072         boost::beast::http::field::link,
1073         "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby");
1074 }
1075 
1076 inline void handleProcessorCollectionHead(
1077     crow::App& app, const crow::Request& req,
1078     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1079     const std::string& /* systemName */)
1080 {
1081     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1082     {
1083         return;
1084     }
1085     asyncResp->res.addHeader(
1086         boost::beast::http::field::link,
1087         "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby");
1088 }
1089 
1090 inline void handleProcessorGet(
1091     App& app, const crow::Request& req,
1092     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1093     const std::string& systemName, const std::string& processorId)
1094 {
1095     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1096     {
1097         return;
1098     }
1099     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1100     {
1101         // Option currently returns no systems.  TBD
1102         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1103                                    systemName);
1104         return;
1105     }
1106     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1107     {
1108         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1109                                    systemName);
1110         return;
1111     }
1112 
1113     getProcessorObject(
1114         asyncResp, processorId,
1115         std::bind_front(getProcessorData, asyncResp, processorId));
1116 }
1117 
1118 inline void doPatchProcessor(
1119     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1120     const std::string& processorId,
1121     const std::optional<std::string>& appliedConfigUri,
1122     std::optional<bool> locationIndicatorActive, const std::string& objectPath,
1123     const dbus::utility::MapperServiceMap& serviceMap)
1124 {
1125     if (appliedConfigUri)
1126     {
1127         patchAppliedOperatingConfig(asyncResp, processorId, *appliedConfigUri,
1128                                     objectPath, serviceMap);
1129     }
1130 
1131     if (locationIndicatorActive)
1132     {
1133         // Utility function handles reporting errors
1134         setLocationIndicatorActive(asyncResp, objectPath,
1135                                    *locationIndicatorActive);
1136     }
1137 }
1138 
1139 inline void handleProcessorPatch(
1140     App& app, const crow::Request& req,
1141     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1142     const std::string& systemName, const std::string& processorId)
1143 {
1144     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1145     {
1146         return;
1147     }
1148     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1149     {
1150         // Option currently returns no systems.  TBD
1151         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1152                                    systemName);
1153         return;
1154     }
1155     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1156     {
1157         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1158                                    systemName);
1159         return;
1160     }
1161 
1162     std::optional<std::string> appliedConfigUri;
1163     std::optional<bool> locationIndicatorActive;
1164     if (!json_util::readJsonPatch(
1165             req, asyncResp->res,                                  //
1166             "AppliedOperatingConfig/@odata.id", appliedConfigUri, //
1167             "LocationIndicatorActive", locationIndicatorActive    //
1168             ))
1169     {
1170         return;
1171     }
1172 
1173     // Check for 404 and find matching D-Bus object, then run
1174     // property patch handlers if that all succeeds.
1175     getProcessorObject(
1176         asyncResp, processorId,
1177         std::bind_front(doPatchProcessor, asyncResp, processorId,
1178                         appliedConfigUri, locationIndicatorActive));
1179 }
1180 
1181 inline void requestRoutesOperatingConfigCollection(App& app)
1182 {
1183     BMCWEB_ROUTE(app,
1184                  "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/")
1185         .privileges(redfish::privileges::getOperatingConfigCollection)
1186         .methods(
1187             boost::beast::http::verb::
1188                 get)([&app](const crow::Request& req,
1189                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1190                             const std::string& systemName,
1191                             const std::string& cpuName) {
1192             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1193             {
1194                 return;
1195             }
1196 
1197             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1198             {
1199                 // Option currently returns no systems.  TBD
1200                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1201                                            systemName);
1202                 return;
1203             }
1204 
1205             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1206             {
1207                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1208                                            systemName);
1209                 return;
1210             }
1211             asyncResp->res.jsonValue["@odata.type"] =
1212                 "#OperatingConfigCollection.OperatingConfigCollection";
1213             asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1214                 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs",
1215                 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName);
1216             asyncResp->res.jsonValue["Name"] = "Operating Config Collection";
1217 
1218             // First find the matching CPU object so we know how to
1219             // constrain our search for related Config objects.
1220             const std::array<std::string_view, 1> interfaces = {
1221                 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"};
1222             dbus::utility::getSubTreePaths(
1223                 "/xyz/openbmc_project/inventory", 0, interfaces,
1224                 [asyncResp,
1225                  cpuName](const boost::system::error_code& ec,
1226                           const dbus::utility::MapperGetSubTreePathsResponse&
1227                               objects) {
1228                     if (ec)
1229                     {
1230                         BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec,
1231                                            ec.message());
1232                         messages::internalError(asyncResp->res);
1233                         return;
1234                     }
1235 
1236                     for (const std::string& object : objects)
1237                     {
1238                         if (!object.ends_with(cpuName))
1239                         {
1240                             continue;
1241                         }
1242 
1243                         // Not expected that there will be multiple matching
1244                         // CPU objects, but if there are just use the first
1245                         // one.
1246 
1247                         // Use the common search routine to construct the
1248                         // Collection of all Config objects under this CPU.
1249                         constexpr std::array<std::string_view, 1> interface{
1250                             "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"};
1251                         collection_util::getCollectionMembers(
1252                             asyncResp,
1253                             boost::urls::format(
1254                                 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs",
1255                                 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName),
1256                             interface, object);
1257                         return;
1258                     }
1259                 });
1260         });
1261 }
1262 
1263 inline void requestRoutesOperatingConfig(App& app)
1264 {
1265     BMCWEB_ROUTE(
1266         app,
1267         "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/<str>/")
1268         .privileges(redfish::privileges::getOperatingConfig)
1269         .methods(
1270             boost::beast::http::verb::
1271                 get)([&app](const crow::Request& req,
1272                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1273                             const std::string& systemName,
1274                             const std::string& cpuName,
1275                             const std::string& configName) {
1276             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1277             {
1278                 return;
1279             }
1280             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1281             {
1282                 // Option currently returns no systems.  TBD
1283                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1284                                            systemName);
1285                 return;
1286             }
1287 
1288             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1289             {
1290                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1291                                            systemName);
1292                 return;
1293             }
1294             // Ask for all objects implementing OperatingConfig so we can search
1295             // for one with a matching name
1296             constexpr std::array<std::string_view, 1> interfaces = {
1297                 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"};
1298             dbus::utility::getSubTree(
1299                 "/xyz/openbmc_project/inventory", 0, interfaces,
1300                 [asyncResp, cpuName, configName](
1301                     const boost::system::error_code& ec,
1302                     const dbus::utility::MapperGetSubTreeResponse& subtree) {
1303                     if (ec)
1304                     {
1305                         BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec,
1306                                            ec.message());
1307                         messages::internalError(asyncResp->res);
1308                         return;
1309                     }
1310                     const std::string expectedEnding =
1311                         cpuName + '/' + configName;
1312                     for (const auto& [objectPath, serviceMap] : subtree)
1313                     {
1314                         // Ignore any configs without matching cpuX/configY
1315                         if (!objectPath.ends_with(expectedEnding) ||
1316                             serviceMap.empty())
1317                         {
1318                             continue;
1319                         }
1320 
1321                         nlohmann::json& json = asyncResp->res.jsonValue;
1322                         json["@odata.type"] =
1323                             "#OperatingConfig.v1_0_0.OperatingConfig";
1324                         json["@odata.id"] = boost::urls::format(
1325                             "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}",
1326                             BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName,
1327                             configName);
1328                         json["Name"] = "Processor Profile";
1329                         json["Id"] = configName;
1330 
1331                         // Just use the first implementation of the object - not
1332                         // expected that there would be multiple matching
1333                         // services
1334                         getOperatingConfigData(
1335                             asyncResp, serviceMap.begin()->first, objectPath);
1336                         return;
1337                     }
1338                     messages::resourceNotFound(asyncResp->res,
1339                                                "OperatingConfig", configName);
1340                 });
1341         });
1342 }
1343 
1344 inline void requestRoutesProcessorCollection(App& app)
1345 {
1346     /**
1347      * Functions triggers appropriate requests on DBus
1348      */
1349     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/")
1350         .privileges(redfish::privileges::headProcessorCollection)
1351         .methods(boost::beast::http::verb::head)(
1352             std::bind_front(handleProcessorCollectionHead, std::ref(app)));
1353 
1354     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/")
1355         .privileges(redfish::privileges::getProcessorCollection)
1356         .methods(
1357             boost::beast::http::verb::
1358                 get)([&app](const crow::Request& req,
1359                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1360                             const std::string& systemName) {
1361             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1362             {
1363                 return;
1364             }
1365             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1366             {
1367                 // Option currently returns no systems.  TBD
1368                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1369                                            systemName);
1370                 return;
1371             }
1372 
1373             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1374             {
1375                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1376                                            systemName);
1377                 return;
1378             }
1379 
1380             asyncResp->res.addHeader(
1381                 boost::beast::http::field::link,
1382                 "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby");
1383 
1384             asyncResp->res.jsonValue["@odata.type"] =
1385                 "#ProcessorCollection.ProcessorCollection";
1386             asyncResp->res.jsonValue["Name"] = "Processor Collection";
1387 
1388             asyncResp->res.jsonValue["@odata.id"] =
1389                 std::format("/redfish/v1/Systems/{}/Processors",
1390                             BMCWEB_REDFISH_SYSTEM_URI_NAME);
1391 
1392             collection_util::getCollectionMembers(
1393                 asyncResp,
1394                 boost::urls::format("/redfish/v1/Systems/{}/Processors",
1395                                     BMCWEB_REDFISH_SYSTEM_URI_NAME),
1396                 processorInterfaces, "/xyz/openbmc_project/inventory");
1397         });
1398 }
1399 
1400 inline void requestRoutesProcessor(App& app)
1401 {
1402     /**
1403      * Functions triggers appropriate requests on DBus
1404      */
1405 
1406     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/")
1407         .privileges(redfish::privileges::headProcessor)
1408         .methods(boost::beast::http::verb::head)(
1409             std::bind_front(handleProcessorHead, std::ref(app)));
1410 
1411     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/")
1412         .privileges(redfish::privileges::getProcessor)
1413         .methods(boost::beast::http::verb::get)(
1414             std::bind_front(handleProcessorGet, std::ref(app)));
1415 
1416     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/")
1417         .privileges(redfish::privileges::patchProcessor)
1418         .methods(boost::beast::http::verb::patch)(
1419             std::bind_front(handleProcessorPatch, std::ref(app)));
1420 }
1421 
1422 } // namespace redfish
1423