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