xref: /openbmc/bmcweb/redfish-core/lib/memory.hpp (revision 8f79c5b6)
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>& asyncResp,
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     asyncResp->res.jsonValue[jsonPtr][key] = "0x" + intToHexString(*value, 4);
164 }
165 
166 inline void getPersistentMemoryProperties(
167     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
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(asyncResp->res);
225         return;
226     }
227 
228     dimmPropToHex(asyncResp, "ModuleManufacturerID", moduleManufacturerID,
229                   jsonPtr);
230     dimmPropToHex(asyncResp, "ModuleProductID", moduleProductID, jsonPtr);
231     dimmPropToHex(asyncResp, "MemorySubsystemControllerManufacturerID",
232                   subsystemVendorID, jsonPtr);
233     dimmPropToHex(asyncResp, "MemorySubsystemControllerProductID",
234                   subsystemDeviceID, jsonPtr);
235 
236     if (volatileRegionSizeLimitInKiB != nullptr)
237     {
238         asyncResp->res.jsonValue[jsonPtr]["VolatileRegionSizeLimitMiB"] =
239             (*volatileRegionSizeLimitInKiB) >> 10;
240     }
241 
242     if (pmRegionSizeLimitInKiB != nullptr)
243     {
244         asyncResp->res.jsonValue[jsonPtr]["PersistentRegionSizeLimitMiB"] =
245             (*pmRegionSizeLimitInKiB) >> 10;
246     }
247 
248     if (volatileSizeInKiB != nullptr)
249     {
250         asyncResp->res.jsonValue[jsonPtr]["VolatileSizeMiB"] =
251             (*volatileSizeInKiB) >> 10;
252     }
253 
254     if (pmSizeInKiB != nullptr)
255     {
256         asyncResp->res.jsonValue[jsonPtr]["NonVolatileSizeMiB"] =
257             (*pmSizeInKiB) >> 10;
258     }
259 
260     if (cacheSizeInKB != nullptr)
261     {
262         asyncResp->res.jsonValue[jsonPtr]["CacheSizeMiB"] =
263             (*cacheSizeInKB >> 10);
264     }
265 
266     if (voltaileRegionMaxSizeInKib != nullptr)
267     {
268         asyncResp->res.jsonValue[jsonPtr]["VolatileRegionSizeMaxMiB"] =
269             (*voltaileRegionMaxSizeInKib) >> 10;
270     }
271 
272     if (pmRegionMaxSizeInKiB != nullptr)
273     {
274         asyncResp->res.jsonValue[jsonPtr]["PersistentRegionSizeMaxMiB"] =
275             (*pmRegionMaxSizeInKiB) >> 10;
276     }
277 
278     if (allocationIncrementInKiB != nullptr)
279     {
280         asyncResp->res.jsonValue[jsonPtr]["AllocationIncrementMiB"] =
281             (*allocationIncrementInKiB) >> 10;
282     }
283 
284     if (allocationAlignmentInKiB != nullptr)
285     {
286         asyncResp->res.jsonValue[jsonPtr]["AllocationAlignmentMiB"] =
287             (*allocationAlignmentInKiB) >> 10;
288     }
289 
290     if (volatileRegionNumberLimit != nullptr)
291     {
292         asyncResp->res.jsonValue[jsonPtr]["VolatileRegionNumberLimit"] =
293             *volatileRegionNumberLimit;
294     }
295 
296     if (pmRegionNumberLimit != nullptr)
297     {
298         asyncResp->res.jsonValue[jsonPtr]["PersistentRegionNumberLimit"] =
299             *pmRegionNumberLimit;
300     }
301 
302     if (spareDeviceCount != nullptr)
303     {
304         asyncResp->res.jsonValue[jsonPtr]["SpareDeviceCount"] =
305             *spareDeviceCount;
306     }
307 
308     if (isSpareDeviceInUse != nullptr)
309     {
310         asyncResp->res.jsonValue[jsonPtr]["IsSpareDeviceEnabled"] =
311             *isSpareDeviceInUse;
312     }
313 
314     if (isRankSpareEnabled != nullptr)
315     {
316         asyncResp->res.jsonValue[jsonPtr]["IsRankSpareEnabled"] =
317             *isRankSpareEnabled;
318     }
319 
320     if (maxAveragePowerLimitmW != nullptr)
321     {
322         asyncResp->res.jsonValue[jsonPtr]["MaxTDPMilliWatts"] =
323             *maxAveragePowerLimitmW;
324     }
325 
326     if (configurationLocked != nullptr)
327     {
328         asyncResp->res.jsonValue[jsonPtr]["ConfigurationLocked"] =
329             *configurationLocked;
330     }
331 
332     if (allowedMemoryModes != nullptr)
333     {
334         constexpr const std::array<const char*, 3> values{"Volatile", "PMEM",
335                                                           "Block"};
336 
337         for (const char* v : values)
338         {
339             if (allowedMemoryModes->ends_with(v))
340             {
341                 asyncResp->res.jsonValue[jsonPtr]["OperatingMemoryModes"]
342                     .push_back(v);
343                 break;
344             }
345         }
346     }
347 
348     if (memoryMedia != nullptr)
349     {
350         constexpr const std::array<const char*, 3> values{"DRAM", "NAND",
351                                                           "Intel3DXPoint"};
352 
353         for (const char* v : values)
354         {
355             if (memoryMedia->ends_with(v))
356             {
357                 asyncResp->res.jsonValue[jsonPtr]["MemoryMedia"].push_back(v);
358                 break;
359             }
360         }
361     }
362 
363     if (configurationLockCapable != nullptr)
364     {
365         asyncResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
366                                 ["ConfigurationLockCapable"] =
367             *configurationLockCapable;
368     }
369 
370     if (dataLockCapable != nullptr)
371     {
372         asyncResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
373                                 ["DataLockCapable"] = *dataLockCapable;
374     }
375 
376     if (passphraseCapable != nullptr)
377     {
378         asyncResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
379                                 ["PassphraseCapable"] = *passphraseCapable;
380     }
381 
382     if (maxPassphraseCount != nullptr)
383     {
384         asyncResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
385                                 ["MaxPassphraseCount"] = *maxPassphraseCount;
386     }
387 
388     if (passphraseLockLimit != nullptr)
389     {
390         asyncResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
391                                 ["PassphraseLockLimit"] = *passphraseLockLimit;
392     }
393 }
394 
395 inline void
396     assembleDimmProperties(std::string_view dimmId,
397                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
398                            const dbus::utility::DBusPropertiesMap& properties,
399                            const nlohmann::json::json_pointer& jsonPtr)
400 {
401     asyncResp->res.jsonValue[jsonPtr]["Id"] = dimmId;
402     asyncResp->res.jsonValue[jsonPtr]["Name"] = "DIMM Slot";
403     asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Enabled";
404     asyncResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "OK";
405 
406     const uint16_t* memoryDataWidth = nullptr;
407     const size_t* memorySizeInKB = nullptr;
408     const std::string* partNumber = nullptr;
409     const std::string* serialNumber = nullptr;
410     const std::string* manufacturer = nullptr;
411     const uint16_t* revisionCode = nullptr;
412     const bool* present = nullptr;
413     const uint16_t* memoryTotalWidth = nullptr;
414     const std::string* ecc = nullptr;
415     const std::string* formFactor = nullptr;
416     const std::vector<uint16_t>* allowedSpeedsMT = nullptr;
417     const size_t* memoryAttributes = nullptr;
418     const uint16_t* memoryConfiguredSpeedInMhz = nullptr;
419     const std::string* memoryType = nullptr;
420     const std::uint8_t* channel = nullptr;
421     const std::uint8_t* memoryController = nullptr;
422     const std::uint8_t* slot = nullptr;
423     const std::uint8_t* socket = nullptr;
424     const std::string* sparePartNumber = nullptr;
425     const std::string* model = nullptr;
426     const std::string* locationCode = nullptr;
427 
428     const bool success = sdbusplus::unpackPropertiesNoThrow(
429         dbus_utils::UnpackErrorPrinter(), properties, "MemoryDataWidth",
430         memoryDataWidth, "MemorySizeInKB", memorySizeInKB, "PartNumber",
431         partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer,
432         "RevisionCode", revisionCode, "Present", present, "MemoryTotalWidth",
433         memoryTotalWidth, "ECC", ecc, "FormFactor", formFactor,
434         "AllowedSpeedsMT", allowedSpeedsMT, "MemoryAttributes",
435         memoryAttributes, "MemoryConfiguredSpeedInMhz",
436         memoryConfiguredSpeedInMhz, "MemoryType", memoryType, "Channel",
437         channel, "MemoryController", memoryController, "Slot", slot, "Socket",
438         socket, "SparePartNumber", sparePartNumber, "Model", model,
439         "LocationCode", locationCode);
440 
441     if (!success)
442     {
443         messages::internalError(asyncResp->res);
444         return;
445     }
446 
447     if (memoryDataWidth != nullptr)
448     {
449         asyncResp->res.jsonValue[jsonPtr]["DataWidthBits"] = *memoryDataWidth;
450     }
451 
452     if (memorySizeInKB != nullptr)
453     {
454         asyncResp->res.jsonValue[jsonPtr]["CapacityMiB"] =
455             (*memorySizeInKB >> 10);
456     }
457 
458     if (partNumber != nullptr)
459     {
460         asyncResp->res.jsonValue[jsonPtr]["PartNumber"] = *partNumber;
461     }
462 
463     if (serialNumber != nullptr)
464     {
465         asyncResp->res.jsonValue[jsonPtr]["SerialNumber"] = *serialNumber;
466     }
467 
468     if (manufacturer != nullptr)
469     {
470         asyncResp->res.jsonValue[jsonPtr]["Manufacturer"] = *manufacturer;
471     }
472 
473     if (revisionCode != nullptr)
474     {
475         asyncResp->res.jsonValue[jsonPtr]["FirmwareRevision"] =
476             std::to_string(*revisionCode);
477     }
478 
479     if (present != nullptr && !*present)
480     {
481         asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent";
482     }
483 
484     if (memoryTotalWidth != nullptr)
485     {
486         asyncResp->res.jsonValue[jsonPtr]["BusWidthBits"] = *memoryTotalWidth;
487     }
488 
489     if (ecc != nullptr)
490     {
491         constexpr const std::array<const char*, 4> values{
492             "NoECC", "SingleBitECC", "MultiBitECC", "AddressParity"};
493 
494         for (const char* v : values)
495         {
496             if (ecc->ends_with(v))
497             {
498                 asyncResp->res.jsonValue[jsonPtr]["ErrorCorrection"] = v;
499                 break;
500             }
501         }
502     }
503 
504     if (formFactor != nullptr)
505     {
506         constexpr const std::array<const char*, 11> values{
507             "RDIMM",       "UDIMM",       "SO_DIMM",      "LRDIMM",
508             "Mini_RDIMM",  "Mini_UDIMM",  "SO_RDIMM_72b", "SO_UDIMM_72b",
509             "SO_DIMM_16b", "SO_DIMM_32b", "Die"};
510 
511         for (const char* v : values)
512         {
513             if (formFactor->ends_with(v))
514             {
515                 asyncResp->res.jsonValue[jsonPtr]["BaseModuleType"] = v;
516                 break;
517             }
518         }
519     }
520 
521     if (allowedSpeedsMT != nullptr)
522     {
523         nlohmann::json& jValue =
524             asyncResp->res.jsonValue[jsonPtr]["AllowedSpeedsMHz"];
525         jValue = nlohmann::json::array();
526         for (uint16_t subVal : *allowedSpeedsMT)
527         {
528             jValue.push_back(subVal);
529         }
530     }
531 
532     if (memoryAttributes != nullptr)
533     {
534         asyncResp->res.jsonValue[jsonPtr]["RankCount"] = *memoryAttributes;
535     }
536 
537     if (memoryConfiguredSpeedInMhz != nullptr)
538     {
539         asyncResp->res.jsonValue[jsonPtr]["OperatingSpeedMhz"] =
540             *memoryConfiguredSpeedInMhz;
541     }
542 
543     if (memoryType != nullptr)
544     {
545         std::string memoryDeviceType =
546             translateMemoryTypeToRedfish(*memoryType);
547         // Values like "Unknown" or "Other" will return empty
548         // so just leave off
549         if (!memoryDeviceType.empty())
550         {
551             asyncResp->res.jsonValue[jsonPtr]["MemoryDeviceType"] =
552                 memoryDeviceType;
553         }
554         if (memoryType->find("DDR") != std::string::npos)
555         {
556             asyncResp->res.jsonValue[jsonPtr]["MemoryType"] = "DRAM";
557         }
558         else if (memoryType->ends_with("Logical"))
559         {
560             asyncResp->res.jsonValue[jsonPtr]["MemoryType"] = "IntelOptane";
561         }
562     }
563 
564     if (channel != nullptr)
565     {
566         asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Channel"] =
567             *channel;
568     }
569 
570     if (memoryController != nullptr)
571     {
572         asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]
573                                 ["MemoryController"] = *memoryController;
574     }
575 
576     if (slot != nullptr)
577     {
578         asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Slot"] = *slot;
579     }
580 
581     if (socket != nullptr)
582     {
583         asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Socket"] = *socket;
584     }
585 
586     if (sparePartNumber != nullptr)
587     {
588         asyncResp->res.jsonValue[jsonPtr]["SparePartNumber"] = *sparePartNumber;
589     }
590 
591     if (model != nullptr)
592     {
593         asyncResp->res.jsonValue[jsonPtr]["Model"] = *model;
594     }
595 
596     if (locationCode != nullptr)
597     {
598         asyncResp->res.jsonValue[jsonPtr]["Location"]["PartLocation"]
599                                 ["ServiceLabel"] = *locationCode;
600     }
601 
602     getPersistentMemoryProperties(asyncResp, properties, jsonPtr);
603 }
604 
605 inline void getDimmDataByService(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
606                                  const std::string& dimmId,
607                                  const std::string& service,
608                                  const std::string& objPath)
609 {
610     if constexpr (bmcwebEnableHealthPopulate)
611     {
612         auto health = std::make_shared<HealthPopulate>(asyncResp);
613         health->selfPath = objPath;
614         health->populate();
615     }
616 
617     BMCWEB_LOG_DEBUG("Get available system components.");
618     sdbusplus::asio::getAllProperties(
619         *crow::connections::systemBus, service, objPath, "",
620         [dimmId, asyncResp{std::move(asyncResp)}](
621             const boost::system::error_code& ec,
622             const dbus::utility::DBusPropertiesMap& properties) {
623         if (ec)
624         {
625             BMCWEB_LOG_DEBUG("DBUS response error");
626             messages::internalError(asyncResp->res);
627             return;
628         }
629         assembleDimmProperties(dimmId, asyncResp, properties, ""_json_pointer);
630     });
631 }
632 
633 inline void assembleDimmPartitionData(
634     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
635     const dbus::utility::DBusPropertiesMap& properties,
636     const nlohmann::json::json_pointer& regionPtr)
637 {
638     const std::string* memoryClassification = nullptr;
639     const uint64_t* offsetInKiB = nullptr;
640     const std::string* partitionId = nullptr;
641     const bool* passphraseState = nullptr;
642     const uint64_t* sizeInKiB = nullptr;
643 
644     const bool success = sdbusplus::unpackPropertiesNoThrow(
645         dbus_utils::UnpackErrorPrinter(), properties, "MemoryClassification",
646         memoryClassification, "OffsetInKiB", offsetInKiB, "PartitionId",
647         partitionId, "PassphraseState", passphraseState, "SizeInKiB",
648         sizeInKiB);
649 
650     if (!success)
651     {
652         messages::internalError(asyncResp->res);
653         return;
654     }
655 
656     nlohmann::json::object_t partition;
657 
658     if (memoryClassification != nullptr)
659     {
660         partition["MemoryClassification"] = *memoryClassification;
661     }
662 
663     if (offsetInKiB != nullptr)
664     {
665         partition["OffsetMiB"] = (*offsetInKiB >> 10);
666     }
667 
668     if (partitionId != nullptr)
669     {
670         partition["RegionId"] = *partitionId;
671     }
672 
673     if (passphraseState != nullptr)
674     {
675         partition["PassphraseEnabled"] = *passphraseState;
676     }
677 
678     if (sizeInKiB != nullptr)
679     {
680         partition["SizeMiB"] = (*sizeInKiB >> 10);
681     }
682 
683     asyncResp->res.jsonValue[regionPtr].emplace_back(std::move(partition));
684 }
685 
686 inline void getDimmPartitionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
687                                  const std::string& service,
688                                  const std::string& path)
689 {
690     sdbusplus::asio::getAllProperties(
691         *crow::connections::systemBus, service, path,
692         "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition",
693         [asyncResp{std::move(asyncResp)}](
694             const boost::system::error_code& ec,
695             const dbus::utility::DBusPropertiesMap& properties) {
696         if (ec)
697         {
698             BMCWEB_LOG_DEBUG("DBUS response error");
699             messages::internalError(asyncResp->res);
700 
701             return;
702         }
703         nlohmann::json::json_pointer regionPtr = "/Regions"_json_pointer;
704         assembleDimmPartitionData(asyncResp, properties, regionPtr);
705     }
706 
707     );
708 }
709 
710 inline void getDimmData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
711                         const std::string& dimmId)
712 {
713     BMCWEB_LOG_DEBUG("Get available system dimm resources.");
714     constexpr std::array<std::string_view, 2> dimmInterfaces = {
715         "xyz.openbmc_project.Inventory.Item.Dimm",
716         "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition"};
717     dbus::utility::getSubTree(
718         "/xyz/openbmc_project/inventory", 0, dimmInterfaces,
719         [dimmId, asyncResp{std::move(asyncResp)}](
720             const boost::system::error_code& ec,
721             const dbus::utility::MapperGetSubTreeResponse& subtree) {
722         if (ec)
723         {
724             BMCWEB_LOG_DEBUG("DBUS response error");
725             messages::internalError(asyncResp->res);
726 
727             return;
728         }
729         bool found = false;
730         for (const auto& [rawPath, object] : subtree)
731         {
732             sdbusplus::message::object_path path(rawPath);
733             for (const auto& [service, interfaces] : object)
734             {
735                 for (const auto& interface : interfaces)
736                 {
737                     if (interface ==
738                             "xyz.openbmc_project.Inventory.Item.Dimm" &&
739                         path.filename() == dimmId)
740                     {
741                         getDimmDataByService(asyncResp, dimmId, service,
742                                              rawPath);
743                         found = true;
744                     }
745 
746                     // partitions are separate as there can be multiple
747                     // per
748                     // device, i.e.
749                     // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1
750                     // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2
751                     if (interface ==
752                             "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition" &&
753                         path.parent_path().filename() == dimmId)
754                     {
755                         getDimmPartitionData(asyncResp, service, rawPath);
756                     }
757                 }
758             }
759         }
760         // Object not found
761         if (!found)
762         {
763             messages::resourceNotFound(asyncResp->res, "Memory", dimmId);
764             return;
765         }
766         // Set @odata only if object is found
767         asyncResp->res.jsonValue["@odata.type"] = "#Memory.v1_11_0.Memory";
768         asyncResp->res.jsonValue["@odata.id"] =
769             boost::urls::format("/redfish/v1/Systems/system/Memory/{}", 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 (bmcwebEnableMultiHost)
790         {
791             // Option currently returns no systems.  TBD
792             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
793                                        systemName);
794             return;
795         }
796         if (systemName != "system")
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"] =
807             "/redfish/v1/Systems/system/Memory";
808 
809         constexpr std::array<std::string_view, 1> interfaces{
810             "xyz.openbmc_project.Inventory.Item.Dimm"};
811         collection_util::getCollectionMembers(
812             asyncResp, boost::urls::url("/redfish/v1/Systems/system/Memory"),
813             interfaces, "/xyz/openbmc_project/inventory");
814     });
815 }
816 
817 inline void requestRoutesMemory(App& app)
818 {
819     /**
820      * Functions triggers appropriate requests on DBus
821      */
822     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/")
823         .privileges(redfish::privileges::getMemory)
824         .methods(boost::beast::http::verb::get)(
825             [&app](const crow::Request& req,
826                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
827                    const std::string& systemName, const std::string& dimmId) {
828         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
829         {
830             return;
831         }
832 
833         if constexpr (bmcwebEnableMultiHost)
834         {
835             // Option currently returns no systems.  TBD
836             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
837                                        systemName);
838             return;
839         }
840 
841         if (systemName != "system")
842         {
843             messages::resourceNotFound(asyncResp->res, "ComputerSystem",
844                                        systemName);
845             return;
846         }
847 
848         getDimmData(asyncResp, dimmId);
849     });
850 }
851 
852 } // namespace redfish
853