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