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
+};