From nobody Fri Apr 26 12:26:58 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) client-ip=66.175.222.108; envelope-from=bounce+27952+73756+1787277+3901457@groups.io; helo=mail02.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+73756+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1617764803; cv=none; d=zohomail.com; s=zohoarc; b=RyhWpGGF3Zydwq2OCUa/zEcWMeGrCrux0oZHQrnR9+60ktmqPw6Myzf509RKJhfCuhrmZpREtyXnKvUTzw7R0DSaAWylOY/1yDXRHfuCgy2/wdgtUVV6M+FbXssgz9wJC5DIZMs21/LMdSE+qWa1TuEWI/fUmuh0MTFLBF9Ki4g= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1617764803; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=l15glHcGP7Yryv1ViwJiM1vCtKFMNl1XPAeODC26XKw=; b=b13KKWhpd3G9KQU6JrV59mSUIdOhxVHuy6+Pe6hZVmSKuV/aEsMFzCkXgAm+s5El6qrRpL5Dyyau5NlkW7OsLxW9y8Fz3Uv4Mngtzvp4DDhg0F1Rd3q44j+Q8WVW9B72QFkxHBm4wYexnd4LsI06fcQZhbOcwt8ZbH4gBPAvcnI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+73756+1787277+3901457@groups.io; dmarc=fail header.from= (p=none dis=none) header.from= Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by mx.zohomail.com with SMTPS id 1617764803562384.4947679867971; Tue, 6 Apr 2021 20:06:43 -0700 (PDT) Return-Path: X-Received: by 127.0.0.2 with SMTP id vPc5YY1788612xQLeYW7DYJa; Tue, 06 Apr 2021 20:06:43 -0700 X-Received: from mga01.intel.com (mga01.intel.com []) by mx.groups.io with SMTP id smtpd.web10.2005.1617764802326915683 for ; Tue, 06 Apr 2021 20:06:42 -0700 IronPort-SDR: rffUxiqJXPnS5AawjC+tF/zJ6vX33HM1I55vIIGLrvmabK9mr1bbwThwHKHNn8H1Sie8OkARUB Gc6tngEMccSg== X-IronPort-AV: E=McAfee;i="6000,8403,9946"; a="213587417" X-IronPort-AV: E=Sophos;i="5.82,201,1613462400"; d="scan'208";a="213587417" X-Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 06 Apr 2021 20:06:41 -0700 IronPort-SDR: m1ymJcYBWpa3KIHQJjPT76wl7Eg1Jf34dse1mMxXXG72sqsKOk5j3HBnS44JxeHR5LtTyX7zKS nV4I1JyWcv7A== X-IronPort-AV: E=Sophos;i="5.82,201,1613462400"; d="scan'208";a="448896655" X-Received: from nldesimo-desk1.amr.corp.intel.com ([10.209.7.29]) by fmsmga002-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 06 Apr 2021 20:06:39 -0700 From: "Nate DeSimone" To: devel@edk2.groups.io Cc: Chasel Chiu , Liming Gao , Eric Dong , Michael Kubacki , Isaac Oram Subject: [edk2-devel] [edk2-platforms] [PATCH v4 3/4] MinPlatformPkg: Add LargeVariableReadLib Date: Tue, 6 Apr 2021 20:04:25 -0700 Message-Id: <20210407030426.8075-4-nathaniel.l.desimone@intel.com> In-Reply-To: <20210407030426.8075-1-nathaniel.l.desimone@intel.com> References: <20210407030426.8075-1-nathaniel.l.desimone@intel.com> MIME-Version: 1.0 Precedence: Bulk List-Unsubscribe: List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,nathaniel.l.desimone@intel.com X-Gm-Message-State: 5G9en5i5ie45knUDgJBysL8Cx1787277AA= Content-Transfer-Encoding: quoted-printable DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1617764803; bh=Z2pUggozcKoF8Vj7h9EssG0CqNPe7UBOijzWRAm3mLs=; h=Cc:Date:From:Reply-To:Subject:To; b=Ame0kmTgw5RhrCagyKMSU9kbtPgkoX0/4AZkznt1a2uEsfuVbr2xgNAlWIUVmsklj33 7oUpdzoysijchy6ZahdVBB8XCbYcRZFbV3KhLAkg8OlCnIfq645UZEAbJBlQshUKh1stH E7v1dip6PjAV3FJ9RH9HA7B1+ww8JuwkySA= X-ZohoMail-DKIM: pass (identity @groups.io) Content-Type: text/plain; charset="utf-8" LargeVariableReadLib is used to retrieve large data sets using the UEFI Variable Services. At time of writting, most UEFI Variable Services implementations to not allow more than 64KB of data to be stored in a single UEFI variable. This library will split data sets across multiple variables as needed. It adds the GetLargeVariable() API to provide this service. The primary use for this library is to create binary compatible drivers and OpROMs which need to work both with TianoCore and other UEFI PI implementations. When customizing and recompiling the platform firmware image is possible, adjusting the value of PcdMaxVariableSize may provide a simpler solution to this problem. Cc: Chasel Chiu Cc: Liming Gao Cc: Eric Dong Cc: Michael Kubacki Cc: Isaac Oram Signed-off-by: Nate DeSimone Reviewed-by: Isaac Oram --- .../Include/Dsc/CoreCommonLib.dsc | 5 +- .../Include/Library/LargeVariableReadLib.h | 61 ++++++ .../BaseLargeVariableReadLib.inf | 51 +++++ .../LargeVariableCommon.h | 47 +++++ .../LargeVariableReadLib.c | 176 ++++++++++++++++++ 5 files changed, 338 insertions(+), 2 deletions(-) create mode 100644 Platform/Intel/MinPlatformPkg/Include/Library/LargeVari= ableReadLib.h create mode 100644 Platform/Intel/MinPlatformPkg/Library/BaseLargeVariable= Lib/BaseLargeVariableReadLib.inf create mode 100644 Platform/Intel/MinPlatformPkg/Library/BaseLargeVariable= Lib/LargeVariableCommon.h create mode 100644 Platform/Intel/MinPlatformPkg/Library/BaseLargeVariable= Lib/LargeVariableReadLib.c diff --git a/Platform/Intel/MinPlatformPkg/Include/Dsc/CoreCommonLib.dsc b/= Platform/Intel/MinPlatformPkg/Include/Dsc/CoreCommonLib.dsc index cf2940cf02..9bc5ff1e3d 100644 --- a/Platform/Intel/MinPlatformPkg/Include/Dsc/CoreCommonLib.dsc +++ b/Platform/Intel/MinPlatformPkg/Include/Dsc/CoreCommonLib.dsc @@ -135,13 +135,14 @@ VarCheckLib|MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf PlatformSecureLib|SecurityPkg/Library/PlatformSecureLibNull/PlatformSecu= reLibNull.inf AuthVariableLib|MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLib= Null.inf - =20 + !if gMinPlatformPkgTokenSpaceGuid.PcdUefiSecureBootEnable =3D=3D TRUE AuthVariableLib|SecurityPkg/Library/AuthVariableLib/AuthVariableLib.inf !endif =20 SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf BmpSupportLib|MdeModulePkg/Library/BaseBmpSupportLib/BaseBmpSupportLib.i= nf + LargeVariableReadLib|MinPlatformPkg/Library/BaseLargeVariableLib/BaseLar= geVariableReadLib.inf =20 # # CryptLib @@ -165,4 +166,4 @@ =20 SmbusLib|MdePkg/Library/BaseSmbusLibNull/BaseSmbusLibNull.inf VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyL= ib.inf - VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/Var= iablePolicyHelperLib.inf \ No newline at end of file + VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/Var= iablePolicyHelperLib.inf diff --git a/Platform/Intel/MinPlatformPkg/Include/Library/LargeVariableRea= dLib.h b/Platform/Intel/MinPlatformPkg/Include/Library/LargeVariableReadLib= .h new file mode 100644 index 0000000000..df5020b95f --- /dev/null +++ b/Platform/Intel/MinPlatformPkg/Include/Library/LargeVariableReadLib.h @@ -0,0 +1,61 @@ +/** @file + Large Variable Read Lib + + This library is used to retrieve large data sets using the UEFI Variable + Services. At time of writing, most UEFI Variable Services implementation= s do + not allow more than 64KB of data to be stored in a single UEFI variable.= This + library will split data sets across multiple variables as needed. + + In the case where more than one variable is needed to store the data, an + integer number will be added to the end of the variable name. This number + will be incremented for each variable as needed to retrieve the entire d= ata + set. + + The primary use for this library is to create binary compatible drivers + and OpROMs which need to work both with TianoCore and other UEFI PI + implementations. When customizing and recompiling the platform firmware = image + is possible, adjusting the value of PcdMaxVariableSize may provide a sim= pler + solution to this problem. + + Copyright (c) 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _LARGE_VARIABLE_READ_LIB_H_ +#define _LARGE_VARIABLE_READ_LIB_H_ + +#include + +/** + Returns the value of a large variable. + + @param[in] VariableName A Null-terminated string that is the name= of the vendor's + variable. + @param[in] VendorGuid A unique identifier for the vendor. + @param[in, out] DataSize On input, the size in bytes of the return= Data buffer. + On output the size of data returned in Da= ta. + @param[out] Data The buffer to return the contents of the = variable. May be NULL + with a zero DataSize in order to determin= e the size buffer needed. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND The variable was not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. + @retval EFI_INVALID_PARAMETER VariableName is NULL. + @retval EFI_INVALID_PARAMETER VendorGuid is NULL. + @retval EFI_INVALID_PARAMETER DataSize is NULL. + @retval EFI_INVALID_PARAMETER The DataSize is not too small and Data is= NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved due t= o a hardware error. + @retval EFI_SECURITY_VIOLATION The variable could not be retrieved due t= o an authentication failure. + +**/ +EFI_STATUS +EFIAPI +GetLargeVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN OUT UINTN *DataSize, + OUT VOID *Data OPTIONAL + ); + +#endif // _LARGE_VARIABLE_READ_LIB_H_ diff --git a/Platform/Intel/MinPlatformPkg/Library/BaseLargeVariableLib/Bas= eLargeVariableReadLib.inf b/Platform/Intel/MinPlatformPkg/Library/BaseLarge= VariableLib/BaseLargeVariableReadLib.inf new file mode 100644 index 0000000000..3cd94e00fb --- /dev/null +++ b/Platform/Intel/MinPlatformPkg/Library/BaseLargeVariableLib/BaseLargeV= ariableReadLib.inf @@ -0,0 +1,51 @@ +## @file +# Component description file for Large Variable Read Library +# +# This library is used to retrieve large data sets using the UEFI Variable +# Services. At time of writing, most UEFI Variable Services implementation= s do +# not allow more than 64KB of data to be stored in a single UEFI variable.= This +# library will split data sets across multiple variables as needed. +# +# In the case where more than one variable is needed to store the data, an +# integer number will be added to the end of the variable name. This number +# will be incremented for each variable as needed to retrieve the entire d= ata +# set. +# +# The primary use for this library is to create binary compatible drivers +# and OpROMs which need to work both with TianoCore and other UEFI PI +# implementations. When customizing and recompiling the platform firmware = image +# is possible, adjusting the value of PcdMaxVariableSize may provide a sim= pler +# solution to this problem. +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 0x00010005 + BASE_NAME =3D BaseLargeVariableReadLib + FILE_GUID =3D 4E9D7D31-A7A0-4004-AE93-D12F1AB08730 + MODULE_TYPE =3D BASE + VERSION_STRING =3D 1.0 + LIBRARY_CLASS =3D LargeVariableReadLib + +# +# VALID_ARCHITECTURES =3D IA32 X64 EBC +# + +[Sources] + LargeVariableReadLib.c + LargeVariableCommon.h + +[Packages] + MdePkg/MdePkg.dec + MinPlatformPkg/MinPlatformPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + PrintLib + VariableReadLib diff --git a/Platform/Intel/MinPlatformPkg/Library/BaseLargeVariableLib/Lar= geVariableCommon.h b/Platform/Intel/MinPlatformPkg/Library/BaseLargeVariabl= eLib/LargeVariableCommon.h new file mode 100644 index 0000000000..b6554764b7 --- /dev/null +++ b/Platform/Intel/MinPlatformPkg/Library/BaseLargeVariableLib/LargeVaria= bleCommon.h @@ -0,0 +1,47 @@ +/** @file + Large Variable Lib Common Header + + These libraries is used to store large data sets using the UEFI Variable + Services. At time of writing, most UEFI Variable Services implementation= s do + not allow more than 64KB of data to be stored in a single UEFI variable.= This + library will split data sets across multiple variables as needed. + + In the case where more than one variable is needed to store the data, an + integer number will be added to the end of the variable name. This number + will be incremented for each variable as needed to retrieve the entire d= ata + set. + + Copyright (c) 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _LARGE_VARIABLE_COMMON_H_ +#define _LARGE_VARIABLE_COMMON_H_ + +// +// 1024 was choosen because this is the size of the SMM communication buff= er +// used by VariableDxeSmm to transfer the VariableName from DXE to SMM. Ch= oosing +// the same size will prevent this library from limiting variable names any +// more than the MdeModulePkg implementation of UEFI Variable Services doe= s. +// +#define MAX_VARIABLE_NAME_SIZE 1024 + +// +// The 2012 Windows Hardware Requirements specified a minimum variable siz= e of +// 32KB. By setting the maximum allowed number of variables to 0x20000, th= is +// allows up to 4GB of data to be stored on most UEFI implementations in +// existence. Older UEFI implementations were known to only provide 8KB per +// variable. In this case, up to 1GB can be stored. Since 1GB vastly excee= ds the +// size of any known NvStorage FV, choosing this number should effectively +// enable all available NvStorage space to be used to store the given data. +// +#define MAX_VARIABLE_SPLIT 131072 // 0x20000 + +// +// There are 6 digits in the number 131072, which means the length of the = string +// representation of this number will be at most 6 characters long. +// +#define MAX_VARIABLE_SPLIT_DIGITS 6 + +#endif // _LARGE_VARIABLE_COMMON_H_ diff --git a/Platform/Intel/MinPlatformPkg/Library/BaseLargeVariableLib/Lar= geVariableReadLib.c b/Platform/Intel/MinPlatformPkg/Library/BaseLargeVariab= leLib/LargeVariableReadLib.c new file mode 100644 index 0000000000..08690d1ffe --- /dev/null +++ b/Platform/Intel/MinPlatformPkg/Library/BaseLargeVariableLib/LargeVaria= bleReadLib.c @@ -0,0 +1,176 @@ +/** @file + Large Variable Read Lib + + This library is used to retrieve large data sets using the UEFI Variable + Services. At time of writing, most UEFI Variable Services implementation= s do + not allow more than 64KB of data to be stored in a single UEFI variable.= This + library will split data sets across multiple variables as needed. + + In the case where more than one variable is needed to store the data, an + integer number will be added to the end of the variable name. This number + will be incremented for each variable as needed to retrieve the entire d= ata + set. + + Copyright (c) 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include + +#include "LargeVariableCommon.h" + +/** + Returns the value of a large variable. + + @param[in] VariableName A Null-terminated string that is the name= of the vendor's + variable. + @param[in] VendorGuid A unique identifier for the vendor. + @param[in, out] DataSize On input, the size in bytes of the return= Data buffer. + On output the size of data returned in Da= ta. + @param[out] Data The buffer to return the contents of the = variable. May be NULL + with a zero DataSize in order to determin= e the size buffer needed. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND The variable was not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. + @retval EFI_INVALID_PARAMETER VariableName is NULL. + @retval EFI_INVALID_PARAMETER VendorGuid is NULL. + @retval EFI_INVALID_PARAMETER DataSize is NULL. + @retval EFI_INVALID_PARAMETER The DataSize is not too small and Data is= NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved due t= o a hardware error. + @retval EFI_SECURITY_VIOLATION The variable could not be retrieved due t= o an authentication failure. + +**/ +EFI_STATUS +EFIAPI +GetLargeVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN OUT UINTN *DataSize, + OUT VOID *Data OPTIONAL + ) +{ + CHAR16 TempVariableName[MAX_VARIABLE_NAME_SIZE]; + EFI_STATUS Status; + UINTN TotalSize; + UINTN VarDataSize; + UINTN Index; + UINTN VariableSize; + UINTN BytesRemaining; + UINT8 *OffsetPtr; + + VarDataSize =3D 0; + + // + // First check if a variable with the given name exists + // + Status =3D VarLibGetVariable (VariableName, VendorGuid, NULL, &VarDataSi= ze, NULL); + if (Status =3D=3D EFI_BUFFER_TOO_SMALL) { + if (*DataSize >=3D VarDataSize) { + if (Data =3D=3D NULL) { + Status =3D EFI_INVALID_PARAMETER; + goto Done; + } + DEBUG ((DEBUG_VERBOSE, "GetLargeVariable: Single Variable Found\n")); + Status =3D VarLibGetVariable (VariableName, VendorGuid, NULL, DataSi= ze, Data); + goto Done; + } else { + *DataSize =3D VarDataSize; + Status =3D EFI_BUFFER_TOO_SMALL; + goto Done; + } + + } else if (Status =3D=3D EFI_NOT_FOUND) { + // + // Check if the first variable of a multi-variable set exists + // + if (StrLen (VariableName) >=3D (MAX_VARIABLE_NAME_SIZE - MAX_VARIABLE_= SPLIT_DIGITS)) { + DEBUG ((DEBUG_ERROR, "GetLargeVariable: Variable name too long\n")); + Status =3D EFI_OUT_OF_RESOURCES; + goto Done; + } + + VarDataSize =3D 0; + Index =3D 0; + ZeroMem (TempVariableName, MAX_VARIABLE_NAME_SIZE); + UnicodeSPrint (TempVariableName, MAX_VARIABLE_NAME_SIZE, L"%s%d", Vari= ableName, Index); + Status =3D VarLibGetVariable (TempVariableName, VendorGuid, NULL, &Var= DataSize, NULL); + + if (Status =3D=3D EFI_BUFFER_TOO_SMALL) { + // + // The first variable exists. Calculate the total size of all the va= riables. + // + DEBUG ((DEBUG_VERBOSE, "GetLargeVariable: Multiple Variables Found\n= ")); + TotalSize =3D 0; + for (Index =3D 0; Index < MAX_VARIABLE_SPLIT; Index++) { + VarDataSize =3D 0; + ZeroMem (TempVariableName, MAX_VARIABLE_NAME_SIZE); + UnicodeSPrint (TempVariableName, MAX_VARIABLE_NAME_SIZE, L"%s%d", = VariableName, Index); + Status =3D VarLibGetVariable (TempVariableName, VendorGuid, NULL, = &VarDataSize, NULL); + if (Status !=3D EFI_BUFFER_TOO_SMALL) { + break; + } + TotalSize +=3D VarDataSize; + } + DEBUG ((DEBUG_VERBOSE, "TotalSize =3D %d, NumVariables =3D %d\n", To= talSize, Index)); + + // + // Check if the user provided a large enough buffer + // + if (*DataSize >=3D TotalSize) { + if (Data =3D=3D NULL) { + Status =3D EFI_INVALID_PARAMETER; + goto Done; + } + + // + // Read the data from all variables + // + OffsetPtr =3D (UINT8 *) Data; + BytesRemaining =3D *DataSize; + for (Index =3D 0; Index < MAX_VARIABLE_SPLIT; Index++) { + VarDataSize =3D 0; + ZeroMem (TempVariableName, MAX_VARIABLE_NAME_SIZE); + UnicodeSPrint (TempVariableName, MAX_VARIABLE_NAME_SIZE, L"%s%d"= , VariableName, Index); + VariableSize =3D BytesRemaining; + DEBUG ((DEBUG_INFO, "Reading %s, Guid =3D %g,", TempVariableName= , VendorGuid)); + Status =3D VarLibGetVariable (TempVariableName, VendorGuid, NULL= , &VariableSize, (VOID *) OffsetPtr); + DEBUG ((DEBUG_INFO, " Size %d\n", VariableSize)); + if (EFI_ERROR (Status)) { + if (Status =3D=3D EFI_NOT_FOUND) { + DEBUG ((DEBUG_VERBOSE, "No more variables found\n")); + Status =3D EFI_SUCCESS; // The end has been reached + } + goto Done; + } + + if (VariableSize < BytesRemaining) { + BytesRemaining -=3D VariableSize; + OffsetPtr +=3D VariableSize; + } else { + DEBUG ((DEBUG_VERBOSE, "All data has been read\n")); + BytesRemaining =3D 0; + break; + } + } //End of for loop + + goto Done; + } else { + *DataSize =3D TotalSize; + Status =3D EFI_BUFFER_TOO_SMALL; + goto Done; + } + } else { + Status =3D EFI_NOT_FOUND; + } + } + +Done: + DEBUG ((DEBUG_ERROR, "GetLargeVariable: Status =3D %r\n", Status)); + return Status; +} --=20 2.27.0.windows.1 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#73756): https://edk2.groups.io/g/devel/message/73756 Mute This Topic: https://groups.io/mt/81907528/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-