blob: efc6c328cbfec97a88f6f39dc809b0b100759e55 [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
zhanghch058d1b46d2021-04-01 11:18:24 +080048 getCpuDataByInterface(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Gunnar Millsac6a4442020-10-14 14:55:29 -050049 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
zhanghch058d1b46d2021-04-01 11:18:24 +0800155inline void getCpuDataByService(std::shared_ptr<bmcweb::AsyncResp> aResp,
Gunnar Millsac6a4442020-10-14 14:55:29 -0500156 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
zhanghch058d1b46d2021-04-01 11:18:24 +0800231inline void getCpuAssetData(std::shared_ptr<bmcweb::AsyncResp> aResp,
Gunnar Millsac6a4442020-10-14 14:55:29 -0500232 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
zhanghch058d1b46d2021-04-01 11:18:24 +0800323inline void getCpuRevisionData(std::shared_ptr<bmcweb::AsyncResp> aResp,
Gunnar Millsac6a4442020-10-14 14:55:29 -0500324 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
zhanghch058d1b46d2021-04-01 11:18:24 +0800359inline void getAcceleratorDataByService(
360 std::shared_ptr<bmcweb::AsyncResp> aResp, const std::string& acclrtrId,
361 const std::string& service, const std::string& objPath)
Gunnar Millsac6a4442020-10-14 14:55:29 -0500362{
363 BMCWEB_LOG_DEBUG
364 << "Get available system Accelerator resources by service.";
365 crow::connections::systemBus->async_method_call(
366 [acclrtrId, aResp{std::move(aResp)}](
367 const boost::system::error_code ec,
368 const boost::container::flat_map<
369 std::string, std::variant<std::string, uint32_t, uint16_t,
370 bool>>& properties) {
371 if (ec)
372 {
373 BMCWEB_LOG_DEBUG << "DBUS response error";
374 messages::internalError(aResp->res);
375 return;
376 }
377 aResp->res.jsonValue["Id"] = acclrtrId;
378 aResp->res.jsonValue["Name"] = "Processor";
379 const bool* accPresent = nullptr;
380 const bool* accFunctional = nullptr;
381
382 for (const auto& property : properties)
383 {
384 if (property.first == "Functional")
385 {
386 accFunctional = std::get_if<bool>(&property.second);
387 }
388 else if (property.first == "Present")
389 {
390 accPresent = std::get_if<bool>(&property.second);
391 }
392 }
393
394 std::string state = "Enabled";
395 std::string health = "OK";
396
397 if (accPresent != nullptr && *accPresent == false)
398 {
399 state = "Absent";
400 }
401
402 if ((accFunctional != nullptr) && (*accFunctional == false))
403 {
404 if (state == "Enabled")
405 {
406 health = "Critical";
407 }
408 }
409
410 aResp->res.jsonValue["Status"]["State"] = state;
411 aResp->res.jsonValue["Status"]["Health"] = health;
412 aResp->res.jsonValue["ProcessorType"] = "Accelerator";
413 },
414 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", "");
415}
416
Jonathan Domandba0c292020-12-02 15:34:13 -0800417// OperatingConfig D-Bus Types
418using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>;
419using BaseSpeedPrioritySettingsProperty =
420 std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>;
421// uint32_t and size_t may or may not be the same type, requiring a dedup'd
422// variant
423using OperatingConfigProperties = std::vector<std::pair<
424 std::string,
425 sdbusplus::utility::dedup_variant<uint32_t, size_t, TurboProfileProperty,
426 BaseSpeedPrioritySettingsProperty>>>;
427
428/**
429 * Fill out the HighSpeedCoreIDs in a Processor resource from the given
430 * OperatingConfig D-Bus property.
431 *
432 * @param[in,out] aResp Async HTTP response.
433 * @param[in] baseSpeedSettings Full list of base speed priority groups,
434 * to use to determine the list of high
435 * speed cores.
436 */
437inline void highSpeedCoreIdsHandler(
zhanghch058d1b46d2021-04-01 11:18:24 +0800438 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Jonathan Domandba0c292020-12-02 15:34:13 -0800439 const BaseSpeedPrioritySettingsProperty& baseSpeedSettings)
440{
441 // The D-Bus property does not indicate which bucket is the "high
442 // priority" group, so let's discern that by looking for the one with
443 // highest base frequency.
444 auto highPriorityGroup = baseSpeedSettings.cend();
445 uint32_t highestBaseSpeed = 0;
446 for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend();
447 ++it)
448 {
449 const uint32_t baseFreq = std::get<uint32_t>(*it);
450 if (baseFreq > highestBaseSpeed)
451 {
452 highestBaseSpeed = baseFreq;
453 highPriorityGroup = it;
454 }
455 }
456
457 nlohmann::json& jsonCoreIds = aResp->res.jsonValue["HighSpeedCoreIDs"];
458 jsonCoreIds = nlohmann::json::array();
459
460 // There may not be any entries in the D-Bus property, so only populate
461 // if there was actually something there.
462 if (highPriorityGroup != baseSpeedSettings.cend())
463 {
464 jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup);
465 }
466}
467
468/**
469 * Fill out OperatingConfig related items in a Processor resource by requesting
470 * data from the given D-Bus object.
471 *
472 * @param[in,out] aResp Async HTTP response.
473 * @param[in] cpuId CPU D-Bus name.
474 * @param[in] service D-Bus service to query.
475 * @param[in] objPath D-Bus object to query.
476 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800477inline void getCpuConfigData(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Jonathan Domandba0c292020-12-02 15:34:13 -0800478 const std::string& cpuId,
479 const std::string& service,
480 const std::string& objPath)
481{
482 BMCWEB_LOG_INFO << "Getting CPU operating configs for " << cpuId;
483
484 // First, GetAll CurrentOperatingConfig properties on the object
485 crow::connections::systemBus->async_method_call(
486 [aResp, cpuId, service](
487 const boost::system::error_code ec,
488 const std::vector<
489 std::pair<std::string,
490 std::variant<sdbusplus::message::object_path, bool>>>&
491 properties) {
492 if (ec)
493 {
494 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
495 << ec.message();
496 messages::internalError(aResp->res);
497 return;
498 }
499
500 nlohmann::json& json = aResp->res.jsonValue;
501
502 for (const auto& [dbusPropName, variantVal] : properties)
503 {
504 if (dbusPropName == "AppliedConfig")
505 {
506 const sdbusplus::message::object_path* dbusPathWrapper =
507 std::get_if<sdbusplus::message::object_path>(
508 &variantVal);
509 if (dbusPathWrapper == nullptr)
510 {
511 continue;
512 }
513
514 const std::string& dbusPath = dbusPathWrapper->str;
515 std::string uri = "/redfish/v1/Systems/system/Processors/" +
516 cpuId + "/OperatingConfigs";
517 json["OperatingConfigs"] = {{"@odata.id", uri}};
518
519 // Reuse the D-Bus config object name for the Redfish
520 // URI
521 size_t baseNamePos = dbusPath.rfind('/');
522 if (baseNamePos == std::string::npos ||
523 baseNamePos == (dbusPath.size() - 1))
524 {
525 // If the AppliedConfig was somehow not a valid path,
526 // skip adding any more properties, since everything
527 // else is tied to this applied config.
528 messages::internalError(aResp->res);
529 break;
530 }
531 uri += '/';
532 uri += dbusPath.substr(baseNamePos + 1);
533 json["AppliedOperatingConfig"] = {{"@odata.id", uri}};
534
535 // Once we found the current applied config, queue another
536 // request to read the base freq core ids out of that
537 // config.
538 crow::connections::systemBus->async_method_call(
539 [aResp](
540 const boost::system::error_code ec,
541 const std::variant<
542 BaseSpeedPrioritySettingsProperty>& property) {
543 if (ec)
544 {
545 BMCWEB_LOG_WARNING
546 << "D-Bus Property Get error: " << ec;
547 messages::internalError(aResp->res);
548 return;
549 }
550 auto baseSpeedList =
551 std::get_if<BaseSpeedPrioritySettingsProperty>(
552 &property);
553 if (baseSpeedList != nullptr)
554 {
555 highSpeedCoreIdsHandler(aResp, *baseSpeedList);
556 }
557 },
558 service, dbusPath, "org.freedesktop.DBus.Properties",
559 "Get",
560 "xyz.openbmc_project.Inventory.Item.Cpu."
561 "OperatingConfig",
562 "BaseSpeedPrioritySettings");
563 }
564 else if (dbusPropName == "BaseSpeedPriorityEnabled")
565 {
566 const bool* state = std::get_if<bool>(&variantVal);
567 if (state != nullptr)
568 {
569 json["BaseSpeedPriorityState"] =
570 *state ? "Enabled" : "Disabled";
571 }
572 }
573 }
574 },
575 service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
576 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig");
577}
578
SunnySrivastava1984cba4f442021-01-07 12:48:16 -0600579/**
580 * @brief Fill out location info of a processor by
581 * requesting data from the given D-Bus object.
582 *
583 * @param[in,out] aResp Async HTTP response.
584 * @param[in] service D-Bus service to query.
585 * @param[in] objPath D-Bus object to query.
586 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800587inline void getCpuLocationCode(std::shared_ptr<bmcweb::AsyncResp> aResp,
SunnySrivastava1984cba4f442021-01-07 12:48:16 -0600588 const std::string& service,
589 const std::string& objPath)
590{
591 BMCWEB_LOG_DEBUG << "Get Cpu Location Data";
592 crow::connections::systemBus->async_method_call(
593 [objPath,
594 aResp{std::move(aResp)}](const boost::system::error_code ec,
595 const std::variant<std::string>& property) {
596 if (ec)
597 {
598 BMCWEB_LOG_DEBUG << "DBUS response error";
599 messages::internalError(aResp->res);
600 return;
601 }
602
603 const std::string* value = std::get_if<std::string>(&property);
604
605 if (value == nullptr)
606 {
607 // illegal value
608 BMCWEB_LOG_DEBUG << "Location code value error";
609 messages::internalError(aResp->res);
610 return;
611 }
612
613 aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
614 *value;
615 },
616 service, objPath, "org.freedesktop.DBus.Properties", "Get",
617 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode");
618}
619
Jonathan Domanc9514482021-02-24 09:20:51 -0800620/**
621 * Find the D-Bus object representing the requested Processor, and call the
622 * handler with the results. If matching object is not found, add 404 error to
623 * response and don't call the handler.
624 *
625 * @param[in,out] resp Async HTTP response.
626 * @param[in] processorId Redfish Processor Id.
627 * @param[in] handler Callback to continue processing request upon
628 * successfully finding object.
629 */
630template <typename Handler>
zhanghch058d1b46d2021-04-01 11:18:24 +0800631inline void getProcessorObject(const std::shared_ptr<bmcweb::AsyncResp>& resp,
Jonathan Domanc9514482021-02-24 09:20:51 -0800632 const std::string& processorId,
633 Handler&& handler)
Gunnar Millsac6a4442020-10-14 14:55:29 -0500634{
635 BMCWEB_LOG_DEBUG << "Get available system processor resources.";
636
Jonathan Domanc9514482021-02-24 09:20:51 -0800637 // GetSubTree on all interfaces which provide info about a Processor
Gunnar Millsac6a4442020-10-14 14:55:29 -0500638 crow::connections::systemBus->async_method_call(
Jonathan Domanc9514482021-02-24 09:20:51 -0800639 [resp, processorId, handler = std::forward<Handler>(handler)](
640 boost::system::error_code ec,
641 const MapperGetSubTreeResponse& subtree) mutable {
Gunnar Millsac6a4442020-10-14 14:55:29 -0500642 if (ec)
643 {
Jonathan Domanc9514482021-02-24 09:20:51 -0800644 BMCWEB_LOG_DEBUG << "DBUS response error: " << ec;
645 messages::internalError(resp->res);
Gunnar Millsac6a4442020-10-14 14:55:29 -0500646 return;
647 }
Jonathan Doman2bab9832020-12-02 15:27:40 -0800648 for (const auto& [objectPath, serviceMap] : subtree)
Gunnar Millsac6a4442020-10-14 14:55:29 -0500649 {
Jonathan Doman2bab9832020-12-02 15:27:40 -0800650 // Ignore any objects which don't end with our desired cpu name
651 if (!boost::ends_with(objectPath, processorId))
Gunnar Millsac6a4442020-10-14 14:55:29 -0500652 {
Jonathan Doman2bab9832020-12-02 15:27:40 -0800653 continue;
654 }
655
Jonathan Domanc9514482021-02-24 09:20:51 -0800656 bool found = false;
657 // Filter out objects that don't have the CPU-specific
658 // interfaces to make sure we can return 404 on non-CPUs
659 // (e.g. /redfish/../Processors/dimm0)
Jonathan Doman2bab9832020-12-02 15:27:40 -0800660 for (const auto& [serviceName, interfaceList] : serviceMap)
661 {
Jonathan Domanc9514482021-02-24 09:20:51 -0800662 if (std::find_first_of(
663 interfaceList.begin(), interfaceList.end(),
664 processorInterfaces.begin(),
665 processorInterfaces.end()) != interfaceList.end())
Gunnar Millsac6a4442020-10-14 14:55:29 -0500666 {
Jonathan Domanc9514482021-02-24 09:20:51 -0800667 found = true;
668 break;
Gunnar Millsac6a4442020-10-14 14:55:29 -0500669 }
Gunnar Millsac6a4442020-10-14 14:55:29 -0500670 }
Jonathan Domanc9514482021-02-24 09:20:51 -0800671
672 if (!found)
673 {
674 continue;
675 }
676
677 // Process the first object which does match our cpu name and
678 // required interfaces, and potentially ignore any other
679 // matching objects. Assume all interfaces we want to process
680 // must be on the same object path.
681
682 handler(resp, processorId, objectPath, serviceMap);
Jonathan Doman2bab9832020-12-02 15:27:40 -0800683 return;
Gunnar Millsac6a4442020-10-14 14:55:29 -0500684 }
Jonathan Domanc9514482021-02-24 09:20:51 -0800685 messages::resourceNotFound(resp->res, "Processor", processorId);
Gunnar Millsac6a4442020-10-14 14:55:29 -0500686 },
687 "xyz.openbmc_project.ObjectMapper",
688 "/xyz/openbmc_project/object_mapper",
689 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
Jonathan Doman2bab9832020-12-02 15:27:40 -0800690 "/xyz/openbmc_project/inventory", 0,
SunnySrivastava1984cba4f442021-01-07 12:48:16 -0600691 std::array<const char*, 6>{
Jonathan Doman2bab9832020-12-02 15:27:40 -0800692 "xyz.openbmc_project.Inventory.Decorator.Asset",
693 "xyz.openbmc_project.Inventory.Decorator.Revision",
694 "xyz.openbmc_project.Inventory.Item.Cpu",
SunnySrivastava1984cba4f442021-01-07 12:48:16 -0600695 "xyz.openbmc_project.Inventory.Decorator.LocationCode",
Jonathan Domandba0c292020-12-02 15:34:13 -0800696 "xyz.openbmc_project.Inventory.Item.Accelerator",
697 "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"});
Gunnar Millsac6a4442020-10-14 14:55:29 -0500698}
699
zhanghch058d1b46d2021-04-01 11:18:24 +0800700inline void getProcessorData(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Jonathan Domanc9514482021-02-24 09:20:51 -0800701 const std::string& processorId,
702 const std::string& objectPath,
703 const MapperServiceMap& serviceMap)
704{
705 for (const auto& [serviceName, interfaceList] : serviceMap)
706 {
707 for (const auto& interface : interfaceList)
708 {
709 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
710 {
711 getCpuAssetData(aResp, serviceName, objectPath);
712 }
713 else if (interface == "xyz.openbmc_project.Inventory."
714 "Decorator.Revision")
715 {
716 getCpuRevisionData(aResp, serviceName, objectPath);
717 }
718 else if (interface == "xyz.openbmc_project.Inventory.Item.Cpu")
719 {
720 getCpuDataByService(aResp, processorId, serviceName,
721 objectPath);
722 }
723 else if (interface == "xyz.openbmc_project.Inventory."
724 "Item.Accelerator")
725 {
726 getAcceleratorDataByService(aResp, processorId, serviceName,
727 objectPath);
728 }
729 else if (interface == "xyz.openbmc_project.Control.Processor."
730 "CurrentOperatingConfig")
731 {
732 getCpuConfigData(aResp, processorId, serviceName, objectPath);
733 }
734 else if (interface == "xyz.openbmc_project.Inventory."
735 "Decorator.LocationCode")
736 {
737 getCpuLocationCode(aResp, serviceName, objectPath);
738 }
739 }
740 }
741}
742
Jonathan Domandba0c292020-12-02 15:34:13 -0800743/**
744 * Request all the properties for the given D-Bus object and fill out the
745 * related entries in the Redfish OperatingConfig response.
746 *
747 * @param[in,out] aResp Async HTTP response.
748 * @param[in] service D-Bus service name to query.
749 * @param[in] objPath D-Bus object to query.
750 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800751inline void
752 getOperatingConfigData(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
753 const std::string& service,
754 const std::string& objPath)
Jonathan Domandba0c292020-12-02 15:34:13 -0800755{
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:
zhanghch058d1b46d2021-04-01 11:18:24 +0800873 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
874 const crow::Request& req,
Jonathan Domandba0c292020-12-02 15:34:13 -0800875 const std::vector<std::string>& params) override
876 {
877 if (params.size() != 1)
878 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800879 messages::internalError(asyncResp->res);
Jonathan Domandba0c292020-12-02 15:34:13 -0800880 return;
881 }
882
883 const std::string& cpuName = params[0];
zhanghch058d1b46d2021-04-01 11:18:24 +0800884 asyncResp->res.jsonValue["@odata.type"] =
Jonathan Domandba0c292020-12-02 15:34:13 -0800885 "#OperatingConfigCollection.OperatingConfigCollection";
zhanghch058d1b46d2021-04-01 11:18:24 +0800886 asyncResp->res.jsonValue["@odata.id"] = req.url;
887 asyncResp->res.jsonValue["Name"] = "Operating Config Collection";
Jonathan Domandba0c292020-12-02 15:34:13 -0800888
889 // First find the matching CPU object so we know how to constrain our
890 // search for related Config objects.
891 crow::connections::systemBus->async_method_call(
892 [asyncResp, cpuName](const boost::system::error_code ec,
893 const std::vector<std::string>& objects) {
894 if (ec)
895 {
896 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
897 << ec.message();
898 messages::internalError(asyncResp->res);
899 return;
900 }
901
902 for (const std::string& object : objects)
903 {
904 if (!boost::ends_with(object, cpuName))
905 {
906 continue;
907 }
908
909 // Not expected that there will be multiple matching CPU
910 // objects, but if there are just use the first one.
911
912 // Use the common search routine to construct the Collection
913 // of all Config objects under this CPU.
914 collection_util::getCollectionMembers(
915 asyncResp,
916 "/redfish/v1/Systems/system/Processors/" + cpuName +
917 "/OperatingConfigs",
918 {"xyz.openbmc_project.Inventory.Item.Cpu."
919 "OperatingConfig"},
920 object.c_str());
921 return;
922 }
923 },
924 "xyz.openbmc_project.ObjectMapper",
925 "/xyz/openbmc_project/object_mapper",
926 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
927 "/xyz/openbmc_project/inventory", 0,
928 std::array<const char*, 1>{"xyz.openbmc_project.Control.Processor."
929 "CurrentOperatingConfig"});
930 }
931};
932
933class OperatingConfig : public Node
934{
935 public:
936 OperatingConfig(App& app) :
937 Node(app,
938 "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/"
939 "<str>/",
940 std::string(), std::string())
941 {
942 // Defined by Redfish spec privilege registry
943 entityPrivileges = {
944 {boost::beast::http::verb::get, {{"Login"}}},
945 {boost::beast::http::verb::head, {{"Login"}}},
946 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
947 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
948 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
949 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
950 }
951
952 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800953 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
954 const crow::Request& req,
Jonathan Domandba0c292020-12-02 15:34:13 -0800955 const std::vector<std::string>& params) override
956 {
957 if (params.size() != 2)
958 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800959 messages::internalError(asyncResp->res);
Jonathan Domandba0c292020-12-02 15:34:13 -0800960 return;
961 }
962
963 const std::string& cpuName = params[0];
964 const std::string& configName = params[1];
965
Jonathan Domandba0c292020-12-02 15:34:13 -0800966 // Ask for all objects implementing OperatingConfig so we can search for
967 // one with a matching name
968 crow::connections::systemBus->async_method_call(
969 [asyncResp, cpuName, configName,
970 reqUrl{req.url}](boost::system::error_code ec,
971 const MapperGetSubTreeResponse& subtree) {
972 if (ec)
973 {
974 BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
975 << ec.message();
976 messages::internalError(asyncResp->res);
977 return;
978 }
979 const std::string expectedEnding = cpuName + '/' + configName;
980 for (const auto& [objectPath, serviceMap] : subtree)
981 {
982 // Ignore any configs without matching cpuX/configY
983 if (!boost::ends_with(objectPath, expectedEnding) ||
984 serviceMap.empty())
985 {
986 continue;
987 }
988
989 nlohmann::json& json = asyncResp->res.jsonValue;
990 json["@odata.type"] =
991 "#OperatingConfig.v1_0_0.OperatingConfig";
992 json["@odata.id"] = reqUrl;
993 json["Name"] = "Processor Profile";
994 json["Id"] = configName;
995
996 // Just use the first implementation of the object - not
997 // expected that there would be multiple matching services
998 getOperatingConfigData(asyncResp, serviceMap.begin()->first,
999 objectPath);
1000 return;
1001 }
1002 messages::resourceNotFound(asyncResp->res, "OperatingConfig",
1003 configName);
1004 },
1005 "xyz.openbmc_project.ObjectMapper",
1006 "/xyz/openbmc_project/object_mapper",
1007 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1008 "/xyz/openbmc_project/inventory", 0,
1009 std::array<const char*, 1>{
1010 "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"});
1011 }
1012};
1013
Gunnar Millsac6a4442020-10-14 14:55:29 -05001014class ProcessorCollection : public Node
1015{
1016 public:
1017 /*
1018 * Default Constructor
1019 */
1020 ProcessorCollection(App& app) :
1021 Node(app, "/redfish/v1/Systems/system/Processors/")
1022 {
1023 entityPrivileges = {
1024 {boost::beast::http::verb::get, {{"Login"}}},
1025 {boost::beast::http::verb::head, {{"Login"}}},
1026 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1027 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1028 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1029 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1030 }
1031
1032 private:
1033 /**
1034 * Functions triggers appropriate requests on DBus
1035 */
zhanghch058d1b46d2021-04-01 11:18:24 +08001036 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1037 const crow::Request&, const std::vector<std::string>&) override
Gunnar Millsac6a4442020-10-14 14:55:29 -05001038 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001039 asyncResp->res.jsonValue["@odata.type"] =
Gunnar Millsac6a4442020-10-14 14:55:29 -05001040 "#ProcessorCollection.ProcessorCollection";
zhanghch058d1b46d2021-04-01 11:18:24 +08001041 asyncResp->res.jsonValue["Name"] = "Processor Collection";
Gunnar Millsac6a4442020-10-14 14:55:29 -05001042
zhanghch058d1b46d2021-04-01 11:18:24 +08001043 asyncResp->res.jsonValue["@odata.id"] =
1044 "/redfish/v1/Systems/system/Processors";
Gunnar Millsac6a4442020-10-14 14:55:29 -05001045
Gunnar Mills05030b82020-10-14 15:51:31 -05001046 collection_util::getCollectionMembers(
1047 asyncResp, "/redfish/v1/Systems/system/Processors",
Jonathan Domanc9514482021-02-24 09:20:51 -08001048 std::vector<const char*>(processorInterfaces.begin(),
1049 processorInterfaces.end()));
Gunnar Millsac6a4442020-10-14 14:55:29 -05001050 }
1051};
1052
1053class Processor : public Node
1054{
1055 public:
1056 /*
1057 * Default Constructor
1058 */
1059 Processor(App& app) :
1060 Node(app, "/redfish/v1/Systems/system/Processors/<str>/", std::string())
1061 {
1062 entityPrivileges = {
1063 {boost::beast::http::verb::get, {{"Login"}}},
1064 {boost::beast::http::verb::head, {{"Login"}}},
1065 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1066 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1067 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1068 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1069 }
1070
1071 private:
1072 /**
1073 * Functions triggers appropriate requests on DBus
1074 */
zhanghch058d1b46d2021-04-01 11:18:24 +08001075 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1076 const crow::Request&,
Gunnar Millsac6a4442020-10-14 14:55:29 -05001077 const std::vector<std::string>& params) override
1078 {
1079 // Check if there is required param, truly entering this shall be
1080 // impossible
1081 if (params.size() != 1)
1082 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001083 messages::internalError(asyncResp->res);
Gunnar Millsac6a4442020-10-14 14:55:29 -05001084 return;
1085 }
1086 const std::string& processorId = params[0];
zhanghch058d1b46d2021-04-01 11:18:24 +08001087 asyncResp->res.jsonValue["@odata.type"] =
1088 "#Processor.v1_11_0.Processor";
1089 asyncResp->res.jsonValue["@odata.id"] =
Gunnar Millsac6a4442020-10-14 14:55:29 -05001090 "/redfish/v1/Systems/system/Processors/" + processorId;
1091
Jonathan Domanc9514482021-02-24 09:20:51 -08001092 getProcessorObject(asyncResp, processorId, getProcessorData);
Gunnar Millsac6a4442020-10-14 14:55:29 -05001093 }
1094};
1095
1096} // namespace redfish