1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4 #pragma once
5
6 #include "bmcweb_config.h"
7
8 #include "app.hpp"
9 #include "async_resp.hpp"
10 #include "dbus_utility.hpp"
11 #include "error_messages.hpp"
12 #include "generated/enums/memory.hpp"
13 #include "generated/enums/resource.hpp"
14 #include "http_request.hpp"
15 #include "led.hpp"
16 #include "logging.hpp"
17 #include "query.hpp"
18 #include "registries/privilege_registry.hpp"
19 #include "utils/collection.hpp"
20 #include "utils/dbus_utils.hpp"
21 #include "utils/hex_utils.hpp"
22 #include "utils/json_utils.hpp"
23
24 #include <asm-generic/errno.h>
25
26 #include <boost/beast/http/verb.hpp>
27 #include <boost/system/error_code.hpp>
28 #include <boost/url/format.hpp>
29 #include <nlohmann/json.hpp>
30 #include <sdbusplus/message/native_types.hpp>
31 #include <sdbusplus/unpack_properties.hpp>
32
33 #include <array>
34 #include <cstddef>
35 #include <cstdint>
36 #include <functional>
37 #include <memory>
38 #include <optional>
39 #include <string>
40 #include <string_view>
41 #include <utility>
42 #include <vector>
43
44 namespace redfish
45 {
46
translateMemoryTypeToRedfish(const std::string & memoryType)47 inline std::string translateMemoryTypeToRedfish(const std::string& memoryType)
48 {
49 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR")
50 {
51 return "DDR";
52 }
53 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2")
54 {
55 return "DDR2";
56 }
57 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR3")
58 {
59 return "DDR3";
60 }
61 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR4")
62 {
63 return "DDR4";
64 }
65 if (memoryType ==
66 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR4E_SDRAM")
67 {
68 return "DDR4E_SDRAM";
69 }
70 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR5")
71 {
72 return "DDR5";
73 }
74 if (memoryType ==
75 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.LPDDR4_SDRAM")
76 {
77 return "LPDDR4_SDRAM";
78 }
79 if (memoryType ==
80 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.LPDDR3_SDRAM")
81 {
82 return "LPDDR3_SDRAM";
83 }
84 if (memoryType ==
85 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2_SDRAM_FB_DIMM")
86 {
87 return "DDR2_SDRAM_FB_DIMM";
88 }
89 if (memoryType ==
90 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2_SDRAM_FB_DIMM_PROB")
91 {
92 return "DDR2_SDRAM_FB_DIMM_PROBE";
93 }
94 if (memoryType ==
95 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR_SGRAM")
96 {
97 return "DDR_SGRAM";
98 }
99 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.ROM")
100 {
101 return "ROM";
102 }
103 if (memoryType ==
104 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.SDRAM")
105 {
106 return "SDRAM";
107 }
108 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.EDO")
109 {
110 return "EDO";
111 }
112 if (memoryType ==
113 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.FastPageMode")
114 {
115 return "FastPageMode";
116 }
117 if (memoryType ==
118 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.PipelinedNibble")
119 {
120 return "PipelinedNibble";
121 }
122 if (memoryType ==
123 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.Logical")
124 {
125 return "Logical";
126 }
127 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM")
128 {
129 return "HBM";
130 }
131 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM2")
132 {
133 return "HBM2";
134 }
135 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM3")
136 {
137 return "HBM3";
138 }
139 // This is values like Other or Unknown
140 // Also D-Bus values:
141 // DRAM
142 // EDRAM
143 // VRAM
144 // SRAM
145 // RAM
146 // FLASH
147 // EEPROM
148 // FEPROM
149 // EPROM
150 // CDRAM
151 // ThreeDRAM
152 // RDRAM
153 // FBD2
154 // LPDDR_SDRAM
155 // LPDDR2_SDRAM
156 // LPDDR5_SDRAM
157 return "";
158 }
159
dimmPropToHex(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const char * key,const uint16_t * value,const nlohmann::json::json_pointer & jsonPtr)160 inline void dimmPropToHex(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
161 const char* key, const uint16_t* value,
162 const nlohmann::json::json_pointer& jsonPtr)
163 {
164 if (value == nullptr)
165 {
166 return;
167 }
168 asyncResp->res.jsonValue[jsonPtr][key] = "0x" + intToHexString(*value, 4);
169 }
170
getPersistentMemoryProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const dbus::utility::DBusPropertiesMap & properties,const nlohmann::json::json_pointer & jsonPtr)171 inline void getPersistentMemoryProperties(
172 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
173 const dbus::utility::DBusPropertiesMap& properties,
174 const nlohmann::json::json_pointer& jsonPtr)
175 {
176 const uint16_t* moduleManufacturerID = nullptr;
177 const uint16_t* moduleProductID = nullptr;
178 const uint16_t* subsystemVendorID = nullptr;
179 const uint16_t* subsystemDeviceID = nullptr;
180 const uint64_t* volatileRegionSizeLimitInKiB = nullptr;
181 const uint64_t* pmRegionSizeLimitInKiB = nullptr;
182 const uint64_t* volatileSizeInKiB = nullptr;
183 const uint64_t* pmSizeInKiB = nullptr;
184 const uint64_t* cacheSizeInKB = nullptr;
185 const uint64_t* voltaileRegionMaxSizeInKib = nullptr;
186 const uint64_t* pmRegionMaxSizeInKiB = nullptr;
187 const uint64_t* allocationIncrementInKiB = nullptr;
188 const uint64_t* allocationAlignmentInKiB = nullptr;
189 const uint64_t* volatileRegionNumberLimit = nullptr;
190 const uint64_t* pmRegionNumberLimit = nullptr;
191 const uint64_t* spareDeviceCount = nullptr;
192 const bool* isSpareDeviceInUse = nullptr;
193 const bool* isRankSpareEnabled = nullptr;
194 const std::vector<uint32_t>* maxAveragePowerLimitmW = nullptr;
195 const bool* configurationLocked = nullptr;
196 const std::string* allowedMemoryModes = nullptr;
197 const std::string* memoryMedia = nullptr;
198 const bool* configurationLockCapable = nullptr;
199 const bool* dataLockCapable = nullptr;
200 const bool* passphraseCapable = nullptr;
201 const uint64_t* maxPassphraseCount = nullptr;
202 const uint64_t* passphraseLockLimit = nullptr;
203
204 const bool success = sdbusplus::unpackPropertiesNoThrow(
205 dbus_utils::UnpackErrorPrinter(), properties, "ModuleManufacturerID",
206 moduleManufacturerID, "ModuleProductID", moduleProductID,
207 "SubsystemVendorID", subsystemVendorID, "SubsystemDeviceID",
208 subsystemDeviceID, "VolatileRegionSizeLimitInKiB",
209 volatileRegionSizeLimitInKiB, "PmRegionSizeLimitInKiB",
210 pmRegionSizeLimitInKiB, "VolatileSizeInKiB", volatileSizeInKiB,
211 "PmSizeInKiB", pmSizeInKiB, "CacheSizeInKB", cacheSizeInKB,
212 "VoltaileRegionMaxSizeInKib", voltaileRegionMaxSizeInKib,
213 "PmRegionMaxSizeInKiB", pmRegionMaxSizeInKiB,
214 "AllocationIncrementInKiB", allocationIncrementInKiB,
215 "AllocationAlignmentInKiB", allocationAlignmentInKiB,
216 "VolatileRegionNumberLimit", volatileRegionNumberLimit,
217 "PmRegionNumberLimit", pmRegionNumberLimit, "SpareDeviceCount",
218 spareDeviceCount, "IsSpareDeviceInUse", isSpareDeviceInUse,
219 "IsRankSpareEnabled", isRankSpareEnabled, "MaxAveragePowerLimitmW",
220 maxAveragePowerLimitmW, "ConfigurationLocked", configurationLocked,
221 "AllowedMemoryModes", allowedMemoryModes, "MemoryMedia", memoryMedia,
222 "ConfigurationLockCapable", configurationLockCapable, "DataLockCapable",
223 dataLockCapable, "PassphraseCapable", passphraseCapable,
224 "MaxPassphraseCount", maxPassphraseCount, "PassphraseLockLimit",
225 passphraseLockLimit);
226
227 if (!success)
228 {
229 messages::internalError(asyncResp->res);
230 return;
231 }
232
233 dimmPropToHex(asyncResp, "ModuleManufacturerID", moduleManufacturerID,
234 jsonPtr);
235 dimmPropToHex(asyncResp, "ModuleProductID", moduleProductID, jsonPtr);
236 dimmPropToHex(asyncResp, "MemorySubsystemControllerManufacturerID",
237 subsystemVendorID, jsonPtr);
238 dimmPropToHex(asyncResp, "MemorySubsystemControllerProductID",
239 subsystemDeviceID, jsonPtr);
240
241 if (volatileRegionSizeLimitInKiB != nullptr)
242 {
243 asyncResp->res.jsonValue[jsonPtr]["VolatileRegionSizeLimitMiB"] =
244 (*volatileRegionSizeLimitInKiB) >> 10;
245 }
246
247 if (pmRegionSizeLimitInKiB != nullptr)
248 {
249 asyncResp->res.jsonValue[jsonPtr]["PersistentRegionSizeLimitMiB"] =
250 (*pmRegionSizeLimitInKiB) >> 10;
251 }
252
253 if (volatileSizeInKiB != nullptr)
254 {
255 asyncResp->res.jsonValue[jsonPtr]["VolatileSizeMiB"] =
256 (*volatileSizeInKiB) >> 10;
257 }
258
259 if (pmSizeInKiB != nullptr)
260 {
261 asyncResp->res.jsonValue[jsonPtr]["NonVolatileSizeMiB"] =
262 (*pmSizeInKiB) >> 10;
263 }
264
265 if (cacheSizeInKB != nullptr)
266 {
267 asyncResp->res.jsonValue[jsonPtr]["CacheSizeMiB"] =
268 (*cacheSizeInKB >> 10);
269 }
270
271 if (voltaileRegionMaxSizeInKib != nullptr)
272 {
273 asyncResp->res.jsonValue[jsonPtr]["VolatileRegionSizeMaxMiB"] =
274 (*voltaileRegionMaxSizeInKib) >> 10;
275 }
276
277 if (pmRegionMaxSizeInKiB != nullptr)
278 {
279 asyncResp->res.jsonValue[jsonPtr]["PersistentRegionSizeMaxMiB"] =
280 (*pmRegionMaxSizeInKiB) >> 10;
281 }
282
283 if (allocationIncrementInKiB != nullptr)
284 {
285 asyncResp->res.jsonValue[jsonPtr]["AllocationIncrementMiB"] =
286 (*allocationIncrementInKiB) >> 10;
287 }
288
289 if (allocationAlignmentInKiB != nullptr)
290 {
291 asyncResp->res.jsonValue[jsonPtr]["AllocationAlignmentMiB"] =
292 (*allocationAlignmentInKiB) >> 10;
293 }
294
295 if (volatileRegionNumberLimit != nullptr)
296 {
297 asyncResp->res.jsonValue[jsonPtr]["VolatileRegionNumberLimit"] =
298 *volatileRegionNumberLimit;
299 }
300
301 if (pmRegionNumberLimit != nullptr)
302 {
303 asyncResp->res.jsonValue[jsonPtr]["PersistentRegionNumberLimit"] =
304 *pmRegionNumberLimit;
305 }
306
307 if (spareDeviceCount != nullptr)
308 {
309 asyncResp->res.jsonValue[jsonPtr]["SpareDeviceCount"] =
310 *spareDeviceCount;
311 }
312
313 if (isSpareDeviceInUse != nullptr)
314 {
315 asyncResp->res.jsonValue[jsonPtr]["IsSpareDeviceEnabled"] =
316 *isSpareDeviceInUse;
317 }
318
319 if (isRankSpareEnabled != nullptr)
320 {
321 asyncResp->res.jsonValue[jsonPtr]["IsRankSpareEnabled"] =
322 *isRankSpareEnabled;
323 }
324
325 if (maxAveragePowerLimitmW != nullptr)
326 {
327 asyncResp->res.jsonValue[jsonPtr]["MaxTDPMilliWatts"] =
328 *maxAveragePowerLimitmW;
329 }
330
331 if (configurationLocked != nullptr)
332 {
333 asyncResp->res.jsonValue[jsonPtr]["ConfigurationLocked"] =
334 *configurationLocked;
335 }
336
337 if (allowedMemoryModes != nullptr)
338 {
339 constexpr const std::array<const char*, 3> values{"Volatile", "PMEM",
340 "Block"};
341
342 for (const char* v : values)
343 {
344 if (allowedMemoryModes->ends_with(v))
345 {
346 asyncResp->res.jsonValue[jsonPtr]["OperatingMemoryModes"]
347 .push_back(v);
348 break;
349 }
350 }
351 }
352
353 if (memoryMedia != nullptr)
354 {
355 constexpr const std::array<const char*, 3> values{"DRAM", "NAND",
356 "Intel3DXPoint"};
357
358 for (const char* v : values)
359 {
360 if (memoryMedia->ends_with(v))
361 {
362 asyncResp->res.jsonValue[jsonPtr]["MemoryMedia"].push_back(v);
363 break;
364 }
365 }
366 }
367
368 if (configurationLockCapable != nullptr)
369 {
370 asyncResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
371 ["ConfigurationLockCapable"] =
372 *configurationLockCapable;
373 }
374
375 if (dataLockCapable != nullptr)
376 {
377 asyncResp->res
378 .jsonValue[jsonPtr]["SecurityCapabilities"]["DataLockCapable"] =
379 *dataLockCapable;
380 }
381
382 if (passphraseCapable != nullptr)
383 {
384 asyncResp->res
385 .jsonValue[jsonPtr]["SecurityCapabilities"]["PassphraseCapable"] =
386 *passphraseCapable;
387 }
388
389 if (maxPassphraseCount != nullptr)
390 {
391 asyncResp->res
392 .jsonValue[jsonPtr]["SecurityCapabilities"]["MaxPassphraseCount"] =
393 *maxPassphraseCount;
394 }
395
396 if (passphraseLockLimit != nullptr)
397 {
398 asyncResp->res
399 .jsonValue[jsonPtr]["SecurityCapabilities"]["PassphraseLockLimit"] =
400 *passphraseLockLimit;
401 }
402 }
403
assembleDimmProperties(std::string_view dimmId,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const dbus::utility::DBusPropertiesMap & properties,const nlohmann::json::json_pointer & jsonPtr)404 inline void assembleDimmProperties(
405 std::string_view dimmId,
406 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
407 const dbus::utility::DBusPropertiesMap& properties,
408 const nlohmann::json::json_pointer& jsonPtr)
409 {
410 asyncResp->res.jsonValue[jsonPtr]["Id"] = dimmId;
411 asyncResp->res.jsonValue[jsonPtr]["Name"] = "DIMM Slot";
412 asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] =
413 resource::State::Enabled;
414 asyncResp->res.jsonValue[jsonPtr]["Status"]["Health"] =
415 resource::Health::OK;
416
417 const uint16_t* memoryDataWidth = nullptr;
418 const size_t* memorySizeInKB = nullptr;
419 const std::string* partNumber = nullptr;
420 const std::string* serialNumber = nullptr;
421 const std::string* manufacturer = nullptr;
422 const uint16_t* revisionCode = nullptr;
423 const bool* present = nullptr;
424 const uint16_t* memoryTotalWidth = nullptr;
425 const std::string* ecc = nullptr;
426 const std::string* formFactor = nullptr;
427 const std::vector<uint16_t>* allowedSpeedsMT = nullptr;
428 const size_t* memoryAttributes = nullptr;
429 const uint16_t* memoryConfiguredSpeedInMhz = nullptr;
430 const std::string* memoryType = nullptr;
431 const std::uint8_t* channel = nullptr;
432 const std::uint8_t* memoryController = nullptr;
433 const std::uint8_t* slot = nullptr;
434 const std::uint8_t* socket = nullptr;
435 const std::string* sparePartNumber = nullptr;
436 const std::string* model = nullptr;
437 const std::string* locationCode = nullptr;
438 const bool* functional = nullptr;
439
440 const bool success = sdbusplus::unpackPropertiesNoThrow(
441 dbus_utils::UnpackErrorPrinter(), properties, "MemoryDataWidth",
442 memoryDataWidth, "MemorySizeInKB", memorySizeInKB, "PartNumber",
443 partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer,
444 "RevisionCode", revisionCode, "Present", present, "MemoryTotalWidth",
445 memoryTotalWidth, "ECC", ecc, "FormFactor", formFactor,
446 "AllowedSpeedsMT", allowedSpeedsMT, "MemoryAttributes",
447 memoryAttributes, "MemoryConfiguredSpeedInMhz",
448 memoryConfiguredSpeedInMhz, "MemoryType", memoryType, "Channel",
449 channel, "MemoryController", memoryController, "Slot", slot, "Socket",
450 socket, "SparePartNumber", sparePartNumber, "Model", model,
451 "LocationCode", locationCode, "Functional", functional);
452
453 if (!success)
454 {
455 messages::internalError(asyncResp->res);
456 return;
457 }
458
459 if (memoryDataWidth != nullptr)
460 {
461 asyncResp->res.jsonValue[jsonPtr]["DataWidthBits"] = *memoryDataWidth;
462 }
463
464 if (memorySizeInKB != nullptr)
465 {
466 asyncResp->res.jsonValue[jsonPtr]["CapacityMiB"] =
467 (*memorySizeInKB >> 10);
468 }
469
470 if (partNumber != nullptr)
471 {
472 asyncResp->res.jsonValue[jsonPtr]["PartNumber"] = *partNumber;
473 }
474
475 if (serialNumber != nullptr)
476 {
477 asyncResp->res.jsonValue[jsonPtr]["SerialNumber"] = *serialNumber;
478 }
479
480 if (manufacturer != nullptr)
481 {
482 asyncResp->res.jsonValue[jsonPtr]["Manufacturer"] = *manufacturer;
483 }
484
485 if (revisionCode != nullptr)
486 {
487 asyncResp->res.jsonValue[jsonPtr]["FirmwareRevision"] =
488 std::to_string(*revisionCode);
489 }
490
491 if (present != nullptr && !*present)
492 {
493 asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] =
494 resource::State::Absent;
495 }
496
497 if (functional != nullptr)
498 {
499 if (!*functional)
500 {
501 asyncResp->res.jsonValue[jsonPtr]["Status"]["Health"] =
502 resource::Health::Critical;
503 }
504 }
505
506 if (memoryTotalWidth != nullptr)
507 {
508 asyncResp->res.jsonValue[jsonPtr]["BusWidthBits"] = *memoryTotalWidth;
509 }
510
511 if (ecc != nullptr)
512 {
513 constexpr const std::array<const char*, 4> values{
514 "NoECC", "SingleBitECC", "MultiBitECC", "AddressParity"};
515
516 for (const char* v : values)
517 {
518 if (ecc->ends_with(v))
519 {
520 asyncResp->res.jsonValue[jsonPtr]["ErrorCorrection"] = v;
521 break;
522 }
523 }
524 }
525
526 if (formFactor != nullptr)
527 {
528 constexpr const std::array<const char*, 11> values{
529 "RDIMM", "UDIMM", "SO_DIMM", "LRDIMM",
530 "Mini_RDIMM", "Mini_UDIMM", "SO_RDIMM_72b", "SO_UDIMM_72b",
531 "SO_DIMM_16b", "SO_DIMM_32b", "Die"};
532
533 for (const char* v : values)
534 {
535 if (formFactor->ends_with(v))
536 {
537 asyncResp->res.jsonValue[jsonPtr]["BaseModuleType"] = v;
538 break;
539 }
540 }
541 }
542
543 if (allowedSpeedsMT != nullptr)
544 {
545 nlohmann::json& jValue =
546 asyncResp->res.jsonValue[jsonPtr]["AllowedSpeedsMHz"];
547 jValue = nlohmann::json::array();
548 for (uint16_t subVal : *allowedSpeedsMT)
549 {
550 jValue.push_back(subVal);
551 }
552 }
553
554 if (memoryAttributes != nullptr)
555 {
556 asyncResp->res.jsonValue[jsonPtr]["RankCount"] = *memoryAttributes;
557 }
558
559 if (memoryConfiguredSpeedInMhz != nullptr)
560 {
561 asyncResp->res.jsonValue[jsonPtr]["OperatingSpeedMhz"] =
562 *memoryConfiguredSpeedInMhz;
563 }
564
565 if (memoryType != nullptr)
566 {
567 std::string memoryDeviceType =
568 translateMemoryTypeToRedfish(*memoryType);
569 // Values like "Unknown" or "Other" will return empty
570 // so just leave off
571 if (!memoryDeviceType.empty())
572 {
573 asyncResp->res.jsonValue[jsonPtr]["MemoryDeviceType"] =
574 memoryDeviceType;
575 }
576 if (memoryType->find("DDR") != std::string::npos)
577 {
578 asyncResp->res.jsonValue[jsonPtr]["MemoryType"] =
579 memory::MemoryType::DRAM;
580 }
581 else if (memoryType->ends_with("Logical"))
582 {
583 asyncResp->res.jsonValue[jsonPtr]["MemoryType"] =
584 memory::MemoryType::IntelOptane;
585 }
586 }
587
588 if (channel != nullptr)
589 {
590 asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Channel"] =
591 *channel;
592 }
593
594 if (memoryController != nullptr)
595 {
596 asyncResp->res
597 .jsonValue[jsonPtr]["MemoryLocation"]["MemoryController"] =
598 *memoryController;
599 }
600
601 if (slot != nullptr)
602 {
603 asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Slot"] = *slot;
604 }
605
606 if (socket != nullptr)
607 {
608 asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Socket"] = *socket;
609 }
610
611 if (sparePartNumber != nullptr)
612 {
613 asyncResp->res.jsonValue[jsonPtr]["SparePartNumber"] = *sparePartNumber;
614 }
615
616 if (model != nullptr)
617 {
618 asyncResp->res.jsonValue[jsonPtr]["Model"] = *model;
619 }
620
621 if (locationCode != nullptr)
622 {
623 asyncResp->res
624 .jsonValue[jsonPtr]["Location"]["PartLocation"]["ServiceLabel"] =
625 *locationCode;
626 }
627
628 getPersistentMemoryProperties(asyncResp, properties, jsonPtr);
629 }
630
getDimmDataByService(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & dimmId,const std::string & service,const std::string & objPath)631 inline void getDimmDataByService(
632 std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& dimmId,
633 const std::string& service, const std::string& objPath)
634 {
635 BMCWEB_LOG_DEBUG("Get available system components.");
636 dbus::utility::getAllProperties(
637 service, objPath, "",
638 [dimmId, asyncResp{std::move(asyncResp)}](
639 const boost::system::error_code& ec,
640 const dbus::utility::DBusPropertiesMap& properties) {
641 if (ec)
642 {
643 BMCWEB_LOG_DEBUG("DBUS response error");
644 messages::internalError(asyncResp->res);
645 return;
646 }
647 assembleDimmProperties(dimmId, asyncResp, properties,
648 ""_json_pointer);
649 });
650 }
651
assembleDimmPartitionData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const dbus::utility::DBusPropertiesMap & properties,const nlohmann::json::json_pointer & regionPtr)652 inline void assembleDimmPartitionData(
653 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
654 const dbus::utility::DBusPropertiesMap& properties,
655 const nlohmann::json::json_pointer& regionPtr)
656 {
657 const std::string* memoryClassification = nullptr;
658 const uint64_t* offsetInKiB = nullptr;
659 const std::string* partitionId = nullptr;
660 const bool* passphraseState = nullptr;
661 const uint64_t* sizeInKiB = nullptr;
662
663 const bool success = sdbusplus::unpackPropertiesNoThrow(
664 dbus_utils::UnpackErrorPrinter(), properties, "MemoryClassification",
665 memoryClassification, "OffsetInKiB", offsetInKiB, "PartitionId",
666 partitionId, "PassphraseState", passphraseState, "SizeInKiB",
667 sizeInKiB);
668
669 if (!success)
670 {
671 messages::internalError(asyncResp->res);
672 return;
673 }
674
675 nlohmann::json::object_t partition;
676
677 if (memoryClassification != nullptr)
678 {
679 partition["MemoryClassification"] = *memoryClassification;
680 }
681
682 if (offsetInKiB != nullptr)
683 {
684 partition["OffsetMiB"] = (*offsetInKiB >> 10);
685 }
686
687 if (partitionId != nullptr)
688 {
689 partition["RegionId"] = *partitionId;
690 }
691
692 if (passphraseState != nullptr)
693 {
694 partition["PassphraseEnabled"] = *passphraseState;
695 }
696
697 if (sizeInKiB != nullptr)
698 {
699 partition["SizeMiB"] = (*sizeInKiB >> 10);
700 }
701
702 asyncResp->res.jsonValue[regionPtr].emplace_back(std::move(partition));
703 }
704
getDimmPartitionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & service,const std::string & path)705 inline void getDimmPartitionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
706 const std::string& service,
707 const std::string& path)
708 {
709 dbus::utility::getAllProperties(
710 service, path,
711 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition",
712 [asyncResp{std::move(asyncResp)}](
713 const boost::system::error_code& ec,
714 const dbus::utility::DBusPropertiesMap& properties) {
715 if (ec)
716 {
717 BMCWEB_LOG_DEBUG("DBUS response error");
718 messages::internalError(asyncResp->res);
719
720 return;
721 }
722 nlohmann::json::json_pointer regionPtr = "/Regions"_json_pointer;
723 assembleDimmPartitionData(asyncResp, properties, regionPtr);
724 }
725
726 );
727 }
728
afterGetDimmData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dimmId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)729 inline void afterGetDimmData(
730 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
731 const std::string& dimmId, const boost::system::error_code& ec,
732 const dbus::utility::MapperGetSubTreeResponse& subtree)
733 {
734 if (ec)
735 {
736 if (ec.value() != EBADR)
737 {
738 BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value());
739 messages::internalError(asyncResp->res);
740 }
741 return;
742 }
743
744 bool found = false;
745 for (const auto& [objectPath, serviceMap] : subtree)
746 {
747 sdbusplus::message::object_path path(objectPath);
748
749 bool dimmInterface = false;
750 bool associationInterface = false;
751 /* Note: Multiple D-Bus objects can provide details for the Memory
752 * object: 1) Dimm is the primary object 2) Additional partitions could
753 * exist per Dimm. Only consider the object found if the Dimm is found.
754 */
755 for (const auto& [serviceName, interfaceList] : serviceMap)
756 {
757 for (const auto& interface : interfaceList)
758 {
759 if (interface == "xyz.openbmc_project.Inventory.Item.Dimm" &&
760 path.filename() == dimmId)
761 {
762 // Found the single Dimm
763 getDimmDataByService(asyncResp, dimmId, serviceName,
764 objectPath);
765 dimmInterface = true;
766 found = true;
767 }
768 else if (interface ==
769 "xyz.openbmc_project.Association.Definitions")
770 {
771 /* Object has associations. If this object is also a Dimm
772 * then the association might provide the LED state
773 * information. After all interfaces for this object have
774 * been checked the LED information will be gathered if the
775 * object was a Dimm
776 */
777 associationInterface = true;
778 }
779 else if (
780 interface ==
781 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition" &&
782 path.parent_path().filename() == dimmId)
783 {
784 // partitions are separate as there can be multiple per
785 // device, i.e.
786 // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1
787 // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2
788 getDimmPartitionData(asyncResp, serviceName, objectPath);
789 }
790 }
791 }
792
793 /* If a Dimm has an Association check if it has a LED */
794 if (associationInterface && dimmInterface)
795 {
796 getLocationIndicatorActive(asyncResp, objectPath);
797 }
798 }
799
800 if (!found)
801 {
802 // Dimm object not found
803 messages::resourceNotFound(asyncResp->res, "Memory", dimmId);
804 return;
805 }
806 // Set @odata only if object is found
807 asyncResp->res.jsonValue["@odata.type"] = "#Memory.v1_11_0.Memory";
808 asyncResp->res.jsonValue["@odata.id"] =
809 boost::urls::format("/redfish/v1/Systems/{}/Memory/{}",
810 BMCWEB_REDFISH_SYSTEM_URI_NAME, dimmId);
811 }
812
getDimmData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dimmId)813 inline void getDimmData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
814 const std::string& dimmId)
815 {
816 BMCWEB_LOG_DEBUG("Get dimm path for {}", dimmId);
817 constexpr std::array<std::string_view, 2> interfaces = {
818 "xyz.openbmc_project.Inventory.Item.Dimm",
819 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition"};
820
821 dbus::utility::getSubTree(
822 "/xyz/openbmc_project/inventory", 0, interfaces,
823 [asyncResp,
824 dimmId](const boost::system::error_code& ec,
825 const dbus::utility::MapperGetSubTreeResponse& subtree) {
826 afterGetDimmData(asyncResp, dimmId, ec, subtree);
827 });
828 }
829
handleSetDimmData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,bool locationIndicatorActive,const std::string & dimmPath)830 inline void handleSetDimmData(
831 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
832 bool locationIndicatorActive, const std::string& dimmPath)
833 {
834 setLocationIndicatorActive(asyncResp, dimmPath, locationIndicatorActive);
835 }
836
afterGetValidDimmPath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dimmId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & subtree,const std::function<void (const std::string & dimmPath)> & callback)837 inline void afterGetValidDimmPath(
838 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
839 const std::string& dimmId, const boost::system::error_code& ec,
840 const dbus::utility::MapperGetSubTreePathsResponse& subtree,
841 const std::function<void(const std::string& dimmPath)>& callback)
842 {
843 if (ec)
844 {
845 if (ec.value() == EBADR)
846 {
847 /* Need to report error for PATCH */
848 BMCWEB_LOG_WARNING("Dimm not found in inventory");
849 messages::resourceNotFound(asyncResp->res, "Memory", dimmId);
850 }
851 else
852 {
853 BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value());
854 messages::internalError(asyncResp->res);
855 }
856 return;
857 }
858
859 for (const auto& objectPath : subtree)
860 {
861 // Ignore any objects which don't end with our desired dimm name
862 sdbusplus::message::object_path path(objectPath);
863 if (path.filename() == dimmId)
864 {
865 callback(path);
866 return;
867 }
868 }
869
870 // Object not found
871 messages::resourceNotFound(asyncResp->res, "Memory", dimmId);
872 }
873
getValidDimmPath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dimmId,std::function<void (const std::string & dimmPath)> && callback)874 inline void getValidDimmPath(
875 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
876 const std::string& dimmId,
877 std::function<void(const std::string& dimmPath)>&& callback)
878 {
879 BMCWEB_LOG_DEBUG("Get dimm path for {}", dimmId);
880 constexpr std::array<std::string_view, 1> interfaces = {
881 "xyz.openbmc_project.Inventory.Item.Dimm"};
882
883 dbus::utility::getSubTreePaths(
884 "/xyz/openbmc_project/inventory", 0, interfaces,
885 [asyncResp, dimmId, callback{std::move(callback)}](
886 const boost::system::error_code& ec,
887 const dbus::utility::MapperGetSubTreePathsResponse& subtree) {
888 afterGetValidDimmPath(asyncResp, dimmId, ec, subtree, callback);
889 });
890 }
891
handleMemoryPatch(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & dimmId)892 inline void handleMemoryPatch(
893 App& app, const crow::Request& req,
894 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
895 const std::string& systemName, const std::string& dimmId)
896 {
897 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
898 {
899 return;
900 }
901
902 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
903 {
904 // Option currently returns no systems. TBD
905 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
906 systemName);
907 return;
908 }
909
910 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
911 {
912 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
913 systemName);
914 return;
915 }
916
917 std::optional<bool> locationIndicatorActive;
918 if (!json_util::readJsonPatch( //
919 req, asyncResp->res, //
920 "LocationIndicatorActive", locationIndicatorActive //
921 ))
922 {
923 return;
924 }
925
926 if (locationIndicatorActive)
927 {
928 getValidDimmPath(asyncResp, dimmId,
929 std::bind_front(handleSetDimmData, asyncResp,
930 *locationIndicatorActive));
931 }
932 }
933
handleMemoryGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & dimmId)934 inline void handleMemoryGet(App& app, const crow::Request& req,
935 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
936 const std::string& systemName,
937 const std::string& dimmId)
938 {
939 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
940 {
941 return;
942 }
943
944 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
945 {
946 // Option currently returns no systems. TBD
947 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
948 systemName);
949 return;
950 }
951
952 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
953 {
954 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
955 systemName);
956 return;
957 }
958
959 getDimmData(asyncResp, dimmId);
960 }
961
requestRoutesMemoryCollection(App & app)962 inline void requestRoutesMemoryCollection(App& app)
963 {
964 /**
965 * Functions triggers appropriate requests on DBus
966 */
967 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/")
968 .privileges(redfish::privileges::getMemoryCollection)
969 .methods(boost::beast::http::verb::get)(
970 [&app](const crow::Request& req,
971 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
972 const std::string& systemName) {
973 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
974 {
975 return;
976 }
977 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
978 {
979 // Option currently returns no systems. TBD
980 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
981 systemName);
982 return;
983 }
984 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
985 {
986 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
987 systemName);
988 return;
989 }
990
991 asyncResp->res.jsonValue["@odata.type"] =
992 "#MemoryCollection.MemoryCollection";
993 asyncResp->res.jsonValue["Name"] = "Memory Module Collection";
994 asyncResp->res.jsonValue["@odata.id"] =
995 boost::urls::format("/redfish/v1/Systems/{}/Memory",
996 BMCWEB_REDFISH_SYSTEM_URI_NAME);
997
998 constexpr std::array<std::string_view, 1> interfaces{
999 "xyz.openbmc_project.Inventory.Item.Dimm"};
1000 collection_util::getCollectionMembers(
1001 asyncResp,
1002 boost::urls::format("/redfish/v1/Systems/{}/Memory",
1003 BMCWEB_REDFISH_SYSTEM_URI_NAME),
1004 interfaces, "/xyz/openbmc_project/inventory");
1005 });
1006 }
1007
requestRoutesMemory(App & app)1008 inline void requestRoutesMemory(App& app)
1009 {
1010 /**
1011 * Functions triggers appropriate requests on DBus
1012 */
1013 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/")
1014 .privileges(redfish::privileges::getMemory)
1015 .methods(boost::beast::http::verb::get)(
1016 std::bind_front(handleMemoryGet, std::ref(app)));
1017
1018 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/")
1019 .privileges(redfish::privileges::patchMemory)
1020 .methods(boost::beast::http::verb::patch)(
1021 std::bind_front(handleMemoryPatch, std::ref(app)));
1022 }
1023
1024 } // namespace redfish
1025