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