From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) by mx.groups.io with SMTP id smtpd.web10.901.1676411915818619316 for ; Tue, 14 Feb 2023 13:58:36 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@quicinc.com header.s=qcppdkim1 header.b=A49lrSdn; spf=permerror, err=parse error for token &{10 18 %{ir}.%{v}.%{d}.spf.has.pphosted.com}: invalid domain name (domain: quicinc.com, ip: 205.220.180.131, mailfrom: quic_rcran@quicinc.com) Received: from pps.filterd (m0279872.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 31ELPZ27021341; Tue, 14 Feb 2023 21:58:27 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h=from : to : cc : subject : date : message-id : mime-version : content-transfer-encoding : content-type; s=qcppdkim1; bh=UyxtWAVrctgYsh6IFG1TbpNOV58e7ntBNL8tDn/+pRc=; b=A49lrSdn4jzooNs3V75kbqzKI47Ehe3PWbO/IT6h+dACO1hKdBeT/NNZaDqmRI7KnrLA NEgu4q9SnMqDTZr54KVBhzkMBtc1yFpMhmaj2Q8uGtwjTJ4Gt9uZ5/DZa/QNYiPo3cnj IRHoG7er2vwJMwKX8fHumQ8Xepa56chf2WaJvtPu6nYxNiL8oypuMJjYY2eHE//Qc7cf Ff7FB/dginbakNjrmQx8MG76cDN42BEkVtftaGWuAzuU06jWKW8vFPA6Tt+hHaqfFhTy i3KZJo1YQP/AfibMJpVnYtfRky4AJV2n5bjfP79sQChGbPjNAmLbidOiGGZguJLv+5pl dA== Received: from nalasppmta05.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 3nr20vtnbp-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 14 Feb 2023 21:58:26 +0000 Received: from nalasex01a.na.qualcomm.com (nalasex01a.na.qualcomm.com [10.47.209.196]) by NALASPPMTA05.qualcomm.com (8.17.1.5/8.17.1.5) with ESMTPS id 31ELwPIW011400 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 14 Feb 2023 21:58:25 GMT Received: from linbox.qualcomm.com (10.80.80.8) by nalasex01a.na.qualcomm.com (10.47.209.196) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.36; Tue, 14 Feb 2023 13:58:25 -0800 From: "Rebecca Cran" To: , Michael D Kinney , Liming Gao , Zhiguang Liu CC: Rebecca Cran Subject: [RFC PATCH 1/1] MdePkg: Add library to parse SPD data and create SMBIOS Type 17 table Date: Tue, 14 Feb 2023 14:58:09 -0700 Message-ID: <20230214215809.577154-1-rebecca@quicinc.com> X-Mailer: git-send-email 2.30.2 MIME-Version: 1.0 X-Originating-IP: [10.80.80.8] X-ClientProxiedBy: nasanex01b.na.qualcomm.com (10.46.141.250) To nalasex01a.na.qualcomm.com (10.47.209.196) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: _rKerKXDMtwG1j1FVWscU9TxDe2RodZS X-Proofpoint-GUID: _rKerKXDMtwG1j1FVWscU9TxDe2RodZS X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.219,Aquarius:18.0.930,Hydra:6.0.562,FMLib:17.11.170.22 definitions=2023-02-14_15,2023-02-14_01,2023-02-09_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 mlxscore=0 mlxlogscore=999 priorityscore=1501 malwarescore=0 clxscore=1015 bulkscore=0 impostorscore=0 spamscore=0 adultscore=0 phishscore=0 suspectscore=0 lowpriorityscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2212070000 definitions=main-2302140187 Content-Transfer-Encoding: 8bit Content-Type: text/plain 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 --- 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.
+# 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.
+# 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.
Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ -#ifndef _SDRAM_SPD_H_ -#define _SDRAM_SPD_H_ - -#include -#include -#include +#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 + +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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef SMBIOS_TYPE17_SPD_LIB_H_ +#define SMBIOS_TYPE17_SPD_LIB_H_ + +#include + +#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.
+ 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +// 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 .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