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