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