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