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