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