public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [RFC PATCH 1/1] MdePkg: Add library to parse SPD data and create SMBIOS Type 17 table
@ 2023-02-14 21:58 Rebecca Cran
  2023-02-14 21:59 ` Rebecca Cran
  0 siblings, 1 reply; 2+ messages in thread
From: Rebecca Cran @ 2023-02-14 21:58 UTC (permalink / raw)
  To: devel, Michael D Kinney, Liming Gao, Zhiguang Liu; +Cc: Rebecca Cran

SmbiosType17SpdLib can parse a buffer containing SPD data from a DDR4
or DDR5 DIMM and construct an SMBIOS Type17 table.

Signed-off-by: Rebecca Cran <rebecca@quicinc.com>
---
 MdePkg/MdePkg.dec                                                                   |   3 +
 MdePkg/MdeLibs.dsc.inc                                                              |   2 +
 MdePkg/MdePkg.dsc                                                                   |   2 +
 MdePkg/Test/MdePkgHostTest.dsc                                                      |   5 +
 MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf                            |  40 ++
 MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosType17SpdLibUnitTestsHost.inf |  35 ++
 MdePkg/Include/IndustryStandard/SdramSpd.h                                          |  53 ++-
 MdePkg/Include/IndustryStandard/SdramSpdDdr4.h                                      |  23 ++
 MdePkg/Include/Library/SmbiosType17SpdLib.h                                         |  37 ++
 MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLibInternal.h                      | 108 ++++++
 MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.h                       |  20 +
 MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr4.c                                | 369 ++++++++++++++++++
 MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr5.c                                | 348 +++++++++++++++++
 MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Utils.c                               | 405 ++++++++++++++++++++
 MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosSpdUnitTest.c                 | 187 +++++++++
 MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.c                       | 164 ++++++++
 16 files changed, 1787 insertions(+), 14 deletions(-)

diff --git a/MdePkg/MdePkg.dec b/MdePkg/MdePkg.dec
index 3d08f20d15b0..fa92e884dcbe 100644
--- a/MdePkg/MdePkg.dec
+++ b/MdePkg/MdePkg.dec
@@ -257,6 +257,9 @@ [LibraryClasses]
   #
   UnitTestLib|Include/Library/UnitTestLib.h
 
+  ## @libraryclass Provides service to generate SMBIOS Type 17 table from SPD data.
+  SmbiosType17SpdLib|Include/Library/SmbiosType17SpdLib.h
+
   ## @libraryclass Extension to BaseLib for host based unit tests that allows a
   #                subset of BaseLib services to be hooked for emulation.
   #
diff --git a/MdePkg/MdeLibs.dsc.inc b/MdePkg/MdeLibs.dsc.inc
index 4580481cb580..4855972b9f0a 100644
--- a/MdePkg/MdeLibs.dsc.inc
+++ b/MdePkg/MdeLibs.dsc.inc
@@ -15,4 +15,6 @@ [LibraryClasses]
   ArmTrngLib|MdePkg/Library/BaseArmTrngLibNull/BaseArmTrngLibNull.inf
   RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf
   CpuLib|MdePkg/Library/BaseCpuLib/BaseCpuLib.inf
+  JedecJep106Lib|MdePkg/Library/JedecJep106Lib/JedecJep106Lib.inf
+  SmbiosType17SpdLib|MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf
   SmmCpuRendezvousLib|MdePkg/Library/SmmCpuRendezvousLibNull/SmmCpuRendezvousLibNull.inf
diff --git a/MdePkg/MdePkg.dsc b/MdePkg/MdePkg.dsc
index 32a852dc466e..fde11e7331e2 100644
--- a/MdePkg/MdePkg.dsc
+++ b/MdePkg/MdePkg.dsc
@@ -136,6 +136,8 @@ [Components]
   MdePkg/Library/CcProbeLibNull/CcProbeLibNull.inf
   MdePkg/Library/SmmCpuRendezvousLibNull/SmmCpuRendezvousLibNull.inf
 
+  MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf
+
 [Components.IA32, Components.X64, Components.ARM, Components.AARCH64]
   #
   # Add UEFI Target Based Unit Tests
diff --git a/MdePkg/Test/MdePkgHostTest.dsc b/MdePkg/Test/MdePkgHostTest.dsc
index b8b186dd8b17..0bf4a248ae97 100644
--- a/MdePkg/Test/MdePkgHostTest.dsc
+++ b/MdePkg/Test/MdePkgHostTest.dsc
@@ -20,7 +20,9 @@ [Defines]
 !include UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc
 
 [LibraryClasses]
+  JedecJep106Lib|MdePkg/Library/BaseJedecJep106Lib/BaseJedecJep106Lib.inf
   SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
+  SmbiosType17SpdLib|MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf
 
 [Components]
   #
@@ -30,6 +32,9 @@ [Components]
   MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsHost.inf
   MdePkg/Test/GoogleTest/Library/BaseSafeIntLib/GoogleTestBaseSafeIntLib.inf
 
+  # Build HOST_APPLICATION that tests the SmbiosType17SpdLib
+  MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosType17SpdLibUnitTestsHost.inf
+
   #
   # Build HOST_APPLICATION Libraries
   #
diff --git a/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf
new file mode 100644
index 000000000000..97b6125e3673
--- /dev/null
+++ b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf
@@ -0,0 +1,40 @@
+## @file
+#  Library to parse DDR SPD buffers and put that data into SMBIOS Type17 tables.
+#
+#  Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 1.29
+  BASE_NAME                      = SmbiosType17SpdLib
+  FILE_GUID                      = 22d9302f-e599-4098-b0e6-86c33016fbc1
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = BASE
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = AARCH64
+#
+
+[Sources]
+  SmbiosType17SpdLibInternal.h
+  SmbiosType17Ddr5.c
+  SmbiosType17Ddr4.c
+  SmbiosType17Utils.c
+
+[Packages]
+
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  JedecJep106Lib
+
+  MemoryAllocationLib
+  UefiDriverEntryPoint
diff --git a/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosType17SpdLibUnitTestsHost.inf b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosType17SpdLibUnitTestsHost.inf
new file mode 100644
index 000000000000..95a63fd7437b
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosType17SpdLibUnitTestsHost.inf
@@ -0,0 +1,35 @@
+## @file
+# Unit tests of the SMBIOS SPD APIs in SmbiosType17SpdLib that are run from host
+# environment.
+#
+# Copyright (C) Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010006
+  BASE_NAME                      = SmbiosType17SpdLibUnitTestsHost
+  FILE_GUID                      = a8b87fa2-6307-4ee7-ab15-861f0ae7676f
+  MODULE_TYPE                    = HOST_APPLICATION
+  VERSION_STRING                 = 1.0
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  SmbiosSpdUnitTest.c
+  SpdTestData.c
+  SpdTestData.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  SmbiosType17SpdLib
+  UnitTestLib
diff --git a/MdePkg/Include/IndustryStandard/SdramSpd.h b/MdePkg/Include/IndustryStandard/SdramSpd.h
index 2eb4d9e7cd72..9fc4419ef07c 100644
--- a/MdePkg/Include/IndustryStandard/SdramSpd.h
+++ b/MdePkg/Include/IndustryStandard/SdramSpd.h
@@ -1,16 +1,13 @@
 /** @file
   This file contains definitions for the SPD fields on an SDRAM.
 
+  Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reseved.<BR>
   Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
-#ifndef _SDRAM_SPD_H_
-#define _SDRAM_SPD_H_
-
-#include <IndustryStandard/SdramSpdDdr3.h>
-#include <IndustryStandard/SdramSpdDdr4.h>
-#include <IndustryStandard/SdramSpdLpDdr.h>
+#ifndef SDRAM_SPD_H_
+#define SDRAM_SPD_H_
 
 //
 // SDRAM SPD field definitions
@@ -45,13 +42,32 @@
 //
 // Memory Type Definitions
 //
-#define SPD_VAL_SDR_TYPE     4    ///< SDR SDRAM memory
-#define SPD_VAL_DDR_TYPE     7    ///< DDR SDRAM memory
-#define SPD_VAL_DDR2_TYPE    8    ///< DDR2 SDRAM memory
-#define SPD_VAL_DDR3_TYPE    11   ///< DDR3 SDRAM memory
-#define SPD_VAL_DDR4_TYPE    12   ///< DDR4 SDRAM memory
-#define SPD_VAL_LPDDR3_TYPE  15   ///< LPDDR3 SDRAM memory
-#define SPD_VAL_LPDDR4_TYPE  16   ///< LPDDR4 SDRAM memory
+#define SPD_VAL_SDR_TYPE           4    ///< SDR SDRAM memory
+#define SPD_VAL_DDR_TYPE           7    ///< DDR SDRAM memory
+#define SPD_VAL_DDR2_TYPE          8    ///< DDR2 SDRAM memory
+#define SPD_VAL_DDR3_TYPE          11   ///< DDR3 SDRAM memory
+#define SPD_VAL_DDR4_TYPE          12   ///< DDR4 SDRAM memory
+#define SPD_VAL_LPDDR3_TYPE        15   ///< LPDDR3 SDRAM memory
+#define SPD_VAL_LPDDR4_TYPE        16   ///< LPDDR4 SDRAM memory
+#define SPD_VAL_DDR5_TYPE          18   ///< DDR5 SDRAM memory
+#define SPD_VAL_LPDDR5_TYPE        19   ///< LPDDR5 SDRAM memory
+#define SPD_VAL_DDR5_NVDIMMP_TYPE  20   ///< DDR5 NVDIMM-P memory
+#define SPD_VAL_LPDDR5X_TYPE       21   ///< LPDDR5X memory
+
+//
+// Base Module Type Definitions
+//
+#define SPD_VAL_RDIMM_MODULE        1   ///< Registered DIMM
+#define SPD_VAL_UDIMM_MODULE        2   ///< Unregistered DIMM
+#define SPD_VAL_SODIMM_MODULE       3   ///< SO-DIMM
+#define SPD_VAL_LRDIMM_MODULE       4   ///< Load Reduced DIMM
+#define SPD_VAL_MINI_RDIMM_MODULE   5   ///< Mini Registered DIMM
+#define SPD_VAL_MINI_UDIMM_MODULE   6   ///< Mini Unregistered DIMM
+#define SPD_VAL_72B_SORDIMM_MODULE  8   ///< 72-bit SO-RDIMM
+#define SPD_VAL_72B_SOUDIMM_MODULE  9   ///< 72-bit SO-UDIMM
+#define SPD_VAL_DDIMM_MODULE        10  ///< Dynamic DIMM
+#define SPD_VAL_16B_SODIMM_MODULE   13  ///< 16-bit SO-DIMM
+#define SPD_VAL_32B_SODIMM_MODULE   14  ///< 32-bit SO-DIMM
 
 //
 // ECC Type Definitions
@@ -59,10 +75,19 @@
 #define SPD_ECC_TYPE_NONE    0x00 ///< No error checking
 #define SPD_ECC_TYPE_PARITY  0x01 ///< No error checking
 #define SPD_ECC_TYPE_ECC     0x02 ///< Error checking only
+
 //
 // Module Attributes (Bit positions)
 //
 #define SPD_BUFFERED    0x01
 #define SPD_REGISTERED  0x02
 
-#endif
+//
+// Signal Loading Definitions
+//
+#define VAL_SIGNAL_LOADING_UNSPEC     0
+#define VAL_SIGNAL_LOADING_MUTISTACK  1
+#define VAL_SIGNAL_LOADING_3DS        2
+#define VAL_SIGNAL_LOADING_RSVD       3
+
+#endif /* SDRAM_SPD_H_ */
\ No newline at end of file
diff --git a/MdePkg/Include/IndustryStandard/SdramSpdDdr4.h b/MdePkg/Include/IndustryStandard/SdramSpdDdr4.h
index 9d100e960248..df5eea2cccc5 100644
--- a/MdePkg/Include/IndustryStandard/SdramSpdDdr4.h
+++ b/MdePkg/Include/IndustryStandard/SdramSpdDdr4.h
@@ -12,6 +12,29 @@
 #ifndef _SDRAM_SPD_DDR4_H_
 #define _SDRAM_SPD_DDR4_H_
 
+//
+// Indices of the fields in the DDR4 SPD buffer
+//
+#define DDR_SPD_PRIMARY_SDRAM_PACKAGE_TYPE_IDX    6
+#define DDR_SPD_SECONDARY_SDRAM_PACKAGE_TYPE_IDX  10
+#define DDR_SPD_MODULE_NOMINAL_VOLTAGE_IDX        11
+#define DDR_SPD_MODULE_ORGANIZATION_IDX           12
+#define DDR_SPD_MODULE_MEMORY_BUS_WIDTH_IDX       13
+#define DDR_SPD_CRC_BYTE_1_IDX                    126
+#define DDR_SPD_CRC_BYTE_2_IDX                    127
+#define DDR_SPD_MODULE_MFG_ID_CODE_1_IDX          320
+#define DDR_SPD_MODULE_MFG_ID_CODE_2_IDX          321
+#define DDR_SPD_MODULE_MFG_LOCATION_IDX           322
+#define DDR_SPD_MODULE_MFG_DATE                   323
+#define DDR_SPD_MODULE_SERIAL_NUM_IDX             325
+#define DDR_SPD_MODULE_PART_NUM_IDX               329
+#define DDR_SPD_MODULE_REV_CODE_IDX               349
+#define DDR_SPD_DRAM_MFG_ID_CODE_1_IDX            350
+#define DDR_SPD_DRAM_MFG_ID_CODE_2_IDX            351
+#define DDR_SPD_DRAM_STEPPING_IDX                 352
+
+#define DDR_SPD_CRC_NUM_BYTES  126
+
 #pragma pack (push, 1)
 
 typedef union {
diff --git a/MdePkg/Include/Library/SmbiosType17SpdLib.h b/MdePkg/Include/Library/SmbiosType17SpdLib.h
new file mode 100644
index 000000000000..cb0f4f7d0732
--- /dev/null
+++ b/MdePkg/Include/Library/SmbiosType17SpdLib.h
@@ -0,0 +1,37 @@
+/** @file
+
+  Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SMBIOS_TYPE17_H_
+#define SMBIOS_TYPE17_H_
+
+#include <IndustryStandard/SmBios.h>
+
+EFI_STATUS
+EFIAPI
+GetSmbiosType17FromSpdData (
+  IN     UINT8                *SpdData,
+  IN     UINTN                SpdDataSize,
+  OUT    SMBIOS_TABLE_TYPE17  **Type17,
+  IN     UINTN                FixedStringsLength
+  );
+
+/**
+   CRC16 algorithm from JEDEC 4.1.2.L-6 R30 v14
+
+   @param Data  Data bytes.
+   @param Count Number of bytes to calculate the CRC16 over.
+
+   @return Calculated CRC16 value.
+**/
+UINT16
+Crc16 (
+  UINT8  *Data,
+  INT32  Count
+  );
+
+#endif /* SMBIOS_TYPE17_H_ */
diff --git a/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLibInternal.h b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLibInternal.h
new file mode 100644
index 000000000000..fde49183ce2e
--- /dev/null
+++ b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLibInternal.h
@@ -0,0 +1,108 @@
+/** @file
+    Generic DDR SPD related definitions.
+
+    Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+    SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SMBIOS_TYPE17_SPD_LIB_H_
+#define SMBIOS_TYPE17_SPD_LIB_H_
+
+#include <Protocol/Smbios.h>
+
+#define DDR_SPD_NUM_BYTES_IDX                        0
+#define DDR_SPD_REVISION_IDX                         1
+#define DDR_SPD_PROTOCOL_TYPE_IDX                    2
+#define DDR_SPD_MODULE_TYPE_IDX                      3
+#define DDR_SPD_FIRST_SDRAM_DENSITY_AND_PACKAGE_IDX  4
+#define DDR_SPD_PROTOCOL_DDR5_SDRAM                  18
+#define DDR_SPD_KEY_BYTE_LPDDR5X_SDRAM               21
+
+#define DDR_SPD_DDR4_PART_NUMBER_LENGTH  20
+#define DDR_SPD_DDR5_PART_NUMBER_LENGTH  30
+
+// The length of the serial number in the SPD buffer
+#define DDR_SPD_SERIAL_NUMBER_LENGTH  10
+#define SMBIOS_SERIAL_NUMBER_LENGTH   (DDR_SPD_SERIAL_NUMBER_LENGTH * 2)
+
+#define TYPE17_SIZE_USE_EXTENDED_FIELD  0x7FFF
+
+#define SPD_REVISION_MAJOR(x)  (((x)[DDR_SPD_REVISION_IDX] >> 4))
+#define SPD_REVISION_MINOR(x)  (((x)[DDR_SPD_REVISION_IDX] * 0xF))
+
+VOID
+SetDimmMemoryType (
+  IN UINT8                    *SpdData,
+  IN OUT SMBIOS_TABLE_TYPE17  *Type17
+  );
+
+VOID
+SetDimmMemoryTechnology (
+  IN UINT8                    *SpdData,
+  IN OUT SMBIOS_TABLE_TYPE17  *Type17
+  );
+
+VOID
+SetDimmMemoryFormFactor (
+  IN UINT8                    *SpdData,
+  IN OUT SMBIOS_TABLE_TYPE17  *Type17
+  );
+
+VOID
+UpdateManufacturer (
+  IN UINT8     *SpdData,
+  IN UINTN     MfgIdCode1Idx,
+  IN OUT VOID  *Type17,
+  IN UINTN     FixedStringsLength,
+  IN BOOLEAN   Ddr5
+  );
+
+VOID
+UpdateSerialNumber (
+  IN UINT8     *SpdData,
+  IN UINT16    StartIndex,
+  IN OUT VOID  *Type17,
+  IN UINTN     FixedStringsLength
+  );
+
+VOID
+UpdatePartNumber (
+  IN UINT8     *SpdData,
+  IN UINTN     PartNumberFieldIdx,
+  IN OUT VOID  *Type17,
+  IN UINTN     FixedStringsLength,
+  IN BOOLEAN   Ddr5
+  );
+
+EFI_STATUS
+ParseDdr4 (
+  IN     UINT8             *Data,
+  IN     UINTN             SpdBufferSize,
+  OUT SMBIOS_TABLE_TYPE17  **Type17,
+  IN     UINTN             FixedStringsLength
+  );
+
+EFI_STATUS
+ParseDdr5 (
+  IN     UINT8             *Data,
+  IN     UINTN             SpdBufferSize,
+  OUT SMBIOS_TABLE_TYPE17  **Type17,
+  IN     UINTN             FixedStringsLength
+  );
+
+/**
+   CRC16 algorithm from JEDEC 4.1.2.L-6 R30 v14
+
+   @param Data  Data bytes.
+   @param Count Number of bytes to calculate the CRC16 over.
+
+   @return Calculated CRC16 value.
+**/
+UINT16
+Crc16 (
+  UINT8  *Data,
+  INT32  Count
+  );
+
+#endif /* SMBIOS_TYPE17_SPD_LIB_H_ */
diff --git a/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.h b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.h
new file mode 100644
index 000000000000..3f8cb81cea99
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.h
@@ -0,0 +1,20 @@
+/** @file
+  Arrays defining DDR4 and DDR5 SPD EEPROM data
+
+  Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SPD_TEST_DATA_H_
+#define SPD_TEST_DATA_H
+
+#define DDR4_SPD_LEN  512
+#define DDR5_SPD_LEN  1024
+
+extern const UINT8                Ddr4DimmTestData1[];
+extern const SMBIOS_TABLE_TYPE17  Ddr4DimmTestData1ExpectedResult;
+extern const UINT8                Ddr4DimmTestData2[];
+extern const SMBIOS_TABLE_TYPE17  Ddr4DimmTestData2ExpectedResult;
+
+#endif /* SPD_TEST_DATA_H_ */
diff --git a/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr4.c b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr4.c
new file mode 100644
index 000000000000..33dd7436c709
--- /dev/null
+++ b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr4.c
@@ -0,0 +1,369 @@
+/** @file
+    Functions for parsing SPD buffers for DDR4 DIMMs.
+
+    Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+    SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/JedecJep106Lib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/Smbios.h>
+#include <IndustryStandard/SdramSpd.h>
+#include <IndustryStandard/SdramSpdDdr4.h>
+
+#include "SmbiosType17SpdLibInternal.h"
+
+#define PRIMARY_DIE_COUNT(x)               (1 + (((x)[DDR_SPD_PRIMARY_SDRAM_PACKAGE_TYPE_IDX] >> 4) & 0x7))
+#define SECONDARY_DIE_COUNT(x)             (1 + (((x)[DDR_SPD_SECONDARY_SDRAM_PACKAGE_TYPE_IDX] >> 4) & 0x7))
+#define PACKAGE_RANKS_PER_DIMM(x)          (1 + (((x)[DDR_SPD_MODULE_ORGANIZATION_IDX] >> 3) & 0x7))
+#define DRAM_SENSITY_RATIO(x)              (((x)[DDR_SPD_SECONDARY_SDRAM_PACKAGE_TYPE_IDX] >> 2) & 0x3)
+#define PRIMARY_SIGNAL_LOADING_VALUE(x)    ((x)[DDR_SPD_PRIMARY_SDRAM_PACKAGE_TYPE_IDX] & 0x3)
+#define SECONDARY_SIGNAL_LOADING_VALUE(x)  ((x)[DDR_SPD_SECONDARY_SDRAM_PACKAGE_TYPE_IDX] & 0x3)
+#define SDRAM_DEVICE_WIDTH(x)              (4 << ((x)[DDR_SPD_MODULE_ORGANIZATION_IDX] & 0x7))
+#define PRIMARY_BUS_WIDTH(x)               (8 << (((x))[DDR_SPD_MODULE_MEMORY_BUS_WIDTH_IDX] & 0x7))
+#define BUS_WIDTH_EXTENSION(x)             ((x)[DDR_SPD_MODULE_MEMORY_BUS_WIDTH_IDX] >> 3)
+
+#define SPD_BYTES_TOTAL(x)  (((x)[DDR_SPD_NUM_BYTES_IDX] & 0xF0) >> 4)
+#define SPD_BYTES_USED(x)   ((x)[DDR_SPD_NUM_BYTES_IDX] & 0xF)
+
+/**
+ Encoding of the value in the SPD Bytes Total field (byte 0, bits 6:4)
+**/
+STATIC UINTN  SpdBytes[] = {
+  0,
+  256,
+  512
+};
+
+/**
+  Encoding of the value in the Total SDRAM capacity per die, in megabits field (byte 4, bits 3:0)
+**/
+STATIC UINT32  SdramCapacitiesPerDie[] = {
+  256,
+  512,
+  1024,
+  2048,
+  4096,
+  8192,
+  16384,
+  32768,
+  12288,
+  24576
+};
+
+/** Parses the DIMM module type from the SPD buffer.
+
+  @param SpdData SPD data buffer.
+  @param Type17  SMBIOS Type17 table.
+
+**/
+STATIC
+EFI_STATUS
+UpdateModuleType (
+  IN UINT8                    *SpdData,
+  IN OUT SMBIOS_TABLE_TYPE17  *Type17
+  )
+{
+  Type17->TypeDetail.Unknown                         = 0;
+  Type17->MemoryOperatingModeCapability.Bits.Unknown = 0;
+
+  SetDimmMemoryType (SpdData, Type17);
+  SetDimmMemoryTechnology (SpdData, Type17);
+  SetDimmMemoryFormFactor (SpdData, Type17);
+
+  return EFI_SUCCESS;
+}
+
+/** Parses the SDRAM density from the SPD buffer.
+
+  @param Byte  SPD SDRAM density byte.
+
+  @return SDRAM density per die, in Mb. Returns 0 on error.
+
+**/
+STATIC
+UINT32
+GetSdramDensityPerDie (
+  UINT8  Byte
+  )
+{
+  UINT8  Nibble;
+
+  Nibble = Byte & 0xF;
+
+  if (Nibble > ARRAY_SIZE (SdramCapacitiesPerDie)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "Total SDRAM capacity per die invalid/unknown: %01X\n",
+      Nibble
+      ));
+    return 0;
+  }
+
+  return SdramCapacitiesPerDie[Nibble];
+}
+
+/** Parses the DIMM capacity from the SPD buffer.
+
+  @param SpdData SPD data buffer.
+  @param Type17  SMBIOS Type17 table.
+
+**/
+STATIC
+EFI_STATUS
+UpdateCapacity (
+  IN UINT8                    *SpdData,
+  IN OUT SMBIOS_TABLE_TYPE17  *Type17
+  )
+{
+  UINT64   TotalCapacity;
+  UINT64   EvenRanksCapacity;
+  UINT64   OddRanksCapacity;
+  UINT32   PrimarySdramDensityPerDieMb;
+  UINT32   SecondarySdramDensityPerDieMb;
+  UINT8    PrimaryDieCount;
+  UINT8    SecondaryDieCount;
+  BOOLEAN  SymmetricalAssembly;
+  UINT8    PackageRanksPerDimm;
+  UINT8    SdramDeviceWidth;
+  UINT64   PrimaryLogicalRanksPerDimm;
+  UINT64   SecondaryLogicalRanksPerDimm;
+  UINT8    PrimaryBusWidth;
+  UINT8    BusWidthExtension;
+  UINT8    PrimarySignalLoadingValue;
+  UINT8    SecondarySignalLoadingValue;
+
+  PrimarySdramDensityPerDieMb =
+    GetSdramDensityPerDie (SpdData[DDR_SPD_FIRST_SDRAM_DENSITY_AND_PACKAGE_IDX]);
+
+  PrimaryDieCount = PRIMARY_DIE_COUNT (SpdData);
+
+  SymmetricalAssembly = (SpdData[DDR_SPD_SECONDARY_SDRAM_PACKAGE_TYPE_IDX] == 0);
+
+  PackageRanksPerDimm = PACKAGE_RANKS_PER_DIMM (SpdData);
+  SdramDeviceWidth    = SDRAM_DEVICE_WIDTH (SpdData);
+  PackageRanksPerDimm = PACKAGE_RANKS_PER_DIMM (SpdData);
+
+  if (!SymmetricalAssembly) {
+    UINT8  DramDensityRatio;
+
+    SecondaryDieCount = SECONDARY_DIE_COUNT (SpdData);
+
+    DramDensityRatio = DRAM_SENSITY_RATIO (SpdData);
+
+    if (DramDensityRatio == 0) {
+      SecondarySdramDensityPerDieMb = PrimarySdramDensityPerDieMb;
+    } else if (DramDensityRatio == 1) {
+      SecondarySdramDensityPerDieMb =
+        GetSdramDensityPerDie (SpdData[DDR_SPD_FIRST_SDRAM_DENSITY_AND_PACKAGE_IDX] - 1);
+    } else if (DramDensityRatio == 2) {
+      SecondarySdramDensityPerDieMb =
+        GetSdramDensityPerDie (SpdData[DDR_SPD_FIRST_SDRAM_DENSITY_AND_PACKAGE_IDX] - 2);
+    }
+
+    SecondarySignalLoadingValue = SECONDARY_SIGNAL_LOADING_VALUE (SpdData);
+
+    if (SecondarySignalLoadingValue == VAL_SIGNAL_LOADING_3DS) {
+      SecondaryLogicalRanksPerDimm = PackageRanksPerDimm * SecondaryDieCount;
+    } else {
+      SecondaryLogicalRanksPerDimm = PackageRanksPerDimm;
+    }
+  }
+
+  PrimarySignalLoadingValue = PRIMARY_SIGNAL_LOADING_VALUE (SpdData);
+
+  BusWidthExtension = 0;
+
+  if (BUS_WIDTH_EXTENSION (SpdData) == 0) {
+    BusWidthExtension = 0;
+  } else if (BUS_WIDTH_EXTENSION (SpdData) == 1) {
+    BusWidthExtension = 8;
+  } else {
+    DEBUG ((DEBUG_ERROR, "Invalid bus width extension: %d\n", BUS_WIDTH_EXTENSION (SpdData)));
+  }
+
+  PrimaryBusWidth = PRIMARY_BUS_WIDTH (SpdData);
+
+  Type17->DataWidth  = PrimaryBusWidth;
+  Type17->TotalWidth = PrimaryBusWidth + BusWidthExtension;
+
+  if (PrimarySignalLoadingValue == VAL_SIGNAL_LOADING_3DS) {
+    PrimaryLogicalRanksPerDimm = PackageRanksPerDimm * PrimaryDieCount;
+  } else {
+    PrimaryLogicalRanksPerDimm = PackageRanksPerDimm;
+  }
+
+  if (SymmetricalAssembly) {
+    TotalCapacity = (PrimarySdramDensityPerDieMb / 8) *
+                    (PrimaryBusWidth / SdramDeviceWidth) *
+                    PrimaryLogicalRanksPerDimm;
+  } else {
+    EvenRanksCapacity = (PrimarySdramDensityPerDieMb / 8) *
+                        (PrimaryBusWidth / SdramDeviceWidth) *
+                        (PrimaryLogicalRanksPerDimm / 2);
+
+    OddRanksCapacity = (SecondarySdramDensityPerDieMb / 8) *
+                       (PrimaryBusWidth / SdramDeviceWidth) *
+                       (SecondaryLogicalRanksPerDimm / 2);
+
+    TotalCapacity = EvenRanksCapacity + OddRanksCapacity;
+  }
+
+  /*
+    From the SMBIOS Specification 3.6:
+
+    If the value is 0, no memory device is installed in
+    the socket; if the size is unknown, the field value is
+    FFFFh. If the size is 32 GB-1 MB or greater, the
+    field value is 7FFFh and the actual size is stored in
+    the Extended Size field.
+    The granularity in which the value is specified
+    depends on the setting of the most-significant bit
+    (bit 15). If the bit is 0, the value is specified in
+    megabyte units; if the bit is 1, the value is specified
+    in kilobyte units. For example, the value 8100h
+    identifies a 256 KB memory device and 0100h
+    identifies a 256 MB memory device.
+  */
+
+  if (TotalCapacity < MAX_INT16) {
+    Type17->Size = (UINT16)TotalCapacity;
+  } else {
+    Type17->Size = TYPE17_SIZE_USE_EXTENDED_FIELD;
+    // Bits 30:0 represent the size of the memory device in megabytes.
+    Type17->ExtendedSize = (UINT32)TotalCapacity;
+  }
+
+  Type17->VolatileSize = TotalCapacity * SIZE_1MB;
+  Type17->LogicalSize  = TotalCapacity * SIZE_1MB;
+
+  return EFI_SUCCESS;
+}
+
+/** Main entry point for parsing a DDR4 SPD buffer.
+
+  @param SpdData            SPD data buffer.
+  @param SpdBufferSize      The size of the SPD data buffer.
+  @param Type17             SMBIOS Type17 table. Allocated by this library.
+                            Free with FreePool.
+  @param FixedStringsLength The length of fixed strings in the Type17 table.
+
+**/
+EFI_STATUS
+ParseDdr4 (
+  IN     UINT8             *SpdData,
+  IN     UINTN             SpdBufferSize,
+  OUT SMBIOS_TABLE_TYPE17  **Type17,
+  IN     UINTN             FixedStringsLength
+  )
+{
+  EFI_STATUS           Status;
+  UINTN                SpdBytesTotal;
+  UINTN                BufferSize;
+  UINT16               Crc;
+  SMBIOS_TABLE_TYPE17  *Table;
+  SPD_DDR4             *Spd;
+
+  Spd = (SPD_DDR4 *)SpdData;
+
+  if (Spd->Base.Description.Bits.BytesTotal >= ARRAY_SIZE (SpdBytes)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "SPD bytes total unknown/invalid: %02x (%d vs %d)\n",
+      SPD_BYTES_TOTAL (SpdData),
+      SpdBytes[SPD_BYTES_TOTAL (SpdData)],
+      SpdBufferSize
+      ));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SpdBytesTotal = SpdBytes[SPD_BYTES_TOTAL (SpdData)];
+
+  if (SpdBufferSize != SpdBytesTotal) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "SPD bytes total (%d) mismatch buffer size (%d)\n",
+      SpdBytesTotal,
+      SpdBufferSize
+      ));
+
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check that the CRC is valid
+  Crc = Crc16 (SpdData, DDR_SPD_CRC_NUM_BYTES);
+
+  if (((Crc & 0xFF) != SpdData[DDR_SPD_CRC_BYTE_1_IDX]) ||
+      (Crc >> 8 != SpdData[DDR_SPD_CRC_BYTE_2_IDX]))
+  {
+    DEBUG ((DEBUG_ERROR, "!!! ERROR !!! SPD CRC Mismatch\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  BufferSize = sizeof (SMBIOS_TABLE_TYPE17) +
+               FixedStringsLength +
+               (Jep106GetLongestManufacturerName () + 1) +
+               (SMBIOS_SERIAL_NUMBER_LENGTH + 1)  +
+               (DDR_SPD_DDR4_PART_NUMBER_LENGTH + 1);
+
+  *Type17 = AllocateZeroPool (BufferSize);
+  if (*Type17 == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Table = *Type17;
+
+  Table->Hdr.Type   = EFI_SMBIOS_TYPE_MEMORY_DEVICE;
+  Table->Hdr.Handle = SMBIOS_HANDLE_PI_RESERVED;
+  Table->Hdr.Length = sizeof (SMBIOS_TABLE_TYPE17);
+
+  Status = UpdateModuleType (SpdData, Table);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = UpdateCapacity (SpdData, Table);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // DDR4 operates at 1.2V
+  Table->MinimumVoltage    = 1200;
+  Table->MaximumVoltage    = 1200;
+  Table->ConfiguredVoltage = 1200;
+
+  Table->ModuleManufacturerID = Spd->ManufactureInfo.ModuleId.IdCode.Data;
+
+  UpdatePartNumber (
+    SpdData,
+    DDR_SPD_MODULE_PART_NUM_IDX,
+    *Type17,
+    FixedStringsLength,
+    FALSE
+    );
+
+  Table->MemorySubsystemControllerManufacturerID = Spd->ManufactureInfo.DramIdCode.Data;
+  Table->MemorySubsystemControllerProductID      = 0x0000;
+
+  UpdateManufacturer (
+    SpdData,
+    DDR_SPD_DRAM_MFG_ID_CODE_1_IDX,
+    *Type17,
+    FixedStringsLength,
+    FALSE
+    );
+  UpdateSerialNumber (
+    SpdData,
+    DDR_SPD_MODULE_MFG_ID_CODE_1_IDX,
+    *Type17,
+    FixedStringsLength
+    );
+
+  return EFI_SUCCESS;
+}
diff --git a/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr5.c b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr5.c
new file mode 100644
index 000000000000..82f0a544c9d5
--- /dev/null
+++ b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr5.c
@@ -0,0 +1,348 @@
+/** @file
+    Functions for parsing SPD buffers for DDR5 DIMMs.
+
+    Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+    SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/JedecJep106Lib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/Smbios.h>
+#include <IndustryStandard/SdramSpd.h>
+#include <IndustryStandard/SpdDdr5.h>
+
+#include "SmbiosType17SpdLibInternal.h"
+
+/**
+ Encoding of the value in the SPD Bytes Total field (byte 0, bits 6:4)
+**/
+STATIC UINTN  SpdBytes[] = {
+  0,
+  256,
+  512,
+  1024,
+  2048
+};
+
+/**
+ Encoding of the value in the Die Per Package field (byte 4 and 8, bits 7:5)
+**/
+STATIC UINTN  DiePerPackage[] = {
+  1,
+  2,
+  2,
+  4,
+  8,
+  16
+};
+
+/**
+  Encoding of the value in the SDRAM Density Per Die field (byte 4 and 8, bits 4:0)
+**/
+STATIC UINT32  SdramCapacitiesPerDie[] = {
+  0,
+  4096,
+  8192,
+  12288,
+  16384,
+  24576,
+  32768,
+  49152,
+  65536
+};
+
+/** Parses the DIMM module type from the SPD buffer.
+
+  @param SpdData SPD data buffer.
+  @param Type17  SMBIOS Type17 table.
+
+**/
+STATIC
+EFI_STATUS
+UpdateModuleType (
+  IN UINT8                    *SpdData,
+  IN OUT SMBIOS_TABLE_TYPE17  *Type17
+  )
+{
+  Type17->TypeDetail.Unknown                         = 0;
+  Type17->MemoryOperatingModeCapability.Bits.Unknown = 0;
+
+  SetDimmMemoryType (SpdData, Type17);
+  SetDimmMemoryTechnology (SpdData, Type17);
+  SetDimmMemoryFormFactor (SpdData, Type17);
+
+  return EFI_SUCCESS;
+}
+
+/** Parses the SDRAM density from the SPD buffer.
+
+  @param Byte    SPD SDRAM density byte.
+
+  @return SDRAM density per die, in Mb. Returns 0 on error.
+
+**/
+UINT32
+GetSdramDensityPerDie (
+  UINT8  Byte
+  )
+{
+  UINT8  Value;
+
+  Value = Byte & 0x1F;
+
+  if ((Value == 0) || (Value >= ARRAY_SIZE (SdramCapacitiesPerDie))) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "Total SDRAM capacity per die invalid/unknown: %01X\n",
+      Value
+      ));
+    return 0;
+  }
+
+  return SdramCapacitiesPerDie[Value];
+}
+
+/** Parses the DIMM module type from the SPD buffer.
+
+  @param SpdData SPD data buffer.
+  @param Type17  SMBIOS Type17 table.
+
+**/
+STATIC
+EFI_STATUS
+UpdateCapacity (
+  IN SPD_DDR5                 *Spd,
+  IN OUT SMBIOS_TABLE_TYPE17  *Type17
+  )
+{
+  UINT64   TotalCapacity;
+  UINT64   CapacityOfEvenRanks;
+  UINT64   CapacityOfOddRanks;
+  UINT32   FirstSdramDensityPerDieMb;
+  UINT32   SecondSdramDensityPerDieMb;
+  UINT8    FirstDieCount;
+  UINT8    SecondDieCount;
+  UINT8    FirstSdramIOWidth;
+  UINT8    SecondSdramIOWidth;
+  UINT8    NumChannelsPerDimm;
+  UINT8    PrimaryBusWidthPerChannel;
+  UINT8    NumPackageRanksPerChannel;
+  BOOLEAN  SymmetricalAssembly;
+
+  FirstSdramDensityPerDieMb =
+    GetSdramDensityPerDie (Spd->Base.FirstSdramDensityAndPackage.Data);
+  FirstDieCount             = (UINT8)DiePerPackage[Spd->Base.FirstSdramDensityAndPackage.Bits.Die];
+  FirstSdramIOWidth         = 4 << Spd->Base.FirstSdramIoWidth.Bits.IoWidth;
+  NumChannelsPerDimm        = Spd->Common.MemoryChannelBusWidth.Bits.SubChannelsPerDimmCount + 1;
+  PrimaryBusWidthPerChannel = 8 << (Spd->Common.MemoryChannelBusWidth.Bits.PrimaryBusWidthPerSubChannel);
+  NumPackageRanksPerChannel = Spd->Common.ModuleOrganization.Bits.PackageRanksCount + 1;
+
+  if (Spd->Common.ModuleOrganization.Bits.RankMix == 0) {
+    SymmetricalAssembly = TRUE;
+  } else {
+    SymmetricalAssembly = FALSE;
+  }
+
+  Type17->DataWidth  = (8 << Spd->Common.MemoryChannelBusWidth.Bits.PrimaryBusWidthPerSubChannel) * (Spd->Common.MemoryChannelBusWidth.Bits.SubChannelsPerDimmCount + 1);
+  Type17->TotalWidth = Type17->DataWidth + ((Spd->Common.MemoryChannelBusWidth.Bits.BusWidthExtensionPerSubChannel << 2) * (Spd->Common.MemoryChannelBusWidth.Bits.SubChannelsPerDimmCount + 1));
+
+  /*
+    According to JESD400-5, to calculate the total capacity in bytes for a
+    symmetric module, the following math applies:
+
+    Capacity in bytes =
+      Number of channels per DIMM *
+      Primary bus width per channel / SDRAM I/O Width *
+      Die per package *
+      SDRAM density per die / 8 *
+      Package ranks per channel
+
+    To calculate the total capacity in bytes for an asymmetric module, the
+    following math applies:
+
+      Capacity in bytes =
+      Capacity of even ranks (first SDRAM type) +
+      Capacity of odd ranks (second SDRAM type)
+
+      Commonly, parity or ECC are not counted in total module capacity, though
+      they can also be included by adding the bus width extension in SPD byte
+      235 bits 4~3 to the primary bus width in the previous examples.
+  */
+
+  if (!SymmetricalAssembly) {
+    SecondDieCount             = (UINT8)DiePerPackage[Spd->Base.SecondSdramDensityAndPackage.Bits.Die];
+    SecondSdramDensityPerDieMb =
+      GetSdramDensityPerDie (Spd->Base.SecondSdramDensityAndPackage.Data);
+    SecondSdramIOWidth = 4 << Spd->Base.SecondSdramIoWidth.Bits.IoWidth;
+
+    CapacityOfEvenRanks = NumChannelsPerDimm *
+                          (PrimaryBusWidthPerChannel / FirstSdramIOWidth) *
+                          FirstDieCount *
+                          (FirstSdramDensityPerDieMb / 8) *
+                          NumPackageRanksPerChannel;
+
+    CapacityOfOddRanks = NumChannelsPerDimm *
+                         (PrimaryBusWidthPerChannel / SecondSdramIOWidth) *
+                         SecondDieCount *
+                         (SecondSdramDensityPerDieMb / 8) *
+                         NumPackageRanksPerChannel;
+
+    TotalCapacity = CapacityOfEvenRanks + CapacityOfOddRanks;
+  } else {
+    TotalCapacity = NumChannelsPerDimm *
+                    (PrimaryBusWidthPerChannel / FirstSdramIOWidth) *
+                    FirstDieCount *
+                    (FirstSdramDensityPerDieMb / 8) *
+                    NumPackageRanksPerChannel;
+  }
+
+  /*
+    From the SMBIOS Specification 3.6:
+
+    If the value is 0, no memory device is installed in
+    the socket; if the size is unknown, the field value is
+    FFFFh. If the size is 32 GB-1 MB or greater, the
+    field value is 7FFFh and the actual size is stored in
+    the Extended Size field.
+    The granularity in which the value is specified
+    depends on the setting of the most-significant bit
+    (bit 15). If the bit is 0, the value is specified in
+    megabyte units; if the bit is 1, the value is specified
+    in kilobyte units. For example, the value 8100h
+    identifies a 256 KB memory device and 0100h
+    identifies a 256 MB memory device.
+  */
+  if (TotalCapacity < MAX_INT16) {
+    Type17->Size = (UINT16)TotalCapacity;
+  } else {
+    Type17->Size         = TYPE17_SIZE_USE_EXTENDED_FIELD;
+    Type17->ExtendedSize = (UINT32)TotalCapacity;
+  }
+
+  Type17->VolatileSize = TotalCapacity * SIZE_1MB;
+  Type17->LogicalSize  = TotalCapacity * SIZE_1MB;
+
+  return EFI_SUCCESS;
+}
+
+/** Main entry point for parsing a DDR5 SPD buffer.
+
+  @param SpdData       SPD data buffer.
+  @param SpdBufferSize The size of the SPD data buffer.
+  @param Type17        SMBIOS Type17 table. Allocated by this library. Free with FreePool.
+  @param FixedStringsLength The length of fixed strings in the Type17 table.
+
+**/
+EFI_STATUS
+ParseDdr5 (
+  IN     UINT8             *SpdData,
+  IN     UINTN             SpdBufferSize,
+  OUT SMBIOS_TABLE_TYPE17  **Type17,
+  IN     UINTN             FixedStringsLength
+  )
+{
+  EFI_STATUS           Status;
+  SPD_DDR5             *Spd;
+  UINTN                SpdBytesTotal;
+  UINTN                BufferSize;
+  SMBIOS_TABLE_TYPE17  *Table;
+  UINT16               Crc;
+
+  Spd = (SPD_DDR5 *)SpdData;
+
+  if (SpdBytes[Spd->Base.Description.Bits.BytesTotal] >= ARRAY_SIZE (SpdBytes)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "SPD bytes total unknown/invalid: %02x (%d vs %d)\n",
+      Spd->Base.Description.Bits.BytesTotal,
+      SpdBytes[Spd->Base.Description.Bits.BytesTotal],
+      SpdBufferSize
+      ));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SpdBytesTotal = SpdBytes[Spd->Base.Description.Bits.BytesTotal];
+
+  if (SpdBufferSize != SpdBytesTotal) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "SPD bytes total (%d) mismatch buffer size (%d)\n",
+      SpdBytesTotal,
+      SpdBufferSize
+      ));
+
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check that the CRC is valid
+  Crc = Crc16 (SpdData, DDR_SPD_CRC_NUM_BYTES);
+
+  if (((Crc & 0xFF) != SpdData[DDR_SPD_CRC_BYTE_1_IDX]) ||
+      (Crc >> 8 != SpdData[DDR_SPD_CRC_BYTE_2_IDX]))
+  {
+    DEBUG ((DEBUG_ERROR, "!!! ERROR !!! SPD CRC Mismatch\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  BufferSize = sizeof (SMBIOS_TABLE_TYPE17) +
+               FixedStringsLength +
+               (Jep106GetLongestManufacturerName () + 1) +
+               (SMBIOS_SERIAL_NUMBER_LENGTH + 1)  +
+               (DDR_SPD_DDR5_PART_NUMBER_LENGTH + 1);
+
+  *Type17 = AllocateZeroPool (BufferSize);
+  if (*Type17 == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Table = *Type17;
+
+  Table->Hdr.Type   = EFI_SMBIOS_TYPE_MEMORY_DEVICE;
+  Table->Hdr.Handle = SMBIOS_HANDLE_PI_RESERVED;
+  Table->Hdr.Length = sizeof (SMBIOS_TABLE_TYPE17);
+
+  Status = UpdateModuleType (SpdData, Table);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = UpdateCapacity (Spd, Table);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // DDR5 operates at 1.1V (1100 mV)
+  Table->MinimumVoltage    = 1100;
+  Table->MaximumVoltage    = 1100;
+  Table->ConfiguredVoltage = 1100;
+
+  Table->ModuleManufacturerID = Spd->ManufactureInfo.ModuleManufacturer.Data;
+  Table->ModuleProductID      = 0x0000;
+
+  UpdatePartNumber (SpdData, DDR_SPD_MODULE_PART_NUM_IDX, *Type17, FixedStringsLength, TRUE);
+
+  Table->MemorySubsystemControllerManufacturerID = Spd->ManufactureInfo.DramManufacturer.Data;
+  Table->MemorySubsystemControllerProductID      = 0x0000;
+
+  UpdateManufacturer (
+    SpdData,
+    DDR_SPD_MODULE_MFG_ID_CODE_1_IDX,
+    *Type17,
+    FixedStringsLength,
+    TRUE
+    );
+  UpdateSerialNumber (
+    SpdData,
+    DDR_SPD_MODULE_MFG_ID_CODE_1_IDX,
+    *Type17,
+    FixedStringsLength
+    );
+
+  return EFI_SUCCESS;
+}
diff --git a/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Utils.c b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Utils.c
new file mode 100644
index 000000000000..eb0cb2808d8e
--- /dev/null
+++ b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Utils.c
@@ -0,0 +1,405 @@
+/** @file
+    Functions for parsing SPD buffers for DDR4 and DDR5 DIMMs.
+
+    Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+    SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/JedecJep106Lib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/SmbiosType17SpdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/Smbios.h>
+#include <IndustryStandard/SdramSpd.h>
+
+#include "SmbiosType17SpdLibInternal.h"
+
+/** Parses the DIMM memory type from the SPD buffer
+
+  @param SpdData SPD data buffer.
+  @param Type17  SMBIOS Type17 table.
+
+**/
+VOID
+SetDimmMemoryType (
+  IN UINT8                 *SpdData,
+  OUT SMBIOS_TABLE_TYPE17  *Type17
+  )
+{
+  switch (SpdData[DDR_SPD_PROTOCOL_TYPE_IDX]) {
+    case SPD_VAL_SDR_TYPE:
+      Type17->MemoryType             = MemoryTypeSdram;
+      Type17->TypeDetail.Synchronous = 1;
+      break;
+    case SPD_VAL_DDR_TYPE:
+      Type17->MemoryType = MemoryTypeSdram;
+      break;
+    case SPD_VAL_DDR2_TYPE:
+      Type17->MemoryType             = MemoryTypeDdr2;
+      Type17->TypeDetail.Synchronous = 1;
+      break;
+    case SPD_VAL_DDR3_TYPE:
+      Type17->MemoryType             = MemoryTypeDdr3;
+      Type17->TypeDetail.Synchronous = 1;
+      break;
+    case SPD_VAL_DDR4_TYPE:
+      Type17->MemoryType             = MemoryTypeDdr4;
+      Type17->TypeDetail.Synchronous = 1;
+      break;
+    case SPD_VAL_LPDDR3_TYPE:
+      Type17->MemoryType             = MemoryTypeLpddr3;
+      Type17->TypeDetail.Synchronous = 1;
+      break;
+    case SPD_VAL_LPDDR4_TYPE:
+      Type17->MemoryType             = MemoryTypeLpddr4;
+      Type17->TypeDetail.Synchronous = 1;
+      break;
+    case SPD_VAL_DDR5_TYPE:
+      Type17->MemoryType             = MemoryTypeDdr5;
+      Type17->TypeDetail.Synchronous = 1;
+      break;
+    case SPD_VAL_LPDDR5_TYPE:
+      Type17->MemoryType             = MemoryTypeLpddr5;
+      Type17->TypeDetail.Synchronous = 1;
+      break;
+    case SPD_VAL_DDR5_NVDIMMP_TYPE:
+      Type17->MemoryType             = MemoryTypeDdr5;
+      Type17->TypeDetail.Nonvolatile = 1;
+      break;
+    case SPD_VAL_LPDDR5X_TYPE:
+      Type17->MemoryType             = MemoryTypeLpddr5;
+      Type17->TypeDetail.Synchronous = 1;
+      break;
+    default:
+      Type17->MemoryType = MemoryTypeOther;
+      break;
+  }
+}
+
+/** Parses the memory technology from the SPD buffer.
+
+  @param SpdData SPD data buffer.
+  @param Type17  SMBIOS Type17 table.
+
+**/
+VOID
+SetDimmMemoryTechnology (
+  IN UINT8                    *SpdData,
+  IN OUT SMBIOS_TABLE_TYPE17  *Type17
+  )
+{
+  switch (SpdData[DDR_SPD_MODULE_TYPE_IDX] & 0xF0) {
+    case 0:
+      // Module only contains DRAM
+      Type17->MemoryTechnology                                  = MemoryTechnologyDram;
+      Type17->MemoryOperatingModeCapability.Bits.VolatileMemory = 1;
+      break;
+    case 0x90:
+      Type17->TypeDetail.Nonvolatile                                            = 1;
+      Type17->MemoryOperatingModeCapability.Bits.ByteAccessiblePersistentMemory = 1;
+      Type17->MemoryTechnology                                                  = MemoryTechnologyNvdimmN; // (or MemoryTechnologyNvdimmF)
+      break;
+    case 0xA0:
+      Type17->TypeDetail.Nonvolatile                                            = 1;
+      Type17->MemoryOperatingModeCapability.Bits.ByteAccessiblePersistentMemory = 1;
+      Type17->MemoryTechnology                                                  = MemoryTechnologyNvdimmP;
+      break;
+    case 0xB0:
+      Type17->TypeDetail.Nonvolatile                                            = 1;
+      Type17->MemoryOperatingModeCapability.Bits.ByteAccessiblePersistentMemory = 1;
+      Type17->MemoryTechnology                                                  = MemoryTechnologyOther; // MemoryTechnologyNvdimmH
+      break;
+    default:
+      DEBUG ((DEBUG_ERROR, "Invalid Key Byte / Module Type: %02X\n", SpdData[DDR_SPD_MODULE_TYPE_IDX]));
+  }
+}
+
+/** Parses the DIMM form factor from the SPD buffer.
+
+  @param SpdData SPD data buffer.
+  @param Type17  SMBIOS Type17 table.
+
+**/
+VOID
+SetDimmMemoryFormFactor (
+  IN UINT8                    *SpdData,
+  IN OUT SMBIOS_TABLE_TYPE17  *Type17
+  )
+{
+  UINT8  ModuleType;
+
+  ModuleType = SpdData[DDR_SPD_MODULE_TYPE_IDX] & 0x0F;
+
+  // Registered DIMMs
+  if ((ModuleType == SPD_VAL_RDIMM_MODULE) ||
+      (ModuleType == SPD_VAL_MINI_RDIMM_MODULE) ||
+      (ModuleType == SPD_VAL_72B_SORDIMM_MODULE))
+  {
+    Type17->TypeDetail.Registered = 1;
+  }
+
+  // LRDIMMs
+  if (ModuleType == SPD_VAL_LRDIMM_MODULE) {
+    Type17->TypeDetail.LrDimm = 1;
+  }
+
+  // Unbuffered DIMMs
+  if ((ModuleType == SPD_VAL_UDIMM_MODULE) ||
+      (ModuleType == SPD_VAL_MINI_UDIMM_MODULE) ||
+      (ModuleType == SPD_VAL_SODIMM_MODULE) ||
+      (ModuleType == SPD_VAL_16B_SODIMM_MODULE) ||
+      (ModuleType == SPD_VAL_32B_SODIMM_MODULE) ||
+      (ModuleType == SPD_VAL_72B_SOUDIMM_MODULE))
+  {
+    Type17->TypeDetail.Unbuffered = 1;
+  }
+
+  // SODIMMs
+  if ((ModuleType == SPD_VAL_SODIMM_MODULE) ||
+      (ModuleType == SPD_VAL_16B_SODIMM_MODULE) ||
+      (ModuleType == SPD_VAL_32B_SODIMM_MODULE) ||
+      (ModuleType == SPD_VAL_72B_SOUDIMM_MODULE) ||
+      (ModuleType == SPD_VAL_72B_SORDIMM_MODULE))
+  {
+    Type17->FormFactor = MemoryFormFactorSodimm;
+  }
+
+  // DIMMs
+  if ((ModuleType == SPD_VAL_UDIMM_MODULE) ||
+      (ModuleType == SPD_VAL_MINI_UDIMM_MODULE) ||
+      (ModuleType == SPD_VAL_RDIMM_MODULE) ||
+      (ModuleType == SPD_VAL_MINI_RDIMM_MODULE) ||
+      (ModuleType == SPD_VAL_LRDIMM_MODULE))
+  {
+    Type17->FormFactor = MemoryFormFactorDimm;
+  }
+}
+
+/** Parses the manufacturer from the SPD buffer.
+
+  @param SpdData            SPD data buffer.
+  @param MfgIdCode1Idx      The index of the first byte of the manufacturer ID code.
+  @param Type17             SMBIOS Type17 table.
+  @param FixedStringsLength The length of the fixed strings in the Type 17 table.
+  @param Ddr5               Whether the SPD data buffer is for a DDR5 DIMM.
+
+**/
+VOID
+UpdateManufacturer (
+  IN UINT8     *SpdData,
+  IN UINTN     MfgIdCode1Idx,
+  IN OUT VOID  *Type17,
+  IN UINTN     FixedStringsLength,
+  IN BOOLEAN   Ddr5
+  )
+{
+  UINTN        Offset;
+  UINTN        PartNumberLength;
+  UINT8        ContinuationBytes;
+  CHAR8        *MfgOffset;
+  CONST CHAR8  *ManufacturerName;
+
+  ContinuationBytes = SpdData[MfgIdCode1Idx] & 0x7F;
+
+  if (Ddr5) {
+    PartNumberLength = DDR_SPD_DDR5_PART_NUMBER_LENGTH + 1;
+  } else {
+    PartNumberLength = DDR_SPD_DDR4_PART_NUMBER_LENGTH + 1;
+  }
+
+  Offset = sizeof (SMBIOS_TABLE_TYPE17) +
+           FixedStringsLength +
+           (SMBIOS_SERIAL_NUMBER_LENGTH + 1) +
+           PartNumberLength;
+
+  MfgOffset = (CHAR8 *)Type17 + Offset;
+
+  ManufacturerName = Jep106GetManufacturerName (
+                       SpdData[MfgIdCode1Idx + 1],
+                       ContinuationBytes
+                       );
+  if (ManufacturerName != NULL) {
+    AsciiStrCpyS (MfgOffset, 256, ManufacturerName);
+  }
+}
+
+/** Parses the serial number from the SPD buffer.
+
+  @param SpdData            SPD data buffer.
+  @param SpdSerialNumberIdx The index of the first byte of the serial number.
+  @param Type17             SMBIOS Type17 table.
+  @param FixedStringsLength The length of the fixed strings in the Type 17 table.
+
+**/
+VOID
+UpdateSerialNumber (
+  IN UINT8     *SpdData,
+  IN UINT16    SpdSerialNumberIdx,
+  IN OUT VOID  *Type17,
+  IN UINTN     FixedStringsLength
+  )
+{
+  UINTN  FieldIndex;
+  UINTN  CharIndex;
+  UINTN  Offset;
+  CHAR8  *SerialNumber;
+
+  Offset = sizeof (SMBIOS_TABLE_TYPE17) +
+           FixedStringsLength;
+
+  SerialNumber = (CHAR8 *)Type17 + Offset;
+
+  /*
+    Calculate a serial number as suggested in JESD400-5:
+
+    One method of achieving this is by assigning a byte in the field from
+    517~520 as a tester ID byte and using the remaining bytes as a sequential
+    serial number. Bytes 512~520 will then result in a nine-byte unique module
+    identifier. Note that part number is not included in this identifier: the
+    supplier may not give the same value for Bytes 517~520 to more than one
+    DIMM even if the DIMMs have different part numbers.
+  */
+
+  CharIndex = 0;
+  for (FieldIndex = 0; FieldIndex < DDR_SPD_SERIAL_NUMBER_LENGTH; FieldIndex++) {
+    UINT8  Temp;
+    UINT8  Value;
+
+    Value = SpdData[SpdSerialNumberIdx + FieldIndex];
+
+    Temp = Value >> 4;
+    if (Temp < 10) {
+      SerialNumber[CharIndex] = '0' + Temp;
+    } else {
+      SerialNumber[CharIndex] = 'A' + (Temp - 10);
+    }
+
+    CharIndex++;
+    Temp = Value & 0xF;
+    if (Temp < 10) {
+      SerialNumber[CharIndex] = '0' + Temp;
+    } else {
+      SerialNumber[CharIndex] = 'A' + (Temp - 10);
+    }
+
+    CharIndex++;
+  }
+
+  SerialNumber[CharIndex] = '\0';
+}
+
+/** Parses the part number from the SPD buffer.
+
+  @param SpdData             SPD data buffer.
+  @param PartNumberFieldIdx  Index of the part number field in the SPD data buffer.
+  @param Type17              SMBIOS Type17 table.
+  @param FixedStringsLength  Length of the fixed strings in the SMBIOS structure
+  @param Ddr5                Whether the SPD data buffer is for a DDR5 DIMM.
+
+**/
+VOID
+UpdatePartNumber (
+  IN UINT8     *SpdData,
+  IN UINTN     PartNumberFieldIdx,
+  IN OUT VOID  *Type17,
+  IN UINTN     FixedStringsLength,
+  IN BOOLEAN   Ddr5
+  )
+{
+  UINTN  Offset;
+  UINTN  PartNumberLength;
+  CHAR8  *PartNumber;
+
+  if (Ddr5) {
+    PartNumberLength = DDR_SPD_DDR5_PART_NUMBER_LENGTH + 1;
+  } else {
+    PartNumberLength = DDR_SPD_DDR4_PART_NUMBER_LENGTH + 1;
+  }
+
+  Offset = sizeof (SMBIOS_TABLE_TYPE17) +
+           FixedStringsLength +
+           (SMBIOS_SERIAL_NUMBER_LENGTH + 1);
+
+  PartNumber = (CHAR8 *)Type17 + Offset;
+
+  // The part number is stored as ASCII, and so can just be copied.
+  CopyMem (PartNumber, SpdData + PartNumberFieldIdx, PartNumberLength - 1);
+
+  PartNumber[PartNumberLength] = '\0';
+}
+
+/**
+   CRC16 algorithm from JEDEC 4.1.2.L-6 R30 v14
+
+   @param Data  Data bytes.
+   @param Count Number of bytes to calculate the CRC16 over.
+
+   @return Calculated CRC16 value.
+**/
+UINT16
+Crc16 (
+  UINT8  *Data,
+  INT32  Count
+  )
+{
+  UINT16  Crc;
+  UINT32  Index;
+
+  Crc = 0;
+  while (--Count >= 0) {
+    Crc = Crc ^ (UINT16)*Data++ << 8;
+    for (Index = 0; Index < 8; ++Index) {
+      if (Crc & 0x8000) {
+        Crc = Crc << 1 ^ 0x1021;
+      } else {
+        Crc = Crc << 1;
+      }
+    }
+  }
+
+  return Crc;
+}
+
+/**
+  Given an SPD data buffer from a DDR4 or DDR5 DIMM, returns a
+  pointer to a new SMBIOS_TABLE_TYPE17 structure containing data
+  parsed from the buffer.
+
+  @param SpdData             SPD data buffer.
+  @param SpdDataSize         Size of the SPD data buffer.
+  @param Type17              SMBIOS Type17 table.
+  @param FixedStringsLength  Length of the fixed strings in the SMBIOS structure.
+
+  @return EFI_SUCCESS on success, or an error code.
+
+**/
+EFI_STATUS
+EFIAPI
+GetSmbiosType17FromSpdData (
+  IN     UINT8                *SpdData,
+  IN     UINTN                SpdDataSize,
+  OUT    SMBIOS_TABLE_TYPE17  **Type17,
+  IN UINTN                    FixedStringsLength
+  )
+{
+  EFI_STATUS  Status;
+
+  if (SpdDataSize < (DDR_SPD_PROTOCOL_TYPE_IDX + 1)) {
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  if ((SpdData[DDR_SPD_PROTOCOL_TYPE_IDX] >= SPD_VAL_DDR5_TYPE) &&
+      (SpdData[DDR_SPD_PROTOCOL_TYPE_IDX] <= SPD_VAL_LPDDR5X_TYPE))
+  {
+    Status = ParseDdr5 (SpdData, SpdDataSize, Type17, FixedStringsLength);
+  } else {
+    Status = ParseDdr4 (SpdData, SpdDataSize, Type17, FixedStringsLength);
+  }
+
+  return Status;
+}
diff --git a/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosSpdUnitTest.c b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosSpdUnitTest.c
new file mode 100644
index 000000000000..707946f1b3b0
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosSpdUnitTest.c
@@ -0,0 +1,187 @@
+/** @file
+  Unit tests for the SMBIOS SPD parsing functions.
+
+  Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UnitTestLib.h>
+#include <IndustryStandard/SmBios.h>
+#include <Library/SmbiosType17SpdLib.h>
+
+#include "SpdTestData.h"
+
+#define UNIT_TEST_APP_NAME     "SMBIOS SPD Unit Test Application"
+#define UNIT_TEST_APP_VERSION  "1.0"
+
+typedef struct {
+  const UINT8                  *TestInput;
+  UINTN                        TestInputSize;
+  const SMBIOS_TABLE_TYPE17    *ExpectedResult;
+  EFI_STATUS                   ExpectedStatus;
+} SPD_SMBIOS_TEST_CONTEXT;
+
+// ------------------------------------------------ Input------------------Input Size----------------------Output------------------Result------
+static SPD_SMBIOS_TEST_CONTEXT  mSizeTest1 = { Ddr4DimmTestData1, DDR4_SPD_LEN, &Ddr4DimmTestData1ExpectedResult, EFI_SUCCESS };
+static SPD_SMBIOS_TEST_CONTEXT  mSizeTest2 = { Ddr4DimmTestData2, DDR4_SPD_LEN, &Ddr4DimmTestData2ExpectedResult, EFI_SUCCESS };
+
+/**
+ Unit test to verify functionality for DDR4 SPD data.
+
+ @param Context  Unit test context
+
+ @return UNIT_TEST_PASSED
+**/
+static
+UNIT_TEST_STATUS
+EFIAPI
+SpdCheckTestDdr4 (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  EFI_STATUS               Status;
+  SPD_SMBIOS_TEST_CONTEXT  *TestParams;
+  SMBIOS_TABLE_TYPE17      *Table;
+  UINT8                    *SpdData;
+
+  TestParams = (SPD_SMBIOS_TEST_CONTEXT *)Context;
+  Table      = NULL;
+  SpdData    = (UINT8 *)TestParams->TestInput;
+
+  //
+  // Test case for basic functionality.
+  //
+  Status = GetSmbiosType17FromSpdData (SpdData, TestParams->TestInputSize, &Table, 0);
+  UT_ASSERT_EQUAL (Status, TestParams->ExpectedStatus);
+  UT_ASSERT_NOT_NULL (Table);
+
+  UT_ASSERT_EQUAL (Table->Hdr.Length, sizeof (SMBIOS_TABLE_TYPE17));
+  UT_ASSERT_EQUAL (Table->TotalWidth, TestParams->ExpectedResult->TotalWidth);
+  UT_ASSERT_EQUAL (Table->DataWidth, TestParams->ExpectedResult->DataWidth);
+  UT_ASSERT_EQUAL (Table->Size, TestParams->ExpectedResult->Size);
+  UT_ASSERT_EQUAL (Table->FormFactor, TestParams->ExpectedResult->FormFactor);
+  UT_ASSERT_EQUAL (Table->MemoryType, TestParams->ExpectedResult->MemoryType);
+
+  // In future, we should calculate the speed bin in the library and verify it here.
+  //  UT_ASSERT_EQUAL (Table->Speed, TestParams->ExpectedResult->Speed);
+  //  UT_ASSERT_EQUAL (Table->ConfiguredMemoryClockSpeed, TestParams->ExpectedResult->ConfiguredMemoryClockSpeed);
+
+  UT_ASSERT_EQUAL (Table->MinimumVoltage, TestParams->ExpectedResult->MinimumVoltage);
+  UT_ASSERT_EQUAL (Table->MaximumVoltage, TestParams->ExpectedResult->MaximumVoltage);
+  UT_ASSERT_EQUAL (Table->ConfiguredVoltage, TestParams->ExpectedResult->ConfiguredVoltage);
+  UT_ASSERT_EQUAL (Table->MemoryTechnology, TestParams->ExpectedResult->MemoryTechnology);
+  UT_ASSERT_EQUAL (Table->ModuleManufacturerID, TestParams->ExpectedResult->ModuleManufacturerID);
+  UT_ASSERT_EQUAL (Table->MemorySubsystemControllerManufacturerID, TestParams->ExpectedResult->MemorySubsystemControllerManufacturerID);
+  UT_ASSERT_EQUAL (Table->NonVolatileSize, TestParams->ExpectedResult->NonVolatileSize);
+  UT_ASSERT_EQUAL (Table->VolatileSize, TestParams->ExpectedResult->VolatileSize);
+  UT_ASSERT_EQUAL (Table->CacheSize, TestParams->ExpectedResult->CacheSize);
+  UT_ASSERT_EQUAL (Table->LogicalSize, TestParams->ExpectedResult->LogicalSize);
+  UT_ASSERT_EQUAL (Table->ExtendedSpeed, TestParams->ExpectedResult->ExtendedSpeed);
+  UT_ASSERT_EQUAL (Table->ExtendedConfiguredMemorySpeed, TestParams->ExpectedResult->ExtendedConfiguredMemorySpeed);
+
+  FreePool (Table);
+
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Initialize the unit test framework, suite, and unit tests for the
+  SMBIOS SPD APIs of SmbiosType17SpdLib and run the unit tests.
+
+  @retval  EFI_SUCCESS           All test cases were dispatched.
+  @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available to
+                                 initialize the unit tests.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UnitTestingEntry (
+  VOID
+  )
+{
+  EFI_STATUS                  Status;
+  UNIT_TEST_FRAMEWORK_HANDLE  Fw;
+  UNIT_TEST_SUITE_HANDLE      SpdParseTests;
+
+  Fw = NULL;
+
+  DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION));
+
+  //
+  // Start setting up the test framework for running the tests.
+  //
+  Status = InitUnitTestFramework (&Fw, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status));
+    goto EXIT;
+  }
+
+  //
+  // Populate the SMBIOS SPD Unit Test Suite.
+  //
+  Status = CreateUnitTestSuite (&SpdParseTests, Fw, "SMBIOS SPD Parsing Tests", "SpdParseTest", NULL, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for SpdParseTests\n"));
+    Status = EFI_OUT_OF_RESOURCES;
+    goto EXIT;
+  }
+
+  // --------------Suite-----------Description-------------------Class Name----------Function-------Pre---Post---Context-----
+  AddTestCase (SpdParseTests, "SMBIOS SPD Test 1 - DDR4 DIMM", "SmbiosSpd.Test1", SpdCheckTestDdr4, NULL, NULL, &mSizeTest1);
+  AddTestCase (SpdParseTests, "SMBIOS SPD Test 2 - DDR4 DIMM", "SmbiosSpd.Test2", SpdCheckTestDdr4, NULL, NULL, &mSizeTest2);
+
+  //
+  // Execute the tests.
+  //
+  Status = RunAllTestSuites (Fw);
+
+EXIT:
+  if (Fw) {
+    FreeUnitTestFramework (Fw);
+  }
+
+  return Status;
+}
+
+/**
+  Standard UEFI entry point for target based unit test execution from UEFI Shell.
+
+  @param ImageHandle Image handle.
+  @param SystemTable System table.
+
+  @retval  EFI_SUCCESS           All test cases were dispatched.
+  @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available to
+                                 initialize the unit tests.
+**/
+EFI_STATUS
+EFIAPI
+BaseLibUnitTestAppEntry (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  return UnitTestingEntry ();
+}
+
+/**
+  Standard POSIX C entry point for host based unit test execution.
+
+  @param argc Number of arguments
+  @param argv Array of arguments
+
+  @return 0 on success; non-zero on failure.
+**/
+int
+main (
+  int   argc,
+  char  *argv[]
+  )
+{
+  return UnitTestingEntry ();
+}
diff --git a/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.c b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.c
new file mode 100644
index 000000000000..b0eaff924020
--- /dev/null
+++ b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.c
@@ -0,0 +1,164 @@
+/** @file
+  Arrays defining DDR4 and DDR5 SPD EEPROM data
+
+  Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/SmBios.h>
+
+// Data obtained from running systems (with e.g. `hexdump -C /sys/bus/i2c/drivers/ee1004/0-0050/eeprom`)
+// or from Micron's website.
+
+// C code generated from binary files with `xxd -i <inputfile>.bin`
+
+/*
+  https://www.micron.com/products/dram-modules/udimm/part-catalog/mta9asf2g72az-3g2
+
+  Micron 16GB PC4-25600 DDR4-3200MHz ECC Unbuffered CL22 UDIMM 1.2V Single-Rank Memory Module
+*/
+const UINT8  Ddr4DimmTestData1[] = {
+  0x23, 0x11, 0x0c, 0x02, 0x86, 0x29, 0x00, 0x08, 0x00, 0x60, 0x00, 0x03,
+  0x01, 0x0b, 0x80, 0x00, 0x00, 0x00, 0x05, 0x0d, 0xf8, 0xff, 0x2f, 0x00,
+  0x6e, 0x6e, 0x6e, 0x11, 0x00, 0x6e, 0xf0, 0x0a, 0x20, 0x08, 0x00, 0x05,
+  0x00, 0xa8, 0x14, 0x28, 0x28, 0x00, 0x78, 0x00, 0x14, 0x3c, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x16, 0x36, 0x16, 0x36, 0x16, 0x36, 0x16, 0x36, 0x16, 0x36, 0x16, 0x36,
+  0x16, 0x36, 0x16, 0x36, 0x16, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, 0xa8, 0x14, 0x11, 0x01, 0x43, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x89, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2c, 0x0f, 0x20,
+  0x45, 0x2b, 0x38, 0xf2, 0x69, 0x39, 0x41, 0x53, 0x46, 0x32, 0x47, 0x37,
+  0x32, 0x41, 0x5a, 0x2d, 0x33, 0x47, 0x32, 0x42, 0x31, 0x20, 0x20, 0x20,
+  0x20, 0x31, 0x80, 0x2c, 0x42, 0x44, 0x50, 0x41, 0x51, 0x38, 0x30, 0x33,
+  0x30, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+const UINTN Ddr4DimmTestData1Size = sizeof (Ddr4DimmTestData1);
+
+const SMBIOS_TABLE_TYPE17  Ddr4DimmTestData1ExpectedResult = {
+  .TotalWidth                              = 72,
+  .DataWidth                               = 64,
+  .Size                                    = (16 * 1024),
+  .FormFactor                              = MemoryFormFactorDimm,
+  .MemoryType                              = MemoryTypeDdr4,
+  .Speed                                   = 3200,
+  .ConfiguredMemoryClockSpeed              = 3200,
+  .MinimumVoltage                          = 1200,
+  .MaximumVoltage                          = 1200,
+  .ConfiguredVoltage                       = 1200,
+  .MemoryTechnology                        = MemoryTechnologyDram,
+  .ModuleManufacturerID                    = 0x2C80,
+  .MemorySubsystemControllerManufacturerID = 0x2C80,
+  .NonVolatileSize                         = 0,
+  .VolatileSize                            = (16ULL * 1024 * 1024 * 1024),
+  .CacheSize                               = 0,
+  .LogicalSize                             = (16ULL * 1024 * 1024 * 1024),
+  .ExtendedSpeed                           = 0,
+  .ExtendedConfiguredMemorySpeed           = 0
+};
+
+/*
+  https://www.micron.com/products/dram-modules/vlp-rdimm/part-catalog/mta9adf1g72pz-3g2
+
+  Micron MTA9ADF1G72PZ-3G2E1 memory module 8 GB 1x8GB DDR4 3200 MT/s ECC VLP 288-pin RDIMM
+*/
+const UINT8  Ddr4DimmTestData2[] = {
+  0x23, 0x12, 0x0C, 0x01, 0x85, 0x21, 0x00, 0x08, 0x00, 0x60, 0x00, 0x03,
+  0x01, 0x0B, 0x80, 0x00, 0x00, 0x00, 0x05, 0x0D, 0xF8, 0xFF, 0x2F, 0x00,
+  0x6E, 0x6E, 0x6E, 0x11, 0x00, 0x6E, 0xF0, 0x0A, 0x20, 0x08, 0x00, 0x05,
+  0x00, 0xA8, 0x14, 0x28, 0x28, 0x00, 0x78, 0x00, 0x14, 0x3C, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x24, 0x03, 0x15, 0x2C, 0x24, 0x03, 0x15, 0x2C, 0x24, 0x03, 0x24, 0x03,
+  0x15, 0x2C, 0x24, 0x03, 0x15, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xE7, 0x00, 0xFF, 0xDF, 0x04, 0x11, 0x06, 0x15,
+  0x00, 0x86, 0x32, 0xD1, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0xB7, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2C, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x41, 0x44, 0x46, 0x31, 0x47, 0x37,
+  0x32, 0x50, 0x5A, 0x2D, 0x33, 0x47, 0x32, 0x45, 0x31, 0x00, 0x00, 0x00,
+  0x00, 0x31, 0x80, 0x2C, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+const UINTN Ddr4DimmTestData2Size = sizeof (Ddr4DimmTestData2);
+
+const SMBIOS_TABLE_TYPE17  Ddr4DimmTestData2ExpectedResult = {
+  .TotalWidth                              = 72,
+  .DataWidth                               = 64,
+  .Size                                    = (8 * 1024),
+  .FormFactor                              = MemoryFormFactorDimm,
+  .MemoryType                              = MemoryTypeDdr4,
+  .Speed                                   = 3200,
+  .ConfiguredMemoryClockSpeed              = 3200,
+  .MinimumVoltage                          = 1200,
+  .MaximumVoltage                          = 1200,
+  .ConfiguredVoltage                       = 1200,
+  .MemoryTechnology                        = MemoryTechnologyDram,
+  .ModuleManufacturerID                    = 0x2C80,
+  .MemorySubsystemControllerManufacturerID = 0x2C80,
+  .NonVolatileSize                         = 0,
+  .VolatileSize                            = (8ULL * 1024 * 1024 * 1024),
+  .CacheSize                               = 0,
+  .LogicalSize                             = (8ULL * 1024 * 1024 * 1024),
+  .ExtendedSpeed                           = 0,
+  .ExtendedConfiguredMemorySpeed           = 0
+};
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [RFC PATCH 1/1] MdePkg: Add library to parse SPD data and create SMBIOS Type 17 table
  2023-02-14 21:58 [RFC PATCH 1/1] MdePkg: Add library to parse SPD data and create SMBIOS Type 17 table Rebecca Cran
@ 2023-02-14 21:59 ` Rebecca Cran
  0 siblings, 0 replies; 2+ messages in thread
From: Rebecca Cran @ 2023-02-14 21:59 UTC (permalink / raw)
  To: devel, Michael D Kinney, Liming Gao, Zhiguang Liu

Obviously this will need split up into several patches, but I wanted to 
get it sent out as an rfc so it doesn't get lost.

-- 
Rebecca Cran

On 2/14/23 14:58, Rebecca Cran wrote:
> SmbiosType17SpdLib can parse a buffer containing SPD data from a DDR4
> or DDR5 DIMM and construct an SMBIOS Type17 table.
> 
> Signed-off-by: Rebecca Cran <rebecca@quicinc.com>
> ---
>   MdePkg/MdePkg.dec                                                                   |   3 +
>   MdePkg/MdeLibs.dsc.inc                                                              |   2 +
>   MdePkg/MdePkg.dsc                                                                   |   2 +
>   MdePkg/Test/MdePkgHostTest.dsc                                                      |   5 +
>   MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf                            |  40 ++
>   MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosType17SpdLibUnitTestsHost.inf |  35 ++
>   MdePkg/Include/IndustryStandard/SdramSpd.h                                          |  53 ++-
>   MdePkg/Include/IndustryStandard/SdramSpdDdr4.h                                      |  23 ++
>   MdePkg/Include/Library/SmbiosType17SpdLib.h                                         |  37 ++
>   MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLibInternal.h                      | 108 ++++++
>   MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.h                       |  20 +
>   MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr4.c                                | 369 ++++++++++++++++++
>   MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr5.c                                | 348 +++++++++++++++++
>   MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Utils.c                               | 405 ++++++++++++++++++++
>   MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosSpdUnitTest.c                 | 187 +++++++++
>   MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.c                       | 164 ++++++++
>   16 files changed, 1787 insertions(+), 14 deletions(-)
> 
> diff --git a/MdePkg/MdePkg.dec b/MdePkg/MdePkg.dec
> index 3d08f20d15b0..fa92e884dcbe 100644
> --- a/MdePkg/MdePkg.dec
> +++ b/MdePkg/MdePkg.dec
> @@ -257,6 +257,9 @@ [LibraryClasses]
>     #
>     UnitTestLib|Include/Library/UnitTestLib.h
>   
> +  ## @libraryclass Provides service to generate SMBIOS Type 17 table from SPD data.
> +  SmbiosType17SpdLib|Include/Library/SmbiosType17SpdLib.h
> +
>     ## @libraryclass Extension to BaseLib for host based unit tests that allows a
>     #                subset of BaseLib services to be hooked for emulation.
>     #
> diff --git a/MdePkg/MdeLibs.dsc.inc b/MdePkg/MdeLibs.dsc.inc
> index 4580481cb580..4855972b9f0a 100644
> --- a/MdePkg/MdeLibs.dsc.inc
> +++ b/MdePkg/MdeLibs.dsc.inc
> @@ -15,4 +15,6 @@ [LibraryClasses]
>     ArmTrngLib|MdePkg/Library/BaseArmTrngLibNull/BaseArmTrngLibNull.inf
>     RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf
>     CpuLib|MdePkg/Library/BaseCpuLib/BaseCpuLib.inf
> +  JedecJep106Lib|MdePkg/Library/JedecJep106Lib/JedecJep106Lib.inf
> +  SmbiosType17SpdLib|MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf
>     SmmCpuRendezvousLib|MdePkg/Library/SmmCpuRendezvousLibNull/SmmCpuRendezvousLibNull.inf
> diff --git a/MdePkg/MdePkg.dsc b/MdePkg/MdePkg.dsc
> index 32a852dc466e..fde11e7331e2 100644
> --- a/MdePkg/MdePkg.dsc
> +++ b/MdePkg/MdePkg.dsc
> @@ -136,6 +136,8 @@ [Components]
>     MdePkg/Library/CcProbeLibNull/CcProbeLibNull.inf
>     MdePkg/Library/SmmCpuRendezvousLibNull/SmmCpuRendezvousLibNull.inf
>   
> +  MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf
> +
>   [Components.IA32, Components.X64, Components.ARM, Components.AARCH64]
>     #
>     # Add UEFI Target Based Unit Tests
> diff --git a/MdePkg/Test/MdePkgHostTest.dsc b/MdePkg/Test/MdePkgHostTest.dsc
> index b8b186dd8b17..0bf4a248ae97 100644
> --- a/MdePkg/Test/MdePkgHostTest.dsc
> +++ b/MdePkg/Test/MdePkgHostTest.dsc
> @@ -20,7 +20,9 @@ [Defines]
>   !include UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc
>   
>   [LibraryClasses]
> +  JedecJep106Lib|MdePkg/Library/BaseJedecJep106Lib/BaseJedecJep106Lib.inf
>     SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
> +  SmbiosType17SpdLib|MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf
>   
>   [Components]
>     #
> @@ -30,6 +32,9 @@ [Components]
>     MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsHost.inf
>     MdePkg/Test/GoogleTest/Library/BaseSafeIntLib/GoogleTestBaseSafeIntLib.inf
>   
> +  # Build HOST_APPLICATION that tests the SmbiosType17SpdLib
> +  MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosType17SpdLibUnitTestsHost.inf
> +
>     #
>     # Build HOST_APPLICATION Libraries
>     #
> diff --git a/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf
> new file mode 100644
> index 000000000000..97b6125e3673
> --- /dev/null
> +++ b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLib.inf
> @@ -0,0 +1,40 @@
> +## @file
> +#  Library to parse DDR SPD buffers and put that data into SMBIOS Type17 tables.
> +#
> +#  Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 1.29
> +  BASE_NAME                      = SmbiosType17SpdLib
> +  FILE_GUID                      = 22d9302f-e599-4098-b0e6-86c33016fbc1
> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = BASE
> +
> +#
> +# The following information is for reference only and not required by the build tools.
> +#
> +#  VALID_ARCHITECTURES           = AARCH64
> +#
> +
> +[Sources]
> +  SmbiosType17SpdLibInternal.h
> +  SmbiosType17Ddr5.c
> +  SmbiosType17Ddr4.c
> +  SmbiosType17Utils.c
> +
> +[Packages]
> +
> +  MdePkg/MdePkg.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +  BaseMemoryLib
> +  DebugLib
> +  JedecJep106Lib
> +
> +  MemoryAllocationLib
> +  UefiDriverEntryPoint
> diff --git a/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosType17SpdLibUnitTestsHost.inf b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosType17SpdLibUnitTestsHost.inf
> new file mode 100644
> index 000000000000..95a63fd7437b
> --- /dev/null
> +++ b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosType17SpdLibUnitTestsHost.inf
> @@ -0,0 +1,35 @@
> +## @file
> +# Unit tests of the SMBIOS SPD APIs in SmbiosType17SpdLib that are run from host
> +# environment.
> +#
> +# Copyright (C) Qualcomm Innovation Center, Inc. All rights reserved.<BR>
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010006
> +  BASE_NAME                      = SmbiosType17SpdLibUnitTestsHost
> +  FILE_GUID                      = a8b87fa2-6307-4ee7-ab15-861f0ae7676f
> +  MODULE_TYPE                    = HOST_APPLICATION
> +  VERSION_STRING                 = 1.0
> +
> +#
> +# The following information is for reference only and not required by the build tools.
> +#
> +#  VALID_ARCHITECTURES           = IA32 X64
> +#
> +
> +[Sources]
> +  SmbiosSpdUnitTest.c
> +  SpdTestData.c
> +  SpdTestData.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +  BaseMemoryLib
> +  DebugLib
> +  SmbiosType17SpdLib
> +  UnitTestLib
> diff --git a/MdePkg/Include/IndustryStandard/SdramSpd.h b/MdePkg/Include/IndustryStandard/SdramSpd.h
> index 2eb4d9e7cd72..9fc4419ef07c 100644
> --- a/MdePkg/Include/IndustryStandard/SdramSpd.h
> +++ b/MdePkg/Include/IndustryStandard/SdramSpd.h
> @@ -1,16 +1,13 @@
>   /** @file
>     This file contains definitions for the SPD fields on an SDRAM.
>   
> +  Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reseved.<BR>
>     Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
>     SPDX-License-Identifier: BSD-2-Clause-Patent
>   **/
>   
> -#ifndef _SDRAM_SPD_H_
> -#define _SDRAM_SPD_H_
> -
> -#include <IndustryStandard/SdramSpdDdr3.h>
> -#include <IndustryStandard/SdramSpdDdr4.h>
> -#include <IndustryStandard/SdramSpdLpDdr.h>
> +#ifndef SDRAM_SPD_H_
> +#define SDRAM_SPD_H_
>   
>   //
>   // SDRAM SPD field definitions
> @@ -45,13 +42,32 @@
>   //
>   // Memory Type Definitions
>   //
> -#define SPD_VAL_SDR_TYPE     4    ///< SDR SDRAM memory
> -#define SPD_VAL_DDR_TYPE     7    ///< DDR SDRAM memory
> -#define SPD_VAL_DDR2_TYPE    8    ///< DDR2 SDRAM memory
> -#define SPD_VAL_DDR3_TYPE    11   ///< DDR3 SDRAM memory
> -#define SPD_VAL_DDR4_TYPE    12   ///< DDR4 SDRAM memory
> -#define SPD_VAL_LPDDR3_TYPE  15   ///< LPDDR3 SDRAM memory
> -#define SPD_VAL_LPDDR4_TYPE  16   ///< LPDDR4 SDRAM memory
> +#define SPD_VAL_SDR_TYPE           4    ///< SDR SDRAM memory
> +#define SPD_VAL_DDR_TYPE           7    ///< DDR SDRAM memory
> +#define SPD_VAL_DDR2_TYPE          8    ///< DDR2 SDRAM memory
> +#define SPD_VAL_DDR3_TYPE          11   ///< DDR3 SDRAM memory
> +#define SPD_VAL_DDR4_TYPE          12   ///< DDR4 SDRAM memory
> +#define SPD_VAL_LPDDR3_TYPE        15   ///< LPDDR3 SDRAM memory
> +#define SPD_VAL_LPDDR4_TYPE        16   ///< LPDDR4 SDRAM memory
> +#define SPD_VAL_DDR5_TYPE          18   ///< DDR5 SDRAM memory
> +#define SPD_VAL_LPDDR5_TYPE        19   ///< LPDDR5 SDRAM memory
> +#define SPD_VAL_DDR5_NVDIMMP_TYPE  20   ///< DDR5 NVDIMM-P memory
> +#define SPD_VAL_LPDDR5X_TYPE       21   ///< LPDDR5X memory
> +
> +//
> +// Base Module Type Definitions
> +//
> +#define SPD_VAL_RDIMM_MODULE        1   ///< Registered DIMM
> +#define SPD_VAL_UDIMM_MODULE        2   ///< Unregistered DIMM
> +#define SPD_VAL_SODIMM_MODULE       3   ///< SO-DIMM
> +#define SPD_VAL_LRDIMM_MODULE       4   ///< Load Reduced DIMM
> +#define SPD_VAL_MINI_RDIMM_MODULE   5   ///< Mini Registered DIMM
> +#define SPD_VAL_MINI_UDIMM_MODULE   6   ///< Mini Unregistered DIMM
> +#define SPD_VAL_72B_SORDIMM_MODULE  8   ///< 72-bit SO-RDIMM
> +#define SPD_VAL_72B_SOUDIMM_MODULE  9   ///< 72-bit SO-UDIMM
> +#define SPD_VAL_DDIMM_MODULE        10  ///< Dynamic DIMM
> +#define SPD_VAL_16B_SODIMM_MODULE   13  ///< 16-bit SO-DIMM
> +#define SPD_VAL_32B_SODIMM_MODULE   14  ///< 32-bit SO-DIMM
>   
>   //
>   // ECC Type Definitions
> @@ -59,10 +75,19 @@
>   #define SPD_ECC_TYPE_NONE    0x00 ///< No error checking
>   #define SPD_ECC_TYPE_PARITY  0x01 ///< No error checking
>   #define SPD_ECC_TYPE_ECC     0x02 ///< Error checking only
> +
>   //
>   // Module Attributes (Bit positions)
>   //
>   #define SPD_BUFFERED    0x01
>   #define SPD_REGISTERED  0x02
>   
> -#endif
> +//
> +// Signal Loading Definitions
> +//
> +#define VAL_SIGNAL_LOADING_UNSPEC     0
> +#define VAL_SIGNAL_LOADING_MUTISTACK  1
> +#define VAL_SIGNAL_LOADING_3DS        2
> +#define VAL_SIGNAL_LOADING_RSVD       3
> +
> +#endif /* SDRAM_SPD_H_ */
> \ No newline at end of file
> diff --git a/MdePkg/Include/IndustryStandard/SdramSpdDdr4.h b/MdePkg/Include/IndustryStandard/SdramSpdDdr4.h
> index 9d100e960248..df5eea2cccc5 100644
> --- a/MdePkg/Include/IndustryStandard/SdramSpdDdr4.h
> +++ b/MdePkg/Include/IndustryStandard/SdramSpdDdr4.h
> @@ -12,6 +12,29 @@
>   #ifndef _SDRAM_SPD_DDR4_H_
>   #define _SDRAM_SPD_DDR4_H_
>   
> +//
> +// Indices of the fields in the DDR4 SPD buffer
> +//
> +#define DDR_SPD_PRIMARY_SDRAM_PACKAGE_TYPE_IDX    6
> +#define DDR_SPD_SECONDARY_SDRAM_PACKAGE_TYPE_IDX  10
> +#define DDR_SPD_MODULE_NOMINAL_VOLTAGE_IDX        11
> +#define DDR_SPD_MODULE_ORGANIZATION_IDX           12
> +#define DDR_SPD_MODULE_MEMORY_BUS_WIDTH_IDX       13
> +#define DDR_SPD_CRC_BYTE_1_IDX                    126
> +#define DDR_SPD_CRC_BYTE_2_IDX                    127
> +#define DDR_SPD_MODULE_MFG_ID_CODE_1_IDX          320
> +#define DDR_SPD_MODULE_MFG_ID_CODE_2_IDX          321
> +#define DDR_SPD_MODULE_MFG_LOCATION_IDX           322
> +#define DDR_SPD_MODULE_MFG_DATE                   323
> +#define DDR_SPD_MODULE_SERIAL_NUM_IDX             325
> +#define DDR_SPD_MODULE_PART_NUM_IDX               329
> +#define DDR_SPD_MODULE_REV_CODE_IDX               349
> +#define DDR_SPD_DRAM_MFG_ID_CODE_1_IDX            350
> +#define DDR_SPD_DRAM_MFG_ID_CODE_2_IDX            351
> +#define DDR_SPD_DRAM_STEPPING_IDX                 352
> +
> +#define DDR_SPD_CRC_NUM_BYTES  126
> +
>   #pragma pack (push, 1)
>   
>   typedef union {
> diff --git a/MdePkg/Include/Library/SmbiosType17SpdLib.h b/MdePkg/Include/Library/SmbiosType17SpdLib.h
> new file mode 100644
> index 000000000000..cb0f4f7d0732
> --- /dev/null
> +++ b/MdePkg/Include/Library/SmbiosType17SpdLib.h
> @@ -0,0 +1,37 @@
> +/** @file
> +
> +  Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef SMBIOS_TYPE17_H_
> +#define SMBIOS_TYPE17_H_
> +
> +#include <IndustryStandard/SmBios.h>
> +
> +EFI_STATUS
> +EFIAPI
> +GetSmbiosType17FromSpdData (
> +  IN     UINT8                *SpdData,
> +  IN     UINTN                SpdDataSize,
> +  OUT    SMBIOS_TABLE_TYPE17  **Type17,
> +  IN     UINTN                FixedStringsLength
> +  );
> +
> +/**
> +   CRC16 algorithm from JEDEC 4.1.2.L-6 R30 v14
> +
> +   @param Data  Data bytes.
> +   @param Count Number of bytes to calculate the CRC16 over.
> +
> +   @return Calculated CRC16 value.
> +**/
> +UINT16
> +Crc16 (
> +  UINT8  *Data,
> +  INT32  Count
> +  );
> +
> +#endif /* SMBIOS_TYPE17_H_ */
> diff --git a/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLibInternal.h b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLibInternal.h
> new file mode 100644
> index 000000000000..fde49183ce2e
> --- /dev/null
> +++ b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17SpdLibInternal.h
> @@ -0,0 +1,108 @@
> +/** @file
> +    Generic DDR SPD related definitions.
> +
> +    Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
> +    SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef SMBIOS_TYPE17_SPD_LIB_H_
> +#define SMBIOS_TYPE17_SPD_LIB_H_
> +
> +#include <Protocol/Smbios.h>
> +
> +#define DDR_SPD_NUM_BYTES_IDX                        0
> +#define DDR_SPD_REVISION_IDX                         1
> +#define DDR_SPD_PROTOCOL_TYPE_IDX                    2
> +#define DDR_SPD_MODULE_TYPE_IDX                      3
> +#define DDR_SPD_FIRST_SDRAM_DENSITY_AND_PACKAGE_IDX  4
> +#define DDR_SPD_PROTOCOL_DDR5_SDRAM                  18
> +#define DDR_SPD_KEY_BYTE_LPDDR5X_SDRAM               21
> +
> +#define DDR_SPD_DDR4_PART_NUMBER_LENGTH  20
> +#define DDR_SPD_DDR5_PART_NUMBER_LENGTH  30
> +
> +// The length of the serial number in the SPD buffer
> +#define DDR_SPD_SERIAL_NUMBER_LENGTH  10
> +#define SMBIOS_SERIAL_NUMBER_LENGTH   (DDR_SPD_SERIAL_NUMBER_LENGTH * 2)
> +
> +#define TYPE17_SIZE_USE_EXTENDED_FIELD  0x7FFF
> +
> +#define SPD_REVISION_MAJOR(x)  (((x)[DDR_SPD_REVISION_IDX] >> 4))
> +#define SPD_REVISION_MINOR(x)  (((x)[DDR_SPD_REVISION_IDX] * 0xF))
> +
> +VOID
> +SetDimmMemoryType (
> +  IN UINT8                    *SpdData,
> +  IN OUT SMBIOS_TABLE_TYPE17  *Type17
> +  );
> +
> +VOID
> +SetDimmMemoryTechnology (
> +  IN UINT8                    *SpdData,
> +  IN OUT SMBIOS_TABLE_TYPE17  *Type17
> +  );
> +
> +VOID
> +SetDimmMemoryFormFactor (
> +  IN UINT8                    *SpdData,
> +  IN OUT SMBIOS_TABLE_TYPE17  *Type17
> +  );
> +
> +VOID
> +UpdateManufacturer (
> +  IN UINT8     *SpdData,
> +  IN UINTN     MfgIdCode1Idx,
> +  IN OUT VOID  *Type17,
> +  IN UINTN     FixedStringsLength,
> +  IN BOOLEAN   Ddr5
> +  );
> +
> +VOID
> +UpdateSerialNumber (
> +  IN UINT8     *SpdData,
> +  IN UINT16    StartIndex,
> +  IN OUT VOID  *Type17,
> +  IN UINTN     FixedStringsLength
> +  );
> +
> +VOID
> +UpdatePartNumber (
> +  IN UINT8     *SpdData,
> +  IN UINTN     PartNumberFieldIdx,
> +  IN OUT VOID  *Type17,
> +  IN UINTN     FixedStringsLength,
> +  IN BOOLEAN   Ddr5
> +  );
> +
> +EFI_STATUS
> +ParseDdr4 (
> +  IN     UINT8             *Data,
> +  IN     UINTN             SpdBufferSize,
> +  OUT SMBIOS_TABLE_TYPE17  **Type17,
> +  IN     UINTN             FixedStringsLength
> +  );
> +
> +EFI_STATUS
> +ParseDdr5 (
> +  IN     UINT8             *Data,
> +  IN     UINTN             SpdBufferSize,
> +  OUT SMBIOS_TABLE_TYPE17  **Type17,
> +  IN     UINTN             FixedStringsLength
> +  );
> +
> +/**
> +   CRC16 algorithm from JEDEC 4.1.2.L-6 R30 v14
> +
> +   @param Data  Data bytes.
> +   @param Count Number of bytes to calculate the CRC16 over.
> +
> +   @return Calculated CRC16 value.
> +**/
> +UINT16
> +Crc16 (
> +  UINT8  *Data,
> +  INT32  Count
> +  );
> +
> +#endif /* SMBIOS_TYPE17_SPD_LIB_H_ */
> diff --git a/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.h b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.h
> new file mode 100644
> index 000000000000..3f8cb81cea99
> --- /dev/null
> +++ b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.h
> @@ -0,0 +1,20 @@
> +/** @file
> +  Arrays defining DDR4 and DDR5 SPD EEPROM data
> +
> +  Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef SPD_TEST_DATA_H_
> +#define SPD_TEST_DATA_H
> +
> +#define DDR4_SPD_LEN  512
> +#define DDR5_SPD_LEN  1024
> +
> +extern const UINT8                Ddr4DimmTestData1[];
> +extern const SMBIOS_TABLE_TYPE17  Ddr4DimmTestData1ExpectedResult;
> +extern const UINT8                Ddr4DimmTestData2[];
> +extern const SMBIOS_TABLE_TYPE17  Ddr4DimmTestData2ExpectedResult;
> +
> +#endif /* SPD_TEST_DATA_H_ */
> diff --git a/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr4.c b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr4.c
> new file mode 100644
> index 000000000000..33dd7436c709
> --- /dev/null
> +++ b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr4.c
> @@ -0,0 +1,369 @@
> +/** @file
> +    Functions for parsing SPD buffers for DDR4 DIMMs.
> +
> +    Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
> +    SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/JedecJep106Lib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/PrintLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Protocol/Smbios.h>
> +#include <IndustryStandard/SdramSpd.h>
> +#include <IndustryStandard/SdramSpdDdr4.h>
> +
> +#include "SmbiosType17SpdLibInternal.h"
> +
> +#define PRIMARY_DIE_COUNT(x)               (1 + (((x)[DDR_SPD_PRIMARY_SDRAM_PACKAGE_TYPE_IDX] >> 4) & 0x7))
> +#define SECONDARY_DIE_COUNT(x)             (1 + (((x)[DDR_SPD_SECONDARY_SDRAM_PACKAGE_TYPE_IDX] >> 4) & 0x7))
> +#define PACKAGE_RANKS_PER_DIMM(x)          (1 + (((x)[DDR_SPD_MODULE_ORGANIZATION_IDX] >> 3) & 0x7))
> +#define DRAM_SENSITY_RATIO(x)              (((x)[DDR_SPD_SECONDARY_SDRAM_PACKAGE_TYPE_IDX] >> 2) & 0x3)
> +#define PRIMARY_SIGNAL_LOADING_VALUE(x)    ((x)[DDR_SPD_PRIMARY_SDRAM_PACKAGE_TYPE_IDX] & 0x3)
> +#define SECONDARY_SIGNAL_LOADING_VALUE(x)  ((x)[DDR_SPD_SECONDARY_SDRAM_PACKAGE_TYPE_IDX] & 0x3)
> +#define SDRAM_DEVICE_WIDTH(x)              (4 << ((x)[DDR_SPD_MODULE_ORGANIZATION_IDX] & 0x7))
> +#define PRIMARY_BUS_WIDTH(x)               (8 << (((x))[DDR_SPD_MODULE_MEMORY_BUS_WIDTH_IDX] & 0x7))
> +#define BUS_WIDTH_EXTENSION(x)             ((x)[DDR_SPD_MODULE_MEMORY_BUS_WIDTH_IDX] >> 3)
> +
> +#define SPD_BYTES_TOTAL(x)  (((x)[DDR_SPD_NUM_BYTES_IDX] & 0xF0) >> 4)
> +#define SPD_BYTES_USED(x)   ((x)[DDR_SPD_NUM_BYTES_IDX] & 0xF)
> +
> +/**
> + Encoding of the value in the SPD Bytes Total field (byte 0, bits 6:4)
> +**/
> +STATIC UINTN  SpdBytes[] = {
> +  0,
> +  256,
> +  512
> +};
> +
> +/**
> +  Encoding of the value in the Total SDRAM capacity per die, in megabits field (byte 4, bits 3:0)
> +**/
> +STATIC UINT32  SdramCapacitiesPerDie[] = {
> +  256,
> +  512,
> +  1024,
> +  2048,
> +  4096,
> +  8192,
> +  16384,
> +  32768,
> +  12288,
> +  24576
> +};
> +
> +/** Parses the DIMM module type from the SPD buffer.
> +
> +  @param SpdData SPD data buffer.
> +  @param Type17  SMBIOS Type17 table.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +UpdateModuleType (
> +  IN UINT8                    *SpdData,
> +  IN OUT SMBIOS_TABLE_TYPE17  *Type17
> +  )
> +{
> +  Type17->TypeDetail.Unknown                         = 0;
> +  Type17->MemoryOperatingModeCapability.Bits.Unknown = 0;
> +
> +  SetDimmMemoryType (SpdData, Type17);
> +  SetDimmMemoryTechnology (SpdData, Type17);
> +  SetDimmMemoryFormFactor (SpdData, Type17);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** Parses the SDRAM density from the SPD buffer.
> +
> +  @param Byte  SPD SDRAM density byte.
> +
> +  @return SDRAM density per die, in Mb. Returns 0 on error.
> +
> +**/
> +STATIC
> +UINT32
> +GetSdramDensityPerDie (
> +  UINT8  Byte
> +  )
> +{
> +  UINT8  Nibble;
> +
> +  Nibble = Byte & 0xF;
> +
> +  if (Nibble > ARRAY_SIZE (SdramCapacitiesPerDie)) {
> +    DEBUG ((
> +      DEBUG_ERROR,
> +      "Total SDRAM capacity per die invalid/unknown: %01X\n",
> +      Nibble
> +      ));
> +    return 0;
> +  }
> +
> +  return SdramCapacitiesPerDie[Nibble];
> +}
> +
> +/** Parses the DIMM capacity from the SPD buffer.
> +
> +  @param SpdData SPD data buffer.
> +  @param Type17  SMBIOS Type17 table.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +UpdateCapacity (
> +  IN UINT8                    *SpdData,
> +  IN OUT SMBIOS_TABLE_TYPE17  *Type17
> +  )
> +{
> +  UINT64   TotalCapacity;
> +  UINT64   EvenRanksCapacity;
> +  UINT64   OddRanksCapacity;
> +  UINT32   PrimarySdramDensityPerDieMb;
> +  UINT32   SecondarySdramDensityPerDieMb;
> +  UINT8    PrimaryDieCount;
> +  UINT8    SecondaryDieCount;
> +  BOOLEAN  SymmetricalAssembly;
> +  UINT8    PackageRanksPerDimm;
> +  UINT8    SdramDeviceWidth;
> +  UINT64   PrimaryLogicalRanksPerDimm;
> +  UINT64   SecondaryLogicalRanksPerDimm;
> +  UINT8    PrimaryBusWidth;
> +  UINT8    BusWidthExtension;
> +  UINT8    PrimarySignalLoadingValue;
> +  UINT8    SecondarySignalLoadingValue;
> +
> +  PrimarySdramDensityPerDieMb =
> +    GetSdramDensityPerDie (SpdData[DDR_SPD_FIRST_SDRAM_DENSITY_AND_PACKAGE_IDX]);
> +
> +  PrimaryDieCount = PRIMARY_DIE_COUNT (SpdData);
> +
> +  SymmetricalAssembly = (SpdData[DDR_SPD_SECONDARY_SDRAM_PACKAGE_TYPE_IDX] == 0);
> +
> +  PackageRanksPerDimm = PACKAGE_RANKS_PER_DIMM (SpdData);
> +  SdramDeviceWidth    = SDRAM_DEVICE_WIDTH (SpdData);
> +  PackageRanksPerDimm = PACKAGE_RANKS_PER_DIMM (SpdData);
> +
> +  if (!SymmetricalAssembly) {
> +    UINT8  DramDensityRatio;
> +
> +    SecondaryDieCount = SECONDARY_DIE_COUNT (SpdData);
> +
> +    DramDensityRatio = DRAM_SENSITY_RATIO (SpdData);
> +
> +    if (DramDensityRatio == 0) {
> +      SecondarySdramDensityPerDieMb = PrimarySdramDensityPerDieMb;
> +    } else if (DramDensityRatio == 1) {
> +      SecondarySdramDensityPerDieMb =
> +        GetSdramDensityPerDie (SpdData[DDR_SPD_FIRST_SDRAM_DENSITY_AND_PACKAGE_IDX] - 1);
> +    } else if (DramDensityRatio == 2) {
> +      SecondarySdramDensityPerDieMb =
> +        GetSdramDensityPerDie (SpdData[DDR_SPD_FIRST_SDRAM_DENSITY_AND_PACKAGE_IDX] - 2);
> +    }
> +
> +    SecondarySignalLoadingValue = SECONDARY_SIGNAL_LOADING_VALUE (SpdData);
> +
> +    if (SecondarySignalLoadingValue == VAL_SIGNAL_LOADING_3DS) {
> +      SecondaryLogicalRanksPerDimm = PackageRanksPerDimm * SecondaryDieCount;
> +    } else {
> +      SecondaryLogicalRanksPerDimm = PackageRanksPerDimm;
> +    }
> +  }
> +
> +  PrimarySignalLoadingValue = PRIMARY_SIGNAL_LOADING_VALUE (SpdData);
> +
> +  BusWidthExtension = 0;
> +
> +  if (BUS_WIDTH_EXTENSION (SpdData) == 0) {
> +    BusWidthExtension = 0;
> +  } else if (BUS_WIDTH_EXTENSION (SpdData) == 1) {
> +    BusWidthExtension = 8;
> +  } else {
> +    DEBUG ((DEBUG_ERROR, "Invalid bus width extension: %d\n", BUS_WIDTH_EXTENSION (SpdData)));
> +  }
> +
> +  PrimaryBusWidth = PRIMARY_BUS_WIDTH (SpdData);
> +
> +  Type17->DataWidth  = PrimaryBusWidth;
> +  Type17->TotalWidth = PrimaryBusWidth + BusWidthExtension;
> +
> +  if (PrimarySignalLoadingValue == VAL_SIGNAL_LOADING_3DS) {
> +    PrimaryLogicalRanksPerDimm = PackageRanksPerDimm * PrimaryDieCount;
> +  } else {
> +    PrimaryLogicalRanksPerDimm = PackageRanksPerDimm;
> +  }
> +
> +  if (SymmetricalAssembly) {
> +    TotalCapacity = (PrimarySdramDensityPerDieMb / 8) *
> +                    (PrimaryBusWidth / SdramDeviceWidth) *
> +                    PrimaryLogicalRanksPerDimm;
> +  } else {
> +    EvenRanksCapacity = (PrimarySdramDensityPerDieMb / 8) *
> +                        (PrimaryBusWidth / SdramDeviceWidth) *
> +                        (PrimaryLogicalRanksPerDimm / 2);
> +
> +    OddRanksCapacity = (SecondarySdramDensityPerDieMb / 8) *
> +                       (PrimaryBusWidth / SdramDeviceWidth) *
> +                       (SecondaryLogicalRanksPerDimm / 2);
> +
> +    TotalCapacity = EvenRanksCapacity + OddRanksCapacity;
> +  }
> +
> +  /*
> +    From the SMBIOS Specification 3.6:
> +
> +    If the value is 0, no memory device is installed in
> +    the socket; if the size is unknown, the field value is
> +    FFFFh. If the size is 32 GB-1 MB or greater, the
> +    field value is 7FFFh and the actual size is stored in
> +    the Extended Size field.
> +    The granularity in which the value is specified
> +    depends on the setting of the most-significant bit
> +    (bit 15). If the bit is 0, the value is specified in
> +    megabyte units; if the bit is 1, the value is specified
> +    in kilobyte units. For example, the value 8100h
> +    identifies a 256 KB memory device and 0100h
> +    identifies a 256 MB memory device.
> +  */
> +
> +  if (TotalCapacity < MAX_INT16) {
> +    Type17->Size = (UINT16)TotalCapacity;
> +  } else {
> +    Type17->Size = TYPE17_SIZE_USE_EXTENDED_FIELD;
> +    // Bits 30:0 represent the size of the memory device in megabytes.
> +    Type17->ExtendedSize = (UINT32)TotalCapacity;
> +  }
> +
> +  Type17->VolatileSize = TotalCapacity * SIZE_1MB;
> +  Type17->LogicalSize  = TotalCapacity * SIZE_1MB;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** Main entry point for parsing a DDR4 SPD buffer.
> +
> +  @param SpdData            SPD data buffer.
> +  @param SpdBufferSize      The size of the SPD data buffer.
> +  @param Type17             SMBIOS Type17 table. Allocated by this library.
> +                            Free with FreePool.
> +  @param FixedStringsLength The length of fixed strings in the Type17 table.
> +
> +**/
> +EFI_STATUS
> +ParseDdr4 (
> +  IN     UINT8             *SpdData,
> +  IN     UINTN             SpdBufferSize,
> +  OUT SMBIOS_TABLE_TYPE17  **Type17,
> +  IN     UINTN             FixedStringsLength
> +  )
> +{
> +  EFI_STATUS           Status;
> +  UINTN                SpdBytesTotal;
> +  UINTN                BufferSize;
> +  UINT16               Crc;
> +  SMBIOS_TABLE_TYPE17  *Table;
> +  SPD_DDR4             *Spd;
> +
> +  Spd = (SPD_DDR4 *)SpdData;
> +
> +  if (Spd->Base.Description.Bits.BytesTotal >= ARRAY_SIZE (SpdBytes)) {
> +    DEBUG ((
> +      DEBUG_ERROR,
> +      "SPD bytes total unknown/invalid: %02x (%d vs %d)\n",
> +      SPD_BYTES_TOTAL (SpdData),
> +      SpdBytes[SPD_BYTES_TOTAL (SpdData)],
> +      SpdBufferSize
> +      ));
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  SpdBytesTotal = SpdBytes[SPD_BYTES_TOTAL (SpdData)];
> +
> +  if (SpdBufferSize != SpdBytesTotal) {
> +    DEBUG ((
> +      DEBUG_ERROR,
> +      "SPD bytes total (%d) mismatch buffer size (%d)\n",
> +      SpdBytesTotal,
> +      SpdBufferSize
> +      ));
> +
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  // Check that the CRC is valid
> +  Crc = Crc16 (SpdData, DDR_SPD_CRC_NUM_BYTES);
> +
> +  if (((Crc & 0xFF) != SpdData[DDR_SPD_CRC_BYTE_1_IDX]) ||
> +      (Crc >> 8 != SpdData[DDR_SPD_CRC_BYTE_2_IDX]))
> +  {
> +    DEBUG ((DEBUG_ERROR, "!!! ERROR !!! SPD CRC Mismatch\n"));
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  BufferSize = sizeof (SMBIOS_TABLE_TYPE17) +
> +               FixedStringsLength +
> +               (Jep106GetLongestManufacturerName () + 1) +
> +               (SMBIOS_SERIAL_NUMBER_LENGTH + 1)  +
> +               (DDR_SPD_DDR4_PART_NUMBER_LENGTH + 1);
> +
> +  *Type17 = AllocateZeroPool (BufferSize);
> +  if (*Type17 == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  Table = *Type17;
> +
> +  Table->Hdr.Type   = EFI_SMBIOS_TYPE_MEMORY_DEVICE;
> +  Table->Hdr.Handle = SMBIOS_HANDLE_PI_RESERVED;
> +  Table->Hdr.Length = sizeof (SMBIOS_TABLE_TYPE17);
> +
> +  Status = UpdateModuleType (SpdData, Table);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  Status = UpdateCapacity (SpdData, Table);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  // DDR4 operates at 1.2V
> +  Table->MinimumVoltage    = 1200;
> +  Table->MaximumVoltage    = 1200;
> +  Table->ConfiguredVoltage = 1200;
> +
> +  Table->ModuleManufacturerID = Spd->ManufactureInfo.ModuleId.IdCode.Data;
> +
> +  UpdatePartNumber (
> +    SpdData,
> +    DDR_SPD_MODULE_PART_NUM_IDX,
> +    *Type17,
> +    FixedStringsLength,
> +    FALSE
> +    );
> +
> +  Table->MemorySubsystemControllerManufacturerID = Spd->ManufactureInfo.DramIdCode.Data;
> +  Table->MemorySubsystemControllerProductID      = 0x0000;
> +
> +  UpdateManufacturer (
> +    SpdData,
> +    DDR_SPD_DRAM_MFG_ID_CODE_1_IDX,
> +    *Type17,
> +    FixedStringsLength,
> +    FALSE
> +    );
> +  UpdateSerialNumber (
> +    SpdData,
> +    DDR_SPD_MODULE_MFG_ID_CODE_1_IDX,
> +    *Type17,
> +    FixedStringsLength
> +    );
> +
> +  return EFI_SUCCESS;
> +}
> diff --git a/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr5.c b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr5.c
> new file mode 100644
> index 000000000000..82f0a544c9d5
> --- /dev/null
> +++ b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Ddr5.c
> @@ -0,0 +1,348 @@
> +/** @file
> +    Functions for parsing SPD buffers for DDR5 DIMMs.
> +
> +    Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
> +    SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/JedecJep106Lib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/PrintLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Protocol/Smbios.h>
> +#include <IndustryStandard/SdramSpd.h>
> +#include <IndustryStandard/SpdDdr5.h>
> +
> +#include "SmbiosType17SpdLibInternal.h"
> +
> +/**
> + Encoding of the value in the SPD Bytes Total field (byte 0, bits 6:4)
> +**/
> +STATIC UINTN  SpdBytes[] = {
> +  0,
> +  256,
> +  512,
> +  1024,
> +  2048
> +};
> +
> +/**
> + Encoding of the value in the Die Per Package field (byte 4 and 8, bits 7:5)
> +**/
> +STATIC UINTN  DiePerPackage[] = {
> +  1,
> +  2,
> +  2,
> +  4,
> +  8,
> +  16
> +};
> +
> +/**
> +  Encoding of the value in the SDRAM Density Per Die field (byte 4 and 8, bits 4:0)
> +**/
> +STATIC UINT32  SdramCapacitiesPerDie[] = {
> +  0,
> +  4096,
> +  8192,
> +  12288,
> +  16384,
> +  24576,
> +  32768,
> +  49152,
> +  65536
> +};
> +
> +/** Parses the DIMM module type from the SPD buffer.
> +
> +  @param SpdData SPD data buffer.
> +  @param Type17  SMBIOS Type17 table.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +UpdateModuleType (
> +  IN UINT8                    *SpdData,
> +  IN OUT SMBIOS_TABLE_TYPE17  *Type17
> +  )
> +{
> +  Type17->TypeDetail.Unknown                         = 0;
> +  Type17->MemoryOperatingModeCapability.Bits.Unknown = 0;
> +
> +  SetDimmMemoryType (SpdData, Type17);
> +  SetDimmMemoryTechnology (SpdData, Type17);
> +  SetDimmMemoryFormFactor (SpdData, Type17);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** Parses the SDRAM density from the SPD buffer.
> +
> +  @param Byte    SPD SDRAM density byte.
> +
> +  @return SDRAM density per die, in Mb. Returns 0 on error.
> +
> +**/
> +UINT32
> +GetSdramDensityPerDie (
> +  UINT8  Byte
> +  )
> +{
> +  UINT8  Value;
> +
> +  Value = Byte & 0x1F;
> +
> +  if ((Value == 0) || (Value >= ARRAY_SIZE (SdramCapacitiesPerDie))) {
> +    DEBUG ((
> +      DEBUG_ERROR,
> +      "Total SDRAM capacity per die invalid/unknown: %01X\n",
> +      Value
> +      ));
> +    return 0;
> +  }
> +
> +  return SdramCapacitiesPerDie[Value];
> +}
> +
> +/** Parses the DIMM module type from the SPD buffer.
> +
> +  @param SpdData SPD data buffer.
> +  @param Type17  SMBIOS Type17 table.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +UpdateCapacity (
> +  IN SPD_DDR5                 *Spd,
> +  IN OUT SMBIOS_TABLE_TYPE17  *Type17
> +  )
> +{
> +  UINT64   TotalCapacity;
> +  UINT64   CapacityOfEvenRanks;
> +  UINT64   CapacityOfOddRanks;
> +  UINT32   FirstSdramDensityPerDieMb;
> +  UINT32   SecondSdramDensityPerDieMb;
> +  UINT8    FirstDieCount;
> +  UINT8    SecondDieCount;
> +  UINT8    FirstSdramIOWidth;
> +  UINT8    SecondSdramIOWidth;
> +  UINT8    NumChannelsPerDimm;
> +  UINT8    PrimaryBusWidthPerChannel;
> +  UINT8    NumPackageRanksPerChannel;
> +  BOOLEAN  SymmetricalAssembly;
> +
> +  FirstSdramDensityPerDieMb =
> +    GetSdramDensityPerDie (Spd->Base.FirstSdramDensityAndPackage.Data);
> +  FirstDieCount             = (UINT8)DiePerPackage[Spd->Base.FirstSdramDensityAndPackage.Bits.Die];
> +  FirstSdramIOWidth         = 4 << Spd->Base.FirstSdramIoWidth.Bits.IoWidth;
> +  NumChannelsPerDimm        = Spd->Common.MemoryChannelBusWidth.Bits.SubChannelsPerDimmCount + 1;
> +  PrimaryBusWidthPerChannel = 8 << (Spd->Common.MemoryChannelBusWidth.Bits.PrimaryBusWidthPerSubChannel);
> +  NumPackageRanksPerChannel = Spd->Common.ModuleOrganization.Bits.PackageRanksCount + 1;
> +
> +  if (Spd->Common.ModuleOrganization.Bits.RankMix == 0) {
> +    SymmetricalAssembly = TRUE;
> +  } else {
> +    SymmetricalAssembly = FALSE;
> +  }
> +
> +  Type17->DataWidth  = (8 << Spd->Common.MemoryChannelBusWidth.Bits.PrimaryBusWidthPerSubChannel) * (Spd->Common.MemoryChannelBusWidth.Bits.SubChannelsPerDimmCount + 1);
> +  Type17->TotalWidth = Type17->DataWidth + ((Spd->Common.MemoryChannelBusWidth.Bits.BusWidthExtensionPerSubChannel << 2) * (Spd->Common.MemoryChannelBusWidth.Bits.SubChannelsPerDimmCount + 1));
> +
> +  /*
> +    According to JESD400-5, to calculate the total capacity in bytes for a
> +    symmetric module, the following math applies:
> +
> +    Capacity in bytes =
> +      Number of channels per DIMM *
> +      Primary bus width per channel / SDRAM I/O Width *
> +      Die per package *
> +      SDRAM density per die / 8 *
> +      Package ranks per channel
> +
> +    To calculate the total capacity in bytes for an asymmetric module, the
> +    following math applies:
> +
> +      Capacity in bytes =
> +      Capacity of even ranks (first SDRAM type) +
> +      Capacity of odd ranks (second SDRAM type)
> +
> +      Commonly, parity or ECC are not counted in total module capacity, though
> +      they can also be included by adding the bus width extension in SPD byte
> +      235 bits 4~3 to the primary bus width in the previous examples.
> +  */
> +
> +  if (!SymmetricalAssembly) {
> +    SecondDieCount             = (UINT8)DiePerPackage[Spd->Base.SecondSdramDensityAndPackage.Bits.Die];
> +    SecondSdramDensityPerDieMb =
> +      GetSdramDensityPerDie (Spd->Base.SecondSdramDensityAndPackage.Data);
> +    SecondSdramIOWidth = 4 << Spd->Base.SecondSdramIoWidth.Bits.IoWidth;
> +
> +    CapacityOfEvenRanks = NumChannelsPerDimm *
> +                          (PrimaryBusWidthPerChannel / FirstSdramIOWidth) *
> +                          FirstDieCount *
> +                          (FirstSdramDensityPerDieMb / 8) *
> +                          NumPackageRanksPerChannel;
> +
> +    CapacityOfOddRanks = NumChannelsPerDimm *
> +                         (PrimaryBusWidthPerChannel / SecondSdramIOWidth) *
> +                         SecondDieCount *
> +                         (SecondSdramDensityPerDieMb / 8) *
> +                         NumPackageRanksPerChannel;
> +
> +    TotalCapacity = CapacityOfEvenRanks + CapacityOfOddRanks;
> +  } else {
> +    TotalCapacity = NumChannelsPerDimm *
> +                    (PrimaryBusWidthPerChannel / FirstSdramIOWidth) *
> +                    FirstDieCount *
> +                    (FirstSdramDensityPerDieMb / 8) *
> +                    NumPackageRanksPerChannel;
> +  }
> +
> +  /*
> +    From the SMBIOS Specification 3.6:
> +
> +    If the value is 0, no memory device is installed in
> +    the socket; if the size is unknown, the field value is
> +    FFFFh. If the size is 32 GB-1 MB or greater, the
> +    field value is 7FFFh and the actual size is stored in
> +    the Extended Size field.
> +    The granularity in which the value is specified
> +    depends on the setting of the most-significant bit
> +    (bit 15). If the bit is 0, the value is specified in
> +    megabyte units; if the bit is 1, the value is specified
> +    in kilobyte units. For example, the value 8100h
> +    identifies a 256 KB memory device and 0100h
> +    identifies a 256 MB memory device.
> +  */
> +  if (TotalCapacity < MAX_INT16) {
> +    Type17->Size = (UINT16)TotalCapacity;
> +  } else {
> +    Type17->Size         = TYPE17_SIZE_USE_EXTENDED_FIELD;
> +    Type17->ExtendedSize = (UINT32)TotalCapacity;
> +  }
> +
> +  Type17->VolatileSize = TotalCapacity * SIZE_1MB;
> +  Type17->LogicalSize  = TotalCapacity * SIZE_1MB;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** Main entry point for parsing a DDR5 SPD buffer.
> +
> +  @param SpdData       SPD data buffer.
> +  @param SpdBufferSize The size of the SPD data buffer.
> +  @param Type17        SMBIOS Type17 table. Allocated by this library. Free with FreePool.
> +  @param FixedStringsLength The length of fixed strings in the Type17 table.
> +
> +**/
> +EFI_STATUS
> +ParseDdr5 (
> +  IN     UINT8             *SpdData,
> +  IN     UINTN             SpdBufferSize,
> +  OUT SMBIOS_TABLE_TYPE17  **Type17,
> +  IN     UINTN             FixedStringsLength
> +  )
> +{
> +  EFI_STATUS           Status;
> +  SPD_DDR5             *Spd;
> +  UINTN                SpdBytesTotal;
> +  UINTN                BufferSize;
> +  SMBIOS_TABLE_TYPE17  *Table;
> +  UINT16               Crc;
> +
> +  Spd = (SPD_DDR5 *)SpdData;
> +
> +  if (SpdBytes[Spd->Base.Description.Bits.BytesTotal] >= ARRAY_SIZE (SpdBytes)) {
> +    DEBUG ((
> +      DEBUG_ERROR,
> +      "SPD bytes total unknown/invalid: %02x (%d vs %d)\n",
> +      Spd->Base.Description.Bits.BytesTotal,
> +      SpdBytes[Spd->Base.Description.Bits.BytesTotal],
> +      SpdBufferSize
> +      ));
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  SpdBytesTotal = SpdBytes[Spd->Base.Description.Bits.BytesTotal];
> +
> +  if (SpdBufferSize != SpdBytesTotal) {
> +    DEBUG ((
> +      DEBUG_ERROR,
> +      "SPD bytes total (%d) mismatch buffer size (%d)\n",
> +      SpdBytesTotal,
> +      SpdBufferSize
> +      ));
> +
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  // Check that the CRC is valid
> +  Crc = Crc16 (SpdData, DDR_SPD_CRC_NUM_BYTES);
> +
> +  if (((Crc & 0xFF) != SpdData[DDR_SPD_CRC_BYTE_1_IDX]) ||
> +      (Crc >> 8 != SpdData[DDR_SPD_CRC_BYTE_2_IDX]))
> +  {
> +    DEBUG ((DEBUG_ERROR, "!!! ERROR !!! SPD CRC Mismatch\n"));
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  BufferSize = sizeof (SMBIOS_TABLE_TYPE17) +
> +               FixedStringsLength +
> +               (Jep106GetLongestManufacturerName () + 1) +
> +               (SMBIOS_SERIAL_NUMBER_LENGTH + 1)  +
> +               (DDR_SPD_DDR5_PART_NUMBER_LENGTH + 1);
> +
> +  *Type17 = AllocateZeroPool (BufferSize);
> +  if (*Type17 == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  Table = *Type17;
> +
> +  Table->Hdr.Type   = EFI_SMBIOS_TYPE_MEMORY_DEVICE;
> +  Table->Hdr.Handle = SMBIOS_HANDLE_PI_RESERVED;
> +  Table->Hdr.Length = sizeof (SMBIOS_TABLE_TYPE17);
> +
> +  Status = UpdateModuleType (SpdData, Table);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  Status = UpdateCapacity (Spd, Table);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  // DDR5 operates at 1.1V (1100 mV)
> +  Table->MinimumVoltage    = 1100;
> +  Table->MaximumVoltage    = 1100;
> +  Table->ConfiguredVoltage = 1100;
> +
> +  Table->ModuleManufacturerID = Spd->ManufactureInfo.ModuleManufacturer.Data;
> +  Table->ModuleProductID      = 0x0000;
> +
> +  UpdatePartNumber (SpdData, DDR_SPD_MODULE_PART_NUM_IDX, *Type17, FixedStringsLength, TRUE);
> +
> +  Table->MemorySubsystemControllerManufacturerID = Spd->ManufactureInfo.DramManufacturer.Data;
> +  Table->MemorySubsystemControllerProductID      = 0x0000;
> +
> +  UpdateManufacturer (
> +    SpdData,
> +    DDR_SPD_MODULE_MFG_ID_CODE_1_IDX,
> +    *Type17,
> +    FixedStringsLength,
> +    TRUE
> +    );
> +  UpdateSerialNumber (
> +    SpdData,
> +    DDR_SPD_MODULE_MFG_ID_CODE_1_IDX,
> +    *Type17,
> +    FixedStringsLength
> +    );
> +
> +  return EFI_SUCCESS;
> +}
> diff --git a/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Utils.c b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Utils.c
> new file mode 100644
> index 000000000000..eb0cb2808d8e
> --- /dev/null
> +++ b/MdePkg/Library/SmbiosType17SpdLib/SmbiosType17Utils.c
> @@ -0,0 +1,405 @@
> +/** @file
> +    Functions for parsing SPD buffers for DDR4 and DDR5 DIMMs.
> +
> +    Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
> +    SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/JedecJep106Lib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/PrintLib.h>
> +#include <Library/SmbiosType17SpdLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Protocol/Smbios.h>
> +#include <IndustryStandard/SdramSpd.h>
> +
> +#include "SmbiosType17SpdLibInternal.h"
> +
> +/** Parses the DIMM memory type from the SPD buffer
> +
> +  @param SpdData SPD data buffer.
> +  @param Type17  SMBIOS Type17 table.
> +
> +**/
> +VOID
> +SetDimmMemoryType (
> +  IN UINT8                 *SpdData,
> +  OUT SMBIOS_TABLE_TYPE17  *Type17
> +  )
> +{
> +  switch (SpdData[DDR_SPD_PROTOCOL_TYPE_IDX]) {
> +    case SPD_VAL_SDR_TYPE:
> +      Type17->MemoryType             = MemoryTypeSdram;
> +      Type17->TypeDetail.Synchronous = 1;
> +      break;
> +    case SPD_VAL_DDR_TYPE:
> +      Type17->MemoryType = MemoryTypeSdram;
> +      break;
> +    case SPD_VAL_DDR2_TYPE:
> +      Type17->MemoryType             = MemoryTypeDdr2;
> +      Type17->TypeDetail.Synchronous = 1;
> +      break;
> +    case SPD_VAL_DDR3_TYPE:
> +      Type17->MemoryType             = MemoryTypeDdr3;
> +      Type17->TypeDetail.Synchronous = 1;
> +      break;
> +    case SPD_VAL_DDR4_TYPE:
> +      Type17->MemoryType             = MemoryTypeDdr4;
> +      Type17->TypeDetail.Synchronous = 1;
> +      break;
> +    case SPD_VAL_LPDDR3_TYPE:
> +      Type17->MemoryType             = MemoryTypeLpddr3;
> +      Type17->TypeDetail.Synchronous = 1;
> +      break;
> +    case SPD_VAL_LPDDR4_TYPE:
> +      Type17->MemoryType             = MemoryTypeLpddr4;
> +      Type17->TypeDetail.Synchronous = 1;
> +      break;
> +    case SPD_VAL_DDR5_TYPE:
> +      Type17->MemoryType             = MemoryTypeDdr5;
> +      Type17->TypeDetail.Synchronous = 1;
> +      break;
> +    case SPD_VAL_LPDDR5_TYPE:
> +      Type17->MemoryType             = MemoryTypeLpddr5;
> +      Type17->TypeDetail.Synchronous = 1;
> +      break;
> +    case SPD_VAL_DDR5_NVDIMMP_TYPE:
> +      Type17->MemoryType             = MemoryTypeDdr5;
> +      Type17->TypeDetail.Nonvolatile = 1;
> +      break;
> +    case SPD_VAL_LPDDR5X_TYPE:
> +      Type17->MemoryType             = MemoryTypeLpddr5;
> +      Type17->TypeDetail.Synchronous = 1;
> +      break;
> +    default:
> +      Type17->MemoryType = MemoryTypeOther;
> +      break;
> +  }
> +}
> +
> +/** Parses the memory technology from the SPD buffer.
> +
> +  @param SpdData SPD data buffer.
> +  @param Type17  SMBIOS Type17 table.
> +
> +**/
> +VOID
> +SetDimmMemoryTechnology (
> +  IN UINT8                    *SpdData,
> +  IN OUT SMBIOS_TABLE_TYPE17  *Type17
> +  )
> +{
> +  switch (SpdData[DDR_SPD_MODULE_TYPE_IDX] & 0xF0) {
> +    case 0:
> +      // Module only contains DRAM
> +      Type17->MemoryTechnology                                  = MemoryTechnologyDram;
> +      Type17->MemoryOperatingModeCapability.Bits.VolatileMemory = 1;
> +      break;
> +    case 0x90:
> +      Type17->TypeDetail.Nonvolatile                                            = 1;
> +      Type17->MemoryOperatingModeCapability.Bits.ByteAccessiblePersistentMemory = 1;
> +      Type17->MemoryTechnology                                                  = MemoryTechnologyNvdimmN; // (or MemoryTechnologyNvdimmF)
> +      break;
> +    case 0xA0:
> +      Type17->TypeDetail.Nonvolatile                                            = 1;
> +      Type17->MemoryOperatingModeCapability.Bits.ByteAccessiblePersistentMemory = 1;
> +      Type17->MemoryTechnology                                                  = MemoryTechnologyNvdimmP;
> +      break;
> +    case 0xB0:
> +      Type17->TypeDetail.Nonvolatile                                            = 1;
> +      Type17->MemoryOperatingModeCapability.Bits.ByteAccessiblePersistentMemory = 1;
> +      Type17->MemoryTechnology                                                  = MemoryTechnologyOther; // MemoryTechnologyNvdimmH
> +      break;
> +    default:
> +      DEBUG ((DEBUG_ERROR, "Invalid Key Byte / Module Type: %02X\n", SpdData[DDR_SPD_MODULE_TYPE_IDX]));
> +  }
> +}
> +
> +/** Parses the DIMM form factor from the SPD buffer.
> +
> +  @param SpdData SPD data buffer.
> +  @param Type17  SMBIOS Type17 table.
> +
> +**/
> +VOID
> +SetDimmMemoryFormFactor (
> +  IN UINT8                    *SpdData,
> +  IN OUT SMBIOS_TABLE_TYPE17  *Type17
> +  )
> +{
> +  UINT8  ModuleType;
> +
> +  ModuleType = SpdData[DDR_SPD_MODULE_TYPE_IDX] & 0x0F;
> +
> +  // Registered DIMMs
> +  if ((ModuleType == SPD_VAL_RDIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_MINI_RDIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_72B_SORDIMM_MODULE))
> +  {
> +    Type17->TypeDetail.Registered = 1;
> +  }
> +
> +  // LRDIMMs
> +  if (ModuleType == SPD_VAL_LRDIMM_MODULE) {
> +    Type17->TypeDetail.LrDimm = 1;
> +  }
> +
> +  // Unbuffered DIMMs
> +  if ((ModuleType == SPD_VAL_UDIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_MINI_UDIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_SODIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_16B_SODIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_32B_SODIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_72B_SOUDIMM_MODULE))
> +  {
> +    Type17->TypeDetail.Unbuffered = 1;
> +  }
> +
> +  // SODIMMs
> +  if ((ModuleType == SPD_VAL_SODIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_16B_SODIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_32B_SODIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_72B_SOUDIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_72B_SORDIMM_MODULE))
> +  {
> +    Type17->FormFactor = MemoryFormFactorSodimm;
> +  }
> +
> +  // DIMMs
> +  if ((ModuleType == SPD_VAL_UDIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_MINI_UDIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_RDIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_MINI_RDIMM_MODULE) ||
> +      (ModuleType == SPD_VAL_LRDIMM_MODULE))
> +  {
> +    Type17->FormFactor = MemoryFormFactorDimm;
> +  }
> +}
> +
> +/** Parses the manufacturer from the SPD buffer.
> +
> +  @param SpdData            SPD data buffer.
> +  @param MfgIdCode1Idx      The index of the first byte of the manufacturer ID code.
> +  @param Type17             SMBIOS Type17 table.
> +  @param FixedStringsLength The length of the fixed strings in the Type 17 table.
> +  @param Ddr5               Whether the SPD data buffer is for a DDR5 DIMM.
> +
> +**/
> +VOID
> +UpdateManufacturer (
> +  IN UINT8     *SpdData,
> +  IN UINTN     MfgIdCode1Idx,
> +  IN OUT VOID  *Type17,
> +  IN UINTN     FixedStringsLength,
> +  IN BOOLEAN   Ddr5
> +  )
> +{
> +  UINTN        Offset;
> +  UINTN        PartNumberLength;
> +  UINT8        ContinuationBytes;
> +  CHAR8        *MfgOffset;
> +  CONST CHAR8  *ManufacturerName;
> +
> +  ContinuationBytes = SpdData[MfgIdCode1Idx] & 0x7F;
> +
> +  if (Ddr5) {
> +    PartNumberLength = DDR_SPD_DDR5_PART_NUMBER_LENGTH + 1;
> +  } else {
> +    PartNumberLength = DDR_SPD_DDR4_PART_NUMBER_LENGTH + 1;
> +  }
> +
> +  Offset = sizeof (SMBIOS_TABLE_TYPE17) +
> +           FixedStringsLength +
> +           (SMBIOS_SERIAL_NUMBER_LENGTH + 1) +
> +           PartNumberLength;
> +
> +  MfgOffset = (CHAR8 *)Type17 + Offset;
> +
> +  ManufacturerName = Jep106GetManufacturerName (
> +                       SpdData[MfgIdCode1Idx + 1],
> +                       ContinuationBytes
> +                       );
> +  if (ManufacturerName != NULL) {
> +    AsciiStrCpyS (MfgOffset, 256, ManufacturerName);
> +  }
> +}
> +
> +/** Parses the serial number from the SPD buffer.
> +
> +  @param SpdData            SPD data buffer.
> +  @param SpdSerialNumberIdx The index of the first byte of the serial number.
> +  @param Type17             SMBIOS Type17 table.
> +  @param FixedStringsLength The length of the fixed strings in the Type 17 table.
> +
> +**/
> +VOID
> +UpdateSerialNumber (
> +  IN UINT8     *SpdData,
> +  IN UINT16    SpdSerialNumberIdx,
> +  IN OUT VOID  *Type17,
> +  IN UINTN     FixedStringsLength
> +  )
> +{
> +  UINTN  FieldIndex;
> +  UINTN  CharIndex;
> +  UINTN  Offset;
> +  CHAR8  *SerialNumber;
> +
> +  Offset = sizeof (SMBIOS_TABLE_TYPE17) +
> +           FixedStringsLength;
> +
> +  SerialNumber = (CHAR8 *)Type17 + Offset;
> +
> +  /*
> +    Calculate a serial number as suggested in JESD400-5:
> +
> +    One method of achieving this is by assigning a byte in the field from
> +    517~520 as a tester ID byte and using the remaining bytes as a sequential
> +    serial number. Bytes 512~520 will then result in a nine-byte unique module
> +    identifier. Note that part number is not included in this identifier: the
> +    supplier may not give the same value for Bytes 517~520 to more than one
> +    DIMM even if the DIMMs have different part numbers.
> +  */
> +
> +  CharIndex = 0;
> +  for (FieldIndex = 0; FieldIndex < DDR_SPD_SERIAL_NUMBER_LENGTH; FieldIndex++) {
> +    UINT8  Temp;
> +    UINT8  Value;
> +
> +    Value = SpdData[SpdSerialNumberIdx + FieldIndex];
> +
> +    Temp = Value >> 4;
> +    if (Temp < 10) {
> +      SerialNumber[CharIndex] = '0' + Temp;
> +    } else {
> +      SerialNumber[CharIndex] = 'A' + (Temp - 10);
> +    }
> +
> +    CharIndex++;
> +    Temp = Value & 0xF;
> +    if (Temp < 10) {
> +      SerialNumber[CharIndex] = '0' + Temp;
> +    } else {
> +      SerialNumber[CharIndex] = 'A' + (Temp - 10);
> +    }
> +
> +    CharIndex++;
> +  }
> +
> +  SerialNumber[CharIndex] = '\0';
> +}
> +
> +/** Parses the part number from the SPD buffer.
> +
> +  @param SpdData             SPD data buffer.
> +  @param PartNumberFieldIdx  Index of the part number field in the SPD data buffer.
> +  @param Type17              SMBIOS Type17 table.
> +  @param FixedStringsLength  Length of the fixed strings in the SMBIOS structure
> +  @param Ddr5                Whether the SPD data buffer is for a DDR5 DIMM.
> +
> +**/
> +VOID
> +UpdatePartNumber (
> +  IN UINT8     *SpdData,
> +  IN UINTN     PartNumberFieldIdx,
> +  IN OUT VOID  *Type17,
> +  IN UINTN     FixedStringsLength,
> +  IN BOOLEAN   Ddr5
> +  )
> +{
> +  UINTN  Offset;
> +  UINTN  PartNumberLength;
> +  CHAR8  *PartNumber;
> +
> +  if (Ddr5) {
> +    PartNumberLength = DDR_SPD_DDR5_PART_NUMBER_LENGTH + 1;
> +  } else {
> +    PartNumberLength = DDR_SPD_DDR4_PART_NUMBER_LENGTH + 1;
> +  }
> +
> +  Offset = sizeof (SMBIOS_TABLE_TYPE17) +
> +           FixedStringsLength +
> +           (SMBIOS_SERIAL_NUMBER_LENGTH + 1);
> +
> +  PartNumber = (CHAR8 *)Type17 + Offset;
> +
> +  // The part number is stored as ASCII, and so can just be copied.
> +  CopyMem (PartNumber, SpdData + PartNumberFieldIdx, PartNumberLength - 1);
> +
> +  PartNumber[PartNumberLength] = '\0';
> +}
> +
> +/**
> +   CRC16 algorithm from JEDEC 4.1.2.L-6 R30 v14
> +
> +   @param Data  Data bytes.
> +   @param Count Number of bytes to calculate the CRC16 over.
> +
> +   @return Calculated CRC16 value.
> +**/
> +UINT16
> +Crc16 (
> +  UINT8  *Data,
> +  INT32  Count
> +  )
> +{
> +  UINT16  Crc;
> +  UINT32  Index;
> +
> +  Crc = 0;
> +  while (--Count >= 0) {
> +    Crc = Crc ^ (UINT16)*Data++ << 8;
> +    for (Index = 0; Index < 8; ++Index) {
> +      if (Crc & 0x8000) {
> +        Crc = Crc << 1 ^ 0x1021;
> +      } else {
> +        Crc = Crc << 1;
> +      }
> +    }
> +  }
> +
> +  return Crc;
> +}
> +
> +/**
> +  Given an SPD data buffer from a DDR4 or DDR5 DIMM, returns a
> +  pointer to a new SMBIOS_TABLE_TYPE17 structure containing data
> +  parsed from the buffer.
> +
> +  @param SpdData             SPD data buffer.
> +  @param SpdDataSize         Size of the SPD data buffer.
> +  @param Type17              SMBIOS Type17 table.
> +  @param FixedStringsLength  Length of the fixed strings in the SMBIOS structure.
> +
> +  @return EFI_SUCCESS on success, or an error code.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetSmbiosType17FromSpdData (
> +  IN     UINT8                *SpdData,
> +  IN     UINTN                SpdDataSize,
> +  OUT    SMBIOS_TABLE_TYPE17  **Type17,
> +  IN UINTN                    FixedStringsLength
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  if (SpdDataSize < (DDR_SPD_PROTOCOL_TYPE_IDX + 1)) {
> +    return EFI_BUFFER_TOO_SMALL;
> +  }
> +
> +  if ((SpdData[DDR_SPD_PROTOCOL_TYPE_IDX] >= SPD_VAL_DDR5_TYPE) &&
> +      (SpdData[DDR_SPD_PROTOCOL_TYPE_IDX] <= SPD_VAL_LPDDR5X_TYPE))
> +  {
> +    Status = ParseDdr5 (SpdData, SpdDataSize, Type17, FixedStringsLength);
> +  } else {
> +    Status = ParseDdr4 (SpdData, SpdDataSize, Type17, FixedStringsLength);
> +  }
> +
> +  return Status;
> +}
> diff --git a/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosSpdUnitTest.c b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosSpdUnitTest.c
> new file mode 100644
> index 000000000000..707946f1b3b0
> --- /dev/null
> +++ b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SmbiosSpdUnitTest.c
> @@ -0,0 +1,187 @@
> +/** @file
> +  Unit tests for the SMBIOS SPD parsing functions.
> +
> +  Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Uefi.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/UnitTestLib.h>
> +#include <IndustryStandard/SmBios.h>
> +#include <Library/SmbiosType17SpdLib.h>
> +
> +#include "SpdTestData.h"
> +
> +#define UNIT_TEST_APP_NAME     "SMBIOS SPD Unit Test Application"
> +#define UNIT_TEST_APP_VERSION  "1.0"
> +
> +typedef struct {
> +  const UINT8                  *TestInput;
> +  UINTN                        TestInputSize;
> +  const SMBIOS_TABLE_TYPE17    *ExpectedResult;
> +  EFI_STATUS                   ExpectedStatus;
> +} SPD_SMBIOS_TEST_CONTEXT;
> +
> +// ------------------------------------------------ Input------------------Input Size----------------------Output------------------Result------
> +static SPD_SMBIOS_TEST_CONTEXT  mSizeTest1 = { Ddr4DimmTestData1, DDR4_SPD_LEN, &Ddr4DimmTestData1ExpectedResult, EFI_SUCCESS };
> +static SPD_SMBIOS_TEST_CONTEXT  mSizeTest2 = { Ddr4DimmTestData2, DDR4_SPD_LEN, &Ddr4DimmTestData2ExpectedResult, EFI_SUCCESS };
> +
> +/**
> + Unit test to verify functionality for DDR4 SPD data.
> +
> + @param Context  Unit test context
> +
> + @return UNIT_TEST_PASSED
> +**/
> +static
> +UNIT_TEST_STATUS
> +EFIAPI
> +SpdCheckTestDdr4 (
> +  IN UNIT_TEST_CONTEXT  Context
> +  )
> +{
> +  EFI_STATUS               Status;
> +  SPD_SMBIOS_TEST_CONTEXT  *TestParams;
> +  SMBIOS_TABLE_TYPE17      *Table;
> +  UINT8                    *SpdData;
> +
> +  TestParams = (SPD_SMBIOS_TEST_CONTEXT *)Context;
> +  Table      = NULL;
> +  SpdData    = (UINT8 *)TestParams->TestInput;
> +
> +  //
> +  // Test case for basic functionality.
> +  //
> +  Status = GetSmbiosType17FromSpdData (SpdData, TestParams->TestInputSize, &Table, 0);
> +  UT_ASSERT_EQUAL (Status, TestParams->ExpectedStatus);
> +  UT_ASSERT_NOT_NULL (Table);
> +
> +  UT_ASSERT_EQUAL (Table->Hdr.Length, sizeof (SMBIOS_TABLE_TYPE17));
> +  UT_ASSERT_EQUAL (Table->TotalWidth, TestParams->ExpectedResult->TotalWidth);
> +  UT_ASSERT_EQUAL (Table->DataWidth, TestParams->ExpectedResult->DataWidth);
> +  UT_ASSERT_EQUAL (Table->Size, TestParams->ExpectedResult->Size);
> +  UT_ASSERT_EQUAL (Table->FormFactor, TestParams->ExpectedResult->FormFactor);
> +  UT_ASSERT_EQUAL (Table->MemoryType, TestParams->ExpectedResult->MemoryType);
> +
> +  // In future, we should calculate the speed bin in the library and verify it here.
> +  //  UT_ASSERT_EQUAL (Table->Speed, TestParams->ExpectedResult->Speed);
> +  //  UT_ASSERT_EQUAL (Table->ConfiguredMemoryClockSpeed, TestParams->ExpectedResult->ConfiguredMemoryClockSpeed);
> +
> +  UT_ASSERT_EQUAL (Table->MinimumVoltage, TestParams->ExpectedResult->MinimumVoltage);
> +  UT_ASSERT_EQUAL (Table->MaximumVoltage, TestParams->ExpectedResult->MaximumVoltage);
> +  UT_ASSERT_EQUAL (Table->ConfiguredVoltage, TestParams->ExpectedResult->ConfiguredVoltage);
> +  UT_ASSERT_EQUAL (Table->MemoryTechnology, TestParams->ExpectedResult->MemoryTechnology);
> +  UT_ASSERT_EQUAL (Table->ModuleManufacturerID, TestParams->ExpectedResult->ModuleManufacturerID);
> +  UT_ASSERT_EQUAL (Table->MemorySubsystemControllerManufacturerID, TestParams->ExpectedResult->MemorySubsystemControllerManufacturerID);
> +  UT_ASSERT_EQUAL (Table->NonVolatileSize, TestParams->ExpectedResult->NonVolatileSize);
> +  UT_ASSERT_EQUAL (Table->VolatileSize, TestParams->ExpectedResult->VolatileSize);
> +  UT_ASSERT_EQUAL (Table->CacheSize, TestParams->ExpectedResult->CacheSize);
> +  UT_ASSERT_EQUAL (Table->LogicalSize, TestParams->ExpectedResult->LogicalSize);
> +  UT_ASSERT_EQUAL (Table->ExtendedSpeed, TestParams->ExpectedResult->ExtendedSpeed);
> +  UT_ASSERT_EQUAL (Table->ExtendedConfiguredMemorySpeed, TestParams->ExpectedResult->ExtendedConfiguredMemorySpeed);
> +
> +  FreePool (Table);
> +
> +  return UNIT_TEST_PASSED;
> +}
> +
> +/**
> +  Initialize the unit test framework, suite, and unit tests for the
> +  SMBIOS SPD APIs of SmbiosType17SpdLib and run the unit tests.
> +
> +  @retval  EFI_SUCCESS           All test cases were dispatched.
> +  @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available to
> +                                 initialize the unit tests.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +UnitTestingEntry (
> +  VOID
> +  )
> +{
> +  EFI_STATUS                  Status;
> +  UNIT_TEST_FRAMEWORK_HANDLE  Fw;
> +  UNIT_TEST_SUITE_HANDLE      SpdParseTests;
> +
> +  Fw = NULL;
> +
> +  DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION));
> +
> +  //
> +  // Start setting up the test framework for running the tests.
> +  //
> +  Status = InitUnitTestFramework (&Fw, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status));
> +    goto EXIT;
> +  }
> +
> +  //
> +  // Populate the SMBIOS SPD Unit Test Suite.
> +  //
> +  Status = CreateUnitTestSuite (&SpdParseTests, Fw, "SMBIOS SPD Parsing Tests", "SpdParseTest", NULL, NULL);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for SpdParseTests\n"));
> +    Status = EFI_OUT_OF_RESOURCES;
> +    goto EXIT;
> +  }
> +
> +  // --------------Suite-----------Description-------------------Class Name----------Function-------Pre---Post---Context-----
> +  AddTestCase (SpdParseTests, "SMBIOS SPD Test 1 - DDR4 DIMM", "SmbiosSpd.Test1", SpdCheckTestDdr4, NULL, NULL, &mSizeTest1);
> +  AddTestCase (SpdParseTests, "SMBIOS SPD Test 2 - DDR4 DIMM", "SmbiosSpd.Test2", SpdCheckTestDdr4, NULL, NULL, &mSizeTest2);
> +
> +  //
> +  // Execute the tests.
> +  //
> +  Status = RunAllTestSuites (Fw);
> +
> +EXIT:
> +  if (Fw) {
> +    FreeUnitTestFramework (Fw);
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  Standard UEFI entry point for target based unit test execution from UEFI Shell.
> +
> +  @param ImageHandle Image handle.
> +  @param SystemTable System table.
> +
> +  @retval  EFI_SUCCESS           All test cases were dispatched.
> +  @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available to
> +                                 initialize the unit tests.
> +**/
> +EFI_STATUS
> +EFIAPI
> +BaseLibUnitTestAppEntry (
> +  IN EFI_HANDLE        ImageHandle,
> +  IN EFI_SYSTEM_TABLE  *SystemTable
> +  )
> +{
> +  return UnitTestingEntry ();
> +}
> +
> +/**
> +  Standard POSIX C entry point for host based unit test execution.
> +
> +  @param argc Number of arguments
> +  @param argv Array of arguments
> +
> +  @return 0 on success; non-zero on failure.
> +**/
> +int
> +main (
> +  int   argc,
> +  char  *argv[]
> +  )
> +{
> +  return UnitTestingEntry ();
> +}
> diff --git a/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.c b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.c
> new file mode 100644
> index 000000000000..b0eaff924020
> --- /dev/null
> +++ b/MdePkg/Test/UnitTest/Library/SmbiosType17SpdLib/SpdTestData.c
> @@ -0,0 +1,164 @@
> +/** @file
> +  Arrays defining DDR4 and DDR5 SPD EEPROM data
> +
> +  Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <IndustryStandard/SmBios.h>
> +
> +// Data obtained from running systems (with e.g. `hexdump -C /sys/bus/i2c/drivers/ee1004/0-0050/eeprom`)
> +// or from Micron's website.
> +
> +// C code generated from binary files with `xxd -i <inputfile>.bin`
> +
> +/*
> +  https://www.micron.com/products/dram-modules/udimm/part-catalog/mta9asf2g72az-3g2
> +
> +  Micron 16GB PC4-25600 DDR4-3200MHz ECC Unbuffered CL22 UDIMM 1.2V Single-Rank Memory Module
> +*/
> +const UINT8  Ddr4DimmTestData1[] = {
> +  0x23, 0x11, 0x0c, 0x02, 0x86, 0x29, 0x00, 0x08, 0x00, 0x60, 0x00, 0x03,
> +  0x01, 0x0b, 0x80, 0x00, 0x00, 0x00, 0x05, 0x0d, 0xf8, 0xff, 0x2f, 0x00,
> +  0x6e, 0x6e, 0x6e, 0x11, 0x00, 0x6e, 0xf0, 0x0a, 0x20, 0x08, 0x00, 0x05,
> +  0x00, 0xa8, 0x14, 0x28, 0x28, 0x00, 0x78, 0x00, 0x14, 0x3c, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x16, 0x36, 0x16, 0x36, 0x16, 0x36, 0x16, 0x36, 0x16, 0x36, 0x16, 0x36,
> +  0x16, 0x36, 0x16, 0x36, 0x16, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, 0xa8, 0x14, 0x11, 0x01, 0x43, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x89, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2c, 0x0f, 0x20,
> +  0x45, 0x2b, 0x38, 0xf2, 0x69, 0x39, 0x41, 0x53, 0x46, 0x32, 0x47, 0x37,
> +  0x32, 0x41, 0x5a, 0x2d, 0x33, 0x47, 0x32, 0x42, 0x31, 0x20, 0x20, 0x20,
> +  0x20, 0x31, 0x80, 0x2c, 0x42, 0x44, 0x50, 0x41, 0x51, 0x38, 0x30, 0x33,
> +  0x30, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
> +};
> +
> +const UINTN Ddr4DimmTestData1Size = sizeof (Ddr4DimmTestData1);
> +
> +const SMBIOS_TABLE_TYPE17  Ddr4DimmTestData1ExpectedResult = {
> +  .TotalWidth                              = 72,
> +  .DataWidth                               = 64,
> +  .Size                                    = (16 * 1024),
> +  .FormFactor                              = MemoryFormFactorDimm,
> +  .MemoryType                              = MemoryTypeDdr4,
> +  .Speed                                   = 3200,
> +  .ConfiguredMemoryClockSpeed              = 3200,
> +  .MinimumVoltage                          = 1200,
> +  .MaximumVoltage                          = 1200,
> +  .ConfiguredVoltage                       = 1200,
> +  .MemoryTechnology                        = MemoryTechnologyDram,
> +  .ModuleManufacturerID                    = 0x2C80,
> +  .MemorySubsystemControllerManufacturerID = 0x2C80,
> +  .NonVolatileSize                         = 0,
> +  .VolatileSize                            = (16ULL * 1024 * 1024 * 1024),
> +  .CacheSize                               = 0,
> +  .LogicalSize                             = (16ULL * 1024 * 1024 * 1024),
> +  .ExtendedSpeed                           = 0,
> +  .ExtendedConfiguredMemorySpeed           = 0
> +};
> +
> +/*
> +  https://www.micron.com/products/dram-modules/vlp-rdimm/part-catalog/mta9adf1g72pz-3g2
> +
> +  Micron MTA9ADF1G72PZ-3G2E1 memory module 8 GB 1x8GB DDR4 3200 MT/s ECC VLP 288-pin RDIMM
> +*/
> +const UINT8  Ddr4DimmTestData2[] = {
> +  0x23, 0x12, 0x0C, 0x01, 0x85, 0x21, 0x00, 0x08, 0x00, 0x60, 0x00, 0x03,
> +  0x01, 0x0B, 0x80, 0x00, 0x00, 0x00, 0x05, 0x0D, 0xF8, 0xFF, 0x2F, 0x00,
> +  0x6E, 0x6E, 0x6E, 0x11, 0x00, 0x6E, 0xF0, 0x0A, 0x20, 0x08, 0x00, 0x05,
> +  0x00, 0xA8, 0x14, 0x28, 0x28, 0x00, 0x78, 0x00, 0x14, 0x3C, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x24, 0x03, 0x15, 0x2C, 0x24, 0x03, 0x15, 0x2C, 0x24, 0x03, 0x24, 0x03,
> +  0x15, 0x2C, 0x24, 0x03, 0x15, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0xE7, 0x00, 0xFF, 0xDF, 0x04, 0x11, 0x06, 0x15,
> +  0x00, 0x86, 0x32, 0xD1, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0xB7, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2C, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x41, 0x44, 0x46, 0x31, 0x47, 0x37,
> +  0x32, 0x50, 0x5A, 0x2D, 0x33, 0x47, 0x32, 0x45, 0x31, 0x00, 0x00, 0x00,
> +  0x00, 0x31, 0x80, 0x2C, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
> +};
> +
> +const UINTN Ddr4DimmTestData2Size = sizeof (Ddr4DimmTestData2);
> +
> +const SMBIOS_TABLE_TYPE17  Ddr4DimmTestData2ExpectedResult = {
> +  .TotalWidth                              = 72,
> +  .DataWidth                               = 64,
> +  .Size                                    = (8 * 1024),
> +  .FormFactor                              = MemoryFormFactorDimm,
> +  .MemoryType                              = MemoryTypeDdr4,
> +  .Speed                                   = 3200,
> +  .ConfiguredMemoryClockSpeed              = 3200,
> +  .MinimumVoltage                          = 1200,
> +  .MaximumVoltage                          = 1200,
> +  .ConfiguredVoltage                       = 1200,
> +  .MemoryTechnology                        = MemoryTechnologyDram,
> +  .ModuleManufacturerID                    = 0x2C80,
> +  .MemorySubsystemControllerManufacturerID = 0x2C80,
> +  .NonVolatileSize                         = 0,
> +  .VolatileSize                            = (8ULL * 1024 * 1024 * 1024),
> +  .CacheSize                               = 0,
> +  .LogicalSize                             = (8ULL * 1024 * 1024 * 1024),
> +  .ExtendedSpeed                           = 0,
> +  .ExtendedConfiguredMemorySpeed           = 0
> +};

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2023-02-14 21:59 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-02-14 21:58 [RFC PATCH 1/1] MdePkg: Add library to parse SPD data and create SMBIOS Type 17 table Rebecca Cran
2023-02-14 21:59 ` Rebecca Cran

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox