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