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