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