xref: /openbmc/bmcweb/redfish-core/lib/memory.hpp (revision 6980011b042c8706b65af9606d1308e96d919b02)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4 #pragma once
5 
6 #include "bmcweb_config.h"
7 
8 #include "app.hpp"
9 #include "async_resp.hpp"
10 #include "dbus_utility.hpp"
11 #include "error_messages.hpp"
12 #include "generated/enums/memory.hpp"
13 #include "generated/enums/resource.hpp"
14 #include "http_request.hpp"
15 #include "led.hpp"
16 #include "logging.hpp"
17 #include "query.hpp"
18 #include "registries/privilege_registry.hpp"
19 #include "utils/collection.hpp"
20 #include "utils/dbus_utils.hpp"
21 #include "utils/hex_utils.hpp"
22 #include "utils/json_utils.hpp"
23 
24 #include <asm-generic/errno.h>
25 
26 #include <boost/beast/http/verb.hpp>
27 #include <boost/system/error_code.hpp>
28 #include <boost/url/format.hpp>
29 #include <nlohmann/json.hpp>
30 #include <sdbusplus/message/native_types.hpp>
31 #include <sdbusplus/unpack_properties.hpp>
32 
33 #include <array>
34 #include <cstddef>
35 #include <cstdint>
36 #include <functional>
37 #include <memory>
38 #include <optional>
39 #include <string>
40 #include <string_view>
41 #include <utility>
42 #include <vector>
43 
44 namespace redfish
45 {
46 
translateMemoryTypeToRedfish(const std::string & memoryType)47 inline std::string translateMemoryTypeToRedfish(const std::string& memoryType)
48 {
49     if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR")
50     {
51         return "DDR";
52     }
53     if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2")
54     {
55         return "DDR2";
56     }
57     if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR3")
58     {
59         return "DDR3";
60     }
61     if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR4")
62     {
63         return "DDR4";
64     }
65     if (memoryType ==
66         "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR4E_SDRAM")
67     {
68         return "DDR4E_SDRAM";
69     }
70     if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR5")
71     {
72         return "DDR5";
73     }
74     if (memoryType ==
75         "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.LPDDR4_SDRAM")
76     {
77         return "LPDDR4_SDRAM";
78     }
79     if (memoryType ==
80         "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.LPDDR3_SDRAM")
81     {
82         return "LPDDR3_SDRAM";
83     }
84     if (memoryType ==
85         "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2_SDRAM_FB_DIMM")
86     {
87         return "DDR2_SDRAM_FB_DIMM";
88     }
89     if (memoryType ==
90         "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2_SDRAM_FB_DIMM_PROB")
91     {
92         return "DDR2_SDRAM_FB_DIMM_PROBE";
93     }
94     if (memoryType ==
95         "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR_SGRAM")
96     {
97         return "DDR_SGRAM";
98     }
99     if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.ROM")
100     {
101         return "ROM";
102     }
103     if (memoryType ==
104         "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.SDRAM")
105     {
106         return "SDRAM";
107     }
108     if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.EDO")
109     {
110         return "EDO";
111     }
112     if (memoryType ==
113         "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.FastPageMode")
114     {
115         return "FastPageMode";
116     }
117     if (memoryType ==
118         "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.PipelinedNibble")
119     {
120         return "PipelinedNibble";
121     }
122     if (memoryType ==
123         "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.Logical")
124     {
125         return "Logical";
126     }
127     if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM")
128     {
129         return "HBM";
130     }
131     if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM2")
132     {
133         return "HBM2";
134     }
135     if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM3")
136     {
137         return "HBM3";
138     }
139     // This is values like Other or Unknown
140     // Also D-Bus values:
141     // DRAM
142     // EDRAM
143     // VRAM
144     // SRAM
145     // RAM
146     // FLASH
147     // EEPROM
148     // FEPROM
149     // EPROM
150     // CDRAM
151     // ThreeDRAM
152     // RDRAM
153     // FBD2
154     // LPDDR_SDRAM
155     // LPDDR2_SDRAM
156     // LPDDR5_SDRAM
157     return "";
158 }
159 
dimmPropToHex(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const char * key,const uint16_t * value,const nlohmann::json::json_pointer & jsonPtr)160 inline void dimmPropToHex(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
161                           const char* key, const uint16_t* value,
162                           const nlohmann::json::json_pointer& jsonPtr)
163 {
164     if (value == nullptr)
165     {
166         return;
167     }
168     asyncResp->res.jsonValue[jsonPtr][key] = "0x" + intToHexString(*value, 4);
169 }
170 
getPersistentMemoryProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const dbus::utility::DBusPropertiesMap & properties,const nlohmann::json::json_pointer & jsonPtr)171 inline void getPersistentMemoryProperties(
172     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
173     const dbus::utility::DBusPropertiesMap& properties,
174     const nlohmann::json::json_pointer& jsonPtr)
175 {
176     const uint16_t* moduleManufacturerID = nullptr;
177     const uint16_t* moduleProductID = nullptr;
178     const uint16_t* subsystemVendorID = nullptr;
179     const uint16_t* subsystemDeviceID = nullptr;
180     const uint64_t* volatileRegionSizeLimitInKiB = nullptr;
181     const uint64_t* pmRegionSizeLimitInKiB = nullptr;
182     const uint64_t* volatileSizeInKiB = nullptr;
183     const uint64_t* pmSizeInKiB = nullptr;
184     const uint64_t* cacheSizeInKB = nullptr;
185     const uint64_t* voltaileRegionMaxSizeInKib = nullptr;
186     const uint64_t* pmRegionMaxSizeInKiB = nullptr;
187     const uint64_t* allocationIncrementInKiB = nullptr;
188     const uint64_t* allocationAlignmentInKiB = nullptr;
189     const uint64_t* volatileRegionNumberLimit = nullptr;
190     const uint64_t* pmRegionNumberLimit = nullptr;
191     const uint64_t* spareDeviceCount = nullptr;
192     const bool* isSpareDeviceInUse = nullptr;
193     const bool* isRankSpareEnabled = nullptr;
194     const std::vector<uint32_t>* maxAveragePowerLimitmW = nullptr;
195     const bool* configurationLocked = nullptr;
196     const std::string* allowedMemoryModes = nullptr;
197     const std::string* memoryMedia = nullptr;
198     const bool* configurationLockCapable = nullptr;
199     const bool* dataLockCapable = nullptr;
200     const bool* passphraseCapable = nullptr;
201     const uint64_t* maxPassphraseCount = nullptr;
202     const uint64_t* passphraseLockLimit = nullptr;
203 
204     const bool success = sdbusplus::unpackPropertiesNoThrow(
205         dbus_utils::UnpackErrorPrinter(), properties, "ModuleManufacturerID",
206         moduleManufacturerID, "ModuleProductID", moduleProductID,
207         "SubsystemVendorID", subsystemVendorID, "SubsystemDeviceID",
208         subsystemDeviceID, "VolatileRegionSizeLimitInKiB",
209         volatileRegionSizeLimitInKiB, "PmRegionSizeLimitInKiB",
210         pmRegionSizeLimitInKiB, "VolatileSizeInKiB", volatileSizeInKiB,
211         "PmSizeInKiB", pmSizeInKiB, "CacheSizeInKB", cacheSizeInKB,
212         "VoltaileRegionMaxSizeInKib", voltaileRegionMaxSizeInKib,
213         "PmRegionMaxSizeInKiB", pmRegionMaxSizeInKiB,
214         "AllocationIncrementInKiB", allocationIncrementInKiB,
215         "AllocationAlignmentInKiB", allocationAlignmentInKiB,
216         "VolatileRegionNumberLimit", volatileRegionNumberLimit,
217         "PmRegionNumberLimit", pmRegionNumberLimit, "SpareDeviceCount",
218         spareDeviceCount, "IsSpareDeviceInUse", isSpareDeviceInUse,
219         "IsRankSpareEnabled", isRankSpareEnabled, "MaxAveragePowerLimitmW",
220         maxAveragePowerLimitmW, "ConfigurationLocked", configurationLocked,
221         "AllowedMemoryModes", allowedMemoryModes, "MemoryMedia", memoryMedia,
222         "ConfigurationLockCapable", configurationLockCapable, "DataLockCapable",
223         dataLockCapable, "PassphraseCapable", passphraseCapable,
224         "MaxPassphraseCount", maxPassphraseCount, "PassphraseLockLimit",
225         passphraseLockLimit);
226 
227     if (!success)
228     {
229         messages::internalError(asyncResp->res);
230         return;
231     }
232 
233     dimmPropToHex(asyncResp, "ModuleManufacturerID", moduleManufacturerID,
234                   jsonPtr);
235     dimmPropToHex(asyncResp, "ModuleProductID", moduleProductID, jsonPtr);
236     dimmPropToHex(asyncResp, "MemorySubsystemControllerManufacturerID",
237                   subsystemVendorID, jsonPtr);
238     dimmPropToHex(asyncResp, "MemorySubsystemControllerProductID",
239                   subsystemDeviceID, jsonPtr);
240 
241     if (volatileRegionSizeLimitInKiB != nullptr)
242     {
243         asyncResp->res.jsonValue[jsonPtr]["VolatileRegionSizeLimitMiB"] =
244             (*volatileRegionSizeLimitInKiB) >> 10;
245     }
246 
247     if (pmRegionSizeLimitInKiB != nullptr)
248     {
249         asyncResp->res.jsonValue[jsonPtr]["PersistentRegionSizeLimitMiB"] =
250             (*pmRegionSizeLimitInKiB) >> 10;
251     }
252 
253     if (volatileSizeInKiB != nullptr)
254     {
255         asyncResp->res.jsonValue[jsonPtr]["VolatileSizeMiB"] =
256             (*volatileSizeInKiB) >> 10;
257     }
258 
259     if (pmSizeInKiB != nullptr)
260     {
261         asyncResp->res.jsonValue[jsonPtr]["NonVolatileSizeMiB"] =
262             (*pmSizeInKiB) >> 10;
263     }
264 
265     if (cacheSizeInKB != nullptr)
266     {
267         asyncResp->res.jsonValue[jsonPtr]["CacheSizeMiB"] =
268             (*cacheSizeInKB >> 10);
269     }
270 
271     if (voltaileRegionMaxSizeInKib != nullptr)
272     {
273         asyncResp->res.jsonValue[jsonPtr]["VolatileRegionSizeMaxMiB"] =
274             (*voltaileRegionMaxSizeInKib) >> 10;
275     }
276 
277     if (pmRegionMaxSizeInKiB != nullptr)
278     {
279         asyncResp->res.jsonValue[jsonPtr]["PersistentRegionSizeMaxMiB"] =
280             (*pmRegionMaxSizeInKiB) >> 10;
281     }
282 
283     if (allocationIncrementInKiB != nullptr)
284     {
285         asyncResp->res.jsonValue[jsonPtr]["AllocationIncrementMiB"] =
286             (*allocationIncrementInKiB) >> 10;
287     }
288 
289     if (allocationAlignmentInKiB != nullptr)
290     {
291         asyncResp->res.jsonValue[jsonPtr]["AllocationAlignmentMiB"] =
292             (*allocationAlignmentInKiB) >> 10;
293     }
294 
295     if (volatileRegionNumberLimit != nullptr)
296     {
297         asyncResp->res.jsonValue[jsonPtr]["VolatileRegionNumberLimit"] =
298             *volatileRegionNumberLimit;
299     }
300 
301     if (pmRegionNumberLimit != nullptr)
302     {
303         asyncResp->res.jsonValue[jsonPtr]["PersistentRegionNumberLimit"] =
304             *pmRegionNumberLimit;
305     }
306 
307     if (spareDeviceCount != nullptr)
308     {
309         asyncResp->res.jsonValue[jsonPtr]["SpareDeviceCount"] =
310             *spareDeviceCount;
311     }
312 
313     if (isSpareDeviceInUse != nullptr)
314     {
315         asyncResp->res.jsonValue[jsonPtr]["IsSpareDeviceEnabled"] =
316             *isSpareDeviceInUse;
317     }
318 
319     if (isRankSpareEnabled != nullptr)
320     {
321         asyncResp->res.jsonValue[jsonPtr]["IsRankSpareEnabled"] =
322             *isRankSpareEnabled;
323     }
324 
325     if (maxAveragePowerLimitmW != nullptr)
326     {
327         asyncResp->res.jsonValue[jsonPtr]["MaxTDPMilliWatts"] =
328             *maxAveragePowerLimitmW;
329     }
330 
331     if (configurationLocked != nullptr)
332     {
333         asyncResp->res.jsonValue[jsonPtr]["ConfigurationLocked"] =
334             *configurationLocked;
335     }
336 
337     if (allowedMemoryModes != nullptr)
338     {
339         constexpr const std::array<const char*, 3> values{"Volatile", "PMEM",
340                                                           "Block"};
341 
342         for (const char* v : values)
343         {
344             if (allowedMemoryModes->ends_with(v))
345             {
346                 asyncResp->res.jsonValue[jsonPtr]["OperatingMemoryModes"]
347                     .push_back(v);
348                 break;
349             }
350         }
351     }
352 
353     if (memoryMedia != nullptr)
354     {
355         constexpr const std::array<const char*, 3> values{"DRAM", "NAND",
356                                                           "Intel3DXPoint"};
357 
358         for (const char* v : values)
359         {
360             if (memoryMedia->ends_with(v))
361             {
362                 asyncResp->res.jsonValue[jsonPtr]["MemoryMedia"].push_back(v);
363                 break;
364             }
365         }
366     }
367 
368     if (configurationLockCapable != nullptr)
369     {
370         asyncResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
371                                 ["ConfigurationLockCapable"] =
372             *configurationLockCapable;
373     }
374 
375     if (dataLockCapable != nullptr)
376     {
377         asyncResp->res
378             .jsonValue[jsonPtr]["SecurityCapabilities"]["DataLockCapable"] =
379             *dataLockCapable;
380     }
381 
382     if (passphraseCapable != nullptr)
383     {
384         asyncResp->res
385             .jsonValue[jsonPtr]["SecurityCapabilities"]["PassphraseCapable"] =
386             *passphraseCapable;
387     }
388 
389     if (maxPassphraseCount != nullptr)
390     {
391         asyncResp->res
392             .jsonValue[jsonPtr]["SecurityCapabilities"]["MaxPassphraseCount"] =
393             *maxPassphraseCount;
394     }
395 
396     if (passphraseLockLimit != nullptr)
397     {
398         asyncResp->res
399             .jsonValue[jsonPtr]["SecurityCapabilities"]["PassphraseLockLimit"] =
400             *passphraseLockLimit;
401     }
402 }
403 
assembleDimmProperties(std::string_view dimmId,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const dbus::utility::DBusPropertiesMap & properties,const nlohmann::json::json_pointer & jsonPtr)404 inline void assembleDimmProperties(
405     std::string_view dimmId,
406     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
407     const dbus::utility::DBusPropertiesMap& properties,
408     const nlohmann::json::json_pointer& jsonPtr)
409 {
410     asyncResp->res.jsonValue[jsonPtr]["Id"] = dimmId;
411     asyncResp->res.jsonValue[jsonPtr]["Name"] = "DIMM Slot";
412     asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] =
413         resource::State::Enabled;
414     asyncResp->res.jsonValue[jsonPtr]["Status"]["Health"] =
415         resource::Health::OK;
416 
417     const uint16_t* memoryDataWidth = nullptr;
418     const size_t* memorySizeInKB = nullptr;
419     const std::string* partNumber = nullptr;
420     const std::string* serialNumber = nullptr;
421     const std::string* manufacturer = nullptr;
422     const uint16_t* revisionCode = nullptr;
423     const bool* present = nullptr;
424     const uint16_t* memoryTotalWidth = nullptr;
425     const std::string* ecc = nullptr;
426     const std::string* formFactor = nullptr;
427     const std::vector<uint16_t>* allowedSpeedsMT = nullptr;
428     const size_t* memoryAttributes = nullptr;
429     const uint16_t* memoryConfiguredSpeedInMhz = nullptr;
430     const std::string* memoryType = nullptr;
431     const std::uint8_t* channel = nullptr;
432     const std::uint8_t* memoryController = nullptr;
433     const std::uint8_t* slot = nullptr;
434     const std::uint8_t* socket = nullptr;
435     const std::string* sparePartNumber = nullptr;
436     const std::string* model = nullptr;
437     const std::string* locationCode = nullptr;
438     const bool* functional = nullptr;
439 
440     const bool success = sdbusplus::unpackPropertiesNoThrow(
441         dbus_utils::UnpackErrorPrinter(), properties, "MemoryDataWidth",
442         memoryDataWidth, "MemorySizeInKB", memorySizeInKB, "PartNumber",
443         partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer,
444         "RevisionCode", revisionCode, "Present", present, "MemoryTotalWidth",
445         memoryTotalWidth, "ECC", ecc, "FormFactor", formFactor,
446         "AllowedSpeedsMT", allowedSpeedsMT, "MemoryAttributes",
447         memoryAttributes, "MemoryConfiguredSpeedInMhz",
448         memoryConfiguredSpeedInMhz, "MemoryType", memoryType, "Channel",
449         channel, "MemoryController", memoryController, "Slot", slot, "Socket",
450         socket, "SparePartNumber", sparePartNumber, "Model", model,
451         "LocationCode", locationCode, "Functional", functional);
452 
453     if (!success)
454     {
455         messages::internalError(asyncResp->res);
456         return;
457     }
458 
459     if (memoryDataWidth != nullptr)
460     {
461         asyncResp->res.jsonValue[jsonPtr]["DataWidthBits"] = *memoryDataWidth;
462     }
463 
464     if (memorySizeInKB != nullptr)
465     {
466         asyncResp->res.jsonValue[jsonPtr]["CapacityMiB"] =
467             (*memorySizeInKB >> 10);
468     }
469 
470     if (partNumber != nullptr)
471     {
472         asyncResp->res.jsonValue[jsonPtr]["PartNumber"] = *partNumber;
473     }
474 
475     if (serialNumber != nullptr)
476     {
477         asyncResp->res.jsonValue[jsonPtr]["SerialNumber"] = *serialNumber;
478     }
479 
480     if (manufacturer != nullptr)
481     {
482         asyncResp->res.jsonValue[jsonPtr]["Manufacturer"] = *manufacturer;
483     }
484 
485     if (revisionCode != nullptr)
486     {
487         asyncResp->res.jsonValue[jsonPtr]["FirmwareRevision"] =
488             std::to_string(*revisionCode);
489     }
490 
491     if (present != nullptr && !*present)
492     {
493         asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] =
494             resource::State::Absent;
495     }
496 
497     if (functional != nullptr)
498     {
499         if (!*functional)
500         {
501             asyncResp->res.jsonValue[jsonPtr]["Status"]["Health"] =
502                 resource::Health::Critical;
503         }
504     }
505 
506     if (memoryTotalWidth != nullptr)
507     {
508         asyncResp->res.jsonValue[jsonPtr]["BusWidthBits"] = *memoryTotalWidth;
509     }
510 
511     if (ecc != nullptr)
512     {
513         constexpr const std::array<const char*, 4> values{
514             "NoECC", "SingleBitECC", "MultiBitECC", "AddressParity"};
515 
516         for (const char* v : values)
517         {
518             if (ecc->ends_with(v))
519             {
520                 asyncResp->res.jsonValue[jsonPtr]["ErrorCorrection"] = v;
521                 break;
522             }
523         }
524     }
525 
526     if (formFactor != nullptr)
527     {
528         constexpr const std::array<const char*, 11> values{
529             "RDIMM",       "UDIMM",       "SO_DIMM",      "LRDIMM",
530             "Mini_RDIMM",  "Mini_UDIMM",  "SO_RDIMM_72b", "SO_UDIMM_72b",
531             "SO_DIMM_16b", "SO_DIMM_32b", "Die"};
532 
533         for (const char* v : values)
534         {
535             if (formFactor->ends_with(v))
536             {
537                 asyncResp->res.jsonValue[jsonPtr]["BaseModuleType"] = v;
538                 break;
539             }
540         }
541     }
542 
543     if (allowedSpeedsMT != nullptr)
544     {
545         nlohmann::json& jValue =
546             asyncResp->res.jsonValue[jsonPtr]["AllowedSpeedsMHz"];
547         jValue = nlohmann::json::array();
548         for (uint16_t subVal : *allowedSpeedsMT)
549         {
550             jValue.push_back(subVal);
551         }
552     }
553 
554     if (memoryAttributes != nullptr)
555     {
556         asyncResp->res.jsonValue[jsonPtr]["RankCount"] = *memoryAttributes;
557     }
558 
559     if (memoryConfiguredSpeedInMhz != nullptr)
560     {
561         asyncResp->res.jsonValue[jsonPtr]["OperatingSpeedMhz"] =
562             *memoryConfiguredSpeedInMhz;
563     }
564 
565     if (memoryType != nullptr)
566     {
567         std::string memoryDeviceType =
568             translateMemoryTypeToRedfish(*memoryType);
569         // Values like "Unknown" or "Other" will return empty
570         // so just leave off
571         if (!memoryDeviceType.empty())
572         {
573             asyncResp->res.jsonValue[jsonPtr]["MemoryDeviceType"] =
574                 memoryDeviceType;
575         }
576         if (memoryType->find("DDR") != std::string::npos)
577         {
578             asyncResp->res.jsonValue[jsonPtr]["MemoryType"] =
579                 memory::MemoryType::DRAM;
580         }
581         else if (memoryType->ends_with("Logical"))
582         {
583             asyncResp->res.jsonValue[jsonPtr]["MemoryType"] =
584                 memory::MemoryType::IntelOptane;
585         }
586     }
587 
588     if (channel != nullptr)
589     {
590         asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Channel"] =
591             *channel;
592     }
593 
594     if (memoryController != nullptr)
595     {
596         asyncResp->res
597             .jsonValue[jsonPtr]["MemoryLocation"]["MemoryController"] =
598             *memoryController;
599     }
600 
601     if (slot != nullptr)
602     {
603         asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Slot"] = *slot;
604     }
605 
606     if (socket != nullptr)
607     {
608         asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Socket"] = *socket;
609     }
610 
611     if (sparePartNumber != nullptr)
612     {
613         asyncResp->res.jsonValue[jsonPtr]["SparePartNumber"] = *sparePartNumber;
614     }
615 
616     if (model != nullptr)
617     {
618         asyncResp->res.jsonValue[jsonPtr]["Model"] = *model;
619     }
620 
621     if (locationCode != nullptr)
622     {
623         asyncResp->res
624             .jsonValue[jsonPtr]["Location"]["PartLocation"]["ServiceLabel"] =
625             *locationCode;
626     }
627 
628     getPersistentMemoryProperties(asyncResp, properties, jsonPtr);
629 }
630 
getDimmDataByService(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & dimmId,const std::string & service,const std::string & objPath)631 inline void getDimmDataByService(
632     std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& dimmId,
633     const std::string& service, const std::string& objPath)
634 {
635     BMCWEB_LOG_DEBUG("Get available system components.");
636     dbus::utility::getAllProperties(
637         service, objPath, "",
638         [dimmId, asyncResp{std::move(asyncResp)}](
639             const boost::system::error_code& ec,
640             const dbus::utility::DBusPropertiesMap& properties) {
641             if (ec)
642             {
643                 BMCWEB_LOG_DEBUG("DBUS response error");
644                 messages::internalError(asyncResp->res);
645                 return;
646             }
647             assembleDimmProperties(dimmId, asyncResp, properties,
648                                    ""_json_pointer);
649         });
650 }
651 
assembleDimmPartitionData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const dbus::utility::DBusPropertiesMap & properties,const nlohmann::json::json_pointer & regionPtr)652 inline void assembleDimmPartitionData(
653     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
654     const dbus::utility::DBusPropertiesMap& properties,
655     const nlohmann::json::json_pointer& regionPtr)
656 {
657     const std::string* memoryClassification = nullptr;
658     const uint64_t* offsetInKiB = nullptr;
659     const std::string* partitionId = nullptr;
660     const bool* passphraseState = nullptr;
661     const uint64_t* sizeInKiB = nullptr;
662 
663     const bool success = sdbusplus::unpackPropertiesNoThrow(
664         dbus_utils::UnpackErrorPrinter(), properties, "MemoryClassification",
665         memoryClassification, "OffsetInKiB", offsetInKiB, "PartitionId",
666         partitionId, "PassphraseState", passphraseState, "SizeInKiB",
667         sizeInKiB);
668 
669     if (!success)
670     {
671         messages::internalError(asyncResp->res);
672         return;
673     }
674 
675     nlohmann::json::object_t partition;
676 
677     if (memoryClassification != nullptr)
678     {
679         partition["MemoryClassification"] = *memoryClassification;
680     }
681 
682     if (offsetInKiB != nullptr)
683     {
684         partition["OffsetMiB"] = (*offsetInKiB >> 10);
685     }
686 
687     if (partitionId != nullptr)
688     {
689         partition["RegionId"] = *partitionId;
690     }
691 
692     if (passphraseState != nullptr)
693     {
694         partition["PassphraseEnabled"] = *passphraseState;
695     }
696 
697     if (sizeInKiB != nullptr)
698     {
699         partition["SizeMiB"] = (*sizeInKiB >> 10);
700     }
701 
702     asyncResp->res.jsonValue[regionPtr].emplace_back(std::move(partition));
703 }
704 
getDimmPartitionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & service,const std::string & path)705 inline void getDimmPartitionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
706                                  const std::string& service,
707                                  const std::string& path)
708 {
709     dbus::utility::getAllProperties(
710         service, path,
711         "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition",
712         [asyncResp{std::move(asyncResp)}](
713             const boost::system::error_code& ec,
714             const dbus::utility::DBusPropertiesMap& properties) {
715             if (ec)
716             {
717                 BMCWEB_LOG_DEBUG("DBUS response error");
718                 messages::internalError(asyncResp->res);
719 
720                 return;
721             }
722             nlohmann::json::json_pointer regionPtr = "/Regions"_json_pointer;
723             assembleDimmPartitionData(asyncResp, properties, regionPtr);
724         }
725 
726     );
727 }
728 
afterGetDimmData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dimmId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)729 inline void afterGetDimmData(
730     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
731     const std::string& dimmId, const boost::system::error_code& ec,
732     const dbus::utility::MapperGetSubTreeResponse& subtree)
733 {
734     if (ec)
735     {
736         if (ec.value() != EBADR)
737         {
738             BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value());
739             messages::internalError(asyncResp->res);
740         }
741         return;
742     }
743 
744     bool found = false;
745     for (const auto& [objectPath, serviceMap] : subtree)
746     {
747         sdbusplus::message::object_path path(objectPath);
748 
749         bool dimmInterface = false;
750         bool associationInterface = false;
751         /* Note: Multiple D-Bus objects can provide details for the Memory
752          * object: 1) Dimm is the primary object 2) Additional partitions could
753          * exist per Dimm. Only consider the object found if the Dimm is found.
754          */
755         for (const auto& [serviceName, interfaceList] : serviceMap)
756         {
757             for (const auto& interface : interfaceList)
758             {
759                 if (interface == "xyz.openbmc_project.Inventory.Item.Dimm" &&
760                     path.filename() == dimmId)
761                 {
762                     // Found the single Dimm
763                     getDimmDataByService(asyncResp, dimmId, serviceName,
764                                          objectPath);
765                     dimmInterface = true;
766                     found = true;
767                 }
768                 else if (interface ==
769                          "xyz.openbmc_project.Association.Definitions")
770                 {
771                     /* Object has associations. If this object is also a Dimm
772                      * then the association might provide the LED state
773                      * information. After all interfaces for this object have
774                      * been checked the LED information will be gathered if the
775                      * object was a Dimm
776                      */
777                     associationInterface = true;
778                 }
779                 else if (
780                     interface ==
781                         "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition" &&
782                     path.parent_path().filename() == dimmId)
783                 {
784                     // partitions are separate as there can be multiple per
785                     // device, i.e.
786                     // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1
787                     // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2
788                     getDimmPartitionData(asyncResp, serviceName, objectPath);
789                 }
790             }
791         }
792 
793         /* If a Dimm has an Association check if it has a LED */
794         if (associationInterface && dimmInterface)
795         {
796             getLocationIndicatorActive(asyncResp, objectPath);
797         }
798     }
799 
800     if (!found)
801     {
802         // Dimm object not found
803         messages::resourceNotFound(asyncResp->res, "Memory", dimmId);
804         return;
805     }
806     // Set @odata only if object is found
807     asyncResp->res.jsonValue["@odata.type"] = "#Memory.v1_11_0.Memory";
808     asyncResp->res.jsonValue["@odata.id"] =
809         boost::urls::format("/redfish/v1/Systems/{}/Memory/{}",
810                             BMCWEB_REDFISH_SYSTEM_URI_NAME, dimmId);
811 }
812 
getDimmData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dimmId)813 inline void getDimmData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
814                         const std::string& dimmId)
815 {
816     BMCWEB_LOG_DEBUG("Get dimm path for {}", dimmId);
817     constexpr std::array<std::string_view, 2> interfaces = {
818         "xyz.openbmc_project.Inventory.Item.Dimm",
819         "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition"};
820 
821     dbus::utility::getSubTree(
822         "/xyz/openbmc_project/inventory", 0, interfaces,
823         [asyncResp,
824          dimmId](const boost::system::error_code& ec,
825                  const dbus::utility::MapperGetSubTreeResponse& subtree) {
826             afterGetDimmData(asyncResp, dimmId, ec, subtree);
827         });
828 }
829 
handleSetDimmData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,bool locationIndicatorActive,const std::string & dimmPath)830 inline void handleSetDimmData(
831     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
832     bool locationIndicatorActive, const std::string& dimmPath)
833 {
834     setLocationIndicatorActive(asyncResp, dimmPath, locationIndicatorActive);
835 }
836 
afterGetValidDimmPath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dimmId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & subtree,const std::function<void (const std::string & dimmPath)> & callback)837 inline void afterGetValidDimmPath(
838     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
839     const std::string& dimmId, const boost::system::error_code& ec,
840     const dbus::utility::MapperGetSubTreePathsResponse& subtree,
841     const std::function<void(const std::string& dimmPath)>& callback)
842 {
843     if (ec)
844     {
845         if (ec.value() == EBADR)
846         {
847             /* Need to report error for PATCH */
848             BMCWEB_LOG_WARNING("Dimm not found in inventory");
849             messages::resourceNotFound(asyncResp->res, "Memory", dimmId);
850         }
851         else
852         {
853             BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value());
854             messages::internalError(asyncResp->res);
855         }
856         return;
857     }
858 
859     for (const auto& objectPath : subtree)
860     {
861         // Ignore any objects which don't end with our desired dimm name
862         sdbusplus::message::object_path path(objectPath);
863         if (path.filename() == dimmId)
864         {
865             callback(path);
866             return;
867         }
868     }
869 
870     // Object not found
871     messages::resourceNotFound(asyncResp->res, "Memory", dimmId);
872 }
873 
getValidDimmPath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dimmId,std::function<void (const std::string & dimmPath)> && callback)874 inline void getValidDimmPath(
875     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
876     const std::string& dimmId,
877     std::function<void(const std::string& dimmPath)>&& callback)
878 {
879     BMCWEB_LOG_DEBUG("Get dimm path for {}", dimmId);
880     constexpr std::array<std::string_view, 1> interfaces = {
881         "xyz.openbmc_project.Inventory.Item.Dimm"};
882 
883     dbus::utility::getSubTreePaths(
884         "/xyz/openbmc_project/inventory", 0, interfaces,
885         [asyncResp, dimmId, callback{std::move(callback)}](
886             const boost::system::error_code& ec,
887             const dbus::utility::MapperGetSubTreePathsResponse& subtree) {
888             afterGetValidDimmPath(asyncResp, dimmId, ec, subtree, callback);
889         });
890 }
891 
handleMemoryPatch(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & dimmId)892 inline void handleMemoryPatch(
893     App& app, const crow::Request& req,
894     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
895     const std::string& systemName, const std::string& dimmId)
896 {
897     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
898     {
899         return;
900     }
901 
902     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
903     {
904         // Option currently returns no systems.  TBD
905         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
906                                    systemName);
907         return;
908     }
909 
910     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
911     {
912         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
913                                    systemName);
914         return;
915     }
916 
917     std::optional<bool> locationIndicatorActive;
918     if (!json_util::readJsonPatch(                             //
919             req, asyncResp->res,                               //
920             "LocationIndicatorActive", locationIndicatorActive //
921             ))
922     {
923         return;
924     }
925 
926     if (locationIndicatorActive)
927     {
928         getValidDimmPath(asyncResp, dimmId,
929                          std::bind_front(handleSetDimmData, asyncResp,
930                                          *locationIndicatorActive));
931     }
932 }
933 
handleMemoryGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & dimmId)934 inline void handleMemoryGet(App& app, const crow::Request& req,
935                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
936                             const std::string& systemName,
937                             const std::string& dimmId)
938 {
939     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
940     {
941         return;
942     }
943 
944     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
945     {
946         // Option currently returns no systems.  TBD
947         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
948                                    systemName);
949         return;
950     }
951 
952     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
953     {
954         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
955                                    systemName);
956         return;
957     }
958 
959     getDimmData(asyncResp, dimmId);
960 }
961 
requestRoutesMemoryCollection(App & app)962 inline void requestRoutesMemoryCollection(App& app)
963 {
964     /**
965      * Functions triggers appropriate requests on DBus
966      */
967     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/")
968         .privileges(redfish::privileges::getMemoryCollection)
969         .methods(boost::beast::http::verb::get)(
970             [&app](const crow::Request& req,
971                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
972                    const std::string& systemName) {
973                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
974                 {
975                     return;
976                 }
977                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
978                 {
979                     // Option currently returns no systems.  TBD
980                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
981                                                systemName);
982                     return;
983                 }
984                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
985                 {
986                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
987                                                systemName);
988                     return;
989                 }
990 
991                 asyncResp->res.jsonValue["@odata.type"] =
992                     "#MemoryCollection.MemoryCollection";
993                 asyncResp->res.jsonValue["Name"] = "Memory Module Collection";
994                 asyncResp->res.jsonValue["@odata.id"] =
995                     boost::urls::format("/redfish/v1/Systems/{}/Memory",
996                                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
997 
998                 constexpr std::array<std::string_view, 1> interfaces{
999                     "xyz.openbmc_project.Inventory.Item.Dimm"};
1000                 collection_util::getCollectionMembers(
1001                     asyncResp,
1002                     boost::urls::format("/redfish/v1/Systems/{}/Memory",
1003                                         BMCWEB_REDFISH_SYSTEM_URI_NAME),
1004                     interfaces, "/xyz/openbmc_project/inventory");
1005             });
1006 }
1007 
requestRoutesMemory(App & app)1008 inline void requestRoutesMemory(App& app)
1009 {
1010     /**
1011      * Functions triggers appropriate requests on DBus
1012      */
1013     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/")
1014         .privileges(redfish::privileges::getMemory)
1015         .methods(boost::beast::http::verb::get)(
1016             std::bind_front(handleMemoryGet, std::ref(app)));
1017 
1018     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/")
1019         .privileges(redfish::privileges::patchMemory)
1020         .methods(boost::beast::http::verb::patch)(
1021             std::bind_front(handleMemoryPatch, std::ref(app)));
1022 }
1023 
1024 } // namespace redfish
1025