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