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