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 "health.hpp"
19 
20 #include <boost/container/flat_map.hpp>
21 #include <node.hpp>
22 #include <sdbusplus/message/native_types.hpp>
23 #include <sdbusplus/utility/dedup_variant.hpp>
24 #include <utils/collection.hpp>
25 #include <utils/json_utils.hpp>
26 
27 namespace redfish
28 {
29 
30 using InterfacesProperties = boost::container::flat_map<
31     std::string,
32     boost::container::flat_map<std::string, dbus::utility::DbusVariantType>>;
33 
34 using MapperGetSubTreeResponse = std::vector<
35     std::pair<std::string,
36               std::vector<std::pair<std::string, std::vector<std::string>>>>>;
37 
38 inline void
39     getCpuDataByInterface(const std::shared_ptr<AsyncResp>& aResp,
40                           const InterfacesProperties& cpuInterfacesProperties)
41 {
42     BMCWEB_LOG_DEBUG << "Get CPU resources by interface.";
43 
44     // Added for future purpose. Once present and functional attributes added
45     // in busctl call, need to add actual logic to fetch original values.
46     bool present = false;
47     const bool functional = true;
48     auto health = std::make_shared<HealthPopulate>(aResp);
49     health->populate();
50 
51     for (const auto& interface : cpuInterfacesProperties)
52     {
53         for (const auto& property : interface.second)
54         {
55             if (property.first == "CoreCount")
56             {
57                 const uint16_t* coresCount =
58                     std::get_if<uint16_t>(&property.second);
59                 if (coresCount == nullptr)
60                 {
61                     // Important property not in desired type
62                     messages::internalError(aResp->res);
63                     return;
64                 }
65                 if (*coresCount == 0)
66                 {
67                     // Slot is not populated, set status end return
68                     aResp->res.jsonValue["Status"]["State"] = "Absent";
69                     // HTTP Code will be set up automatically, just return
70                     return;
71                 }
72                 aResp->res.jsonValue["Status"]["State"] = "Enabled";
73                 present = true;
74                 aResp->res.jsonValue["TotalCores"] = *coresCount;
75             }
76             else if (property.first == "MaxSpeedInMhz")
77             {
78                 const uint32_t* value = std::get_if<uint32_t>(&property.second);
79                 if (value != nullptr)
80                 {
81                     aResp->res.jsonValue["MaxSpeedMHz"] = *value;
82                 }
83             }
84             else if (property.first == "Socket")
85             {
86                 const std::string* value =
87                     std::get_if<std::string>(&property.second);
88                 if (value != nullptr)
89                 {
90                     aResp->res.jsonValue["Socket"] = *value;
91                 }
92             }
93             else if (property.first == "ThreadCount")
94             {
95                 const uint16_t* value = std::get_if<uint16_t>(&property.second);
96                 if (value != nullptr)
97                 {
98                     aResp->res.jsonValue["TotalThreads"] = *value;
99                 }
100             }
101             else if (property.first == "Family")
102             {
103                 const std::string* value =
104                     std::get_if<std::string>(&property.second);
105                 if (value != nullptr)
106                 {
107                     aResp->res.jsonValue["ProcessorId"]["EffectiveFamily"] =
108                         *value;
109                 }
110             }
111             else if (property.first == "Id")
112             {
113                 const uint64_t* value = std::get_if<uint64_t>(&property.second);
114                 if (value != nullptr && *value != 0)
115                 {
116                     present = true;
117                     aResp->res
118                         .jsonValue["ProcessorId"]["IdentificationRegisters"] =
119                         boost::lexical_cast<std::string>(*value);
120                 }
121             }
122         }
123     }
124 
125     if (present == false)
126     {
127         aResp->res.jsonValue["Status"]["State"] = "Absent";
128         aResp->res.jsonValue["Status"]["Health"] = "OK";
129     }
130     else
131     {
132         aResp->res.jsonValue["Status"]["State"] = "Enabled";
133         if (functional)
134         {
135             aResp->res.jsonValue["Status"]["Health"] = "OK";
136         }
137         else
138         {
139             aResp->res.jsonValue["Status"]["Health"] = "Critical";
140         }
141     }
142 
143     return;
144 }
145 
146 inline void getCpuDataByService(std::shared_ptr<AsyncResp> aResp,
147                                 const std::string& cpuId,
148                                 const std::string& service,
149                                 const std::string& objPath)
150 {
151     BMCWEB_LOG_DEBUG << "Get available system cpu resources by service.";
152 
153     crow::connections::systemBus->async_method_call(
154         [cpuId, service, objPath, aResp{std::move(aResp)}](
155             const boost::system::error_code ec,
156             const dbus::utility::ManagedObjectType& dbusData) {
157             if (ec)
158             {
159                 BMCWEB_LOG_DEBUG << "DBUS response error";
160                 messages::internalError(aResp->res);
161                 return;
162             }
163             aResp->res.jsonValue["Id"] = cpuId;
164             aResp->res.jsonValue["Name"] = "Processor";
165             aResp->res.jsonValue["ProcessorType"] = "CPU";
166 
167             bool slotPresent = false;
168             std::string corePath = objPath + "/core";
169             size_t totalCores = 0;
170             for (const auto& object : dbusData)
171             {
172                 if (object.first.str == objPath)
173                 {
174                     getCpuDataByInterface(aResp, object.second);
175                 }
176                 else if (boost::starts_with(object.first.str, corePath))
177                 {
178                     for (const auto& interface : object.second)
179                     {
180                         if (interface.first ==
181                             "xyz.openbmc_project.Inventory.Item")
182                         {
183                             for (const auto& property : interface.second)
184                             {
185                                 if (property.first == "Present")
186                                 {
187                                     const bool* present =
188                                         std::get_if<bool>(&property.second);
189                                     if (present != nullptr)
190                                     {
191                                         if (*present == true)
192                                         {
193                                             slotPresent = true;
194                                             totalCores++;
195                                         }
196                                     }
197                                 }
198                             }
199                         }
200                     }
201                 }
202             }
203             // In getCpuDataByInterface(), state and health are set
204             // based on the present and functional status. If core
205             // count is zero, then it has a higher precedence.
206             if (slotPresent)
207             {
208                 if (totalCores == 0)
209                 {
210                     // Slot is not populated, set status end return
211                     aResp->res.jsonValue["Status"]["State"] = "Absent";
212                     aResp->res.jsonValue["Status"]["Health"] = "OK";
213                 }
214                 aResp->res.jsonValue["TotalCores"] = totalCores;
215             }
216             return;
217         },
218         service, "/xyz/openbmc_project/inventory",
219         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
220 }
221 
222 inline void getCpuAssetData(std::shared_ptr<AsyncResp> aResp,
223                             const std::string& service,
224                             const std::string& objPath)
225 {
226     BMCWEB_LOG_DEBUG << "Get Cpu Asset Data";
227     crow::connections::systemBus->async_method_call(
228         [objPath, aResp{std::move(aResp)}](
229             const boost::system::error_code ec,
230             const boost::container::flat_map<
231                 std::string, std::variant<std::string, uint32_t, uint16_t,
232                                           bool>>& properties) {
233             if (ec)
234             {
235                 BMCWEB_LOG_DEBUG << "DBUS response error";
236                 messages::internalError(aResp->res);
237                 return;
238             }
239 
240             for (const auto& property : properties)
241             {
242                 if (property.first == "SerialNumber")
243                 {
244                     const std::string* sn =
245                         std::get_if<std::string>(&property.second);
246                     if (sn != nullptr && !sn->empty())
247                     {
248                         aResp->res.jsonValue["SerialNumber"] = *sn;
249                     }
250                 }
251                 else if (property.first == "Model")
252                 {
253                     const std::string* model =
254                         std::get_if<std::string>(&property.second);
255                     if (model != nullptr && !model->empty())
256                     {
257                         aResp->res.jsonValue["Model"] = *model;
258                     }
259                 }
260                 else if (property.first == "Manufacturer")
261                 {
262 
263                     const std::string* mfg =
264                         std::get_if<std::string>(&property.second);
265                     if (mfg != nullptr)
266                     {
267                         aResp->res.jsonValue["Manufacturer"] = *mfg;
268 
269                         // Otherwise would be unexpected.
270                         if (mfg->find("Intel") != std::string::npos)
271                         {
272                             aResp->res.jsonValue["ProcessorArchitecture"] =
273                                 "x86";
274                             aResp->res.jsonValue["InstructionSet"] = "x86-64";
275                         }
276                         else if (mfg->find("IBM") != std::string::npos)
277                         {
278                             aResp->res.jsonValue["ProcessorArchitecture"] =
279                                 "Power";
280                             aResp->res.jsonValue["InstructionSet"] = "PowerISA";
281                         }
282                     }
283                 }
284             }
285         },
286         service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
287         "xyz.openbmc_project.Inventory.Decorator.Asset");
288 }
289 
290 inline void getCpuRevisionData(std::shared_ptr<AsyncResp> aResp,
291                                const std::string& service,
292                                const std::string& objPath)
293 {
294     BMCWEB_LOG_DEBUG << "Get Cpu Revision Data";
295     crow::connections::systemBus->async_method_call(
296         [objPath, aResp{std::move(aResp)}](
297             const boost::system::error_code ec,
298             const boost::container::flat_map<
299                 std::string, std::variant<std::string, uint32_t, uint16_t,
300                                           bool>>& properties) {
301             if (ec)
302             {
303                 BMCWEB_LOG_DEBUG << "DBUS response error";
304                 messages::internalError(aResp->res);
305                 return;
306             }
307 
308             for (const auto& property : properties)
309             {
310                 if (property.first == "Version")
311                 {
312                     const std::string* ver =
313                         std::get_if<std::string>(&property.second);
314                     if (ver != nullptr)
315                     {
316                         aResp->res.jsonValue["Version"] = *ver;
317                     }
318                     break;
319                 }
320             }
321         },
322         service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
323         "xyz.openbmc_project.Inventory.Decorator.Revision");
324 }
325 
326 inline void getAcceleratorDataByService(std::shared_ptr<AsyncResp> aResp,
327                                         const std::string& acclrtrId,
328                                         const std::string& service,
329                                         const std::string& objPath)
330 {
331     BMCWEB_LOG_DEBUG
332         << "Get available system Accelerator resources by service.";
333     crow::connections::systemBus->async_method_call(
334         [acclrtrId, aResp{std::move(aResp)}](
335             const boost::system::error_code ec,
336             const boost::container::flat_map<
337                 std::string, std::variant<std::string, uint32_t, uint16_t,
338                                           bool>>& properties) {
339             if (ec)
340             {
341                 BMCWEB_LOG_DEBUG << "DBUS response error";
342                 messages::internalError(aResp->res);
343                 return;
344             }
345             aResp->res.jsonValue["Id"] = acclrtrId;
346             aResp->res.jsonValue["Name"] = "Processor";
347             const bool* accPresent = nullptr;
348             const bool* accFunctional = nullptr;
349 
350             for (const auto& property : properties)
351             {
352                 if (property.first == "Functional")
353                 {
354                     accFunctional = std::get_if<bool>(&property.second);
355                 }
356                 else if (property.first == "Present")
357                 {
358                     accPresent = std::get_if<bool>(&property.second);
359                 }
360             }
361 
362             std::string state = "Enabled";
363             std::string health = "OK";
364 
365             if (accPresent != nullptr && *accPresent == false)
366             {
367                 state = "Absent";
368             }
369 
370             if ((accFunctional != nullptr) && (*accFunctional == false))
371             {
372                 if (state == "Enabled")
373                 {
374                     health = "Critical";
375                 }
376             }
377 
378             aResp->res.jsonValue["Status"]["State"] = state;
379             aResp->res.jsonValue["Status"]["Health"] = health;
380             aResp->res.jsonValue["ProcessorType"] = "Accelerator";
381         },
382         service, objPath, "org.freedesktop.DBus.Properties", "GetAll", "");
383 }
384 
385 // OperatingConfig D-Bus Types
386 using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>;
387 using BaseSpeedPrioritySettingsProperty =
388     std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>;
389 // uint32_t and size_t may or may not be the same type, requiring a dedup'd
390 // variant
391 using OperatingConfigProperties = std::vector<std::pair<
392     std::string,
393     sdbusplus::utility::dedup_variant<uint32_t, size_t, TurboProfileProperty,
394                                       BaseSpeedPrioritySettingsProperty>>>;
395 
396 /**
397  * Fill out the HighSpeedCoreIDs in a Processor resource from the given
398  * OperatingConfig D-Bus property.
399  *
400  * @param[in,out]   aResp               Async HTTP response.
401  * @param[in]       baseSpeedSettings   Full list of base speed priority groups,
402  *                                      to use to determine the list of high
403  *                                      speed cores.
404  */
405 inline void highSpeedCoreIdsHandler(
406     const std::shared_ptr<AsyncResp>& aResp,
407     const BaseSpeedPrioritySettingsProperty& baseSpeedSettings)
408 {
409     // The D-Bus property does not indicate which bucket is the "high
410     // priority" group, so let's discern that by looking for the one with
411     // highest base frequency.
412     auto highPriorityGroup = baseSpeedSettings.cend();
413     uint32_t highestBaseSpeed = 0;
414     for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend();
415          ++it)
416     {
417         const uint32_t baseFreq = std::get<uint32_t>(*it);
418         if (baseFreq > highestBaseSpeed)
419         {
420             highestBaseSpeed = baseFreq;
421             highPriorityGroup = it;
422         }
423     }
424 
425     nlohmann::json& jsonCoreIds = aResp->res.jsonValue["HighSpeedCoreIDs"];
426     jsonCoreIds = nlohmann::json::array();
427 
428     // There may not be any entries in the D-Bus property, so only populate
429     // if there was actually something there.
430     if (highPriorityGroup != baseSpeedSettings.cend())
431     {
432         jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup);
433     }
434 }
435 
436 /**
437  * Fill out OperatingConfig related items in a Processor resource by requesting
438  * data from the given D-Bus object.
439  *
440  * @param[in,out]   aResp       Async HTTP response.
441  * @param[in]       cpuId       CPU D-Bus name.
442  * @param[in]       service     D-Bus service to query.
443  * @param[in]       objPath     D-Bus object to query.
444  */
445 inline void getCpuConfigData(const std::shared_ptr<AsyncResp>& aResp,
446                              const std::string& cpuId,
447                              const std::string& service,
448                              const std::string& objPath)
449 {
450     BMCWEB_LOG_INFO << "Getting CPU operating configs for " << cpuId;
451 
452     // First, GetAll CurrentOperatingConfig properties on the object
453     crow::connections::systemBus->async_method_call(
454         [aResp, cpuId, service](
455             const boost::system::error_code ec,
456             const std::vector<
457                 std::pair<std::string,
458                           std::variant<sdbusplus::message::object_path, bool>>>&
459                 properties) {
460             if (ec)
461             {
462                 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
463                                    << ec.message();
464                 messages::internalError(aResp->res);
465                 return;
466             }
467 
468             nlohmann::json& json = aResp->res.jsonValue;
469 
470             for (const auto& [dbusPropName, variantVal] : properties)
471             {
472                 if (dbusPropName == "AppliedConfig")
473                 {
474                     const sdbusplus::message::object_path* dbusPathWrapper =
475                         std::get_if<sdbusplus::message::object_path>(
476                             &variantVal);
477                     if (dbusPathWrapper == nullptr)
478                     {
479                         continue;
480                     }
481 
482                     const std::string& dbusPath = dbusPathWrapper->str;
483                     std::string uri = "/redfish/v1/Systems/system/Processors/" +
484                                       cpuId + "/OperatingConfigs";
485                     json["OperatingConfigs"] = {{"@odata.id", uri}};
486 
487                     // Reuse the D-Bus config object name for the Redfish
488                     // URI
489                     size_t baseNamePos = dbusPath.rfind('/');
490                     if (baseNamePos == std::string::npos ||
491                         baseNamePos == (dbusPath.size() - 1))
492                     {
493                         // If the AppliedConfig was somehow not a valid path,
494                         // skip adding any more properties, since everything
495                         // else is tied to this applied config.
496                         messages::internalError(aResp->res);
497                         break;
498                     }
499                     uri += '/';
500                     uri += dbusPath.substr(baseNamePos + 1);
501                     json["AppliedOperatingConfig"] = {{"@odata.id", uri}};
502 
503                     // Once we found the current applied config, queue another
504                     // request to read the base freq core ids out of that
505                     // config.
506                     crow::connections::systemBus->async_method_call(
507                         [aResp](
508                             const boost::system::error_code ec,
509                             const std::variant<
510                                 BaseSpeedPrioritySettingsProperty>& property) {
511                             if (ec)
512                             {
513                                 BMCWEB_LOG_WARNING
514                                     << "D-Bus Property Get error: " << ec;
515                                 messages::internalError(aResp->res);
516                                 return;
517                             }
518                             auto baseSpeedList =
519                                 std::get_if<BaseSpeedPrioritySettingsProperty>(
520                                     &property);
521                             if (baseSpeedList != nullptr)
522                             {
523                                 highSpeedCoreIdsHandler(aResp, *baseSpeedList);
524                             }
525                         },
526                         service, dbusPath, "org.freedesktop.DBus.Properties",
527                         "Get",
528                         "xyz.openbmc_project.Inventory.Item.Cpu."
529                         "OperatingConfig",
530                         "BaseSpeedPrioritySettings");
531                 }
532                 else if (dbusPropName == "BaseSpeedPriorityEnabled")
533                 {
534                     const bool* state = std::get_if<bool>(&variantVal);
535                     if (state != nullptr)
536                     {
537                         json["BaseSpeedPriorityState"] =
538                             *state ? "Enabled" : "Disabled";
539                     }
540                 }
541             }
542         },
543         service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
544         "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig");
545 }
546 
547 inline void getProcessorData(std::shared_ptr<AsyncResp> aResp,
548                              const std::string& processorId)
549 {
550     BMCWEB_LOG_DEBUG << "Get available system processor resources.";
551 
552     crow::connections::systemBus->async_method_call(
553         [processorId,
554          aResp{std::move(aResp)}](const boost::system::error_code ec,
555                                   const MapperGetSubTreeResponse& subtree) {
556             if (ec)
557             {
558                 BMCWEB_LOG_DEBUG << "DBUS response error";
559                 messages::internalError(aResp->res);
560                 return;
561             }
562             for (const auto& [objectPath, serviceMap] : subtree)
563             {
564                 // Ignore any objects which don't end with our desired cpu name
565                 if (!boost::ends_with(objectPath, processorId))
566                 {
567                     continue;
568                 }
569 
570                 // Process the first object which does match our cpu name
571                 // suffix, and potentially ignore any other matching objects.
572                 // Assume all interfaces we want to process must be on the same
573                 // object.
574 
575                 for (const auto& [serviceName, interfaceList] : serviceMap)
576                 {
577                     for (const auto& interface : interfaceList)
578                     {
579                         if (interface ==
580                             "xyz.openbmc_project.Inventory.Decorator.Asset")
581                         {
582                             getCpuAssetData(aResp, serviceName, objectPath);
583                         }
584                         else if (interface == "xyz.openbmc_project.Inventory."
585                                               "Decorator.Revision")
586                         {
587                             getCpuRevisionData(aResp, serviceName, objectPath);
588                         }
589                         else if (interface ==
590                                  "xyz.openbmc_project.Inventory.Item.Cpu")
591                         {
592                             getCpuDataByService(aResp, processorId, serviceName,
593                                                 objectPath);
594                         }
595                         else if (interface == "xyz.openbmc_project.Inventory."
596                                               "Item.Accelerator")
597                         {
598                             getAcceleratorDataByService(
599                                 aResp, processorId, serviceName, objectPath);
600                         }
601                         else if (interface ==
602                                  "xyz.openbmc_project.Control.Processor."
603                                  "CurrentOperatingConfig")
604                         {
605                             getCpuConfigData(aResp, processorId, serviceName,
606                                              objectPath);
607                         }
608                     }
609                 }
610                 return;
611             }
612             // Object not found
613             messages::resourceNotFound(aResp->res, "Processor", processorId);
614             return;
615         },
616         "xyz.openbmc_project.ObjectMapper",
617         "/xyz/openbmc_project/object_mapper",
618         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
619         "/xyz/openbmc_project/inventory", 0,
620         std::array<const char*, 5>{
621             "xyz.openbmc_project.Inventory.Decorator.Asset",
622             "xyz.openbmc_project.Inventory.Decorator.Revision",
623             "xyz.openbmc_project.Inventory.Item.Cpu",
624             "xyz.openbmc_project.Inventory.Item.Accelerator",
625             "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"});
626 }
627 
628 /**
629  * Request all the properties for the given D-Bus object and fill out the
630  * related entries in the Redfish OperatingConfig response.
631  *
632  * @param[in,out]   aResp       Async HTTP response.
633  * @param[in]       service     D-Bus service name to query.
634  * @param[in]       objPath     D-Bus object to query.
635  */
636 inline void getOperatingConfigData(const std::shared_ptr<AsyncResp>& aResp,
637                                    const std::string& service,
638                                    const std::string& objPath)
639 {
640     crow::connections::systemBus->async_method_call(
641         [aResp](boost::system::error_code ec,
642                 const OperatingConfigProperties& properties) {
643             if (ec)
644             {
645                 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
646                                    << ec.message();
647                 messages::internalError(aResp->res);
648                 return;
649             }
650 
651             nlohmann::json& json = aResp->res.jsonValue;
652             for (const auto& [key, variant] : properties)
653             {
654                 if (key == "AvailableCoreCount")
655                 {
656                     const size_t* cores = std::get_if<size_t>(&variant);
657                     if (cores != nullptr)
658                     {
659                         json["TotalAvailableCoreCount"] = *cores;
660                     }
661                 }
662                 else if (key == "BaseSpeed")
663                 {
664                     const uint32_t* speed = std::get_if<uint32_t>(&variant);
665                     if (speed != nullptr)
666                     {
667                         json["BaseSpeedMHz"] = *speed;
668                     }
669                 }
670                 else if (key == "MaxJunctionTemperature")
671                 {
672                     const uint32_t* temp = std::get_if<uint32_t>(&variant);
673                     if (temp != nullptr)
674                     {
675                         json["MaxJunctionTemperatureCelsius"] = *temp;
676                     }
677                 }
678                 else if (key == "MaxSpeed")
679                 {
680                     const uint32_t* speed = std::get_if<uint32_t>(&variant);
681                     if (speed != nullptr)
682                     {
683                         json["MaxSpeedMHz"] = *speed;
684                     }
685                 }
686                 else if (key == "PowerLimit")
687                 {
688                     const uint32_t* tdp = std::get_if<uint32_t>(&variant);
689                     if (tdp != nullptr)
690                     {
691                         json["TDPWatts"] = *tdp;
692                     }
693                 }
694                 else if (key == "TurboProfile")
695                 {
696                     const auto* turboList =
697                         std::get_if<TurboProfileProperty>(&variant);
698                     if (turboList == nullptr)
699                     {
700                         continue;
701                     }
702 
703                     nlohmann::json& turboArray = json["TurboProfile"];
704                     turboArray = nlohmann::json::array();
705                     for (const auto& [turboSpeed, coreCount] : *turboList)
706                     {
707                         turboArray.push_back({{"ActiveCoreCount", coreCount},
708                                               {"MaxSpeedMHz", turboSpeed}});
709                     }
710                 }
711                 else if (key == "BaseSpeedPrioritySettings")
712                 {
713                     const auto* baseSpeedList =
714                         std::get_if<BaseSpeedPrioritySettingsProperty>(
715                             &variant);
716                     if (baseSpeedList == nullptr)
717                     {
718                         continue;
719                     }
720 
721                     nlohmann::json& baseSpeedArray =
722                         json["BaseSpeedPrioritySettings"];
723                     baseSpeedArray = nlohmann::json::array();
724                     for (const auto& [baseSpeed, coreList] : *baseSpeedList)
725                     {
726                         baseSpeedArray.push_back(
727                             {{"CoreCount", coreList.size()},
728                              {"CoreIDs", coreList},
729                              {"BaseSpeedMHz", baseSpeed}});
730                     }
731                 }
732             }
733         },
734         service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
735         "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig");
736 }
737 
738 class OperatingConfigCollection : public Node
739 {
740   public:
741     OperatingConfigCollection(App& app) :
742         Node(app,
743              "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/",
744              std::string())
745     {
746         // Defined by Redfish spec privilege registry
747         entityPrivileges = {
748             {boost::beast::http::verb::get, {{"Login"}}},
749             {boost::beast::http::verb::head, {{"Login"}}},
750             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
751             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
752             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
753             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
754     }
755 
756   private:
757     void doGet(crow::Response& res, const crow::Request& req,
758                const std::vector<std::string>& params) override
759     {
760         if (params.size() != 1)
761         {
762             messages::internalError(res);
763             res.end();
764             return;
765         }
766 
767         const std::string& cpuName = params[0];
768         res.jsonValue["@odata.type"] =
769             "#OperatingConfigCollection.OperatingConfigCollection";
770         res.jsonValue["@odata.id"] = req.url;
771         res.jsonValue["Name"] = "Operating Config Collection";
772 
773         auto asyncResp = std::make_shared<AsyncResp>(res);
774 
775         // First find the matching CPU object so we know how to constrain our
776         // search for related Config objects.
777         crow::connections::systemBus->async_method_call(
778             [asyncResp, cpuName](const boost::system::error_code ec,
779                                  const std::vector<std::string>& objects) {
780                 if (ec)
781                 {
782                     BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
783                                        << ec.message();
784                     messages::internalError(asyncResp->res);
785                     return;
786                 }
787 
788                 for (const std::string& object : objects)
789                 {
790                     if (!boost::ends_with(object, cpuName))
791                     {
792                         continue;
793                     }
794 
795                     // Not expected that there will be multiple matching CPU
796                     // objects, but if there are just use the first one.
797 
798                     // Use the common search routine to construct the Collection
799                     // of all Config objects under this CPU.
800                     collection_util::getCollectionMembers(
801                         asyncResp,
802                         "/redfish/v1/Systems/system/Processors/" + cpuName +
803                             "/OperatingConfigs",
804                         {"xyz.openbmc_project.Inventory.Item.Cpu."
805                          "OperatingConfig"},
806                         object.c_str());
807                     return;
808                 }
809             },
810             "xyz.openbmc_project.ObjectMapper",
811             "/xyz/openbmc_project/object_mapper",
812             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
813             "/xyz/openbmc_project/inventory", 0,
814             std::array<const char*, 1>{"xyz.openbmc_project.Control.Processor."
815                                        "CurrentOperatingConfig"});
816     }
817 };
818 
819 class OperatingConfig : public Node
820 {
821   public:
822     OperatingConfig(App& app) :
823         Node(app,
824              "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/"
825              "<str>/",
826              std::string(), std::string())
827     {
828         // Defined by Redfish spec privilege registry
829         entityPrivileges = {
830             {boost::beast::http::verb::get, {{"Login"}}},
831             {boost::beast::http::verb::head, {{"Login"}}},
832             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
833             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
834             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
835             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
836     }
837 
838   private:
839     void doGet(crow::Response& res, const crow::Request& req,
840                const std::vector<std::string>& params) override
841     {
842         if (params.size() != 2)
843         {
844             messages::internalError(res);
845             res.end();
846             return;
847         }
848 
849         const std::string& cpuName = params[0];
850         const std::string& configName = params[1];
851 
852         auto asyncResp = std::make_shared<AsyncResp>(res);
853 
854         // Ask for all objects implementing OperatingConfig so we can search for
855         // one with a matching name
856         crow::connections::systemBus->async_method_call(
857             [asyncResp, cpuName, configName,
858              reqUrl{req.url}](boost::system::error_code ec,
859                               const MapperGetSubTreeResponse& subtree) {
860                 if (ec)
861                 {
862                     BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
863                                        << ec.message();
864                     messages::internalError(asyncResp->res);
865                     return;
866                 }
867                 const std::string expectedEnding = cpuName + '/' + configName;
868                 for (const auto& [objectPath, serviceMap] : subtree)
869                 {
870                     // Ignore any configs without matching cpuX/configY
871                     if (!boost::ends_with(objectPath, expectedEnding) ||
872                         serviceMap.empty())
873                     {
874                         continue;
875                     }
876 
877                     nlohmann::json& json = asyncResp->res.jsonValue;
878                     json["@odata.type"] =
879                         "#OperatingConfig.v1_0_0.OperatingConfig";
880                     json["@odata.id"] = reqUrl;
881                     json["Name"] = "Processor Profile";
882                     json["Id"] = configName;
883 
884                     // Just use the first implementation of the object - not
885                     // expected that there would be multiple matching services
886                     getOperatingConfigData(asyncResp, serviceMap.begin()->first,
887                                            objectPath);
888                     return;
889                 }
890                 messages::resourceNotFound(asyncResp->res, "OperatingConfig",
891                                            configName);
892             },
893             "xyz.openbmc_project.ObjectMapper",
894             "/xyz/openbmc_project/object_mapper",
895             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
896             "/xyz/openbmc_project/inventory", 0,
897             std::array<const char*, 1>{
898                 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"});
899     }
900 };
901 
902 class ProcessorCollection : public Node
903 {
904   public:
905     /*
906      * Default Constructor
907      */
908     ProcessorCollection(App& app) :
909         Node(app, "/redfish/v1/Systems/system/Processors/")
910     {
911         entityPrivileges = {
912             {boost::beast::http::verb::get, {{"Login"}}},
913             {boost::beast::http::verb::head, {{"Login"}}},
914             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
915             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
916             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
917             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
918     }
919 
920   private:
921     /**
922      * Functions triggers appropriate requests on DBus
923      */
924     void doGet(crow::Response& res, const crow::Request&,
925                const std::vector<std::string>&) override
926     {
927         res.jsonValue["@odata.type"] =
928             "#ProcessorCollection.ProcessorCollection";
929         res.jsonValue["Name"] = "Processor Collection";
930 
931         res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Processors";
932         auto asyncResp = std::make_shared<AsyncResp>(res);
933 
934         collection_util::getCollectionMembers(
935             asyncResp, "/redfish/v1/Systems/system/Processors",
936             {"xyz.openbmc_project.Inventory.Item.Cpu",
937              "xyz.openbmc_project.Inventory.Item.Accelerator"});
938     }
939 };
940 
941 class Processor : public Node
942 {
943   public:
944     /*
945      * Default Constructor
946      */
947     Processor(App& app) :
948         Node(app, "/redfish/v1/Systems/system/Processors/<str>/", std::string())
949     {
950         entityPrivileges = {
951             {boost::beast::http::verb::get, {{"Login"}}},
952             {boost::beast::http::verb::head, {{"Login"}}},
953             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
954             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
955             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
956             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
957     }
958 
959   private:
960     /**
961      * Functions triggers appropriate requests on DBus
962      */
963     void doGet(crow::Response& res, const crow::Request&,
964                const std::vector<std::string>& params) override
965     {
966         // Check if there is required param, truly entering this shall be
967         // impossible
968         if (params.size() != 1)
969         {
970             messages::internalError(res);
971 
972             res.end();
973             return;
974         }
975         const std::string& processorId = params[0];
976         res.jsonValue["@odata.type"] = "#Processor.v1_9_0.Processor";
977         res.jsonValue["@odata.id"] =
978             "/redfish/v1/Systems/system/Processors/" + processorId;
979 
980         auto asyncResp = std::make_shared<AsyncResp>(res);
981 
982         getProcessorData(asyncResp, processorId);
983     }
984 };
985 
986 } // namespace redfish
987