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