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