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