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