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