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