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