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