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