blob: c57d387390114ff44728a8889caaf6158d6a1f39 [file] [log] [blame]
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +02001/*
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
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080018#include "app.hpp"
19#include "dbus_utility.hpp"
James Feist35e257a2020-06-05 13:30:51 -070020#include "health.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080021#include "query.hpp"
22#include "registries/privilege_registry.hpp"
23#include "utils/collection.hpp"
24#include "utils/dbus_utils.hpp"
25#include "utils/hex_utils.hpp"
26#include "utils/json_utils.hpp"
James Feist35e257a2020-06-05 13:30:51 -070027
George Liue99073f2022-12-09 11:06:16 +080028#include <boost/system/error_code.hpp>
Nan Zhoud7f04fd2022-05-01 01:11:07 +000029#include <nlohmann/json.hpp>
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +020030#include <sdbusplus/asio/property.hpp>
31#include <sdbusplus/unpack_properties.hpp>
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +020032
George Liu7a1dbc42022-12-07 16:03:22 +080033#include <array>
34#include <string_view>
35
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +020036namespace redfish
37{
38
Gunnar Mills313efb12020-10-26 16:05:08 -050039inline std::string translateMemoryTypeToRedfish(const std::string& memoryType)
40{
41 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR")
42 {
43 return "DDR";
44 }
45 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2")
46 {
47 return "DDR2";
48 }
49 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR3")
50 {
51 return "DDR3";
52 }
53 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR4")
54 {
55 return "DDR4";
56 }
57 if (memoryType ==
58 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR4E_SDRAM")
59 {
60 return "DDR4E_SDRAM";
61 }
Mansi Joshi11a2f0f2021-07-19 14:33:27 +053062 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR5")
63 {
64 return "DDR5";
65 }
Gunnar Mills313efb12020-10-26 16:05:08 -050066 if (memoryType ==
67 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.LPDDR4_SDRAM")
68 {
69 return "LPDDR4_SDRAM";
70 }
71 if (memoryType ==
72 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.LPDDR3_SDRAM")
73 {
74 return "LPDDR3_SDRAM";
75 }
76 if (memoryType ==
77 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2_SDRAM_FB_DIMM")
78 {
79 return "DDR2_SDRAM_FB_DIMM";
80 }
George Liu0fda0f12021-11-16 10:06:17 +080081 if (memoryType ==
82 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2_SDRAM_FB_DIMM_PROB")
Gunnar Mills313efb12020-10-26 16:05:08 -050083 {
84 return "DDR2_SDRAM_FB_DIMM_PROBE";
85 }
86 if (memoryType ==
87 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR_SGRAM")
88 {
89 return "DDR_SGRAM";
90 }
91 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.ROM")
92 {
93 return "ROM";
94 }
95 if (memoryType ==
96 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.SDRAM")
97 {
98 return "SDRAM";
99 }
100 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.EDO")
101 {
102 return "EDO";
103 }
104 if (memoryType ==
105 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.FastPageMode")
106 {
107 return "FastPageMode";
108 }
109 if (memoryType ==
110 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.PipelinedNibble")
111 {
112 return "PipelinedNibble";
113 }
114 if (memoryType ==
115 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.Logical")
116 {
117 return "Logical";
118 }
119 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM")
120 {
121 return "HBM";
122 }
123 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM2")
124 {
125 return "HBM2";
126 }
Tyson Tuckerbearce34d512022-09-13 16:26:15 -0700127 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM3")
128 {
129 return "HBM3";
130 }
Gunnar Mills313efb12020-10-26 16:05:08 -0500131 // This is values like Other or Unknown
132 // Also D-Bus values:
133 // DRAM
134 // EDRAM
135 // VRAM
136 // SRAM
137 // RAM
138 // FLASH
139 // EEPROM
140 // FEPROM
141 // EPROM
142 // CDRAM
143 // ThreeDRAM
144 // RDRAM
145 // FBD2
146 // LPDDR_SDRAM
147 // LPDDR2_SDRAM
Mansi Joshi11a2f0f2021-07-19 14:33:27 +0530148 // LPDDR5_SDRAM
Gunnar Mills313efb12020-10-26 16:05:08 -0500149 return "";
150}
151
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200152inline void dimmPropToHex(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
153 const char* key, const uint16_t* value,
154 const nlohmann::json::json_pointer& jsonPtr)
James Feistc50e7c62020-07-27 15:39:36 -0700155{
James Feistc50e7c62020-07-27 15:39:36 -0700156 if (value == nullptr)
157 {
James Feistc50e7c62020-07-27 15:39:36 -0700158 return;
159 }
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000160 aResp->res.jsonValue[jsonPtr][key] = "0x" + intToHexString(*value, 4);
James Feistc50e7c62020-07-27 15:39:36 -0700161}
162
zhanghch058d1b46d2021-04-01 11:18:24 +0800163inline void getPersistentMemoryProperties(
164 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200165 const dbus::utility::DBusPropertiesMap& properties,
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000166 const nlohmann::json::json_pointer& jsonPtr)
James Feistc50e7c62020-07-27 15:39:36 -0700167{
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200168 const uint16_t* moduleManufacturerID = nullptr;
169 const uint16_t* moduleProductID = nullptr;
170 const uint16_t* subsystemVendorID = nullptr;
171 const uint16_t* subsystemDeviceID = nullptr;
172 const uint64_t* volatileRegionSizeLimitInKiB = nullptr;
173 const uint64_t* pmRegionSizeLimitInKiB = nullptr;
174 const uint64_t* volatileSizeInKiB = nullptr;
175 const uint64_t* pmSizeInKiB = nullptr;
176 const uint64_t* cacheSizeInKB = nullptr;
177 const uint64_t* voltaileRegionMaxSizeInKib = nullptr;
178 const uint64_t* pmRegionMaxSizeInKiB = nullptr;
179 const uint64_t* allocationIncrementInKiB = nullptr;
180 const uint64_t* allocationAlignmentInKiB = nullptr;
181 const uint64_t* volatileRegionNumberLimit = nullptr;
182 const uint64_t* pmRegionNumberLimit = nullptr;
183 const uint64_t* spareDeviceCount = nullptr;
184 const bool* isSpareDeviceInUse = nullptr;
185 const bool* isRankSpareEnabled = nullptr;
186 const std::vector<uint32_t>* maxAveragePowerLimitmW = nullptr;
187 const bool* configurationLocked = nullptr;
188 const std::string* allowedMemoryModes = nullptr;
189 const std::string* memoryMedia = nullptr;
190 const bool* configurationLockCapable = nullptr;
191 const bool* dataLockCapable = nullptr;
192 const bool* passphraseCapable = nullptr;
193 const uint64_t* maxPassphraseCount = nullptr;
194 const uint64_t* passphraseLockLimit = nullptr;
James Feistc50e7c62020-07-27 15:39:36 -0700195
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200196 const bool success = sdbusplus::unpackPropertiesNoThrow(
197 dbus_utils::UnpackErrorPrinter(), properties, "ModuleManufacturerID",
198 moduleManufacturerID, "ModuleProductID", moduleProductID,
199 "SubsystemVendorID", subsystemVendorID, "SubsystemDeviceID",
200 subsystemDeviceID, "VolatileRegionSizeLimitInKiB",
201 volatileRegionSizeLimitInKiB, "PmRegionSizeLimitInKiB",
202 pmRegionSizeLimitInKiB, "VolatileSizeInKiB", volatileSizeInKiB,
203 "PmSizeInKiB", pmSizeInKiB, "CacheSizeInKB", cacheSizeInKB,
204 "VoltaileRegionMaxSizeInKib", voltaileRegionMaxSizeInKib,
205 "PmRegionMaxSizeInKiB", pmRegionMaxSizeInKiB,
206 "AllocationIncrementInKiB", allocationIncrementInKiB,
207 "AllocationAlignmentInKiB", allocationAlignmentInKiB,
208 "VolatileRegionNumberLimit", volatileRegionNumberLimit,
209 "PmRegionNumberLimit", pmRegionNumberLimit, "SpareDeviceCount",
210 spareDeviceCount, "IsSpareDeviceInUse", isSpareDeviceInUse,
211 "IsRankSpareEnabled", isRankSpareEnabled, "MaxAveragePowerLimitmW",
212 maxAveragePowerLimitmW, "ConfigurationLocked", configurationLocked,
213 "AllowedMemoryModes", allowedMemoryModes, "MemoryMedia", memoryMedia,
214 "ConfigurationLockCapable", configurationLockCapable, "DataLockCapable",
215 dataLockCapable, "PassphraseCapable", passphraseCapable,
216 "MaxPassphraseCount", maxPassphraseCount, "PassphraseLockLimit",
217 passphraseLockLimit);
218
219 if (!success)
220 {
221 messages::internalError(aResp->res);
222 return;
223 }
224
225 dimmPropToHex(aResp, "ModuleManufacturerID", moduleManufacturerID, jsonPtr);
226 dimmPropToHex(aResp, "ModuleProductID", moduleProductID, jsonPtr);
227 dimmPropToHex(aResp, "MemorySubsystemControllerManufacturerID",
228 subsystemVendorID, jsonPtr);
229 dimmPropToHex(aResp, "MemorySubsystemControllerProductID",
230 subsystemDeviceID, jsonPtr);
231
232 if (volatileRegionSizeLimitInKiB != nullptr)
233 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000234 aResp->res.jsonValue[jsonPtr]["VolatileRegionSizeLimitMiB"] =
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200235 (*volatileRegionSizeLimitInKiB) >> 10;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800236 }
James Feistc50e7c62020-07-27 15:39:36 -0700237
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200238 if (pmRegionSizeLimitInKiB != nullptr)
239 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000240 aResp->res.jsonValue[jsonPtr]["PersistentRegionSizeLimitMiB"] =
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200241 (*pmRegionSizeLimitInKiB) >> 10;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800242 }
James Feistc50e7c62020-07-27 15:39:36 -0700243
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200244 if (volatileSizeInKiB != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800245 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200246 aResp->res.jsonValue[jsonPtr]["VolatileSizeMiB"] =
247 (*volatileSizeInKiB) >> 10;
248 }
249
250 if (pmSizeInKiB != nullptr)
251 {
Patrick Williams89492a12023-05-10 07:51:34 -0500252 aResp->res.jsonValue[jsonPtr]["NonVolatileSizeMiB"] = (*pmSizeInKiB) >>
253 10;
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200254 }
255
256 if (cacheSizeInKB != nullptr)
257 {
258 aResp->res.jsonValue[jsonPtr]["CacheSizeMiB"] = (*cacheSizeInKB >> 10);
259 }
260
261 if (voltaileRegionMaxSizeInKib != nullptr)
262 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000263 aResp->res.jsonValue[jsonPtr]["VolatileRegionSizeMaxMiB"] =
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200264 (*voltaileRegionMaxSizeInKib) >> 10;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800265 }
James Feistc50e7c62020-07-27 15:39:36 -0700266
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200267 if (pmRegionMaxSizeInKiB != nullptr)
268 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000269 aResp->res.jsonValue[jsonPtr]["PersistentRegionSizeMaxMiB"] =
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200270 (*pmRegionMaxSizeInKiB) >> 10;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800271 }
James Feistc50e7c62020-07-27 15:39:36 -0700272
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200273 if (allocationIncrementInKiB != nullptr)
274 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000275 aResp->res.jsonValue[jsonPtr]["AllocationIncrementMiB"] =
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200276 (*allocationIncrementInKiB) >> 10;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800277 }
James Feistc50e7c62020-07-27 15:39:36 -0700278
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200279 if (allocationAlignmentInKiB != nullptr)
280 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000281 aResp->res.jsonValue[jsonPtr]["AllocationAlignmentMiB"] =
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200282 (*allocationAlignmentInKiB) >> 10;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800283 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200284
285 if (volatileRegionNumberLimit != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800286 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200287 aResp->res.jsonValue[jsonPtr]["VolatileRegionNumberLimit"] =
288 *volatileRegionNumberLimit;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800289 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200290
291 if (pmRegionNumberLimit != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800292 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200293 aResp->res.jsonValue[jsonPtr]["PersistentRegionNumberLimit"] =
294 *pmRegionNumberLimit;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800295 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200296
297 if (spareDeviceCount != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800298 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200299 aResp->res.jsonValue[jsonPtr]["SpareDeviceCount"] = *spareDeviceCount;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800300 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200301
302 if (isSpareDeviceInUse != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800303 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200304 aResp->res.jsonValue[jsonPtr]["IsSpareDeviceEnabled"] =
305 *isSpareDeviceInUse;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800306 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200307
308 if (isRankSpareEnabled != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800309 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200310 aResp->res.jsonValue[jsonPtr]["IsRankSpareEnabled"] =
311 *isRankSpareEnabled;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800312 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200313
314 if (maxAveragePowerLimitmW != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800315 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200316 aResp->res.jsonValue[jsonPtr]["MaxTDPMilliWatts"] =
317 *maxAveragePowerLimitmW;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800318 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200319
320 if (configurationLocked != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800321 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200322 aResp->res.jsonValue[jsonPtr]["ConfigurationLocked"] =
323 *configurationLocked;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800324 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200325
326 if (allowedMemoryModes != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800327 {
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800328 constexpr const std::array<const char*, 3> values{"Volatile", "PMEM",
329 "Block"};
James Feistc50e7c62020-07-27 15:39:36 -0700330
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800331 for (const char* v : values)
332 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200333 if (allowedMemoryModes->ends_with(v))
James Feistc50e7c62020-07-27 15:39:36 -0700334 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000335 aResp->res.jsonValue[jsonPtr]["OperatingMemoryModes"].push_back(
336 v);
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800337 break;
James Feistc50e7c62020-07-27 15:39:36 -0700338 }
339 }
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800340 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200341
342 if (memoryMedia != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800343 {
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800344 constexpr const std::array<const char*, 3> values{"DRAM", "NAND",
345 "Intel3DXPoint"};
James Feistc50e7c62020-07-27 15:39:36 -0700346
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800347 for (const char* v : values)
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700348 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200349 if (memoryMedia->ends_with(v))
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700350 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000351 aResp->res.jsonValue[jsonPtr]["MemoryMedia"].push_back(v);
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800352 break;
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700353 }
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700354 }
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800355 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200356
357 if (configurationLockCapable != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800358 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200359 aResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
360 ["ConfigurationLockCapable"] =
361 *configurationLockCapable;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800362 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200363
364 if (dataLockCapable != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800365 {
Patrick Williams89492a12023-05-10 07:51:34 -0500366 aResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
367 ["DataLockCapable"] = *dataLockCapable;
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200368 }
369
370 if (passphraseCapable != nullptr)
371 {
Patrick Williams89492a12023-05-10 07:51:34 -0500372 aResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
373 ["PassphraseCapable"] = *passphraseCapable;
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200374 }
375
376 if (maxPassphraseCount != nullptr)
377 {
Patrick Williams89492a12023-05-10 07:51:34 -0500378 aResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
379 ["MaxPassphraseCount"] = *maxPassphraseCount;
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200380 }
381
382 if (passphraseLockLimit != nullptr)
383 {
Patrick Williams89492a12023-05-10 07:51:34 -0500384 aResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
385 ["PassphraseLockLimit"] = *passphraseLockLimit;
James Feistc50e7c62020-07-27 15:39:36 -0700386 }
387}
388
Nan Zhou9a5acea2022-05-17 21:12:43 +0000389inline void
390 assembleDimmProperties(std::string_view dimmId,
391 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000392 const dbus::utility::DBusPropertiesMap& properties,
393 const nlohmann::json::json_pointer& jsonPtr)
Nan Zhou9a5acea2022-05-17 21:12:43 +0000394{
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000395 aResp->res.jsonValue[jsonPtr]["Id"] = dimmId;
396 aResp->res.jsonValue[jsonPtr]["Name"] = "DIMM Slot";
397 aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Enabled";
398 aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "OK";
Nan Zhou9a5acea2022-05-17 21:12:43 +0000399
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200400 const uint16_t* memoryDataWidth = nullptr;
401 const size_t* memorySizeInKB = nullptr;
402 const std::string* partNumber = nullptr;
403 const std::string* serialNumber = nullptr;
404 const std::string* manufacturer = nullptr;
405 const uint16_t* revisionCode = nullptr;
406 const bool* present = nullptr;
407 const uint16_t* memoryTotalWidth = nullptr;
408 const std::string* ecc = nullptr;
409 const std::string* formFactor = nullptr;
410 const std::vector<uint16_t>* allowedSpeedsMT = nullptr;
411 const uint8_t* memoryAttributes = nullptr;
412 const uint16_t* memoryConfiguredSpeedInMhz = nullptr;
413 const std::string* memoryType = nullptr;
414 const std::string* channel = nullptr;
415 const std::string* memoryController = nullptr;
416 const std::string* slot = nullptr;
417 const std::string* socket = nullptr;
418 const std::string* sparePartNumber = nullptr;
419 const std::string* model = nullptr;
420 const std::string* locationCode = nullptr;
421
422 const bool success = sdbusplus::unpackPropertiesNoThrow(
423 dbus_utils::UnpackErrorPrinter(), properties, "MemoryDataWidth",
424 memoryDataWidth, "MemorySizeInKB", memorySizeInKB, "PartNumber",
Nikhil Namjoshi656472d2022-09-13 20:54:44 +0000425 partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer,
426 "RevisionCode", revisionCode, "Present", present, "MemoryTotalWidth",
427 memoryTotalWidth, "ECC", ecc, "FormFactor", formFactor,
428 "AllowedSpeedsMT", allowedSpeedsMT, "MemoryAttributes",
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200429 memoryAttributes, "MemoryConfiguredSpeedInMhz",
430 memoryConfiguredSpeedInMhz, "MemoryType", memoryType, "Channel",
431 channel, "MemoryController", memoryController, "Slot", slot, "Socket",
432 socket, "SparePartNumber", sparePartNumber, "Model", model,
433 "LocationCode", locationCode);
434
435 if (!success)
Nan Zhou9a5acea2022-05-17 21:12:43 +0000436 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200437 messages::internalError(aResp->res);
438 return;
439 }
Nan Zhou9a5acea2022-05-17 21:12:43 +0000440
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200441 if (memoryDataWidth != nullptr)
442 {
443 aResp->res.jsonValue[jsonPtr]["DataWidthBits"] = *memoryDataWidth;
444 }
Nan Zhou9a5acea2022-05-17 21:12:43 +0000445
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200446 if (memorySizeInKB != nullptr)
447 {
448 aResp->res.jsonValue[jsonPtr]["CapacityMiB"] = (*memorySizeInKB >> 10);
449 }
Nan Zhou9a5acea2022-05-17 21:12:43 +0000450
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200451 if (partNumber != nullptr)
452 {
453 aResp->res.jsonValue[jsonPtr]["PartNumber"] = *partNumber;
454 }
Nan Zhou9a5acea2022-05-17 21:12:43 +0000455
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200456 if (serialNumber != nullptr)
457 {
458 aResp->res.jsonValue[jsonPtr]["SerialNumber"] = *serialNumber;
459 }
460
461 if (manufacturer != nullptr)
462 {
463 aResp->res.jsonValue[jsonPtr]["Manufacturer"] = *manufacturer;
464 }
465
466 if (revisionCode != nullptr)
467 {
468 aResp->res.jsonValue[jsonPtr]["FirmwareRevision"] =
469 std::to_string(*revisionCode);
470 }
471
472 if (present != nullptr && !*present)
473 {
474 aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent";
475 }
476
477 if (memoryTotalWidth != nullptr)
478 {
479 aResp->res.jsonValue[jsonPtr]["BusWidthBits"] = *memoryTotalWidth;
480 }
481
482 if (ecc != nullptr)
483 {
484 constexpr const std::array<const char*, 4> values{
485 "NoECC", "SingleBitECC", "MultiBitECC", "AddressParity"};
486
487 for (const char* v : values)
Nan Zhou9a5acea2022-05-17 21:12:43 +0000488 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200489 if (ecc->ends_with(v))
Nan Zhou9a5acea2022-05-17 21:12:43 +0000490 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200491 aResp->res.jsonValue[jsonPtr]["ErrorCorrection"] = v;
492 break;
Nan Zhou9a5acea2022-05-17 21:12:43 +0000493 }
Nan Zhou9a5acea2022-05-17 21:12:43 +0000494 }
495 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200496
497 if (formFactor != nullptr)
498 {
499 constexpr const std::array<const char*, 11> values{
500 "RDIMM", "UDIMM", "SO_DIMM", "LRDIMM",
501 "Mini_RDIMM", "Mini_UDIMM", "SO_RDIMM_72b", "SO_UDIMM_72b",
502 "SO_DIMM_16b", "SO_DIMM_32b", "Die"};
503
504 for (const char* v : values)
505 {
506 if (formFactor->ends_with(v))
507 {
508 aResp->res.jsonValue[jsonPtr]["BaseModuleType"] = v;
509 break;
510 }
511 }
512 }
513
514 if (allowedSpeedsMT != nullptr)
515 {
516 nlohmann::json& jValue =
517 aResp->res.jsonValue[jsonPtr]["AllowedSpeedsMHz"];
518 jValue = nlohmann::json::array();
519 for (uint16_t subVal : *allowedSpeedsMT)
520 {
521 jValue.push_back(subVal);
522 }
523 }
524
525 if (memoryAttributes != nullptr)
526 {
527 aResp->res.jsonValue[jsonPtr]["RankCount"] =
528 static_cast<uint64_t>(*memoryAttributes);
529 }
530
531 if (memoryConfiguredSpeedInMhz != nullptr)
532 {
533 aResp->res.jsonValue[jsonPtr]["OperatingSpeedMhz"] =
534 *memoryConfiguredSpeedInMhz;
535 }
536
537 if (memoryType != nullptr)
538 {
539 std::string memoryDeviceType =
540 translateMemoryTypeToRedfish(*memoryType);
541 // Values like "Unknown" or "Other" will return empty
542 // so just leave off
543 if (!memoryDeviceType.empty())
544 {
545 aResp->res.jsonValue[jsonPtr]["MemoryDeviceType"] =
546 memoryDeviceType;
547 }
548 if (memoryType->find("DDR") != std::string::npos)
549 {
550 aResp->res.jsonValue[jsonPtr]["MemoryType"] = "DRAM";
551 }
552 else if (memoryType->ends_with("Logical"))
553 {
554 aResp->res.jsonValue[jsonPtr]["MemoryType"] = "IntelOptane";
555 }
556 }
557
558 if (channel != nullptr)
559 {
560 aResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Channel"] = *channel;
561 }
562
563 if (memoryController != nullptr)
564 {
565 aResp->res.jsonValue[jsonPtr]["MemoryLocation"]["MemoryController"] =
566 *memoryController;
567 }
568
569 if (slot != nullptr)
570 {
571 aResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Slot"] = *slot;
572 }
573
574 if (socket != nullptr)
575 {
576 aResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Socket"] = *socket;
577 }
578
579 if (sparePartNumber != nullptr)
580 {
581 aResp->res.jsonValue[jsonPtr]["SparePartNumber"] = *sparePartNumber;
582 }
583
584 if (model != nullptr)
585 {
586 aResp->res.jsonValue[jsonPtr]["Model"] = *model;
587 }
588
589 if (locationCode != nullptr)
590 {
Patrick Williams89492a12023-05-10 07:51:34 -0500591 aResp->res.jsonValue[jsonPtr]["Location"]["PartLocation"]
592 ["ServiceLabel"] = *locationCode;
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200593 }
594
595 getPersistentMemoryProperties(aResp, properties, jsonPtr);
Nan Zhou9a5acea2022-05-17 21:12:43 +0000596}
597
zhanghch058d1b46d2021-04-01 11:18:24 +0800598inline void getDimmDataByService(std::shared_ptr<bmcweb::AsyncResp> aResp,
Ed Tanous80789c82020-08-19 09:19:09 -0700599 const std::string& dimmId,
600 const std::string& service,
601 const std::string& objPath)
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200602{
James Feist35e257a2020-06-05 13:30:51 -0700603 auto health = std::make_shared<HealthPopulate>(aResp);
604 health->selfPath = objPath;
605 health->populate();
606
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200607 BMCWEB_LOG_DEBUG << "Get available system components.";
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200608 sdbusplus::asio::getAllProperties(
609 *crow::connections::systemBus, service, objPath, "",
Ed Tanousb9d36b42022-02-26 21:42:46 -0800610 [dimmId, aResp{std::move(aResp)}](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800611 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800612 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700613 if (ec)
614 {
615 BMCWEB_LOG_DEBUG << "DBUS response error";
616 messages::internalError(aResp->res);
617 return;
618 }
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000619 assembleDimmProperties(dimmId, aResp, properties, ""_json_pointer);
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200620 });
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200621}
622
Nan Zhouef00d7d2022-05-20 21:22:32 +0000623inline void assembleDimmPartitionData(
624 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000625 const dbus::utility::DBusPropertiesMap& properties,
626 const nlohmann::json::json_pointer& regionPtr)
Nan Zhouef00d7d2022-05-20 21:22:32 +0000627{
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200628 const std::string* memoryClassification = nullptr;
629 const uint64_t* offsetInKiB = nullptr;
630 const std::string* partitionId = nullptr;
631 const bool* passphraseState = nullptr;
632 const uint64_t* sizeInKiB = nullptr;
633
634 const bool success = sdbusplus::unpackPropertiesNoThrow(
635 dbus_utils::UnpackErrorPrinter(), properties, "MemoryClassification",
636 memoryClassification, "OffsetInKiB", offsetInKiB, "PartitionId",
637 partitionId, "PassphraseState", passphraseState, "SizeInKiB",
638 sizeInKiB);
639
640 if (!success)
Nan Zhouef00d7d2022-05-20 21:22:32 +0000641 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200642 messages::internalError(aResp->res);
643 return;
Nan Zhouef00d7d2022-05-20 21:22:32 +0000644 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200645
646 nlohmann::json::object_t partition;
647
648 if (memoryClassification != nullptr)
649 {
650 partition["MemoryClassification"] = *memoryClassification;
651 }
652
653 if (offsetInKiB != nullptr)
654 {
655 partition["OffsetMiB"] = (*offsetInKiB >> 10);
656 }
657
658 if (partitionId != nullptr)
659 {
660 partition["RegionId"] = *partitionId;
661 }
662
663 if (passphraseState != nullptr)
664 {
665 partition["PassphraseEnabled"] = *passphraseState;
666 }
667
668 if (sizeInKiB != nullptr)
669 {
670 partition["SizeMiB"] = (*sizeInKiB >> 10);
671 }
672
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000673 aResp->res.jsonValue[regionPtr].emplace_back(std::move(partition));
Nan Zhouef00d7d2022-05-20 21:22:32 +0000674}
675
zhanghch058d1b46d2021-04-01 11:18:24 +0800676inline void getDimmPartitionData(std::shared_ptr<bmcweb::AsyncResp> aResp,
Ed Tanous23a21a12020-07-25 04:45:05 +0000677 const std::string& service,
678 const std::string& path)
James Feist45094ad2020-04-29 14:02:30 -0700679{
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200680 sdbusplus::asio::getAllProperties(
681 *crow::connections::systemBus, service, path,
682 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition",
James Feist45094ad2020-04-29 14:02:30 -0700683 [aResp{std::move(aResp)}](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800684 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800685 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700686 if (ec)
687 {
688 BMCWEB_LOG_DEBUG << "DBUS response error";
689 messages::internalError(aResp->res);
James Feist45094ad2020-04-29 14:02:30 -0700690
Ed Tanous002d39b2022-05-31 08:59:27 -0700691 return;
692 }
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000693 nlohmann::json::json_pointer regionPtr = "/Regions"_json_pointer;
694 assembleDimmPartitionData(aResp, properties, regionPtr);
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200695 }
James Feist45094ad2020-04-29 14:02:30 -0700696
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200697 );
James Feist45094ad2020-04-29 14:02:30 -0700698}
699
zhanghch058d1b46d2021-04-01 11:18:24 +0800700inline void getDimmData(std::shared_ptr<bmcweb::AsyncResp> aResp,
Ed Tanous23a21a12020-07-25 04:45:05 +0000701 const std::string& dimmId)
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200702{
703 BMCWEB_LOG_DEBUG << "Get available system dimm resources.";
George Liue99073f2022-12-09 11:06:16 +0800704 constexpr std::array<std::string_view, 2> dimmInterfaces = {
705 "xyz.openbmc_project.Inventory.Item.Dimm",
706 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition"};
707 dbus::utility::getSubTree(
708 "/xyz/openbmc_project/inventory", 0, dimmInterfaces,
Ed Tanous029573d2019-02-01 10:57:49 -0800709 [dimmId, aResp{std::move(aResp)}](
George Liue99073f2022-12-09 11:06:16 +0800710 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800711 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700712 if (ec)
713 {
714 BMCWEB_LOG_DEBUG << "DBUS response error";
715 messages::internalError(aResp->res);
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200716
Ed Tanous002d39b2022-05-31 08:59:27 -0700717 return;
718 }
719 bool found = false;
Nan Zhou76686dc2022-06-17 23:01:51 +0000720 for (const auto& [rawPath, object] : subtree)
Ed Tanous002d39b2022-05-31 08:59:27 -0700721 {
Nan Zhou76686dc2022-06-17 23:01:51 +0000722 sdbusplus::message::object_path path(rawPath);
723 for (const auto& [service, interfaces] : object)
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200724 {
Nan Zhou76686dc2022-06-17 23:01:51 +0000725 for (const auto& interface : interfaces)
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200726 {
Nan Zhou76686dc2022-06-17 23:01:51 +0000727 if (interface ==
728 "xyz.openbmc_project.Inventory.Item.Dimm" &&
729 path.filename() == dimmId)
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200730 {
Nan Zhou76686dc2022-06-17 23:01:51 +0000731 getDimmDataByService(aResp, dimmId, service, rawPath);
732 found = true;
733 }
James Feist45094ad2020-04-29 14:02:30 -0700734
Nan Zhou76686dc2022-06-17 23:01:51 +0000735 // partitions are separate as there can be multiple
736 // per
737 // device, i.e.
738 // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1
739 // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2
740 if (interface ==
741 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition" &&
742 path.parent_path().filename() == dimmId)
743 {
744 getDimmPartitionData(aResp, service, rawPath);
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200745 }
746 }
747 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700748 }
749 // Object not found
750 if (!found)
751 {
752 messages::resourceNotFound(aResp->res, "Memory", dimmId);
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200753 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700754 }
755 // Set @odata only if object is found
756 aResp->res.jsonValue["@odata.type"] = "#Memory.v1_11_0.Memory";
Willy Tueddfc432022-09-26 16:46:38 +0000757 aResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
758 "redfish", "v1", "Systems", "system", "Memory", dimmId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700759 return;
George Liue99073f2022-12-09 11:06:16 +0800760 });
Ed Tanous271584a2019-07-09 16:24:22 -0700761}
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200762
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700763inline void requestRoutesMemoryCollection(App& app)
Ed Tanous029573d2019-02-01 10:57:49 -0800764{
Ed Tanous029573d2019-02-01 10:57:49 -0800765 /**
766 * Functions triggers appropriate requests on DBus
767 */
Ed Tanous22d268c2022-05-19 09:39:07 -0700768 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/")
Ed Tanoused398212021-06-09 17:05:54 -0700769 .privileges(redfish::privileges::getMemoryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700770 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700771 [&app](const crow::Request& req,
Ed Tanous22d268c2022-05-19 09:39:07 -0700772 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
773 const std::string& systemName) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000774 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700775 {
776 return;
777 }
Ed Tanous22d268c2022-05-19 09:39:07 -0700778 if (systemName != "system")
779 {
780 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
781 systemName);
782 return;
783 }
784
Ed Tanous002d39b2022-05-31 08:59:27 -0700785 asyncResp->res.jsonValue["@odata.type"] =
786 "#MemoryCollection.MemoryCollection";
787 asyncResp->res.jsonValue["Name"] = "Memory Module Collection";
788 asyncResp->res.jsonValue["@odata.id"] =
789 "/redfish/v1/Systems/system/Memory";
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200790
George Liu7a1dbc42022-12-07 16:03:22 +0800791 constexpr std::array<std::string_view, 1> interfaces{
792 "xyz.openbmc_project.Inventory.Item.Dimm"};
Ed Tanous002d39b2022-05-31 08:59:27 -0700793 collection_util::getCollectionMembers(
Willy Tuae9031f2022-09-27 05:48:07 +0000794 asyncResp, boost::urls::url("/redfish/v1/Systems/system/Memory"),
George Liu7a1dbc42022-12-07 16:03:22 +0800795 interfaces);
Ed Tanous002d39b2022-05-31 08:59:27 -0700796 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700797}
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200798
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700799inline void requestRoutesMemory(App& app)
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200800{
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200801 /**
802 * Functions triggers appropriate requests on DBus
803 */
Ed Tanous22d268c2022-05-19 09:39:07 -0700804 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700805 .privileges(redfish::privileges::getMemory)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700806 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700807 [&app](const crow::Request& req,
808 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous22d268c2022-05-19 09:39:07 -0700809 const std::string& systemName, const std::string& dimmId) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000810 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700811 {
812 return;
813 }
Ed Tanous22d268c2022-05-19 09:39:07 -0700814 if (systemName != "system")
815 {
816 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
817 systemName);
818 return;
819 }
820
Ed Tanous002d39b2022-05-31 08:59:27 -0700821 getDimmData(asyncResp, dimmId);
822 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700823}
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200824
825} // namespace redfish