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