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