xref: /openbmc/bmcweb/redfish-core/lib/memory.hpp (revision a14c9113)
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
374             .jsonValue[jsonPtr]["SecurityCapabilities"]["DataLockCapable"] =
375             *dataLockCapable;
376     }
377 
378     if (passphraseCapable != nullptr)
379     {
380         asyncResp->res
381             .jsonValue[jsonPtr]["SecurityCapabilities"]["PassphraseCapable"] =
382             *passphraseCapable;
383     }
384 
385     if (maxPassphraseCount != nullptr)
386     {
387         asyncResp->res
388             .jsonValue[jsonPtr]["SecurityCapabilities"]["MaxPassphraseCount"] =
389             *maxPassphraseCount;
390     }
391 
392     if (passphraseLockLimit != nullptr)
393     {
394         asyncResp->res
395             .jsonValue[jsonPtr]["SecurityCapabilities"]["PassphraseLockLimit"] =
396             *passphraseLockLimit;
397     }
398 }
399 
400 inline void assembleDimmProperties(
401     std::string_view dimmId,
402     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
403     const dbus::utility::DBusPropertiesMap& properties,
404     const nlohmann::json::json_pointer& jsonPtr)
405 {
406     asyncResp->res.jsonValue[jsonPtr]["Id"] = dimmId;
407     asyncResp->res.jsonValue[jsonPtr]["Name"] = "DIMM Slot";
408     asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] =
409         resource::State::Enabled;
410     asyncResp->res.jsonValue[jsonPtr]["Status"]["Health"] =
411         resource::Health::OK;
412 
413     const uint16_t* memoryDataWidth = nullptr;
414     const size_t* memorySizeInKB = nullptr;
415     const std::string* partNumber = nullptr;
416     const std::string* serialNumber = nullptr;
417     const std::string* manufacturer = nullptr;
418     const uint16_t* revisionCode = nullptr;
419     const bool* present = nullptr;
420     const uint16_t* memoryTotalWidth = nullptr;
421     const std::string* ecc = nullptr;
422     const std::string* formFactor = nullptr;
423     const std::vector<uint16_t>* allowedSpeedsMT = nullptr;
424     const size_t* memoryAttributes = nullptr;
425     const uint16_t* memoryConfiguredSpeedInMhz = nullptr;
426     const std::string* memoryType = nullptr;
427     const std::uint8_t* channel = nullptr;
428     const std::uint8_t* memoryController = nullptr;
429     const std::uint8_t* slot = nullptr;
430     const std::uint8_t* socket = nullptr;
431     const std::string* sparePartNumber = nullptr;
432     const std::string* model = nullptr;
433     const std::string* locationCode = nullptr;
434 
435     const bool success = sdbusplus::unpackPropertiesNoThrow(
436         dbus_utils::UnpackErrorPrinter(), properties, "MemoryDataWidth",
437         memoryDataWidth, "MemorySizeInKB", memorySizeInKB, "PartNumber",
438         partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer,
439         "RevisionCode", revisionCode, "Present", present, "MemoryTotalWidth",
440         memoryTotalWidth, "ECC", ecc, "FormFactor", formFactor,
441         "AllowedSpeedsMT", allowedSpeedsMT, "MemoryAttributes",
442         memoryAttributes, "MemoryConfiguredSpeedInMhz",
443         memoryConfiguredSpeedInMhz, "MemoryType", memoryType, "Channel",
444         channel, "MemoryController", memoryController, "Slot", slot, "Socket",
445         socket, "SparePartNumber", sparePartNumber, "Model", model,
446         "LocationCode", locationCode);
447 
448     if (!success)
449     {
450         messages::internalError(asyncResp->res);
451         return;
452     }
453 
454     if (memoryDataWidth != nullptr)
455     {
456         asyncResp->res.jsonValue[jsonPtr]["DataWidthBits"] = *memoryDataWidth;
457     }
458 
459     if (memorySizeInKB != nullptr)
460     {
461         asyncResp->res.jsonValue[jsonPtr]["CapacityMiB"] =
462             (*memorySizeInKB >> 10);
463     }
464 
465     if (partNumber != nullptr)
466     {
467         asyncResp->res.jsonValue[jsonPtr]["PartNumber"] = *partNumber;
468     }
469 
470     if (serialNumber != nullptr)
471     {
472         asyncResp->res.jsonValue[jsonPtr]["SerialNumber"] = *serialNumber;
473     }
474 
475     if (manufacturer != nullptr)
476     {
477         asyncResp->res.jsonValue[jsonPtr]["Manufacturer"] = *manufacturer;
478     }
479 
480     if (revisionCode != nullptr)
481     {
482         asyncResp->res.jsonValue[jsonPtr]["FirmwareRevision"] =
483             std::to_string(*revisionCode);
484     }
485 
486     if (present != nullptr && !*present)
487     {
488         asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] =
489             resource::State::Absent;
490     }
491 
492     if (memoryTotalWidth != nullptr)
493     {
494         asyncResp->res.jsonValue[jsonPtr]["BusWidthBits"] = *memoryTotalWidth;
495     }
496 
497     if (ecc != nullptr)
498     {
499         constexpr const std::array<const char*, 4> values{
500             "NoECC", "SingleBitECC", "MultiBitECC", "AddressParity"};
501 
502         for (const char* v : values)
503         {
504             if (ecc->ends_with(v))
505             {
506                 asyncResp->res.jsonValue[jsonPtr]["ErrorCorrection"] = v;
507                 break;
508             }
509         }
510     }
511 
512     if (formFactor != nullptr)
513     {
514         constexpr const std::array<const char*, 11> values{
515             "RDIMM",       "UDIMM",       "SO_DIMM",      "LRDIMM",
516             "Mini_RDIMM",  "Mini_UDIMM",  "SO_RDIMM_72b", "SO_UDIMM_72b",
517             "SO_DIMM_16b", "SO_DIMM_32b", "Die"};
518 
519         for (const char* v : values)
520         {
521             if (formFactor->ends_with(v))
522             {
523                 asyncResp->res.jsonValue[jsonPtr]["BaseModuleType"] = v;
524                 break;
525             }
526         }
527     }
528 
529     if (allowedSpeedsMT != nullptr)
530     {
531         nlohmann::json& jValue =
532             asyncResp->res.jsonValue[jsonPtr]["AllowedSpeedsMHz"];
533         jValue = nlohmann::json::array();
534         for (uint16_t subVal : *allowedSpeedsMT)
535         {
536             jValue.push_back(subVal);
537         }
538     }
539 
540     if (memoryAttributes != nullptr)
541     {
542         asyncResp->res.jsonValue[jsonPtr]["RankCount"] = *memoryAttributes;
543     }
544 
545     if (memoryConfiguredSpeedInMhz != nullptr)
546     {
547         asyncResp->res.jsonValue[jsonPtr]["OperatingSpeedMhz"] =
548             *memoryConfiguredSpeedInMhz;
549     }
550 
551     if (memoryType != nullptr)
552     {
553         std::string memoryDeviceType =
554             translateMemoryTypeToRedfish(*memoryType);
555         // Values like "Unknown" or "Other" will return empty
556         // so just leave off
557         if (!memoryDeviceType.empty())
558         {
559             asyncResp->res.jsonValue[jsonPtr]["MemoryDeviceType"] =
560                 memoryDeviceType;
561         }
562         if (memoryType->find("DDR") != std::string::npos)
563         {
564             asyncResp->res.jsonValue[jsonPtr]["MemoryType"] =
565                 memory::MemoryType::DRAM;
566         }
567         else if (memoryType->ends_with("Logical"))
568         {
569             asyncResp->res.jsonValue[jsonPtr]["MemoryType"] =
570                 memory::MemoryType::IntelOptane;
571         }
572     }
573 
574     if (channel != nullptr)
575     {
576         asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Channel"] =
577             *channel;
578     }
579 
580     if (memoryController != nullptr)
581     {
582         asyncResp->res
583             .jsonValue[jsonPtr]["MemoryLocation"]["MemoryController"] =
584             *memoryController;
585     }
586 
587     if (slot != nullptr)
588     {
589         asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Slot"] = *slot;
590     }
591 
592     if (socket != nullptr)
593     {
594         asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Socket"] = *socket;
595     }
596 
597     if (sparePartNumber != nullptr)
598     {
599         asyncResp->res.jsonValue[jsonPtr]["SparePartNumber"] = *sparePartNumber;
600     }
601 
602     if (model != nullptr)
603     {
604         asyncResp->res.jsonValue[jsonPtr]["Model"] = *model;
605     }
606 
607     if (locationCode != nullptr)
608     {
609         asyncResp->res
610             .jsonValue[jsonPtr]["Location"]["PartLocation"]["ServiceLabel"] =
611             *locationCode;
612     }
613 
614     getPersistentMemoryProperties(asyncResp, properties, jsonPtr);
615 }
616 
617 inline void getDimmDataByService(
618     std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& dimmId,
619     const std::string& service, const std::string& objPath)
620 {
621     BMCWEB_LOG_DEBUG("Get available system components.");
622     sdbusplus::asio::getAllProperties(
623         *crow::connections::systemBus, service, objPath, "",
624         [dimmId, asyncResp{std::move(asyncResp)}](
625             const boost::system::error_code& ec,
626             const dbus::utility::DBusPropertiesMap& properties) {
627             if (ec)
628             {
629                 BMCWEB_LOG_DEBUG("DBUS response error");
630                 messages::internalError(asyncResp->res);
631                 return;
632             }
633             assembleDimmProperties(dimmId, asyncResp, properties,
634                                    ""_json_pointer);
635         });
636 }
637 
638 inline void assembleDimmPartitionData(
639     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
640     const dbus::utility::DBusPropertiesMap& properties,
641     const nlohmann::json::json_pointer& regionPtr)
642 {
643     const std::string* memoryClassification = nullptr;
644     const uint64_t* offsetInKiB = nullptr;
645     const std::string* partitionId = nullptr;
646     const bool* passphraseState = nullptr;
647     const uint64_t* sizeInKiB = nullptr;
648 
649     const bool success = sdbusplus::unpackPropertiesNoThrow(
650         dbus_utils::UnpackErrorPrinter(), properties, "MemoryClassification",
651         memoryClassification, "OffsetInKiB", offsetInKiB, "PartitionId",
652         partitionId, "PassphraseState", passphraseState, "SizeInKiB",
653         sizeInKiB);
654 
655     if (!success)
656     {
657         messages::internalError(asyncResp->res);
658         return;
659     }
660 
661     nlohmann::json::object_t partition;
662 
663     if (memoryClassification != nullptr)
664     {
665         partition["MemoryClassification"] = *memoryClassification;
666     }
667 
668     if (offsetInKiB != nullptr)
669     {
670         partition["OffsetMiB"] = (*offsetInKiB >> 10);
671     }
672 
673     if (partitionId != nullptr)
674     {
675         partition["RegionId"] = *partitionId;
676     }
677 
678     if (passphraseState != nullptr)
679     {
680         partition["PassphraseEnabled"] = *passphraseState;
681     }
682 
683     if (sizeInKiB != nullptr)
684     {
685         partition["SizeMiB"] = (*sizeInKiB >> 10);
686     }
687 
688     asyncResp->res.jsonValue[regionPtr].emplace_back(std::move(partition));
689 }
690 
691 inline void getDimmPartitionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
692                                  const std::string& service,
693                                  const std::string& path)
694 {
695     sdbusplus::asio::getAllProperties(
696         *crow::connections::systemBus, service, path,
697         "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition",
698         [asyncResp{std::move(asyncResp)}](
699             const boost::system::error_code& ec,
700             const dbus::utility::DBusPropertiesMap& properties) {
701             if (ec)
702             {
703                 BMCWEB_LOG_DEBUG("DBUS response error");
704                 messages::internalError(asyncResp->res);
705 
706                 return;
707             }
708             nlohmann::json::json_pointer regionPtr = "/Regions"_json_pointer;
709             assembleDimmPartitionData(asyncResp, properties, regionPtr);
710         }
711 
712     );
713 }
714 
715 inline void getDimmData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
716                         const std::string& dimmId)
717 {
718     BMCWEB_LOG_DEBUG("Get available system dimm resources.");
719     constexpr std::array<std::string_view, 2> dimmInterfaces = {
720         "xyz.openbmc_project.Inventory.Item.Dimm",
721         "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition"};
722     dbus::utility::getSubTree(
723         "/xyz/openbmc_project/inventory", 0, dimmInterfaces,
724         [dimmId, asyncResp{std::move(asyncResp)}](
725             const boost::system::error_code& ec,
726             const dbus::utility::MapperGetSubTreeResponse& subtree) {
727             if (ec)
728             {
729                 BMCWEB_LOG_DEBUG("DBUS response error");
730                 messages::internalError(asyncResp->res);
731 
732                 return;
733             }
734             bool found = false;
735             for (const auto& [rawPath, object] : subtree)
736             {
737                 sdbusplus::message::object_path path(rawPath);
738                 for (const auto& [service, interfaces] : object)
739                 {
740                     for (const auto& interface : interfaces)
741                     {
742                         if (interface ==
743                                 "xyz.openbmc_project.Inventory.Item.Dimm" &&
744                             path.filename() == dimmId)
745                         {
746                             getDimmDataByService(asyncResp, dimmId, service,
747                                                  rawPath);
748                             found = true;
749                         }
750 
751                         // partitions are separate as there can be multiple
752                         // per
753                         // device, i.e.
754                         // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1
755                         // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2
756                         if (interface ==
757                                 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition" &&
758                             path.parent_path().filename() == dimmId)
759                         {
760                             getDimmPartitionData(asyncResp, service, rawPath);
761                         }
762                     }
763                 }
764             }
765             // Object not found
766             if (!found)
767             {
768                 messages::resourceNotFound(asyncResp->res, "Memory", dimmId);
769                 return;
770             }
771             // Set @odata only if object is found
772             asyncResp->res.jsonValue["@odata.type"] = "#Memory.v1_11_0.Memory";
773             asyncResp->res.jsonValue["@odata.id"] =
774                 boost::urls::format("/redfish/v1/Systems/{}/Memory/{}",
775                                     BMCWEB_REDFISH_SYSTEM_URI_NAME, dimmId);
776             return;
777         });
778 }
779 
780 inline void requestRoutesMemoryCollection(App& app)
781 {
782     /**
783      * Functions triggers appropriate requests on DBus
784      */
785     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/")
786         .privileges(redfish::privileges::getMemoryCollection)
787         .methods(boost::beast::http::verb::get)(
788             [&app](const crow::Request& req,
789                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
790                    const std::string& systemName) {
791                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
792                 {
793                     return;
794                 }
795                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
796                 {
797                     // Option currently returns no systems.  TBD
798                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
799                                                systemName);
800                     return;
801                 }
802                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
803                 {
804                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
805                                                systemName);
806                     return;
807                 }
808 
809                 asyncResp->res.jsonValue["@odata.type"] =
810                     "#MemoryCollection.MemoryCollection";
811                 asyncResp->res.jsonValue["Name"] = "Memory Module Collection";
812                 asyncResp->res.jsonValue["@odata.id"] =
813                     boost::urls::format("/redfish/v1/Systems/{}/Memory",
814                                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
815 
816                 constexpr std::array<std::string_view, 1> interfaces{
817                     "xyz.openbmc_project.Inventory.Item.Dimm"};
818                 collection_util::getCollectionMembers(
819                     asyncResp,
820                     boost::urls::format("/redfish/v1/Systems/{}/Memory",
821                                         BMCWEB_REDFISH_SYSTEM_URI_NAME),
822                     interfaces, "/xyz/openbmc_project/inventory");
823             });
824 }
825 
826 inline void requestRoutesMemory(App& app)
827 {
828     /**
829      * Functions triggers appropriate requests on DBus
830      */
831     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/")
832         .privileges(redfish::privileges::getMemory)
833         .methods(boost::beast::http::verb::get)(
834             [&app](const crow::Request& req,
835                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
836                    const std::string& systemName, const std::string& dimmId) {
837                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
838                 {
839                     return;
840                 }
841 
842                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
843                 {
844                     // Option currently returns no systems.  TBD
845                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
846                                                systemName);
847                     return;
848                 }
849 
850                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
851                 {
852                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
853                                                systemName);
854                     return;
855                 }
856 
857                 getDimmData(asyncResp, dimmId);
858             });
859 }
860 
861 } // namespace redfish
862