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