xref: /openbmc/bmcweb/redfish-core/lib/processor.hpp (revision 8d1b46d7f8d39db2ba048f9e9007106ca3a28c9b)
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     // 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<bmcweb::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<bmcweb::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<bmcweb::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(
360     std::shared_ptr<bmcweb::AsyncResp> aResp, const std::string& acclrtrId,
361     const std::string& service, const std::string& objPath)
362 {
363     BMCWEB_LOG_DEBUG
364         << "Get available system Accelerator resources by service.";
365     crow::connections::systemBus->async_method_call(
366         [acclrtrId, aResp{std::move(aResp)}](
367             const boost::system::error_code ec,
368             const boost::container::flat_map<
369                 std::string, std::variant<std::string, uint32_t, uint16_t,
370                                           bool>>& properties) {
371             if (ec)
372             {
373                 BMCWEB_LOG_DEBUG << "DBUS response error";
374                 messages::internalError(aResp->res);
375                 return;
376             }
377             aResp->res.jsonValue["Id"] = acclrtrId;
378             aResp->res.jsonValue["Name"] = "Processor";
379             const bool* accPresent = nullptr;
380             const bool* accFunctional = nullptr;
381 
382             for (const auto& property : properties)
383             {
384                 if (property.first == "Functional")
385                 {
386                     accFunctional = std::get_if<bool>(&property.second);
387                 }
388                 else if (property.first == "Present")
389                 {
390                     accPresent = std::get_if<bool>(&property.second);
391                 }
392             }
393 
394             std::string state = "Enabled";
395             std::string health = "OK";
396 
397             if (accPresent != nullptr && *accPresent == false)
398             {
399                 state = "Absent";
400             }
401 
402             if ((accFunctional != nullptr) && (*accFunctional == false))
403             {
404                 if (state == "Enabled")
405                 {
406                     health = "Critical";
407                 }
408             }
409 
410             aResp->res.jsonValue["Status"]["State"] = state;
411             aResp->res.jsonValue["Status"]["Health"] = health;
412             aResp->res.jsonValue["ProcessorType"] = "Accelerator";
413         },
414         service, objPath, "org.freedesktop.DBus.Properties", "GetAll", "");
415 }
416 
417 // OperatingConfig D-Bus Types
418 using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>;
419 using BaseSpeedPrioritySettingsProperty =
420     std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>;
421 // uint32_t and size_t may or may not be the same type, requiring a dedup'd
422 // variant
423 using OperatingConfigProperties = std::vector<std::pair<
424     std::string,
425     sdbusplus::utility::dedup_variant<uint32_t, size_t, TurboProfileProperty,
426                                       BaseSpeedPrioritySettingsProperty>>>;
427 
428 /**
429  * Fill out the HighSpeedCoreIDs in a Processor resource from the given
430  * OperatingConfig D-Bus property.
431  *
432  * @param[in,out]   aResp               Async HTTP response.
433  * @param[in]       baseSpeedSettings   Full list of base speed priority groups,
434  *                                      to use to determine the list of high
435  *                                      speed cores.
436  */
437 inline void highSpeedCoreIdsHandler(
438     const std::shared_ptr<bmcweb::AsyncResp>& aResp,
439     const BaseSpeedPrioritySettingsProperty& baseSpeedSettings)
440 {
441     // The D-Bus property does not indicate which bucket is the "high
442     // priority" group, so let's discern that by looking for the one with
443     // highest base frequency.
444     auto highPriorityGroup = baseSpeedSettings.cend();
445     uint32_t highestBaseSpeed = 0;
446     for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend();
447          ++it)
448     {
449         const uint32_t baseFreq = std::get<uint32_t>(*it);
450         if (baseFreq > highestBaseSpeed)
451         {
452             highestBaseSpeed = baseFreq;
453             highPriorityGroup = it;
454         }
455     }
456 
457     nlohmann::json& jsonCoreIds = aResp->res.jsonValue["HighSpeedCoreIDs"];
458     jsonCoreIds = nlohmann::json::array();
459 
460     // There may not be any entries in the D-Bus property, so only populate
461     // if there was actually something there.
462     if (highPriorityGroup != baseSpeedSettings.cend())
463     {
464         jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup);
465     }
466 }
467 
468 /**
469  * Fill out OperatingConfig related items in a Processor resource by requesting
470  * data from the given D-Bus object.
471  *
472  * @param[in,out]   aResp       Async HTTP response.
473  * @param[in]       cpuId       CPU D-Bus name.
474  * @param[in]       service     D-Bus service to query.
475  * @param[in]       objPath     D-Bus object to query.
476  */
477 inline void getCpuConfigData(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
478                              const std::string& cpuId,
479                              const std::string& service,
480                              const std::string& objPath)
481 {
482     BMCWEB_LOG_INFO << "Getting CPU operating configs for " << cpuId;
483 
484     // First, GetAll CurrentOperatingConfig properties on the object
485     crow::connections::systemBus->async_method_call(
486         [aResp, cpuId, service](
487             const boost::system::error_code ec,
488             const std::vector<
489                 std::pair<std::string,
490                           std::variant<sdbusplus::message::object_path, bool>>>&
491                 properties) {
492             if (ec)
493             {
494                 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
495                                    << ec.message();
496                 messages::internalError(aResp->res);
497                 return;
498             }
499 
500             nlohmann::json& json = aResp->res.jsonValue;
501 
502             for (const auto& [dbusPropName, variantVal] : properties)
503             {
504                 if (dbusPropName == "AppliedConfig")
505                 {
506                     const sdbusplus::message::object_path* dbusPathWrapper =
507                         std::get_if<sdbusplus::message::object_path>(
508                             &variantVal);
509                     if (dbusPathWrapper == nullptr)
510                     {
511                         continue;
512                     }
513 
514                     const std::string& dbusPath = dbusPathWrapper->str;
515                     std::string uri = "/redfish/v1/Systems/system/Processors/" +
516                                       cpuId + "/OperatingConfigs";
517                     json["OperatingConfigs"] = {{"@odata.id", uri}};
518 
519                     // Reuse the D-Bus config object name for the Redfish
520                     // URI
521                     size_t baseNamePos = dbusPath.rfind('/');
522                     if (baseNamePos == std::string::npos ||
523                         baseNamePos == (dbusPath.size() - 1))
524                     {
525                         // If the AppliedConfig was somehow not a valid path,
526                         // skip adding any more properties, since everything
527                         // else is tied to this applied config.
528                         messages::internalError(aResp->res);
529                         break;
530                     }
531                     uri += '/';
532                     uri += dbusPath.substr(baseNamePos + 1);
533                     json["AppliedOperatingConfig"] = {{"@odata.id", uri}};
534 
535                     // Once we found the current applied config, queue another
536                     // request to read the base freq core ids out of that
537                     // config.
538                     crow::connections::systemBus->async_method_call(
539                         [aResp](
540                             const boost::system::error_code ec,
541                             const std::variant<
542                                 BaseSpeedPrioritySettingsProperty>& property) {
543                             if (ec)
544                             {
545                                 BMCWEB_LOG_WARNING
546                                     << "D-Bus Property Get error: " << ec;
547                                 messages::internalError(aResp->res);
548                                 return;
549                             }
550                             auto baseSpeedList =
551                                 std::get_if<BaseSpeedPrioritySettingsProperty>(
552                                     &property);
553                             if (baseSpeedList != nullptr)
554                             {
555                                 highSpeedCoreIdsHandler(aResp, *baseSpeedList);
556                             }
557                         },
558                         service, dbusPath, "org.freedesktop.DBus.Properties",
559                         "Get",
560                         "xyz.openbmc_project.Inventory.Item.Cpu."
561                         "OperatingConfig",
562                         "BaseSpeedPrioritySettings");
563                 }
564                 else if (dbusPropName == "BaseSpeedPriorityEnabled")
565                 {
566                     const bool* state = std::get_if<bool>(&variantVal);
567                     if (state != nullptr)
568                     {
569                         json["BaseSpeedPriorityState"] =
570                             *state ? "Enabled" : "Disabled";
571                     }
572                 }
573             }
574         },
575         service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
576         "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig");
577 }
578 
579 /**
580  * @brief Fill out location info of a processor by
581  * requesting data from the given D-Bus object.
582  *
583  * @param[in,out]   aResp       Async HTTP response.
584  * @param[in]       service     D-Bus service to query.
585  * @param[in]       objPath     D-Bus object to query.
586  */
587 inline void getCpuLocationCode(std::shared_ptr<bmcweb::AsyncResp> aResp,
588                                const std::string& service,
589                                const std::string& objPath)
590 {
591     BMCWEB_LOG_DEBUG << "Get Cpu Location Data";
592     crow::connections::systemBus->async_method_call(
593         [objPath,
594          aResp{std::move(aResp)}](const boost::system::error_code ec,
595                                   const std::variant<std::string>& property) {
596             if (ec)
597             {
598                 BMCWEB_LOG_DEBUG << "DBUS response error";
599                 messages::internalError(aResp->res);
600                 return;
601             }
602 
603             const std::string* value = std::get_if<std::string>(&property);
604 
605             if (value == nullptr)
606             {
607                 // illegal value
608                 BMCWEB_LOG_DEBUG << "Location code value error";
609                 messages::internalError(aResp->res);
610                 return;
611             }
612 
613             aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
614                 *value;
615         },
616         service, objPath, "org.freedesktop.DBus.Properties", "Get",
617         "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode");
618 }
619 
620 /**
621  * Find the D-Bus object representing the requested Processor, and call the
622  * handler with the results. If matching object is not found, add 404 error to
623  * response and don't call the handler.
624  *
625  * @param[in,out]   resp            Async HTTP response.
626  * @param[in]       processorId     Redfish Processor Id.
627  * @param[in]       handler         Callback to continue processing request upon
628  *                                  successfully finding object.
629  */
630 template <typename Handler>
631 inline void getProcessorObject(const std::shared_ptr<bmcweb::AsyncResp>& resp,
632                                const std::string& processorId,
633                                Handler&& handler)
634 {
635     BMCWEB_LOG_DEBUG << "Get available system processor resources.";
636 
637     // GetSubTree on all interfaces which provide info about a Processor
638     crow::connections::systemBus->async_method_call(
639         [resp, processorId, handler = std::forward<Handler>(handler)](
640             boost::system::error_code ec,
641             const MapperGetSubTreeResponse& subtree) mutable {
642             if (ec)
643             {
644                 BMCWEB_LOG_DEBUG << "DBUS response error: " << ec;
645                 messages::internalError(resp->res);
646                 return;
647             }
648             for (const auto& [objectPath, serviceMap] : subtree)
649             {
650                 // Ignore any objects which don't end with our desired cpu name
651                 if (!boost::ends_with(objectPath, processorId))
652                 {
653                     continue;
654                 }
655 
656                 bool found = false;
657                 // Filter out objects that don't have the CPU-specific
658                 // interfaces to make sure we can return 404 on non-CPUs
659                 // (e.g. /redfish/../Processors/dimm0)
660                 for (const auto& [serviceName, interfaceList] : serviceMap)
661                 {
662                     if (std::find_first_of(
663                             interfaceList.begin(), interfaceList.end(),
664                             processorInterfaces.begin(),
665                             processorInterfaces.end()) != interfaceList.end())
666                     {
667                         found = true;
668                         break;
669                     }
670                 }
671 
672                 if (!found)
673                 {
674                     continue;
675                 }
676 
677                 // Process the first object which does match our cpu name and
678                 // required interfaces, and potentially ignore any other
679                 // matching objects. Assume all interfaces we want to process
680                 // must be on the same object path.
681 
682                 handler(resp, processorId, objectPath, serviceMap);
683                 return;
684             }
685             messages::resourceNotFound(resp->res, "Processor", processorId);
686         },
687         "xyz.openbmc_project.ObjectMapper",
688         "/xyz/openbmc_project/object_mapper",
689         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
690         "/xyz/openbmc_project/inventory", 0,
691         std::array<const char*, 6>{
692             "xyz.openbmc_project.Inventory.Decorator.Asset",
693             "xyz.openbmc_project.Inventory.Decorator.Revision",
694             "xyz.openbmc_project.Inventory.Item.Cpu",
695             "xyz.openbmc_project.Inventory.Decorator.LocationCode",
696             "xyz.openbmc_project.Inventory.Item.Accelerator",
697             "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"});
698 }
699 
700 inline void getProcessorData(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
701                              const std::string& processorId,
702                              const std::string& objectPath,
703                              const MapperServiceMap& serviceMap)
704 {
705     for (const auto& [serviceName, interfaceList] : serviceMap)
706     {
707         for (const auto& interface : interfaceList)
708         {
709             if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
710             {
711                 getCpuAssetData(aResp, serviceName, objectPath);
712             }
713             else if (interface == "xyz.openbmc_project.Inventory."
714                                   "Decorator.Revision")
715             {
716                 getCpuRevisionData(aResp, serviceName, objectPath);
717             }
718             else if (interface == "xyz.openbmc_project.Inventory.Item.Cpu")
719             {
720                 getCpuDataByService(aResp, processorId, serviceName,
721                                     objectPath);
722             }
723             else if (interface == "xyz.openbmc_project.Inventory."
724                                   "Item.Accelerator")
725             {
726                 getAcceleratorDataByService(aResp, processorId, serviceName,
727                                             objectPath);
728             }
729             else if (interface == "xyz.openbmc_project.Control.Processor."
730                                   "CurrentOperatingConfig")
731             {
732                 getCpuConfigData(aResp, processorId, serviceName, objectPath);
733             }
734             else if (interface == "xyz.openbmc_project.Inventory."
735                                   "Decorator.LocationCode")
736             {
737                 getCpuLocationCode(aResp, serviceName, objectPath);
738             }
739         }
740     }
741 }
742 
743 /**
744  * Request all the properties for the given D-Bus object and fill out the
745  * related entries in the Redfish OperatingConfig response.
746  *
747  * @param[in,out]   aResp       Async HTTP response.
748  * @param[in]       service     D-Bus service name to query.
749  * @param[in]       objPath     D-Bus object to query.
750  */
751 inline void
752     getOperatingConfigData(const std::shared_ptr<bmcweb::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(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
874                const crow::Request& req,
875                const std::vector<std::string>& params) override
876     {
877         if (params.size() != 1)
878         {
879             messages::internalError(asyncResp->res);
880             return;
881         }
882 
883         const std::string& cpuName = params[0];
884         asyncResp->res.jsonValue["@odata.type"] =
885             "#OperatingConfigCollection.OperatingConfigCollection";
886         asyncResp->res.jsonValue["@odata.id"] = req.url;
887         asyncResp->res.jsonValue["Name"] = "Operating Config Collection";
888 
889         // First find the matching CPU object so we know how to constrain our
890         // search for related Config objects.
891         crow::connections::systemBus->async_method_call(
892             [asyncResp, cpuName](const boost::system::error_code ec,
893                                  const std::vector<std::string>& objects) {
894                 if (ec)
895                 {
896                     BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
897                                        << ec.message();
898                     messages::internalError(asyncResp->res);
899                     return;
900                 }
901 
902                 for (const std::string& object : objects)
903                 {
904                     if (!boost::ends_with(object, cpuName))
905                     {
906                         continue;
907                     }
908 
909                     // Not expected that there will be multiple matching CPU
910                     // objects, but if there are just use the first one.
911 
912                     // Use the common search routine to construct the Collection
913                     // of all Config objects under this CPU.
914                     collection_util::getCollectionMembers(
915                         asyncResp,
916                         "/redfish/v1/Systems/system/Processors/" + cpuName +
917                             "/OperatingConfigs",
918                         {"xyz.openbmc_project.Inventory.Item.Cpu."
919                          "OperatingConfig"},
920                         object.c_str());
921                     return;
922                 }
923             },
924             "xyz.openbmc_project.ObjectMapper",
925             "/xyz/openbmc_project/object_mapper",
926             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
927             "/xyz/openbmc_project/inventory", 0,
928             std::array<const char*, 1>{"xyz.openbmc_project.Control.Processor."
929                                        "CurrentOperatingConfig"});
930     }
931 };
932 
933 class OperatingConfig : public Node
934 {
935   public:
936     OperatingConfig(App& app) :
937         Node(app,
938              "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/"
939              "<str>/",
940              std::string(), std::string())
941     {
942         // Defined by Redfish spec privilege registry
943         entityPrivileges = {
944             {boost::beast::http::verb::get, {{"Login"}}},
945             {boost::beast::http::verb::head, {{"Login"}}},
946             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
947             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
948             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
949             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
950     }
951 
952   private:
953     void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
954                const crow::Request& req,
955                const std::vector<std::string>& params) override
956     {
957         if (params.size() != 2)
958         {
959             messages::internalError(asyncResp->res);
960             return;
961         }
962 
963         const std::string& cpuName = params[0];
964         const std::string& configName = params[1];
965 
966         // Ask for all objects implementing OperatingConfig so we can search for
967         // one with a matching name
968         crow::connections::systemBus->async_method_call(
969             [asyncResp, cpuName, configName,
970              reqUrl{req.url}](boost::system::error_code ec,
971                               const MapperGetSubTreeResponse& subtree) {
972                 if (ec)
973                 {
974                     BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
975                                        << ec.message();
976                     messages::internalError(asyncResp->res);
977                     return;
978                 }
979                 const std::string expectedEnding = cpuName + '/' + configName;
980                 for (const auto& [objectPath, serviceMap] : subtree)
981                 {
982                     // Ignore any configs without matching cpuX/configY
983                     if (!boost::ends_with(objectPath, expectedEnding) ||
984                         serviceMap.empty())
985                     {
986                         continue;
987                     }
988 
989                     nlohmann::json& json = asyncResp->res.jsonValue;
990                     json["@odata.type"] =
991                         "#OperatingConfig.v1_0_0.OperatingConfig";
992                     json["@odata.id"] = reqUrl;
993                     json["Name"] = "Processor Profile";
994                     json["Id"] = configName;
995 
996                     // Just use the first implementation of the object - not
997                     // expected that there would be multiple matching services
998                     getOperatingConfigData(asyncResp, serviceMap.begin()->first,
999                                            objectPath);
1000                     return;
1001                 }
1002                 messages::resourceNotFound(asyncResp->res, "OperatingConfig",
1003                                            configName);
1004             },
1005             "xyz.openbmc_project.ObjectMapper",
1006             "/xyz/openbmc_project/object_mapper",
1007             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1008             "/xyz/openbmc_project/inventory", 0,
1009             std::array<const char*, 1>{
1010                 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"});
1011     }
1012 };
1013 
1014 class ProcessorCollection : public Node
1015 {
1016   public:
1017     /*
1018      * Default Constructor
1019      */
1020     ProcessorCollection(App& app) :
1021         Node(app, "/redfish/v1/Systems/system/Processors/")
1022     {
1023         entityPrivileges = {
1024             {boost::beast::http::verb::get, {{"Login"}}},
1025             {boost::beast::http::verb::head, {{"Login"}}},
1026             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1027             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1028             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1029             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1030     }
1031 
1032   private:
1033     /**
1034      * Functions triggers appropriate requests on DBus
1035      */
1036     void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1037                const crow::Request&, const std::vector<std::string>&) override
1038     {
1039         asyncResp->res.jsonValue["@odata.type"] =
1040             "#ProcessorCollection.ProcessorCollection";
1041         asyncResp->res.jsonValue["Name"] = "Processor Collection";
1042 
1043         asyncResp->res.jsonValue["@odata.id"] =
1044             "/redfish/v1/Systems/system/Processors";
1045 
1046         collection_util::getCollectionMembers(
1047             asyncResp, "/redfish/v1/Systems/system/Processors",
1048             std::vector<const char*>(processorInterfaces.begin(),
1049                                      processorInterfaces.end()));
1050     }
1051 };
1052 
1053 class Processor : public Node
1054 {
1055   public:
1056     /*
1057      * Default Constructor
1058      */
1059     Processor(App& app) :
1060         Node(app, "/redfish/v1/Systems/system/Processors/<str>/", std::string())
1061     {
1062         entityPrivileges = {
1063             {boost::beast::http::verb::get, {{"Login"}}},
1064             {boost::beast::http::verb::head, {{"Login"}}},
1065             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1066             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1067             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1068             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1069     }
1070 
1071   private:
1072     /**
1073      * Functions triggers appropriate requests on DBus
1074      */
1075     void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1076                const crow::Request&,
1077                const std::vector<std::string>& params) override
1078     {
1079         // Check if there is required param, truly entering this shall be
1080         // impossible
1081         if (params.size() != 1)
1082         {
1083             messages::internalError(asyncResp->res);
1084             return;
1085         }
1086         const std::string& processorId = params[0];
1087         asyncResp->res.jsonValue["@odata.type"] =
1088             "#Processor.v1_11_0.Processor";
1089         asyncResp->res.jsonValue["@odata.id"] =
1090             "/redfish/v1/Systems/system/Processors/" + processorId;
1091 
1092         getProcessorObject(asyncResp, processorId, getProcessorData);
1093     }
1094 };
1095 
1096 } // namespace redfish
1097