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