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