xref: /openbmc/bmcweb/redfish-core/lib/processor.hpp (revision 3577e44683a5ade8ad02a6418984b56f4ca2bcac)
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  * Request all the properties for the given D-Bus object and fill out the
893  * related entries in the Redfish OperatingConfig response.
894  *
895  * @param[in,out]   asyncResp       Async HTTP response.
896  * @param[in]       service     D-Bus service name to query.
897  * @param[in]       objPath     D-Bus object to query.
898  */
getOperatingConfigData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & objPath)899 inline void getOperatingConfigData(
900     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
901     const std::string& service, const std::string& objPath)
902 {
903     dbus::utility::getAllProperties(
904         service, objPath,
905         "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig",
906         [asyncResp](const boost::system::error_code& ec,
907                     const dbus::utility::DBusPropertiesMap& properties) {
908             if (ec)
909             {
910                 BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message());
911                 messages::internalError(asyncResp->res);
912                 return;
913             }
914 
915             const size_t* availableCoreCount = nullptr;
916             const uint32_t* baseSpeed = nullptr;
917             const uint32_t* maxJunctionTemperature = nullptr;
918             const uint32_t* maxSpeed = nullptr;
919             const uint32_t* powerLimit = nullptr;
920             const TurboProfileProperty* turboProfile = nullptr;
921             const BaseSpeedPrioritySettingsProperty* baseSpeedPrioritySettings =
922                 nullptr;
923 
924             const bool success = sdbusplus::unpackPropertiesNoThrow(
925                 dbus_utils::UnpackErrorPrinter(), properties,
926                 "AvailableCoreCount", availableCoreCount, "BaseSpeed",
927                 baseSpeed, "MaxJunctionTemperature", maxJunctionTemperature,
928                 "MaxSpeed", maxSpeed, "PowerLimit", powerLimit, "TurboProfile",
929                 turboProfile, "BaseSpeedPrioritySettings",
930                 baseSpeedPrioritySettings);
931 
932             if (!success)
933             {
934                 messages::internalError(asyncResp->res);
935                 return;
936             }
937 
938             nlohmann::json& json = asyncResp->res.jsonValue;
939 
940             if (availableCoreCount != nullptr)
941             {
942                 json["TotalAvailableCoreCount"] = *availableCoreCount;
943             }
944 
945             if (baseSpeed != nullptr)
946             {
947                 json["BaseSpeedMHz"] = *baseSpeed;
948             }
949 
950             if (maxJunctionTemperature != nullptr)
951             {
952                 json["MaxJunctionTemperatureCelsius"] = *maxJunctionTemperature;
953             }
954 
955             if (maxSpeed != nullptr)
956             {
957                 json["MaxSpeedMHz"] = *maxSpeed;
958             }
959 
960             if (powerLimit != nullptr)
961             {
962                 json["TDPWatts"] = *powerLimit;
963             }
964 
965             if (turboProfile != nullptr)
966             {
967                 nlohmann::json& turboArray = json["TurboProfile"];
968                 turboArray = nlohmann::json::array();
969                 for (const auto& [turboSpeed, coreCount] : *turboProfile)
970                 {
971                     nlohmann::json::object_t turbo;
972                     turbo["ActiveCoreCount"] = coreCount;
973                     turbo["MaxSpeedMHz"] = turboSpeed;
974                     turboArray.emplace_back(std::move(turbo));
975                 }
976             }
977 
978             if (baseSpeedPrioritySettings != nullptr)
979             {
980                 nlohmann::json& baseSpeedArray =
981                     json["BaseSpeedPrioritySettings"];
982                 baseSpeedArray = nlohmann::json::array();
983                 for (const auto& [baseSpeedMhz, coreList] :
984                      *baseSpeedPrioritySettings)
985                 {
986                     nlohmann::json::object_t speed;
987                     speed["CoreCount"] = coreList.size();
988                     speed["CoreIDs"] = coreList;
989                     speed["BaseSpeedMHz"] = baseSpeedMhz;
990                     baseSpeedArray.emplace_back(std::move(speed));
991                 }
992             }
993         });
994 }
995 
996 /**
997  * Handle the PATCH operation of the AppliedOperatingConfig property. Do basic
998  * validation of the input data, and then set the D-Bus property.
999  *
1000  * @param[in,out]   resp            Async HTTP response.
1001  * @param[in]       processorId     Processor's Id.
1002  * @param[in]       appliedConfigUri    New property value to apply.
1003  * @param[in]       cpuObjectPath   Path of CPU object to modify.
1004  * @param[in]       serviceMap      Service map for CPU object.
1005  */
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)1006 inline void patchAppliedOperatingConfig(
1007     const std::shared_ptr<bmcweb::AsyncResp>& resp,
1008     const std::string& processorId, const std::string& appliedConfigUri,
1009     const std::string& cpuObjectPath,
1010     const dbus::utility::MapperServiceMap& serviceMap)
1011 {
1012     // Check that the property even exists by checking for the interface
1013     const std::string* controlService = nullptr;
1014     for (const auto& [serviceName, interfaceList] : serviceMap)
1015     {
1016         if (std::ranges::find(interfaceList,
1017                               "xyz.openbmc_project.Control.Processor."
1018                               "CurrentOperatingConfig") != interfaceList.end())
1019         {
1020             controlService = &serviceName;
1021             break;
1022         }
1023     }
1024 
1025     if (controlService == nullptr)
1026     {
1027         messages::internalError(resp->res);
1028         return;
1029     }
1030 
1031     // Check that the config URI is a child of the cpu URI being patched.
1032     std::string expectedPrefix(std::format("/redfish/v1/Systems/{}/Processors/",
1033                                            BMCWEB_REDFISH_SYSTEM_URI_NAME));
1034     expectedPrefix += processorId;
1035     expectedPrefix += "/OperatingConfigs/";
1036     if (!appliedConfigUri.starts_with(expectedPrefix) ||
1037         expectedPrefix.size() == appliedConfigUri.size())
1038     {
1039         messages::propertyValueIncorrect(resp->res, "AppliedOperatingConfig",
1040                                          appliedConfigUri);
1041         return;
1042     }
1043 
1044     // Generate the D-Bus path of the OperatingConfig object, by assuming it's a
1045     // direct child of the CPU object.
1046     // Strip the expectedPrefix from the config URI to get the "filename", and
1047     // append to the CPU's path.
1048     std::string configBaseName = appliedConfigUri.substr(expectedPrefix.size());
1049     sdbusplus::message::object_path configPath(cpuObjectPath);
1050     configPath /= configBaseName;
1051 
1052     BMCWEB_LOG_INFO("Setting config to {}", configPath.str);
1053 
1054     // Set the property, with handler to check error responses
1055     setDbusProperty(
1056         resp, "AppliedOperatingConfig", *controlService, cpuObjectPath,
1057         "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig",
1058         "AppliedConfig", configPath);
1059 }
1060 
handleProcessorHead(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string &,const std::string &)1061 inline void handleProcessorHead(
1062     crow::App& app, const crow::Request& req,
1063     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1064     const std::string& /* systemName */, const std::string& /* processorId */)
1065 {
1066     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1067     {
1068         return;
1069     }
1070     asyncResp->res.addHeader(
1071         boost::beast::http::field::link,
1072         "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby");
1073 }
1074 
handleProcessorCollectionHead(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string &)1075 inline void handleProcessorCollectionHead(
1076     crow::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     asyncResp->res.addHeader(
1085         boost::beast::http::field::link,
1086         "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby");
1087 }
1088 
handleProcessorGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & processorId)1089 inline void handleProcessorGet(
1090     App& app, const crow::Request& req,
1091     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1092     const std::string& systemName, const std::string& processorId)
1093 {
1094     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1095     {
1096         return;
1097     }
1098     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1099     {
1100         // Option currently returns no systems.  TBD
1101         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1102                                    systemName);
1103         return;
1104     }
1105     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1106     {
1107         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1108                                    systemName);
1109         return;
1110     }
1111 
1112     getProcessorObject(
1113         asyncResp, processorId,
1114         std::bind_front(getProcessorData, asyncResp, processorId));
1115 }
1116 
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)1117 inline void doPatchProcessor(
1118     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1119     const std::string& processorId,
1120     const std::optional<std::string>& appliedConfigUri,
1121     std::optional<bool> locationIndicatorActive, const std::string& objectPath,
1122     const dbus::utility::MapperServiceMap& serviceMap)
1123 {
1124     if (appliedConfigUri)
1125     {
1126         patchAppliedOperatingConfig(asyncResp, processorId, *appliedConfigUri,
1127                                     objectPath, serviceMap);
1128     }
1129 
1130     if (locationIndicatorActive)
1131     {
1132         // Utility function handles reporting errors
1133         setLocationIndicatorActive(asyncResp, objectPath,
1134                                    *locationIndicatorActive);
1135     }
1136 }
1137 
handleProcessorPatch(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & processorId)1138 inline void handleProcessorPatch(
1139     App& app, const crow::Request& req,
1140     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1141     const std::string& systemName, const std::string& processorId)
1142 {
1143     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1144     {
1145         return;
1146     }
1147     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1148     {
1149         // Option currently returns no systems.  TBD
1150         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1151                                    systemName);
1152         return;
1153     }
1154     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1155     {
1156         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1157                                    systemName);
1158         return;
1159     }
1160 
1161     std::optional<std::string> appliedConfigUri;
1162     std::optional<bool> locationIndicatorActive;
1163     if (!json_util::readJsonPatch(
1164             req, asyncResp->res,                                  //
1165             "AppliedOperatingConfig/@odata.id", appliedConfigUri, //
1166             "LocationIndicatorActive", locationIndicatorActive    //
1167             ))
1168     {
1169         return;
1170     }
1171 
1172     // Check for 404 and find matching D-Bus object, then run
1173     // property patch handlers if that all succeeds.
1174     getProcessorObject(
1175         asyncResp, processorId,
1176         std::bind_front(doPatchProcessor, asyncResp, processorId,
1177                         appliedConfigUri, locationIndicatorActive));
1178 }
1179 
requestRoutesOperatingConfigCollection(App & app)1180 inline void requestRoutesOperatingConfigCollection(App& app)
1181 {
1182     BMCWEB_ROUTE(app,
1183                  "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/")
1184         .privileges(redfish::privileges::getOperatingConfigCollection)
1185         .methods(
1186             boost::beast::http::verb::
1187                 get)([&app](const crow::Request& req,
1188                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1189                             const std::string& systemName,
1190                             const std::string& cpuName) {
1191             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1192             {
1193                 return;
1194             }
1195 
1196             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1197             {
1198                 // Option currently returns no systems.  TBD
1199                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1200                                            systemName);
1201                 return;
1202             }
1203 
1204             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1205             {
1206                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1207                                            systemName);
1208                 return;
1209             }
1210             asyncResp->res.jsonValue["@odata.type"] =
1211                 "#OperatingConfigCollection.OperatingConfigCollection";
1212             asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1213                 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs",
1214                 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName);
1215             asyncResp->res.jsonValue["Name"] = "Operating Config Collection";
1216 
1217             // First find the matching CPU object so we know how to
1218             // constrain our search for related Config objects.
1219             const std::array<std::string_view, 1> interfaces = {
1220                 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"};
1221             dbus::utility::getSubTreePaths(
1222                 "/xyz/openbmc_project/inventory", 0, interfaces,
1223                 [asyncResp,
1224                  cpuName](const boost::system::error_code& ec,
1225                           const dbus::utility::MapperGetSubTreePathsResponse&
1226                               objects) {
1227                     if (ec)
1228                     {
1229                         BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec,
1230                                            ec.message());
1231                         messages::internalError(asyncResp->res);
1232                         return;
1233                     }
1234 
1235                     for (const std::string& object : objects)
1236                     {
1237                         if (!object.ends_with(cpuName))
1238                         {
1239                             continue;
1240                         }
1241 
1242                         // Not expected that there will be multiple matching
1243                         // CPU objects, but if there are just use the first
1244                         // one.
1245 
1246                         // Use the common search routine to construct the
1247                         // Collection of all Config objects under this CPU.
1248                         constexpr std::array<std::string_view, 1> interface{
1249                             "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"};
1250                         collection_util::getCollectionMembers(
1251                             asyncResp,
1252                             boost::urls::format(
1253                                 "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs",
1254                                 BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName),
1255                             interface, object);
1256                         return;
1257                     }
1258                 });
1259         });
1260 }
1261 
requestRoutesOperatingConfig(App & app)1262 inline void requestRoutesOperatingConfig(App& app)
1263 {
1264     BMCWEB_ROUTE(
1265         app,
1266         "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/<str>/")
1267         .privileges(redfish::privileges::getOperatingConfig)
1268         .methods(
1269             boost::beast::http::verb::
1270                 get)([&app](const crow::Request& req,
1271                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1272                             const std::string& systemName,
1273                             const std::string& cpuName,
1274                             const std::string& configName) {
1275             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1276             {
1277                 return;
1278             }
1279             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1280             {
1281                 // Option currently returns no systems.  TBD
1282                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1283                                            systemName);
1284                 return;
1285             }
1286 
1287             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1288             {
1289                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1290                                            systemName);
1291                 return;
1292             }
1293             // Ask for all objects implementing OperatingConfig so we can search
1294             // for one with a matching name
1295             constexpr std::array<std::string_view, 1> interfaces = {
1296                 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"};
1297             dbus::utility::getSubTree(
1298                 "/xyz/openbmc_project/inventory", 0, interfaces,
1299                 [asyncResp, cpuName, configName](
1300                     const boost::system::error_code& ec,
1301                     const dbus::utility::MapperGetSubTreeResponse& subtree) {
1302                     if (ec)
1303                     {
1304                         BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec,
1305                                            ec.message());
1306                         messages::internalError(asyncResp->res);
1307                         return;
1308                     }
1309                     const std::string expectedEnding =
1310                         cpuName + '/' + configName;
1311                     for (const auto& [objectPath, serviceMap] : subtree)
1312                     {
1313                         // Ignore any configs without matching cpuX/configY
1314                         if (!objectPath.ends_with(expectedEnding) ||
1315                             serviceMap.empty())
1316                         {
1317                             continue;
1318                         }
1319 
1320                         nlohmann::json& json = asyncResp->res.jsonValue;
1321                         json["@odata.type"] =
1322                             "#OperatingConfig.v1_0_0.OperatingConfig";
1323                         json["@odata.id"] = boost::urls::format(
1324                             "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}",
1325                             BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName,
1326                             configName);
1327                         json["Name"] = "Processor Profile";
1328                         json["Id"] = configName;
1329 
1330                         // Just use the first implementation of the object - not
1331                         // expected that there would be multiple matching
1332                         // services
1333                         getOperatingConfigData(
1334                             asyncResp, serviceMap.begin()->first, objectPath);
1335                         return;
1336                     }
1337                     messages::resourceNotFound(asyncResp->res,
1338                                                "OperatingConfig", configName);
1339                 });
1340         });
1341 }
1342 
requestRoutesProcessorCollection(App & app)1343 inline void requestRoutesProcessorCollection(App& app)
1344 {
1345     /**
1346      * Functions triggers appropriate requests on DBus
1347      */
1348     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/")
1349         .privileges(redfish::privileges::headProcessorCollection)
1350         .methods(boost::beast::http::verb::head)(
1351             std::bind_front(handleProcessorCollectionHead, std::ref(app)));
1352 
1353     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/")
1354         .privileges(redfish::privileges::getProcessorCollection)
1355         .methods(
1356             boost::beast::http::verb::
1357                 get)([&app](const crow::Request& req,
1358                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1359                             const std::string& systemName) {
1360             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1361             {
1362                 return;
1363             }
1364             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1365             {
1366                 // Option currently returns no systems.  TBD
1367                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1368                                            systemName);
1369                 return;
1370             }
1371 
1372             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1373             {
1374                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1375                                            systemName);
1376                 return;
1377             }
1378 
1379             asyncResp->res.addHeader(
1380                 boost::beast::http::field::link,
1381                 "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby");
1382 
1383             asyncResp->res.jsonValue["@odata.type"] =
1384                 "#ProcessorCollection.ProcessorCollection";
1385             asyncResp->res.jsonValue["Name"] = "Processor Collection";
1386 
1387             asyncResp->res.jsonValue["@odata.id"] =
1388                 std::format("/redfish/v1/Systems/{}/Processors",
1389                             BMCWEB_REDFISH_SYSTEM_URI_NAME);
1390 
1391             collection_util::getCollectionMembers(
1392                 asyncResp,
1393                 boost::urls::format("/redfish/v1/Systems/{}/Processors",
1394                                     BMCWEB_REDFISH_SYSTEM_URI_NAME),
1395                 processorInterfaces, "/xyz/openbmc_project/inventory");
1396         });
1397 }
1398 
requestRoutesProcessor(App & app)1399 inline void requestRoutesProcessor(App& app)
1400 {
1401     /**
1402      * Functions triggers appropriate requests on DBus
1403      */
1404 
1405     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/")
1406         .privileges(redfish::privileges::headProcessor)
1407         .methods(boost::beast::http::verb::head)(
1408             std::bind_front(handleProcessorHead, std::ref(app)));
1409 
1410     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/")
1411         .privileges(redfish::privileges::getProcessor)
1412         .methods(boost::beast::http::verb::get)(
1413             std::bind_front(handleProcessorGet, std::ref(app)));
1414 
1415     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/")
1416         .privileges(redfish::privileges::patchProcessor)
1417         .methods(boost::beast::http::verb::patch)(
1418             std::bind_front(handleProcessorPatch, std::ref(app)));
1419 }
1420 
1421 } // namespace redfish
1422