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