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 <rebe...@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
+};


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#100193): https://edk2.groups.io/g/devel/message/100193
Mute This Topic: https://groups.io/mt/96971232/21656
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to