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