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