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