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