blob: f816566fe6a45793fd599680d05ca7847a6ea4f6 [file] [log] [blame]
Gunnar Millsac6a4442020-10-14 14:55:29 -05001/*
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 "health.hpp"
19
20#include <boost/container/flat_map.hpp>
21#include <node.hpp>
Jonathan Domandba0c292020-12-02 15:34:13 -080022#include <sdbusplus/message/native_types.hpp>
23#include <sdbusplus/utility/dedup_variant.hpp>
Gunnar Millsac6a4442020-10-14 14:55:29 -050024#include <utils/collection.hpp>
25#include <utils/json_utils.hpp>
26
27namespace redfish
28{
29
30using InterfacesProperties = boost::container::flat_map<
31 std::string,
32 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>>;
33
Jonathan Domanc9514482021-02-24 09:20:51 -080034// Map of service name to list of interfaces
35using MapperServiceMap =
36 std::vector<std::pair<std::string, std::vector<std::string>>>;
37
38// Map of object paths to MapperServiceMaps
39using MapperGetSubTreeResponse =
40 std::vector<std::pair<std::string, MapperServiceMap>>;
41
42// Interfaces which imply a D-Bus object represents a Processor
43constexpr std::array<const char*, 2> processorInterfaces = {
44 "xyz.openbmc_project.Inventory.Item.Cpu",
45 "xyz.openbmc_project.Inventory.Item.Accelerator"};
Jonathan Doman2bab9832020-12-02 15:27:40 -080046
Gunnar Millsac6a4442020-10-14 14:55:29 -050047inline void
48 getCpuDataByInterface(const std::shared_ptr<AsyncResp>& aResp,
49 const InterfacesProperties& cpuInterfacesProperties)
50{
51 BMCWEB_LOG_DEBUG << "Get CPU resources by interface.";
52
53 // Added for future purpose. Once present and functional attributes added
54 // in busctl call, need to add actual logic to fetch original values.
55 bool present = false;
56 const bool functional = true;
57 auto health = std::make_shared<HealthPopulate>(aResp);
58 health->populate();
59
60 for (const auto& interface : cpuInterfacesProperties)
61 {
62 for (const auto& property : interface.second)
63 {
64 if (property.first == "CoreCount")
65 {
66 const uint16_t* coresCount =
67 std::get_if<uint16_t>(&property.second);
68 if (coresCount == nullptr)
69 {
70 // Important property not in desired type
71 messages::internalError(aResp->res);
72 return;
73 }
74 if (*coresCount == 0)
75 {
76 // Slot is not populated, set status end return
77 aResp->res.jsonValue["Status"]["State"] = "Absent";
78 // HTTP Code will be set up automatically, just return
79 return;
80 }
81 aResp->res.jsonValue["Status"]["State"] = "Enabled";
82 present = true;
83 aResp->res.jsonValue["TotalCores"] = *coresCount;
84 }
Jonathan Domandc3fa662020-10-26 23:10:24 -070085 else if (property.first == "MaxSpeedInMhz")
86 {
87 const uint32_t* value = std::get_if<uint32_t>(&property.second);
88 if (value != nullptr)
89 {
90 aResp->res.jsonValue["MaxSpeedMHz"] = *value;
91 }
92 }
Gunnar Millsac6a4442020-10-14 14:55:29 -050093 else if (property.first == "Socket")
94 {
95 const std::string* value =
96 std::get_if<std::string>(&property.second);
97 if (value != nullptr)
98 {
99 aResp->res.jsonValue["Socket"] = *value;
100 }
101 }
102 else if (property.first == "ThreadCount")
103 {
Jonathan Domandc3fa662020-10-26 23:10:24 -0700104 const uint16_t* value = std::get_if<uint16_t>(&property.second);
Gunnar Millsac6a4442020-10-14 14:55:29 -0500105 if (value != nullptr)
106 {
107 aResp->res.jsonValue["TotalThreads"] = *value;
108 }
109 }
110 else if (property.first == "Family")
111 {
112 const std::string* value =
113 std::get_if<std::string>(&property.second);
114 if (value != nullptr)
115 {
116 aResp->res.jsonValue["ProcessorId"]["EffectiveFamily"] =
117 *value;
118 }
119 }
120 else if (property.first == "Id")
121 {
122 const uint64_t* value = std::get_if<uint64_t>(&property.second);
123 if (value != nullptr && *value != 0)
124 {
125 present = true;
126 aResp->res
127 .jsonValue["ProcessorId"]["IdentificationRegisters"] =
128 boost::lexical_cast<std::string>(*value);
129 }
130 }
131 }
132 }
133
134 if (present == false)
135 {
136 aResp->res.jsonValue["Status"]["State"] = "Absent";
137 aResp->res.jsonValue["Status"]["Health"] = "OK";
138 }
139 else
140 {
141 aResp->res.jsonValue["Status"]["State"] = "Enabled";
142 if (functional)
143 {
144 aResp->res.jsonValue["Status"]["Health"] = "OK";
145 }
146 else
147 {
148 aResp->res.jsonValue["Status"]["Health"] = "Critical";
149 }
150 }
151
152 return;
153}
154
155inline void getCpuDataByService(std::shared_ptr<AsyncResp> aResp,
156 const std::string& cpuId,
157 const std::string& service,
158 const std::string& objPath)
159{
160 BMCWEB_LOG_DEBUG << "Get available system cpu resources by service.";
161
162 crow::connections::systemBus->async_method_call(
163 [cpuId, service, objPath, aResp{std::move(aResp)}](
164 const boost::system::error_code ec,
165 const dbus::utility::ManagedObjectType& dbusData) {
166 if (ec)
167 {
168 BMCWEB_LOG_DEBUG << "DBUS response error";
169 messages::internalError(aResp->res);
170 return;
171 }
172 aResp->res.jsonValue["Id"] = cpuId;
173 aResp->res.jsonValue["Name"] = "Processor";
174 aResp->res.jsonValue["ProcessorType"] = "CPU";
175
176 bool slotPresent = false;
177 std::string corePath = objPath + "/core";
178 size_t totalCores = 0;
179 for (const auto& object : dbusData)
180 {
181 if (object.first.str == objPath)
182 {
183 getCpuDataByInterface(aResp, object.second);
184 }
185 else if (boost::starts_with(object.first.str, corePath))
186 {
187 for (const auto& interface : object.second)
188 {
189 if (interface.first ==
190 "xyz.openbmc_project.Inventory.Item")
191 {
192 for (const auto& property : interface.second)
193 {
194 if (property.first == "Present")
195 {
196 const bool* present =
197 std::get_if<bool>(&property.second);
198 if (present != nullptr)
199 {
200 if (*present == true)
201 {
202 slotPresent = true;
203 totalCores++;
204 }
205 }
206 }
207 }
208 }
209 }
210 }
211 }
212 // In getCpuDataByInterface(), state and health are set
213 // based on the present and functional status. If core
214 // count is zero, then it has a higher precedence.
215 if (slotPresent)
216 {
217 if (totalCores == 0)
218 {
219 // Slot is not populated, set status end return
220 aResp->res.jsonValue["Status"]["State"] = "Absent";
221 aResp->res.jsonValue["Status"]["Health"] = "OK";
222 }
223 aResp->res.jsonValue["TotalCores"] = totalCores;
224 }
225 return;
226 },
227 service, "/xyz/openbmc_project/inventory",
228 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
229}
230
231inline void getCpuAssetData(std::shared_ptr<AsyncResp> aResp,
232 const std::string& service,
233 const std::string& objPath)
234{
235 BMCWEB_LOG_DEBUG << "Get Cpu Asset Data";
236 crow::connections::systemBus->async_method_call(
237 [objPath, aResp{std::move(aResp)}](
238 const boost::system::error_code ec,
239 const boost::container::flat_map<
240 std::string, std::variant<std::string, uint32_t, uint16_t,
241 bool>>& properties) {
242 if (ec)
243 {
244 BMCWEB_LOG_DEBUG << "DBUS response error";
245 messages::internalError(aResp->res);
246 return;
247 }
248
249 for (const auto& property : properties)
250 {
251 if (property.first == "SerialNumber")
252 {
253 const std::string* sn =
254 std::get_if<std::string>(&property.second);
255 if (sn != nullptr && !sn->empty())
256 {
257 aResp->res.jsonValue["SerialNumber"] = *sn;
258 }
259 }
260 else if (property.first == "Model")
261 {
262 const std::string* model =
263 std::get_if<std::string>(&property.second);
264 if (model != nullptr && !model->empty())
265 {
266 aResp->res.jsonValue["Model"] = *model;
267 }
268 }
269 else if (property.first == "Manufacturer")
270 {
271
272 const std::string* mfg =
273 std::get_if<std::string>(&property.second);
274 if (mfg != nullptr)
275 {
276 aResp->res.jsonValue["Manufacturer"] = *mfg;
277
278 // Otherwise would be unexpected.
279 if (mfg->find("Intel") != std::string::npos)
280 {
281 aResp->res.jsonValue["ProcessorArchitecture"] =
282 "x86";
283 aResp->res.jsonValue["InstructionSet"] = "x86-64";
284 }
285 else if (mfg->find("IBM") != std::string::npos)
286 {
287 aResp->res.jsonValue["ProcessorArchitecture"] =
288 "Power";
289 aResp->res.jsonValue["InstructionSet"] = "PowerISA";
290 }
291 }
292 }
SunnySrivastava1984cba4f442021-01-07 12:48:16 -0600293 else if (property.first == "PartNumber")
294 {
295 const std::string* partNumber =
296 std::get_if<std::string>(&property.second);
297
298 if (partNumber == nullptr)
299 {
300 messages::internalError(aResp->res);
301 return;
302 }
303 aResp->res.jsonValue["PartNumber"] = *partNumber;
304 }
305 else if (property.first == "SparePartNumber")
306 {
307 const std::string* sparePartNumber =
308 std::get_if<std::string>(&property.second);
309
310 if (sparePartNumber == nullptr)
311 {
312 messages::internalError(aResp->res);
313 return;
314 }
315 aResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
316 }
Gunnar Millsac6a4442020-10-14 14:55:29 -0500317 }
318 },
319 service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
320 "xyz.openbmc_project.Inventory.Decorator.Asset");
321}
322
323inline void getCpuRevisionData(std::shared_ptr<AsyncResp> aResp,
324 const std::string& service,
325 const std::string& objPath)
326{
327 BMCWEB_LOG_DEBUG << "Get Cpu Revision Data";
328 crow::connections::systemBus->async_method_call(
329 [objPath, aResp{std::move(aResp)}](
330 const boost::system::error_code ec,
331 const boost::container::flat_map<
332 std::string, std::variant<std::string, uint32_t, uint16_t,
333 bool>>& properties) {
334 if (ec)
335 {
336 BMCWEB_LOG_DEBUG << "DBUS response error";
337 messages::internalError(aResp->res);
338 return;
339 }
340
341 for (const auto& property : properties)
342 {
343 if (property.first == "Version")
344 {
345 const std::string* ver =
346 std::get_if<std::string>(&property.second);
347 if (ver != nullptr)
348 {
349 aResp->res.jsonValue["Version"] = *ver;
350 }
351 break;
352 }
353 }
354 },
355 service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
356 "xyz.openbmc_project.Inventory.Decorator.Revision");
357}
358
359inline void getAcceleratorDataByService(std::shared_ptr<AsyncResp> aResp,
360 const std::string& acclrtrId,
361 const std::string& service,
362 const std::string& objPath)
363{
364 BMCWEB_LOG_DEBUG
365 << "Get available system Accelerator resources by service.";
366 crow::connections::systemBus->async_method_call(
367 [acclrtrId, aResp{std::move(aResp)}](
368 const boost::system::error_code ec,
369 const boost::container::flat_map<
370 std::string, std::variant<std::string, uint32_t, uint16_t,
371 bool>>& properties) {
372 if (ec)
373 {
374 BMCWEB_LOG_DEBUG << "DBUS response error";
375 messages::internalError(aResp->res);
376 return;
377 }
378 aResp->res.jsonValue["Id"] = acclrtrId;
379 aResp->res.jsonValue["Name"] = "Processor";
380 const bool* accPresent = nullptr;
381 const bool* accFunctional = nullptr;
382
383 for (const auto& property : properties)
384 {
385 if (property.first == "Functional")
386 {
387 accFunctional = std::get_if<bool>(&property.second);
388 }
389 else if (property.first == "Present")
390 {
391 accPresent = std::get_if<bool>(&property.second);
392 }
393 }
394
395 std::string state = "Enabled";
396 std::string health = "OK";
397
398 if (accPresent != nullptr && *accPresent == false)
399 {
400 state = "Absent";
401 }
402
403 if ((accFunctional != nullptr) && (*accFunctional == false))
404 {
405 if (state == "Enabled")
406 {
407 health = "Critical";
408 }
409 }
410
411 aResp->res.jsonValue["Status"]["State"] = state;
412 aResp->res.jsonValue["Status"]["Health"] = health;
413 aResp->res.jsonValue["ProcessorType"] = "Accelerator";
414 },
415 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", "");
416}
417
Jonathan Domandba0c292020-12-02 15:34:13 -0800418// OperatingConfig D-Bus Types
419using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>;
420using BaseSpeedPrioritySettingsProperty =
421 std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>;
422// uint32_t and size_t may or may not be the same type, requiring a dedup'd
423// variant
424using OperatingConfigProperties = std::vector<std::pair<
425 std::string,
426 sdbusplus::utility::dedup_variant<uint32_t, size_t, TurboProfileProperty,
427 BaseSpeedPrioritySettingsProperty>>>;
428
429/**
430 * Fill out the HighSpeedCoreIDs in a Processor resource from the given
431 * OperatingConfig D-Bus property.
432 *
433 * @param[in,out] aResp Async HTTP response.
434 * @param[in] baseSpeedSettings Full list of base speed priority groups,
435 * to use to determine the list of high
436 * speed cores.
437 */
438inline void highSpeedCoreIdsHandler(
439 const std::shared_ptr<AsyncResp>& aResp,
440 const BaseSpeedPrioritySettingsProperty& baseSpeedSettings)
441{
442 // The D-Bus property does not indicate which bucket is the "high
443 // priority" group, so let's discern that by looking for the one with
444 // highest base frequency.
445 auto highPriorityGroup = baseSpeedSettings.cend();
446 uint32_t highestBaseSpeed = 0;
447 for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend();
448 ++it)
449 {
450 const uint32_t baseFreq = std::get<uint32_t>(*it);
451 if (baseFreq > highestBaseSpeed)
452 {
453 highestBaseSpeed = baseFreq;
454 highPriorityGroup = it;
455 }
456 }
457
458 nlohmann::json& jsonCoreIds = aResp->res.jsonValue["HighSpeedCoreIDs"];
459 jsonCoreIds = nlohmann::json::array();
460
461 // There may not be any entries in the D-Bus property, so only populate
462 // if there was actually something there.
463 if (highPriorityGroup != baseSpeedSettings.cend())
464 {
465 jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup);
466 }
467}
468
469/**
470 * Fill out OperatingConfig related items in a Processor resource by requesting
471 * data from the given D-Bus object.
472 *
473 * @param[in,out] aResp Async HTTP response.
474 * @param[in] cpuId CPU D-Bus name.
475 * @param[in] service D-Bus service to query.
476 * @param[in] objPath D-Bus object to query.
477 */
478inline void getCpuConfigData(const std::shared_ptr<AsyncResp>& aResp,
479 const std::string& cpuId,
480 const std::string& service,
481 const std::string& objPath)
482{
483 BMCWEB_LOG_INFO << "Getting CPU operating configs for " << cpuId;
484
485 // First, GetAll CurrentOperatingConfig properties on the object
486 crow::connections::systemBus->async_method_call(
487 [aResp, cpuId, service](
488 const boost::system::error_code ec,
489 const std::vector<
490 std::pair<std::string,
491 std::variant<sdbusplus::message::object_path, bool>>>&
492 properties) {
493 if (ec)
494 {
495 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
496 << ec.message();
497 messages::internalError(aResp->res);
498 return;
499 }
500
501 nlohmann::json& json = aResp->res.jsonValue;
502
503 for (const auto& [dbusPropName, variantVal] : properties)
504 {
505 if (dbusPropName == "AppliedConfig")
506 {
507 const sdbusplus::message::object_path* dbusPathWrapper =
508 std::get_if<sdbusplus::message::object_path>(
509 &variantVal);
510 if (dbusPathWrapper == nullptr)
511 {
512 continue;
513 }
514
515 const std::string& dbusPath = dbusPathWrapper->str;
516 std::string uri = "/redfish/v1/Systems/system/Processors/" +
517 cpuId + "/OperatingConfigs";
518 json["OperatingConfigs"] = {{"@odata.id", uri}};
519
520 // Reuse the D-Bus config object name for the Redfish
521 // URI
522 size_t baseNamePos = dbusPath.rfind('/');
523 if (baseNamePos == std::string::npos ||
524 baseNamePos == (dbusPath.size() - 1))
525 {
526 // If the AppliedConfig was somehow not a valid path,
527 // skip adding any more properties, since everything
528 // else is tied to this applied config.
529 messages::internalError(aResp->res);
530 break;
531 }
532 uri += '/';
533 uri += dbusPath.substr(baseNamePos + 1);
534 json["AppliedOperatingConfig"] = {{"@odata.id", uri}};
535
536 // Once we found the current applied config, queue another
537 // request to read the base freq core ids out of that
538 // config.
539 crow::connections::systemBus->async_method_call(
540 [aResp](
541 const boost::system::error_code ec,
542 const std::variant<
543 BaseSpeedPrioritySettingsProperty>& property) {
544 if (ec)
545 {
546 BMCWEB_LOG_WARNING
547 << "D-Bus Property Get error: " << ec;
548 messages::internalError(aResp->res);
549 return;
550 }
551 auto baseSpeedList =
552 std::get_if<BaseSpeedPrioritySettingsProperty>(
553 &property);
554 if (baseSpeedList != nullptr)
555 {
556 highSpeedCoreIdsHandler(aResp, *baseSpeedList);
557 }
558 },
559 service, dbusPath, "org.freedesktop.DBus.Properties",
560 "Get",
561 "xyz.openbmc_project.Inventory.Item.Cpu."
562 "OperatingConfig",
563 "BaseSpeedPrioritySettings");
564 }
565 else if (dbusPropName == "BaseSpeedPriorityEnabled")
566 {
567 const bool* state = std::get_if<bool>(&variantVal);
568 if (state != nullptr)
569 {
570 json["BaseSpeedPriorityState"] =
571 *state ? "Enabled" : "Disabled";
572 }
573 }
574 }
575 },
576 service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
577 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig");
578}
579
SunnySrivastava1984cba4f442021-01-07 12:48:16 -0600580/**
581 * @brief Fill out location info of a processor by
582 * requesting data from the given D-Bus object.
583 *
584 * @param[in,out] aResp Async HTTP response.
585 * @param[in] service D-Bus service to query.
586 * @param[in] objPath D-Bus object to query.
587 */
588inline void getCpuLocationCode(std::shared_ptr<AsyncResp> aResp,
589 const std::string& service,
590 const std::string& objPath)
591{
592 BMCWEB_LOG_DEBUG << "Get Cpu Location Data";
593 crow::connections::systemBus->async_method_call(
594 [objPath,
595 aResp{std::move(aResp)}](const boost::system::error_code ec,
596 const std::variant<std::string>& property) {
597 if (ec)
598 {
599 BMCWEB_LOG_DEBUG << "DBUS response error";
600 messages::internalError(aResp->res);
601 return;
602 }
603
604 const std::string* value = std::get_if<std::string>(&property);
605
606 if (value == nullptr)
607 {
608 // illegal value
609 BMCWEB_LOG_DEBUG << "Location code value error";
610 messages::internalError(aResp->res);
611 return;
612 }
613
614 aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
615 *value;
616 },
617 service, objPath, "org.freedesktop.DBus.Properties", "Get",
618 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode");
619}
620
Jonathan Domanc9514482021-02-24 09:20:51 -0800621/**
622 * Find the D-Bus object representing the requested Processor, and call the
623 * handler with the results. If matching object is not found, add 404 error to
624 * response and don't call the handler.
625 *
626 * @param[in,out] resp Async HTTP response.
627 * @param[in] processorId Redfish Processor Id.
628 * @param[in] handler Callback to continue processing request upon
629 * successfully finding object.
630 */
631template <typename Handler>
632inline void getProcessorObject(const std::shared_ptr<AsyncResp>& resp,
633 const std::string& processorId,
634 Handler&& handler)
Gunnar Millsac6a4442020-10-14 14:55:29 -0500635{
636 BMCWEB_LOG_DEBUG << "Get available system processor resources.";
637
Jonathan Domanc9514482021-02-24 09:20:51 -0800638 // GetSubTree on all interfaces which provide info about a Processor
Gunnar Millsac6a4442020-10-14 14:55:29 -0500639 crow::connections::systemBus->async_method_call(
Jonathan Domanc9514482021-02-24 09:20:51 -0800640 [resp, processorId, handler = std::forward<Handler>(handler)](
641 boost::system::error_code ec,
642 const MapperGetSubTreeResponse& subtree) mutable {
Gunnar Millsac6a4442020-10-14 14:55:29 -0500643 if (ec)
644 {
Jonathan Domanc9514482021-02-24 09:20:51 -0800645 BMCWEB_LOG_DEBUG << "DBUS response error: " << ec;
646 messages::internalError(resp->res);
Gunnar Millsac6a4442020-10-14 14:55:29 -0500647 return;
648 }
Jonathan Doman2bab9832020-12-02 15:27:40 -0800649 for (const auto& [objectPath, serviceMap] : subtree)
Gunnar Millsac6a4442020-10-14 14:55:29 -0500650 {
Jonathan Doman2bab9832020-12-02 15:27:40 -0800651 // Ignore any objects which don't end with our desired cpu name
652 if (!boost::ends_with(objectPath, processorId))
Gunnar Millsac6a4442020-10-14 14:55:29 -0500653 {
Jonathan Doman2bab9832020-12-02 15:27:40 -0800654 continue;
655 }
656
Jonathan Domanc9514482021-02-24 09:20:51 -0800657 bool found = false;
658 // Filter out objects that don't have the CPU-specific
659 // interfaces to make sure we can return 404 on non-CPUs
660 // (e.g. /redfish/../Processors/dimm0)
Jonathan Doman2bab9832020-12-02 15:27:40 -0800661 for (const auto& [serviceName, interfaceList] : serviceMap)
662 {
Jonathan Domanc9514482021-02-24 09:20:51 -0800663 if (std::find_first_of(
664 interfaceList.begin(), interfaceList.end(),
665 processorInterfaces.begin(),
666 processorInterfaces.end()) != interfaceList.end())
Gunnar Millsac6a4442020-10-14 14:55:29 -0500667 {
Jonathan Domanc9514482021-02-24 09:20:51 -0800668 found = true;
669 break;
Gunnar Millsac6a4442020-10-14 14:55:29 -0500670 }
Gunnar Millsac6a4442020-10-14 14:55:29 -0500671 }
Jonathan Domanc9514482021-02-24 09:20:51 -0800672
673 if (!found)
674 {
675 continue;
676 }
677
678 // Process the first object which does match our cpu name and
679 // required interfaces, and potentially ignore any other
680 // matching objects. Assume all interfaces we want to process
681 // must be on the same object path.
682
683 handler(resp, processorId, objectPath, serviceMap);
Jonathan Doman2bab9832020-12-02 15:27:40 -0800684 return;
Gunnar Millsac6a4442020-10-14 14:55:29 -0500685 }
Jonathan Domanc9514482021-02-24 09:20:51 -0800686 messages::resourceNotFound(resp->res, "Processor", processorId);
Gunnar Millsac6a4442020-10-14 14:55:29 -0500687 },
688 "xyz.openbmc_project.ObjectMapper",
689 "/xyz/openbmc_project/object_mapper",
690 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
Jonathan Doman2bab9832020-12-02 15:27:40 -0800691 "/xyz/openbmc_project/inventory", 0,
SunnySrivastava1984cba4f442021-01-07 12:48:16 -0600692 std::array<const char*, 6>{
Jonathan Doman2bab9832020-12-02 15:27:40 -0800693 "xyz.openbmc_project.Inventory.Decorator.Asset",
694 "xyz.openbmc_project.Inventory.Decorator.Revision",
695 "xyz.openbmc_project.Inventory.Item.Cpu",
SunnySrivastava1984cba4f442021-01-07 12:48:16 -0600696 "xyz.openbmc_project.Inventory.Decorator.LocationCode",
Jonathan Domandba0c292020-12-02 15:34:13 -0800697 "xyz.openbmc_project.Inventory.Item.Accelerator",
698 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"});
Gunnar Millsac6a4442020-10-14 14:55:29 -0500699}
700
Jonathan Domanc9514482021-02-24 09:20:51 -0800701inline void getProcessorData(const std::shared_ptr<AsyncResp>& aResp,
702 const std::string& processorId,
703 const std::string& objectPath,
704 const MapperServiceMap& serviceMap)
705{
706 for (const auto& [serviceName, interfaceList] : serviceMap)
707 {
708 for (const auto& interface : interfaceList)
709 {
710 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
711 {
712 getCpuAssetData(aResp, serviceName, objectPath);
713 }
714 else if (interface == "xyz.openbmc_project.Inventory."
715 "Decorator.Revision")
716 {
717 getCpuRevisionData(aResp, serviceName, objectPath);
718 }
719 else if (interface == "xyz.openbmc_project.Inventory.Item.Cpu")
720 {
721 getCpuDataByService(aResp, processorId, serviceName,
722 objectPath);
723 }
724 else if (interface == "xyz.openbmc_project.Inventory."
725 "Item.Accelerator")
726 {
727 getAcceleratorDataByService(aResp, processorId, serviceName,
728 objectPath);
729 }
730 else if (interface == "xyz.openbmc_project.Control.Processor."
731 "CurrentOperatingConfig")
732 {
733 getCpuConfigData(aResp, processorId, serviceName, objectPath);
734 }
735 else if (interface == "xyz.openbmc_project.Inventory."
736 "Decorator.LocationCode")
737 {
738 getCpuLocationCode(aResp, serviceName, objectPath);
739 }
740 }
741 }
742}
743
Jonathan Domandba0c292020-12-02 15:34:13 -0800744/**
745 * Request all the properties for the given D-Bus object and fill out the
746 * related entries in the Redfish OperatingConfig response.
747 *
748 * @param[in,out] aResp Async HTTP response.
749 * @param[in] service D-Bus service name to query.
750 * @param[in] objPath D-Bus object to query.
751 */
752inline void getOperatingConfigData(const std::shared_ptr<AsyncResp>& aResp,
753 const std::string& service,
754 const std::string& objPath)
755{
756 crow::connections::systemBus->async_method_call(
757 [aResp](boost::system::error_code ec,
758 const OperatingConfigProperties& properties) {
759 if (ec)
760 {
761 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
762 << ec.message();
763 messages::internalError(aResp->res);
764 return;
765 }
766
767 nlohmann::json& json = aResp->res.jsonValue;
768 for (const auto& [key, variant] : properties)
769 {
770 if (key == "AvailableCoreCount")
771 {
772 const size_t* cores = std::get_if<size_t>(&variant);
773 if (cores != nullptr)
774 {
775 json["TotalAvailableCoreCount"] = *cores;
776 }
777 }
778 else if (key == "BaseSpeed")
779 {
780 const uint32_t* speed = std::get_if<uint32_t>(&variant);
781 if (speed != nullptr)
782 {
783 json["BaseSpeedMHz"] = *speed;
784 }
785 }
786 else if (key == "MaxJunctionTemperature")
787 {
788 const uint32_t* temp = std::get_if<uint32_t>(&variant);
789 if (temp != nullptr)
790 {
791 json["MaxJunctionTemperatureCelsius"] = *temp;
792 }
793 }
794 else if (key == "MaxSpeed")
795 {
796 const uint32_t* speed = std::get_if<uint32_t>(&variant);
797 if (speed != nullptr)
798 {
799 json["MaxSpeedMHz"] = *speed;
800 }
801 }
802 else if (key == "PowerLimit")
803 {
804 const uint32_t* tdp = std::get_if<uint32_t>(&variant);
805 if (tdp != nullptr)
806 {
807 json["TDPWatts"] = *tdp;
808 }
809 }
810 else if (key == "TurboProfile")
811 {
812 const auto* turboList =
813 std::get_if<TurboProfileProperty>(&variant);
814 if (turboList == nullptr)
815 {
816 continue;
817 }
818
819 nlohmann::json& turboArray = json["TurboProfile"];
820 turboArray = nlohmann::json::array();
821 for (const auto& [turboSpeed, coreCount] : *turboList)
822 {
823 turboArray.push_back({{"ActiveCoreCount", coreCount},
824 {"MaxSpeedMHz", turboSpeed}});
825 }
826 }
827 else if (key == "BaseSpeedPrioritySettings")
828 {
829 const auto* baseSpeedList =
830 std::get_if<BaseSpeedPrioritySettingsProperty>(
831 &variant);
832 if (baseSpeedList == nullptr)
833 {
834 continue;
835 }
836
837 nlohmann::json& baseSpeedArray =
838 json["BaseSpeedPrioritySettings"];
839 baseSpeedArray = nlohmann::json::array();
840 for (const auto& [baseSpeed, coreList] : *baseSpeedList)
841 {
842 baseSpeedArray.push_back(
843 {{"CoreCount", coreList.size()},
844 {"CoreIDs", coreList},
845 {"BaseSpeedMHz", baseSpeed}});
846 }
847 }
848 }
849 },
850 service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
851 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig");
852}
853
854class OperatingConfigCollection : public Node
855{
856 public:
857 OperatingConfigCollection(App& app) :
858 Node(app,
859 "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/",
860 std::string())
861 {
862 // Defined by Redfish spec privilege registry
863 entityPrivileges = {
864 {boost::beast::http::verb::get, {{"Login"}}},
865 {boost::beast::http::verb::head, {{"Login"}}},
866 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
867 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
868 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
869 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
870 }
871
872 private:
873 void doGet(crow::Response& res, const crow::Request& req,
874 const std::vector<std::string>& params) override
875 {
876 if (params.size() != 1)
877 {
878 messages::internalError(res);
879 res.end();
880 return;
881 }
882
883 const std::string& cpuName = params[0];
884 res.jsonValue["@odata.type"] =
885 "#OperatingConfigCollection.OperatingConfigCollection";
886 res.jsonValue["@odata.id"] = req.url;
887 res.jsonValue["Name"] = "Operating Config Collection";
888
889 auto asyncResp = std::make_shared<AsyncResp>(res);
890
891 // First find the matching CPU object so we know how to constrain our
892 // search for related Config objects.
893 crow::connections::systemBus->async_method_call(
894 [asyncResp, cpuName](const boost::system::error_code ec,
895 const std::vector<std::string>& objects) {
896 if (ec)
897 {
898 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
899 << ec.message();
900 messages::internalError(asyncResp->res);
901 return;
902 }
903
904 for (const std::string& object : objects)
905 {
906 if (!boost::ends_with(object, cpuName))
907 {
908 continue;
909 }
910
911 // Not expected that there will be multiple matching CPU
912 // objects, but if there are just use the first one.
913
914 // Use the common search routine to construct the Collection
915 // of all Config objects under this CPU.
916 collection_util::getCollectionMembers(
917 asyncResp,
918 "/redfish/v1/Systems/system/Processors/" + cpuName +
919 "/OperatingConfigs",
920 {"xyz.openbmc_project.Inventory.Item.Cpu."
921 "OperatingConfig"},
922 object.c_str());
923 return;
924 }
925 },
926 "xyz.openbmc_project.ObjectMapper",
927 "/xyz/openbmc_project/object_mapper",
928 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
929 "/xyz/openbmc_project/inventory", 0,
930 std::array<const char*, 1>{"xyz.openbmc_project.Control.Processor."
931 "CurrentOperatingConfig"});
932 }
933};
934
935class OperatingConfig : public Node
936{
937 public:
938 OperatingConfig(App& app) :
939 Node(app,
940 "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/"
941 "<str>/",
942 std::string(), std::string())
943 {
944 // Defined by Redfish spec privilege registry
945 entityPrivileges = {
946 {boost::beast::http::verb::get, {{"Login"}}},
947 {boost::beast::http::verb::head, {{"Login"}}},
948 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
949 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
950 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
951 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
952 }
953
954 private:
955 void doGet(crow::Response& res, const crow::Request& req,
956 const std::vector<std::string>& params) override
957 {
958 if (params.size() != 2)
959 {
960 messages::internalError(res);
961 res.end();
962 return;
963 }
964
965 const std::string& cpuName = params[0];
966 const std::string& configName = params[1];
967
968 auto asyncResp = std::make_shared<AsyncResp>(res);
969
970 // Ask for all objects implementing OperatingConfig so we can search for
971 // one with a matching name
972 crow::connections::systemBus->async_method_call(
973 [asyncResp, cpuName, configName,
974 reqUrl{req.url}](boost::system::error_code ec,
975 const MapperGetSubTreeResponse& subtree) {
976 if (ec)
977 {
978 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
979 << ec.message();
980 messages::internalError(asyncResp->res);
981 return;
982 }
983 const std::string expectedEnding = cpuName + '/' + configName;
984 for (const auto& [objectPath, serviceMap] : subtree)
985 {
986 // Ignore any configs without matching cpuX/configY
987 if (!boost::ends_with(objectPath, expectedEnding) ||
988 serviceMap.empty())
989 {
990 continue;
991 }
992
993 nlohmann::json& json = asyncResp->res.jsonValue;
994 json["@odata.type"] =
995 "#OperatingConfig.v1_0_0.OperatingConfig";
996 json["@odata.id"] = reqUrl;
997 json["Name"] = "Processor Profile";
998 json["Id"] = configName;
999
1000 // Just use the first implementation of the object - not
1001 // expected that there would be multiple matching services
1002 getOperatingConfigData(asyncResp, serviceMap.begin()->first,
1003 objectPath);
1004 return;
1005 }
1006 messages::resourceNotFound(asyncResp->res, "OperatingConfig",
1007 configName);
1008 },
1009 "xyz.openbmc_project.ObjectMapper",
1010 "/xyz/openbmc_project/object_mapper",
1011 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1012 "/xyz/openbmc_project/inventory", 0,
1013 std::array<const char*, 1>{
1014 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"});
1015 }
1016};
1017
Gunnar Millsac6a4442020-10-14 14:55:29 -05001018class ProcessorCollection : public Node
1019{
1020 public:
1021 /*
1022 * Default Constructor
1023 */
1024 ProcessorCollection(App& app) :
1025 Node(app, "/redfish/v1/Systems/system/Processors/")
1026 {
1027 entityPrivileges = {
1028 {boost::beast::http::verb::get, {{"Login"}}},
1029 {boost::beast::http::verb::head, {{"Login"}}},
1030 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1031 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1032 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1033 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1034 }
1035
1036 private:
1037 /**
1038 * Functions triggers appropriate requests on DBus
1039 */
1040 void doGet(crow::Response& res, const crow::Request&,
1041 const std::vector<std::string>&) override
1042 {
1043 res.jsonValue["@odata.type"] =
1044 "#ProcessorCollection.ProcessorCollection";
1045 res.jsonValue["Name"] = "Processor Collection";
1046
Gunnar Mills9dedf572020-10-14 16:36:35 -05001047 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Processors";
Gunnar Millsac6a4442020-10-14 14:55:29 -05001048 auto asyncResp = std::make_shared<AsyncResp>(res);
1049
Gunnar Mills05030b82020-10-14 15:51:31 -05001050 collection_util::getCollectionMembers(
1051 asyncResp, "/redfish/v1/Systems/system/Processors",
Jonathan Domanc9514482021-02-24 09:20:51 -08001052 std::vector<const char*>(processorInterfaces.begin(),
1053 processorInterfaces.end()));
Gunnar Millsac6a4442020-10-14 14:55:29 -05001054 }
1055};
1056
1057class Processor : public Node
1058{
1059 public:
1060 /*
1061 * Default Constructor
1062 */
1063 Processor(App& app) :
1064 Node(app, "/redfish/v1/Systems/system/Processors/<str>/", std::string())
1065 {
1066 entityPrivileges = {
1067 {boost::beast::http::verb::get, {{"Login"}}},
1068 {boost::beast::http::verb::head, {{"Login"}}},
1069 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1070 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1071 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1072 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1073 }
1074
1075 private:
1076 /**
1077 * Functions triggers appropriate requests on DBus
1078 */
1079 void doGet(crow::Response& res, const crow::Request&,
1080 const std::vector<std::string>& params) override
1081 {
1082 // Check if there is required param, truly entering this shall be
1083 // impossible
1084 if (params.size() != 1)
1085 {
1086 messages::internalError(res);
1087
1088 res.end();
1089 return;
1090 }
1091 const std::string& processorId = params[0];
SunnySrivastava1984cba4f442021-01-07 12:48:16 -06001092 res.jsonValue["@odata.type"] = "#Processor.v1_11_0.Processor";
Gunnar Millsac6a4442020-10-14 14:55:29 -05001093 res.jsonValue["@odata.id"] =
1094 "/redfish/v1/Systems/system/Processors/" + processorId;
1095
1096 auto asyncResp = std::make_shared<AsyncResp>(res);
1097
Jonathan Domanc9514482021-02-24 09:20:51 -08001098 getProcessorObject(asyncResp, processorId, getProcessorData);
Gunnar Millsac6a4442020-10-14 14:55:29 -05001099 }
1100};
1101
1102} // namespace redfish