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