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