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