diff options
Diffstat (limited to 'MdePkg/Library')
51 files changed, 2363 insertions, 138 deletions
diff --git a/MdePkg/Library/BaseFdtLib/LibFdtSupport.h b/MdePkg/Library/BaseFdtLib/LibFdtSupport.h index ebe1eb6b26c8..03532c8c4d96 100644 --- a/MdePkg/Library/BaseFdtLib/LibFdtSupport.h +++ b/MdePkg/Library/BaseFdtLib/LibFdtSupport.h @@ -14,17 +14,21 @@ #include <Library/BaseLib.h> #include <Library/BaseMemoryLib.h> -typedef UINT8 uint8_t; -typedef UINT16 uint16_t; -typedef INT32 int32_t; -typedef UINT32 uint32_t; -typedef UINT64 uint64_t; -typedef UINTN uintptr_t; -typedef UINTN size_t; -typedef BOOLEAN bool; - +typedef UINT8 uint8_t; +typedef UINT16 uint16_t; +typedef INT32 int32_t; +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; +typedef UINTN uintptr_t; +typedef UINTN size_t; + +#if defined __STDC_VERSION__ && __STDC_VERSION__ > 201710L +/* bool, true and false are keywords. */ +#else +typedef BOOLEAN bool; #define true (1 == 1) #define false (1 == 0) +#endif // // Definitions for global constants used by libfdt library routines diff --git a/MdePkg/Library/BasePeCoffLib/BasePeCoff.c b/MdePkg/Library/BasePeCoffLib/BasePeCoff.c index 9e74622fe48a..8c9f0c9450e4 100644 --- a/MdePkg/Library/BasePeCoffLib/BasePeCoff.c +++ b/MdePkg/Library/BasePeCoffLib/BasePeCoff.c @@ -24,6 +24,7 @@ **/ #include "BasePeCoffLibInternals.h" +#include <Library/SafeIntLib.h> /** Adjust some fields in section header for TE image. @@ -975,6 +976,7 @@ PeCoffLoaderRelocateImage ( PHYSICAL_ADDRESS BaseAddress; UINT32 NumberOfRvaAndSizes; UINT32 TeStrippedOffset; + UINT32 EndAddress; ASSERT (ImageContext != NULL); @@ -1054,22 +1056,24 @@ PeCoffLoaderRelocateImage ( RelocDir = &Hdr.Te->DataDirectory[0]; } - if ((RelocDir != NULL) && (RelocDir->Size > 0) && ((RelocDir->Size - 1) < (MAX_UINT32 - RelocDir->VirtualAddress))) { - RelocBase = (EFI_IMAGE_BASE_RELOCATION *)PeCoffLoaderImageAddress (ImageContext, RelocDir->VirtualAddress, TeStrippedOffset); - RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION *)PeCoffLoaderImageAddress ( - ImageContext, - RelocDir->VirtualAddress + RelocDir->Size - 1, - TeStrippedOffset - ); + RelocBase = NULL; + RelocBaseEnd = NULL; + if ((RelocDir != NULL) && (RelocDir->Size > 0)) { + Status = SafeUint32Add (RelocDir->VirtualAddress, (RelocDir->Size - 1), &EndAddress); + if (!RETURN_ERROR (Status)) { + RelocBase = (EFI_IMAGE_BASE_RELOCATION *)PeCoffLoaderImageAddress (ImageContext, RelocDir->VirtualAddress, TeStrippedOffset); + RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION *)PeCoffLoaderImageAddress ( + ImageContext, + EndAddress, + TeStrippedOffset + ); + } + if ((RelocBase == NULL) || (RelocBaseEnd == NULL) || ((UINTN)RelocBaseEnd < (UINTN)RelocBase)) { ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION; + DEBUG ((DEBUG_ERROR, "Relocation block is not valid\n")); return RETURN_LOAD_ERROR; } - } else { - // - // Set base and end to bypass processing below. - // - RelocBase = RelocBaseEnd = NULL; } RelocBaseOrg = RelocBase; @@ -1767,6 +1771,7 @@ PeCoffLoaderRelocateImageForRuntime ( UINTN Adjust; RETURN_STATUS Status; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + UINT32 EndAddress; if ((RelocationData == NULL) || (ImageBase == 0x0) || (VirtImageBase == 0x0)) { return; @@ -1828,12 +1833,15 @@ PeCoffLoaderRelocateImageForRuntime ( if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) { RelocDir = DataDirectory + EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC; if ((RelocDir != NULL) && (RelocDir->Size > 0)) { - RelocBase = (EFI_IMAGE_BASE_RELOCATION *)PeCoffLoaderImageAddress (&ImageContext, RelocDir->VirtualAddress, 0); - RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION *)PeCoffLoaderImageAddress ( - &ImageContext, - RelocDir->VirtualAddress + RelocDir->Size - 1, - 0 - ); + Status = SafeUint32Add (RelocDir->VirtualAddress, (RelocDir->Size - 1), &EndAddress); + if (!RETURN_ERROR (Status)) { + RelocBase = (EFI_IMAGE_BASE_RELOCATION *)PeCoffLoaderImageAddress (&ImageContext, RelocDir->VirtualAddress, 0); + RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION *)PeCoffLoaderImageAddress ( + &ImageContext, + EndAddress, + 0 + ); + } } if ((RelocBase == NULL) || (RelocBaseEnd == NULL) || ((UINTN)RelocBaseEnd < (UINTN)RelocBase)) { diff --git a/MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf b/MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf index 0172d2691c68..80fd5344539d 100644 --- a/MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf +++ b/MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf @@ -58,4 +58,5 @@ DebugLib PeCoffExtraActionLib BaseMemoryLib + SafeIntLib diff --git a/MdePkg/Library/BasePeCoffLib/LoongArch/PeCoffLoaderEx.c b/MdePkg/Library/BasePeCoffLib/LoongArch/PeCoffLoaderEx.c index 738b056dd1f5..f3ec94c07c5d 100644 --- a/MdePkg/Library/BasePeCoffLib/LoongArch/PeCoffLoaderEx.c +++ b/MdePkg/Library/BasePeCoffLib/LoongArch/PeCoffLoaderEx.c @@ -104,7 +104,15 @@ PeCoffLoaderImageFormatSupported ( IN UINT16 Machine ) { - if (Machine == IMAGE_FILE_MACHINE_LOONGARCH64) { + /* + * ARM64 and X64 may allow such foreign images to be used when + * a driver implementing EDKII_PECOFF_IMAGE_EMULATOR_PROTOCOL is + * present. + */ + if ((Machine == IMAGE_FILE_MACHINE_LOONGARCH64) || + (Machine == IMAGE_FILE_MACHINE_ARM64) || + (Machine == IMAGE_FILE_MACHINE_X64)) + { return TRUE; } diff --git a/MdePkg/Library/BaseRngLib/AArch64/ArmRng.S b/MdePkg/Library/BaseRngLib/AArch64/ArmRng.S index 688abba68b9e..10de2ecca456 100644 --- a/MdePkg/Library/BaseRngLib/AArch64/ArmRng.S +++ b/MdePkg/Library/BaseRngLib/AArch64/ArmRng.S @@ -8,7 +8,7 @@ # #------------------------------------------------------------------------------ -#include "BaseRngLibInternals.h" +#include "ArmRng.h" .text .p2align 2 diff --git a/MdePkg/Library/BaseRngLib/AArch64/ArmRng.asm b/MdePkg/Library/BaseRngLib/AArch64/ArmRng.asm index 76f6e960ac11..c8666926c4db 100644 --- a/MdePkg/Library/BaseRngLib/AArch64/ArmRng.asm +++ b/MdePkg/Library/BaseRngLib/AArch64/ArmRng.asm @@ -8,7 +8,7 @@ ; ;------------------------------------------------------------------------------ -#include "BaseRngLibInternals.h" +#include "ArmRng.h" EXPORT ArmRndr AREA BaseLib_LowLevel, CODE, READONLY diff --git a/MdePkg/Library/BaseRngLib/AArch64/ArmRng.h b/MdePkg/Library/BaseRngLib/AArch64/ArmRng.h index 5937dd7deac2..12521a1a6f83 100644 --- a/MdePkg/Library/BaseRngLib/AArch64/ArmRng.h +++ b/MdePkg/Library/BaseRngLib/AArch64/ArmRng.h @@ -11,6 +11,8 @@ #ifndef ARM_RNG_H_ #define ARM_RNG_H_ +#include <AArch64/AArch64.h> + /** Generates a random number using RNDR. Returns TRUE on success; FALSE on failure. diff --git a/MdePkg/Library/BaseRngLib/BaseRngLibInternals.h b/MdePkg/Library/BaseRngLib/BaseRngLibInternals.h index 221f158066a7..3b5bdcf8a2e3 100644 --- a/MdePkg/Library/BaseRngLib/BaseRngLibInternals.h +++ b/MdePkg/Library/BaseRngLib/BaseRngLibInternals.h @@ -69,11 +69,4 @@ ArchIsRngSupported ( VOID ); -#if defined (MDE_CPU_AARCH64) - -// RNDR, Random Number -#define RNDR S3_3_C2_C4_0 - -#endif - #endif // BASE_RNGLIB_INTERNALS_H_ diff --git a/MdePkg/Library/BaseRngLib/Rand/RdRand.c b/MdePkg/Library/BaseRngLib/Rand/RdRand.c index 63ad43283d8d..d37808913875 100644 --- a/MdePkg/Library/BaseRngLib/Rand/RdRand.c +++ b/MdePkg/Library/BaseRngLib/Rand/RdRand.c @@ -2,6 +2,7 @@ Random number generator services that uses RdRand instruction access to provide high-quality random numbers. +Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.<BR> Copyright (c) 2023, Arm Limited. All rights reserved.<BR> Copyright (c) 2022, Pedro Falcato. All rights reserved.<BR> Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR> @@ -23,8 +24,6 @@ SPDX-License-Identifier: BSD-2-Clause-Patent // #define RDRAND_MASK BIT30 -STATIC BOOLEAN mRdRandSupported; - // // Intel SDM says 10 tries is good enough for reliable RDRAND usage. // @@ -124,20 +123,6 @@ BaseRngLibConstructor ( VOID ) { - UINT32 RegEcx; - - // - // Determine RDRAND support by examining bit 30 of the ECX register returned by - // CPUID. A value of 1 indicates that processor support RDRAND instruction. - // - AsmCpuid (1, 0, 0, &RegEcx, 0); - - mRdRandSupported = ((RegEcx & RDRAND_MASK) == RDRAND_MASK); - - if (mRdRandSupported) { - mRdRandSupported = TestRdRand (); - } - return EFI_SUCCESS; } @@ -156,7 +141,6 @@ ArchGetRandomNumber16 ( OUT UINT16 *Rand ) { - ASSERT (mRdRandSupported); return AsmRdRand16 (Rand); } @@ -175,7 +159,6 @@ ArchGetRandomNumber32 ( OUT UINT32 *Rand ) { - ASSERT (mRdRandSupported); return AsmRdRand32 (Rand); } @@ -194,7 +177,6 @@ ArchGetRandomNumber64 ( OUT UINT64 *Rand ) { - ASSERT (mRdRandSupported); return AsmRdRand64 (Rand); } @@ -211,7 +193,22 @@ ArchIsRngSupported ( VOID ) { - return mRdRandSupported; + BOOLEAN RdRandSupported; + UINT32 RegEcx; + + // + // Determine RDRAND support by examining bit 30 of the ECX register returned by + // CPUID. A value of 1 indicates that processor support RDRAND instruction. + // + AsmCpuid (1, 0, 0, &RegEcx, 0); + + RdRandSupported = ((RegEcx & RDRAND_MASK) == RDRAND_MASK); + + if (RdRandSupported) { + RdRandSupported = TestRdRand (); + } + + return RdRandSupported; } /** diff --git a/MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf b/MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf index 279263b04119..10b57e236d85 100644 --- a/MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf +++ b/MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf @@ -33,4 +33,5 @@ [LibraryClasses] BaseLib DebugLib + StackCheckLib diff --git a/MdePkg/Library/DxeRngLib/DxeRngLib.c b/MdePkg/Library/DxeRngLib/DxeRngLib.c index 2afce29d589f..c7c146e10c94 100644 --- a/MdePkg/Library/DxeRngLib/DxeRngLib.c +++ b/MdePkg/Library/DxeRngLib/DxeRngLib.c @@ -32,7 +32,7 @@ typedef struct { // These represent UEFI SPEC defined algorithms that should be supported by // the RNG protocol and are generally considered secure. // -static GLOBAL_REMOVE_IF_UNREFERENCED SECURE_RNG_ALGO_ARRAY mSecureHashAlgorithms[] = { +static SECURE_RNG_ALGO_ARRAY mSecureHashAlgorithms[] = { #ifdef MDE_CPU_AARCH64 { &gEfiRngAlgorithmArmRndr, // unspecified SP800-90A DRBG (through RNDR instr.) @@ -204,7 +204,10 @@ GenerateRandomNumberViaNist800Algorithm ( } } - if (!PcdGetBool (PcdEnforceSecureRngAlgorithms)) { + if (PcdGetBool (PcdEnforceSecureRngAlgorithms)) { + // Platform does not permit the use of the default (insecure) algorithm. + Status = EFI_SECURITY_VIOLATION; + } else { // If all the other methods have failed, use the default method from the RngProtocol Status = mRngProtocol->GetRNG (mRngProtocol, NULL, BufferSize, Buffer); DEBUG ((DEBUG_INFO, "%a: GetRNG algorithm default - Status = %r\n", __func__, Status)); diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/AArch64/DynamicCookieGcc.S b/MdePkg/Library/DynamicStackCookieEntryPointLib/AArch64/DynamicCookieGcc.S new file mode 100644 index 000000000000..f508f692d3f9 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/AArch64/DynamicCookieGcc.S @@ -0,0 +1,56 @@ +#------------------------------------------------------------------------------ +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# Module Name: +# +# DynamicCookie.S +# +# Abstract: +# +# Generates random number through the RNDR instruction on a 64-bit AARCH64 platform +# to store a random value in the GCC __stack_check_guard stack cookie. +# The first byte is 0'd to prevent string copy functions from clobbering +# the stack cookie. +# +# Notes: +# +# If RNDR fails, the build time static stack cookie value will be used instead. +# +#------------------------------------------------------------------------------ + +#include <AArch64/AArch64.h> + +.text +.p2align 2 + +GCC_ASM_IMPORT(__stack_chk_guard) +GCC_ASM_IMPORT(_CModuleEntryPoint) +GCC_ASM_EXPORT(_ModuleEntryPoint) + +#------------------------------------------------------------------------------ +# VOID +# EFIAPI +# _ModuleEntryPoint ( +# Parameters are passed through. +# ) +#------------------------------------------------------------------------------ +ASM_PFX(_ModuleEntryPoint): + AARCH64_BTI(c) + + mrs x9, ID_AA64ISAR0_EL1 // Read the AArch64 Instruction Set Attribute Register 0 + ubfx x9, x9, #60, #4 // Extract the RNDR bit field (bits 60-63) + cbz x9, c_entry // If RNDR is not supported, jump to c_entry + + mrs x9, RNDR // Generate a random number + b.eq c_entry // RNDR sets NZCV to 0b0100 on failure + // So if the zero flag is set, use the static stack guard + + and x9, x9, #0xFFFFFFFFFFFFFF00 // Zero the first byte of the random value + + adrp x8, ASM_PFX(__stack_chk_guard) // Load the page address of __stack_chk_guard + str x9, [x8, :lo12:ASM_PFX(__stack_chk_guard)] // Store the random value in __stack_chk_guard + +c_entry: + b ASM_PFX(_CModuleEntryPoint) // Jump to the C module entry point diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/DxeCore/DxeCoreEntryPoint.c b/MdePkg/Library/DynamicStackCookieEntryPointLib/DxeCore/DxeCoreEntryPoint.c new file mode 100644 index 000000000000..5118132bd2c3 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/DxeCore/DxeCoreEntryPoint.c @@ -0,0 +1,70 @@ +/** @file + Entry point to the DXE Core. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> + +#include <Library/DxeCoreEntryPoint.h> +#include <Library/DebugLib.h> +#include <Library/BaseLib.h> + +// +// Cache copy of HobList pointer. +// +VOID *gHobList = NULL; + +/** + The entry point of PE/COFF Image for the DXE Core. + + This function is the entry point for the DXE Core. This function is required to call + ProcessModuleEntryPointList() and ProcessModuleEntryPointList() is never expected to return. + The DXE Core is responsible for calling ProcessLibraryConstructorList() as soon as the EFI + System Table and the image handle for the DXE Core itself have been established. + If ProcessModuleEntryPointList() returns, then ASSERT() and halt the system. + + @param HobStart The pointer to the beginning of the HOB List passed in from the PEI Phase. + +**/ +VOID +EFIAPI +_CModuleEntryPoint ( + IN VOID *HobStart + ) +{ + // + // Cache a pointer to the HobList + // + gHobList = HobStart; + + // + // Call the DXE Core entry point + // + ProcessModuleEntryPointList (HobStart); + + // + // Should never return + // + ASSERT (FALSE); + CpuDeadLoop (); +} + +/** + Required by the EBC compiler and identical in functionality to _ModuleEntryPoint(). + + This function is required to call _ModuleEntryPoint() passing in HobStart. + + @param HobStart The pointer to the beginning of the HOB List passed in from the PEI Phase. + +**/ +VOID +EFIAPI +EfiMain ( + IN VOID *HobStart + ) +{ + _ModuleEntryPoint (HobStart); +} diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/DxeCore/DxeCoreEntryPoint.uni b/MdePkg/Library/DynamicStackCookieEntryPointLib/DxeCore/DxeCoreEntryPoint.uni new file mode 100644 index 000000000000..e6515659a9e0 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/DxeCore/DxeCoreEntryPoint.uni @@ -0,0 +1,16 @@ +// /** @file +// Module entry point library for DXE core. +// +// Module entry point library for DXE core. +// +// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR> +// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Module entry point library for DXE core" + +#string STR_MODULE_DESCRIPTION #language en-US "Module entry point library for DXE core." + diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/DxeCoreEntryPoint.inf b/MdePkg/Library/DynamicStackCookieEntryPointLib/DxeCoreEntryPoint.inf new file mode 100644 index 000000000000..1da877ae1d97 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/DxeCoreEntryPoint.inf @@ -0,0 +1,45 @@ +## @file +# Module entry point library for DXE core that dynamically updates the stack cookie. +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeCoreEntryPointDynamicInit + MODULE_UNI_FILE = DxeCore/DxeCoreEntryPoint.uni + FILE_GUID = FD044D85-1407-4043-B527-471F16ABD8C6 + MODULE_TYPE = DXE_CORE + VERSION_STRING = 1.0 + LIBRARY_CLASS = DxeCoreEntryPoint|DXE_CORE + + +# +# VALID_ARCHITECTURES = IA32 X64 AARCH64 +# + +[Sources] + DxeCore/DxeCoreEntryPoint.c + +[Sources.IA32] + IA32/DynamicCookieGcc.nasm | GCC + IA32/DynamicCookieMsvc.nasm | MSFT + +[Sources.X64] + X64/DynamicCookieGcc.nasm | GCC + X64/DynamicCookieMsvc.nasm | MSFT + +[Sources.AARCH64] + AArch64/DynamicCookieGcc.S | GCC + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + StackCheckLib diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/IA32/DynamicCookieGcc.nasm b/MdePkg/Library/DynamicStackCookieEntryPointLib/IA32/DynamicCookieGcc.nasm new file mode 100644 index 000000000000..c3c3e0e9931c --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/IA32/DynamicCookieGcc.nasm @@ -0,0 +1,63 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) Microsoft Corporation. +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; DynamicCookie.nasm +; +; Abstract: +; +; Generates random number through CPU RdRand instruction on a 32-bit platform +; to store a random value in the GCC __stack_check_guard stack cookie. +; The first byte is 0'd to prevent string copy functions from clobbering +; the stack cookie. +; +; Notes: +; +; If RdRand fails, the build time static stack cookie value will be used instead. +; +;------------------------------------------------------------------------------ + +SECTION .text + +extern ASM_PFX(__stack_chk_guard) +extern ASM_PFX(_CModuleEntryPoint) +global ASM_PFX(_ModuleEntryPoint) + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; _ModuleEntryPoint ( +; Parameters are passed through +; ); +;------------------------------------------------------------------------------ +global _ModuleEntryPoint +_ModuleEntryPoint: + push ebx + push ecx + push edx + + mov eax, 1 ; CPUID function 1 + cpuid + test ecx, 0x40000000 ; Check if the RdRand bit (bit 30) is set in ECX + jz c_entry ; If not set, jump to c_entry + + rdrand eax ; Use rdrand, getting a 32 bit value as on + ; IA32, __stack_chk_guard is a 32 bit value. + ; CF=1 if RN generated ok, otherwise CF=0 + jnc c_entry ; If the cmd fails, don't update __stack_chk_guard, we'll have to move forward + ; with the static value provided at build time. + + lea ebx, [ASM_PFX(__stack_chk_guard)] ; load the address of __stack_chk_guard into ebx + + xor ah, ah ; Zero a byte of the __stack_chk_guard value to protect against string functions + ; (such as strcpy like functions) clobbering past the canary + mov [ebx], eax ; Store our random value, with 0'd first byte to __stack_chk_guard + +c_entry: + pop edx + pop ecx + pop ebx + jmp ASM_PFX(_CModuleEntryPoint) diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/IA32/DynamicCookieMsvc.nasm b/MdePkg/Library/DynamicStackCookieEntryPointLib/IA32/DynamicCookieMsvc.nasm new file mode 100644 index 000000000000..2b8ec94fdbe9 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/IA32/DynamicCookieMsvc.nasm @@ -0,0 +1,63 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) Microsoft Corporation. +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; DynamicCookie.nasm +; +; Abstract: +; +; Generates random number through CPU RdRand instruction on a 32-bit platform +; to store a random value in the GCC __stack_check_guard stack cookie. +; The first byte is 0'd to prevent string copy functions from clobbering +; the stack cookie. +; +; Notes: +; +; If RdRand fails, the build time static stack cookie value will be used instead. +; +;------------------------------------------------------------------------------ + +SECTION .text + +extern ASM_PFX(__security_cookie) +extern ASM_PFX(_CModuleEntryPoint) +global ASM_PFX(_ModuleEntryPoint) + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; _ModuleEntryPoint ( +; Parameters are passed through +; ); +;------------------------------------------------------------------------------ +global _ModuleEntryPoint +_ModuleEntryPoint: + push ebx + push ecx + push edx + + mov eax, 1 ; CPUID function 1 + cpuid + test ecx, 0x40000000 ; Check if the RdRand bit (bit 30) is set in ECX + jz c_entry ; If not set, jump to c_entry + + rdrand eax ; Use rdrand, getting a 32 bit value as on + ; IA32, __security_cookie is a 32 bit value. + ; CF=1 if RN generated ok, otherwise CF=0 + jnc c_entry ; If the cmd fails, don't update __security_cookie, we'll have to move forward + ; with the static value provided at build time. + + lea ebx, [ASM_PFX(__security_cookie)] ; load the address of __stack_chk_guard into ebx + + xor ah, ah ; Zero a byte of the __security_cookie value to protect against string functions + ; (such as strcpy like functions) clobbering past the canary + mov [ebx], eax ; Store our random value, with 0'd first byte to __security_cookie + +c_entry: + pop edx + pop ecx + pop ebx + jmp ASM_PFX(_CModuleEntryPoint) diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmCore/X64/StandaloneMmCoreEntryPoint.c b/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmCore/X64/StandaloneMmCoreEntryPoint.c new file mode 100644 index 000000000000..204702727319 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmCore/X64/StandaloneMmCoreEntryPoint.c @@ -0,0 +1,65 @@ +/** @file + Entry point to the Standalone Mm Core. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiMm.h> + +#include <Library/StandaloneMmCoreEntryPoint.h> +#include <Library/DebugLib.h> +#include <Library/BaseLib.h> + +// +// Cache copy of HobList pointer. +// +VOID *gHobList = NULL; + +/** + The entry point of PE/COFF Image for the STANDALONE MM Core. + + This function is the entry point for the STANDALONE MM Core. This function is required to call + ProcessModuleEntryPointList() and ProcessModuleEntryPointList() is never expected to return. + The STANDALONE MM Core is responsible for calling ProcessLibraryConstructorList() as soon as the EFI + System Table and the image handle for the STANDALONE MM Core itself have been established. + If ProcessModuleEntryPointList() returns, then ASSERT() and halt the system. + + @param HobStart Pointer to the beginning of the HOB List passed in from the PEI Phase. + +**/ +VOID +EFIAPI +_CModuleEntryPoint ( + IN VOID *HobStart + ) +{ + // + // Cache a pointer to the HobList + // + gHobList = HobStart; + + // + // Call the Standalone MM Core entry point + // + ProcessModuleEntryPointList (HobStart); +} + +/** + Required by the EBC compiler and identical in functionality to _ModuleEntryPoint(). + + This function is required to call _ModuleEntryPoint() passing in HobStart. + + @param HobStart Pointer to the beginning of the HOB List passed in from the PEI Phase. + +**/ +VOID +EFIAPI +EfiMain ( + IN VOID *HobStart + ) +{ + _ModuleEntryPoint (HobStart); +} diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmCoreEntryPoint.inf b/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmCoreEntryPoint.inf new file mode 100644 index 000000000000..b82415e6b8fc --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmCoreEntryPoint.inf @@ -0,0 +1,38 @@ +## @file +# Module entry point library for StandaloneMm core that dynamically updates the stack cookie. +# The AARCH64 version of this library lives in ArmPkg. +# +# Copyright (c) 2017 - 2021, Arm Ltd. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x0001001A + BASE_NAME = StandaloneMmCoreEntryPointDynamicInit + FILE_GUID = 490073A1-4DBC-4E9E-B30D-A4204139FC5F + MODULE_TYPE = MM_CORE_STANDALONE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x00010032 + LIBRARY_CLASS = StandaloneMmCoreEntryPoint|MM_CORE_STANDALONE + +# +# VALID_ARCHITECTURES = X64 +# + +[Sources.X64] + StandaloneMmCore/X64/StandaloneMmCoreEntryPoint.c + +[Sources.X64] + X64/DynamicCookieGcc.nasm | GCC + X64/DynamicCookieMsvc.nasm | MSFT + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + StackCheckLib diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmDriver/StandaloneMmDriverEntryPoint.c b/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmDriver/StandaloneMmDriverEntryPoint.c new file mode 100644 index 000000000000..51df419953d5 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmDriver/StandaloneMmDriverEntryPoint.c @@ -0,0 +1,135 @@ +/** @file + Entry point to a Standalone MM driver. + +Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2016 - 2018, ARM Ltd. All rights reserved.<BR> +Copyright (c) 2018, Linaro, Limited. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiMm.h> + +#include <Protocol/LoadedImage.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/MmServicesTableLib.h> +#include <Library/StandaloneMmDriverEntryPoint.h> + +/** + Unloads an image from memory. + + This function is a callback that a driver registers to do cleanup + when the UnloadImage boot service function is called. + + @param ImageHandle The handle to the image to unload. + + @return Status returned by all unload(). + +**/ +EFI_STATUS +EFIAPI +_DriverUnloadHandler ( + EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + + // + // If an UnloadImage() handler is specified, then call it + // + Status = ProcessModuleUnloadList (ImageHandle); + + // + // If the driver specific unload handler does not return an error, then call all of the + // library destructors. If the unload handler returned an error, then the driver can not be + // unloaded, and the library destructors should not be called + // + if (!EFI_ERROR (Status)) { + ProcessLibraryDestructorList (ImageHandle, gMmst); + } + + // + // Return the status from the driver specific unload handler + // + return Status; +} + +/** + The entry point of PE/COFF Image for a Standalone MM Driver. + + This function is the entry point for a Standalone MM Driver. + This function must call ProcessLibraryConstructorList() and + ProcessModuleEntryPointList(). + If the return status from ProcessModuleEntryPointList() + is an error status, then ProcessLibraryDestructorList() must be called. + The return value from ProcessModuleEntryPointList() is returned. + If _gMmRevision is not zero and SystemTable->Hdr.Revision is + less than _gMmRevision, then return EFI_INCOMPATIBLE_VERSION. + + @param ImageHandle The image handle of the Standalone MM Driver. + @param MmSystemTable A pointer to the MM System Table. + + @retval EFI_SUCCESS The Standalone MM Driver exited normally. + @retval EFI_INCOMPATIBLE_VERSION _gMmRevision is greater than + MmSystemTable->Hdr.Revision. + @retval Other Return value from + ProcessModuleEntryPointList(). + +**/ +EFI_STATUS +EFIAPI +_CModuleEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN IN EFI_MM_SYSTEM_TABLE *MmSystemTable + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + + if (_gMmRevision != 0) { + // + // Make sure that the MM spec revision of the platform + // is >= MM spec revision of the driver + // + if (MmSystemTable->Hdr.Revision < _gMmRevision) { + return EFI_INCOMPATIBLE_VERSION; + } + } + + // + // Call constructor for all libraries + // + ProcessLibraryConstructorList (ImageHandle, MmSystemTable); + + // + // Install unload handler... + // + if (_gDriverUnloadImageCount != 0) { + Status = gMmst->MmHandleProtocol ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + ASSERT_EFI_ERROR (Status); + LoadedImage->Unload = _DriverUnloadHandler; + } + + // + // Call the driver entry point + // + Status = ProcessModuleEntryPointList (ImageHandle, MmSystemTable); + + // + // If all of the drivers returned errors, then invoke all of the library destructors + // + if (EFI_ERROR (Status)) { + ProcessLibraryDestructorList (ImageHandle, MmSystemTable); + } + + // + // Return the cumulative return status code from all of the driver entry points + // + return Status; +} diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmDriver/StandaloneMmDriverEntryPoint.uni b/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmDriver/StandaloneMmDriverEntryPoint.uni new file mode 100644 index 000000000000..de36debd74ec --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmDriver/StandaloneMmDriverEntryPoint.uni @@ -0,0 +1,17 @@ +// /** @file +// +// Module entry point library for standalone MM driver +// +// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR> +// Copyright (c) 2016 - 2018, ARM Limited. All rights reserved.<BR> +// Copyright (c) 2018, Linaro, Limited. All rights reserved.<BR> +// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Module entry point library for standalone MM driver" + +#string STR_MODULE_DESCRIPTION #language en-US "Module entry point library for standalone MM driver." + diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmDriverEntryPoint.inf b/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmDriverEntryPoint.inf new file mode 100644 index 000000000000..a5cb42518c1b --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmDriverEntryPoint.inf @@ -0,0 +1,53 @@ +## @file +# Module entry point library for Standalone MM drivers that dynamically updates the stack cookie. +# +# Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2016-2018, ARM Ltd. All rights reserved.<BR> +# Copyright (c) 2018, Linaro, Limited. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = StandaloneMmDriverEntryPointDynamicInit + MODULE_UNI_FILE = StandaloneMmDriver/StandaloneMmDriverEntryPoint.uni + FILE_GUID = 28CBCD87-2FEE-4D46-BB5C-B37732BBEEB1 + MODULE_TYPE = MM_STANDALONE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x00010032 + LIBRARY_CLASS = StandaloneMmDriverEntryPoint|MM_STANDALONE + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 AARCH64 +# + +[Sources] + StandaloneMmDriver/StandaloneMmDriverEntryPoint.c + +[Sources.IA32] + IA32/DynamicCookieGcc.nasm | GCC + IA32/DynamicCookieMsvc.nasm | MSFT + +[Sources.X64] + X64/DynamicCookieGcc.nasm | GCC + X64/DynamicCookieMsvc.nasm | MSFT + +[Sources.AARCH64] + AArch64/DynamicCookieGcc.S | GCC + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + MmServicesTableLib + StackCheckLib + +[Protocols] + gEfiLoadedImageProtocolGuid ## SOMETIMES_CONSUMES diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiApplication/ApplicationEntryPoint.c b/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiApplication/ApplicationEntryPoint.c new file mode 100644 index 000000000000..81b9b55130e3 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiApplication/ApplicationEntryPoint.c @@ -0,0 +1,112 @@ +/** @file + Entry point library instance to a UEFI application. + +Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi.h> +#include <Library/UefiApplicationEntryPoint.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> + +/** + Entry point to UEFI Application. + + This function is the entry point for a UEFI Application. This function must call + ProcessLibraryConstructorList(), ProcessModuleEntryPointList(), and ProcessLibraryDestructorList(). + The return value from ProcessModuleEntryPointList() is returned. + If _gUefiDriverRevision is not zero and SystemTable->Hdr.Revision is less than _gUefiDriverRevison, + then return EFI_INCOMPATIBLE_VERSION. + + @param ImageHandle The image handle of the UEFI Application. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The UEFI Application exited normally. + @retval EFI_INCOMPATIBLE_VERSION _gUefiDriverRevision is greater than SystemTable->Hdr.Revision. + @retval Other Return value from ProcessModuleEntryPointList(). + +**/ +EFI_STATUS +EFIAPI +_CModuleEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + if (_gUefiDriverRevision != 0) { + // + // Make sure that the EFI/UEFI spec revision of the platform is >= EFI/UEFI spec revision of the application. + // + if (SystemTable->Hdr.Revision < _gUefiDriverRevision) { + return EFI_INCOMPATIBLE_VERSION; + } + } + + // + // Call constructor for all libraries. + // + ProcessLibraryConstructorList (ImageHandle, SystemTable); + + // + // Call the module's entry point + // + Status = ProcessModuleEntryPointList (ImageHandle, SystemTable); + + // + // Process destructor for all libraries. + // + ProcessLibraryDestructorList (ImageHandle, SystemTable); + + // + // Return the return status code from the driver entry point + // + return Status; +} + +/** + Invokes the library destructors for all dependent libraries and terminates + the UEFI Application. + + This function calls ProcessLibraryDestructorList() and the EFI Boot Service Exit() + with a status specified by Status. + + @param Status Status returned by the application that is exiting. + +**/ +VOID +EFIAPI +Exit ( + IN EFI_STATUS Status + ) + +{ + ProcessLibraryDestructorList (gImageHandle, gST); + + gBS->Exit (gImageHandle, Status, 0, NULL); +} + +/** + Required by the EBC compiler and identical in functionality to _ModuleEntryPoint(). + + @param ImageHandle The image handle of the UEFI Application. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The UEFI Application exited normally. + @retval EFI_INCOMPATIBLE_VERSION _gUefiDriverRevision is greater than SystemTable->Hdr.Revision. + @retval Other Return value from ProcessModuleEntryPointList(). + +**/ +EFI_STATUS +EFIAPI +EfiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return _ModuleEntryPoint (ImageHandle, SystemTable); +} diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiApplication/UefiApplicationEntryPoint.uni b/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiApplication/UefiApplicationEntryPoint.uni new file mode 100644 index 000000000000..168ce7469bb7 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiApplication/UefiApplicationEntryPoint.uni @@ -0,0 +1,16 @@ +// /** @file +// Module entry point library for UEFI Application. +// +// Module entry point library for UEFI Application. +// +// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR> +// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Module entry point library for UEFI Application" + +#string STR_MODULE_DESCRIPTION #language en-US "Module entry point library for UEFI Application." + diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiApplicationEntryPoint.inf b/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiApplicationEntryPoint.inf new file mode 100644 index 000000000000..1a82fc498de1 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiApplicationEntryPoint.inf @@ -0,0 +1,45 @@ +## @file +# Module entry point library for UEFI Application that dynamically updates the stack cookie. +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UefiApplicationEntryPointDynamicInit + MODULE_UNI_FILE = UefiApplication/UefiApplicationEntryPoint.uni + FILE_GUID = 755B9094-E5AF-4E5B-BE33-D430CDE2C5D2 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + LIBRARY_CLASS = UefiApplicationEntryPoint|UEFI_APPLICATION + +# +# VALID_ARCHITECTURES = IA32 X64 AARCH64 +# + +[Sources] + UefiApplication/ApplicationEntryPoint.c + +[Sources.IA32] + IA32/DynamicCookieGcc.nasm | GCC + IA32/DynamicCookieMsvc.nasm | MSFT + +[Sources.X64] + X64/DynamicCookieGcc.nasm | GCC + X64/DynamicCookieMsvc.nasm | MSFT + +[Sources.AARCH64] + AArch64/DynamicCookieGcc.S | GCC + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + DebugLib + BaseLib + StackCheckLib diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiDriver/DriverEntryPoint.c b/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiDriver/DriverEntryPoint.c new file mode 100644 index 000000000000..ad17e0d1c616 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiDriver/DriverEntryPoint.c @@ -0,0 +1,162 @@ +/** @file + Entry point to a EFI/DXE driver. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi.h> + +#include <Protocol/LoadedImage.h> + +#include <Library/UefiDriverEntryPoint.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> + +/** + Unloads an image from memory. + + This function is a callback that a driver registers to do cleanup + when the UnloadImage boot service function is called. + + @param ImageHandle The handle to the image to unload. + + @return Status returned by all unload(). + +**/ +EFI_STATUS +EFIAPI +_DriverUnloadHandler ( + EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + + // + // If an UnloadImage() handler is specified, then call it + // + Status = ProcessModuleUnloadList (ImageHandle); + + // + // If the driver specific unload handler does not return an error, then call all of the + // library destructors. If the unload handler returned an error, then the driver can not be + // unloaded, and the library destructors should not be called + // + if (!EFI_ERROR (Status)) { + ProcessLibraryDestructorList (ImageHandle, gST); + } + + // + // Return the status from the driver specific unload handler + // + return Status; +} + +/** + The entry point of PE/COFF Image for a DXE Driver, DXE Runtime Driver, DXE SMM + Driver, or UEFI Driver. + + This function is the entry point for a DXE Driver, DXE Runtime Driver, DXE SMM Driver, + or UEFI Driver. This function must call ProcessLibraryConstructorList() and + ProcessModuleEntryPointList(). If the return status from ProcessModuleEntryPointList() + is an error status, then ProcessLibraryDestructorList() must be called. The return + value from ProcessModuleEntryPointList() is returned. If _gDriverUnloadImageCount + is greater than zero, then an unload handler must be registered for this image + and the unload handler must invoke ProcessModuleUnloadList(). + If _gUefiDriverRevision is not zero and SystemTable->Hdr.Revision is less than + _gUefiDriverRevison, then return EFI_INCOMPATIBLE_VERSION. + + + @param ImageHandle The image handle of the DXE Driver, DXE Runtime Driver, + DXE SMM Driver, or UEFI Driver. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The DXE Driver, DXE Runtime Driver, DXE SMM + Driver, or UEFI Driver exited normally. + @retval EFI_INCOMPATIBLE_VERSION _gUefiDriverRevision is greater than + SystemTable->Hdr.Revision. + @retval Other Return value from ProcessModuleEntryPointList(). + +**/ +EFI_STATUS +EFIAPI +_CModuleEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + + if (_gUefiDriverRevision != 0) { + // + // Make sure that the EFI/UEFI spec revision of the platform is >= EFI/UEFI spec revision of the driver + // + if (SystemTable->Hdr.Revision < _gUefiDriverRevision) { + return EFI_INCOMPATIBLE_VERSION; + } + } + + // + // Call constructor for all libraries + // + ProcessLibraryConstructorList (ImageHandle, SystemTable); + + // + // Install unload handler... + // + if (_gDriverUnloadImageCount != 0) { + Status = gBS->HandleProtocol ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + ASSERT_EFI_ERROR (Status); + LoadedImage->Unload = _DriverUnloadHandler; + } + + // + // Call the driver entry point + // + Status = ProcessModuleEntryPointList (ImageHandle, SystemTable); + + // + // If all of the drivers returned errors, then invoke all of the library destructors + // + if (EFI_ERROR (Status)) { + ProcessLibraryDestructorList (ImageHandle, SystemTable); + } + + // + // Return the cummalative return status code from all of the driver entry points + // + return Status; +} + +/** + Required by the EBC compiler and identical in functionality to _ModuleEntryPoint(). + + This function is required to call _ModuleEntryPoint() passing in ImageHandle, + and SystemTable. + + @param ImageHandle The image handle of the DXE Driver, DXE Runtime Driver, DXE + SMM Driver, or UEFI Driver. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The DXE Driver, DXE Runtime Driver, DXE SMM + Driver, or UEFI Driver exited normally. + @retval EFI_INCOMPATIBLE_VERSION _gUefiDriverRevision is greater than + SystemTable->Hdr.Revision. + @retval Other Return value from ProcessModuleEntryPointList(). +**/ +EFI_STATUS +EFIAPI +EfiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return _ModuleEntryPoint (ImageHandle, SystemTable); +} diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiDriver/UefiDriverEntryPoint.uni b/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiDriver/UefiDriverEntryPoint.uni new file mode 100644 index 000000000000..9d820b25f063 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiDriver/UefiDriverEntryPoint.uni @@ -0,0 +1,16 @@ +// /** @file +// Module entry point library for UEFI driver, DXE driver and SMM driver. +// +// Module entry point library for UEFI driver, DXE driver and SMM driver. +// +// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR> +// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Module entry point library for UEFI driver, DXE driver and SMM driver" + +#string STR_MODULE_DESCRIPTION #language en-US "Module entry point library for UEFI driver, DXE driver and SMM driver." + diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiDriverEntryPoint.inf b/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiDriverEntryPoint.inf new file mode 100644 index 000000000000..522bcd930c23 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/UefiDriverEntryPoint.inf @@ -0,0 +1,68 @@ +## @file +# Module entry point library for UEFI driver, DXE driver and SMM driver that dynamically sets the stack cookie. +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UefiDriverEntryPointDynamicInit + MODULE_UNI_FILE = UefiDriver/UefiDriverEntryPoint.uni + FILE_GUID = 900238F9-1421-4596-9548-A1BF58C97693 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = UefiDriverEntryPoint|DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_DRIVER SMM_CORE DXE_SMM_DRIVER + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 AARCH64 +# + +[Sources] + UefiDriver/DriverEntryPoint.c + +[Packages] + MdePkg/MdePkg.dec + +[Sources.IA32] + IA32/DynamicCookieGcc.nasm | GCC + IA32/DynamicCookieMsvc.nasm | MSFT + +[Sources.X64] + X64/DynamicCookieGcc.nasm | GCC + X64/DynamicCookieMsvc.nasm | MSFT + +[Sources.AARCH64] + AArch64/DynamicCookieGcc.S | GCC + +[LibraryClasses] + UefiBootServicesTableLib + DebugLib + BaseLib + StackCheckLib + +[Protocols] + gEfiLoadedImageProtocolGuid ## SOMETIMES_CONSUMES + +# +# For UEFI drivers, these architectural protocols defined in PI 1.0 spec need +# to be appended and merged to the final dependency section. +# +[Depex.common.UEFI_DRIVER] + gEfiBdsArchProtocolGuid AND + gEfiCpuArchProtocolGuid AND + gEfiMetronomeArchProtocolGuid AND + gEfiMonotonicCounterArchProtocolGuid AND + gEfiRealTimeClockArchProtocolGuid AND + gEfiResetArchProtocolGuid AND + gEfiRuntimeArchProtocolGuid AND + gEfiSecurityArchProtocolGuid AND + gEfiTimerArchProtocolGuid AND + gEfiVariableWriteArchProtocolGuid AND + gEfiVariableArchProtocolGuid AND + gEfiWatchdogTimerArchProtocolGuid diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/X64/DynamicCookieGcc.nasm b/MdePkg/Library/DynamicStackCookieEntryPointLib/X64/DynamicCookieGcc.nasm new file mode 100644 index 000000000000..782e8bbb791a --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/X64/DynamicCookieGcc.nasm @@ -0,0 +1,63 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) Microsoft Corporation. +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; DynamicCookie.nasm +; +; Abstract: +; +; Generates random number through CPU RdRand instruction on 64-bit platform +; to store a random value in the GCC __stack_check_guard stack cookie. +; The first byte is 0'd to prevent string copy functions from clobbering +; the stack cookie. +; +; Notes: +; +; If RdRand fails, the build time static stack cookie value will be used instead. +; +;------------------------------------------------------------------------------ + +DEFAULT REL +SECTION .text + +extern ASM_PFX(__stack_chk_guard) +extern ASM_PFX(_CModuleEntryPoint) + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; _ModuleEntryPoint ( +; Parameters are passed through. TODO: Make sure there are only two args on X64 +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(_ModuleEntryPoint) +ASM_PFX(_ModuleEntryPoint): + push rbx + push rcx + push rdx + + mov eax, 1 ; Set eax to 1 to get feature information + cpuid ; Call cpuid + test ecx, 0x40000000 ; Test the rdrand bit (bit 30) in ecx + jz c_entry ; If rdrand is not supported, jump to c_entry + + rdrand rax ; Call rdrand functionality here, getting a 64 bit value as on + ; X64, __stack_chk_guard is a 64 bit value. + ; CF=1 if RN generated ok, otherwise CF=0 + jnc c_entry ; If the cmd fails, don't, update __stack_chk_guard, we'll have to move forward + ; with the static value provided at build time. + + lea rbx, [rel ASM_PFX(__stack_chk_guard)] ; load the address of __stack_check_guard into rbx + + xor ah, ah ; Zero a byte of the __stack_chk_guard value to protect against string functions + ; (such as strcpy like functions) clobbering past the canary + mov [rbx], rax ; Store our random value, with 0'd first byte to __stack_chk_guard + +c_entry: + pop rdx + pop rcx + pop rbx + jmp ASM_PFX(_CModuleEntryPoint) diff --git a/MdePkg/Library/DynamicStackCookieEntryPointLib/X64/DynamicCookieMsvc.nasm b/MdePkg/Library/DynamicStackCookieEntryPointLib/X64/DynamicCookieMsvc.nasm new file mode 100644 index 000000000000..bd290f712634 --- /dev/null +++ b/MdePkg/Library/DynamicStackCookieEntryPointLib/X64/DynamicCookieMsvc.nasm @@ -0,0 +1,63 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) Microsoft Corporation. +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; DynamicCookie.nasm +; +; Abstract: +; +; Generates random number through CPU RdRand instruction on 64-bit platform +; to store a random value in the GCC __stack_check_guard stack cookie. +; The first byte is 0'd to prevent string copy functions from clobbering +; the stack cookie. +; +; Notes: +; +; If RdRand fails, the build time static stack cookie value will be used instead. +; +;------------------------------------------------------------------------------ + +DEFAULT REL +SECTION .text + +extern ASM_PFX(__security_cookie) +extern ASM_PFX(_CModuleEntryPoint) + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; _ModuleEntryPoint ( +; Parameters are passed through. TODO: Make sure there are only two args on X64 +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(_ModuleEntryPoint) +ASM_PFX(_ModuleEntryPoint): + push rbx + push rcx + push rdx + + mov eax, 1 ; Set eax to 1 to get feature information + cpuid ; Call cpuid + test ecx, 0x40000000 ; Test the rdrand bit (bit 30) in ecx + jz c_entry ; If rdrand is not supported, jump to c_entry + + rdrand rax ; Call rdrand functionality here, getting a 64 bit value as on + ; X64, __stack_chk_guard is a 64 bit value. + ; CF=1 if RN generated ok, otherwise CF=0 + jnc c_entry ; If the cmd fails, don't, update __stack_chk_guard, we'll have to move forward + ; with the static value provided at build time. + + lea rbx, [rel ASM_PFX(__security_cookie)] ; load the address of __stack_check_guard into rbx + + xor ah, ah ; Zero a byte of the __stack_chk_guard value to protect against string functions + ; (such as strcpy like functions) clobbering past the canary + mov [rbx], rax ; Store our random value, with 0'd first byte to __stack_chk_guard + +c_entry: + pop rdx + pop rcx + pop rbx + jmp ASM_PFX(_CModuleEntryPoint) diff --git a/MdePkg/Library/PeiCoreEntryPoint/PeiCoreEntryPoint.inf b/MdePkg/Library/PeiCoreEntryPoint/PeiCoreEntryPoint.inf index 163daa67afd3..02d1c582a114 100644 --- a/MdePkg/Library/PeiCoreEntryPoint/PeiCoreEntryPoint.inf +++ b/MdePkg/Library/PeiCoreEntryPoint/PeiCoreEntryPoint.inf @@ -31,4 +31,4 @@ [LibraryClasses] BaseLib DebugLib - + StackCheckLib diff --git a/MdePkg/Library/PeiRngLib/PeiRngLib.c b/MdePkg/Library/PeiRngLib/PeiRngLib.c new file mode 100644 index 000000000000..d6c957327c6b --- /dev/null +++ b/MdePkg/Library/PeiRngLib/PeiRngLib.c @@ -0,0 +1,229 @@ +/** @file + RNG library instance that uses the Random Number Generator (RNG) PPI to provide + random numbers. + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include <PiPei.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/PeiServicesLib.h> +#include <Ppi/Rng.h> + +/** + Generates a random number via the NIST 800-9A algorithm. Refer to + http://csrc.nist.gov/groups/STM/cavp/documents/drbg/DRBGVS.pdf for more information. + + @param[out] Buffer Buffer to receive the random number. + @param[in] BufferSize Number of bytes in Buffer. + + @retval EFI_SUCCESS or underlying failure code. + +**/ +STATIC +EFI_STATUS +GenerateRandomNumberViaNist800Algorithm ( + OUT UINT8 *Buffer, + IN UINTN BufferSize + ) +{ + EFI_STATUS Status; + RNG_PPI *RngPpi; + + RngPpi = NULL; + + if (Buffer == NULL) { + DEBUG ((DEBUG_ERROR, "%a: Buffer == NULL.\n", __func__)); + return EFI_INVALID_PARAMETER; + } + + Status = PeiServicesLocatePpi (&gEfiRngPpiGuid, 0, NULL, (VOID **)&RngPpi); + if (EFI_ERROR (Status) || (RngPpi == NULL)) { + DEBUG ((DEBUG_ERROR, "%a: Could not locate RNG PPI, Status = %r\n", __func__, Status)); + return Status; + } + + Status = RngPpi->GetRNG (RngPpi, &gEfiRngAlgorithmSp80090Ctr256Guid, BufferSize, Buffer); + DEBUG ((DEBUG_INFO, "%a: GetRNG algorithm CTR-256 - Status = %r\n", __func__, Status)); + if (!EFI_ERROR (Status)) { + return Status; + } + + Status = RngPpi->GetRNG (RngPpi, &gEfiRngAlgorithmSp80090Hmac256Guid, BufferSize, Buffer); + DEBUG ((DEBUG_INFO, "%a: GetRNG algorithm HMAC-256 - Status = %r\n", __func__, Status)); + if (!EFI_ERROR (Status)) { + return Status; + } + + Status = RngPpi->GetRNG (RngPpi, &gEfiRngAlgorithmSp80090Hash256Guid, BufferSize, Buffer); + DEBUG ((DEBUG_INFO, "%a: GetRNG algorithm Hash-256 - Status = %r\n", __func__, Status)); + if (!EFI_ERROR (Status)) { + return Status; + } + + Status = RngPpi->GetRNG (RngPpi, &gEfiRngAlgorithmRaw, BufferSize, Buffer); + DEBUG ((DEBUG_INFO, "%a: GetRNG algorithm Raw - Status = %r\n", __func__, Status)); + if (!EFI_ERROR (Status)) { + return Status; + } + + // If all the other methods have failed, use the default method from the RngPpi + Status = RngPpi->GetRNG (RngPpi, NULL, BufferSize, Buffer); + DEBUG ((DEBUG_INFO, "%a: GetRNG algorithm default - Status = %r\n", __func__, Status)); + if (!EFI_ERROR (Status)) { + return Status; + } + + // If we get to this point, we have failed + DEBUG ((DEBUG_ERROR, "%a: GetRNG() failed, staus = %r\n", __func__, Status)); + + return Status; +} + +/** + Generates a 16-bit random number. + + if Rand is NULL, return FALSE. + + @param[out] Rand Buffer pointer to store the 16-bit random value. + + @retval TRUE Random number generated successfully. + @retval FALSE Failed to generate the random number. + +**/ +BOOLEAN +EFIAPI +GetRandomNumber16 ( + OUT UINT16 *Rand + ) +{ + EFI_STATUS Status; + + if (Rand == NULL) { + return FALSE; + } + + Status = GenerateRandomNumberViaNist800Algorithm ((UINT8 *)Rand, sizeof (UINT16)); + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; +} + +/** + Generates a 32-bit random number. + + if Rand is NULL, return FALSE. + + @param[out] Rand Buffer pointer to store the 32-bit random value. + + @retval TRUE Random number generated successfully. + @retval FALSE Failed to generate the random number. + +**/ +BOOLEAN +EFIAPI +GetRandomNumber32 ( + OUT UINT32 *Rand + ) +{ + EFI_STATUS Status; + + if (Rand == NULL) { + return FALSE; + } + + Status = GenerateRandomNumberViaNist800Algorithm ((UINT8 *)Rand, sizeof (UINT32)); + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; +} + +/** + Generates a 64-bit random number. + + if Rand is NULL, return FALSE. + + @param[out] Rand Buffer pointer to store the 64-bit random value. + + @retval TRUE Random number generated successfully. + @retval FALSE Failed to generate the random number. + +**/ +BOOLEAN +EFIAPI +GetRandomNumber64 ( + OUT UINT64 *Rand + ) +{ + EFI_STATUS Status; + + if (Rand == NULL) { + return FALSE; + } + + Status = GenerateRandomNumberViaNist800Algorithm ((UINT8 *)Rand, sizeof (UINT64)); + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; +} + +/** + Generates a 128-bit random number. + + if Rand is NULL, return FALSE. + + @param[out] Rand Buffer pointer to store the 128-bit random value. + + @retval TRUE Random number generated successfully. + @retval FALSE Failed to generate the random number. + +**/ +BOOLEAN +EFIAPI +GetRandomNumber128 ( + OUT UINT64 *Rand + ) +{ + EFI_STATUS Status; + + if (Rand == NULL) { + return FALSE; + } + + Status = GenerateRandomNumberViaNist800Algorithm ((UINT8 *)Rand, 2 * sizeof (UINT64)); + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; +} + +/** + Get a GUID identifying the RNG algorithm implementation. + + @param [out] RngGuid If success, contains the GUID identifying + the RNG algorithm implementation. + + @retval EFI_SUCCESS Success. + @retval EFI_UNSUPPORTED Not supported. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +GetRngGuid ( + GUID *RngGuid + ) +{ + // Similar to DxeRngLib, EFI_UNSUPPORTED is returned for this library instance since it is unknown + // which exact algorithm may be used for a given request. + + return EFI_UNSUPPORTED; +} diff --git a/MdePkg/Library/PeiRngLib/PeiRngLib.inf b/MdePkg/Library/PeiRngLib/PeiRngLib.inf new file mode 100644 index 000000000000..1588366eebff --- /dev/null +++ b/MdePkg/Library/PeiRngLib/PeiRngLib.inf @@ -0,0 +1,41 @@ +## @file +# PPI-based Instance of RNG (Random Number Generator) Library. +# +# This library instance requires a RNG PPI to be produced so that the module may use +# it for RNG operations. A RNG PPI DEPEX will be placed on the module. +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = PeiRngLib + MODULE_UNI_FILE = PeiRngLib.uni + FILE_GUID = FF240232-C25D-4277-AB81-D6B0C51F2D25 + VERSION_STRING = 1.0 + MODULE_TYPE = PEIM + LIBRARY_CLASS = RngLib | PEIM + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + PeiServicesLib + +[Guids] + gEfiRngAlgorithmSp80090Ctr256Guid + gEfiRngAlgorithmSp80090Hash256Guid + gEfiRngAlgorithmSp80090Hmac256Guid + gEfiRngAlgorithmRaw + +[Sources] + PeiRngLib.c + +[Ppis] + gEfiRngPpiGuid ## CONSUMES + +[Depex] + gEfiRngPpiGuid diff --git a/MdePkg/Library/PeiRngLib/PeiRngLib.uni b/MdePkg/Library/PeiRngLib/PeiRngLib.uni new file mode 100644 index 000000000000..8aea8ab7415f --- /dev/null +++ b/MdePkg/Library/PeiRngLib/PeiRngLib.uni @@ -0,0 +1,12 @@ +// @file +// PEI Instance of the RNG (Random Number Generator) Library. +// +// A RngLib instance that uses the RNG PPI to provide random numbers. +// +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: BSD-2-Clause-Patent +// + +#string STR_MODULE_ABSTRACT #language en-US "Instance of the RNG Library for PEI" + +#string STR_MODULE_DESCRIPTION #language en-US "A library that uses the RNG PPI to provide random numbers" diff --git a/MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf b/MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf index 8d11a2b35f6c..dee997aa3ca4 100644 --- a/MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf +++ b/MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf @@ -31,4 +31,4 @@ [LibraryClasses] DebugLib - + StackCheckLib diff --git a/MdePkg/Library/SmmPciExpressLib/SmmPciExpressLib.inf b/MdePkg/Library/SmmPciExpressLib/SmmPciExpressLib.inf index 523a3e1d9f19..b8ebf3303d61 100644 --- a/MdePkg/Library/SmmPciExpressLib/SmmPciExpressLib.inf +++ b/MdePkg/Library/SmmPciExpressLib/SmmPciExpressLib.inf @@ -32,6 +32,7 @@ PcdLib DebugLib IoLib + UefiBootServicesTableLib [Pcd] gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress ## CONSUMES diff --git a/MdePkg/Library/StackCheckLib/Readme.md b/MdePkg/Library/StackCheckLib/Readme.md index 6558c222cd24..2d3d34a8ef06 100644 --- a/MdePkg/Library/StackCheckLib/Readme.md +++ b/MdePkg/Library/StackCheckLib/Readme.md @@ -1,12 +1,12 @@ -# StackCheckLib +# StackCheckLib Overview ## Table of Contents - [StackCheckLib](#stackchecklib) - [Table of Contents](#table-of-contents) - [Introduction and Library Instances](#introduction-and-library-instances) - - [StackCheckLibStaticInit](#stackchecklibstaticinit) - - [StackCheckLibDynamicInit](#stackchecklibdynamicinit) + - [StackCheckLib](#stackchecklib) + - [DynamicStackCookieEntryPointLib](#dynamicstackcookieentrypointlib) - [StackCheckLibNull](#stackchecklibnull) - [How Failures are Handled](#how-failures-are-handled) - [Debugging Stack Cookie Check Failures](#debugging-stack-cookie-check-failures) @@ -15,44 +15,79 @@ ## Introduction and Library Instances `StackCheckLib` contains the required functionality for initializing the stack cookie -value, checking the value, and triggering an interrupt when a mismatch occurs. -The stack cookie is a random value placed on the stack between the stack variables -and the return address so that continuously writing past the stack variables will -cause the stack cookie to be overwritten. Before the function returns, the stack -cookie value will be checked and if there is a mismatch then `StackCheckLib` handles -the failure. +value (based on a randomly generated value during build time), checking the value, +and triggering an interrupt when a mismatch occurs. The stack cookie is a random value +placed on the stack between the stack variables and the return address so that +continuously writing past the stack variables will cause the stack cookie to be +overwritten. Before the function returns, the stack cookie value will be checked and +if there is a mismatch then `StackCheckLib` handles the failure. Because UEFI doesn't use the C runtime libraries provided by MSVC, the stack check code is written in assembly within this library. GCC and Clang compilers have built-in support for stack cookie checking, so this library only handles failures. -### StackCheckLibStaticInit - -`StackCheckLibStaticInit` is an instance of `StackCheckLib` which does not update the -stack cookie value for the module at runtime. It's always preferable to use -`StackCheckLibDynamicInit` for improved security but there are cases where the stack -cookie global cannot be written to such as in execute-in-place (XIP) modules and during -the Cache-as-RAM (CAR) phase of the boot process. The stack cookie value is initialized -at compile time via updates to the AutoGen process. Each module will define -`STACK_COOKIE_VALUE` which is used for the module stack cookie value. - -### StackCheckLibDynamicInit - -This section is future work. The below is the proposed instance. - -`StackCheckLibDynamicInit` is an instance of `StackCheckLib` which updates the stack -cookie value for the module at runtime. This is the preferred method for stack cookie -initialization as it provides improved security. The stack cookie value is initialized -at runtime by calling `GetRandomNumber32()` or `GetRandomNumber64()` to generate a random -value via the platform's random number generator protocol. If the random number generator -returns an error, then the value will still have the build-time randomized value to fall -back on. +The stack cookie value is initialized at compile time via updates to the AutoGen process. +Each module will define `STACK_COOKIE_VALUE` which is used for the module stack cookie +value. + +The entry point libraries under `MdePkg/DynamicStackCookieEntryPointLib/` update the stack +cookie value at runtime for improved security, but there are cases where the stack cookie +global cannot be written to such as in execute-in-place (XIP) modules and during the +temporary RAM phase of the boot process. It is always preferable to use +one of the dynamic stack cookie entry points when possible. + +### StackCheckLib + +`StackCheckLib` provides the stack cookie checking functionality per architecture and +toolchain. The currently supported pairs are IA32{GCC,MSVC}, X64{GCC, MSVC}, +ARM{GCC, MSVC}, and AARCH64{GCC, MSVC}. `StackCheckLib` is agnostic as to +whether the stack cookie was updated during build time or run time; it simply +checks the cookie in the MSVC case and in both GCC and MSVC responds to stack +cookie checking failures. + +To add support for other architectures/toolchains, additional assembly files +should be added to `StackCheckLib.inf` and scoped to that architecture/toolchain. + +Note: Stack cookie failures generate exceptions and SEC and PEI_CORE may not have +exception handlers registered. In order to safely use stack cookie checking in +these phases, a platform should implement exception handlers because unhandled +exceptions may lead to a hung system state. If a platform does not implement +exception handlers in SEC and PEI_CORE, it is recommended to use `StackCheckLibNull` +for these phases, except for development purposes. + +### DynamicStackCookieEntryPointLib + +Each EntryPoint lib under `MdePkg/DynamicStackCookieEntryPointLib/` is an instance of +that module type entry point lib which updates the stack cookie value for the module at +runtime. This is the preferred method for stack cookie initialization as it provides +improved security. The stack cookie value is initialized at runtime in `_ModuleEntryPoint` +by calling `rdrand` on x86 and `RNDR` on AARCH64. If the random number generator is not +supported on that platform or otherwise returns an error, then the value will still have +the build-time randomized value to fall back on. + +Typically, dynamic cookies cannot be used for SEC, PEI_CORE, and PEIM modules, due to +the lack of the ability to write to globals for many architectures. If a given platform +can write to globals during these phases, it is recommended to use the provided dynamic +stack cookie entry point lib for those types. Note that SEC does not have a universal +entry point, so there is no dynamic stack cookie entry point lib there. + +The dynamic stack cookie entry point lib is used in place of the standard entry point lib, +e.g. for UefiDriverEntryPoint to have dynamic stack cookies, a platform would remove +MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf from its DSC and instead +include MdePkg/Library/DynamicStackCookieEntryPointLib/UefiDriverEntryPoint.inf. + +See the Usage section for other ways of including these libraries. + +Note: Standalone MM Core support for dynamic cookies for AARCH64 is currently not +supported, due to the unique entry point mechanism there. This support will be +added at a future date. ### StackCheckLibNull `StackCheckLibNull` is an instance of `StackCheckLib` which does not perform any stack cookie checks. This is useful for modules which will fail if stack cookie checks are -inserted. Of course, this is not recommended for production code. +inserted. Of course, this is not recommended for production code outside of +SEC and PEI_CORE. ## How Failures are Handled @@ -104,8 +139,9 @@ edk2 updated the tools_def to add `/GS` to VS2022 and VS2019 IA32/X64 builds and `-fstack-protector` to GCC builds. This will cause stack cookie references to be inserted throughout the code. Every module should have a `StackCheckLib` instance linked to satisfy these references. So every module doesn't need to add `StackCheckLib` to the LibraryClasses -section of the INF file, `StackCheckLib` instances should be linked as NULL in the platform -DSC files. The only exception to this is MSVC built host-based unit tests as they will be +section of the INF file, `StackCheckLib` is added as a dependency for each entry point lib. +This means that custom entry point libs need to have StackCheckLib added as a dependency. +The only exception to this is MSVC built host-based unit tests as they will be compiled with the runtime libraries which already contain the stack cookie definitions and will collide with `StackCheckLib`. A `StackCheckLibHostApplication.inf` is linked by `UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc` that provides the stack @@ -113,70 +149,75 @@ cookie functions for GCC HOST_APPLICATIONS but not for MSVC HOST_APPLICATIONS. ### Default Stack Check Library Configuration -`MdePkg/MdeLibs.dsc.inc` links `StackCheckLibNull` for all types except SEC, HOST_APPLICATION, -and USER_DEFINED in order to not break existing DSCs. SEC cannot be generically linked to -because there are some SEC modules which do not link against the standard entry point -libraries and thus do not get stack cookies inserted by the compiler. USER_DEFINED modules -are by nature different from other modules, so we do not make any assumptions about their -state. +`MdePkg/MdeLibs.dsc.inc` links `StackCheckLibNull` and `StackCheckFailureLibNull` for all +types. As stated above, all HOST_APPLICATIONS will link against a HOST_APPLICATION specific implementation provided in `UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc`. -To link the rest of a platform's modules to `StackCheckLibNull`, a platform would needs -to link it for all SEC and USER_DEFINED modules. If all of the DSC's SEC and USER_DEFINED -modules link against the entry point libs, it would look like the following: +### Custom Stack Check Library Configuration + +In order to use a different instance of StackCheckLib than `MdeLibs.dsc.inc` provides, a DSC +should add one of the following: + +#### Static Stack Check Cookie Configuration ```inf -[LibraryClasses.common.SEC, LibraryClasses.common.USER_DEFINED] - NULL|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf +[Defines] + DEFINE CUSTOM_STACK_CHECK_LIB = STATIC ``` -If some do not, then the individual SEC/USER_DEFINED modules that do link against -the entry point libs will need to be linked to `StackCheckLibNull`, such as below. -This case is identifiable if a DSC is built and the linker complains the stack -check functions are not found for a module. +This will cause `MdeLibs.dsc.inc` to not link `StackCheckLibNull` and instead link +`StackCheckLib` to perform stack cookie checking on the static stack cookies, but not update +any of the stack cookies at runtime. + +Because edk2 does not implement exception handling for `SEC` and `PEI_CORE`, `MdeLibs.dsc.inc` +uses `StackCheckLibNull` for these phases always. If a platform wishes to use `StackCheckLib` +for these phases, it should override this in its DSC, e.g.: ```inf -UefiCpuPkg/SecCore/SecCore.inf { - <LibraryClasses> - NULL|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf - } +[LibraryClasses.common.SEC, LibraryClasses.common.PEI_CORE] + StackCheckLib|MdePkg/Library/StackCheckLib/StackCheckLib.inf ``` -### Custom Stack Check Library Configuration +It is recommended that a platform only do this for debugging or if they have implemented +exception handlers for these phases. -In order to use a different instance of StackCheckLib than `MdeLibs.dsc.inc` provides, a DSC -should add the following: +#### Dynamic Stack Cookie Configuration ```inf [Defines] - DEFINE CUSTOM_STACK_CHECK_LIB = TRUE + DEFINE CUSTOM_STACK_CHECK_LIB = DYNAMIC ``` -This will cause `MdeLibs.dsc.inc` to not link `StackCheckLibNull` and rely on a DSC to -link whichever version(s) of `StackCheckLib` it desires. - -It is recommended that SEC and PEI_CORE modules use `StackCheckLibNull` and pre-memory modules -should use `StackCheckLibStaticInit`. All other modules should use `StackCheckLibDynamicInit`. +This will cause `MdeLibs.dsc.inc` to not link `StackCheckLibNull` and instead link +`StackCheckLib` to perform stack cookie checking. It will also link the dynamic +stack cookie updating versions of `DxeCoreEntryPoint`, `StandaloneMmDriverEntryPoint`, +`UefiApplicationEntryPoint`, and `UefiDriverEntryPoint`. -Below is an **example** of how to link the `StackCheckLib` instances in the platform DSC file -but it may need customization based on the platform's requirements: +Because edk2 does not implement exception handling for `SEC` and `PEI_CORE`, +`MdeLibs.dsc.inc` uses `StackCheckLibNull` for these phases always. If a +platform wishes to use `StackCheckLib` for these phases, it can enable static +stack cookie checking, as documented in the previous section. Due to the fact +that writable global variables are not supported in the `SEC` or `PEI` phases +of execution, dynamic stack cookie checking is not supported here. -```inf -[LibraryClasses.common.SEC, LibraryClasses.common.PEI_CORE] - NULL|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf +It is recommended that a platform only do this for debugging or if they have implemented +exception handlers for these phases. -[LibraryClasses.common.PEIM] - NULL|MdePkg/Library/StackCheckLib/StackCheckLibStaticInit.inf +Note: `StandaloneMmCoreEntryPoint` is recommended to use the dynamic stack cookie if +possible, but as it is not supported on AARCH64 today, it is not included in MdeLibs.dsc.inc. +Platforms should include this separately, e.g.: -[LibraryClasses.common.MM_CORE_STANDALONE, LibraryClasses.common.MM_STANDALONE, LibraryClasses.common.DXE_CORE, -LibraryClasses.common.SMM_CORE, LibraryClasses.common.DXE_SMM_DRIVER, LibraryClasses.common.DXE_DRIVER, -LibraryClasses.common.DXE_RUNTIME_DRIVER, LibraryClasses.common.DXE_SAL_DRIVER, LibraryClasses.common.UEFI_DRIVER, -LibraryClasses.common.UEFI_APPLICATION] - NULL|MdePkg/Library/StackCheckLib/StackCheckLibDynamicInit.inf +```inf +[LibraryClasses.X64] + StandaloneMmCoreEntryPoint|MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmCoreEntryPoint.inf ``` +Platforms then must remove any references to these entry point libs in their DSC, so that +the `MdeLibs.dsc.inc` versions are chosen. Alternatively, for better DSC readability, +a platform can directly reference the dynamic stack cookie entry points. + ### Disable Stack Check Library If a platform would like to disable stack cookies (say for debugging purposes), @@ -191,4 +232,14 @@ they can add the following to their DSC: The same build options can be put in a module's INF to only disable stack cookies for that module. +Alternatively, a module can have the stack cookies inserted but checking disabled +by including the following in a DSC: + +```inf +SomePkg/SomeDirectory/SomeModule.inf { + <LibraryClasses> + StackCheckLib|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf +} +``` + It is not recommended to disable stack cookie checking in production scenarios. diff --git a/MdePkg/Library/StackCheckLib/StackCheckLib.inf b/MdePkg/Library/StackCheckLib/StackCheckLib.inf new file mode 100644 index 000000000000..417d340a851f --- /dev/null +++ b/MdePkg/Library/StackCheckLib/StackCheckLib.inf @@ -0,0 +1,56 @@ +## @file +# Provides the required functionality for checking the stack cookie. +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 1.29 + BASE_NAME = StackCheckLib + FILE_GUID = 1C4CA056-8FEA-413C-89D2-59A7E22847B3 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = StackCheckLib + +[Sources] + StackCheckLibCommonMsvc.c | MSFT + StackCheckLibCommonGcc.c | GCC + +[Sources.IA32] + IA32/CheckCookieMsvc.nasm | MSFT + +[Sources.X64] + X64/CheckCookieMsvc.nasm | MSFT + +[Sources.IA32, Sources.X64] + IA32/StackCookieInterrupt.nasm + +[Sources.ARM] + Arm/StackCookieInterrupt.S |GCC + Arm/StackCookieInterrupt.asm |MSFT + +[Sources.AARCH64] + AArch64/StackCookieInterrupt.S |GCC + AArch64/StackCookieInterrupt.asm |MSFT + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + StackCheckFailureHookLib + +[FixedPcd] + gEfiMdePkgTokenSpaceGuid.PcdStackCookieExceptionVector + +[BuildOptions] + # We cannot build the MSVC version with /GL (whole program optimization) because we run into linker error + # LNK1237, which is a failure to link against a symbol from a library compiled with /GL. The whole program + # optimization tries to do away with references to this symbol. The solution is to not compile the stack + # check libs with /GL + MSFT:*_*_*_CC_FLAGS = /GL- + + # We cannot build the GCC version with LTO (link time optimization) because we run into linker errors where + # the stack cookie variable has been optimized away, as it looks to GCC like the variable is not used, because + # the compiler inserts the usage. + GCC:*_*_*_CC_FLAGS = -fno-lto diff --git a/MdePkg/Library/StackCheckLib/StackCheckLibCommonGcc.c b/MdePkg/Library/StackCheckLib/StackCheckLibCommonGcc.c index 996d1e6d732f..cccab4212bbf 100644 --- a/MdePkg/Library/StackCheckLib/StackCheckLibCommonGcc.c +++ b/MdePkg/Library/StackCheckLib/StackCheckLibCommonGcc.c @@ -10,6 +10,7 @@ #include <Library/DebugLib.h> #include <Library/BaseLib.h> +#include <Library/StackCheckLib.h> #include <Library/StackCheckFailureHookLib.h> /** @@ -28,6 +29,7 @@ VOID *__stack_chk_guard = (VOID *)(UINTN)STACK_COOKIE_VALUE; **/ VOID +EFIAPI __stack_chk_fail ( VOID ) diff --git a/MdePkg/Library/StackCheckLib/StackCheckLibCommonMsvc.c b/MdePkg/Library/StackCheckLib/StackCheckLibCommonMsvc.c index 6018e3790990..449ffe6da171 100644 --- a/MdePkg/Library/StackCheckLib/StackCheckLibCommonMsvc.c +++ b/MdePkg/Library/StackCheckLib/StackCheckLibCommonMsvc.c @@ -10,6 +10,7 @@ #include <Library/DebugLib.h> #include <Library/BaseLib.h> +#include <Library/StackCheckLib.h> #include <Library/StackCheckFailureHookLib.h> /** diff --git a/MdePkg/Library/StackCheckLibNull/StackCheckLibNullGcc.c b/MdePkg/Library/StackCheckLibNull/StackCheckLibNullGcc.c index 5c4278d60bf4..91568dee88c5 100644 --- a/MdePkg/Library/StackCheckLibNull/StackCheckLibNullGcc.c +++ b/MdePkg/Library/StackCheckLibNull/StackCheckLibNullGcc.c @@ -6,6 +6,7 @@ **/ #include <Uefi.h> +#include <Library/StackCheckLib.h> VOID *__stack_chk_guard = (VOID *)(UINTN)0x0; diff --git a/MdePkg/Library/StackCheckLibNull/StackCheckLibNullMsvc.c b/MdePkg/Library/StackCheckLibNull/StackCheckLibNullMsvc.c index a9c26dc36b21..ff99120ad2fd 100644 --- a/MdePkg/Library/StackCheckLibNull/StackCheckLibNullMsvc.c +++ b/MdePkg/Library/StackCheckLibNull/StackCheckLibNullMsvc.c @@ -6,5 +6,6 @@ **/ #include <Uefi.h> +#include <Library/StackCheckLib.h> VOID *__security_cookie = (VOID *)(UINTN)0x0; diff --git a/MdePkg/Library/StandaloneMmCoreEntryPoint/StandaloneMmCoreEntryPoint.inf b/MdePkg/Library/StandaloneMmCoreEntryPoint/StandaloneMmCoreEntryPoint.inf new file mode 100644 index 000000000000..3052788d8216 --- /dev/null +++ b/MdePkg/Library/StandaloneMmCoreEntryPoint/StandaloneMmCoreEntryPoint.inf @@ -0,0 +1,33 @@ +## @file +# Module entry point library for Standalone MM core. +# +# Copyright (c) 2017 - 2021, Arm Ltd. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x0001001A + BASE_NAME = StandaloneMmCoreEntryPoint + FILE_GUID = 6D1B60B2-5DD1-4523-8F0A-607DB4D6B0CE + MODULE_TYPE = MM_CORE_STANDALONE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x00010032 + LIBRARY_CLASS = StandaloneMmCoreEntryPoint|MM_CORE_STANDALONE + +# +# VALID_ARCHITECTURES = X64 +# + +[Sources.X64] + X64/StandaloneMmCoreEntryPoint.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + StackCheckLib diff --git a/MdePkg/Library/StandaloneMmCoreEntryPoint/X64/StandaloneMmCoreEntryPoint.c b/MdePkg/Library/StandaloneMmCoreEntryPoint/X64/StandaloneMmCoreEntryPoint.c new file mode 100644 index 000000000000..7c67c4d3c73a --- /dev/null +++ b/MdePkg/Library/StandaloneMmCoreEntryPoint/X64/StandaloneMmCoreEntryPoint.c @@ -0,0 +1,69 @@ +/** @file + Entry point to the Standalone Mm Core. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiMm.h> + +#include <Library/StandaloneMmCoreEntryPoint.h> +#include <Library/DebugLib.h> +#include <Library/BaseLib.h> + +// +// Cache copy of HobList pointer. +// +VOID *gHobList = NULL; + +/** + The entry point of PE/COFF Image for the STANDALONE MM Core. + + This function is the entry point for the STANDALONE MM Core. This function is required to call + ProcessModuleEntryPointList() and ProcessModuleEntryPointList() is never expected to return. + The STANDALONE MM Core is responsible for calling ProcessLibraryConstructorList() as soon as the EFI + System Table and the image handle for the STANDALONE MM Core itself have been established. + If ProcessModuleEntryPointList() returns, then ASSERT() and halt the system. + + @param HobStart Pointer to the beginning of the HOB List passed in from the PEI Phase. + +**/ +VOID +EFIAPI +_ModuleEntryPoint ( + IN VOID *HobStart + ) +{ + // + // Cache a pointer to the HobList + // + gHobList = HobStart; + + // + // Call the Standalone MM Core entry point + // + ProcessModuleEntryPointList (HobStart); + + // + // TODO: Set page table here?? AARCH64 has this step for some reason + // +} + +/** + Required by the EBC compiler and identical in functionality to _ModuleEntryPoint(). + + This function is required to call _ModuleEntryPoint() passing in HobStart. + + @param HobStart Pointer to the beginning of the HOB List passed in from the PEI Phase. + +**/ +VOID +EFIAPI +EfiMain ( + IN VOID *HobStart + ) +{ + _ModuleEntryPoint (HobStart); +} diff --git a/MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf b/MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf index a83545aac051..9fafd44d3a04 100644 --- a/MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf +++ b/MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf @@ -36,6 +36,7 @@ BaseLib DebugLib MmServicesTableLib + StackCheckLib [Protocols] gEfiLoadedImageProtocolGuid ## SOMETIMES_CONSUMES diff --git a/MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf b/MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf index ced65892ad11..ecd033f7f741 100644 --- a/MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf +++ b/MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf @@ -32,4 +32,5 @@ UefiBootServicesTableLib DebugLib BaseLib + StackCheckLib diff --git a/MdePkg/Library/UefiDevicePathLib/DevicePathToText.c b/MdePkg/Library/UefiDevicePathLib/DevicePathToText.c index 23f3010286e6..f1d1f4bc97eb 100644 --- a/MdePkg/Library/UefiDevicePathLib/DevicePathToText.c +++ b/MdePkg/Library/UefiDevicePathLib/DevicePathToText.c @@ -1882,7 +1882,7 @@ DevPathToTextHardDrive ( case SIGNATURE_TYPE_MBR: UefiDevicePathLibCatPrint ( Str, - L"HD(%d,%s,0x%08x,", + L"HD(%d,%s,0x%08x", Hd->PartitionNumber, L"MBR", *((UINT32 *)(&(Hd->Signature[0]))) @@ -1892,7 +1892,7 @@ DevPathToTextHardDrive ( case SIGNATURE_TYPE_GUID: UefiDevicePathLibCatPrint ( Str, - L"HD(%d,%s,%g,", + L"HD(%d,%s,%g", Hd->PartitionNumber, L"GPT", (EFI_GUID *)&(Hd->Signature[0]) @@ -1902,14 +1902,18 @@ DevPathToTextHardDrive ( default: UefiDevicePathLibCatPrint ( Str, - L"HD(%d,%d,0,", + L"HD(%d,%d,0", Hd->PartitionNumber, Hd->SignatureType ); break; } - UefiDevicePathLibCatPrint (Str, L"0x%lx,0x%lx)", Hd->PartitionStart, Hd->PartitionSize); + if (DisplayOnly) { + UefiDevicePathLibCatPrint (Str, L")"); + } else { + UefiDevicePathLibCatPrint (Str, L",0x%lx,0x%lx)", Hd->PartitionStart, Hd->PartitionSize); + } } /** diff --git a/MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf b/MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf index bef65fb22631..8432fd56b921 100644 --- a/MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf +++ b/MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf @@ -36,6 +36,7 @@ UefiBootServicesTableLib DebugLib BaseLib + StackCheckLib [Protocols] diff --git a/MdePkg/Library/UefiUsbLib/UefiUsbLib.inf b/MdePkg/Library/UefiUsbLib/UefiUsbLib.inf index 2f1224081165..8f7b26138132 100644 --- a/MdePkg/Library/UefiUsbLib/UefiUsbLib.inf +++ b/MdePkg/Library/UefiUsbLib/UefiUsbLib.inf @@ -5,6 +5,7 @@ # Usb Hid 1.1 spec and the standard requests defined in Usb 1.1 spec. # # Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2024, American Megatrends International LLC. All rights reserved.<BR> # # SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -19,6 +20,7 @@ MODULE_TYPE = UEFI_DRIVER VERSION_STRING = 1.0 LIBRARY_CLASS = UefiUsbLib|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER + DESTRUCTOR = UefiUsbLibDestructor # @@ -37,6 +39,7 @@ DebugLib BaseMemoryLib PcdLib + UefiBootServicesTableLib [Pcd] gEfiMdePkgTokenSpaceGuid.PcdUsbTransferTimeoutValue ## CONSUMES diff --git a/MdePkg/Library/UefiUsbLib/UefiUsbLibInternal.h b/MdePkg/Library/UefiUsbLib/UefiUsbLibInternal.h index 30cb4a2cb864..1c43b348b19f 100644 --- a/MdePkg/Library/UefiUsbLib/UefiUsbLibInternal.h +++ b/MdePkg/Library/UefiUsbLib/UefiUsbLibInternal.h @@ -5,6 +5,7 @@ This file includes package header files, library classes and protocol, PPI & GUID definitions. Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> + Copyright (c) 2024, American Megatrends International LLC. All rights reserved.<BR> SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -17,6 +18,7 @@ #include <Library/BaseMemoryLib.h> #include <Library/DebugLib.h> #include <Library/PcdLib.h> +#include <Library/UefiBootServicesTableLib.h> #include <IndustryStandard/Usb.h> diff --git a/MdePkg/Library/UefiUsbLib/UsbDxeLib.c b/MdePkg/Library/UefiUsbLib/UsbDxeLib.c index 6d46529ae2f4..f25e8097fc41 100644 --- a/MdePkg/Library/UefiUsbLib/UsbDxeLib.c +++ b/MdePkg/Library/UefiUsbLib/UsbDxeLib.c @@ -4,12 +4,16 @@ in Usb specification 9.4 section. Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR> + Copyright (c) 2024, American Megatrends International LLC. All rights reserved.<BR> SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "UefiUsbLibInternal.h" +static UINT8 *mConfigData = NULL; +static EFI_USB_DEVICE_DESCRIPTOR mDeviceDescriptor; + /** Get the descriptor of the specified USB device. @@ -650,3 +654,532 @@ UsbClearEndpointHalt ( return Result; } + +/** + Global library data initialization. + + Library public functions' input is the instance of UsbIo protocol. Check if the global + data relevant to the UsbIo. If not, read the device and update the global data. + + @param UsbIo The instance of EFI_USB_IO_PROTOCOL. + + @retval EFI_SUCCESS The global data is updated. + @retval EFI_NOT_FOUND The UsbIo configuration was not found. + +**/ +static +EFI_STATUS +InitUsbConfigDescriptorData ( + EFI_USB_IO_PROTOCOL *UsbIo + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + EFI_USB_CONFIG_DESCRIPTOR CnfDesc; + UINT8 ConfigNum; + UINT8 ConfigValue; + UINT32 UsbStatus; + + // + // Get UsbIo device and configuration descriptors. + // + Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); + ASSERT_EFI_ERROR (Status); + + Status = UsbIo->UsbGetConfigDescriptor (UsbIo, &CnfDesc); + ASSERT_EFI_ERROR (Status); + + if (mConfigData != NULL) { + if ( (CompareMem (&DevDesc, &mDeviceDescriptor, sizeof (EFI_USB_DEVICE_DESCRIPTOR)) == 0) + && (CompareMem (&CnfDesc, mConfigData, sizeof (EFI_USB_CONFIG_DESCRIPTOR)) == 0)) + { + return EFI_SUCCESS; + } + + gBS->FreePool (mConfigData); + mConfigData = NULL; + } + + CopyMem (&mDeviceDescriptor, &DevDesc, sizeof (EFI_USB_DEVICE_DESCRIPTOR)); + + // + // Examine device with multiple configurations: find configuration index of UsbIo config descriptor. + // + // Use EFI_USB_DEVICE_DESCRIPTOR.NumConfigurations to loop through configuration descriptors, match + // EFI_USB_CONFIG_DESCRIPTOR.ConfigurationValue to the configuration value reported by UsbIo->UsbGetConfigDescriptor. + // The index of the matched configuration is used in wValue of the following GET_DESCRIPTOR request. + // + ConfigValue = CnfDesc.ConfigurationValue; + for (ConfigNum = 0; ConfigNum < DevDesc.NumConfigurations; ConfigNum++) { + Status = UsbGetDescriptor ( + UsbIo, + (USB_DESC_TYPE_CONFIG << 8) | ConfigNum, + 0, + sizeof (EFI_USB_CONFIG_DESCRIPTOR), + &CnfDesc, + &UsbStatus + ); + ASSERT_EFI_ERROR (Status); + + if (CnfDesc.ConfigurationValue == ConfigValue) { + break; + } + } + + ASSERT (ConfigNum < DevDesc.NumConfigurations); + if (ConfigNum == DevDesc.NumConfigurations) { + return EFI_NOT_FOUND; + } + + // + // ConfigNum has zero based index of the configuration that UsbIo belongs to. Use this index to retrieve + // full configuration descriptor data. + // + Status = gBS->AllocatePool (EfiBootServicesData, CnfDesc.TotalLength, (VOID **)&mConfigData); + ASSERT_EFI_ERROR (Status); + + Status = UsbGetDescriptor ( + UsbIo, + (USB_DESC_TYPE_CONFIG << 8) | ConfigNum, + 0, + CnfDesc.TotalLength, + mConfigData, + &UsbStatus + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Find descriptor of a given type within data area pointed by mConfigData. + + The following are the assumptions of the configuration descriptor layout: + - mConfigData is populated with the configuration data that contains USB interface referenced by UsbIo. + - Endpoint may have only one class specific descriptor that immediately follows the endpoint descriptor. + + @param[in] UsbIo A pointer to the EFI_USB_IO_PROTOCOL instance. + @param[in] DescType Type of descriptor to look for. + @param[in] Setting Interface alternate setting. + @param[in] Index Index of the descriptor. This descriptor index is used to find a specific + descriptor (only for endpoint descriptors and class specific interface descriptors) + when several descriptors of the same type are implemented in a device. For other + descriptor types, a descriptor index of zero must be used. + @param[out] Data A pointer to the caller allocated Descriptor. + + @retval EFI_SUCCESS Output parameters were updated successfully. + @retval EFI_UNSUPPORTED Setting is greater than the number of alternate settings in this interface. + @retval EFI_NOT_FOUND Index is greater than the number of descriptors of the requested type in this + interface. +**/ +static +EFI_STATUS +FindUsbDescriptor ( + EFI_USB_IO_PROTOCOL *UsbIo, + UINT8 DescType, + UINT16 Setting, + UINTN Index, + VOID **Data + ) +{ + EFI_USB_INTERFACE_DESCRIPTOR IntfDesc; + EFI_STATUS Status; + UINT8 *BufferPtr; + UINT8 *BufferEnd; + UINT8 *ConfigEnd; + UINTN Idx; + + // + // Find the interface descriptor referenced by UsbIo in the current configuration + // + Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IntfDesc); + ASSERT_EFI_ERROR (Status); + + ConfigEnd = mConfigData + ((EFI_USB_CONFIG_DESCRIPTOR *)mConfigData)->TotalLength; + + for (BufferPtr = mConfigData; BufferPtr < ConfigEnd; BufferPtr += BufferPtr[0]) { + if (BufferPtr[1] == USB_DESC_TYPE_INTERFACE) { + if ((BufferPtr[2] == IntfDesc.InterfaceNumber) && (BufferPtr[3] == (UINT8)Setting)) { + break; + } + } + } + + if (BufferPtr >= ConfigEnd) { + return EFI_UNSUPPORTED; + } + + // + // Found the beginning of the interface, find the ending + // + for (BufferEnd = BufferPtr + BufferPtr[0]; BufferEnd < ConfigEnd; BufferEnd += BufferEnd[0]) { + if (BufferEnd[1] == USB_DESC_TYPE_INTERFACE) { + break; + } + } + + Idx = 0; + + if (DescType == USB_DESC_TYPE_INTERFACE) { + *Data = BufferPtr; + return EFI_SUCCESS; + } + + if ((DescType == USB_DESC_TYPE_ENDPOINT) || (DescType == USB_DESC_TYPE_CS_ENDPOINT)) { + while (BufferPtr < BufferEnd) { + BufferPtr += BufferPtr[0]; + if (BufferPtr[1] == USB_DESC_TYPE_ENDPOINT) { + if (Idx == Index) { + if (DescType == USB_DESC_TYPE_CS_ENDPOINT) { + BufferPtr += BufferPtr[0]; + if (BufferPtr[1] != USB_DESC_TYPE_CS_ENDPOINT) { + break; + } + } + + *Data = BufferPtr; + return EFI_SUCCESS; + } + + Idx++; + } + } + } + + if (DescType == USB_DESC_TYPE_CS_INTERFACE) { + while (BufferPtr < BufferEnd) { + BufferPtr += BufferPtr[0]; + if (BufferPtr[1] == USB_DESC_TYPE_CS_INTERFACE) { + if (Idx == Index) { + *Data = BufferPtr; + return EFI_SUCCESS; + } + + Idx++; + } + } + } + + return EFI_NOT_FOUND; +} + +/** + Retrieve the number of class specific interface descriptors. + + @param[in] Data A pointer to the USB interface descriptor that may contain class code descriptors. + + @retval UINT8 Number of the class code interface descriptors. + +**/ +static +UINT8 +FindNumberOfCsInterfaces ( + VOID *Data + ) +{ + UINT8 *Buffer; + UINT8 *ConfigEnd; + UINT8 Index; + + Buffer = Data; + ConfigEnd = mConfigData + ((EFI_USB_CONFIG_DESCRIPTOR *)mConfigData)->TotalLength; + + Index = 0; + + for (Buffer += Buffer[0]; Buffer < ConfigEnd; Buffer += Buffer[0]) { + if (Buffer[1] == USB_DESC_TYPE_INTERFACE) { + break; + } + + if (Buffer[1] == USB_DESC_TYPE_CS_INTERFACE) { + Index++; + } + } + + return Index; +} + +/** + Retrieve the interface descriptor details from the interface setting. + + This is an extended version of UsbIo->GetInterfaceDescriptor. It returns the interface + descriptor for an alternate setting of the interface without executing SET_INTERFACE + transfer. It also returns the number of class specific interfaces. + AlternateSetting parameter is the zero-based interface descriptor index that is used in USB + interface descriptor as USB_INTERFACE_DESCRIPTOR.AlternateSetting. + + + @param[in] This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param[in] AlternateSetting Interface alternate setting. + @param[out] Descriptor The caller allocated buffer to return the contents of the Interface descriptor. + @param[out] CsInterfaceNumber Number of class specific interfaces for this interface setting. + + @retval EFI_SUCCESS Output parameters were updated successfully. + @retval EFI_INVALID_PARAMETER Descriptor or CsInterfaceNumber is NULL. + @retval EFI_UNSUPPORTED AlternateSetting is greater than the number of alternate settings in this interface. + @retval EFI_DEVICE_ERROR Error reading device data. + +**/ +EFI_STATUS +EFIAPI +UsbGetInterfaceDescriptorSetting ( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT16 AlternateSetting, + OUT EFI_USB_INTERFACE_DESCRIPTOR *Descriptor, + OUT UINTN *CsInterfacesNumber + ) +{ + EFI_STATUS Status; + VOID *Data; + EFI_TPL OldTpl; + + if ((Descriptor == NULL) || (CsInterfacesNumber == NULL)) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + Status = InitUsbConfigDescriptorData (This); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Status = FindUsbDescriptor (This, USB_DESC_TYPE_INTERFACE, AlternateSetting, 0, &Data); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + *CsInterfacesNumber = FindNumberOfCsInterfaces (Data); + CopyMem (Descriptor, Data, sizeof (EFI_USB_INTERFACE_DESCRIPTOR)); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Retrieve the endpoint descriptor from the interface setting. + + This is an extended version of UsbIo->GetEndpointDescriptor. It returns the endpoint + descriptor for an alternate setting of a given interface. + AlternateSetting parameter is the zero-based interface descriptor index that is used in USB + interface descriptor as USB_INTERFACE_DESCRIPTOR.AlternateSetting. + + Note: The total number of endpoints can be retrieved from the interface descriptor + returned by EDKII_USBIO_EXT_GET_INTERFACE_DESCRIPTOR function. + + @param[in] This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param[in] AlternateSetting Interface alternate setting. + @param[in] Index Index of the endpoint to retrieve. The valid range is 0..15. + @param[out] Descriptor A pointer to the caller allocated USB Interface Descriptor. + + @retval EFI_SUCCESS Output parameters were updated successfully. + @retval EFI_INVALID_PARAMETER Descriptor is NULL. + @retval EFI_UNSUPPORTED AlternateSetting is greater than the number of alternate settings in this interface. + @retval EFI_NOT_FOUND Index is greater than the number of endpoints in this interface. + @retval EFI_DEVICE_ERROR Error reading device data. + +**/ +EFI_STATUS +EFIAPI +UsbGetEndpointDescriptorSetting ( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT16 AlternateSetting, + IN UINTN Index, + OUT EFI_USB_ENDPOINT_DESCRIPTOR *Descriptor + ) +{ + EFI_STATUS Status; + VOID *Data; + EFI_TPL OldTpl; + + if (Descriptor == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + Status = InitUsbConfigDescriptorData (This); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Status = FindUsbDescriptor (This, USB_DESC_TYPE_ENDPOINT, AlternateSetting, Index, &Data); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + CopyMem (Descriptor, Data, sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Retrieve class specific interface descriptor. + + AlternateSetting parameter is the zero-based interface descriptor index that is used in USB + interface descriptor as USB_INTERFACE_DESCRIPTOR.AlternateSetting. + + @param[in] This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param[in] AlternateSetting Interface alternate setting. + @param[in] Index Zero-based index of the class specific interface. + @param[in][out] BufferSize On input, the size in bytes of the return Descriptor buffer. + On output the size of data returned in Descriptor. + @param[out] Descriptor The buffer to return the contents of the class specific interface descriptor. May + be NULL with a zero BufferSize in order to determine the size buffer needed. + + @retval EFI_SUCCESS Output parameters were updated successfully. + @retval EFI_INVALID_PARAMETER BufferSize is NULL. + Buffer is NULL and *BufferSize is not zero. + @retval EFI_UNSUPPORTED AlternateSetting is greater than the number of alternate settings in this interface. + @retval EFI_NOT_FOUND Index is greater than the number of class specific interfaces. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small for the result. BufferSize has been updated with the size + needed to complete the request. + @retval EFI_DEVICE_ERROR Error reading device data. + +**/ +EFI_STATUS +EFIAPI +UsbGetCsInterfaceDescriptor ( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT16 AlternateSetting, + IN UINTN Index, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + VOID *Data; + UINT8 DescLength; + EFI_TPL OldTpl; + + if ((BufferSize == NULL) || ((Buffer == NULL) && (*BufferSize != 0))) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + Status = InitUsbConfigDescriptorData (This); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Status = FindUsbDescriptor (This, USB_DESC_TYPE_CS_INTERFACE, AlternateSetting, Index, &Data); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + DescLength = ((UINT8 *)Data)[0]; + + if ((Buffer == NULL) || (DescLength > *BufferSize)) { + *BufferSize = DescLength; + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + + CopyMem (Buffer, Data, DescLength); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Retrieve class specific endpoint descriptor. + + AlternateSetting parameter is the zero-based interface descriptor index that is used in USB + interface descriptor as USB_INTERFACE_DESCRIPTOR.AlternateSetting. + + @param[in] This A pointer to the EFI_USB_IO_PROTOCOL instance. + @param[in] AlternateSetting Interface alternate setting. + @param[in] Index Zero-based index of the non-zero endpoint. + @param[in][out] BufferSize On input, the size in bytes of the return Descriptor buffer. + On output the size of data returned in Descriptor. + @param[out] Descriptor The buffer to return the contents of the class specific endpoint descriptor. May + be NULL with a zero BufferSize in order to determine the size buffer needed. + + @retval EFI_SUCCESS Output parameters were updated successfully. + @retval EFI_INVALID_PARAMETER BufferSize is NULL. + Buffer is NULL and *BufferSize is not zero. + @retval EFI_UNSUPPORTED AlternateSetting is greater than the number of alternate settings in this interface. + @retval EFI_NOT_FOUND Index is greater than the number of endpoints in this interface. + Endpoint does not have class specific endpoint descriptor. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small for the result. BufferSize has been updated with the size + needed to complete the request. + @retval EFI_DEVICE_ERROR Error reading device data. + +**/ +EFI_STATUS +EFIAPI +UsbGetCsEndpointDescriptor ( + IN EFI_USB_IO_PROTOCOL *This, + IN UINT16 AlternateSetting, + IN UINTN Index, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + VOID *Data; + UINT8 DescLength; + EFI_TPL OldTpl; + + if ((BufferSize == NULL) || ((Buffer == NULL) && (*BufferSize != 0))) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + Status = InitUsbConfigDescriptorData (This); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Status = FindUsbDescriptor (This, USB_DESC_TYPE_CS_ENDPOINT, AlternateSetting, Index, &Data); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + DescLength = ((UINT8 *)Data)[0]; + + if ((Buffer == NULL) || (DescLength > *BufferSize)) { + *BufferSize = DescLength; + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + + CopyMem (Buffer, Data, DescLength); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Destructor frees memory which was allocated by the library functions. + + @param ImageHandle Handle that identifies the image to be unloaded. + @param SystemTable The system table. + + @retval EFI_SUCCESS The image has been unloaded. + +**/ +EFI_STATUS +EFIAPI +UefiUsbLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + if (mConfigData != NULL) { + gBS->FreePool (mConfigData); + } + + return EFI_SUCCESS; +} |
