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