diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/UefiCpuPkg | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/UefiCpuPkg')
318 files changed, 79323 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Application/Cpuid/Cpuid.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Application/Cpuid/Cpuid.c new file mode 100644 index 00000000..49f5864b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Application/Cpuid/Cpuid.c @@ -0,0 +1,1612 @@ +/** @file + UEFI Application to display CPUID leaf information. + + Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi.h> +#include <Library/BaseLib.h> +#include <Library/UefiLib.h> +#include <Register/Intel/Cpuid.h> + +/// +/// Macro used to display the value of a bit field in a register returned by CPUID. +/// +#define PRINT_BIT_FIELD(Variable, FieldName) \ + Print (L"%5a%42a: %x\n", #Variable, #FieldName, Variable.Bits.FieldName); + +/// +/// Macro used to display the value of a register returned by CPUID. +/// +#define PRINT_VALUE(Variable, Description) \ + Print (L"%5a%42a: %x\n", #Variable, #Description, Variable); + +/// +/// Structure for cache description lookup table +/// +typedef struct { + UINT8 CacheDescriptor; + CHAR8 *Type; + CHAR8 *Description; +} CPUID_CACHE_INFO_DESCRIPTION; + +/// +/// Cache description lookup table +/// +CPUID_CACHE_INFO_DESCRIPTION mCpuidCacheInfoDescription[] = { + { 0x00 , "General" , "Null descriptor, this byte contains no information" }, + { 0x01 , "TLB" , "Instruction TLB: 4 KByte pages, 4-way set associative, 32 entries" }, + { 0x02 , "TLB" , "Instruction TLB: 4 MByte pages, fully associative, 2 entries" }, + { 0x03 , "TLB" , "Data TLB: 4 KByte pages, 4-way set associative, 64 entries" }, + { 0x04 , "TLB" , "Data TLB: 4 MByte pages, 4-way set associative, 8 entries" }, + { 0x05 , "TLB" , "Data TLB1: 4 MByte pages, 4-way set associative, 32 entries" }, + { 0x06 , "Cache" , "1st-level instruction cache: 8 KBytes, 4-way set associative, 32 byte line size" }, + { 0x08 , "Cache" , "1st-level instruction cache: 16 KBytes, 4-way set associative, 32 byte line size" }, + { 0x09 , "Cache" , "1st-level instruction cache: 32KBytes, 4-way set associative, 64 byte line size" }, + { 0x0A , "Cache" , "1st-level data cache: 8 KBytes, 2-way set associative, 32 byte line size" }, + { 0x0B , "TLB" , "Instruction TLB: 4 MByte pages, 4-way set associative, 4 entries" }, + { 0x0C , "Cache" , "1st-level data cache: 16 KBytes, 4-way set associative, 32 byte line size" }, + { 0x0D , "Cache" , "1st-level data cache: 16 KBytes, 4-way set associative, 64 byte line size" }, + { 0x0E , "Cache" , "1st-level data cache: 24 KBytes, 6-way set associative, 64 byte line size" }, + { 0x1D , "Cache" , "2nd-level cache: 128 KBytes, 2-way set associative, 64 byte line size" }, + { 0x21 , "Cache" , "2nd-level cache: 256 KBytes, 8-way set associative, 64 byte line size" }, + { 0x22 , "Cache" , "3rd-level cache: 512 KBytes, 4-way set associative, 64 byte line size, 2 lines per sector" }, + { 0x23 , "Cache" , "3rd-level cache: 1 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector" }, + { 0x24 , "Cache" , "2nd-level cache: 1 MBytes, 16-way set associative, 64 byte line size" }, + { 0x25 , "Cache" , "3rd-level cache: 2 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector" }, + { 0x29 , "Cache" , "3rd-level cache: 4 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector" }, + { 0x2C , "Cache" , "1st-level data cache: 32 KBytes, 8-way set associative, 64 byte line size" }, + { 0x30 , "Cache" , "1st-level instruction cache: 32 KBytes, 8-way set associative, 64 byte line size" }, + { 0x40 , "Cache" , "No 2nd-level cache or, if processor contains a valid 2nd-level cache, no 3rd-level cache" }, + { 0x41 , "Cache" , "2nd-level cache: 128 KBytes, 4-way set associative, 32 byte line size" }, + { 0x42 , "Cache" , "2nd-level cache: 256 KBytes, 4-way set associative, 32 byte line size" }, + { 0x43 , "Cache" , "2nd-level cache: 512 KBytes, 4-way set associative, 32 byte line size" }, + { 0x44 , "Cache" , "2nd-level cache: 1 MByte, 4-way set associative, 32 byte line size" }, + { 0x45 , "Cache" , "2nd-level cache: 2 MByte, 4-way set associative, 32 byte line size" }, + { 0x46 , "Cache" , "3rd-level cache: 4 MByte, 4-way set associative, 64 byte line size" }, + { 0x47 , "Cache" , "3rd-level cache: 8 MByte, 8-way set associative, 64 byte line size" }, + { 0x48 , "Cache" , "2nd-level cache: 3MByte, 12-way set associative, 64 byte line size" }, + { 0x49 , "Cache" , "3rd-level cache: 4MB, 16-way set associative, 64-byte line size (Intel Xeon processor MP, Family 0FH, Model 06H). 2nd-level cache: 4 MByte, 16-way set associative, 64 byte line size" }, + { 0x4A , "Cache" , "3rd-level cache: 6MByte, 12-way set associative, 64 byte line size" }, + { 0x4B , "Cache" , "3rd-level cache: 8MByte, 16-way set associative, 64 byte line size" }, + { 0x4C , "Cache" , "3rd-level cache: 12MByte, 12-way set associative, 64 byte line size" }, + { 0x4D , "Cache" , "3rd-level cache: 16MByte, 16-way set associative, 64 byte line size" }, + { 0x4E , "Cache" , "2nd-level cache: 6MByte, 24-way set associative, 64 byte line size" }, + { 0x4F , "TLB" , "Instruction TLB: 4 KByte pages, 32 entries" }, + { 0x50 , "TLB" , "Instruction TLB: 4 KByte and 2-MByte or 4-MByte pages, 64 entries" }, + { 0x51 , "TLB" , "Instruction TLB: 4 KByte and 2-MByte or 4-MByte pages, 128 entries" }, + { 0x52 , "TLB" , "Instruction TLB: 4 KByte and 2-MByte or 4-MByte pages, 256 entries" }, + { 0x55 , "TLB" , "Instruction TLB: 2-MByte or 4-MByte pages, fully associative, 7 entries" }, + { 0x56 , "TLB" , "Data TLB0: 4 MByte pages, 4-way set associative, 16 entries" }, + { 0x57 , "TLB" , "Data TLB0: 4 KByte pages, 4-way associative, 16 entries" }, + { 0x59 , "TLB" , "Data TLB0: 4 KByte pages, fully associative, 16 entries" }, + { 0x5A , "TLB" , "Data TLB0: 2 MByte or 4 MByte pages, 4-way set associative, 32 entries" }, + { 0x5B , "TLB" , "Data TLB: 4 KByte and 4 MByte pages, 64 entries" }, + { 0x5C , "TLB" , "Data TLB: 4 KByte and 4 MByte pages,128 entries" }, + { 0x5D , "TLB" , "Data TLB: 4 KByte and 4 MByte pages,256 entries" }, + { 0x60 , "Cache" , "1st-level data cache: 16 KByte, 8-way set associative, 64 byte line size" }, + { 0x61 , "TLB" , "Instruction TLB: 4 KByte pages, fully associative, 48 entries" }, + { 0x63 , "TLB" , "Data TLB: 2 MByte or 4 MByte pages, 4-way set associative, 32 entries and a separate array with 1 GByte pages, 4-way set associative, 4 entries" }, + { 0x64 , "TLB" , "Data TLB: 4 KByte pages, 4-way set associative, 512 entries" }, + { 0x66 , "Cache" , "1st-level data cache: 8 KByte, 4-way set associative, 64 byte line size" }, + { 0x67 , "Cache" , "1st-level data cache: 16 KByte, 4-way set associative, 64 byte line size" }, + { 0x68 , "Cache" , "1st-level data cache: 32 KByte, 4-way set associative, 64 byte line size" }, + { 0x6A , "Cache" , "uTLB: 4 KByte pages, 8-way set associative, 64 entries" }, + { 0x6B , "Cache" , "DTLB: 4 KByte pages, 8-way set associative, 256 entries" }, + { 0x6C , "Cache" , "DTLB: 2M/4M pages, 8-way set associative, 128 entries" }, + { 0x6D , "Cache" , "DTLB: 1 GByte pages, fully associative, 16 entries" }, + { 0x70 , "Cache" , "Trace cache: 12 K-uop, 8-way set associative" }, + { 0x71 , "Cache" , "Trace cache: 16 K-uop, 8-way set associative" }, + { 0x72 , "Cache" , "Trace cache: 32 K-uop, 8-way set associative" }, + { 0x76 , "TLB" , "Instruction TLB: 2M/4M pages, fully associative, 8 entries" }, + { 0x78 , "Cache" , "2nd-level cache: 1 MByte, 4-way set associative, 64byte line size" }, + { 0x79 , "Cache" , "2nd-level cache: 128 KByte, 8-way set associative, 64 byte line size, 2 lines per sector" }, + { 0x7A , "Cache" , "2nd-level cache: 256 KByte, 8-way set associative, 64 byte line size, 2 lines per sector" }, + { 0x7B , "Cache" , "2nd-level cache: 512 KByte, 8-way set associative, 64 byte line size, 2 lines per sector" }, + { 0x7C , "Cache" , "2nd-level cache: 1 MByte, 8-way set associative, 64 byte line size, 2 lines per sector" }, + { 0x7D , "Cache" , "2nd-level cache: 2 MByte, 8-way set associative, 64byte line size" }, + { 0x7F , "Cache" , "2nd-level cache: 512 KByte, 2-way set associative, 64-byte line size" }, + { 0x80 , "Cache" , "2nd-level cache: 512 KByte, 8-way set associative, 64-byte line size" }, + { 0x82 , "Cache" , "2nd-level cache: 256 KByte, 8-way set associative, 32 byte line size" }, + { 0x83 , "Cache" , "2nd-level cache: 512 KByte, 8-way set associative, 32 byte line size" }, + { 0x84 , "Cache" , "2nd-level cache: 1 MByte, 8-way set associative, 32 byte line size" }, + { 0x85 , "Cache" , "2nd-level cache: 2 MByte, 8-way set associative, 32 byte line size" }, + { 0x86 , "Cache" , "2nd-level cache: 512 KByte, 4-way set associative, 64 byte line size" }, + { 0x87 , "Cache" , "2nd-level cache: 1 MByte, 8-way set associative, 64 byte line size" }, + { 0xA0 , "DTLB" , "DTLB: 4k pages, fully associative, 32 entries" }, + { 0xB0 , "TLB" , "Instruction TLB: 4 KByte pages, 4-way set associative, 128 entries" }, + { 0xB1 , "TLB" , "Instruction TLB: 2M pages, 4-way, 8 entries or 4M pages, 4-way, 4 entries" }, + { 0xB2 , "TLB" , "Instruction TLB: 4KByte pages, 4-way set associative, 64 entries" }, + { 0xB3 , "TLB" , "Data TLB: 4 KByte pages, 4-way set associative, 128 entries" }, + { 0xB4 , "TLB" , "Data TLB1: 4 KByte pages, 4-way associative, 256 entries" }, + { 0xB5 , "TLB" , "Instruction TLB: 4KByte pages, 8-way set associative, 64 entries" }, + { 0xB6 , "TLB" , "Instruction TLB: 4KByte pages, 8-way set associative, 128 entries" }, + { 0xBA , "TLB" , "Data TLB1: 4 KByte pages, 4-way associative, 64 entries" }, + { 0xC0 , "TLB" , "Data TLB: 4 KByte and 4 MByte pages, 4-way associative, 8 entries" }, + { 0xC1 , "STLB" , "Shared 2nd-Level TLB: 4 KByte/2MByte pages, 8-way associative, 1024 entries" }, + { 0xC2 , "DTLB" , "DTLB: 4 KByte/2 MByte pages, 4-way associative, 16 entries" }, + { 0xC3 , "STLB" , "Shared 2nd-Level TLB: 4 KByte /2 MByte pages, 6-way associative, 1536 entries. Also 1GBbyte pages, 4-way, 16 entries." }, + { 0xC4 , "DTLB" , "DTLB: 2M/4M Byte pages, 4-way associative, 32 entries" }, + { 0xCA , "STLB" , "Shared 2nd-Level TLB: 4 KByte pages, 4-way associative, 512 entries" }, + { 0xD0 , "Cache" , "3rd-level cache: 512 KByte, 4-way set associative, 64 byte line size" }, + { 0xD1 , "Cache" , "3rd-level cache: 1 MByte, 4-way set associative, 64 byte line size" }, + { 0xD2 , "Cache" , "3rd-level cache: 2 MByte, 4-way set associative, 64 byte line size" }, + { 0xD6 , "Cache" , "3rd-level cache: 1 MByte, 8-way set associative, 64 byte line size" }, + { 0xD7 , "Cache" , "3rd-level cache: 2 MByte, 8-way set associative, 64 byte line size" }, + { 0xD8 , "Cache" , "3rd-level cache: 4 MByte, 8-way set associative, 64 byte line size" }, + { 0xDC , "Cache" , "3rd-level cache: 1.5 MByte, 12-way set associative, 64 byte line size" }, + { 0xDD , "Cache" , "3rd-level cache: 3 MByte, 12-way set associative, 64 byte line size" }, + { 0xDE , "Cache" , "3rd-level cache: 6 MByte, 12-way set associative, 64 byte line size" }, + { 0xE2 , "Cache" , "3rd-level cache: 2 MByte, 16-way set associative, 64 byte line size" }, + { 0xE3 , "Cache" , "3rd-level cache: 4 MByte, 16-way set associative, 64 byte line size" }, + { 0xE4 , "Cache" , "3rd-level cache: 8 MByte, 16-way set associative, 64 byte line size" }, + { 0xEA , "Cache" , "3rd-level cache: 12MByte, 24-way set associative, 64 byte line size" }, + { 0xEB , "Cache" , "3rd-level cache: 18MByte, 24-way set associative, 64 byte line size" }, + { 0xEC , "Cache" , "3rd-level cache: 24MByte, 24-way set associative, 64 byte line size" }, + { 0xF0 , "Prefetch" , "64-Byte prefetching" }, + { 0xF1 , "Prefetch" , "128-Byte prefetching" }, + { 0xFE , "General" , "CPUID leaf 2 does not report TLB descriptor information; use CPUID leaf 18H to query TLB and other address translation parameters." }, + { 0xFF , "General" , "CPUID leaf 2 does not report cache descriptor information, use CPUID leaf 4 to query cache parameters" } +}; + +/// +/// The maximum supported CPUID leaf index starting from leaf 0x00000000. +/// +UINT32 gMaximumBasicFunction = CPUID_SIGNATURE; + +/// +/// The maximum supported CPUID leaf index starting from leaf 0x80000000. +/// +UINT32 gMaximumExtendedFunction = CPUID_EXTENDED_FUNCTION; + +/** + Display CPUID_SIGNATURE leaf. + +**/ +VOID +CpuidSignature ( + VOID + ) +{ + UINT32 Eax; + UINT32 Ebx; + UINT32 Ecx; + UINT32 Edx; + CHAR8 Signature[13]; + + AsmCpuid (CPUID_SIGNATURE, &Eax, &Ebx, &Ecx, &Edx); + + Print (L"CPUID_SIGNATURE (Leaf %08x)\n", CPUID_SIGNATURE); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax, Ebx, Ecx, Edx); + PRINT_VALUE (Eax, MaximumLeaf); + *(UINT32 *)(Signature + 0) = Ebx; + *(UINT32 *)(Signature + 4) = Edx; + *(UINT32 *)(Signature + 8) = Ecx; + Signature [12] = 0; + Print (L" Signature = %a\n", Signature); + + gMaximumBasicFunction = Eax; +} + +/** + Display CPUID_VERSION_INFO leaf. + +**/ +VOID +CpuidVersionInfo ( + VOID + ) +{ + CPUID_VERSION_INFO_EAX Eax; + CPUID_VERSION_INFO_EBX Ebx; + CPUID_VERSION_INFO_ECX Ecx; + CPUID_VERSION_INFO_EDX Edx; + UINT32 DisplayFamily; + UINT32 DisplayModel; + + if (CPUID_VERSION_INFO > gMaximumBasicFunction) { + return; + } + + AsmCpuid (CPUID_VERSION_INFO, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32); + + Print (L"CPUID_VERSION_INFO (Leaf %08x)\n", CPUID_VERSION_INFO); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32); + + DisplayFamily = Eax.Bits.FamilyId; + if (Eax.Bits.FamilyId == 0x0F) { + DisplayFamily |= (Eax.Bits.ExtendedFamilyId << 4); + } + + DisplayModel = Eax.Bits.Model; + if (Eax.Bits.FamilyId == 0x06 || Eax.Bits.FamilyId == 0x0f) { + DisplayModel |= (Eax.Bits.ExtendedModelId << 4); + } + + Print (L" Family = %x Model = %x Stepping = %x\n", DisplayFamily, DisplayModel, Eax.Bits.SteppingId); + + PRINT_BIT_FIELD (Eax, SteppingId); + PRINT_BIT_FIELD (Eax, Model); + PRINT_BIT_FIELD (Eax, FamilyId); + PRINT_BIT_FIELD (Eax, ProcessorType); + PRINT_BIT_FIELD (Eax, ExtendedModelId); + PRINT_BIT_FIELD (Eax, ExtendedFamilyId); + PRINT_BIT_FIELD (Ebx, BrandIndex); + PRINT_BIT_FIELD (Ebx, CacheLineSize); + PRINT_BIT_FIELD (Ebx, MaximumAddressableIdsForLogicalProcessors); + PRINT_BIT_FIELD (Ebx, InitialLocalApicId); + PRINT_BIT_FIELD (Ecx, SSE3); + PRINT_BIT_FIELD (Ecx, PCLMULQDQ); + PRINT_BIT_FIELD (Ecx, DTES64); + PRINT_BIT_FIELD (Ecx, MONITOR); + PRINT_BIT_FIELD (Ecx, DS_CPL); + PRINT_BIT_FIELD (Ecx, VMX); + PRINT_BIT_FIELD (Ecx, SMX); + PRINT_BIT_FIELD (Ecx, TM2); + PRINT_BIT_FIELD (Ecx, SSSE3); + PRINT_BIT_FIELD (Ecx, CNXT_ID); + PRINT_BIT_FIELD (Ecx, SDBG); + PRINT_BIT_FIELD (Ecx, FMA); + PRINT_BIT_FIELD (Ecx, CMPXCHG16B); + PRINT_BIT_FIELD (Ecx, xTPR_Update_Control); + PRINT_BIT_FIELD (Ecx, PDCM); + PRINT_BIT_FIELD (Ecx, PCID); + PRINT_BIT_FIELD (Ecx, DCA); + PRINT_BIT_FIELD (Ecx, SSE4_1); + PRINT_BIT_FIELD (Ecx, SSE4_2); + PRINT_BIT_FIELD (Ecx, x2APIC); + PRINT_BIT_FIELD (Ecx, MOVBE); + PRINT_BIT_FIELD (Ecx, POPCNT); + PRINT_BIT_FIELD (Ecx, TSC_Deadline); + PRINT_BIT_FIELD (Ecx, AESNI); + PRINT_BIT_FIELD (Ecx, XSAVE); + PRINT_BIT_FIELD (Ecx, OSXSAVE); + PRINT_BIT_FIELD (Ecx, AVX); + PRINT_BIT_FIELD (Ecx, F16C); + PRINT_BIT_FIELD (Ecx, RDRAND); + PRINT_BIT_FIELD (Edx, FPU); + PRINT_BIT_FIELD (Edx, VME); + PRINT_BIT_FIELD (Edx, DE); + PRINT_BIT_FIELD (Edx, PSE); + PRINT_BIT_FIELD (Edx, TSC); + PRINT_BIT_FIELD (Edx, MSR); + PRINT_BIT_FIELD (Edx, PAE); + PRINT_BIT_FIELD (Edx, MCE); + PRINT_BIT_FIELD (Edx, CX8); + PRINT_BIT_FIELD (Edx, APIC); + PRINT_BIT_FIELD (Edx, SEP); + PRINT_BIT_FIELD (Edx, MTRR); + PRINT_BIT_FIELD (Edx, PGE); + PRINT_BIT_FIELD (Edx, MCA); + PRINT_BIT_FIELD (Edx, CMOV); + PRINT_BIT_FIELD (Edx, PAT); + PRINT_BIT_FIELD (Edx, PSE_36); + PRINT_BIT_FIELD (Edx, PSN); + PRINT_BIT_FIELD (Edx, CLFSH); + PRINT_BIT_FIELD (Edx, DS); + PRINT_BIT_FIELD (Edx, ACPI); + PRINT_BIT_FIELD (Edx, MMX); + PRINT_BIT_FIELD (Edx, FXSR); + PRINT_BIT_FIELD (Edx, SSE); + PRINT_BIT_FIELD (Edx, SSE2); + PRINT_BIT_FIELD (Edx, SS); + PRINT_BIT_FIELD (Edx, HTT); + PRINT_BIT_FIELD (Edx, TM); + PRINT_BIT_FIELD (Edx, PBE); +} + +/** + Lookup a cache description string from the mCpuidCacheInfoDescription table. + + @param[in] CacheDescriptor Cache descriptor value from CPUID_CACHE_INFO. + +**/ +CPUID_CACHE_INFO_DESCRIPTION * +LookupCacheDescription ( + UINT8 CacheDescriptor + ) +{ + UINTN NumDescriptors; + UINTN Descriptor; + + if (CacheDescriptor == 0x00) { + return NULL; + } + NumDescriptors = sizeof (mCpuidCacheInfoDescription)/sizeof (mCpuidCacheInfoDescription[0]); + for (Descriptor = 0; Descriptor < NumDescriptors; Descriptor++) { + if (CacheDescriptor == mCpuidCacheInfoDescription[Descriptor].CacheDescriptor) { + return &mCpuidCacheInfoDescription[Descriptor]; + } + } + return NULL; +} + +/** + Display CPUID_CACHE_INFO leaf for each supported cache descriptor. + +**/ +VOID +CpuidCacheInfo ( + VOID + ) +{ + CPUID_CACHE_INFO_CACHE_TLB Eax; + CPUID_CACHE_INFO_CACHE_TLB Ebx; + CPUID_CACHE_INFO_CACHE_TLB Ecx; + CPUID_CACHE_INFO_CACHE_TLB Edx; + UINTN Index; + CPUID_CACHE_INFO_DESCRIPTION *CacheDescription; + + if (CPUID_CACHE_INFO > gMaximumBasicFunction) { + return; + } + + AsmCpuid (CPUID_CACHE_INFO, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32); + + Print (L"CPUID_CACHE_INFO (Leaf %08x)\n", CPUID_CACHE_INFO); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32); + if (Eax.Bits.NotValid == 0) { + // + // Process Eax.CacheDescriptor[1..3]. Ignore Eax.CacheDescriptor[0] + // + for (Index = 1; Index < 4; Index++) { + CacheDescription = LookupCacheDescription (Eax.CacheDescriptor[Index]); + if (CacheDescription != NULL) { + Print (L" %-8a %a\n", + CacheDescription->Type, + CacheDescription->Description + ); + } + } + } + if (Ebx.Bits.NotValid == 0) { + // + // Process Ebx.CacheDescriptor[0..3] + // + for (Index = 0; Index < 4; Index++) { + CacheDescription = LookupCacheDescription (Ebx.CacheDescriptor[Index]); + if (CacheDescription != NULL) { + Print (L" %-8a %a\n", + CacheDescription->Type, + CacheDescription->Description + ); + } + } + } + if (Ecx.Bits.NotValid == 0) { + // + // Process Ecx.CacheDescriptor[0..3] + // + for (Index = 0; Index < 4; Index++) { + CacheDescription = LookupCacheDescription (Ecx.CacheDescriptor[Index]); + if (CacheDescription != NULL) { + Print (L" %-8a %a\n", + CacheDescription->Type, + CacheDescription->Description + ); + } + } + } + if (Edx.Bits.NotValid == 0) { + // + // Process Edx.CacheDescriptor[0..3] + // + for (Index = 0; Index < 4; Index++) { + CacheDescription = LookupCacheDescription (Edx.CacheDescriptor[Index]); + if (CacheDescription != NULL) { + Print (L" %-8a %a\n", + CacheDescription->Type, + CacheDescription->Description + ); + } + } + } +} + +/** + Display CPUID_SERIAL_NUMBER leaf if it is supported. + +**/ +VOID +CpuidSerialNumber ( + VOID + ) +{ + CPUID_VERSION_INFO_EDX VersionInfoEdx; + UINT32 Ecx; + UINT32 Edx; + + Print (L"CPUID_SERIAL_NUMBER (Leaf %08x)\n", CPUID_SERIAL_NUMBER); + + if (CPUID_SERIAL_NUMBER > gMaximumBasicFunction) { + return; + } + + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32); + if (VersionInfoEdx.Bits.PSN == 0) { + Print (L" Not Supported\n"); + return; + } + + AsmCpuid (CPUID_SERIAL_NUMBER, NULL, NULL, &Ecx, &Edx); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", 0, 0, Ecx, Edx); + Print (L" Processor Serial Number = %08x%08x%08x\n", 0, Edx, Ecx); +} + +/** + Display CPUID_CACHE_PARAMS for all supported sub-leafs. + +**/ +VOID +CpuidCacheParams ( + VOID + ) +{ + UINT32 CacheLevel; + CPUID_CACHE_PARAMS_EAX Eax; + CPUID_CACHE_PARAMS_EBX Ebx; + UINT32 Ecx; + CPUID_CACHE_PARAMS_EDX Edx; + + if (CPUID_CACHE_PARAMS > gMaximumBasicFunction) { + return; + } + + CacheLevel = 0; + do { + AsmCpuidEx ( + CPUID_CACHE_PARAMS, CacheLevel, + &Eax.Uint32, &Ebx.Uint32, &Ecx, &Edx.Uint32 + ); + if (Eax.Bits.CacheType != CPUID_CACHE_PARAMS_CACHE_TYPE_NULL) { + Print (L"CPUID_CACHE_PARAMS (Leaf %08x, Sub-Leaf %08x)\n", CPUID_CACHE_PARAMS, CacheLevel); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx, Edx.Uint32); + PRINT_BIT_FIELD (Eax, CacheType); + PRINT_BIT_FIELD (Eax, CacheLevel); + PRINT_BIT_FIELD (Eax, SelfInitializingCache); + PRINT_BIT_FIELD (Eax, FullyAssociativeCache); + PRINT_BIT_FIELD (Eax, MaximumAddressableIdsForLogicalProcessors); + PRINT_BIT_FIELD (Eax, MaximumAddressableIdsForProcessorCores); + PRINT_BIT_FIELD (Ebx, LineSize); + PRINT_BIT_FIELD (Ebx, LinePartitions); + PRINT_BIT_FIELD (Ebx, Ways); + PRINT_VALUE (Ecx, NumberOfSets); + PRINT_BIT_FIELD (Edx, Invalidate); + PRINT_BIT_FIELD (Edx, CacheInclusiveness); + PRINT_BIT_FIELD (Edx, ComplexCacheIndexing); + } + CacheLevel++; + } while (Eax.Bits.CacheType != CPUID_CACHE_PARAMS_CACHE_TYPE_NULL); +} + +/** + Display CPUID_MONITOR_MWAIT leaf. + +**/ +VOID +CpuidMonitorMwait ( + VOID + ) +{ + CPUID_MONITOR_MWAIT_EAX Eax; + CPUID_MONITOR_MWAIT_EBX Ebx; + CPUID_MONITOR_MWAIT_ECX Ecx; + CPUID_MONITOR_MWAIT_EDX Edx; + + if (CPUID_MONITOR_MWAIT > gMaximumBasicFunction) { + return; + } + + AsmCpuid (CPUID_MONITOR_MWAIT, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32); + + Print (L"CPUID_MONITOR_MWAIT (Leaf %08x)\n", CPUID_MONITOR_MWAIT); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32); + + PRINT_BIT_FIELD (Eax, SmallestMonitorLineSize); + PRINT_BIT_FIELD (Ebx, LargestMonitorLineSize); + PRINT_BIT_FIELD (Ecx, ExtensionsSupported); + PRINT_BIT_FIELD (Ecx, InterruptAsBreak); + PRINT_BIT_FIELD (Edx, C0States); + PRINT_BIT_FIELD (Edx, C1States); + PRINT_BIT_FIELD (Edx, C2States); + PRINT_BIT_FIELD (Edx, C3States); + PRINT_BIT_FIELD (Edx, C4States); + PRINT_BIT_FIELD (Edx, C5States); + PRINT_BIT_FIELD (Edx, C6States); + PRINT_BIT_FIELD (Edx, C7States); +} + +/** + Display CPUID_THERMAL_POWER_MANAGEMENT leaf. + +**/ +VOID +CpuidThermalPowerManagement ( + VOID + ) +{ + CPUID_THERMAL_POWER_MANAGEMENT_EAX Eax; + CPUID_THERMAL_POWER_MANAGEMENT_EBX Ebx; + CPUID_THERMAL_POWER_MANAGEMENT_ECX Ecx; + + if (CPUID_THERMAL_POWER_MANAGEMENT > gMaximumBasicFunction) { + return; + } + + AsmCpuid (CPUID_THERMAL_POWER_MANAGEMENT, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, NULL); + + Print (L"CPUID_THERMAL_POWER_MANAGEMENT (Leaf %08x)\n", CPUID_THERMAL_POWER_MANAGEMENT); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, 0); + + PRINT_BIT_FIELD (Eax, DigitalTemperatureSensor); + PRINT_BIT_FIELD (Eax, TurboBoostTechnology); + PRINT_BIT_FIELD (Eax, ARAT); + PRINT_BIT_FIELD (Eax, PLN); + PRINT_BIT_FIELD (Eax, ECMD); + PRINT_BIT_FIELD (Eax, PTM); + PRINT_BIT_FIELD (Eax, HWP); + PRINT_BIT_FIELD (Eax, HWP_Notification); + PRINT_BIT_FIELD (Eax, HWP_Activity_Window); + PRINT_BIT_FIELD (Eax, HWP_Energy_Performance_Preference); + PRINT_BIT_FIELD (Eax, HWP_Package_Level_Request); + PRINT_BIT_FIELD (Eax, HDC); + PRINT_BIT_FIELD (Eax, TurboBoostMaxTechnology30); + PRINT_BIT_FIELD (Eax, HWPCapabilities); + PRINT_BIT_FIELD (Eax, HWPPECIOverride); + PRINT_BIT_FIELD (Eax, FlexibleHWP); + PRINT_BIT_FIELD (Eax, FastAccessMode); + PRINT_BIT_FIELD (Eax, IgnoringIdleLogicalProcessorHWPRequest); + PRINT_BIT_FIELD (Ebx, InterruptThresholds); + PRINT_BIT_FIELD (Ecx, HardwareCoordinationFeedback); + PRINT_BIT_FIELD (Ecx, PerformanceEnergyBias); +} + +/** + Display CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS for all supported sub-leafs. + +**/ +VOID +CpuidStructuredExtendedFeatureFlags ( + VOID + ) +{ + UINT32 Eax; + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_EBX Ebx; + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_ECX Ecx; + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_EDX Edx; + UINT32 SubLeaf; + + if (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS > gMaximumBasicFunction) { + return; + } + + AsmCpuidEx ( + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_SUB_LEAF_INFO, + &Eax, NULL, NULL, NULL + ); + for (SubLeaf = 0; SubLeaf <= Eax; SubLeaf++) { + AsmCpuidEx ( + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, + SubLeaf, + NULL, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32 + ); + if (Ebx.Uint32 != 0 || Ecx.Uint32 != 0 || Edx.Uint32 != 0) { + Print (L"CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS (Leaf %08x, Sub-Leaf %08x)\n", CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, SubLeaf); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax, Ebx.Uint32, Ecx.Uint32, Edx.Uint32); + PRINT_BIT_FIELD (Ebx, FSGSBASE); + PRINT_BIT_FIELD (Ebx, IA32_TSC_ADJUST); + PRINT_BIT_FIELD (Ebx, SGX); + PRINT_BIT_FIELD (Ebx, BMI1); + PRINT_BIT_FIELD (Ebx, HLE); + PRINT_BIT_FIELD (Ebx, AVX2); + PRINT_BIT_FIELD (Ebx, FDP_EXCPTN_ONLY); + PRINT_BIT_FIELD (Ebx, SMEP); + PRINT_BIT_FIELD (Ebx, BMI2); + PRINT_BIT_FIELD (Ebx, EnhancedRepMovsbStosb); + PRINT_BIT_FIELD (Ebx, INVPCID); + PRINT_BIT_FIELD (Ebx, RTM); + PRINT_BIT_FIELD (Ebx, RDT_M); + PRINT_BIT_FIELD (Ebx, DeprecateFpuCsDs); + PRINT_BIT_FIELD (Ebx, MPX); + PRINT_BIT_FIELD (Ebx, RDT_A); + PRINT_BIT_FIELD (Ebx, AVX512F); + PRINT_BIT_FIELD (Ebx, AVX512DQ); + PRINT_BIT_FIELD (Ebx, RDSEED); + PRINT_BIT_FIELD (Ebx, ADX); + PRINT_BIT_FIELD (Ebx, SMAP); + PRINT_BIT_FIELD (Ebx, AVX512_IFMA); + PRINT_BIT_FIELD (Ebx, CLFLUSHOPT); + PRINT_BIT_FIELD (Ebx, CLWB); + PRINT_BIT_FIELD (Ebx, IntelProcessorTrace); + PRINT_BIT_FIELD (Ebx, AVX512PF); + PRINT_BIT_FIELD (Ebx, AVX512ER); + PRINT_BIT_FIELD (Ebx, AVX512CD); + PRINT_BIT_FIELD (Ebx, SHA); + PRINT_BIT_FIELD (Ebx, AVX512BW); + PRINT_BIT_FIELD (Ebx, AVX512VL); + + PRINT_BIT_FIELD (Ecx, PREFETCHWT1); + PRINT_BIT_FIELD (Ecx, AVX512_VBMI); + PRINT_BIT_FIELD (Ecx, UMIP); + PRINT_BIT_FIELD (Ecx, PKU); + PRINT_BIT_FIELD (Ecx, OSPKE); + PRINT_BIT_FIELD (Ecx, AVX512_VPOPCNTDQ); + PRINT_BIT_FIELD (Ecx, MAWAU); + PRINT_BIT_FIELD (Ecx, RDPID); + PRINT_BIT_FIELD (Ecx, SGX_LC); + + PRINT_BIT_FIELD (Edx, AVX512_4VNNIW); + PRINT_BIT_FIELD (Edx, AVX512_4FMAPS); + PRINT_BIT_FIELD (Edx, EnumeratesSupportForIBRSAndIBPB); + PRINT_BIT_FIELD (Edx, EnumeratesSupportForSTIBP); + PRINT_BIT_FIELD (Edx, EnumeratesSupportForL1D_FLUSH); + PRINT_BIT_FIELD (Edx, EnumeratesSupportForCapability); + PRINT_BIT_FIELD (Edx, EnumeratesSupportForSSBD); + } + } +} + +/** + Display CPUID_DIRECT_CACHE_ACCESS_INFO leaf. + +**/ +VOID +CpuidDirectCacheAccessInfo ( + VOID + ) +{ + UINT32 Eax; + + if (CPUID_DIRECT_CACHE_ACCESS_INFO > gMaximumBasicFunction) { + return; + } + + AsmCpuid (CPUID_DIRECT_CACHE_ACCESS_INFO, &Eax, NULL, NULL, NULL); + Print (L"CPUID_DIRECT_CACHE_ACCESS_INFO (Leaf %08x)\n", CPUID_DIRECT_CACHE_ACCESS_INFO); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax, 0, 0, 0); +} + +/** + Display CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING leaf. + +**/ +VOID +CpuidArchitecturalPerformanceMonitoring ( + VOID + ) +{ + CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING_EAX Eax; + CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING_EBX Ebx; + CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING_EDX Edx; + + if (CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING > gMaximumBasicFunction) { + return; + } + + AsmCpuid (CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING, &Eax.Uint32, &Ebx.Uint32, NULL, &Edx.Uint32); + Print (L"CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING (Leaf %08x)\n", CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, 0, Edx.Uint32); + PRINT_BIT_FIELD (Eax, ArchPerfMonVerID); + PRINT_BIT_FIELD (Eax, PerformanceMonitorCounters); + PRINT_BIT_FIELD (Eax, PerformanceMonitorCounterWidth); + PRINT_BIT_FIELD (Eax, EbxBitVectorLength); + PRINT_BIT_FIELD (Ebx, UnhaltedCoreCycles); + PRINT_BIT_FIELD (Ebx, InstructionsRetired); + PRINT_BIT_FIELD (Ebx, UnhaltedReferenceCycles); + PRINT_BIT_FIELD (Ebx, LastLevelCacheReferences); + PRINT_BIT_FIELD (Ebx, LastLevelCacheMisses); + PRINT_BIT_FIELD (Ebx, BranchInstructionsRetired); + PRINT_BIT_FIELD (Ebx, AllBranchMispredictRetired); + PRINT_BIT_FIELD (Edx, FixedFunctionPerformanceCounters); + PRINT_BIT_FIELD (Edx, FixedFunctionPerformanceCounterWidth); + PRINT_BIT_FIELD (Edx, AnyThreadDeprecation); +} + +/** + Display CPUID_EXTENDED_TOPOLOGY leafs for all supported levels. + + @param[in] LeafFunction Leaf function index for CPUID_EXTENDED_TOPOLOGY. + +**/ +VOID +CpuidExtendedTopology ( + UINT32 LeafFunction + ) +{ + CPUID_EXTENDED_TOPOLOGY_EAX Eax; + CPUID_EXTENDED_TOPOLOGY_EBX Ebx; + CPUID_EXTENDED_TOPOLOGY_ECX Ecx; + UINT32 Edx; + UINT32 LevelNumber; + + if (LeafFunction > gMaximumBasicFunction) { + return; + } + if ((LeafFunction != CPUID_EXTENDED_TOPOLOGY) && (LeafFunction != CPUID_V2_EXTENDED_TOPOLOGY)) { + return; + } + + LevelNumber = 0; + for (LevelNumber = 0; ; LevelNumber++) { + AsmCpuidEx ( + LeafFunction, LevelNumber, + &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx + ); + if (Ecx.Bits.LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID) { + break; + } + Print ( + L"%a (Leaf %08x, Sub-Leaf %08x)\n", + LeafFunction == CPUID_EXTENDED_TOPOLOGY ? "CPUID_EXTENDED_TOPOLOGY" : "CPUID_V2_EXTENDED_TOPOLOGY", + LeafFunction, LevelNumber + ); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx); + PRINT_BIT_FIELD (Eax, ApicIdShift); + PRINT_BIT_FIELD (Ebx, LogicalProcessors); + PRINT_BIT_FIELD (Ecx, LevelNumber); + PRINT_BIT_FIELD (Ecx, LevelType); + PRINT_VALUE (Edx, x2APIC_ID); + } +} + +/** + Display CPUID_EXTENDED_STATE sub-leaf. + +**/ +VOID +CpuidExtendedStateSubLeaf ( + VOID + ) +{ + CPUID_EXTENDED_STATE_SUB_LEAF_EAX Eax; + UINT32 Ebx; + CPUID_EXTENDED_STATE_SUB_LEAF_ECX Ecx; + UINT32 Edx; + + AsmCpuidEx ( + CPUID_EXTENDED_STATE, CPUID_EXTENDED_STATE_SUB_LEAF, + &Eax.Uint32, &Ebx, &Ecx.Uint32, &Edx + ); + Print (L"CPUID_EXTENDED_STATE (Leaf %08x, Sub-Leaf %08x)\n", CPUID_EXTENDED_STATE, CPUID_EXTENDED_STATE_SUB_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx, Ecx.Uint32, Edx); + PRINT_BIT_FIELD (Eax, XSAVEOPT); + PRINT_BIT_FIELD (Eax, XSAVEC); + PRINT_BIT_FIELD (Eax, XGETBV); + PRINT_BIT_FIELD (Eax, XSAVES); + PRINT_VALUE (Ebx, EnabledSaveStateSize_XCR0_IA32_XSS); + PRINT_BIT_FIELD (Ecx, XCR0); + PRINT_BIT_FIELD (Ecx, HWPState); + PRINT_BIT_FIELD (Ecx, PT); + PRINT_BIT_FIELD (Ecx, XCR0_1); + PRINT_VALUE (Edx, IA32_XSS_Supported_32_63); +} + +/** + Display CPUID_EXTENDED_STATE size and offset information sub-leaf. + +**/ +VOID +CpuidExtendedStateSizeOffset ( + VOID + ) +{ + UINT32 Eax; + UINT32 Ebx; + CPUID_EXTENDED_STATE_SIZE_OFFSET_ECX Ecx; + UINT32 Edx; + UINT32 SubLeaf; + + for (SubLeaf = CPUID_EXTENDED_STATE_SIZE_OFFSET; SubLeaf < 32; SubLeaf++) { + AsmCpuidEx ( + CPUID_EXTENDED_STATE, SubLeaf, + &Eax, &Ebx, &Ecx.Uint32, &Edx + ); + if (Edx != 0) { + Print (L"CPUID_EXTENDED_STATE (Leaf %08x, Sub-Leaf %08x)\n", CPUID_EXTENDED_STATE, SubLeaf); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax, Ebx, Ecx.Uint32, Edx); + PRINT_VALUE (Eax, FeatureSaveStateSize); + PRINT_VALUE (Ebx, FeatureSaveStateOffset); + PRINT_BIT_FIELD (Ecx, XSS); + PRINT_BIT_FIELD (Ecx, Compacted); + } + } +} + +/** + Display CPUID_EXTENDED_STATE main leaf and sub-leafs. + +**/ +VOID +CpuidExtendedStateMainLeaf ( + VOID + ) +{ + CPUID_EXTENDED_STATE_MAIN_LEAF_EAX Eax; + UINT32 Ebx; + UINT32 Ecx; + UINT32 Edx; + + if (CPUID_EXTENDED_STATE > gMaximumBasicFunction) { + return; + } + + AsmCpuidEx ( + CPUID_EXTENDED_STATE, CPUID_EXTENDED_STATE_MAIN_LEAF, + &Eax.Uint32, &Ebx, &Ecx, &Edx + ); + Print (L"CPUID_EXTENDED_STATE (Leaf %08x, Sub-Leaf %08x)\n", CPUID_EXTENDED_STATE, CPUID_EXTENDED_STATE_MAIN_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx, Ecx, Edx); + PRINT_BIT_FIELD (Eax, x87); + PRINT_BIT_FIELD (Eax, SSE); + PRINT_BIT_FIELD (Eax, AVX); + PRINT_BIT_FIELD (Eax, MPX); + PRINT_BIT_FIELD (Eax, AVX_512); + PRINT_BIT_FIELD (Eax, IA32_XSS); + PRINT_BIT_FIELD (Eax, PKRU); + PRINT_BIT_FIELD (Eax, IA32_XSS_2); + PRINT_VALUE (Ebx, EnabledSaveStateSize); + PRINT_VALUE (Ecx, SupportedSaveStateSize); + PRINT_VALUE (Edx, XCR0_Supported_32_63); + + CpuidExtendedStateSubLeaf (); + CpuidExtendedStateSizeOffset (); +} + +/** + Display CPUID_INTEL_RDT_MONITORING enumeration sub-leaf. + +**/ +VOID +CpuidIntelRdtMonitoringEnumerationSubLeaf ( + VOID + ) +{ + UINT32 Ebx; + CPUID_INTEL_RDT_MONITORING_ENUMERATION_SUB_LEAF_EDX Edx; + + if (CPUID_INTEL_RDT_MONITORING > gMaximumBasicFunction) { + return; + } + + AsmCpuidEx ( + CPUID_INTEL_RDT_MONITORING, CPUID_INTEL_RDT_MONITORING_ENUMERATION_SUB_LEAF, + NULL, &Ebx, NULL, &Edx.Uint32 + ); + Print (L"CPUID_INTEL_RDT_MONITORING (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_RDT_MONITORING, CPUID_INTEL_RDT_MONITORING_ENUMERATION_SUB_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", 0, Ebx, 0, Edx.Uint32); + PRINT_VALUE (Ebx, Maximum_RMID_Range); + PRINT_BIT_FIELD (Edx, L3CacheRDT_M); +} + +/** + Display CPUID_INTEL_RDT_MONITORING L3 cache capability sub-leaf. + +**/ +VOID +CpuidIntelRdtMonitoringL3CacheCapabilitySubLeaf ( + VOID + ) +{ + UINT32 Ebx; + UINT32 Ecx; + CPUID_INTEL_RDT_MONITORING_L3_CACHE_SUB_LEAF_EDX Edx; + + if (CPUID_INTEL_RDT_MONITORING > gMaximumBasicFunction) { + return; + } + + AsmCpuidEx ( + CPUID_INTEL_RDT_MONITORING, CPUID_INTEL_RDT_MONITORING_L3_CACHE_SUB_LEAF, + NULL, &Ebx, &Ecx, &Edx.Uint32 + ); + Print (L"CPUID_INTEL_RDT_MONITORING (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_RDT_MONITORING, CPUID_INTEL_RDT_MONITORING_L3_CACHE_SUB_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", 0, Ebx, Ecx, Edx.Uint32); + PRINT_VALUE (Ebx, OccupancyConversionFactor); + PRINT_VALUE (Ecx, Maximum_RMID_Range); + PRINT_BIT_FIELD (Edx, L3CacheOccupancyMonitoring); + PRINT_BIT_FIELD (Edx, L3CacheTotalBandwidthMonitoring); + PRINT_BIT_FIELD (Edx, L3CacheLocalBandwidthMonitoring); +} + +/** + Display CPUID_INTEL_RDT_ALLOCATION memory bandwidth allocation technology enumeration + sub-leaf. + +**/ +VOID +CpuidIntelRdtAllocationMemoryBandwidthSubLeaf ( + VOID + ) +{ + CPUID_INTEL_RDT_ALLOCATION_MEMORY_BANDWIDTH_SUB_LEAF_EAX Eax; + UINT32 Ebx; + CPUID_INTEL_RDT_ALLOCATION_MEMORY_BANDWIDTH_SUB_LEAF_ECX Ecx; + CPUID_INTEL_RDT_ALLOCATION_MEMORY_BANDWIDTH_SUB_LEAF_EDX Edx; + + AsmCpuidEx ( + CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_MEMORY_BANDWIDTH_SUB_LEAF, + &Eax.Uint32, &Ebx, &Ecx.Uint32, &Edx.Uint32 + ); + Print (L"CPUID_INTEL_RDT_ALLOCATION (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_MEMORY_BANDWIDTH_SUB_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx, Ecx.Uint32, Edx.Uint32); + PRINT_BIT_FIELD (Eax, MaximumMBAThrottling); + PRINT_VALUE (Ebx, AllocationUnitBitMap); + PRINT_BIT_FIELD (Ecx, Liner); + PRINT_BIT_FIELD (Edx, HighestCosNumber); +} + +/** + Display CPUID_INTEL_RDT_ALLOCATION L3 cache allocation technology enumeration + sub-leaf. + +**/ +VOID +CpuidIntelRdtAllocationL3CacheSubLeaf ( + VOID + ) +{ + CPUID_INTEL_RDT_ALLOCATION_L3_CACHE_SUB_LEAF_EAX Eax; + UINT32 Ebx; + CPUID_INTEL_RDT_ALLOCATION_L3_CACHE_SUB_LEAF_ECX Ecx; + CPUID_INTEL_RDT_ALLOCATION_L3_CACHE_SUB_LEAF_EDX Edx; + + AsmCpuidEx ( + CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_L3_CACHE_SUB_LEAF, + &Eax.Uint32, &Ebx, &Ecx.Uint32, &Edx.Uint32 + ); + Print (L"CPUID_INTEL_RDT_ALLOCATION (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_L3_CACHE_SUB_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx, Ecx.Uint32, Edx.Uint32); + PRINT_BIT_FIELD (Eax, CapacityLength); + PRINT_VALUE (Ebx, AllocationUnitBitMap); + PRINT_BIT_FIELD (Ecx, CodeDataPrioritization); + PRINT_BIT_FIELD (Edx, HighestCosNumber); +} + +/** + Display CPUID_INTEL_RDT_ALLOCATION L2 cache allocation technology enumeration + sub-leaf. + +**/ +VOID +CpuidIntelRdtAllocationL2CacheSubLeaf ( + VOID + ) +{ + CPUID_INTEL_RDT_ALLOCATION_L2_CACHE_SUB_LEAF_EAX Eax; + UINT32 Ebx; + CPUID_INTEL_RDT_ALLOCATION_L2_CACHE_SUB_LEAF_EDX Edx; + + AsmCpuidEx ( + CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_L2_CACHE_SUB_LEAF, + &Eax.Uint32, &Ebx, NULL, &Edx.Uint32 + ); + Print (L"CPUID_INTEL_RDT_ALLOCATION (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_L2_CACHE_SUB_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx, 0, Edx.Uint32); + PRINT_BIT_FIELD (Eax, CapacityLength); + PRINT_VALUE (Ebx, AllocationUnitBitMap); + PRINT_BIT_FIELD (Edx, HighestCosNumber); +} + +/** + Display CPUID_INTEL_RDT_ALLOCATION main leaf and sub-leaves. + +**/ +VOID +CpuidIntelRdtAllocationMainLeaf ( + VOID + ) +{ + CPUID_INTEL_RDT_ALLOCATION_ENUMERATION_SUB_LEAF_EBX Ebx; + + if (CPUID_INTEL_RDT_ALLOCATION > gMaximumBasicFunction) { + return; + } + + AsmCpuidEx ( + CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_ENUMERATION_SUB_LEAF, + NULL, &Ebx.Uint32, NULL, NULL + ); + Print (L"CPUID_INTEL_RDT_ALLOCATION (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_ENUMERATION_SUB_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", 0, Ebx.Uint32, 0, 0); + PRINT_BIT_FIELD (Ebx, L3CacheAllocation); + PRINT_BIT_FIELD (Ebx, L2CacheAllocation); + PRINT_BIT_FIELD (Ebx, MemoryBandwidth); + CpuidIntelRdtAllocationMemoryBandwidthSubLeaf (); + CpuidIntelRdtAllocationL3CacheSubLeaf (); + CpuidIntelRdtAllocationL2CacheSubLeaf (); +} + +/** + Display Sub-Leaf 0 Enumeration of Intel SGX Capabilities. + +**/ +VOID +CpuidEnumerationOfIntelSgxCapabilities0SubLeaf ( + VOID + ) +{ + CPUID_INTEL_SGX_CAPABILITIES_0_SUB_LEAF_EAX Eax; + UINT32 Ebx; + CPUID_INTEL_SGX_CAPABILITIES_0_SUB_LEAF_EDX Edx; + + AsmCpuidEx ( + CPUID_INTEL_SGX, CPUID_INTEL_SGX_CAPABILITIES_0_SUB_LEAF, + &Eax.Uint32, &Ebx, NULL, &Edx.Uint32 + ); + Print (L"CPUID_INTEL_SGX (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_SGX, CPUID_INTEL_SGX_CAPABILITIES_0_SUB_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx, 0, Edx.Uint32); + PRINT_BIT_FIELD (Eax, SGX1); + PRINT_BIT_FIELD (Eax, SGX2); + PRINT_BIT_FIELD (Eax, ENCLV); + PRINT_BIT_FIELD (Eax, ENCLS); + PRINT_BIT_FIELD (Edx, MaxEnclaveSize_Not64); + PRINT_BIT_FIELD (Edx, MaxEnclaveSize_64); +} + +/** + Display Sub-Leaf 1 Enumeration of Intel SGX Capabilities. + +**/ +VOID +CpuidEnumerationOfIntelSgxCapabilities1SubLeaf ( + VOID + ) +{ + UINT32 Eax; + UINT32 Ebx; + UINT32 Ecx; + UINT32 Edx; + + AsmCpuidEx ( + CPUID_INTEL_SGX, CPUID_INTEL_SGX_CAPABILITIES_1_SUB_LEAF, + &Eax, &Ebx, &Ecx, &Edx + ); + Print (L"CPUID_INTEL_SGX (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_SGX, CPUID_INTEL_SGX_CAPABILITIES_1_SUB_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax, Ebx, Ecx, Edx); +} + +/** + Display Sub-Leaf Index 2 or Higher Enumeration of Intel SGX Resources. + +**/ +VOID +CpuidEnumerationOfIntelSgxResourcesSubLeaf ( + VOID + ) +{ + CPUID_INTEL_SGX_CAPABILITIES_RESOURCES_SUB_LEAF_EAX Eax; + CPUID_INTEL_SGX_CAPABILITIES_RESOURCES_SUB_LEAF_EBX Ebx; + CPUID_INTEL_SGX_CAPABILITIES_RESOURCES_SUB_LEAF_ECX Ecx; + CPUID_INTEL_SGX_CAPABILITIES_RESOURCES_SUB_LEAF_EDX Edx; + UINT32 SubLeaf; + + SubLeaf = CPUID_INTEL_SGX_CAPABILITIES_RESOURCES_SUB_LEAF; + do { + AsmCpuidEx ( + CPUID_INTEL_SGX, SubLeaf, + &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32 + ); + if (Eax.Bits.SubLeafType == 0x1) { + Print (L"CPUID_INTEL_SGX (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_SGX, SubLeaf); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32); + PRINT_BIT_FIELD (Eax, SubLeafType); + PRINT_BIT_FIELD (Eax, LowAddressOfEpcSection); + PRINT_BIT_FIELD (Ebx, HighAddressOfEpcSection); + PRINT_BIT_FIELD (Ecx, EpcSection); + PRINT_BIT_FIELD (Ecx, LowSizeOfEpcSection); + PRINT_BIT_FIELD (Edx, HighSizeOfEpcSection); + } + SubLeaf++; + } while (Eax.Bits.SubLeafType == 0x1); +} + +/** + Display Intel SGX Resource Enumeration. + +**/ +VOID +CpuidEnumerationOfIntelSgx ( + VOID + ) +{ + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_EBX Ebx; + + if (CPUID_INTEL_SGX > gMaximumBasicFunction) { + return; + } + + AsmCpuidEx ( + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_SUB_LEAF_INFO, + NULL, &Ebx.Uint32, NULL, NULL + ); + if (Ebx.Bits.SGX != 1) { + // + // Only if CPUID.(EAX=07H, ECX=0H):EBX.SGX = 1, the processor has support + // for Intel SGX. + // + return; + } + + CpuidEnumerationOfIntelSgxCapabilities0SubLeaf (); + CpuidEnumerationOfIntelSgxCapabilities1SubLeaf (); + CpuidEnumerationOfIntelSgxResourcesSubLeaf (); +} + +/** + Display CPUID_INTEL_PROCESSOR_TRACE sub-leafs. + + @param[in] MaximumSubLeaf Maximum sub-leaf index for CPUID_INTEL_PROCESSOR_TRACE. + +**/ +VOID +CpuidIntelProcessorTraceSubLeaf ( + UINT32 MaximumSubLeaf + ) +{ + UINT32 SubLeaf; + CPUID_INTEL_PROCESSOR_TRACE_SUB_LEAF_EAX Eax; + CPUID_INTEL_PROCESSOR_TRACE_SUB_LEAF_EBX Ebx; + + for (SubLeaf = CPUID_INTEL_PROCESSOR_TRACE_SUB_LEAF; SubLeaf <= MaximumSubLeaf; SubLeaf++) { + AsmCpuidEx ( + CPUID_INTEL_PROCESSOR_TRACE, SubLeaf, + &Eax.Uint32, &Ebx.Uint32, NULL, NULL + ); + Print (L"CPUID_INTEL_PROCESSOR_TRACE (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_PROCESSOR_TRACE, SubLeaf); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, 0, 0); + PRINT_BIT_FIELD (Eax, ConfigurableAddressRanges); + PRINT_BIT_FIELD (Eax, MtcPeriodEncodings); + PRINT_BIT_FIELD (Ebx, CycleThresholdEncodings); + PRINT_BIT_FIELD (Ebx, PsbFrequencyEncodings); + } +} + +/** + Display CPUID_INTEL_PROCESSOR_TRACE main leaf and sub-leafs. + +**/ +VOID +CpuidIntelProcessorTraceMainLeaf ( + VOID + ) +{ + UINT32 Eax; + CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF_EBX Ebx; + CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF_ECX Ecx; + + if (CPUID_INTEL_PROCESSOR_TRACE > gMaximumBasicFunction) { + return; + } + + AsmCpuidEx ( + CPUID_INTEL_PROCESSOR_TRACE, CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF, + &Eax, &Ebx.Uint32, &Ecx.Uint32, NULL + ); + Print (L"CPUID_INTEL_PROCESSOR_TRACE (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_PROCESSOR_TRACE, CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax, Ebx.Uint32, Ecx.Uint32, 0); + PRINT_VALUE (Eax, MaximumSubLeaf); + PRINT_BIT_FIELD (Ebx, Cr3Filter); + PRINT_BIT_FIELD (Ebx, ConfigurablePsb); + PRINT_BIT_FIELD (Ebx, IpTraceStopFiltering); + PRINT_BIT_FIELD (Ebx, Mtc); + PRINT_BIT_FIELD (Ebx, PTWrite); + PRINT_BIT_FIELD (Ebx, PowerEventTrace); + PRINT_BIT_FIELD (Ecx, RTIT); + PRINT_BIT_FIELD (Ecx, ToPA); + PRINT_BIT_FIELD (Ecx, SingleRangeOutput); + PRINT_BIT_FIELD (Ecx, TraceTransportSubsystem); + PRINT_BIT_FIELD (Ecx, LIP); + + CpuidIntelProcessorTraceSubLeaf (Eax); +} + +/** + Display CPUID_TIME_STAMP_COUNTER leaf. + +**/ +VOID +CpuidTimeStampCounter ( + VOID + ) +{ + UINT32 Eax; + UINT32 Ebx; + UINT32 Ecx; + + if (CPUID_TIME_STAMP_COUNTER > gMaximumBasicFunction) { + return; + } + + AsmCpuid (CPUID_TIME_STAMP_COUNTER, &Eax, &Ebx, &Ecx, NULL); + Print (L"CPUID_TIME_STAMP_COUNTER (Leaf %08x)\n", CPUID_TIME_STAMP_COUNTER); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax, Ebx, Ecx, 0); +} + +/** + Display CPUID_PROCESSOR_FREQUENCY leaf. + +**/ +VOID +CpuidProcessorFrequency ( + VOID + ) +{ + CPUID_PROCESSOR_FREQUENCY_EAX Eax; + CPUID_PROCESSOR_FREQUENCY_EBX Ebx; + CPUID_PROCESSOR_FREQUENCY_ECX Ecx; + + if (CPUID_PROCESSOR_FREQUENCY > gMaximumBasicFunction) { + return; + } + + AsmCpuid (CPUID_PROCESSOR_FREQUENCY, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, NULL); + Print (L"CPUID_PROCESSOR_FREQUENCY (Leaf %08x)\n", CPUID_PROCESSOR_FREQUENCY); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, 0); + PRINT_BIT_FIELD (Eax, ProcessorBaseFrequency); + PRINT_BIT_FIELD (Ebx, MaximumFrequency); + PRINT_BIT_FIELD (Ecx, BusFrequency); +} + +/** + Display CPUID_SOC_VENDOR sub-leafs that contain the SoC Vendor Brand String. + Also display these sub-leafs as a single SoC Vendor Brand String. + +**/ +VOID +CpuidSocVendorBrandString ( + VOID + ) +{ + CPUID_SOC_VENDOR_BRAND_STRING_DATA Eax; + CPUID_SOC_VENDOR_BRAND_STRING_DATA Ebx; + CPUID_SOC_VENDOR_BRAND_STRING_DATA Ecx; + CPUID_SOC_VENDOR_BRAND_STRING_DATA Edx; + // + // Array to store brand string from 3 brand string leafs with + // 4 32-bit brand string values per leaf and an extra value to + // null terminate the string. + // + UINT32 BrandString[3 * 4 + 1]; + + AsmCpuidEx ( + CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_BRAND_STRING1, + &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32 + ); + Print (L"CPUID_SOC_VENDOR (Leaf %08x, Sub-Leaf %08x)\n", CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_BRAND_STRING1); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32); + BrandString[0] = Eax.Uint32; + BrandString[1] = Ebx.Uint32; + BrandString[2] = Ecx.Uint32; + BrandString[3] = Edx.Uint32; + + AsmCpuidEx ( + CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_BRAND_STRING2, + &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32 + ); + Print (L"CPUID_SOC_VENDOR (Leaf %08x, Sub-Leaf %08x)\n", CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_BRAND_STRING2); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32); + BrandString[4] = Eax.Uint32; + BrandString[5] = Ebx.Uint32; + BrandString[6] = Ecx.Uint32; + BrandString[7] = Edx.Uint32; + + AsmCpuidEx ( + CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_BRAND_STRING3, + &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32 + ); + Print (L"CPUID_SOC_VENDOR (Leaf %08x, Sub-Leaf %08x)\n", CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_BRAND_STRING3); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32); + BrandString[8] = Eax.Uint32; + BrandString[9] = Ebx.Uint32; + BrandString[10] = Ecx.Uint32; + BrandString[11] = Edx.Uint32; + + BrandString[12] = 0; + + Print (L"Vendor Brand String = %a\n", (CHAR8 *)BrandString); +} + +/** + Display CPUID_SOC_VENDOR main leaf and sub-leafs. + +**/ +VOID +CpuidSocVendor ( + VOID + ) +{ + UINT32 Eax; + CPUID_SOC_VENDOR_MAIN_LEAF_EBX Ebx; + UINT32 Ecx; + UINT32 Edx; + + if (CPUID_SOC_VENDOR > gMaximumBasicFunction) { + return; + } + + AsmCpuidEx ( + CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_MAIN_LEAF, + &Eax, &Ebx.Uint32, &Ecx, &Edx + ); + Print (L"CPUID_SOC_VENDOR (Leaf %08x, Sub-Leaf %08x)\n", CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_MAIN_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax, Ebx.Uint32, Ecx, Edx); + if (Eax < 3) { + Print (L" Not Supported\n"); + return; + } + PRINT_VALUE (Eax, MaxSOCID_Index); + PRINT_BIT_FIELD (Ebx, SocVendorId); + PRINT_BIT_FIELD (Ebx, IsVendorScheme); + PRINT_VALUE (Ecx, ProjectID); + PRINT_VALUE (Edx, SteppingID); + CpuidSocVendorBrandString (); +} + +/** + Display CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS main leaf and sub-leafs. + +**/ +VOID +CpuidDeterministicAddressTranslationParameters ( + VOID + ) +{ + UINT32 Eax; + CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS_EBX Ebx; + UINT32 Ecx; + CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS_EDX Edx; + + if (CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS > gMaximumBasicFunction) { + return; + } + + AsmCpuidEx ( + CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS, + CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS_MAIN_LEAF, + &Eax, &Ebx.Uint32, &Ecx, &Edx.Uint32 + ); + Print (L"CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS (Leaf %08x, Sub-Leaf %08x)\n", CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS, CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS_MAIN_LEAF); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax, Ebx.Uint32, Ecx, Edx.Uint32); + + PRINT_VALUE (Eax, MaxID_Index); + PRINT_BIT_FIELD (Ebx, Page4K); + PRINT_BIT_FIELD (Ebx, Page2M); + PRINT_BIT_FIELD (Ebx, Page4M); + PRINT_BIT_FIELD (Ebx, Page1G); + PRINT_BIT_FIELD (Ebx, Partitioning); + PRINT_BIT_FIELD (Ebx, Way); + + PRINT_VALUE (Ecx, NumberOfSets); + + PRINT_BIT_FIELD (Edx, TranslationCacheType); + PRINT_BIT_FIELD (Edx, TranslationCacheLevel); + PRINT_BIT_FIELD (Edx, FullyAssociative); + PRINT_BIT_FIELD (Edx, MaximumNum); +} + +/** + Display CPUID_EXTENDED_FUNCTION leaf. + +**/ +VOID +CpuidExtendedFunction ( + VOID + ) +{ + UINT32 Eax; + + AsmCpuid (CPUID_EXTENDED_FUNCTION, &Eax, NULL, NULL, NULL); + Print (L"CPUID_EXTENDED_FUNCTION (Leaf %08x)\n", CPUID_EXTENDED_FUNCTION); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax, 0, 0, 0); + PRINT_VALUE (Eax, MaximumExtendedFunction); + + gMaximumExtendedFunction = Eax; +} + +/** + Display CPUID_EXTENDED_CPU_SIG leaf. + +**/ +VOID +CpuidExtendedCpuSig ( + VOID + ) +{ + UINT32 Eax; + CPUID_EXTENDED_CPU_SIG_ECX Ecx; + CPUID_EXTENDED_CPU_SIG_EDX Edx; + + if (CPUID_EXTENDED_CPU_SIG > gMaximumExtendedFunction) { + return; + } + + AsmCpuid (CPUID_EXTENDED_CPU_SIG, &Eax, NULL, &Ecx.Uint32, &Edx.Uint32); + Print (L"CPUID_EXTENDED_CPU_SIG (Leaf %08x)\n", CPUID_EXTENDED_CPU_SIG); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax, 0, Ecx.Uint32, Edx.Uint32); + PRINT_BIT_FIELD (Ecx, LAHF_SAHF); + PRINT_BIT_FIELD (Ecx, LZCNT); + PRINT_BIT_FIELD (Ecx, PREFETCHW); + PRINT_BIT_FIELD (Edx, SYSCALL_SYSRET); + PRINT_BIT_FIELD (Edx, NX); + PRINT_BIT_FIELD (Edx, Page1GB); + PRINT_BIT_FIELD (Edx, RDTSCP); + PRINT_BIT_FIELD (Edx, LM); +} + +/** + Display CPUID_BRAND_STRING1, CPUID_BRAND_STRING2 and CPUID_BRAND_STRING3 + leafs. Also display these three leafs as a single brand string. + +**/ +VOID +CpuidProcessorBrandString ( + VOID + ) +{ + CPUID_BRAND_STRING_DATA Eax; + CPUID_BRAND_STRING_DATA Ebx; + CPUID_BRAND_STRING_DATA Ecx; + CPUID_BRAND_STRING_DATA Edx; + // + // Array to store brand string from 3 brand string leafs with + // 4 32-bit brand string values per leaf and an extra value to + // null terminate the string. + // + UINT32 BrandString[3 * 4 + 1]; + + if (CPUID_BRAND_STRING1 <= gMaximumExtendedFunction) { + AsmCpuid (CPUID_BRAND_STRING1, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32); + Print (L"CPUID_BRAND_STRING1 (Leaf %08x)\n", CPUID_BRAND_STRING1); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32); + BrandString[0] = Eax.Uint32; + BrandString[1] = Ebx.Uint32; + BrandString[2] = Ecx.Uint32; + BrandString[3] = Edx.Uint32; + } + + if (CPUID_BRAND_STRING2 <= gMaximumExtendedFunction) { + AsmCpuid (CPUID_BRAND_STRING2, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32); + Print (L"CPUID_BRAND_STRING2 (Leaf %08x)\n", CPUID_BRAND_STRING2); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32); + BrandString[4] = Eax.Uint32; + BrandString[5] = Ebx.Uint32; + BrandString[6] = Ecx.Uint32; + BrandString[7] = Edx.Uint32; + } + + if (CPUID_BRAND_STRING3 <= gMaximumExtendedFunction) { + AsmCpuid (CPUID_BRAND_STRING3, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32); + Print (L"CPUID_BRAND_STRING3 (Leaf %08x)\n", CPUID_BRAND_STRING3); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32); + BrandString[8] = Eax.Uint32; + BrandString[9] = Ebx.Uint32; + BrandString[10] = Ecx.Uint32; + BrandString[11] = Edx.Uint32; + } + + BrandString[12] = 0; + + Print (L"Brand String = %a\n", (CHAR8 *)BrandString); +} + +/** + Display CPUID_EXTENDED_CACHE_INFO leaf. + +**/ +VOID +CpuidExtendedCacheInfo ( + VOID + ) +{ + CPUID_EXTENDED_CACHE_INFO_ECX Ecx; + + if (CPUID_EXTENDED_CACHE_INFO > gMaximumExtendedFunction) { + return; + } + + AsmCpuid (CPUID_EXTENDED_CACHE_INFO, NULL, NULL, &Ecx.Uint32, NULL); + Print (L"CPUID_EXTENDED_CACHE_INFO (Leaf %08x)\n", CPUID_EXTENDED_CACHE_INFO); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", 0, 0, Ecx.Uint32, 0); + PRINT_BIT_FIELD (Ecx, CacheLineSize); + PRINT_BIT_FIELD (Ecx, L2Associativity); + PRINT_BIT_FIELD (Ecx, CacheSize); +} + +/** + Display CPUID_EXTENDED_TIME_STAMP_COUNTER leaf. + +**/ +VOID +CpuidExtendedTimeStampCounter ( + VOID + ) +{ + CPUID_EXTENDED_TIME_STAMP_COUNTER_EDX Edx; + + if (CPUID_EXTENDED_TIME_STAMP_COUNTER > gMaximumExtendedFunction) { + return; + } + + AsmCpuid (CPUID_EXTENDED_TIME_STAMP_COUNTER, NULL, NULL, NULL, &Edx.Uint32); + Print (L"CPUID_EXTENDED_TIME_STAMP_COUNTER (Leaf %08x)\n", CPUID_EXTENDED_TIME_STAMP_COUNTER); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", 0, 0, 0, Edx.Uint32); + PRINT_BIT_FIELD (Edx, InvariantTsc); +} + +/** + Display CPUID_VIR_PHY_ADDRESS_SIZE leaf. + +**/ +VOID +CpuidVirPhyAddressSize ( + VOID + ) +{ + CPUID_VIR_PHY_ADDRESS_SIZE_EAX Eax; + + if (CPUID_VIR_PHY_ADDRESS_SIZE > gMaximumExtendedFunction) { + return; + } + + AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &Eax.Uint32, NULL, NULL, NULL); + Print (L"CPUID_VIR_PHY_ADDRESS_SIZE (Leaf %08x)\n", CPUID_VIR_PHY_ADDRESS_SIZE); + Print (L" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", Eax.Uint32, 0, 0, 0); + PRINT_BIT_FIELD (Eax, PhysicalAddressBits); + PRINT_BIT_FIELD (Eax, LinearAddressBits); +} + +/** + The user Entry Point for Application. The user code starts with this function + as the real entry point for the application. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + Print (L"UEFI CPUID Version 0.5\n"); + + CpuidSignature (); + CpuidVersionInfo (); + CpuidCacheInfo (); + CpuidSerialNumber (); + CpuidCacheParams(); + CpuidMonitorMwait (); + CpuidThermalPowerManagement (); + CpuidStructuredExtendedFeatureFlags (); + CpuidDirectCacheAccessInfo(); + CpuidArchitecturalPerformanceMonitoring (); + CpuidExtendedTopology (CPUID_EXTENDED_TOPOLOGY); + CpuidExtendedStateMainLeaf (); + CpuidIntelRdtMonitoringEnumerationSubLeaf (); + CpuidIntelRdtMonitoringL3CacheCapabilitySubLeaf (); + CpuidIntelRdtAllocationMainLeaf (); + CpuidEnumerationOfIntelSgx (); + CpuidIntelProcessorTraceMainLeaf (); + CpuidTimeStampCounter (); + CpuidProcessorFrequency (); + CpuidSocVendor (); + CpuidDeterministicAddressTranslationParameters (); + CpuidExtendedTopology (CPUID_V2_EXTENDED_TOPOLOGY); + CpuidExtendedFunction (); + CpuidExtendedCpuSig (); + CpuidProcessorBrandString (); + CpuidExtendedCacheInfo (); + CpuidExtendedTimeStampCounter (); + CpuidVirPhyAddressSize (); + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Application/Cpuid/Cpuid.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Application/Cpuid/Cpuid.inf new file mode 100644 index 00000000..c992768d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Application/Cpuid/Cpuid.inf @@ -0,0 +1,43 @@ +## @file +# UEFI Application to display CPUID leaf information. +# +# This UEFI application displays the registers values returned by CPUID for +# all the CPUID leafs and sub-leafs that a CPU supports. It also displays +# the values of all the bit fields in the registers returned by each CPUID +# leaf and sub-leaf. +# +# Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Cpuid + MODULE_UNI_FILE = Cpuid.uni + FILE_GUID = 4AE7E1E8-9DFE-4e3e-85B4-A5F6ABD470FB + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 0.5 + ENTRY_POINT = UefiMain + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + Cpuid.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + UefiApplicationEntryPoint + BaseLib + UefiLib + +[UserExtensions.TianoCore."ExtraFiles"] + CpuidExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Application/Cpuid/Cpuid.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Application/Cpuid/Cpuid.uni new file mode 100644 index 00000000..b22b5984 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Application/Cpuid/Cpuid.uni @@ -0,0 +1,17 @@ +// /** @file
+// UEFI Application to display CPUID leaf information.
+//
+// This UEFI application displays the registers values returned by CPUID for
+// all the CPUID leafs and sub-leafs that a CPU supports. It also displays
+// the values of all the bit fields in the registers returned by each CPUID
+// leaf and sub-leaf.
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "UEFI Application to display CPUID leaf information"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This UEFI application displays the registers values returned by CPUID for all the CPUID leafs and sub-leafs that a CPU supports. It also displays the values of all the bit fields in the registers returned by each CPUID leaf and sub-leaf."
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Application/Cpuid/CpuidExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Application/Cpuid/CpuidExtra.uni new file mode 100644 index 00000000..1b3e5627 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Application/Cpuid/CpuidExtra.uni @@ -0,0 +1,17 @@ +// /** @file
+// UEFI Application to display CPUID leaf information.
+//
+// This UEFI application displays the registers values returned by CPUID for
+// all the CPUID leafs and sub-leafs that a CPU supports. It also displays
+// the values of all the bit fields in the registers returned by each CPUID
+// leaf and sub-leaf.
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"CPUID Application"
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxe.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxe.c new file mode 100644 index 00000000..999e2801 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxe.c @@ -0,0 +1,1215 @@ +/** @file + CPU DXE Module to produce CPU ARCH Protocol. + + Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuDxe.h" +#include "CpuMp.h" +#include "CpuPageTable.h" + +// +// Global Variables +// +BOOLEAN InterruptState = FALSE; +EFI_HANDLE mCpuHandle = NULL; +BOOLEAN mIsFlushingGCD; +BOOLEAN mIsAllocatingPageTable = FALSE; +UINT64 mValidMtrrAddressMask; +UINT64 mValidMtrrBitsMask; +UINT64 mTimerPeriod = 0; + +FIXED_MTRR mFixedMtrrTable[] = { + { + MSR_IA32_MTRR_FIX64K_00000, + 0, + 0x10000 + }, + { + MSR_IA32_MTRR_FIX16K_80000, + 0x80000, + 0x4000 + }, + { + MSR_IA32_MTRR_FIX16K_A0000, + 0xA0000, + 0x4000 + }, + { + MSR_IA32_MTRR_FIX4K_C0000, + 0xC0000, + 0x1000 + }, + { + MSR_IA32_MTRR_FIX4K_C8000, + 0xC8000, + 0x1000 + }, + { + MSR_IA32_MTRR_FIX4K_D0000, + 0xD0000, + 0x1000 + }, + { + MSR_IA32_MTRR_FIX4K_D8000, + 0xD8000, + 0x1000 + }, + { + MSR_IA32_MTRR_FIX4K_E0000, + 0xE0000, + 0x1000 + }, + { + MSR_IA32_MTRR_FIX4K_E8000, + 0xE8000, + 0x1000 + }, + { + MSR_IA32_MTRR_FIX4K_F0000, + 0xF0000, + 0x1000 + }, + { + MSR_IA32_MTRR_FIX4K_F8000, + 0xF8000, + 0x1000 + }, +}; + + +EFI_CPU_ARCH_PROTOCOL gCpu = { + CpuFlushCpuDataCache, + CpuEnableInterrupt, + CpuDisableInterrupt, + CpuGetInterruptState, + CpuInit, + CpuRegisterInterruptHandler, + CpuGetTimerValue, + CpuSetMemoryAttributes, + 1, // NumberOfTimers + 4 // DmaBufferAlignment +}; + +// +// CPU Arch Protocol Functions +// + +/** + Flush CPU data cache. If the instruction cache is fully coherent + with all DMA operations then function can just return EFI_SUCCESS. + + @param This Protocol instance structure + @param Start Physical address to start flushing from. + @param Length Number of bytes to flush. Round up to chipset + granularity. + @param FlushType Specifies the type of flush operation to perform. + + @retval EFI_SUCCESS If cache was flushed + @retval EFI_UNSUPPORTED If flush type is not supported. + @retval EFI_DEVICE_ERROR If requested range could not be flushed. + +**/ +EFI_STATUS +EFIAPI +CpuFlushCpuDataCache ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS Start, + IN UINT64 Length, + IN EFI_CPU_FLUSH_TYPE FlushType + ) +{ + if (FlushType == EfiCpuFlushTypeWriteBackInvalidate) { + AsmWbinvd (); + return EFI_SUCCESS; + } else if (FlushType == EfiCpuFlushTypeInvalidate) { + AsmInvd (); + return EFI_SUCCESS; + } else { + return EFI_UNSUPPORTED; + } +} + + +/** + Enables CPU interrupts. + + @param This Protocol instance structure + + @retval EFI_SUCCESS If interrupts were enabled in the CPU + @retval EFI_DEVICE_ERROR If interrupts could not be enabled on the CPU. + +**/ +EFI_STATUS +EFIAPI +CpuEnableInterrupt ( + IN EFI_CPU_ARCH_PROTOCOL *This + ) +{ + EnableInterrupts (); + + InterruptState = TRUE; + return EFI_SUCCESS; +} + + +/** + Disables CPU interrupts. + + @param This Protocol instance structure + + @retval EFI_SUCCESS If interrupts were disabled in the CPU. + @retval EFI_DEVICE_ERROR If interrupts could not be disabled on the CPU. + +**/ +EFI_STATUS +EFIAPI +CpuDisableInterrupt ( + IN EFI_CPU_ARCH_PROTOCOL *This + ) +{ + DisableInterrupts (); + + InterruptState = FALSE; + return EFI_SUCCESS; +} + + +/** + Return the state of interrupts. + + @param This Protocol instance structure + @param State Pointer to the CPU's current interrupt state + + @retval EFI_SUCCESS If interrupts were disabled in the CPU. + @retval EFI_INVALID_PARAMETER State is NULL. + +**/ +EFI_STATUS +EFIAPI +CpuGetInterruptState ( + IN EFI_CPU_ARCH_PROTOCOL *This, + OUT BOOLEAN *State + ) +{ + if (State == NULL) { + return EFI_INVALID_PARAMETER; + } + + *State = InterruptState; + return EFI_SUCCESS; +} + + +/** + Generates an INIT to the CPU. + + @param This Protocol instance structure + @param InitType Type of CPU INIT to perform + + @retval EFI_SUCCESS If CPU INIT occurred. This value should never be + seen. + @retval EFI_DEVICE_ERROR If CPU INIT failed. + @retval EFI_UNSUPPORTED Requested type of CPU INIT not supported. + +**/ +EFI_STATUS +EFIAPI +CpuInit ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_CPU_INIT_TYPE InitType + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Registers a function to be called from the CPU interrupt handler. + + @param This Protocol instance structure + @param InterruptType Defines which interrupt to hook. IA-32 + valid range is 0x00 through 0xFF + @param InterruptHandler A pointer to a function of type + EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. A null + pointer is an error condition. + + @retval EFI_SUCCESS If handler installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler + for InterruptType was previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for + InterruptType was not previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType + is not supported. + +**/ +EFI_STATUS +EFIAPI +CpuRegisterInterruptHandler ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ) +{ + return RegisterCpuInterruptHandler (InterruptType, InterruptHandler); +} + + +/** + Returns a timer value from one of the CPU's internal timers. There is no + inherent time interval between ticks but is a function of the CPU frequency. + + @param This - Protocol instance structure. + @param TimerIndex - Specifies which CPU timer is requested. + @param TimerValue - Pointer to the returned timer value. + @param TimerPeriod - A pointer to the amount of time that passes + in femtoseconds (10-15) for each increment + of TimerValue. If TimerValue does not + increment at a predictable rate, then 0 is + returned. The amount of time that has + passed between two calls to GetTimerValue() + can be calculated with the formula + (TimerValue2 - TimerValue1) * TimerPeriod. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS - If the CPU timer count was returned. + @retval EFI_UNSUPPORTED - If the CPU does not have any readable timers. + @retval EFI_DEVICE_ERROR - If an error occurred while reading the timer. + @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL. + +**/ +EFI_STATUS +EFIAPI +CpuGetTimerValue ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN UINT32 TimerIndex, + OUT UINT64 *TimerValue, + OUT UINT64 *TimerPeriod OPTIONAL + ) +{ + UINT64 BeginValue; + UINT64 EndValue; + + if (TimerValue == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (TimerIndex != 0) { + return EFI_INVALID_PARAMETER; + } + + *TimerValue = AsmReadTsc (); + + if (TimerPeriod != NULL) { + if (mTimerPeriod == 0) { + // + // Read time stamp counter before and after delay of 100 microseconds + // + BeginValue = AsmReadTsc (); + MicroSecondDelay (100); + EndValue = AsmReadTsc (); + // + // Calculate the actual frequency + // + mTimerPeriod = DivU64x64Remainder ( + MultU64x32 ( + 1000 * 1000 * 1000, + 100 + ), + EndValue - BeginValue, + NULL + ); + } + *TimerPeriod = mTimerPeriod; + } + + return EFI_SUCCESS; +} + +/** + A minimal wrapper function that allows MtrrSetAllMtrrs() to be passed to + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() as Procedure. + + @param[in] Buffer Pointer to an MTRR_SETTINGS object, to be passed to + MtrrSetAllMtrrs(). +**/ +VOID +EFIAPI +SetMtrrsFromBuffer ( + IN VOID *Buffer + ) +{ + MtrrSetAllMtrrs (Buffer); +} + +/** + Implementation of SetMemoryAttributes() service of CPU Architecture Protocol. + + This function modifies the attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param BaseAddress The physical address that is the start address of a memory region. + @param Length The size in bytes of the memory region. + @param Attributes The bit mask of attributes to set for the memory region. + + @retval EFI_SUCCESS The attributes were set for the memory region. + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be set together. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +CpuSetMemoryAttributes ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ) +{ + RETURN_STATUS Status; + MTRR_MEMORY_CACHE_TYPE CacheType; + EFI_STATUS MpStatus; + EFI_MP_SERVICES_PROTOCOL *MpService; + MTRR_SETTINGS MtrrSettings; + UINT64 CacheAttributes; + UINT64 MemoryAttributes; + MTRR_MEMORY_CACHE_TYPE CurrentCacheType; + + // + // If this function is called because GCD SetMemorySpaceAttributes () is called + // by RefreshGcdMemoryAttributes (), then we are just synchronizing GCD memory + // map with MTRR values. So there is no need to modify MTRRs, just return immediately + // to avoid unnecessary computing. + // + if (mIsFlushingGCD) { + DEBUG((DEBUG_VERBOSE, " Flushing GCD\n")); + return EFI_SUCCESS; + } + + // + // During memory attributes updating, new pages may be allocated to setup + // smaller granularity of page table. Page allocation action might then cause + // another calling of CpuSetMemoryAttributes() recursively, due to memory + // protection policy configured (such as PcdDxeNxMemoryProtectionPolicy). + // Since this driver will always protect memory used as page table by itself, + // there's no need to apply protection policy requested from memory service. + // So it's safe to just return EFI_SUCCESS if this time of calling is caused + // by page table memory allocation. + // + if (mIsAllocatingPageTable) { + DEBUG((DEBUG_VERBOSE, " Allocating page table memory\n")); + return EFI_SUCCESS; + } + + CacheAttributes = Attributes & EFI_CACHE_ATTRIBUTE_MASK; + MemoryAttributes = Attributes & EFI_MEMORY_ATTRIBUTE_MASK; + + if (Attributes != (CacheAttributes | MemoryAttributes)) { + return EFI_INVALID_PARAMETER; + } + + if (CacheAttributes != 0) { + if (!IsMtrrSupported ()) { + return EFI_UNSUPPORTED; + } + + switch (CacheAttributes) { + case EFI_MEMORY_UC: + CacheType = CacheUncacheable; + break; + + case EFI_MEMORY_WC: + CacheType = CacheWriteCombining; + break; + + case EFI_MEMORY_WT: + CacheType = CacheWriteThrough; + break; + + case EFI_MEMORY_WP: + CacheType = CacheWriteProtected; + break; + + case EFI_MEMORY_WB: + CacheType = CacheWriteBack; + break; + + default: + return EFI_INVALID_PARAMETER; + } + CurrentCacheType = MtrrGetMemoryAttribute(BaseAddress); + if (CurrentCacheType != CacheType) { + // + // call MTRR library function + // + Status = MtrrSetMemoryAttribute ( + BaseAddress, + Length, + CacheType + ); + + if (!RETURN_ERROR (Status)) { + MpStatus = gBS->LocateProtocol ( + &gEfiMpServiceProtocolGuid, + NULL, + (VOID **)&MpService + ); + // + // Synchronize the update with all APs + // + if (!EFI_ERROR (MpStatus)) { + MtrrGetAllMtrrs (&MtrrSettings); + MpStatus = MpService->StartupAllAPs ( + MpService, // This + SetMtrrsFromBuffer, // Procedure + FALSE, // SingleThread + NULL, // WaitEvent + 0, // TimeoutInMicrosecsond + &MtrrSettings, // ProcedureArgument + NULL // FailedCpuList + ); + ASSERT (MpStatus == EFI_SUCCESS || MpStatus == EFI_NOT_STARTED); + } + } + if (EFI_ERROR(Status)) { + return Status; + } + } + } + + // + // Set memory attribute by page table + // + return AssignMemoryPageAttributes (NULL, BaseAddress, Length, MemoryAttributes, NULL); +} + +/** + Initializes the valid bits mask and valid address mask for MTRRs. + + This function initializes the valid bits mask and valid address mask for MTRRs. + +**/ +VOID +InitializeMtrrMask ( + VOID + ) +{ + UINT32 RegEax; + UINT8 PhysicalAddressBits; + + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + + if (RegEax >= 0x80000008) { + AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); + + PhysicalAddressBits = (UINT8) RegEax; + } else { + PhysicalAddressBits = 36; + } + + mValidMtrrBitsMask = LShiftU64 (1, PhysicalAddressBits) - 1; + mValidMtrrAddressMask = mValidMtrrBitsMask & 0xfffffffffffff000ULL; +} + +/** + Gets GCD Mem Space type from MTRR Type. + + This function gets GCD Mem Space type from MTRR Type. + + @param MtrrAttributes MTRR memory type + + @return GCD Mem Space type + +**/ +UINT64 +GetMemorySpaceAttributeFromMtrrType ( + IN UINT8 MtrrAttributes + ) +{ + switch (MtrrAttributes) { + case MTRR_CACHE_UNCACHEABLE: + return EFI_MEMORY_UC; + case MTRR_CACHE_WRITE_COMBINING: + return EFI_MEMORY_WC; + case MTRR_CACHE_WRITE_THROUGH: + return EFI_MEMORY_WT; + case MTRR_CACHE_WRITE_PROTECTED: + return EFI_MEMORY_WP; + case MTRR_CACHE_WRITE_BACK: + return EFI_MEMORY_WB; + default: + return 0; + } +} + +/** + Searches memory descriptors covered by given memory range. + + This function searches into the Gcd Memory Space for descriptors + (from StartIndex to EndIndex) that contains the memory range + specified by BaseAddress and Length. + + @param MemorySpaceMap Gcd Memory Space Map as array. + @param NumberOfDescriptors Number of descriptors in map. + @param BaseAddress BaseAddress for the requested range. + @param Length Length for the requested range. + @param StartIndex Start index into the Gcd Memory Space Map. + @param EndIndex End index into the Gcd Memory Space Map. + + @retval EFI_SUCCESS Search successfully. + @retval EFI_NOT_FOUND The requested descriptors does not exist. + +**/ +EFI_STATUS +SearchGcdMemorySpaces ( + IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap, + IN UINTN NumberOfDescriptors, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + OUT UINTN *StartIndex, + OUT UINTN *EndIndex + ) +{ + UINTN Index; + + *StartIndex = 0; + *EndIndex = 0; + for (Index = 0; Index < NumberOfDescriptors; Index++) { + if (BaseAddress >= MemorySpaceMap[Index].BaseAddress && + BaseAddress < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) { + *StartIndex = Index; + } + if (BaseAddress + Length - 1 >= MemorySpaceMap[Index].BaseAddress && + BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) { + *EndIndex = Index; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; +} + +/** + Sets the attributes for a specified range in Gcd Memory Space Map. + + This function sets the attributes for a specified range in + Gcd Memory Space Map. + + @param MemorySpaceMap Gcd Memory Space Map as array + @param NumberOfDescriptors Number of descriptors in map + @param BaseAddress BaseAddress for the range + @param Length Length for the range + @param Attributes Attributes to set + + @retval EFI_SUCCESS Memory attributes set successfully + @retval EFI_NOT_FOUND The specified range does not exist in Gcd Memory Space + +**/ +EFI_STATUS +SetGcdMemorySpaceAttributes ( + IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap, + IN UINTN NumberOfDescriptors, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN StartIndex; + UINTN EndIndex; + EFI_PHYSICAL_ADDRESS RegionStart; + UINT64 RegionLength; + + // + // Get all memory descriptors covered by the memory range + // + Status = SearchGcdMemorySpaces ( + MemorySpaceMap, + NumberOfDescriptors, + BaseAddress, + Length, + &StartIndex, + &EndIndex + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Go through all related descriptors and set attributes accordingly + // + for (Index = StartIndex; Index <= EndIndex; Index++) { + if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) { + continue; + } + // + // Calculate the start and end address of the overlapping range + // + if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) { + RegionStart = BaseAddress; + } else { + RegionStart = MemorySpaceMap[Index].BaseAddress; + } + if (BaseAddress + Length - 1 < MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length) { + RegionLength = BaseAddress + Length - RegionStart; + } else { + RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart; + } + // + // Set memory attributes according to MTRR attribute and the original attribute of descriptor + // + gDS->SetMemorySpaceAttributes ( + RegionStart, + RegionLength, + (MemorySpaceMap[Index].Attributes & ~EFI_CACHE_ATTRIBUTE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes) + ); + } + + return EFI_SUCCESS; +} + + +/** + Refreshes the GCD Memory Space attributes according to MTRRs. + + This function refreshes the GCD Memory Space attributes according to MTRRs. + +**/ +VOID +RefreshMemoryAttributesFromMtrr ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN SubIndex; + UINT64 RegValue; + EFI_PHYSICAL_ADDRESS BaseAddress; + UINT64 Length; + UINT64 Attributes; + UINT64 CurrentAttributes; + UINT8 MtrrType; + UINTN NumberOfDescriptors; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; + UINT64 DefaultAttributes; + VARIABLE_MTRR VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR]; + MTRR_FIXED_SETTINGS MtrrFixedSettings; + UINT32 FirmwareVariableMtrrCount; + UINT8 DefaultMemoryType; + + FirmwareVariableMtrrCount = GetFirmwareVariableMtrrCount (); + ASSERT (FirmwareVariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR); + + MemorySpaceMap = NULL; + + // + // Initialize the valid bits mask and valid address mask for MTRRs + // + InitializeMtrrMask (); + + // + // Get the memory attribute of variable MTRRs + // + MtrrGetMemoryAttributeInVariableMtrr ( + mValidMtrrBitsMask, + mValidMtrrAddressMask, + VariableMtrr + ); + + // + // Get the memory space map from GCD + // + Status = gDS->GetMemorySpaceMap ( + &NumberOfDescriptors, + &MemorySpaceMap + ); + ASSERT_EFI_ERROR (Status); + + DefaultMemoryType = (UINT8) MtrrGetDefaultMemoryType (); + DefaultAttributes = GetMemorySpaceAttributeFromMtrrType (DefaultMemoryType); + + // + // Set default attributes to all spaces. + // + for (Index = 0; Index < NumberOfDescriptors; Index++) { + if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) { + continue; + } + gDS->SetMemorySpaceAttributes ( + MemorySpaceMap[Index].BaseAddress, + MemorySpaceMap[Index].Length, + (MemorySpaceMap[Index].Attributes & ~EFI_CACHE_ATTRIBUTE_MASK) | + (MemorySpaceMap[Index].Capabilities & DefaultAttributes) + ); + } + + // + // Go for variable MTRRs with WB attribute + // + for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) { + if (VariableMtrr[Index].Valid && + VariableMtrr[Index].Type == MTRR_CACHE_WRITE_BACK) { + SetGcdMemorySpaceAttributes ( + MemorySpaceMap, + NumberOfDescriptors, + VariableMtrr[Index].BaseAddress, + VariableMtrr[Index].Length, + EFI_MEMORY_WB + ); + } + } + + // + // Go for variable MTRRs with the attribute except for WB and UC attributes + // + for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) { + if (VariableMtrr[Index].Valid && + VariableMtrr[Index].Type != MTRR_CACHE_WRITE_BACK && + VariableMtrr[Index].Type != MTRR_CACHE_UNCACHEABLE) { + Attributes = GetMemorySpaceAttributeFromMtrrType ((UINT8) VariableMtrr[Index].Type); + SetGcdMemorySpaceAttributes ( + MemorySpaceMap, + NumberOfDescriptors, + VariableMtrr[Index].BaseAddress, + VariableMtrr[Index].Length, + Attributes + ); + } + } + + // + // Go for variable MTRRs with UC attribute + // + for (Index = 0; Index < FirmwareVariableMtrrCount; Index++) { + if (VariableMtrr[Index].Valid && + VariableMtrr[Index].Type == MTRR_CACHE_UNCACHEABLE) { + SetGcdMemorySpaceAttributes ( + MemorySpaceMap, + NumberOfDescriptors, + VariableMtrr[Index].BaseAddress, + VariableMtrr[Index].Length, + EFI_MEMORY_UC + ); + } + } + + // + // Go for fixed MTRRs + // + Attributes = 0; + BaseAddress = 0; + Length = 0; + MtrrGetFixedMtrr (&MtrrFixedSettings); + for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) { + RegValue = MtrrFixedSettings.Mtrr[Index]; + // + // Check for continuous fixed MTRR sections + // + for (SubIndex = 0; SubIndex < 8; SubIndex++) { + MtrrType = (UINT8) RShiftU64 (RegValue, SubIndex * 8); + CurrentAttributes = GetMemorySpaceAttributeFromMtrrType (MtrrType); + if (Length == 0) { + // + // A new MTRR attribute begins + // + Attributes = CurrentAttributes; + } else { + // + // If fixed MTRR attribute changed, then set memory attribute for previous attribute + // + if (CurrentAttributes != Attributes) { + SetGcdMemorySpaceAttributes ( + MemorySpaceMap, + NumberOfDescriptors, + BaseAddress, + Length, + Attributes + ); + BaseAddress = mFixedMtrrTable[Index].BaseAddress + mFixedMtrrTable[Index].Length * SubIndex; + Length = 0; + Attributes = CurrentAttributes; + } + } + Length += mFixedMtrrTable[Index].Length; + } + } + // + // Handle the last fixed MTRR region + // + SetGcdMemorySpaceAttributes ( + MemorySpaceMap, + NumberOfDescriptors, + BaseAddress, + Length, + Attributes + ); + + // + // Free memory space map allocated by GCD service GetMemorySpaceMap () + // + if (MemorySpaceMap != NULL) { + FreePool (MemorySpaceMap); + } +} + +/** + Check if paging is enabled or not. +**/ +BOOLEAN +IsPagingAndPageAddressExtensionsEnabled ( + VOID + ) +{ + IA32_CR0 Cr0; + IA32_CR4 Cr4; + + Cr0.UintN = AsmReadCr0 (); + Cr4.UintN = AsmReadCr4 (); + + return ((Cr0.Bits.PG != 0) && (Cr4.Bits.PAE != 0)); +} + +/** + Refreshes the GCD Memory Space attributes according to MTRRs and Paging. + + This function refreshes the GCD Memory Space attributes according to MTRRs + and page tables. + +**/ +VOID +RefreshGcdMemoryAttributes ( + VOID + ) +{ + mIsFlushingGCD = TRUE; + + if (IsMtrrSupported ()) { + RefreshMemoryAttributesFromMtrr (); + } + + if (IsPagingAndPageAddressExtensionsEnabled ()) { + RefreshGcdMemoryAttributesFromPaging (); + } + + mIsFlushingGCD = FALSE; +} + +/** + Initialize Interrupt Descriptor Table for interrupt handling. + +**/ +VOID +InitInterruptDescriptorTable ( + VOID + ) +{ + EFI_STATUS Status; + EFI_VECTOR_HANDOFF_INFO *VectorInfoList; + EFI_VECTOR_HANDOFF_INFO *VectorInfo; + + VectorInfo = NULL; + Status = EfiGetSystemConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID **) &VectorInfoList); + if (Status == EFI_SUCCESS && VectorInfoList != NULL) { + VectorInfo = VectorInfoList; + } + Status = InitializeCpuInterruptHandlers (VectorInfo); + ASSERT_EFI_ERROR (Status); +} + + +/** + Callback function for idle events. + + @param Event Event whose notification function is being invoked. + @param Context The pointer to the notification function's context, + which is implementation-dependent. + +**/ +VOID +EFIAPI +IdleLoopEventCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + CpuSleep (); +} + +/** + Ensure the compatibility of a memory space descriptor with the MMIO aperture. + + The memory space descriptor can come from the GCD memory space map, or it can + represent a gap between two neighboring memory space descriptors. In the + latter case, the GcdMemoryType field is expected to be + EfiGcdMemoryTypeNonExistent. + + If the memory space descriptor already has type + EfiGcdMemoryTypeMemoryMappedIo, and its capabilities are a superset of the + required capabilities, then no action is taken -- it is by definition + compatible with the aperture. + + Otherwise, the intersection of the memory space descriptor is calculated with + the aperture. If the intersection is the empty set (no overlap), no action is + taken; the memory space descriptor is compatible with the aperture. + + Otherwise, the type of the descriptor is investigated again. If the type is + EfiGcdMemoryTypeNonExistent (representing a gap, or a genuine descriptor with + such a type), then an attempt is made to add the intersection as MMIO space + to the GCD memory space map, with the specified capabilities. This ensures + continuity for the aperture, and the descriptor is deemed compatible with the + aperture. + + Otherwise, the memory space descriptor is incompatible with the MMIO + aperture. + + @param[in] Base Base address of the aperture. + @param[in] Length Length of the aperture. + @param[in] Capabilities Capabilities required by the aperture. + @param[in] Descriptor The descriptor to ensure compatibility with the + aperture for. + + @retval EFI_SUCCESS The descriptor is compatible. The GCD memory + space map may have been updated, for + continuity within the aperture. + @retval EFI_INVALID_PARAMETER The descriptor is incompatible. + @return Error codes from gDS->AddMemorySpace(). +**/ +EFI_STATUS +IntersectMemoryDescriptor ( + IN UINT64 Base, + IN UINT64 Length, + IN UINT64 Capabilities, + IN CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Descriptor + ) +{ + UINT64 IntersectionBase; + UINT64 IntersectionEnd; + EFI_STATUS Status; + + if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo && + (Descriptor->Capabilities & Capabilities) == Capabilities) { + return EFI_SUCCESS; + } + + IntersectionBase = MAX (Base, Descriptor->BaseAddress); + IntersectionEnd = MIN (Base + Length, + Descriptor->BaseAddress + Descriptor->Length); + if (IntersectionBase >= IntersectionEnd) { + // + // The descriptor and the aperture don't overlap. + // + return EFI_SUCCESS; + } + + if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeNonExistent) { + Status = gDS->AddMemorySpace (EfiGcdMemoryTypeMemoryMappedIo, + IntersectionBase, IntersectionEnd - IntersectionBase, + Capabilities); + + DEBUG ((EFI_ERROR (Status) ? DEBUG_ERROR : DEBUG_VERBOSE, + "%a: %a: add [%Lx, %Lx): %r\n", gEfiCallerBaseName, __FUNCTION__, + IntersectionBase, IntersectionEnd, Status)); + return Status; + } + + DEBUG ((DEBUG_ERROR, "%a: %a: desc [%Lx, %Lx) type %u cap %Lx conflicts " + "with aperture [%Lx, %Lx) cap %Lx\n", gEfiCallerBaseName, __FUNCTION__, + Descriptor->BaseAddress, Descriptor->BaseAddress + Descriptor->Length, + (UINT32)Descriptor->GcdMemoryType, Descriptor->Capabilities, + Base, Base + Length, Capabilities)); + return EFI_INVALID_PARAMETER; +} + +/** + Add MMIO space to GCD. + The routine checks the GCD database and only adds those which are + not added in the specified range to GCD. + + @param Base Base address of the MMIO space. + @param Length Length of the MMIO space. + @param Capabilities Capabilities of the MMIO space. + + @retval EFI_SUCCESS The MMIO space was added successfully. +**/ +EFI_STATUS +AddMemoryMappedIoSpace ( + IN UINT64 Base, + IN UINT64 Length, + IN UINT64 Capabilities + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN NumberOfDescriptors; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; + + Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: %a: GetMemorySpaceMap(): %r\n", + gEfiCallerBaseName, __FUNCTION__, Status)); + return Status; + } + + for (Index = 0; Index < NumberOfDescriptors; Index++) { + Status = IntersectMemoryDescriptor (Base, Length, Capabilities, + &MemorySpaceMap[Index]); + if (EFI_ERROR (Status)) { + goto FreeMemorySpaceMap; + } + } + + DEBUG_CODE ( + // + // Make sure there are adjacent descriptors covering [Base, Base + Length). + // It is possible that they have not been merged; merging can be prevented + // by allocation and different capabilities. + // + UINT64 CheckBase; + EFI_STATUS CheckStatus; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor; + + for (CheckBase = Base; + CheckBase < Base + Length; + CheckBase = Descriptor.BaseAddress + Descriptor.Length) { + CheckStatus = gDS->GetMemorySpaceDescriptor (CheckBase, &Descriptor); + ASSERT_EFI_ERROR (CheckStatus); + ASSERT (Descriptor.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo); + ASSERT ((Descriptor.Capabilities & Capabilities) == Capabilities); + } + ); + +FreeMemorySpaceMap: + FreePool (MemorySpaceMap); + + return Status; +} + +/** + Add and allocate CPU local APIC memory mapped space. + + @param[in]ImageHandle Image handle this driver. + +**/ +VOID +AddLocalApicMemorySpace ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS BaseAddress; + + BaseAddress = (EFI_PHYSICAL_ADDRESS) GetLocalApicBaseAddress(); + Status = AddMemoryMappedIoSpace (BaseAddress, SIZE_4KB, EFI_MEMORY_UC); + ASSERT_EFI_ERROR (Status); + + // + // Try to allocate APIC memory mapped space, does not check return + // status because it may be allocated by other driver, or DXE Core if + // this range is built into Memory Allocation HOB. + // + Status = gDS->AllocateMemorySpace ( + EfiGcdAllocateAddress, + EfiGcdMemoryTypeMemoryMappedIo, + 0, + SIZE_4KB, + &BaseAddress, + ImageHandle, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "%a: %a: AllocateMemorySpace() Status - %r\n", + gEfiCallerBaseName, __FUNCTION__, Status)); + } +} + +/** + Initialize the state information for the CPU Architectural Protocol. + + @param ImageHandle Image handle this driver. + @param SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS Thread can be successfully created + @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure + @retval EFI_DEVICE_ERROR Cannot create the thread + +**/ +EFI_STATUS +EFIAPI +InitializeCpu ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_EVENT IdleLoopEvent; + + InitializePageTableLib(); + + InitializeFloatingPointUnits (); + + // + // Make sure interrupts are disabled + // + DisableInterrupts (); + + // + // Init GDT for DXE + // + InitGlobalDescriptorTable (); + + // + // Setup IDT pointer, IDT and interrupt entry points + // + InitInterruptDescriptorTable (); + + // + // Install CPU Architectural Protocol + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mCpuHandle, + &gEfiCpuArchProtocolGuid, &gCpu, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Refresh GCD memory space map according to MTRR value. + // + RefreshGcdMemoryAttributes (); + + // + // Add and allocate local APIC memory mapped space + // + AddLocalApicMemorySpace (ImageHandle); + + // + // Setup a callback for idle events + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + IdleLoopEventCallback, + NULL, + &gIdleLoopEventGuid, + &IdleLoopEvent + ); + ASSERT_EFI_ERROR (Status); + + InitializeMpSupport (); + + return Status; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxe.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxe.h new file mode 100644 index 00000000..9ed0134e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxe.h @@ -0,0 +1,300 @@ +/** @file + CPU DXE Module to produce CPU ARCH Protocol and CPU MP Protocol. + + Copyright (c) 2008 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_DXE_H_ +#define _CPU_DXE_H_ + +#include <PiDxe.h> + +#include <Protocol/Cpu.h> +#include <Protocol/MpService.h> +#include <Register/Intel/Msr.h> + +#include <Ppi/SecPlatformInformation.h> +#include <Ppi/SecPlatformInformation2.h> + +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/DxeServicesTableLib.h> +#include <Library/BaseLib.h> +#include <Library/CpuLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DebugLib.h> +#include <Library/MtrrLib.h> +#include <Library/LocalApicLib.h> +#include <Library/UefiCpuLib.h> +#include <Library/UefiLib.h> +#include <Library/CpuExceptionHandlerLib.h> +#include <Library/HobLib.h> +#include <Library/ReportStatusCodeLib.h> +#include <Library/MpInitLib.h> +#include <Library/TimerLib.h> + +#include <Guid/IdleLoopEvent.h> +#include <Guid/VectorHandoffTable.h> + +#define HEAP_GUARD_NONSTOP_MODE \ + ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT6|BIT4|BIT1|BIT0)) > BIT6) + +#define NULL_DETECTION_NONSTOP_MODE \ + ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & (BIT6|BIT0)) > BIT6) + +/** + Flush CPU data cache. If the instruction cache is fully coherent + with all DMA operations then function can just return EFI_SUCCESS. + + @param This Protocol instance structure + @param Start Physical address to start flushing from. + @param Length Number of bytes to flush. Round up to chipset + granularity. + @param FlushType Specifies the type of flush operation to perform. + + @retval EFI_SUCCESS If cache was flushed + @retval EFI_UNSUPPORTED If flush type is not supported. + @retval EFI_DEVICE_ERROR If requested range could not be flushed. + +**/ +EFI_STATUS +EFIAPI +CpuFlushCpuDataCache ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS Start, + IN UINT64 Length, + IN EFI_CPU_FLUSH_TYPE FlushType + ); + +/** + Enables CPU interrupts. + + @param This Protocol instance structure + + @retval EFI_SUCCESS If interrupts were enabled in the CPU + @retval EFI_DEVICE_ERROR If interrupts could not be enabled on the CPU. + +**/ +EFI_STATUS +EFIAPI +CpuEnableInterrupt ( + IN EFI_CPU_ARCH_PROTOCOL *This + ); + +/** + Disables CPU interrupts. + + @param This Protocol instance structure + + @retval EFI_SUCCESS If interrupts were disabled in the CPU. + @retval EFI_DEVICE_ERROR If interrupts could not be disabled on the CPU. + +**/ +EFI_STATUS +EFIAPI +CpuDisableInterrupt ( + IN EFI_CPU_ARCH_PROTOCOL *This + ); + +/** + Return the state of interrupts. + + @param This Protocol instance structure + @param State Pointer to the CPU's current interrupt state + + @retval EFI_SUCCESS If interrupts were disabled in the CPU. + @retval EFI_INVALID_PARAMETER State is NULL. + +**/ +EFI_STATUS +EFIAPI +CpuGetInterruptState ( + IN EFI_CPU_ARCH_PROTOCOL *This, + OUT BOOLEAN *State + ); + +/** + Generates an INIT to the CPU. + + @param This Protocol instance structure + @param InitType Type of CPU INIT to perform + + @retval EFI_SUCCESS If CPU INIT occurred. This value should never be + seen. + @retval EFI_DEVICE_ERROR If CPU INIT failed. + @retval EFI_UNSUPPORTED Requested type of CPU INIT not supported. + +**/ +EFI_STATUS +EFIAPI +CpuInit ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_CPU_INIT_TYPE InitType + ); + +/** + Registers a function to be called from the CPU interrupt handler. + + @param This Protocol instance structure + @param InterruptType Defines which interrupt to hook. IA-32 + valid range is 0x00 through 0xFF + @param InterruptHandler A pointer to a function of type + EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. A null + pointer is an error condition. + + @retval EFI_SUCCESS If handler installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler + for InterruptType was previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for + InterruptType was not previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType + is not supported. + +**/ +EFI_STATUS +EFIAPI +CpuRegisterInterruptHandler ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ); + +/** + Returns a timer value from one of the CPU's internal timers. There is no + inherent time interval between ticks but is a function of the CPU frequency. + + @param This - Protocol instance structure. + @param TimerIndex - Specifies which CPU timer is requested. + @param TimerValue - Pointer to the returned timer value. + @param TimerPeriod - A pointer to the amount of time that passes + in femtoseconds (10-15) for each increment + of TimerValue. If TimerValue does not + increment at a predictable rate, then 0 is + returned. The amount of time that has + passed between two calls to GetTimerValue() + can be calculated with the formula + (TimerValue2 - TimerValue1) * TimerPeriod. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS - If the CPU timer count was returned. + @retval EFI_UNSUPPORTED - If the CPU does not have any readable timers. + @retval EFI_DEVICE_ERROR - If an error occurred while reading the timer. + @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL. + +**/ +EFI_STATUS +EFIAPI +CpuGetTimerValue ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN UINT32 TimerIndex, + OUT UINT64 *TimerValue, + OUT UINT64 *TimerPeriod OPTIONAL + ); + +/** + Set memory cacheability attributes for given range of memory. + + @param This Protocol instance structure + @param BaseAddress Specifies the start address of the + memory range + @param Length Specifies the length of the memory range + @param Attributes The memory cacheability for the memory range + + @retval EFI_SUCCESS If the cacheability of that memory range is + set successfully + @retval EFI_UNSUPPORTED If the desired operation cannot be done + @retval EFI_INVALID_PARAMETER The input parameter is not correct, + such as Length = 0 + +**/ +EFI_STATUS +EFIAPI +CpuSetMemoryAttributes ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +/** + Initialize Global Descriptor Table. + +**/ +VOID +InitGlobalDescriptorTable ( + VOID + ); + +/** + Sets the code selector (CS). + + @param Selector Value of code selector. + +**/ +VOID +EFIAPI +SetCodeSelector ( + UINT16 Selector + ); + +/** + Sets the data selector (DS). + + @param Selector Value of data selector. + +**/ +VOID +EFIAPI +SetDataSelectors ( + UINT16 Selector + ); + +/** + Update GCD memory space attributes according to current page table setup. +**/ +VOID +RefreshGcdMemoryAttributesFromPaging ( + VOID + ); + +/** + Special handler for #DB exception, which will restore the page attributes + (not-present). It should work with #PF handler which will set pages to + 'present'. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. + +**/ +VOID +EFIAPI +DebugExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +/** + Special handler for #PF exception, which will set the pages which caused + #PF to be 'present'. The attribute of those pages should be restored in + the subsequent #DB handler. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. + +**/ +VOID +EFIAPI +PageFaultExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +extern BOOLEAN mIsAllocatingPageTable; +extern UINTN mNumberOfProcessors; + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxe.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxe.inf new file mode 100644 index 00000000..986f0619 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxe.inf @@ -0,0 +1,88 @@ +## @file +# CPU driver installs CPU Architecture Protocol and CPU MP protocol. +# +# Copyright (c) 2008 - 2019, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuDxe + MODULE_UNI_FILE = CpuDxe.uni + FILE_GUID = 1A1E4886-9517-440e-9FDE-3BE44CEE2136 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeCpu + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + CpuLib + DebugLib + DxeServicesTableLib + MemoryAllocationLib + MtrrLib + UefiBootServicesTableLib + UefiDriverEntryPoint + LocalApicLib + UefiCpuLib + UefiLib + CpuExceptionHandlerLib + HobLib + ReportStatusCodeLib + MpInitLib + TimerLib + PeCoffGetEntryPointLib + +[Sources] + CpuDxe.c + CpuDxe.h + CpuGdt.c + CpuGdt.h + CpuMp.c + CpuMp.h + CpuPageTable.h + CpuPageTable.c + +[Sources.IA32] + Ia32/CpuAsm.nasm + Ia32/PagingAttribute.c + +[Sources.X64] + X64/CpuAsm.nasm + X64/PagingAttribute.c + +[Protocols] + gEfiCpuArchProtocolGuid ## PRODUCES + gEfiMpServiceProtocolGuid ## PRODUCES + gEfiSmmBase2ProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + gIdleLoopEventGuid ## CONSUMES ## Event + gEfiVectorHandoffTableGuid ## SOMETIMES_CONSUMES ## SystemTable + +[Ppis] + gEfiSecPlatformInformation2PpiGuid ## UNDEFINED # HOB + gEfiSecPlatformInformationPpiGuid ## UNDEFINED # HOB + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize ## CONSUMES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + CpuDxeExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxe.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxe.uni new file mode 100644 index 00000000..85d977d8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxe.uni @@ -0,0 +1,16 @@ +// /** @file
+// CPU driver installs CPU Architecture Protocol and CPU MP Protocol.
+//
+// CPU driver installs CPU Architecture Protocol and CPU MP Protocol.
+//
+// Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU driver installs CPU Architecture Protocol and CPU MP Protocol."
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU driver installs CPU Architecture Protocol and CPU MP Protocol."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxeExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxeExtra.uni new file mode 100644 index 00000000..5bb116fe --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// CpuDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"CPU Architectural and CPU Multi-processor DXE Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuGdt.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuGdt.c new file mode 100644 index 00000000..8fed6209 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuGdt.c @@ -0,0 +1,183 @@ +/** @file + C based implementation of IA32 interrupt handling only + requiring a minimal assembly interrupt entry point. + + Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuDxe.h" +#include "CpuGdt.h" + +// +// Global descriptor table (GDT) Template +// +STATIC GDT_ENTRIES mGdtTemplate = { + // + // NULL_SEL + // + { + 0x0, // limit 15:0 + 0x0, // base 15:0 + 0x0, // base 23:16 + 0x0, // type + 0x0, // limit 19:16, flags + 0x0, // base 31:24 + }, + // + // LINEAR_SEL + // + { + 0x0FFFF, // limit 15:0 + 0x0, // base 15:0 + 0x0, // base 23:16 + 0x092, // present, ring 0, data, read/write + 0x0CF, // page-granular, 32-bit + 0x0, + }, + // + // LINEAR_CODE_SEL + // + { + 0x0FFFF, // limit 15:0 + 0x0, // base 15:0 + 0x0, // base 23:16 + 0x09F, // present, ring 0, code, execute/read, conforming, accessed + 0x0CF, // page-granular, 32-bit + 0x0, + }, + // + // SYS_DATA_SEL + // + { + 0x0FFFF, // limit 15:0 + 0x0, // base 15:0 + 0x0, // base 23:16 + 0x093, // present, ring 0, data, read/write, accessed + 0x0CF, // page-granular, 32-bit + 0x0, + }, + // + // SYS_CODE_SEL + // + { + 0x0FFFF, // limit 15:0 + 0x0, // base 15:0 + 0x0, // base 23:16 + 0x09A, // present, ring 0, code, execute/read + 0x0CF, // page-granular, 32-bit + 0x0, + }, + // + // SYS_CODE16_SEL + // + { + 0x0FFFF, // limit 15:0 + 0x0, // base 15:0 + 0x0, // base 23:16 + 0x09A, // present, ring 0, code, execute/read + 0x08F, // page-granular, 16-bit + 0x0, // base 31:24 + }, + // + // LINEAR_DATA64_SEL + // + { + 0x0FFFF, // limit 15:0 + 0x0, // base 15:0 + 0x0, // base 23:16 + 0x092, // present, ring 0, data, read/write + 0x0CF, // page-granular, 32-bit + 0x0, + }, + // + // LINEAR_CODE64_SEL + // + { + 0x0FFFF, // limit 15:0 + 0x0, // base 15:0 + 0x0, // base 23:16 + 0x09A, // present, ring 0, code, execute/read + 0x0AF, // page-granular, 64-bit code + 0x0, // base (high) + }, + // + // SPARE5_SEL + // + { + 0x0, // limit 15:0 + 0x0, // base 15:0 + 0x0, // base 23:16 + 0x0, // type + 0x0, // limit 19:16, flags + 0x0, // base 31:24 + }, +}; + +/** + Initialize Global Descriptor Table. + +**/ +VOID +InitGlobalDescriptorTable ( + VOID + ) +{ +#ifndef VBOX + EFI_STATUS Status; +#endif + GDT_ENTRIES *Gdt; + IA32_DESCRIPTOR Gdtr; +#ifndef VBOX + EFI_PHYSICAL_ADDRESS Memory; +#endif + + // + // Allocate Runtime Data below 4GB for the GDT + // AP uses the same GDT when it's waken up from real mode so + // the GDT needs to be below 4GB. + // +#ifndef VBOX + Memory = SIZE_4GB - 1; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiRuntimeServicesData, + EFI_SIZE_TO_PAGES (sizeof (mGdtTemplate)), + &Memory + ); + ASSERT_EFI_ERROR (Status); + ASSERT ((Memory != 0) && (Memory < SIZE_4GB)); + Gdt = (GDT_ENTRIES *) (UINTN) Memory; +#else + /* + * Apples bootloader boot.efi for at least OS X Tiger, Leopard and Snow Leopard + * relocates runtime regions which doesn't make sense for the GDT as the GDTR is not + * updated and would point to invalid memory. Allocate the memory as reserved to hopefully + * keep the bootloaders hands off of it, see also OvmfPkg/PlatformPei/Platform.c + * (search for PeiServicesAllocatePages()) for a more detailed explanation of a + * related bug in Apples bootloader. + */ + Gdt = AllocateReservedPool (sizeof (mGdtTemplate) + 8); + ASSERT (Gdt != NULL); + Gdt = ALIGN_POINTER (Gdt, 8); +#endif + + // + // Initialize all GDT entries + // + CopyMem (Gdt, &mGdtTemplate, sizeof (mGdtTemplate)); + + // + // Write GDT register + // + Gdtr.Base = (UINT32) (UINTN) Gdt; + Gdtr.Limit = (UINT16) (sizeof (mGdtTemplate) - 1); + AsmWriteGdtr (&Gdtr); + + // + // Update selector (segment) registers base on new GDT + // + SetCodeSelector ((UINT16)CPU_CODE_SEL); + SetDataSelectors ((UINT16)CPU_DATA_SEL); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuGdt.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuGdt.h new file mode 100644 index 00000000..fa54e674 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuGdt.h @@ -0,0 +1,68 @@ +/** @file + C based implementation of IA32 interrupt handling only + requiring a minimal assembly interrupt entry point. + + Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_GDT_H_ +#define _CPU_GDT_H_ + +// +// Local structure definitions +// + +#pragma pack (1) + +// +// Global Descriptor Entry structures +// + +typedef struct _GDT_ENTRY { + UINT16 Limit15_0; + UINT16 Base15_0; + UINT8 Base23_16; + UINT8 Type; + UINT8 Limit19_16_and_flags; + UINT8 Base31_24; +} GDT_ENTRY; + +typedef +struct _GDT_ENTRIES { + GDT_ENTRY Null; + GDT_ENTRY Linear; + GDT_ENTRY LinearCode; + GDT_ENTRY SysData; + GDT_ENTRY SysCode; + GDT_ENTRY SysCode16; + GDT_ENTRY LinearData64; + GDT_ENTRY LinearCode64; + GDT_ENTRY Spare5; +} GDT_ENTRIES; + +#pragma pack () + +#define NULL_SEL OFFSET_OF (GDT_ENTRIES, Null) +#define LINEAR_SEL OFFSET_OF (GDT_ENTRIES, Linear) +#define LINEAR_CODE_SEL OFFSET_OF (GDT_ENTRIES, LinearCode) +#define SYS_DATA_SEL OFFSET_OF (GDT_ENTRIES, SysData) +#define SYS_CODE_SEL OFFSET_OF (GDT_ENTRIES, SysCode) +#define SYS_CODE16_SEL OFFSET_OF (GDT_ENTRIES, SysCode16) +#define LINEAR_DATA64_SEL OFFSET_OF (GDT_ENTRIES, LinearData64) +#define LINEAR_CODE64_SEL OFFSET_OF (GDT_ENTRIES, LinearCode64) +#define SPARE5_SEL OFFSET_OF (GDT_ENTRIES, Spare5) + +#if defined (MDE_CPU_IA32) +#define CPU_CODE_SEL LINEAR_CODE_SEL +#define CPU_DATA_SEL LINEAR_SEL +#elif defined (MDE_CPU_X64) +#define CPU_CODE_SEL LINEAR_CODE64_SEL +#define CPU_DATA_SEL LINEAR_DATA64_SEL +#else +#error CPU type not supported for CPU GDT initialization! +#endif + +#endif // _CPU_GDT_H_ + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuMp.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuMp.c new file mode 100644 index 00000000..e63bd05f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuMp.c @@ -0,0 +1,849 @@ +/** @file + CPU DXE Module to produce CPU MP Protocol. + + Copyright (c) 2008 - 2017, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuDxe.h" +#include "CpuMp.h" + +EFI_HANDLE mMpServiceHandle = NULL; +UINTN mNumberOfProcessors = 1; + +EFI_MP_SERVICES_PROTOCOL mMpServicesTemplate = { + GetNumberOfProcessors, + GetProcessorInfo, + StartupAllAPs, + StartupThisAP, + SwitchBSP, + EnableDisableAP, + WhoAmI +}; + +/** + This service retrieves the number of logical processor in the platform + and the number of those logical processors that are enabled on this boot. + This service may only be called from the BSP. + + This function is used to retrieve the following information: + - The number of logical processors that are present in the system. + - The number of enabled logical processors in the system at the instant + this call is made. + + Because MP Service Protocol provides services to enable and disable processors + dynamically, the number of enabled logical processors may vary during the + course of a boot session. + + If this service is called from an AP, then EFI_DEVICE_ERROR is returned. + If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then + EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors + is returned in NumberOfProcessors, the number of currently enabled processor + is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL + instance. + @param[out] NumberOfProcessors Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL. + @retval EFI_INVALID_PARAMETER NumberOfEnabledProcessors is NULL. + +**/ +EFI_STATUS +EFIAPI +GetNumberOfProcessors ( + IN EFI_MP_SERVICES_PROTOCOL *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ) +{ + if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) { + return EFI_INVALID_PARAMETER; + } + + return MpInitLibGetNumberOfProcessors ( + NumberOfProcessors, + NumberOfEnabledProcessors + ); +} + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + This service retrieves detailed MP-related information about any processor + on the platform. Note the following: + - The processor information may change during the course of a boot session. + - The information presented here is entirely MP related. + + Information regarding the number of caches and their sizes, frequency of operation, + slot numbers is all considered platform-related information and is not provided + by this service. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL + instance. + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. + +**/ +EFI_STATUS +EFIAPI +GetProcessorInfo ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ) +{ + return MpInitLibGetProcessorInfo (ProcessorNumber, ProcessorInfoBuffer, NULL); +} + +/** + This service executes a caller provided function on all enabled APs. APs can + run either simultaneously or one at a time in sequence. This service supports + both blocking and non-blocking requests. The non-blocking requests use EFI + events so the BSP can detect when the APs have finished. This service may only + be called from the BSP. + + This function is used to dispatch all the enabled APs to the function specified + by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned + immediately and Procedure is not started on any AP. + + If SingleThread is TRUE, all the enabled APs execute the function specified by + Procedure one by one, in ascending order of processor handle number. Otherwise, + all the enabled APs execute the function specified by Procedure simultaneously. + + If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all + APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in non-blocking + mode, and the BSP returns from this service without waiting for APs. If a + non-blocking mode is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT + is signaled, then EFI_UNSUPPORTED must be returned. + + If the timeout specified by TimeoutInMicroseconds expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. All enabled APs + are always available for further calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() + and EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its + content points to the list of processor handle numbers in which Procedure was + terminated. + + Note: It is the responsibility of the consumer of the EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() + to make sure that the nature of the code that is executed on the BSP and the + dispatched APs is well controlled. The MP Services Protocol does not guarantee + that the Procedure function is MP-safe. Hence, the tasks that can be run in + parallel are limited to certain independent tasks and well-controlled exclusive + code. EFI services and protocols may not be called by APs unless otherwise + specified. + + In blocking execution mode, BSP waits until all APs finish or + TimeoutInMicroseconds expires. + + In non-blocking execution mode, BSP is freed to return to the caller and then + proceed to the next task without having to wait for APs. The following + sequence needs to occur in a non-blocking execution mode: + + -# The caller that intends to use this MP Services Protocol in non-blocking + mode creates WaitEvent by calling the EFI CreateEvent() service. The caller + invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter WaitEvent + is not NULL, then StartupAllAPs() executes in non-blocking mode. It requests + the function specified by Procedure to be started on all the enabled APs, + and releases the BSP to continue with other tasks. + -# The caller can use the CheckEvent() and WaitForEvent() services to check + the state of the WaitEvent created in step 1. + -# When the APs complete their task or TimeoutInMicroSeconds expires, the MP + Service signals WaitEvent by calling the EFI SignalEvent() function. If + FailedCpuList is not NULL, its content is available when WaitEvent is + signaled. If all APs returned from Procedure prior to the timeout, then + FailedCpuList is set to NULL. If not all APs return from Procedure before + the timeout, then FailedCpuList is filled in with the list of the failed + APs. The buffer is allocated by MP Service Protocol using AllocatePool(). + It is the caller's responsibility to free the buffer with FreePool() service. + -# This invocation of SignalEvent() function informs the caller that invoked + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs completed + the specified task or a timeout occurred. The contents of FailedCpuList + can be examined to determine which APs did not complete the specified task + prior to the timeout. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL + instance. + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] SingleThread If TRUE, then all the enabled APs execute + the function specified by Procedure one by + one, in ascending order of processor handle + number. If FALSE, then all the enabled APs + execute the function specified by Procedure + simultaneously. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until all APs finish + or TimeoutInMicroseconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on all the enabled + APs, and go on executing immediately. If + all return from Procedure, or TimeoutInMicroseconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + all APs return from Procedure, then Procedure + on the failed APs is terminated. All enabled + APs are available for next function assigned + by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() + or EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise, + if all APs finish successfully, then its + content is set to NULL. If not all APs + finish before timeout expires, then its + content is set to address of the buffer + holding handle numbers of the failed APs. + The buffer is allocated by MP Service Protocol, + and it's the caller's responsibility to + free the buffer with FreePool() service. + In blocking mode, it is ready for consumption + when the call returns. In non-blocking mode, + it is ready when WaitEvent is signaled. The + list of failed CPU is terminated by + END_OF_CPU_LIST. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + all enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +StartupAllAPs ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT UINTN **FailedCpuList OPTIONAL + ) +{ + return MpInitLibStartupAllAPs ( + Procedure, + SingleThread, + WaitEvent, + TimeoutInMicroseconds, + ProcedureArgument, + FailedCpuList + ); +} + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. The caller can request the BSP to either wait for the completion + of the AP or just proceed with the next task by using the EFI event mechanism. + See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking + execution support. This service may only be called from the BSP. + + This function is used to dispatch one enabled AP to the function specified by + Procedure passing in the argument specified by ProcedureArgument. If WaitEvent + is NULL, execution is in blocking mode. The BSP waits until the AP finishes or + TimeoutInMicroSeconds expires. Otherwise, execution is in non-blocking mode. + BSP proceeds to the next task without waiting for the AP. If a non-blocking mode + is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled, + then EFI_UNSUPPORTED must be returned. + + If the timeout specified by TimeoutInMicroseconds expires before the AP returns + from Procedure, then execution of Procedure by the AP is terminated. The AP is + available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and + EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL + instance. + @param[in] Procedure A pointer to the function to be run on the + designated AP of the system. See type + EFI_AP_PROCEDURE. + @param[in] ProcessorNumber The handle number of the AP. The range is + from 0 to the total number of logical + processors minus 1. The total number of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until this AP finish + or TimeoutInMicroSeconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on this AP, + and go on executing immediately. If this AP + return from Procedure or TimeoutInMicroSeconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + this AP to finish this Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + this AP returns from Procedure, then Procedure + on the AP is terminated. The + AP is available for next function assigned + by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() + or EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure on the + specified AP. + @param[out] Finished If NULL, this parameter is ignored. In + blocking mode, this parameter is ignored. + In non-blocking mode, if AP returns from + Procedure before the timeout expires, its + content is set to TRUE. Otherwise, the + value is set to FALSE. The caller can + determine if the AP returned from Procedure + by evaluating this value. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before + the timeout expires. + @retval EFI_SUCCESS In non-blocking mode, the function has been + dispatched to specified AP. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + the specified AP has finished. + @retval EFI_NOT_READY The specified AP is busy. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +StartupThisAP ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT BOOLEAN *Finished OPTIONAL + ) +{ + return MpInitLibStartupThisAP ( + Procedure, + ProcessorNumber, + WaitEvent, + TimeoutInMicroseconds, + ProcedureArgument, + Finished + ); +} + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. The new BSP can take over the + execution of the old BSP and continue seamlessly from where the old one left + off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT + is signaled. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of AP that is to become the new + BSP. The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an + enabled AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to + this service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or + a disabled AP. + @retval EFI_NOT_READY The specified AP is busy. + +**/ +EFI_STATUS +EFIAPI +SwitchBSP ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + return MpInitLibSwitchBSP (ProcessorNumber, EnableOldBSP); +} + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + This service allows the caller enable or disable an AP from this point onward. + The caller can optionally specify the health status of the AP by Health. If + an AP is being disabled, then the state of the disabled AP is implementation + dependent. If an AP is enabled, then the implementation must guarantee that a + complete initialization sequence is performed on the AP, so the AP is in a state + that is compatible with an MP operating system. This service may not be supported + after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled. + + If the enable or disable AP operation cannot be completed prior to the return + from this service, then EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of AP. + The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for + enabled, FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies + the new health status of the AP. This flag + corresponds to StatusFlag defined in + EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only + the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter + is ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed + prior to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. + +**/ +EFI_STATUS +EFIAPI +EnableDisableAP ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + return MpInitLibEnableDisableAP (ProcessorNumber, EnableAP, HealthFlag); +} + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + This service returns the processor handle number for the calling processor. + The returned value is in the range from 0 to the total number of logical + processors minus 1. The total number of logical processors can be retrieved + with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be + called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER + is returned. Otherwise, the current processors handle number is returned in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance. + @param[out] ProcessorNumber Pointer to the handle number of AP. + The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned + in ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. + +**/ +EFI_STATUS +EFIAPI +WhoAmI ( + IN EFI_MP_SERVICES_PROTOCOL *This, + OUT UINTN *ProcessorNumber + ) +{ + return MpInitLibWhoAmI (ProcessorNumber);; +} + +/** + Collects BIST data from HOB. + + This function collects BIST data from HOB built from Sec Platform Information + PPI or SEC Platform Information2 PPI. + +**/ +VOID +CollectBistDataFromHob ( + VOID + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + EFI_SEC_PLATFORM_INFORMATION_RECORD2 *SecPlatformInformation2; + EFI_SEC_PLATFORM_INFORMATION_RECORD *SecPlatformInformation; + UINTN NumberOfData; + EFI_SEC_PLATFORM_INFORMATION_CPU *CpuInstance; + EFI_SEC_PLATFORM_INFORMATION_CPU BspCpuInstance; + UINTN ProcessorNumber; + EFI_PROCESSOR_INFORMATION ProcessorInfo; + EFI_HEALTH_FLAGS BistData; + UINTN CpuInstanceNumber; + + SecPlatformInformation2 = NULL; + SecPlatformInformation = NULL; + + // + // Get gEfiSecPlatformInformation2PpiGuid Guided HOB firstly + // + GuidHob = GetFirstGuidHob (&gEfiSecPlatformInformation2PpiGuid); + if (GuidHob != NULL) { + // + // Sec Platform Information2 PPI includes BSP/APs' BIST information + // + SecPlatformInformation2 = GET_GUID_HOB_DATA (GuidHob); + NumberOfData = SecPlatformInformation2->NumberOfCpus; + CpuInstance = SecPlatformInformation2->CpuInstance; + } else { + // + // Otherwise, get gEfiSecPlatformInformationPpiGuid Guided HOB + // + GuidHob = GetFirstGuidHob (&gEfiSecPlatformInformationPpiGuid); + if (GuidHob != NULL) { + SecPlatformInformation = GET_GUID_HOB_DATA (GuidHob); + NumberOfData = 1; + // + // SEC Platform Information only includes BSP's BIST information + // does not have BSP's APIC ID + // + BspCpuInstance.CpuLocation = GetApicId (); + BspCpuInstance.InfoRecord.IA32HealthFlags.Uint32 = SecPlatformInformation->IA32HealthFlags.Uint32; + CpuInstance = &BspCpuInstance; + } else { + DEBUG ((DEBUG_INFO, "Does not find any HOB stored CPU BIST information!\n")); + // + // Does not find any HOB stored BIST information + // + return; + } + } + + for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) { + MpInitLibGetProcessorInfo (ProcessorNumber, &ProcessorInfo, &BistData); + for (CpuInstanceNumber = 0; CpuInstanceNumber < NumberOfData; CpuInstanceNumber++) { + if (ProcessorInfo.ProcessorId == CpuInstance[CpuInstanceNumber].CpuLocation) { + // + // Update CPU health status for MP Services Protocol according to BIST data. + // + BistData = CpuInstance[CpuInstanceNumber].InfoRecord.IA32HealthFlags; + } + } + if (BistData.Uint32 != 0) { + // + // Report Status Code that self test is failed + // + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_EC_SELF_TEST) + ); + } + } +} + +/** + Get GDT register value. + + This function is mainly for AP purpose because AP may have different GDT + table than BSP. + + @param[in,out] Buffer The pointer to private data buffer. + +**/ +VOID +EFIAPI +GetGdtr ( + IN OUT VOID *Buffer + ) +{ + AsmReadGdtr ((IA32_DESCRIPTOR *)Buffer); +} + +/** + Initializes CPU exceptions handlers for the sake of stack switch requirement. + + This function is a wrapper of InitializeCpuExceptionHandlersEx. It's mainly + for the sake of AP's init because of EFI_AP_PROCEDURE API requirement. + + @param[in,out] Buffer The pointer to private data buffer. + +**/ +VOID +EFIAPI +InitializeExceptionStackSwitchHandlers ( + IN OUT VOID *Buffer + ) +{ + CPU_EXCEPTION_INIT_DATA *EssData; + IA32_DESCRIPTOR Idtr; + EFI_STATUS Status; + + EssData = Buffer; + // + // We don't plan to replace IDT table with a new one, but we should not assume + // the AP's IDT is the same as BSP's IDT either. + // + AsmReadIdtr (&Idtr); + EssData->Ia32.IdtTable = (VOID *)Idtr.Base; + EssData->Ia32.IdtTableSize = Idtr.Limit + 1; + Status = InitializeCpuExceptionHandlersEx (NULL, EssData); + ASSERT_EFI_ERROR (Status); +} + +/** + Initializes MP exceptions handlers for the sake of stack switch requirement. + + This function will allocate required resources required to setup stack switch + and pass them through CPU_EXCEPTION_INIT_DATA to each logic processor. + +**/ +VOID +InitializeMpExceptionStackSwitchHandlers ( + VOID + ) +{ + UINTN Index; + UINTN Bsp; + UINTN ExceptionNumber; + UINTN OldGdtSize; + UINTN NewGdtSize; + UINTN NewStackSize; + IA32_DESCRIPTOR Gdtr; + CPU_EXCEPTION_INIT_DATA EssData; + UINT8 *GdtBuffer; + UINT8 *StackTop; + + ExceptionNumber = FixedPcdGetSize (PcdCpuStackSwitchExceptionList); + NewStackSize = FixedPcdGet32 (PcdCpuKnownGoodStackSize) * ExceptionNumber; + + StackTop = AllocateRuntimeZeroPool (NewStackSize * mNumberOfProcessors); + ASSERT (StackTop != NULL); + StackTop += NewStackSize * mNumberOfProcessors; + + // + // The default exception handlers must have been initialized. Let's just skip + // it in this method. + // + EssData.Ia32.Revision = CPU_EXCEPTION_INIT_DATA_REV; + EssData.Ia32.InitDefaultHandlers = FALSE; + + EssData.Ia32.StackSwitchExceptions = FixedPcdGetPtr(PcdCpuStackSwitchExceptionList); + EssData.Ia32.StackSwitchExceptionNumber = ExceptionNumber; + EssData.Ia32.KnownGoodStackSize = FixedPcdGet32(PcdCpuKnownGoodStackSize); + + // + // Initialize Gdtr to suppress incorrect compiler/analyzer warnings. + // + Gdtr.Base = 0; + Gdtr.Limit = 0; + MpInitLibWhoAmI (&Bsp); + for (Index = 0; Index < mNumberOfProcessors; ++Index) { + // + // To support stack switch, we need to re-construct GDT but not IDT. + // + if (Index == Bsp) { + GetGdtr (&Gdtr); + } else { + // + // AP might have different size of GDT from BSP. + // + MpInitLibStartupThisAP (GetGdtr, Index, NULL, 0, (VOID *)&Gdtr, NULL); + } + + // + // X64 needs only one TSS of current task working for all exceptions + // because of its IST feature. IA32 needs one TSS for each exception + // in addition to current task. Since AP is not supposed to allocate + // memory, we have to do it in BSP. To simplify the code, we allocate + // memory for IA32 case to cover both IA32 and X64 exception stack + // switch. + // + // Layout of memory to allocate for each processor: + // -------------------------------- + // | Alignment | (just in case) + // -------------------------------- + // | | + // | Original GDT | + // | | + // -------------------------------- + // | Current task descriptor | + // -------------------------------- + // | | + // | Exception task descriptors | X ExceptionNumber + // | | + // -------------------------------- + // | Current task-state segment | + // -------------------------------- + // | | + // | Exception task-state segment | X ExceptionNumber + // | | + // -------------------------------- + // + OldGdtSize = Gdtr.Limit + 1; + EssData.Ia32.ExceptionTssDescSize = sizeof (IA32_TSS_DESCRIPTOR) * + (ExceptionNumber + 1); + EssData.Ia32.ExceptionTssSize = sizeof (IA32_TASK_STATE_SEGMENT) * + (ExceptionNumber + 1); + NewGdtSize = sizeof (IA32_TSS_DESCRIPTOR) + + OldGdtSize + + EssData.Ia32.ExceptionTssDescSize + + EssData.Ia32.ExceptionTssSize; + + GdtBuffer = AllocateRuntimeZeroPool (NewGdtSize); + ASSERT (GdtBuffer != NULL); + + // + // Make sure GDT table alignment + // + EssData.Ia32.GdtTable = ALIGN_POINTER(GdtBuffer, sizeof (IA32_TSS_DESCRIPTOR)); + NewGdtSize -= ((UINT8 *)EssData.Ia32.GdtTable - GdtBuffer); + EssData.Ia32.GdtTableSize = NewGdtSize; + + EssData.Ia32.ExceptionTssDesc = ((UINT8 *)EssData.Ia32.GdtTable + OldGdtSize); + EssData.Ia32.ExceptionTss = ((UINT8 *)EssData.Ia32.GdtTable + OldGdtSize + + EssData.Ia32.ExceptionTssDescSize); + + EssData.Ia32.KnownGoodStackTop = (UINTN)StackTop; + DEBUG ((DEBUG_INFO, + "Exception stack top[cpu%lu]: 0x%lX\n", + (UINT64)(UINTN)Index, + (UINT64)(UINTN)StackTop)); + + if (Index == Bsp) { + InitializeExceptionStackSwitchHandlers (&EssData); + } else { + MpInitLibStartupThisAP ( + InitializeExceptionStackSwitchHandlers, + Index, + NULL, + 0, + (VOID *)&EssData, + NULL + ); + } + + StackTop -= NewStackSize; + } +} + +/** + Initializes MP exceptions handlers for special features, such as Heap Guard + and Stack Guard. +**/ +VOID +InitializeMpExceptionHandlers ( + VOID + ) +{ + // + // Enable non-stop mode for #PF triggered by Heap Guard or NULL Pointer + // Detection. + // + if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) { + RegisterCpuInterruptHandler (EXCEPT_IA32_DEBUG, DebugExceptionHandler); + RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, PageFaultExceptionHandler); + } + + // + // Setup stack switch for Stack Guard feature. + // + if (PcdGetBool (PcdCpuStackGuard)) { + InitializeMpExceptionStackSwitchHandlers (); + } +} + +/** + Initialize Multi-processor support. + +**/ +VOID +InitializeMpSupport ( + VOID + ) +{ + EFI_STATUS Status; + UINTN NumberOfProcessors; + UINTN NumberOfEnabledProcessors; + + // + // Wakeup APs to do initialization + // + Status = MpInitLibInitialize (); + ASSERT_EFI_ERROR (Status); + + MpInitLibGetNumberOfProcessors (&NumberOfProcessors, &NumberOfEnabledProcessors); + mNumberOfProcessors = NumberOfProcessors; + DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mNumberOfProcessors)); + + // + // Initialize special exception handlers for each logic processor. + // + InitializeMpExceptionHandlers (); + + // + // Update CPU healthy information from Guided HOB + // + CollectBistDataFromHob (); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &mMpServiceHandle, + &gEfiMpServiceProtocolGuid, &mMpServicesTemplate, + NULL + ); + ASSERT_EFI_ERROR (Status); +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuMp.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuMp.h new file mode 100644 index 00000000..fdd69714 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuMp.h @@ -0,0 +1,470 @@ +/** @file + CPU DXE MP support + + Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_MP_H_ +#define _CPU_MP_H_ + +/** + Initialize Multi-processor support. + +**/ +VOID +InitializeMpSupport ( + VOID + ); + +/** + This service retrieves the number of logical processor in the platform + and the number of those logical processors that are enabled on this boot. + This service may only be called from the BSP. + + This function is used to retrieve the following information: + - The number of logical processors that are present in the system. + - The number of enabled logical processors in the system at the instant + this call is made. + + Because MP Service Protocol provides services to enable and disable processors + dynamically, the number of enabled logical processors may vary during the + course of a boot session. + + If this service is called from an AP, then EFI_DEVICE_ERROR is returned. + If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then + EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors + is returned in NumberOfProcessors, the number of currently enabled processor + is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL + instance. + @param[out] NumberOfProcessors Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL. + @retval EFI_INVALID_PARAMETER NumberOfEnabledProcessors is NULL. + +**/ +EFI_STATUS +EFIAPI +GetNumberOfProcessors ( + IN EFI_MP_SERVICES_PROTOCOL *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ); + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + This service retrieves detailed MP-related information about any processor + on the platform. Note the following: + - The processor information may change during the course of a boot session. + - The information presented here is entirely MP related. + + Information regarding the number of caches and their sizes, frequency of operation, + slot numbers is all considered platform-related information and is not provided + by this service. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL + instance. + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. + +**/ +EFI_STATUS +EFIAPI +GetProcessorInfo ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ); + +/** + This service executes a caller provided function on all enabled APs. APs can + run either simultaneously or one at a time in sequence. This service supports + both blocking and non-blocking requests. The non-blocking requests use EFI + events so the BSP can detect when the APs have finished. This service may only + be called from the BSP. + + This function is used to dispatch all the enabled APs to the function specified + by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned + immediately and Procedure is not started on any AP. + + If SingleThread is TRUE, all the enabled APs execute the function specified by + Procedure one by one, in ascending order of processor handle number. Otherwise, + all the enabled APs execute the function specified by Procedure simultaneously. + + If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all + APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in non-blocking + mode, and the BSP returns from this service without waiting for APs. If a + non-blocking mode is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT + is signaled, then EFI_UNSUPPORTED must be returned. + + If the timeout specified by TimeoutInMicroseconds expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. All enabled APs + are always available for further calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() + and EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its + content points to the list of processor handle numbers in which Procedure was + terminated. + + Note: It is the responsibility of the consumer of the EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() + to make sure that the nature of the code that is executed on the BSP and the + dispatched APs is well controlled. The MP Services Protocol does not guarantee + that the Procedure function is MP-safe. Hence, the tasks that can be run in + parallel are limited to certain independent tasks and well-controlled exclusive + code. EFI services and protocols may not be called by APs unless otherwise + specified. + + In blocking execution mode, BSP waits until all APs finish or + TimeoutInMicroseconds expires. + + In non-blocking execution mode, BSP is freed to return to the caller and then + proceed to the next task without having to wait for APs. The following + sequence needs to occur in a non-blocking execution mode: + + -# The caller that intends to use this MP Services Protocol in non-blocking + mode creates WaitEvent by calling the EFI CreateEvent() service. The caller + invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter WaitEvent + is not NULL, then StartupAllAPs() executes in non-blocking mode. It requests + the function specified by Procedure to be started on all the enabled APs, + and releases the BSP to continue with other tasks. + -# The caller can use the CheckEvent() and WaitForEvent() services to check + the state of the WaitEvent created in step 1. + -# When the APs complete their task or TimeoutInMicroSeconds expires, the MP + Service signals WaitEvent by calling the EFI SignalEvent() function. If + FailedCpuList is not NULL, its content is available when WaitEvent is + signaled. If all APs returned from Procedure prior to the timeout, then + FailedCpuList is set to NULL. If not all APs return from Procedure before + the timeout, then FailedCpuList is filled in with the list of the failed + APs. The buffer is allocated by MP Service Protocol using AllocatePool(). + It is the caller's responsibility to free the buffer with FreePool() service. + -# This invocation of SignalEvent() function informs the caller that invoked + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs completed + the specified task or a timeout occurred. The contents of FailedCpuList + can be examined to determine which APs did not complete the specified task + prior to the timeout. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL + instance. + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] SingleThread If TRUE, then all the enabled APs execute + the function specified by Procedure one by + one, in ascending order of processor handle + number. If FALSE, then all the enabled APs + execute the function specified by Procedure + simultaneously. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until all APs finish + or TimeoutInMicroseconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on all the enabled + APs, and go on executing immediately. If + all return from Procedure, or TimeoutInMicroseconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + all APs return from Procedure, then Procedure + on the failed APs is terminated. All enabled + APs are available for next function assigned + by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() + or EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise, + if all APs finish successfully, then its + content is set to NULL. If not all APs + finish before timeout expires, then its + content is set to address of the buffer + holding handle numbers of the failed APs. + The buffer is allocated by MP Service Protocol, + and it's the caller's responsibility to + free the buffer with FreePool() service. + In blocking mode, it is ready for consumption + when the call returns. In non-blocking mode, + it is ready when WaitEvent is signaled. The + list of failed CPU is terminated by + END_OF_CPU_LIST. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + all enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +StartupAllAPs ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT UINTN **FailedCpuList OPTIONAL + ); + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. The caller can request the BSP to either wait for the completion + of the AP or just proceed with the next task by using the EFI event mechanism. + See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking + execution support. This service may only be called from the BSP. + + This function is used to dispatch one enabled AP to the function specified by + Procedure passing in the argument specified by ProcedureArgument. If WaitEvent + is NULL, execution is in blocking mode. The BSP waits until the AP finishes or + TimeoutInMicroSeconds expires. Otherwise, execution is in non-blocking mode. + BSP proceeds to the next task without waiting for the AP. If a non-blocking mode + is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled, + then EFI_UNSUPPORTED must be returned. + + If the timeout specified by TimeoutInMicroseconds expires before the AP returns + from Procedure, then execution of Procedure by the AP is terminated. The AP is + available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and + EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL + instance. + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] ProcessorNumber The handle number of the AP. The range is + from 0 to the total number of logical + processors minus 1. The total number of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until all APs finish + or TimeoutInMicroseconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on all the enabled + APs, and go on executing immediately. If + all return from Procedure or TimeoutInMicroseconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + all APs return from Procedure, then Procedure + on the failed APs is terminated. All enabled + APs are available for next function assigned + by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() + or EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] Finished If NULL, this parameter is ignored. In + blocking mode, this parameter is ignored. + In non-blocking mode, if AP returns from + Procedure before the timeout expires, its + content is set to TRUE. Otherwise, the + value is set to FALSE. The caller can + determine if the AP returned from Procedure + by evaluating this value. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before + the timeout expires. + @retval EFI_SUCCESS In non-blocking mode, the function has been + dispatched to specified AP. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + the specified AP has finished. + @retval EFI_NOT_READY The specified AP is busy. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +StartupThisAP ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT BOOLEAN *Finished OPTIONAL + ); + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. The new BSP can take over the + execution of the old BSP and continue seamlessly from where the old one left + off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT + is signaled. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of AP that is to become the new + BSP. The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an + enabled AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to + this service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or + a disabled AP. + @retval EFI_NOT_READY The specified AP is busy. + +**/ +EFI_STATUS +EFIAPI +SwitchBSP ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ); + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + This service allows the caller enable or disable an AP from this point onward. + The caller can optionally specify the health status of the AP by Health. If + an AP is being disabled, then the state of the disabled AP is implementation + dependent. If an AP is enabled, then the implementation must guarantee that a + complete initialization sequence is performed on the AP, so the AP is in a state + that is compatible with an MP operating system. This service may not be supported + after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled. + + If the enable or disable AP operation cannot be completed prior to the return + from this service, then EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of AP that is to become the new + BSP. The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for + enabled, FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies + the new health status of the AP. This flag + corresponds to StatusFlag defined in + EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only + the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter + is ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed + prior to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. + +**/ +EFI_STATUS +EFIAPI +EnableDisableAP ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ); + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + This service returns the processor handle number for the calling processor. + The returned value is in the range from 0 to the total number of logical + processors minus 1. The total number of logical processors can be retrieved + with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be + called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER + is returned. Otherwise, the current processors handle number is returned in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance. + @param[out] ProcessorNumber The handle number of AP that is to become the new + BSP. The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned + in ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. + +**/ +EFI_STATUS +EFIAPI +WhoAmI ( + IN EFI_MP_SERVICES_PROTOCOL *This, + OUT UINTN *ProcessorNumber + ); + +#endif // _CPU_MP_H_ + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuPageTable.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuPageTable.c new file mode 100644 index 00000000..3f1411b5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuPageTable.c @@ -0,0 +1,1454 @@ +/** @file + Page table management support. + + Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR> + Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Base.h> +#include <Uefi.h> +#include <Library/PeCoffGetEntryPointLib.h> +#include <Library/SerialPortLib.h> +#include <Library/SynchronizationLib.h> +#include <Library/PrintLib.h> +#include <Protocol/SmmBase2.h> +#include <Register/Intel/Cpuid.h> +#include <Register/Intel/Msr.h> +#ifdef VBOX +# define IN_RING0 +# include <iprt/asm.h> +#endif + +#include "CpuDxe.h" +#include "CpuPageTable.h" + +/// +/// Page Table Entry +/// +#define IA32_PG_P BIT0 +#define IA32_PG_RW BIT1 +#define IA32_PG_U BIT2 +#define IA32_PG_WT BIT3 +#define IA32_PG_CD BIT4 +#define IA32_PG_A BIT5 +#define IA32_PG_D BIT6 +#define IA32_PG_PS BIT7 +#define IA32_PG_PAT_2M BIT12 +#define IA32_PG_PAT_4K IA32_PG_PS +#define IA32_PG_PMNT BIT62 +#define IA32_PG_NX BIT63 + +#define PAGE_ATTRIBUTE_BITS (IA32_PG_D | IA32_PG_A | IA32_PG_U | IA32_PG_RW | IA32_PG_P) +// +// Bits 1, 2, 5, 6 are reserved in the IA32 PAE PDPTE +// X64 PAE PDPTE does not have such restriction +// +#define IA32_PAE_PDPTE_ATTRIBUTE_BITS (IA32_PG_P) + +#define PAGE_PROGATE_BITS (IA32_PG_NX | PAGE_ATTRIBUTE_BITS) + +#define PAGING_4K_MASK 0xFFF +#define PAGING_2M_MASK 0x1FFFFF +#define PAGING_1G_MASK 0x3FFFFFFF + +#define PAGING_PAE_INDEX_MASK 0x1FF + +#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull +#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull +#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull + +#define MAX_PF_ENTRY_COUNT 10 +#define MAX_DEBUG_MESSAGE_LENGTH 0x100 +#define IA32_PF_EC_ID BIT4 + +typedef enum { + PageNone, + Page4K, + Page2M, + Page1G, +} PAGE_ATTRIBUTE; + +typedef struct { + PAGE_ATTRIBUTE Attribute; + UINT64 Length; + UINT64 AddressMask; +} PAGE_ATTRIBUTE_TABLE; + +typedef enum { + PageActionAssign, + PageActionSet, + PageActionClear, +} PAGE_ACTION; + +PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = { + {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64}, + {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64}, + {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64}, +}; + +PAGE_TABLE_POOL *mPageTablePool = NULL; +BOOLEAN mPageTablePoolLock = FALSE; +PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext; +EFI_SMM_BASE2_PROTOCOL *mSmmBase2 = NULL; + +// +// Record the page fault exception count for one instruction execution. +// +UINTN *mPFEntryCount; +UINT64 *(*mLastPFEntryPointer)[MAX_PF_ENTRY_COUNT]; + +#ifdef VBOX +/** + Safe page table entry write function, make 104% sure the compiler won't + split up the access (fatal if modifying entries for current code or data). + + @param[in] PageEntry The page table entry to modify.* + @param[in] CurrentPageEntry The old page table value (for cmpxchg8b). + @param[in] NewPageEntry What to write. +**/ +static VOID SafePageTableEntryWrite64 (UINT64 volatile *PageEntry, UINT64 CurrentPageEntry, UINT64 NewPageEntry) +{ +# ifdef VBOX + ASMAtomicWriteU64(PageEntry, NewPageEntry); RT_NOREF(CurrentPageEntry); +# else + for (;;) { + UINT64 CurValue = InterlockedCompareExchange64(PageEntry, CurrentPageEntry, NewPageEntry); + if (CurValue == CurrentPageEntry) + return; + CurrentPageEntry = CurValue; + } +# endif +} +#endif + +/** + Check if current execution environment is in SMM mode or not, via + EFI_SMM_BASE2_PROTOCOL. + + This is necessary because of the fact that MdePkg\Library\SmmMemoryAllocationLib + supports to free memory outside SMRAM. The library will call gBS->FreePool() or + gBS->FreePages() and then SetMemorySpaceAttributes interface in turn to change + memory paging attributes during free operation, if some memory related features + are enabled (like Heap Guard). + + This means that SetMemorySpaceAttributes() has chance to run in SMM mode. This + will cause incorrect result because SMM mode always loads its own page tables, + which are usually different from DXE. This function can be used to detect such + situation and help to avoid further misoperations. + + @retval TRUE In SMM mode. + @retval FALSE Not in SMM mode. +**/ +BOOLEAN +IsInSmm ( + VOID + ) +{ + BOOLEAN InSmm; + + InSmm = FALSE; + if (mSmmBase2 == NULL) { + gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&mSmmBase2); + } + + if (mSmmBase2 != NULL) { + mSmmBase2->InSmm (mSmmBase2, &InSmm); + } + + // + // mSmmBase2->InSmm() can only detect if the caller is running in SMRAM + // or from SMM driver. It cannot tell if the caller is running in SMM mode. + // Check page table base address to guarantee that because SMM mode willl + // load its own page table. + // + return (InSmm && + mPagingContext.ContextData.X64.PageTableBase != (UINT64)AsmReadCr3()); +} + +/** + Return current paging context. + + @param[in,out] PagingContext The paging context. +**/ +VOID +GetCurrentPagingContext ( + IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext + ) +{ + UINT32 RegEax; + CPUID_EXTENDED_CPU_SIG_EDX RegEdx; + MSR_IA32_EFER_REGISTER MsrEfer; + IA32_CR4 Cr4; + IA32_CR0 Cr0; + UINT32 *Attributes; + UINTN *PageTableBase; + + // + // Don't retrieve current paging context from processor if in SMM mode. + // + if (!IsInSmm ()) { + ZeroMem (&mPagingContext, sizeof(mPagingContext)); + if (sizeof(UINTN) == sizeof(UINT64)) { + mPagingContext.MachineType = IMAGE_FILE_MACHINE_X64; + } else { + mPagingContext.MachineType = IMAGE_FILE_MACHINE_I386; + } + + GetPagingDetails (&mPagingContext.ContextData, &PageTableBase, &Attributes); + + Cr0.UintN = AsmReadCr0 (); + Cr4.UintN = AsmReadCr4 (); + + if (Cr0.Bits.PG != 0) { + *PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64); + } else { + *PageTableBase = 0; + } + if (Cr0.Bits.WP != 0) { + *Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE; + } + if (Cr4.Bits.PSE != 0) { + *Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE; + } + if (Cr4.Bits.PAE != 0) { + *Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE; + } + if (Cr4.Bits.LA57 != 0) { + *Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_5_LEVEL; + } + + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_EXTENDED_CPU_SIG) { + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx.Uint32); + + if (RegEdx.Bits.NX != 0) { + // XD supported + MsrEfer.Uint64 = AsmReadMsr64(MSR_CORE_IA32_EFER); + if (MsrEfer.Bits.NXE != 0) { + // XD activated + *Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED; + } + } + + if (RegEdx.Bits.Page1GB != 0) { + *Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT; + } + } + } + + // + // This can avoid getting SMM paging context if in SMM mode. We cannot assume + // SMM mode shares the same paging context as DXE. + // + CopyMem (PagingContext, &mPagingContext, sizeof (mPagingContext)); +} + +/** + Return length according to page attributes. + + @param[in] PageAttributes The page attribute of the page entry. + + @return The length of page entry. +**/ +UINTN +PageAttributeToLength ( + IN PAGE_ATTRIBUTE PageAttribute + ) +{ + UINTN Index; + for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) { + if (PageAttribute == mPageAttributeTable[Index].Attribute) { + return (UINTN)mPageAttributeTable[Index].Length; + } + } + return 0; +} + +/** + Return address mask according to page attributes. + + @param[in] PageAttributes The page attribute of the page entry. + + @return The address mask of page entry. +**/ +UINTN +PageAttributeToMask ( + IN PAGE_ATTRIBUTE PageAttribute + ) +{ + UINTN Index; + for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) { + if (PageAttribute == mPageAttributeTable[Index].Attribute) { + return (UINTN)mPageAttributeTable[Index].AddressMask; + } + } + return 0; +} + +/** + Return page table entry to match the address. + + @param[in] PagingContext The paging context. + @param[in] Address The address to be checked. + @param[out] PageAttributes The page attribute of the page entry. + + @return The page entry. +**/ +VOID * +GetPageTableEntry ( + IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext, + IN PHYSICAL_ADDRESS Address, + OUT PAGE_ATTRIBUTE *PageAttribute + ) +{ + UINTN Index1; + UINTN Index2; + UINTN Index3; + UINTN Index4; + UINTN Index5; + UINT64 *L1PageTable; + UINT64 *L2PageTable; + UINT64 *L3PageTable; + UINT64 *L4PageTable; + UINT64 *L5PageTable; + UINT64 AddressEncMask; + + ASSERT (PagingContext != NULL); + + Index5 = ((UINTN)RShiftU64 (Address, 48)) & PAGING_PAE_INDEX_MASK; + Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK; + Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK; + Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK; + Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK; + + // Make sure AddressEncMask is contained to smallest supported address field. + // + AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64; + + if (PagingContext->MachineType == IMAGE_FILE_MACHINE_X64) { + if ((PagingContext->ContextData.X64.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_5_LEVEL) != 0) { + L5PageTable = (UINT64 *)(UINTN)PagingContext->ContextData.X64.PageTableBase; + if (L5PageTable[Index5] == 0) { + *PageAttribute = PageNone; + return NULL; + } + + L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64); + } else { + L4PageTable = (UINT64 *)(UINTN)PagingContext->ContextData.X64.PageTableBase; + } + if (L4PageTable[Index4] == 0) { + *PageAttribute = PageNone; + return NULL; + } + + L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64); + } else { + ASSERT((PagingContext->ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) != 0); + L3PageTable = (UINT64 *)(UINTN)PagingContext->ContextData.Ia32.PageTableBase; + } + if (L3PageTable[Index3] == 0) { + *PageAttribute = PageNone; + return NULL; + } + if ((L3PageTable[Index3] & IA32_PG_PS) != 0) { + // 1G + *PageAttribute = Page1G; + return &L3PageTable[Index3]; + } + + L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64); + if (L2PageTable[Index2] == 0) { + *PageAttribute = PageNone; + return NULL; + } + if ((L2PageTable[Index2] & IA32_PG_PS) != 0) { + // 2M + *PageAttribute = Page2M; + return &L2PageTable[Index2]; + } + + // 4k + L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64); + if ((L1PageTable[Index1] == 0) && (Address != 0)) { + *PageAttribute = PageNone; + return NULL; + } + *PageAttribute = Page4K; + return &L1PageTable[Index1]; +} + +/** + Return memory attributes of page entry. + + @param[in] PageEntry The page entry. + + @return Memory attributes of page entry. +**/ +UINT64 +GetAttributesFromPageEntry ( + IN UINT64 *PageEntry + ) +{ + UINT64 Attributes; + Attributes = 0; + if ((*PageEntry & IA32_PG_P) == 0) { + Attributes |= EFI_MEMORY_RP; + } + if ((*PageEntry & IA32_PG_RW) == 0) { + Attributes |= EFI_MEMORY_RO; + } + if ((*PageEntry & IA32_PG_NX) != 0) { + Attributes |= EFI_MEMORY_XP; + } + return Attributes; +} + +/** + Modify memory attributes of page entry. + + @param[in] PagingContext The paging context. + @param[in] PageEntry The page entry. + @param[in] Attributes The bit mask of attributes to modify for the memory region. + @param[in] PageAction The page action. + @param[out] IsModified TRUE means page table modified. FALSE means page table not modified. +**/ +VOID +ConvertPageEntryAttribute ( + IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext, +#ifdef VBOX + IN UINT64 volatile *PageEntry, +#else + IN UINT64 *PageEntry, +#endif + IN UINT64 Attributes, + IN PAGE_ACTION PageAction, + OUT BOOLEAN *IsModified + ) +{ + UINT64 CurrentPageEntry; + UINT64 NewPageEntry; + UINT32 *PageAttributes; + + CurrentPageEntry = *PageEntry; + NewPageEntry = CurrentPageEntry; + if ((Attributes & EFI_MEMORY_RP) != 0) { + switch (PageAction) { + case PageActionAssign: + case PageActionSet: + NewPageEntry &= ~(UINT64)IA32_PG_P; + break; + case PageActionClear: + NewPageEntry |= IA32_PG_P; + break; + } + } else { + switch (PageAction) { + case PageActionAssign: + NewPageEntry |= IA32_PG_P; + break; + case PageActionSet: + case PageActionClear: + break; + } + } + if ((Attributes & EFI_MEMORY_RO) != 0) { + switch (PageAction) { + case PageActionAssign: + case PageActionSet: + NewPageEntry &= ~(UINT64)IA32_PG_RW; + break; + case PageActionClear: + NewPageEntry |= IA32_PG_RW; + break; + } + } else { + switch (PageAction) { + case PageActionAssign: + NewPageEntry |= IA32_PG_RW; + break; + case PageActionSet: + case PageActionClear: + break; + } + } + + GetPagingDetails (&PagingContext->ContextData, NULL, &PageAttributes); + + if ((*PageAttributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED) != 0) { + if ((Attributes & EFI_MEMORY_XP) != 0) { + switch (PageAction) { + case PageActionAssign: + case PageActionSet: + NewPageEntry |= IA32_PG_NX; + break; + case PageActionClear: + NewPageEntry &= ~IA32_PG_NX; + break; + } + } else { + switch (PageAction) { + case PageActionAssign: + NewPageEntry &= ~IA32_PG_NX; + break; + case PageActionSet: + case PageActionClear: + break; + } + } + } +#ifndef VBOX + *PageEntry = NewPageEntry; +#endif + if (CurrentPageEntry != NewPageEntry) { +#ifdef VBOX + SafePageTableEntryWrite64 (PageEntry, CurrentPageEntry, NewPageEntry); +#endif + *IsModified = TRUE; + DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry)); + DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry)); + } else { + *IsModified = FALSE; + } +} + +/** + This function returns if there is need to split page entry. + + @param[in] BaseAddress The base address to be checked. + @param[in] Length The length to be checked. + @param[in] PageEntry The page entry to be checked. + @param[in] PageAttribute The page attribute of the page entry. + + @retval SplitAttributes on if there is need to split page entry. +**/ +PAGE_ATTRIBUTE +NeedSplitPage ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 *PageEntry, + IN PAGE_ATTRIBUTE PageAttribute + ) +{ + UINT64 PageEntryLength; + + PageEntryLength = PageAttributeToLength (PageAttribute); + + if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) { + return PageNone; + } + + if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) { + return Page4K; + } + + return Page2M; +} + +/** + This function splits one page entry to small page entries. + + @param[in] PageEntry The page entry to be splitted. + @param[in] PageAttribute The page attribute of the page entry. + @param[in] SplitAttribute How to split the page entry. + @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages. + + @retval RETURN_SUCCESS The page entry is splitted. + @retval RETURN_UNSUPPORTED The page entry does not support to be splitted. + @retval RETURN_OUT_OF_RESOURCES No resource to split page entry. +**/ +RETURN_STATUS +SplitPage ( +#ifdef VBOX + IN UINT64 volatile *PageEntry, +#else + IN UINT64 *PageEntry, +#endif + IN PAGE_ATTRIBUTE PageAttribute, + IN PAGE_ATTRIBUTE SplitAttribute, + IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc + ) +{ + UINT64 BaseAddress; +#ifdef VBOX + UINT64 CurrentPageEntry; +#endif + UINT64 *NewPageEntry; + UINTN Index; + UINT64 AddressEncMask; + + ASSERT (PageAttribute == Page2M || PageAttribute == Page1G); + + ASSERT (AllocatePagesFunc != NULL); + + // Make sure AddressEncMask is contained to smallest supported address field. + // + AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64; + + if (PageAttribute == Page2M) { + // + // Split 2M to 4K + // + ASSERT (SplitAttribute == Page4K); + if (SplitAttribute == Page4K) { + NewPageEntry = AllocatePagesFunc (1); + DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry)); + if (NewPageEntry == NULL) { + return RETURN_OUT_OF_RESOURCES; + } +#ifdef VBOX + CurrentPageEntry = *PageEntry; + BaseAddress = CurrentPageEntry & ~AddressEncMask & PAGING_2M_ADDRESS_MASK_64; +#else + BaseAddress = *PageEntry & ~AddressEncMask & PAGING_2M_ADDRESS_MASK_64; +#endif + for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { +#ifdef VBOX + NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | AddressEncMask | (CurrentPageEntry & PAGE_PROGATE_BITS); +#else + NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS); +#endif + } +#ifdef VBOX + SafePageTableEntryWrite64 (PageEntry, CurrentPageEntry, + (UINT64)(UINTN)NewPageEntry | AddressEncMask | (CurrentPageEntry & PAGE_ATTRIBUTE_BITS)); +#else + (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_ATTRIBUTE_BITS); +#endif + return RETURN_SUCCESS; + } else { + return RETURN_UNSUPPORTED; + } + } else if (PageAttribute == Page1G) { + // + // Split 1G to 2M + // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table. + // + ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K); + if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) { + NewPageEntry = AllocatePagesFunc (1); + DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry)); + if (NewPageEntry == NULL) { + return RETURN_OUT_OF_RESOURCES; + } +#ifdef VBOX + CurrentPageEntry = *PageEntry; + BaseAddress = CurrentPageEntry & ~AddressEncMask & PAGING_1G_ADDRESS_MASK_64; +#else + BaseAddress = *PageEntry & ~AddressEncMask & PAGING_1G_ADDRESS_MASK_64; +#endif + for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { +#ifdef VBOX + NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | AddressEncMask | IA32_PG_PS | (CurrentPageEntry & PAGE_PROGATE_BITS); +#else + NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | AddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS); +#endif + } +#ifdef VBOX + SafePageTableEntryWrite64 (PageEntry, CurrentPageEntry, + (UINT64)(UINTN)NewPageEntry | AddressEncMask | (CurrentPageEntry & PAGE_ATTRIBUTE_BITS)); +#else + (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_ATTRIBUTE_BITS); +#endif + return RETURN_SUCCESS; + } else { + return RETURN_UNSUPPORTED; + } + } else { + return RETURN_UNSUPPORTED; + } +} + +/** + Check the WP status in CR0 register. This bit is used to lock or unlock write + access to pages marked as read-only. + + @retval TRUE Write protection is enabled. + @retval FALSE Write protection is disabled. +**/ +BOOLEAN +IsReadOnlyPageWriteProtected ( + VOID + ) +{ + IA32_CR0 Cr0; + // + // To avoid unforseen consequences, don't touch paging settings in SMM mode + // in this driver. + // + if (!IsInSmm ()) { + Cr0.UintN = AsmReadCr0 (); + return (BOOLEAN) (Cr0.Bits.WP != 0); + } + return FALSE; +} + +/** + Disable Write Protect on pages marked as read-only. +**/ +VOID +DisableReadOnlyPageWriteProtect ( + VOID + ) +{ + IA32_CR0 Cr0; + // + // To avoid unforseen consequences, don't touch paging settings in SMM mode + // in this driver. + // + if (!IsInSmm ()) { + Cr0.UintN = AsmReadCr0 (); + Cr0.Bits.WP = 0; + AsmWriteCr0 (Cr0.UintN); + } +} + +/** + Enable Write Protect on pages marked as read-only. +**/ +VOID +EnableReadOnlyPageWriteProtect ( + VOID + ) +{ + IA32_CR0 Cr0; + // + // To avoid unforseen consequences, don't touch paging settings in SMM mode + // in this driver. + // + if (!IsInSmm ()) { + Cr0.UintN = AsmReadCr0 (); + Cr0.Bits.WP = 1; + AsmWriteCr0 (Cr0.UintN); + } +} + +/** + This function modifies the page attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + Caller should make sure BaseAddress and Length is at page boundary. + + @param[in] PagingContext The paging context. NULL means get page table from current CPU context. + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attributes The bit mask of attributes to modify for the memory region. + @param[in] PageAction The page action. + @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages. + NULL mean page split is unsupported. + @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted. + @param[out] IsModified TRUE means page table modified. FALSE means page table not modified. + + @retval RETURN_SUCCESS The attributes were modified for the memory region. + @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval RETURN_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be set together. + @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. +**/ +RETURN_STATUS +ConvertMemoryPageAttributes ( + IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes, + IN PAGE_ACTION PageAction, + IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL, + OUT BOOLEAN *IsSplitted, OPTIONAL + OUT BOOLEAN *IsModified OPTIONAL + ) +{ + PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext; + UINT64 *PageEntry; + PAGE_ATTRIBUTE PageAttribute; + UINTN PageEntryLength; + PAGE_ATTRIBUTE SplitAttribute; + RETURN_STATUS Status; + BOOLEAN IsEntryModified; + BOOLEAN IsWpEnabled; + + if ((BaseAddress & (SIZE_4KB - 1)) != 0) { + DEBUG ((DEBUG_ERROR, "BaseAddress(0x%lx) is not aligned!\n", BaseAddress)); + return EFI_UNSUPPORTED; + } + if ((Length & (SIZE_4KB - 1)) != 0) { + DEBUG ((DEBUG_ERROR, "Length(0x%lx) is not aligned!\n", Length)); + return EFI_UNSUPPORTED; + } + if (Length == 0) { + DEBUG ((DEBUG_ERROR, "Length is 0!\n")); + return RETURN_INVALID_PARAMETER; + } + + if ((Attributes & ~EFI_MEMORY_ATTRIBUTE_MASK) != 0) { + DEBUG ((DEBUG_ERROR, "Attributes(0x%lx) has unsupported bit\n", Attributes)); + return EFI_UNSUPPORTED; + } + + if (PagingContext == NULL) { + GetCurrentPagingContext (&CurrentPagingContext); + } else { + CopyMem (&CurrentPagingContext, PagingContext, sizeof(CurrentPagingContext)); + } + switch(CurrentPagingContext.MachineType) { + case IMAGE_FILE_MACHINE_I386: + if (CurrentPagingContext.ContextData.Ia32.PageTableBase == 0) { + if (Attributes == 0) { + return EFI_SUCCESS; + } else { + DEBUG ((DEBUG_ERROR, "PageTable is 0!\n")); + return EFI_UNSUPPORTED; + } + } + if ((CurrentPagingContext.ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) == 0) { + DEBUG ((DEBUG_ERROR, "Non-PAE Paging!\n")); + return EFI_UNSUPPORTED; + } + if ((BaseAddress + Length) > BASE_4GB) { + DEBUG ((DEBUG_ERROR, "Beyond 4GB memory in 32-bit mode!\n")); + return EFI_UNSUPPORTED; + } + break; + case IMAGE_FILE_MACHINE_X64: + ASSERT (CurrentPagingContext.ContextData.X64.PageTableBase != 0); + break; + default: + ASSERT(FALSE); + return EFI_UNSUPPORTED; + break; + } + +// DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes)); + + if (IsSplitted != NULL) { + *IsSplitted = FALSE; + } + if (IsModified != NULL) { + *IsModified = FALSE; + } + if (AllocatePagesFunc == NULL) { + AllocatePagesFunc = AllocatePageTableMemory; + } + + // + // Make sure that the page table is changeable. + // + IsWpEnabled = IsReadOnlyPageWriteProtected (); + if (IsWpEnabled) { + DisableReadOnlyPageWriteProtect (); + } + + // + // Below logic is to check 2M/4K page to make sure we do not waste memory. + // + Status = EFI_SUCCESS; + while (Length != 0) { + PageEntry = GetPageTableEntry (&CurrentPagingContext, BaseAddress, &PageAttribute); + if (PageEntry == NULL) { + Status = RETURN_UNSUPPORTED; + goto Done; + } + PageEntryLength = PageAttributeToLength (PageAttribute); + SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute); + if (SplitAttribute == PageNone) { + ConvertPageEntryAttribute (&CurrentPagingContext, PageEntry, Attributes, PageAction, &IsEntryModified); + if (IsEntryModified) { + if (IsModified != NULL) { + *IsModified = TRUE; + } + } + // + // Convert success, move to next + // + BaseAddress += PageEntryLength; + Length -= PageEntryLength; + } else { + if (AllocatePagesFunc == NULL) { + Status = RETURN_UNSUPPORTED; + goto Done; + } + Status = SplitPage (PageEntry, PageAttribute, SplitAttribute, AllocatePagesFunc); + if (RETURN_ERROR (Status)) { + Status = RETURN_UNSUPPORTED; + goto Done; + } + if (IsSplitted != NULL) { + *IsSplitted = TRUE; + } + if (IsModified != NULL) { + *IsModified = TRUE; + } + // + // Just split current page + // Convert success in next around + // + } + } + +Done: + // + // Restore page table write protection, if any. + // + if (IsWpEnabled) { + EnableReadOnlyPageWriteProtect (); + } + return Status; +} + +/** + This function assigns the page attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + Caller should make sure BaseAddress and Length is at page boundary. + + Caller need guarantee the TPL <= TPL_NOTIFY, if there is split page request. + + @param[in] PagingContext The paging context. NULL means get page table from current CPU context. + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attributes The bit mask of attributes to set for the memory region. + @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages. + NULL mean page split is unsupported. + + @retval RETURN_SUCCESS The attributes were cleared for the memory region. + @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval RETURN_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be set together. + @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. +**/ +RETURN_STATUS +EFIAPI +AssignMemoryPageAttributes ( + IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes, + IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL + ) +{ + RETURN_STATUS Status; + BOOLEAN IsModified; + BOOLEAN IsSplitted; + +// DEBUG((DEBUG_INFO, "AssignMemoryPageAttributes: 0x%lx - 0x%lx (0x%lx)\n", BaseAddress, Length, Attributes)); + Status = ConvertMemoryPageAttributes (PagingContext, BaseAddress, Length, Attributes, PageActionAssign, AllocatePagesFunc, &IsSplitted, &IsModified); + if (!EFI_ERROR(Status)) { + if ((PagingContext == NULL) && IsModified) { + // + // Flush TLB as last step. + // + // Note: Since APs will always init CR3 register in HLT loop mode or do + // TLB flush in MWAIT loop mode, there's no need to flush TLB for them + // here. + // + CpuFlushTlb(); + } + } + + return Status; +} + +/** + Check if Execute Disable feature is enabled or not. +**/ +BOOLEAN +IsExecuteDisableEnabled ( + VOID + ) +{ + MSR_CORE_IA32_EFER_REGISTER MsrEfer; + + MsrEfer.Uint64 = AsmReadMsr64 (MSR_IA32_EFER); + return (MsrEfer.Bits.NXE == 1); +} + +/** + Update GCD memory space attributes according to current page table setup. +**/ +VOID +RefreshGcdMemoryAttributesFromPaging ( + VOID + ) +{ + EFI_STATUS Status; + UINTN NumberOfDescriptors; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; + PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext; + PAGE_ATTRIBUTE PageAttribute; + UINT64 *PageEntry; + UINT64 PageLength; + UINT64 MemorySpaceLength; + UINT64 Length; + UINT64 BaseAddress; + UINT64 PageStartAddress; + UINT64 Attributes; + UINT64 Capabilities; + UINT64 NewAttributes; + UINTN Index; + + // + // Assuming that memory space map returned is sorted already; otherwise sort + // them in the order of lowest address to highest address. + // + Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); + ASSERT_EFI_ERROR (Status); + + GetCurrentPagingContext (&PagingContext); + + Attributes = 0; + NewAttributes = 0; + BaseAddress = 0; + PageLength = 0; + + if (IsExecuteDisableEnabled ()) { + Capabilities = EFI_MEMORY_RO | EFI_MEMORY_RP | EFI_MEMORY_XP; + } else { + Capabilities = EFI_MEMORY_RO | EFI_MEMORY_RP; + } + + for (Index = 0; Index < NumberOfDescriptors; Index++) { + if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) { + continue; + } + + // + // Sync the actual paging related capabilities back to GCD service first. + // As a side effect (good one), this can also help to avoid unnecessary + // memory map entries due to the different capabilities of the same type + // memory, such as multiple RT_CODE and RT_DATA entries in memory map, + // which could cause boot failure of some old Linux distro (before v4.3). + // + Status = gDS->SetMemorySpaceCapabilities ( + MemorySpaceMap[Index].BaseAddress, + MemorySpaceMap[Index].Length, + MemorySpaceMap[Index].Capabilities | Capabilities + ); + if (EFI_ERROR (Status)) { + // + // If we cannot update the capabilities, we cannot update its + // attributes either. So just simply skip current block of memory. + // + DEBUG (( + DEBUG_WARN, + "Failed to update capability: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n", + (UINT64)Index, MemorySpaceMap[Index].BaseAddress, + MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - 1, + MemorySpaceMap[Index].Capabilities, + MemorySpaceMap[Index].Capabilities | Capabilities + )); + continue; + } + + if (MemorySpaceMap[Index].BaseAddress >= (BaseAddress + PageLength)) { + // + // Current memory space starts at a new page. Resetting PageLength will + // trigger a retrieval of page attributes at new address. + // + PageLength = 0; + } else { + // + // In case current memory space is not adjacent to last one + // + PageLength -= (MemorySpaceMap[Index].BaseAddress - BaseAddress); + } + + // + // Sync actual page attributes to GCD + // + BaseAddress = MemorySpaceMap[Index].BaseAddress; + MemorySpaceLength = MemorySpaceMap[Index].Length; + while (MemorySpaceLength > 0) { + if (PageLength == 0) { + PageEntry = GetPageTableEntry (&PagingContext, BaseAddress, &PageAttribute); + if (PageEntry == NULL) { + break; + } + + // + // Note current memory space might start in the middle of a page + // + PageStartAddress = (*PageEntry) & (UINT64)PageAttributeToMask(PageAttribute); + PageLength = PageAttributeToLength (PageAttribute) - (BaseAddress - PageStartAddress); + Attributes = GetAttributesFromPageEntry (PageEntry); + } + + Length = MIN (PageLength, MemorySpaceLength); + if (Attributes != (MemorySpaceMap[Index].Attributes & + EFI_MEMORY_ATTRIBUTE_MASK)) { + NewAttributes = (MemorySpaceMap[Index].Attributes & + ~EFI_MEMORY_ATTRIBUTE_MASK) | Attributes; + Status = gDS->SetMemorySpaceAttributes ( + BaseAddress, + Length, + NewAttributes + ); + ASSERT_EFI_ERROR (Status); + DEBUG (( + DEBUG_VERBOSE, + "Updated memory space attribute: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n", + (UINT64)Index, BaseAddress, BaseAddress + Length - 1, + MemorySpaceMap[Index].Attributes, + NewAttributes + )); + } + + PageLength -= Length; + MemorySpaceLength -= Length; + BaseAddress += Length; + } + } + + FreePool (MemorySpaceMap); +} + +/** + Initialize a buffer pool for page table use only. + + To reduce the potential split operation on page table, the pages reserved for + page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and + at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always + initialized with number of pages greater than or equal to the given PoolPages. + + Once the pages in the pool are used up, this method should be called again to + reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. Usually this won't happen + often in practice. + + @param[in] PoolPages The least page number of the pool to be created. + + @retval TRUE The pool is initialized successfully. + @retval FALSE The memory is out of resource. +**/ +BOOLEAN +InitializePageTablePool ( + IN UINTN PoolPages + ) +{ + VOID *Buffer; + BOOLEAN IsModified; + + // + // Do not allow re-entrance. + // + if (mPageTablePoolLock) { + return FALSE; + } + + mPageTablePoolLock = TRUE; + IsModified = FALSE; + + // + // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for + // header. + // + PoolPages += 1; // Add one page for header. + PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) * + PAGE_TABLE_POOL_UNIT_PAGES; + Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT); + if (Buffer == NULL) { + DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n")); + goto Done; + } + + DEBUG (( + DEBUG_INFO, + "Paging: added %lu pages to page table pool\r\n", + (UINT64)PoolPages + )); + + // + // Link all pools into a list for easier track later. + // + if (mPageTablePool == NULL) { + mPageTablePool = Buffer; + mPageTablePool->NextPool = mPageTablePool; + } else { + ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool; + mPageTablePool->NextPool = Buffer; + mPageTablePool = Buffer; + } + + // + // Reserve one page for pool header. + // + mPageTablePool->FreePages = PoolPages - 1; + mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1); + + // + // Mark the whole pool pages as read-only. + // + ConvertMemoryPageAttributes ( + NULL, + (PHYSICAL_ADDRESS)(UINTN)Buffer, + EFI_PAGES_TO_SIZE (PoolPages), + EFI_MEMORY_RO, + PageActionSet, + AllocatePageTableMemory, + NULL, + &IsModified + ); + ASSERT (IsModified == TRUE); + +Done: + mPageTablePoolLock = FALSE; + return IsModified; +} + +/** + This API provides a way to allocate memory for page table. + + This API can be called more than once to allocate memory for page tables. + + Allocates the number of 4KB pages and returns a pointer to the allocated + buffer. The buffer returned is aligned on a 4KB boundary. + + If Pages is 0, then NULL is returned. + If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocatePageTableMemory ( + IN UINTN Pages + ) +{ + VOID *Buffer; + + if (Pages == 0) { + return NULL; + } + + // + // Renew the pool if necessary. + // + if (mPageTablePool == NULL || + Pages > mPageTablePool->FreePages) { + if (!InitializePageTablePool (Pages)) { + return NULL; + } + } + + Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset; + + mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages); + mPageTablePool->FreePages -= Pages; + + return Buffer; +} + +/** + Special handler for #DB exception, which will restore the page attributes + (not-present). It should work with #PF handler which will set pages to + 'present'. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. + +**/ +VOID +EFIAPI +DebugExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN CpuIndex; + UINTN PFEntry; + BOOLEAN IsWpEnabled; + + MpInitLibWhoAmI (&CpuIndex); + + // + // Clear last PF entries + // + IsWpEnabled = IsReadOnlyPageWriteProtected (); + if (IsWpEnabled) { + DisableReadOnlyPageWriteProtect (); + } + + for (PFEntry = 0; PFEntry < mPFEntryCount[CpuIndex]; PFEntry++) { + if (mLastPFEntryPointer[CpuIndex][PFEntry] != NULL) { + *mLastPFEntryPointer[CpuIndex][PFEntry] &= ~(UINT64)IA32_PG_P; + } + } + + if (IsWpEnabled) { + EnableReadOnlyPageWriteProtect (); + } + + // + // Reset page fault exception count for next page fault. + // + mPFEntryCount[CpuIndex] = 0; + + // + // Flush TLB + // + CpuFlushTlb (); + + // + // Clear TF in EFLAGS + // + if (mPagingContext.MachineType == IMAGE_FILE_MACHINE_I386) { + SystemContext.SystemContextIa32->Eflags &= (UINT32)~BIT8; + } else { + SystemContext.SystemContextX64->Rflags &= (UINT64)~BIT8; + } +} + +/** + Special handler for #PF exception, which will set the pages which caused + #PF to be 'present'. The attribute of those pages should be restored in + the subsequent #DB handler. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. + +**/ +VOID +EFIAPI +PageFaultExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EFI_STATUS Status; + UINT64 PFAddress; + PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext; + PAGE_ATTRIBUTE PageAttribute; + UINT64 Attributes; + UINT64 *PageEntry; + UINTN Index; + UINTN CpuIndex; + UINTN PageNumber; + BOOLEAN NonStopMode; + + PFAddress = AsmReadCr2 () & ~EFI_PAGE_MASK; + if (PFAddress < BASE_4KB) { + NonStopMode = NULL_DETECTION_NONSTOP_MODE ? TRUE : FALSE; + } else { + NonStopMode = HEAP_GUARD_NONSTOP_MODE ? TRUE : FALSE; + } + + if (NonStopMode) { + MpInitLibWhoAmI (&CpuIndex); + GetCurrentPagingContext (&PagingContext); + // + // Memory operation cross page boundary, like "rep mov" instruction, will + // cause infinite loop between this and Debug Trap handler. We have to make + // sure that current page and the page followed are both in PRESENT state. + // + PageNumber = 2; + while (PageNumber > 0) { + PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute); + ASSERT(PageEntry != NULL); + + if (PageEntry != NULL) { + Attributes = GetAttributesFromPageEntry (PageEntry); + if ((Attributes & EFI_MEMORY_RP) != 0) { + Attributes &= ~EFI_MEMORY_RP; + Status = AssignMemoryPageAttributes (&PagingContext, PFAddress, + EFI_PAGE_SIZE, Attributes, NULL); + if (!EFI_ERROR(Status)) { + Index = mPFEntryCount[CpuIndex]; + // + // Re-retrieve page entry because above calling might update page + // table due to table split. + // + PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute); + mLastPFEntryPointer[CpuIndex][Index++] = PageEntry; + mPFEntryCount[CpuIndex] = Index; + } + } + } + + PFAddress += EFI_PAGE_SIZE; + --PageNumber; + } + } + + // + // Initialize the serial port before dumping. + // + SerialPortInitialize (); + // + // Display ExceptionType, CPU information and Image information + // + DumpCpuContext (ExceptionType, SystemContext); + if (NonStopMode) { + // + // Set TF in EFLAGS + // + if (mPagingContext.MachineType == IMAGE_FILE_MACHINE_I386) { + SystemContext.SystemContextIa32->Eflags |= (UINT32)BIT8; + } else { + SystemContext.SystemContextX64->Rflags |= (UINT64)BIT8; + } + } else { + CpuDeadLoop (); + } +} + +/** + Initialize the Page Table lib. +**/ +VOID +InitializePageTableLib ( + VOID + ) +{ + PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext; + UINT32 *Attributes; + UINTN *PageTableBase; + + GetCurrentPagingContext (&CurrentPagingContext); + + GetPagingDetails (&CurrentPagingContext.ContextData, &PageTableBase, &Attributes); + + // + // Reserve memory of page tables for future uses, if paging is enabled. + // + if ((*PageTableBase != 0) && + (*Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) != 0) { + DisableReadOnlyPageWriteProtect (); + InitializePageTablePool (1); + EnableReadOnlyPageWriteProtect (); + } + + if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) { + mPFEntryCount = (UINTN *)AllocateZeroPool (sizeof (UINTN) * mNumberOfProcessors); + ASSERT (mPFEntryCount != NULL); + + mLastPFEntryPointer = (UINT64 *(*)[MAX_PF_ENTRY_COUNT]) + AllocateZeroPool (sizeof (mLastPFEntryPointer[0]) * mNumberOfProcessors); + ASSERT (mLastPFEntryPointer != NULL); + } + + DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n")); + DEBUG ((DEBUG_INFO, " MachineType - 0x%x\n", CurrentPagingContext.MachineType)); + DEBUG ((DEBUG_INFO, " PageTableBase - 0x%Lx\n", (UINT64)*PageTableBase)); + DEBUG ((DEBUG_INFO, " Attributes - 0x%x\n", *Attributes)); + + return ; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuPageTable.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuPageTable.h new file mode 100644 index 00000000..2822b1f9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/CpuPageTable.h @@ -0,0 +1,157 @@ +/** @file + Page table management header file. + + Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _PAGE_TABLE_LIB_H_ +#define _PAGE_TABLE_LIB_H_ + +#include <IndustryStandard/PeImage.h> + +#define PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE BIT0 +#define PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE BIT1 +#define PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT BIT2 +#define PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_5_LEVEL BIT3 +#define PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE BIT30 +#define PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED BIT31 +// Other bits are reserved for future use +typedef struct { + UINT32 PageTableBase; + UINT32 Reserved; + UINT32 Attributes; +} PAGE_TABLE_LIB_PAGING_CONTEXT_IA32; + +typedef struct { + UINT64 PageTableBase; + UINT32 Attributes; +} PAGE_TABLE_LIB_PAGING_CONTEXT_X64; + +typedef union { + PAGE_TABLE_LIB_PAGING_CONTEXT_IA32 Ia32; + PAGE_TABLE_LIB_PAGING_CONTEXT_X64 X64; +} PAGE_TABLE_LIB_PAGING_CONTEXT_DATA; + +typedef struct { + // + // PE32+ Machine type for EFI images + // + // #define IMAGE_FILE_MACHINE_I386 0x014c + // #define IMAGE_FILE_MACHINE_X64 0x8664 + // + UINT16 MachineType; + PAGE_TABLE_LIB_PAGING_CONTEXT_DATA ContextData; +} PAGE_TABLE_LIB_PAGING_CONTEXT; + +#define PAGE_TABLE_POOL_ALIGNMENT BASE_2MB +#define PAGE_TABLE_POOL_UNIT_SIZE SIZE_2MB +#define PAGE_TABLE_POOL_UNIT_PAGES EFI_SIZE_TO_PAGES (PAGE_TABLE_POOL_UNIT_SIZE) +#define PAGE_TABLE_POOL_ALIGN_MASK \ + (~(EFI_PHYSICAL_ADDRESS)(PAGE_TABLE_POOL_ALIGNMENT - 1)) + +typedef struct { + VOID *NextPool; + UINTN Offset; + UINTN FreePages; +} PAGE_TABLE_POOL; + + +/** + Allocates one or more 4KB pages for page table. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +typedef +VOID * +(EFIAPI *PAGE_TABLE_LIB_ALLOCATE_PAGES) ( + IN UINTN Pages + ); + +/** + This function assigns the page attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + Caller should make sure BaseAddress and Length is at page boundary. + + Caller need guarantee the TPL <= TPL_NOTIFY, if there is split page request. + + @param PagingContext The paging context. NULL means get page table from current CPU context. + @param BaseAddress The physical address that is the start address of a memory region. + @param Length The size in bytes of the memory region. + @param Attributes The bit mask of attributes to set for the memory region. + @param AllocatePagesFunc If page split is needed, this function is used to allocate more pages. + NULL mean page split is unsupported. + + @retval RETURN_SUCCESS The attributes were cleared for the memory region. + @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval RETURN_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be set together. + @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. +**/ +RETURN_STATUS +EFIAPI +AssignMemoryPageAttributes ( + IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes, + IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL + ); + +/** + Initialize the Page Table lib. +**/ +VOID +InitializePageTableLib ( + VOID + ); + +/** + This API provides a way to allocate memory for page table. + + This API can be called more once to allocate memory for page tables. + + Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the + allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL + is returned. If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocatePageTableMemory ( + IN UINTN Pages + ); + +/** + Get paging details. + + @param PagingContextData The paging context. + @param PageTableBase Return PageTableBase field. + @param Attributes Return Attributes field. + +**/ +VOID +GetPagingDetails ( + IN PAGE_TABLE_LIB_PAGING_CONTEXT_DATA *PagingContextData, + OUT UINTN **PageTableBase OPTIONAL, + OUT UINT32 **Attributes OPTIONAL + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/Ia32/CpuAsm.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/Ia32/CpuAsm.nasm new file mode 100644 index 00000000..b9d440a7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/Ia32/CpuAsm.nasm @@ -0,0 +1,47 @@ +;------------------------------------------------------------------------------ +;* +;* Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +;* SPDX-License-Identifier: BSD-2-Clause-Patent +;* +;* CpuAsm.nasm +;* +;* Abstract: +;* +;------------------------------------------------------------------------------ + + SECTION .text + +;------------------------------------------------------------------------------ +; VOID +; SetCodeSelector ( +; UINT16 Selector +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(SetCodeSelector) +ASM_PFX(SetCodeSelector): + mov ecx, [esp+4] + sub esp, 0x10 + lea eax, [setCodeSelectorLongJump] + mov [esp], eax + mov [esp+4], cx + jmp dword far [esp] +setCodeSelectorLongJump: + add esp, 0x10 + ret + +;------------------------------------------------------------------------------ +; VOID +; SetDataSelectors ( +; UINT16 Selector +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(SetDataSelectors) +ASM_PFX(SetDataSelectors): + mov ecx, [esp+4] +o16 mov ss, cx +o16 mov ds, cx +o16 mov es, cx +o16 mov fs, cx +o16 mov gs, cx + ret + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/Ia32/PagingAttribute.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/Ia32/PagingAttribute.c new file mode 100644 index 00000000..348c96ac --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/Ia32/PagingAttribute.c @@ -0,0 +1,34 @@ +/** @file + Return Paging attribute. + + Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuPageTable.h" + + +/** + Get paging details. + + @param PagingContextData The paging context. + @param PageTableBase Return PageTableBase field. + @param Attributes Return Attributes field. + +**/ +VOID +GetPagingDetails ( + IN PAGE_TABLE_LIB_PAGING_CONTEXT_DATA *PagingContextData, + OUT UINTN **PageTableBase OPTIONAL, + OUT UINT32 **Attributes OPTIONAL + ) +{ + if (PageTableBase != NULL) { + *PageTableBase = &PagingContextData->Ia32.PageTableBase; + } + if (Attributes != NULL) { + *Attributes = &PagingContextData->Ia32.Attributes; + } +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/X64/CpuAsm.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/X64/CpuAsm.nasm new file mode 100644 index 00000000..04607127 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/X64/CpuAsm.nasm @@ -0,0 +1,44 @@ +;------------------------------------------------------------------------------ +;* +;* Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +;* SPDX-License-Identifier: BSD-2-Clause-Patent +;* +;* CpuAsm.nasm +;* +;* Abstract: +;* +;------------------------------------------------------------------------------ + + DEFAULT REL + SECTION .text + +;------------------------------------------------------------------------------ +; VOID +; SetCodeSelector ( +; UINT16 Selector +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(SetCodeSelector) +ASM_PFX(SetCodeSelector): + push rcx + lea rax, [setCodeSelectorLongJump] + push rax + o64 retf +setCodeSelectorLongJump: + ret + +;------------------------------------------------------------------------------ +; VOID +; SetDataSelectors ( +; UINT16 Selector +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(SetDataSelectors) +ASM_PFX(SetDataSelectors): +o16 mov ss, cx +o16 mov ds, cx +o16 mov es, cx +o16 mov fs, cx +o16 mov gs, cx + ret + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/X64/PagingAttribute.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/X64/PagingAttribute.c new file mode 100644 index 00000000..50b05f2c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuDxe/X64/PagingAttribute.c @@ -0,0 +1,34 @@ +/** @file + Return Paging attribute. + + Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuPageTable.h" + + +/** + Get paging details. + + @param PagingContextData The paging context. + @param PageTableBase Return PageTableBase field. + @param Attributes Return Attributes field. + +**/ +VOID +GetPagingDetails ( + IN PAGE_TABLE_LIB_PAGING_CONTEXT_DATA *PagingContextData, + OUT UINTN **PageTableBase OPTIONAL, + OUT UINT32 **Attributes OPTIONAL + ) +{ + if (PageTableBase != NULL) { + *PageTableBase = &PagingContextData->X64.PageTableBase; + } + if (Attributes != NULL) { + *Attributes = &PagingContextData->X64.Attributes; + } +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesDxe.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesDxe.c new file mode 100644 index 00000000..11896ded --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesDxe.c @@ -0,0 +1,136 @@ +/** @file + CPU Features DXE driver to initialize CPU features. + + Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/RegisterCpuFeaturesLib.h> +#include <Library/HobLib.h> + +#include <Protocol/SmmConfiguration.h> +#include <Guid/CpuFeaturesInitDone.h> + + +/** + Worker function to perform CPU feature initialization. + +**/ +VOID +CpuFeaturesInitializeWorker ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + + CpuFeaturesDetect (); + + CpuFeaturesInitialize (); + + // + // Install CPU Features Init Done Protocol + // + Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &Handle, + &gEdkiiCpuFeaturesInitDoneGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Event notification that initialize CPU features when gEfiSmmConfigurationProtocol installs. + + @param[in] Event The Event that is being processed, not used. + @param[in] Context Event Context, not used. +**/ +VOID +EFIAPI +SmmConfigurationEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_SMM_CONFIGURATION_PROTOCOL *SmmConfiguration; + + // + // Make sure this notification is for this handler + // + Status = gBS->LocateProtocol (&gEfiSmmConfigurationProtocolGuid, NULL, (VOID **)&SmmConfiguration); + if (EFI_ERROR (Status)) { + return; + } + + CpuFeaturesInitializeWorker (); +} + +/** + CPU Features driver entry point function. + + If PcdCpuFeaturesInitAfterSmmRelocation is TRUE, it will register one + SMM Configuration Protocol notify function to perform CPU features + initialization. Otherwise, it will perform CPU features initialization + directly. + + @param ImageHandle Image handle this driver. + @param SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS CPU Features is initialized successfully. +**/ +EFI_STATUS +EFIAPI +CpuFeaturesDxeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + VOID *Registration; + EFI_STATUS Status; + EFI_HANDLE Handle; + + if (GetFirstGuidHob (&gEdkiiCpuFeaturesInitDoneGuid) != NULL) { + // + // Try to find HOB first. This HOB exist means CPU features have + // been initialized by CpuFeaturesPei driver, just install + // gEdkiiCpuFeaturesInitDoneGuid. + // + Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &Handle, + &gEdkiiCpuFeaturesInitDoneGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); + return Status; + } + + if (PcdGetBool (PcdCpuFeaturesInitAfterSmmRelocation)) { + // + // Install notification callback on SMM Configuration Protocol + // + EfiCreateProtocolNotifyEvent ( + &gEfiSmmConfigurationProtocolGuid, + TPL_CALLBACK, + SmmConfigurationEventNotify, + NULL, + &Registration + ); + } else { + CpuFeaturesInitializeWorker (); + } + + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesDxe.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesDxe.inf new file mode 100644 index 00000000..b80ee549 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesDxe.inf @@ -0,0 +1,48 @@ +## @file +# CPU Features DXE driver. +# +# Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuFeaturesDxe + MODULE_UNI_FILE = CpuFeaturesDxe.uni + FILE_GUID = 63EB1B62-10C9-4693-88AC-AE0999EA87F8 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = CpuFeaturesDxeInitialize + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + UefiLib + UefiDriverEntryPoint + UefiBootServicesTableLib + RegisterCpuFeaturesLib + HobLib + +[Sources] + CpuFeaturesDxe.c + +[Guids] + gEdkiiCpuFeaturesInitDoneGuid ## PRODUCES ## UNDEFINED # protocol GUID installed + +[Protocols] + gEfiSmmConfigurationProtocolGuid ## NOTIFY + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesInitAfterSmmRelocation ## CONSUMES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + CpuFeaturesDxeExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesDxe.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesDxe.uni new file mode 100644 index 00000000..e970eb30 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesDxe.uni @@ -0,0 +1,16 @@ +// /** @file
+// CPU Features DXE driver.
+//
+// CPU Features DXE driver.
+//
+// Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU Features DXE driver to initialize CPU features."
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU Features DXE driver to initialize CPU features."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesDxeExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesDxeExtra.uni new file mode 100644 index 00000000..82cb70c9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// CpuFeaturesDxe Localized Strings and Content
+//
+// Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"CPU Features DXE Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesPei.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesPei.c new file mode 100644 index 00000000..f631797a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesPei.c @@ -0,0 +1,75 @@ +/** @file + CPU Features PEIM driver to initialize CPU features. + + Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiPei.h> + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/PeiServicesLib.h> +#include <Library/RegisterCpuFeaturesLib.h> +#include <Library/HobLib.h> + +#include <Guid/CpuFeaturesInitDone.h> + +EFI_PEI_PPI_DESCRIPTOR mPeiCpuFeaturesInitDonePpiDesc = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEdkiiCpuFeaturesInitDoneGuid, + NULL +}; + +/** + CPU Features driver entry point function. + + It will perform CPU features initialization, except for + PcdCpuFeaturesInitOnS3Resume is FALSE on S3 resume. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS CPU Features is initialized successfully. +**/ +EFI_STATUS +EFIAPI +CpuFeaturesPeimInitialize ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + EFI_BOOT_MODE BootMode; + + Status = PeiServicesGetBootMode (&BootMode); + ASSERT_EFI_ERROR (Status); + + if (BootMode == BOOT_ON_S3_RESUME && + !PcdGetBool (PcdCpuFeaturesInitOnS3Resume)) { + // + // Does nothing when if PcdCpuFeaturesInitOnS3Resume is FLASE + // on S3 boot mode + // + return EFI_SUCCESS; + } + + CpuFeaturesDetect (); + + CpuFeaturesInitialize (); + + // + // Install CPU Features Init Done PPI + // + Status = PeiServicesInstallPpi(&mPeiCpuFeaturesInitDonePpiDesc); + ASSERT_EFI_ERROR (Status); + + // + // Build HOB to let CpuFeatureDxe driver skip the initialization process. + // + BuildGuidHob (&gEdkiiCpuFeaturesInitDoneGuid, 0); + + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesPei.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesPei.inf new file mode 100644 index 00000000..9fbf6109 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesPei.inf @@ -0,0 +1,44 @@ +## @file +# CPU Features PEIM driver. +# +# Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuFeaturesPei + MODULE_UNI_FILE = CpuFeaturesPei.uni + FILE_GUID = 183BB3E1-A1E5-4445-8AC9-0E83B6547E0E + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = CpuFeaturesPeimInitialize + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + PeimEntryPoint + PeiServicesLib + RegisterCpuFeaturesLib + HobLib + +[Sources] + CpuFeaturesPei.c + +[Guids] + gEdkiiCpuFeaturesInitDoneGuid ## PRODUCES ## UNDEFINED # PPI GUID installed + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesInitOnS3Resume ## CONSUMES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + CpuFeaturesPeiExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesPei.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesPei.uni new file mode 100644 index 00000000..d57fa8fc --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesPei.uni @@ -0,0 +1,16 @@ +// /** @file
+// CPU Features PEIM driver.
+//
+// CPU Features PEIM driver.
+//
+// Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU Features PEIM driver to initialize CPU features."
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU Features PEIM driver to initialize CPU features."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesPeiExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesPeiExtra.uni new file mode 100644 index 00000000..dcc8fc71 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuFeatures/CpuFeaturesPeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// CpuFeaturesPei Localized Strings and Content
+//
+// Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"CPU Features PEIM Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.c new file mode 100644 index 00000000..e6ddc24f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.c @@ -0,0 +1,582 @@ +/** @file + Produces the CPU I/O 2 Protocol. + +Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuIo2Dxe.h" + +// +// Handle for the CPU I/O 2 Protocol +// +EFI_HANDLE mHandle = NULL; + +// +// CPU I/O 2 Protocol instance +// +EFI_CPU_IO2_PROTOCOL mCpuIo2 = { + { + CpuMemoryServiceRead, + CpuMemoryServiceWrite + }, + { + CpuIoServiceRead, + CpuIoServiceWrite + } +}; + +// +// Lookup table for increment values based on transfer widths +// +UINT8 mInStride[] = { + 1, // EfiCpuIoWidthUint8 + 2, // EfiCpuIoWidthUint16 + 4, // EfiCpuIoWidthUint32 + 8, // EfiCpuIoWidthUint64 + 0, // EfiCpuIoWidthFifoUint8 + 0, // EfiCpuIoWidthFifoUint16 + 0, // EfiCpuIoWidthFifoUint32 + 0, // EfiCpuIoWidthFifoUint64 + 1, // EfiCpuIoWidthFillUint8 + 2, // EfiCpuIoWidthFillUint16 + 4, // EfiCpuIoWidthFillUint32 + 8 // EfiCpuIoWidthFillUint64 +}; + +// +// Lookup table for increment values based on transfer widths +// +UINT8 mOutStride[] = { + 1, // EfiCpuIoWidthUint8 + 2, // EfiCpuIoWidthUint16 + 4, // EfiCpuIoWidthUint32 + 8, // EfiCpuIoWidthUint64 + 1, // EfiCpuIoWidthFifoUint8 + 2, // EfiCpuIoWidthFifoUint16 + 4, // EfiCpuIoWidthFifoUint32 + 8, // EfiCpuIoWidthFifoUint64 + 0, // EfiCpuIoWidthFillUint8 + 0, // EfiCpuIoWidthFillUint16 + 0, // EfiCpuIoWidthFillUint32 + 0 // EfiCpuIoWidthFillUint64 +}; + +/** + Check parameters to a CPU I/O 2 Protocol service request. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + @param[in] MmioOperation TRUE for an MMIO operation, FALSE for I/O Port operation. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[in] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The parameters for this request pass the checks. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +EFI_STATUS +CpuIoCheckParameter ( + IN BOOLEAN MmioOperation, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + UINT64 MaxCount; + UINT64 Limit; + + // + // Check to see if Buffer is NULL + // + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check to see if Width is in the valid range + // + if ((UINT32)Width >= EfiCpuIoWidthMaximum) { + return EFI_INVALID_PARAMETER; + } + + // + // For FIFO type, the target address won't increase during the access, + // so treat Count as 1 + // + if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) { + Count = 1; + } + + // + // Check to see if Width is in the valid range for I/O Port operations + // + Width = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03); + if (!MmioOperation && (Width == EfiCpuIoWidthUint64)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check to see if Address is aligned + // + if ((Address & ((UINT64)mInStride[Width] - 1)) != 0) { + return EFI_UNSUPPORTED; + } + + // + // Check to see if any address associated with this transfer exceeds the maximum + // allowed address. The maximum address implied by the parameters passed in is + // Address + Size * Count. If the following condition is met, then the transfer + // is not supported. + // + // Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1 + // + // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count + // can also be the maximum integer value supported by the CPU, this range + // check must be adjusted to avoid all oveflow conditions. + // + // The following form of the range check is equivalent but assumes that + // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1). + // + Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS); + if (Count == 0) { + if (Address > Limit) { + return EFI_UNSUPPORTED; + } + } else { + MaxCount = RShiftU64 (Limit, Width); + if (MaxCount < (Count - 1)) { + return EFI_UNSUPPORTED; + } + if (Address > LShiftU64 (MaxCount - Count + 1, Width)) { + return EFI_UNSUPPORTED; + } + } + + // + // Check to see if Buffer is aligned + // (IA-32 allows UINT64 and INT64 data types to be 32-bit aligned.) + // + if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width]) - 1))) != 0) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Reads memory-mapped registers. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[out] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +EFI_STATUS +EFIAPI +CpuMemoryServiceRead ( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 InStride; + UINT8 OutStride; + EFI_CPU_IO_PROTOCOL_WIDTH OperationWidth; + UINT8 *Uint8Buffer; + + Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + InStride = mInStride[Width]; + OutStride = mOutStride[Width]; + OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03); + for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) { + if (OperationWidth == EfiCpuIoWidthUint8) { + *Uint8Buffer = MmioRead8 ((UINTN)Address); + } else if (OperationWidth == EfiCpuIoWidthUint16) { + *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address); + } else if (OperationWidth == EfiCpuIoWidthUint32) { + *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address); + } else if (OperationWidth == EfiCpuIoWidthUint64) { + *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address); + } + } + return EFI_SUCCESS; +} + +/** + Writes memory-mapped registers. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[in] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +EFI_STATUS +EFIAPI +CpuMemoryServiceWrite ( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 InStride; + UINT8 OutStride; + EFI_CPU_IO_PROTOCOL_WIDTH OperationWidth; + UINT8 *Uint8Buffer; + + Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + InStride = mInStride[Width]; + OutStride = mOutStride[Width]; + OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03); + for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) { + if (OperationWidth == EfiCpuIoWidthUint8) { + MmioWrite8 ((UINTN)Address, *Uint8Buffer); + } else if (OperationWidth == EfiCpuIoWidthUint16) { + MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer)); + } else if (OperationWidth == EfiCpuIoWidthUint32) { + MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer)); + } else if (OperationWidth == EfiCpuIoWidthUint64) { + MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer)); + } + } + return EFI_SUCCESS; +} + +/** + Reads I/O registers. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[out] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +EFI_STATUS +EFIAPI +CpuIoServiceRead ( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 InStride; + UINT8 OutStride; + EFI_CPU_IO_PROTOCOL_WIDTH OperationWidth; + UINT8 *Uint8Buffer; + + Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + InStride = mInStride[Width]; + OutStride = mOutStride[Width]; + OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03); + + // + // Fifo operations supported for (mInStride[Width] == 0) + // + if (InStride == 0) { + switch (OperationWidth) { + case EfiCpuIoWidthUint8: + IoReadFifo8 ((UINTN)Address, Count, Buffer); + return EFI_SUCCESS; + case EfiCpuIoWidthUint16: + IoReadFifo16 ((UINTN)Address, Count, Buffer); + return EFI_SUCCESS; + case EfiCpuIoWidthUint32: + IoReadFifo32 ((UINTN)Address, Count, Buffer); + return EFI_SUCCESS; + default: + // + // The CpuIoCheckParameter call above will ensure that this + // path is not taken. + // + ASSERT (FALSE); + break; + } + } + + for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) { + if (OperationWidth == EfiCpuIoWidthUint8) { + *Uint8Buffer = IoRead8 ((UINTN)Address); + } else if (OperationWidth == EfiCpuIoWidthUint16) { + *((UINT16 *)Uint8Buffer) = IoRead16 ((UINTN)Address); + } else if (OperationWidth == EfiCpuIoWidthUint32) { + *((UINT32 *)Uint8Buffer) = IoRead32 ((UINTN)Address); + } + } + + return EFI_SUCCESS; +} + +/** + Write I/O registers. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[in] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +EFI_STATUS +EFIAPI +CpuIoServiceWrite ( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 InStride; + UINT8 OutStride; + EFI_CPU_IO_PROTOCOL_WIDTH OperationWidth; + UINT8 *Uint8Buffer; + + // + // Make sure the parameters are valid + // + Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + InStride = mInStride[Width]; + OutStride = mOutStride[Width]; + OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03); + + // + // Fifo operations supported for (mInStride[Width] == 0) + // + if (InStride == 0) { + switch (OperationWidth) { + case EfiCpuIoWidthUint8: + IoWriteFifo8 ((UINTN)Address, Count, Buffer); + return EFI_SUCCESS; + case EfiCpuIoWidthUint16: + IoWriteFifo16 ((UINTN)Address, Count, Buffer); + return EFI_SUCCESS; + case EfiCpuIoWidthUint32: + IoWriteFifo32 ((UINTN)Address, Count, Buffer); + return EFI_SUCCESS; + default: + // + // The CpuIoCheckParameter call above will ensure that this + // path is not taken. + // + ASSERT (FALSE); + break; + } + } + + for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) { + if (OperationWidth == EfiCpuIoWidthUint8) { + IoWrite8 ((UINTN)Address, *Uint8Buffer); + } else if (OperationWidth == EfiCpuIoWidthUint16) { + IoWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer)); + } else if (OperationWidth == EfiCpuIoWidthUint32) { + IoWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer)); + } + } + + return EFI_SUCCESS; +} + +/** + The user Entry Point for module CpuIo2Dxe. The user code starts with this function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +CpuIo2Initialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiCpuIo2ProtocolGuid); + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEfiCpuIo2ProtocolGuid, &mCpuIo2, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.h new file mode 100644 index 00000000..c905a37a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.h @@ -0,0 +1,219 @@ +/** @file + Internal include file for the CPU I/O 2 Protocol. + +Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_IO2_DXE_H_ +#define _CPU_IO2_DXE_H_ + +#include <PiDxe.h> + +#include <Protocol/CpuIo2.h> + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/IoLib.h> +#include <Library/UefiBootServicesTableLib.h> + +#define MAX_IO_PORT_ADDRESS 0xFFFF + +/** + Reads memory-mapped registers. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[out] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +EFI_STATUS +EFIAPI +CpuMemoryServiceRead ( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ); + +/** + Writes memory-mapped registers. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[in] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +EFI_STATUS +EFIAPI +CpuMemoryServiceWrite ( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ); + +/** + Reads I/O registers. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[out] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +EFI_STATUS +EFIAPI +CpuIoServiceRead ( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ); + +/** + Write I/O registers. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[in] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +EFI_STATUS +EFIAPI +CpuIoServiceWrite ( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf new file mode 100644 index 00000000..8b2f337f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf @@ -0,0 +1,47 @@ +## @file +# Produces the CPU I/O 2 Protocol by using the services of the I/O Library. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuIo2Dxe + MODULE_UNI_FILE = CpuIo2Dxe.uni + FILE_GUID = A19B1FE7-C1BC-49F8-875F-54A5D542443F + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = CpuIo2Initialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + CpuIo2Dxe.c + CpuIo2Dxe.h + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseLib + DebugLib + IoLib + UefiBootServicesTableLib + +[Protocols] + gEfiCpuIo2ProtocolGuid ## PRODUCES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + CpuIo2DxeExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.uni new file mode 100644 index 00000000..8d4e5dd6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.uni @@ -0,0 +1,16 @@ +// /** @file
+// Produces the CPU I/O 2 Protocol by using the services of the I/O Library.
+//
+// Produces the CPU I/O 2 Protocol by using the services of the I/O Library.
+//
+// Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces the CPU I/O 2 Protocol by using the services of the I/O Library"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Produces the CPU I/O 2 Protocol by using the services of the I/O Library."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2DxeExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2DxeExtra.uni new file mode 100644 index 00000000..5bd426df --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Dxe/CpuIo2DxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// CpuIo2Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"CPU I/O v2 DXE Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Mm.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Mm.c new file mode 100644 index 00000000..4ad96b19 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Mm.c @@ -0,0 +1,402 @@ +/** @file + Produces the SMM CPU I/O Protocol. + +Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuIo2Mm.h" + +// +// Handle for the SMM CPU I/O Protocol +// +EFI_HANDLE mHandle = NULL; + +// +// SMM CPU I/O Protocol instance +// +EFI_SMM_CPU_IO2_PROTOCOL mSmmCpuIo2 = { + { + CpuMemoryServiceRead, + CpuMemoryServiceWrite + }, + { + CpuIoServiceRead, + CpuIoServiceWrite + } +}; + +// +// Lookup table for increment values based on transfer widths +// +UINT8 mStride[] = { + 1, // SMM_IO_UINT8 + 2, // SMM_IO_UINT16 + 4, // SMM_IO_UINT32 + 8 // SMM_IO_UINT64 +}; + +/** + Check parameters to a SMM CPU I/O Protocol service request. + + @param[in] MmioOperation TRUE for an MMIO operation, FALSE for I/O Port operation. + @param[in] Width Signifies the width of the I/O operations. + @param[in] Address The base address of the I/O operations. The caller is + responsible for aligning the Address if required. + @param[in] Count The number of I/O operations to perform. + @param[in] Buffer For read operations, the destination buffer to store + the results. For write operations, the source buffer + from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the device. + @retval EFI_UNSUPPORTED The Address is not valid for this system. + @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. + +**/ +EFI_STATUS +CpuIoCheckParameter ( + IN BOOLEAN MmioOperation, + IN EFI_SMM_IO_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + UINT64 MaxCount; + UINT64 Limit; + + // + // Check to see if Buffer is NULL + // + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check to see if Width is in the valid range + // + if ((UINT32)Width > SMM_IO_UINT64) { + return EFI_INVALID_PARAMETER; + } + + // + // Check to see if Width is in the valid range for I/O Port operations + // + if (!MmioOperation && (Width == SMM_IO_UINT64)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check to see if any address associated with this transfer exceeds the maximum + // allowed address. The maximum address implied by the parameters passed in is + // Address + Size * Count. If the following condition is met, then the transfer + // is not supported. + // + // Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1 + // + // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count + // can also be the maximum integer value supported by the CPU, this range + // check must be adjusted to avoid all overflow conditions. + // + // The following form of the range check is equivalent but assumes that + // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1). + // + Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS); + if (Count == 0) { + if (Address > Limit) { + return EFI_UNSUPPORTED; + } + } else { + MaxCount = RShiftU64 (Limit, Width); + if (MaxCount < (Count - 1)) { + return EFI_UNSUPPORTED; + } + if (Address > LShiftU64 (MaxCount - Count + 1, Width)) { + return EFI_UNSUPPORTED; + } + } + + // + // Check to see if Address is aligned + // + if ((Address & ((UINT64)mStride[Width] - 1)) != 0) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Reads memory-mapped registers. + + The I/O operations are carried out exactly as requested. The caller is + responsible for any alignment and I/O width issues that the bus, device, + platform, or type of I/O might require. + + @param[in] This The EFI_SMM_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O operations. + @param[in] Address The base address of the I/O operations. The caller is + responsible for aligning the Address if required. + @param[in] Count The number of I/O operations to perform. + @param[out] Buffer For read operations, the destination buffer to store + the results. For write operations, the source buffer + from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the device. + @retval EFI_UNSUPPORTED The Address is not valid for this system. + @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources + +**/ +EFI_STATUS +EFIAPI +CpuMemoryServiceRead ( + IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This, + IN EFI_SMM_IO_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 Stride; + UINT8 *Uint8Buffer; + + Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + Stride = mStride[Width]; + for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) { + if (Width == SMM_IO_UINT8) { + *Uint8Buffer = MmioRead8 ((UINTN)Address); + } else if (Width == SMM_IO_UINT16) { + *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address); + } else if (Width == SMM_IO_UINT32) { + *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address); + } else if (Width == SMM_IO_UINT64) { + *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address); + } + } + return EFI_SUCCESS; +} + +/** + Writes memory-mapped registers. + + The I/O operations are carried out exactly as requested. The caller is + responsible for any alignment and I/O width issues that the bus, device, + platform, or type of I/O might require. + + @param[in] This The EFI_SMM_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O operations. + @param[in] Address The base address of the I/O operations. The caller is + responsible for aligning the Address if required. + @param[in] Count The number of I/O operations to perform. + @param[in] Buffer For read operations, the destination buffer to store + the results. For write operations, the source buffer + from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the device. + @retval EFI_UNSUPPORTED The Address is not valid for this system. + @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources + +**/ +EFI_STATUS +EFIAPI +CpuMemoryServiceWrite ( + IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This, + IN EFI_SMM_IO_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 Stride; + UINT8 *Uint8Buffer; + + Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + Stride = mStride[Width]; + for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) { + if (Width == SMM_IO_UINT8) { + MmioWrite8 ((UINTN)Address, *Uint8Buffer); + } else if (Width == SMM_IO_UINT16) { + MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer)); + } else if (Width == SMM_IO_UINT32) { + MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer)); + } else if (Width == SMM_IO_UINT64) { + MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer)); + } + } + return EFI_SUCCESS; +} + +/** + Reads I/O registers. + + The I/O operations are carried out exactly as requested. The caller is + responsible for any alignment and I/O width issues that the bus, device, + platform, or type of I/O might require. + + @param[in] This The EFI_SMM_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O operations. + @param[in] Address The base address of the I/O operations. The caller is + responsible for aligning the Address if required. + @param[in] Count The number of I/O operations to perform. + @param[out] Buffer For read operations, the destination buffer to store + the results. For write operations, the source buffer + from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the device. + @retval EFI_UNSUPPORTED The Address is not valid for this system. + @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources + +**/ +EFI_STATUS +EFIAPI +CpuIoServiceRead ( + IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This, + IN EFI_SMM_IO_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 Stride; + UINT8 *Uint8Buffer; + + Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + Stride = mStride[Width]; + for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) { + if (Width == SMM_IO_UINT8) { + *Uint8Buffer = IoRead8 ((UINTN)Address); + } else if (Width == SMM_IO_UINT16) { + *((UINT16 *)Uint8Buffer) = IoRead16 ((UINTN)Address); + } else if (Width == SMM_IO_UINT32) { + *((UINT32 *)Uint8Buffer) = IoRead32 ((UINTN)Address); + } + } + + return EFI_SUCCESS; +} + +/** + Write I/O registers. + + The I/O operations are carried out exactly as requested. The caller is + responsible for any alignment and I/O width issues that the bus, device, + platform, or type of I/O might require. + + @param[in] This The EFI_SMM_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O operations. + @param[in] Address The base address of the I/O operations. The caller is + responsible for aligning the Address if required. + @param[in] Count The number of I/O operations to perform. + @param[in] Buffer For read operations, the destination buffer to store + the results. For write operations, the source buffer + from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the device. + @retval EFI_UNSUPPORTED The Address is not valid for this system. + @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources + +**/ +EFI_STATUS +EFIAPI +CpuIoServiceWrite ( + IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This, + IN EFI_SMM_IO_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 Stride; + UINT8 *Uint8Buffer; + + // + // Make sure the parameters are valid + // + Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + Stride = mStride[Width]; + for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) { + if (Width == SMM_IO_UINT8) { + IoWrite8 ((UINTN)Address, *Uint8Buffer); + } else if (Width == SMM_IO_UINT16) { + IoWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer)); + } else if (Width == SMM_IO_UINT32) { + IoWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer)); + } + } + + return EFI_SUCCESS; +} + +/** + The module Entry Point SmmCpuIoProtocol driver + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval Other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +CommonCpuIo2Initialize ( + VOID + ) +{ + EFI_STATUS Status; + + // + // Copy the SMM CPU I/O Protocol instance into the System Management System Table + // + CopyMem (&gMmst->MmIo, &mSmmCpuIo2, sizeof (mSmmCpuIo2)); + + // + // Install the SMM CPU I/O Protocol into the MM protocol database + // + Status = gMmst->MmInstallProtocolInterface ( + &mHandle, + &gEfiSmmCpuIo2ProtocolGuid, + EFI_NATIVE_INTERFACE, + &mSmmCpuIo2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Mm.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Mm.h new file mode 100644 index 00000000..3237b585 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Mm.h @@ -0,0 +1,168 @@ +/** @file + Internal include file for the SMM CPU I/O Protocol. + +Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_IO2_SMM_H_ +#define _CPU_IO2_SMM_H_ + +#include <PiSmm.h> + +#include <Protocol/SmmCpuIo2.h> + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/IoLib.h> +#include <Library/MmServicesTableLib.h> +#include <Library/BaseMemoryLib.h> + +#define MAX_IO_PORT_ADDRESS 0xFFFF + +/** + Reads memory-mapped registers. + + The I/O operations are carried out exactly as requested. The caller is + responsible for any alignment and I/O width issues that the bus, device, + platform, or type of I/O might require. + + @param[in] This The EFI_SMM_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O operations. + @param[in] Address The base address of the I/O operations. The caller is + responsible for aligning the Address if required. + @param[in] Count The number of I/O operations to perform. + @param[out] Buffer For read operations, the destination buffer to store + the results. For write operations, the source buffer + from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the device. + @retval EFI_UNSUPPORTED The Address is not valid for this system. + @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources + +**/ +EFI_STATUS +EFIAPI +CpuMemoryServiceRead ( + IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This, + IN EFI_SMM_IO_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ); + +/** + Writes memory-mapped registers. + + The I/O operations are carried out exactly as requested. The caller is + responsible for any alignment and I/O width issues that the bus, device, + platform, or type of I/O might require. + + @param[in] This The EFI_SMM_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O operations. + @param[in] Address The base address of the I/O operations. The caller is + responsible for aligning the Address if required. + @param[in] Count The number of I/O operations to perform. + @param[in] Buffer For read operations, the destination buffer to store + the results. For write operations, the source buffer + from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the device. + @retval EFI_UNSUPPORTED The Address is not valid for this system. + @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources + +**/ +EFI_STATUS +EFIAPI +CpuMemoryServiceWrite ( + IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This, + IN EFI_SMM_IO_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ); + +/** + Reads I/O registers. + + The I/O operations are carried out exactly as requested. The caller is + responsible for any alignment and I/O width issues that the bus, device, + platform, or type of I/O might require. + + @param[in] This The EFI_SMM_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O operations. + @param[in] Address The base address of the I/O operations. The caller is + responsible for aligning the Address if required. + @param[in] Count The number of I/O operations to perform. + @param[out] Buffer For read operations, the destination buffer to store + the results. For write operations, the source buffer + from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the device. + @retval EFI_UNSUPPORTED The Address is not valid for this system. + @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources + +**/ +EFI_STATUS +EFIAPI +CpuIoServiceRead ( + IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This, + IN EFI_SMM_IO_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ); + +/** + Write I/O registers. + + The I/O operations are carried out exactly as requested. The caller is + responsible for any alignment and I/O width issues that the bus, device, + platform, or type of I/O might require. + + @param[in] This The EFI_SMM_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O operations. + @param[in] Address The base address of the I/O operations. The caller is + responsible for aligning the Address if required. + @param[in] Count The number of I/O operations to perform. + @param[in] Buffer For read operations, the destination buffer to store + the results. For write operations, the source buffer + from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the device. + @retval EFI_UNSUPPORTED The Address is not valid for this system. + @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources + +**/ +EFI_STATUS +EFIAPI +CpuIoServiceWrite ( + IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This, + IN EFI_SMM_IO_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ); + +/** + The module Entry Point SmmCpuIoProtocol driver + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval Other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +CommonCpuIo2Initialize ( + VOID + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.c new file mode 100644 index 00000000..ee37f540 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.c @@ -0,0 +1,32 @@ +/** @file + Produces the SMM CPU I/O Protocol. + +Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiSmm.h> + +#include "CpuIo2Mm.h" + +/** + The module Entry Point for Traditional MM CpuIoProtocol driver + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval Other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +SmmCpuIo2Initialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return CommonCpuIo2Initialize (); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf new file mode 100644 index 00000000..8d15f3c6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf @@ -0,0 +1,48 @@ +## @file +# Produces the SMM CPU I/O 2 Protocol by using the services of the I/O Library. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuIo2Smm + MODULE_UNI_FILE = CpuIo2Smm.uni + FILE_GUID = A47EE2D8-F60E-42fd-8E58-7BD65EE4C29B + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = SmmCpuIo2Initialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + CpuIo2Smm.c + CpuIo2Mm.c + CpuIo2Mm.h + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseLib + DebugLib + IoLib + MmServicesTableLib + BaseMemoryLib + +[Protocols] + gEfiSmmCpuIo2ProtocolGuid ## PRODUCES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + CpuIo2SmmExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.uni new file mode 100644 index 00000000..6ab9960d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.uni @@ -0,0 +1,16 @@ +// /** @file
+// Produces the SMM CPU I/O 2 Protocol by using the services of the I/O Library.
+//
+// Produces the SMM CPU I/O 2 Protocol by using the services of the I/O Library.
+//
+// Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces the SMM CPU I/O 2 Protocol by using the services of the I/O Library"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Produces the SMM CPU I/O 2 Protocol by using the services of the I/O Library."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2SmmExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2SmmExtra.uni new file mode 100644 index 00000000..6a91f87f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2SmmExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// CpuIo2Smm Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"CPU I/O v2 SMM Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2StandaloneMm.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2StandaloneMm.c new file mode 100644 index 00000000..10eb6058 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2StandaloneMm.c @@ -0,0 +1,32 @@ +/** @file + Produces the SMM CPU I/O Protocol. + +Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiMm.h> + +#include "CpuIo2Mm.h" + +/** + The module Entry Point for Standalone MM CpuIoProtocol driver + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval Other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +StandaloneMmCpuIo2Initialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_MM_SYSTEM_TABLE *SystemTable + ) +{ + return CommonCpuIo2Initialize (); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2StandaloneMm.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2StandaloneMm.inf new file mode 100644 index 00000000..f7a5c806 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIo2Smm/CpuIo2StandaloneMm.inf @@ -0,0 +1,45 @@ +## @file +# Produces the SMM CPU I/O 2 Protocol by using the services of the I/O Library. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuIo2StandaloneMm + FILE_GUID = E3121A26-BB1C-4A18-8E23-2EA3F0412248 + MODULE_TYPE = MM_STANDALONE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x00010032 + ENTRY_POINT = StandaloneMmCpuIo2Initialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + CpuIo2StandaloneMm.c + CpuIo2Mm.c + CpuIo2Mm.h + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + StandaloneMmDriverEntryPoint + BaseLib + DebugLib + IoLib + MmServicesTableLib + BaseMemoryLib + +[Protocols] + gEfiSmmCpuIo2ProtocolGuid ## PRODUCES + +[Depex] + TRUE diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPei.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPei.c new file mode 100644 index 00000000..517ac7a8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPei.c @@ -0,0 +1,910 @@ +/** @file + Produces the CPU I/O PPI. + +Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuIoPei.h" + +// +// Instance of CPU I/O PPI +// +EFI_PEI_CPU_IO_PPI gCpuIoPpi = { + { + CpuMemoryServiceRead, + CpuMemoryServiceWrite + }, + { + CpuIoServiceRead, + CpuIoServiceWrite + }, + CpuIoRead8, + CpuIoRead16, + CpuIoRead32, + CpuIoRead64, + CpuIoWrite8, + CpuIoWrite16, + CpuIoWrite32, + CpuIoWrite64, + CpuMemRead8, + CpuMemRead16, + CpuMemRead32, + CpuMemRead64, + CpuMemWrite8, + CpuMemWrite16, + CpuMemWrite32, + CpuMemWrite64 +}; + +// +// PPI Descriptor used to install the CPU I/O PPI +// +EFI_PEI_PPI_DESCRIPTOR gPpiList = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiCpuIoPpiInstalledGuid, + NULL +}; + +// +// Lookup table for increment values based on transfer widths +// +UINT8 mInStride[] = { + 1, // EfiPeiCpuIoWidthUint8 + 2, // EfiPeiCpuIoWidthUint16 + 4, // EfiPeiCpuIoWidthUint32 + 8, // EfiPeiCpuIoWidthUint64 + 0, // EfiPeiCpuIoWidthFifoUint8 + 0, // EfiPeiCpuIoWidthFifoUint16 + 0, // EfiPeiCpuIoWidthFifoUint32 + 0, // EfiPeiCpuIoWidthFifoUint64 + 1, // EfiPeiCpuIoWidthFillUint8 + 2, // EfiPeiCpuIoWidthFillUint16 + 4, // EfiPeiCpuIoWidthFillUint32 + 8 // EfiPeiCpuIoWidthFillUint64 +}; + +// +// Lookup table for increment values based on transfer widths +// +UINT8 mOutStride[] = { + 1, // EfiPeiCpuIoWidthUint8 + 2, // EfiPeiCpuIoWidthUint16 + 4, // EfiPeiCpuIoWidthUint32 + 8, // EfiPeiCpuIoWidthUint64 + 1, // EfiPeiCpuIoWidthFifoUint8 + 2, // EfiPeiCpuIoWidthFifoUint16 + 4, // EfiPeiCpuIoWidthFifoUint32 + 8, // EfiPeiCpuIoWidthFifoUint64 + 0, // EfiPeiCpuIoWidthFillUint8 + 0, // EfiPeiCpuIoWidthFillUint16 + 0, // EfiPeiCpuIoWidthFillUint32 + 0 // EfiPeiCpuIoWidthFillUint64 +}; + +/** + Check parameters to a CPU I/O PPI service request. + + @param[in] MmioOperation TRUE for an MMIO operation, FALSE for I/O Port operation. + @param[in] Width The width of the access. Enumerated in bytes. + @param[in] Address The physical address of the access. + @param[in] Count The number of accesses to perform. + @param[in] Buffer A pointer to the buffer of data. + + @retval EFI_SUCCESS The parameters for this request pass the checks. + @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this EFI system. + +**/ +EFI_STATUS +CpuIoCheckParameter ( + IN BOOLEAN MmioOperation, + IN EFI_PEI_CPU_IO_PPI_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + UINT64 MaxCount; + UINT64 Limit; + + // + // Check to see if Buffer is NULL + // + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check to see if Width is in the valid range + // + if ((UINT32)Width >= EfiPeiCpuIoWidthMaximum) { + return EFI_INVALID_PARAMETER; + } + + // + // For FIFO type, the target address won't increase during the access, + // so treat Count as 1 + // + if (Width >= EfiPeiCpuIoWidthFifoUint8 && Width <= EfiPeiCpuIoWidthFifoUint64) { + Count = 1; + } + + // + // Check to see if Width is in the valid range for I/O Port operations + // + Width = (EFI_PEI_CPU_IO_PPI_WIDTH) (Width & 0x03); + if (!MmioOperation && (Width == EfiPeiCpuIoWidthUint64)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check to see if any address associated with this transfer exceeds the maximum + // allowed address. The maximum address implied by the parameters passed in is + // Address + Size * Count. If the following condition is met, then the transfer + // is not supported. + // + // Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1 + // + // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count + // can also be the maximum integer value supported by the CPU, this range + // check must be adjusted to avoid all overflow conditions. + // + // The following form of the range check is equivalent but assumes that + // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1). + // + Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS); + if (Count == 0) { + if (Address > Limit) { + return EFI_UNSUPPORTED; + } + } else { + MaxCount = RShiftU64 (Limit, Width); + if (MaxCount < (Count - 1)) { + return EFI_UNSUPPORTED; + } + if (Address > LShiftU64 (MaxCount - Count + 1, Width)) { + return EFI_UNSUPPORTED; + } + } + + return EFI_SUCCESS; +} + +/** + Reads memory-mapped registers. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Width The width of the access. Enumerated in bytes. + @param[in] Address The physical address of the access. + @param[in] Count The number of accesses to perform. + @param[out] Buffer A pointer to the buffer of data. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this EFI system. + +**/ +EFI_STATUS +EFIAPI +CpuMemoryServiceRead ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN EFI_PEI_CPU_IO_PPI_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 InStride; + UINT8 OutStride; + EFI_PEI_CPU_IO_PPI_WIDTH OperationWidth; + BOOLEAN Aligned; + UINT8 *Uint8Buffer; + + Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + InStride = mInStride[Width]; + OutStride = mOutStride[Width]; + OperationWidth = (EFI_PEI_CPU_IO_PPI_WIDTH) (Width & 0x03); + Aligned = (BOOLEAN)(((UINTN)Buffer & (mInStride[OperationWidth] - 1)) == 0x00); + for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) { + if (OperationWidth == EfiPeiCpuIoWidthUint8) { + *Uint8Buffer = MmioRead8 ((UINTN)Address); + } else if (OperationWidth == EfiPeiCpuIoWidthUint16) { + if (Aligned) { + *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address); + } else { + WriteUnaligned16 ((UINT16 *)Uint8Buffer, MmioRead16 ((UINTN)Address)); + } + } else if (OperationWidth == EfiPeiCpuIoWidthUint32) { + if (Aligned) { + *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address); + } else { + WriteUnaligned32 ((UINT32 *)Uint8Buffer, MmioRead32 ((UINTN)Address)); + } + } else if (OperationWidth == EfiPeiCpuIoWidthUint64) { + if (Aligned) { + *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address); + } else { + WriteUnaligned64 ((UINT64 *)Uint8Buffer, MmioRead64 ((UINTN)Address)); + } + } + } + return EFI_SUCCESS; +} + +/** + Writes memory-mapped registers. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Width The width of the access. Enumerated in bytes. + @param[in] Address The physical address of the access. + @param[in] Count The number of accesses to perform. + @param[in] Buffer A pointer to the buffer of data. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this EFI system. + +**/ +EFI_STATUS +EFIAPI +CpuMemoryServiceWrite ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN EFI_PEI_CPU_IO_PPI_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 InStride; + UINT8 OutStride; + EFI_PEI_CPU_IO_PPI_WIDTH OperationWidth; + BOOLEAN Aligned; + UINT8 *Uint8Buffer; + + Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + InStride = mInStride[Width]; + OutStride = mOutStride[Width]; + OperationWidth = (EFI_PEI_CPU_IO_PPI_WIDTH) (Width & 0x03); + Aligned = (BOOLEAN)(((UINTN)Buffer & (mInStride[OperationWidth] - 1)) == 0x00); + for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) { + if (OperationWidth == EfiPeiCpuIoWidthUint8) { + MmioWrite8 ((UINTN)Address, *Uint8Buffer); + } else if (OperationWidth == EfiPeiCpuIoWidthUint16) { + if (Aligned) { + MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer)); + } else { + MmioWrite16 ((UINTN)Address, ReadUnaligned16 ((UINT16 *)Uint8Buffer)); + } + } else if (OperationWidth == EfiPeiCpuIoWidthUint32) { + if (Aligned) { + MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer)); + } else { + MmioWrite32 ((UINTN)Address, ReadUnaligned32 ((UINT32 *)Uint8Buffer)); + } + } else if (OperationWidth == EfiPeiCpuIoWidthUint64) { + if (Aligned) { + MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer)); + } else { + MmioWrite64 ((UINTN)Address, ReadUnaligned64 ((UINT64 *)Uint8Buffer)); + } + } + } + return EFI_SUCCESS; +} + +/** + Reads I/O registers. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Width The width of the access. Enumerated in bytes. + @param[in] Address The physical address of the access. + @param[in] Count The number of accesses to perform. + @param[out] Buffer A pointer to the buffer of data. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this EFI system. + +**/ +EFI_STATUS +EFIAPI +CpuIoServiceRead ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN EFI_PEI_CPU_IO_PPI_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 InStride; + UINT8 OutStride; + EFI_PEI_CPU_IO_PPI_WIDTH OperationWidth; + BOOLEAN Aligned; + UINT8 *Uint8Buffer; + + Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + InStride = mInStride[Width]; + OutStride = mOutStride[Width]; + OperationWidth = (EFI_PEI_CPU_IO_PPI_WIDTH) (Width & 0x03); + + // + // Fifo operations supported for (mInStride[Width] == 0) + // + if (InStride == 0) { + switch (OperationWidth) { + case EfiPeiCpuIoWidthUint8: + IoReadFifo8 ((UINTN)Address, Count, Buffer); + return EFI_SUCCESS; + case EfiPeiCpuIoWidthUint16: + IoReadFifo16 ((UINTN)Address, Count, Buffer); + return EFI_SUCCESS; + case EfiPeiCpuIoWidthUint32: + IoReadFifo32 ((UINTN)Address, Count, Buffer); + return EFI_SUCCESS; + default: + // + // The CpuIoCheckParameter call above will ensure that this + // path is not taken. + // + ASSERT (FALSE); + break; + } + } + + Aligned = (BOOLEAN)(((UINTN)Buffer & (mInStride[OperationWidth] - 1)) == 0x00); + for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) { + if (OperationWidth == EfiPeiCpuIoWidthUint8) { + *Uint8Buffer = IoRead8 ((UINTN)Address); + } else if (OperationWidth == EfiPeiCpuIoWidthUint16) { + if (Aligned) { + *((UINT16 *)Uint8Buffer) = IoRead16 ((UINTN)Address); + } else { + WriteUnaligned16 ((UINT16 *)Uint8Buffer, IoRead16 ((UINTN)Address)); + } + } else if (OperationWidth == EfiPeiCpuIoWidthUint32) { + if (Aligned) { + *((UINT32 *)Uint8Buffer) = IoRead32 ((UINTN)Address); + } else { + WriteUnaligned32 ((UINT32 *)Uint8Buffer, IoRead32 ((UINTN)Address)); + } + } + } + + return EFI_SUCCESS; +} + +/** + Write I/O registers. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Width The width of the access. Enumerated in bytes. + @param[in] Address The physical address of the access. + @param[in] Count The number of accesses to perform. + @param[in] Buffer A pointer to the buffer of data. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this EFI system. + +**/ +EFI_STATUS +EFIAPI +CpuIoServiceWrite ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN EFI_PEI_CPU_IO_PPI_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 InStride; + UINT8 OutStride; + EFI_PEI_CPU_IO_PPI_WIDTH OperationWidth; + BOOLEAN Aligned; + UINT8 *Uint8Buffer; + + // + // Make sure the parameters are valid + // + Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + InStride = mInStride[Width]; + OutStride = mOutStride[Width]; + OperationWidth = (EFI_PEI_CPU_IO_PPI_WIDTH) (Width & 0x03); + + // + // Fifo operations supported for (mInStride[Width] == 0) + // + if (InStride == 0) { + switch (OperationWidth) { + case EfiPeiCpuIoWidthUint8: + IoWriteFifo8 ((UINTN)Address, Count, Buffer); + return EFI_SUCCESS; + case EfiPeiCpuIoWidthUint16: + IoWriteFifo16 ((UINTN)Address, Count, Buffer); + return EFI_SUCCESS; + case EfiPeiCpuIoWidthUint32: + IoWriteFifo32 ((UINTN)Address, Count, Buffer); + return EFI_SUCCESS; + default: + // + // The CpuIoCheckParameter call above will ensure that this + // path is not taken. + // + ASSERT (FALSE); + break; + } + } + + Aligned = (BOOLEAN)(((UINTN)Buffer & (mInStride[OperationWidth] - 1)) == 0x00); + for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) { + if (OperationWidth == EfiPeiCpuIoWidthUint8) { + IoWrite8 ((UINTN)Address, *Uint8Buffer); + } else if (OperationWidth == EfiPeiCpuIoWidthUint16) { + if (Aligned) { + IoWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer)); + } else { + IoWrite16 ((UINTN)Address, ReadUnaligned16 ((UINT16 *)Uint8Buffer)); + } + } else if (OperationWidth == EfiPeiCpuIoWidthUint32) { + if (Aligned) { + IoWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer)); + } else { + IoWrite32 ((UINTN)Address, ReadUnaligned32 ((UINT32 *)Uint8Buffer)); + } + } + } + + return EFI_SUCCESS; +} + +/** + 8-bit I/O read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return An 8-bit value returned from the I/O space. +**/ +UINT8 +EFIAPI +CpuIoRead8 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ) +{ + return IoRead8 ((UINTN)Address); +} + +/** + 16-bit I/O read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return A 16-bit value returned from the I/O space. + +**/ +UINT16 +EFIAPI +CpuIoRead16 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ) +{ + return IoRead16 ((UINTN)Address); +} + +/** + 32-bit I/O read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return A 32-bit value returned from the I/O space. + +**/ +UINT32 +EFIAPI +CpuIoRead32 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ) +{ + return IoRead32 ((UINTN)Address); +} + +/** + 64-bit I/O read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return A 64-bit value returned from the I/O space. + +**/ +UINT64 +EFIAPI +CpuIoRead64 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ) +{ + return IoRead64 ((UINTN)Address); +} + +/** + 8-bit I/O write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuIoWrite8 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT8 Data + ) +{ + IoWrite8 ((UINTN)Address, Data); +} + +/** + 16-bit I/O write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuIoWrite16 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT16 Data + ) +{ + IoWrite16 ((UINTN)Address, Data); +} + +/** + 32-bit I/O write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuIoWrite32 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT32 Data + ) +{ + IoWrite32 ((UINTN)Address, Data); +} + +/** + 64-bit I/O write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuIoWrite64 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT64 Data + ) +{ + IoWrite64 ((UINTN)Address, Data); +} + +/** + 8-bit memory read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return An 8-bit value returned from the memory space. + +**/ +UINT8 +EFIAPI +CpuMemRead8 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ) +{ + return MmioRead8 ((UINTN)Address); +} + +/** + 16-bit memory read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return A 16-bit value returned from the memory space. + +**/ +UINT16 +EFIAPI +CpuMemRead16 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ) +{ + return MmioRead16 ((UINTN)Address); +} + +/** + 32-bit memory read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return A 32-bit value returned from the memory space. + +**/ +UINT32 +EFIAPI +CpuMemRead32 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ) +{ + return MmioRead32 ((UINTN)Address); +} + +/** + 64-bit memory read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return A 64-bit value returned from the memory space. + +**/ +UINT64 +EFIAPI +CpuMemRead64 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ) +{ + return MmioRead64 ((UINTN)Address); +} + +/** + 8-bit memory write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuMemWrite8 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT8 Data + ) +{ + MmioWrite8 ((UINTN)Address, Data); +} + +/** + 16-bit memory write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuMemWrite16 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT16 Data + ) +{ + MmioWrite16 ((UINTN)Address, Data); +} + +/** + 32-bit memory write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuMemWrite32 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT32 Data + ) +{ + MmioWrite32 ((UINTN)Address, Data); +} + +/** + 64-bit memory write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuMemWrite64 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT64 Data + ) +{ + MmioWrite64 ((UINTN)Address, Data); +} + +/** + The Entry point of the CPU I/O PEIM + + This function is the Entry point of the CPU I/O PEIM which installs CpuIoPpi. + + @param[in] FileHandle Pointer to image file handle. + @param[in] PeiServices Pointer to PEI Services Table + + @retval EFI_SUCCESS CPU I/O PPI successfully installed + +**/ +EFI_STATUS +EFIAPI +CpuIoInitialize ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + + // + // Register so it will be automatically shadowed to memory + // + Status = PeiServicesRegisterForShadow (FileHandle); + + // + // Make CpuIo pointer in PeiService table point to gCpuIoPpi + // + (*((EFI_PEI_SERVICES **)PeiServices))->CpuIo = &gCpuIoPpi; + + if (Status == EFI_ALREADY_STARTED) { + // + // Shadow completed and running from memory + // + DEBUG ((EFI_D_INFO, "CpuIO PPI has been loaded into memory. Reinstalled PPI=0x%x\n", &gCpuIoPpi)); + } else { + Status = PeiServicesInstallPpi (&gPpiList); + ASSERT_EFI_ERROR (Status); + } + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPei.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPei.h new file mode 100644 index 00000000..2b19cc7c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPei.h @@ -0,0 +1,442 @@ +/** @file + Internal include file for the CPU I/O PPI. + +Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_IO2_PEI_H_ +#define _CPU_IO2_PEI_H_ + +#include <PiDxe.h> + +#include <Ppi/CpuIo.h> + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/IoLib.h> +#include <Library/PeiServicesLib.h> + +#define MAX_IO_PORT_ADDRESS 0xFFFF + +/** + Reads memory-mapped registers. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Width The width of the access. Enumerated in bytes. + @param[in] Address The physical address of the access. + @param[in] Count The number of accesses to perform. + @param[out] Buffer A pointer to the buffer of data. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this EFI system. + +**/ +EFI_STATUS +EFIAPI +CpuMemoryServiceRead ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN EFI_PEI_CPU_IO_PPI_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ); + +/** + Writes memory-mapped registers. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Width The width of the access. Enumerated in bytes. + @param[in] Address The physical address of the access. + @param[in] Count The number of accesses to perform. + @param[in] Buffer A pointer to the buffer of data. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this EFI system. + +**/ +EFI_STATUS +EFIAPI +CpuMemoryServiceWrite ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN EFI_PEI_CPU_IO_PPI_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ); + +/** + Reads I/O registers. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Width The width of the access. Enumerated in bytes. + @param[in] Address The physical address of the access. + @param[in] Count The number of accesses to perform. + @param[out] Buffer A pointer to the buffer of data. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this EFI system. + +**/ +EFI_STATUS +EFIAPI +CpuIoServiceRead ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN EFI_PEI_CPU_IO_PPI_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ); + +/** + Write I/O registers. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Width The width of the access. Enumerated in bytes. + @param[in] Address The physical address of the access. + @param[in] Count The number of accesses to perform. + @param[in] Buffer A pointer to the buffer of data. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this EFI system. + +**/ +EFI_STATUS +EFIAPI +CpuIoServiceWrite ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN EFI_PEI_CPU_IO_PPI_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ); + +/** + 8-bit I/O read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return An 8-bit value returned from the I/O space. +**/ +UINT8 +EFIAPI +CpuIoRead8 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ); + +/** + 16-bit I/O read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return A 16-bit value returned from the I/O space. + +**/ +UINT16 +EFIAPI +CpuIoRead16 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ); + +/** + 32-bit I/O read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return A 32-bit value returned from the I/O space. + +**/ +UINT32 +EFIAPI +CpuIoRead32 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ); + +/** + 64-bit I/O read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return A 64-bit value returned from the I/O space. + +**/ +UINT64 +EFIAPI +CpuIoRead64 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ); + +/** + 8-bit I/O write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuIoWrite8 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT8 Data + ); + +/** + 16-bit I/O write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuIoWrite16 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT16 Data + ); + +/** + 32-bit I/O write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuIoWrite32 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT32 Data + ); + +/** + 64-bit I/O write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuIoWrite64 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT64 Data + ); + +/** + 8-bit memory read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return An 8-bit value returned from the memory space. + +**/ +UINT8 +EFIAPI +CpuMemRead8 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ); + +/** + 16-bit memory read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return A 16-bit value returned from the memory space. + +**/ +UINT16 +EFIAPI +CpuMemRead16 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ); + +/** + 32-bit memory read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return A 32-bit value returned from the memory space. + +**/ +UINT32 +EFIAPI +CpuMemRead32 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ); + +/** + 64-bit memory read operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + + @return A 64-bit value returned from the memory space. + +**/ +UINT64 +EFIAPI +CpuMemRead64 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address + ); + +/** + 8-bit memory write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuMemWrite8 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT8 Data + ); + +/** + 16-bit memory write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuMemWrite16 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT16 Data + ); + +/** + 32-bit memory write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuMemWrite32 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT32 Data + ); + +/** + 64-bit memory write operations. + + @param[in] PeiServices An indirect pointer to the PEI Services Table published + by the PEI Foundation. + @param[in] This Pointer to local data for the interface. + @param[in] Address The physical address of the access. + @param[in] Data The data to write. + +**/ +VOID +EFIAPI +CpuMemWrite64 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_PEI_CPU_IO_PPI *This, + IN UINT64 Address, + IN UINT64 Data + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPei.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPei.inf new file mode 100644 index 00000000..01797e74 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPei.inf @@ -0,0 +1,45 @@ +## @file +# Produces the CPU I/O PPI by using the services of the I/O Library. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuIoPei + MODULE_UNI_FILE = CpuIoPei.uni + FILE_GUID = AE265864-CF5D-41a8-913D-71C155E76442 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = CpuIoInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + CpuIoPei.c + CpuIoPei.h + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + PeimEntryPoint + BaseLib + DebugLib + IoLib + PeiServicesLib + +[Ppis] + gEfiPeiCpuIoPpiInstalledGuid ## PRODUCES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + CpuIoPeiExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPei.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPei.uni new file mode 100644 index 00000000..40f444c0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPei.uni @@ -0,0 +1,16 @@ +// /** @file
+// Produces the CPU I/O PPI by using the services of the I/O Library.
+//
+// Produces the CPU I/O PPI by using the services of the I/O Library.
+//
+// Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces the CPU I/O PPI by using the services of the I/O Library"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Produces the CPU I/O PPI by using the services of the I/O Library."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPeiExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPeiExtra.uni new file mode 100644 index 00000000..efabb477 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuIoPei/CpuIoPeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// CpuIoPei Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"CPU I/O PEI Module"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuBist.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuBist.c new file mode 100644 index 00000000..3c0a0148 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuBist.c @@ -0,0 +1,291 @@ +/** @file + Update and publish processors' BIST information. + + Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuMpPei.h" + +EFI_SEC_PLATFORM_INFORMATION2_PPI mSecPlatformInformation2Ppi = { + SecPlatformInformation2 +}; + +EFI_PEI_PPI_DESCRIPTOR mPeiSecPlatformInformation2Ppi = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiSecPlatformInformation2PpiGuid, + &mSecPlatformInformation2Ppi +}; + +/** + Implementation of the PlatformInformation2 service in EFI_SEC_PLATFORM_INFORMATION2_PPI. + + @param PeiServices The pointer to the PEI Services Table. + @param StructureSize The pointer to the variable describing size of the input buffer. + @param PlatformInformationRecord2 The pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD2. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. The current buffer size needed to + hold the record is returned in StructureSize. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformation2 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD2 *PlatformInformationRecord2 + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + VOID *DataInHob; + UINTN DataSize; + + GuidHob = GetFirstGuidHob (&gEfiSecPlatformInformation2PpiGuid); + if (GuidHob == NULL) { + *StructureSize = 0; + return EFI_SUCCESS; + } + + DataInHob = GET_GUID_HOB_DATA (GuidHob); + DataSize = GET_GUID_HOB_DATA_SIZE (GuidHob); + + // + // return the information from BistHob + // + if ((*StructureSize) < (UINT64) DataSize) { + *StructureSize = (UINT64) DataSize; + return EFI_BUFFER_TOO_SMALL; + } + + *StructureSize = (UINT64) DataSize; + CopyMem (PlatformInformationRecord2, DataInHob, DataSize); + return EFI_SUCCESS; +} + +/** + Worker function to get CPUs' BIST by calling SecPlatformInformationPpi + or SecPlatformInformation2Ppi. + + @param PeiServices Pointer to PEI Services Table + @param Guid PPI Guid + @param PpiDescriptor Return a pointer to instance of the + EFI_PEI_PPI_DESCRIPTOR + @param BistInformationData Pointer to BIST information data + @param BistInformationSize Return the size in bytes of BIST information + + @retval EFI_SUCCESS Retrieve of the BIST data successfully + @retval EFI_NOT_FOUND No sec platform information(2) ppi export + @retval EFI_DEVICE_ERROR Failed to get CPU Information + +**/ +EFI_STATUS +GetBistInfoFromPpi ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_GUID *Guid, + OUT EFI_PEI_PPI_DESCRIPTOR **PpiDescriptor, + OUT VOID **BistInformationData, + OUT UINT64 *BistInformationSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SEC_PLATFORM_INFORMATION2_PPI *SecPlatformInformation2Ppi; + EFI_SEC_PLATFORM_INFORMATION_RECORD2 *SecPlatformInformation2; + UINT64 InformationSize; + + Status = PeiServicesLocatePpi ( + Guid, // GUID + 0, // INSTANCE + PpiDescriptor, // EFI_PEI_PPI_DESCRIPTOR + (VOID **)&SecPlatformInformation2Ppi // PPI + ); + if (Status == EFI_NOT_FOUND) { + return EFI_NOT_FOUND; + } + + if (Status == EFI_SUCCESS) { + // + // Get the size of the sec platform information2(BSP/APs' BIST data) + // + InformationSize = 0; + SecPlatformInformation2 = NULL; + Status = SecPlatformInformation2Ppi->PlatformInformation2 ( + PeiServices, + &InformationSize, + SecPlatformInformation2 + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + Status = PeiServicesAllocatePool ( + (UINTN) InformationSize, + (VOID **) &SecPlatformInformation2 + ); + if (Status == EFI_SUCCESS) { + // + // Retrieve BIST data + // + Status = SecPlatformInformation2Ppi->PlatformInformation2 ( + PeiServices, + &InformationSize, + SecPlatformInformation2 + ); + if (Status == EFI_SUCCESS) { + *BistInformationData = SecPlatformInformation2; + if (BistInformationSize != NULL) { + *BistInformationSize = InformationSize; + } + return EFI_SUCCESS; + } + } + } + } + + return EFI_DEVICE_ERROR; +} + +/** + Collects BIST data from PPI. + + This function collects BIST data from Sec Platform Information2 PPI + or SEC Platform Information PPI. + + @param PeiServices Pointer to PEI Services Table + +**/ +VOID +CollectBistDataFromPpi ( + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + EFI_PEI_PPI_DESCRIPTOR *SecInformationDescriptor; + EFI_SEC_PLATFORM_INFORMATION_RECORD2 *SecPlatformInformation2; + EFI_SEC_PLATFORM_INFORMATION_RECORD *SecPlatformInformation; + UINTN NumberOfData; + EFI_SEC_PLATFORM_INFORMATION_CPU *CpuInstance; + EFI_SEC_PLATFORM_INFORMATION_CPU BspCpuInstance; + UINTN ProcessorNumber; + UINTN CpuIndex; + EFI_PROCESSOR_INFORMATION ProcessorInfo; + EFI_HEALTH_FLAGS BistData; + UINTN NumberOfProcessors; + UINTN NumberOfEnabledProcessors; + UINTN BistInformationSize; + EFI_SEC_PLATFORM_INFORMATION_RECORD2 *PlatformInformationRecord2; + EFI_SEC_PLATFORM_INFORMATION_CPU *CpuInstanceInHob; + + + MpInitLibGetNumberOfProcessors(&NumberOfProcessors, &NumberOfEnabledProcessors); + + BistInformationSize = sizeof (EFI_SEC_PLATFORM_INFORMATION_RECORD2) + + sizeof (EFI_SEC_PLATFORM_INFORMATION_CPU) * NumberOfProcessors; + Status = PeiServicesAllocatePool ( + (UINTN) BistInformationSize, + (VOID **) &PlatformInformationRecord2 + ); + ASSERT_EFI_ERROR (Status); + PlatformInformationRecord2->NumberOfCpus = (UINT32)NumberOfProcessors; + + SecPlatformInformation2 = NULL; + SecPlatformInformation = NULL; + NumberOfData = 0; + CpuInstance = NULL; + // + // Get BIST information from Sec Platform Information2 Ppi firstly + // + Status = GetBistInfoFromPpi ( + PeiServices, + &gEfiSecPlatformInformation2PpiGuid, + &SecInformationDescriptor, + (VOID *) &SecPlatformInformation2, + NULL + ); + if (Status == EFI_SUCCESS) { + // + // Sec Platform Information2 PPI includes BSP/APs' BIST information + // + NumberOfData = SecPlatformInformation2->NumberOfCpus; + CpuInstance = SecPlatformInformation2->CpuInstance; + } else { + // + // Otherwise, get BIST information from Sec Platform Information Ppi + // + Status = GetBistInfoFromPpi ( + PeiServices, + &gEfiSecPlatformInformationPpiGuid, + &SecInformationDescriptor, + (VOID *) &SecPlatformInformation, + NULL + ); + if (Status == EFI_SUCCESS) { + NumberOfData = 1; + // + // SEC Platform Information only includes BSP's BIST information + // and does not have BSP's APIC ID + // + BspCpuInstance.CpuLocation = GetInitialApicId (); + BspCpuInstance.InfoRecord.IA32HealthFlags.Uint32 = SecPlatformInformation->IA32HealthFlags.Uint32; + CpuInstance = &BspCpuInstance; + } else { + DEBUG ((EFI_D_INFO, "Does not find any stored CPU BIST information from PPI!\n")); + } + } + for (ProcessorNumber = 0; ProcessorNumber < NumberOfProcessors; ProcessorNumber ++) { + MpInitLibGetProcessorInfo (ProcessorNumber, &ProcessorInfo, &BistData); + for (CpuIndex = 0; CpuIndex < NumberOfData; CpuIndex ++) { + ASSERT (CpuInstance != NULL); + if (ProcessorInfo.ProcessorId == CpuInstance[CpuIndex].CpuLocation) { + // + // Update processor's BIST data if it is already stored before + // + BistData = CpuInstance[CpuIndex].InfoRecord.IA32HealthFlags; + } + } + if (BistData.Uint32 != 0) { + // + // Report Status Code that self test is failed + // + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_EC_SELF_TEST) + ); + } + DEBUG ((EFI_D_INFO, " APICID - 0x%08x, BIST - 0x%08x\n", + (UINT32) ProcessorInfo.ProcessorId, + BistData + )); + CpuInstanceInHob = PlatformInformationRecord2->CpuInstance; + CpuInstanceInHob[ProcessorNumber].CpuLocation = (UINT32) ProcessorInfo.ProcessorId; + CpuInstanceInHob[ProcessorNumber].InfoRecord.IA32HealthFlags = BistData; + } + + // + // Build SecPlatformInformation2 PPI GUIDed HOB that also could be consumed + // by CPU MP driver to get CPU BIST data + // + BuildGuidDataHob ( + &gEfiSecPlatformInformation2PpiGuid, + PlatformInformationRecord2, + (UINTN) BistInformationSize + ); + + if (SecPlatformInformation2 != NULL) { + if (NumberOfData < NumberOfProcessors) { + // + // Reinstall SecPlatformInformation2 PPI to include new BIST information + // + Status = PeiServicesReInstallPpi ( + SecInformationDescriptor, + &mPeiSecPlatformInformation2Ppi + ); + ASSERT_EFI_ERROR (Status); + } + } else { + // + // Install SecPlatformInformation2 PPI + // + Status = PeiServicesInstallPpi (&mPeiSecPlatformInformation2Ppi); + ASSERT_EFI_ERROR(Status); + } +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMp2Pei.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMp2Pei.c new file mode 100644 index 00000000..88176676 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMp2Pei.c @@ -0,0 +1,417 @@ +/** @file + EDKII_PEI_MP_SERVICES2_PPI Implementation code. + + Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuMpPei.h" + +/** + This service retrieves the number of logical processor in the platform + and the number of those logical processors that are enabled on this boot. + This service may only be called from the BSP. + + This function is used to retrieve the following information: + - The number of logical processors that are present in the system. + - The number of enabled logical processors in the system at the instant + this call is made. + + Because MP Service Ppi provides services to enable and disable processors + dynamically, the number of enabled logical processors may vary during the + course of a boot session. + + If this service is called from an AP, then EFI_DEVICE_ERROR is returned. + If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then + EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors + is returned in NumberOfProcessors, the number of currently enabled processor + is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned. + + @param[in] This Pointer to this instance of the PPI. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors + Number of processors in the system that are enabled. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL. + NumberOfEnabledProcessors is NULL. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiGetNumberOfProcessors ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ) +{ + if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) { + return EFI_INVALID_PARAMETER; + } + + return MpInitLibGetNumberOfProcessors ( + NumberOfProcessors, + NumberOfEnabledProcessors + ); +} + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + This service retrieves detailed MP-related information about any processor + on the platform. Note the following: + - The processor information may change during the course of a boot session. + - The information presented here is entirely MP related. + + Information regarding the number of caches and their sizes, frequency of operation, + slot numbers is all considered platform-related information and is not provided + by this service. + + @param[in] This Pointer to this instance of the PPI. + @param[in] ProcessorNumber Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] ProcessorInfoBuffer Number of processors in the system that are enabled. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiGetProcessorInfo ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ) +{ + return MpInitLibGetProcessorInfo (ProcessorNumber, ProcessorInfoBuffer, NULL); +} + +/** + This service executes a caller provided function on all enabled APs. APs can + run either simultaneously or one at a time in sequence. This service supports + both blocking requests only. This service may only + be called from the BSP. + + This function is used to dispatch all the enabled APs to the function specified + by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned + immediately and Procedure is not started on any AP. + + If SingleThread is TRUE, all the enabled APs execute the function specified by + Procedure one by one, in ascending order of processor handle number. Otherwise, + all the enabled APs execute the function specified by Procedure simultaneously. + + If the timeout specified by TimeoutInMicroSeconds expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. All enabled APs + are always available for further calls to EDKII_PEI_MP_SERVICES2_PPI.StartupAllAPs() + and EDKII_PEI_MP_SERVICES2_PPI.StartupThisAP(). If FailedCpuList is not NULL, its + content points to the list of processor handle numbers in which Procedure was + terminated. + + Note: It is the responsibility of the consumer of the EDKII_PEI_MP_SERVICES2_PPI.StartupAllAPs() + to make sure that the nature of the code that is executed on the BSP and the + dispatched APs is well controlled. The MP Services Ppi does not guarantee + that the Procedure function is MP-safe. Hence, the tasks that can be run in + parallel are limited to certain independent tasks and well-controlled exclusive + code. PEI services and Ppis may not be called by APs unless otherwise + specified. + + In blocking execution mode, BSP waits until all APs finish or + TimeoutInMicroSeconds expires. + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute the function + specified by Procedure one by one, in ascending order + of processor handle number. If FALSE, then all the + enabled APs execute the function specified by Procedure + simultaneously. + @param[in] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EDKII_PEI_MP_SERVICES2_PPI.StartupAllAPs() + or EDKII_PEI_MP_SERVICES2_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before the + timeout expired. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before all + enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiStartupAllAPs ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + return MpInitLibStartupAllAPs ( + Procedure, + SingleThread, + NULL, + TimeoutInMicroSeconds, + ProcedureArgument, + NULL + ); +} + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. The caller can request the BSP to wait for the completion + of the AP. This service may only be called from the BSP. + + This function is used to dispatch one enabled AP to the function specified by + Procedure passing in the argument specified by ProcedureArgument. + The execution is in blocking mode. The BSP waits until the AP finishes or + TimeoutInMicroSecondss expires. + + If the timeout specified by TimeoutInMicroseconds expires before the AP returns + from Procedure, then execution of Procedure by the AP is terminated. The AP is + available for subsequent calls to EDKII_PEI_MP_SERVICES2_PPI.StartupAllAPs() and + EDKII_PEI_MP_SERVICES2_PPI.StartupThisAP(). + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EDKII_PEI_MP_SERVICES2_PPI.GetNumberOfProcessors(). + @param[in] TimeoutInMicroseconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EDKII_PEI_MP_SERVICES2_PPI.StartupAllAPs() + or EDKII_PEI_MP_SERVICES2_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before the + timeout expires. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the + specified AP has finished. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiStartupThisAP ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + return MpInitLibStartupThisAP ( + Procedure, + ProcessorNumber, + NULL, + TimeoutInMicroseconds, + ProcedureArgument, + NULL + ); +} + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. The new BSP can take over the + execution of the old BSP and continue seamlessly from where the old one left + off. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EDKII_PEI_MP_SERVICES2_PPI.GetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an enabled + AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to this + service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or a disabled + AP. + @retval EFI_NOT_READY The specified AP is busy. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiSwitchBSP ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + return MpInitLibSwitchBSP (ProcessorNumber, EnableOldBSP); +} + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + This service allows the caller enable or disable an AP from this point onward. + The caller can optionally specify the health status of the AP by Health. If + an AP is being disabled, then the state of the disabled AP is implementation + dependent. If an AP is enabled, then the implementation must guarantee that a + complete initialization sequence is performed on the AP, so the AP is in a state + that is compatible with an MP operating system. + + If the enable or disable AP operation cannot be completed prior to the return + from this service, then EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EDKII_PEI_MP_SERVICES2_PPI.GetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for enabled, + FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies the + new health status of the AP. This flag corresponds to + StatusFlag defined in EDKII_PEI_MP_SERVICES2_PPI.GetProcessorInfo(). + Only the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter is + ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed prior + to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiEnableDisableAP ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + return MpInitLibEnableDisableAP (ProcessorNumber, EnableAP, HealthFlag); +} + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + This service returns the processor handle number for the calling processor. + The returned value is in the range from 0 to the total number of logical + processors minus 1. The total number of logical processors can be retrieved + with EDKII_PEI_MP_SERVICES2_PPI.GetNumberOfProcessors(). This service may be + called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER + is returned. Otherwise, the current processors handle number is returned in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[out] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EDKII_PEI_MP_SERVICES2_PPI.GetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned in + ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiWhoAmI ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + OUT UINTN *ProcessorNumber + ) +{ + return MpInitLibWhoAmI (ProcessorNumber); +} + +/** + This service executes a caller provided function on all enabled CPUs. CPUs can + run either simultaneously or one at a time in sequence. This service may only + be called from the BSP. + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires in blocking + mode, BSP returns EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all CPUs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before the + timeout expired. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before all + enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +EdkiiPeiStartupAllCPUs ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + return MpInitLibStartupAllCPUs ( + Procedure, + TimeoutInMicroSeconds, + ProcedureArgument + ); +} + +// +// CPU MP2 PPI to be installed +// +EDKII_PEI_MP_SERVICES2_PPI mMpServices2Ppi = { + EdkiiPeiGetNumberOfProcessors, + EdkiiPeiGetProcessorInfo, + EdkiiPeiStartupAllAPs, + EdkiiPeiStartupThisAP, + EdkiiPeiSwitchBSP, + EdkiiPeiEnableDisableAP, + EdkiiPeiWhoAmI, + EdkiiPeiStartupAllCPUs +}; + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.c new file mode 100644 index 00000000..07b950f3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.c @@ -0,0 +1,710 @@ +/** @file + CPU PEI Module installs CPU Multiple Processor PPI. + + Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuMpPei.h" + +extern EDKII_PEI_MP_SERVICES2_PPI mMpServices2Ppi; + +// +// CPU MP PPI to be installed +// +EFI_PEI_MP_SERVICES_PPI mMpServicesPpi = { + PeiGetNumberOfProcessors, + PeiGetProcessorInfo, + PeiStartupAllAPs, + PeiStartupThisAP, + PeiSwitchBSP, + PeiEnableDisableAP, + PeiWhoAmI, +}; + +EFI_PEI_PPI_DESCRIPTOR mPeiCpuMpPpiList[] = { + { + EFI_PEI_PPI_DESCRIPTOR_PPI, + &gEdkiiPeiMpServices2PpiGuid, + &mMpServices2Ppi + }, + { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiMpServicesPpiGuid, + &mMpServicesPpi + } +}; + +/** + This service retrieves the number of logical processor in the platform + and the number of those logical processors that are enabled on this boot. + This service may only be called from the BSP. + + This function is used to retrieve the following information: + - The number of logical processors that are present in the system. + - The number of enabled logical processors in the system at the instant + this call is made. + + Because MP Service Ppi provides services to enable and disable processors + dynamically, the number of enabled logical processors may vary during the + course of a boot session. + + If this service is called from an AP, then EFI_DEVICE_ERROR is returned. + If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then + EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors + is returned in NumberOfProcessors, the number of currently enabled processor + is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to this instance of the PPI. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors + Number of processors in the system that are enabled. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL. + NumberOfEnabledProcessors is NULL. +**/ +EFI_STATUS +EFIAPI +PeiGetNumberOfProcessors ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ) +{ + if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) { + return EFI_INVALID_PARAMETER; + } + + return MpInitLibGetNumberOfProcessors ( + NumberOfProcessors, + NumberOfEnabledProcessors + ); +} + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + This service retrieves detailed MP-related information about any processor + on the platform. Note the following: + - The processor information may change during the course of a boot session. + - The information presented here is entirely MP related. + + Information regarding the number of caches and their sizes, frequency of operation, + slot numbers is all considered platform-related information and is not provided + by this service. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to this instance of the PPI. + @param[in] ProcessorNumber Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] ProcessorInfoBuffer Number of processors in the system that are enabled. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. +**/ +EFI_STATUS +EFIAPI +PeiGetProcessorInfo ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ) +{ + return MpInitLibGetProcessorInfo (ProcessorNumber, ProcessorInfoBuffer, NULL); +} + +/** + This service executes a caller provided function on all enabled APs. APs can + run either simultaneously or one at a time in sequence. This service supports + both blocking requests only. This service may only + be called from the BSP. + + This function is used to dispatch all the enabled APs to the function specified + by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned + immediately and Procedure is not started on any AP. + + If SingleThread is TRUE, all the enabled APs execute the function specified by + Procedure one by one, in ascending order of processor handle number. Otherwise, + all the enabled APs execute the function specified by Procedure simultaneously. + + If the timeout specified by TimeoutInMicroSeconds expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. All enabled APs + are always available for further calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + and EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If FailedCpuList is not NULL, its + content points to the list of processor handle numbers in which Procedure was + terminated. + + Note: It is the responsibility of the consumer of the EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + to make sure that the nature of the code that is executed on the BSP and the + dispatched APs is well controlled. The MP Services Ppi does not guarantee + that the Procedure function is MP-safe. Hence, the tasks that can be run in + parallel are limited to certain independent tasks and well-controlled exclusive + code. PEI services and Ppis may not be called by APs unless otherwise + specified. + + In blocking execution mode, BSP waits until all APs finish or + TimeoutInMicroSeconds expires. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute the function + specified by Procedure one by one, in ascending order + of processor handle number. If FALSE, then all the + enabled APs execute the function specified by Procedure + simultaneously. + @param[in] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before the + timeout expired. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before all + enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +PeiStartupAllAPs ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + return MpInitLibStartupAllAPs ( + Procedure, + SingleThread, + NULL, + TimeoutInMicroSeconds, + ProcedureArgument, + NULL + ); +} + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. The caller can request the BSP to wait for the completion + of the AP. This service may only be called from the BSP. + + This function is used to dispatch one enabled AP to the function specified by + Procedure passing in the argument specified by ProcedureArgument. + The execution is in blocking mode. The BSP waits until the AP finishes or + TimeoutInMicroSecondss expires. + + If the timeout specified by TimeoutInMicroseconds expires before the AP returns + from Procedure, then execution of Procedure by the AP is terminated. The AP is + available for subsequent calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() and + EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] TimeoutInMicroseconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before the + timeout expires. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the + specified AP has finished. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +PeiStartupThisAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + return MpInitLibStartupThisAP ( + Procedure, + ProcessorNumber, + NULL, + TimeoutInMicroseconds, + ProcedureArgument, + NULL + ); +} + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. The new BSP can take over the + execution of the old BSP and continue seamlessly from where the old one left + off. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an enabled + AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to this + service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or a disabled + AP. + @retval EFI_NOT_READY The specified AP is busy. +**/ +EFI_STATUS +EFIAPI +PeiSwitchBSP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + return MpInitLibSwitchBSP (ProcessorNumber, EnableOldBSP); +} + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + This service allows the caller enable or disable an AP from this point onward. + The caller can optionally specify the health status of the AP by Health. If + an AP is being disabled, then the state of the disabled AP is implementation + dependent. If an AP is enabled, then the implementation must guarantee that a + complete initialization sequence is performed on the AP, so the AP is in a state + that is compatible with an MP operating system. + + If the enable or disable AP operation cannot be completed prior to the return + from this service, then EFI_UNSUPPORTED must be returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for enabled, + FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies the + new health status of the AP. This flag corresponds to + StatusFlag defined in EFI_PEI_MP_SERVICES_PPI.GetProcessorInfo(). + Only the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter is + ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed prior + to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. +**/ +EFI_STATUS +EFIAPI +PeiEnableDisableAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + return MpInitLibEnableDisableAP (ProcessorNumber, EnableAP, HealthFlag); +} + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + This service returns the processor handle number for the calling processor. + The returned value is in the range from 0 to the total number of logical + processors minus 1. The total number of logical processors can be retrieved + with EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). This service may be + called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER + is returned. Otherwise, the current processors handle number is returned in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[out] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned in + ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. +**/ +EFI_STATUS +EFIAPI +PeiWhoAmI ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + OUT UINTN *ProcessorNumber + ) +{ + return MpInitLibWhoAmI (ProcessorNumber); +} + +/** + Get GDT register value. + + This function is mainly for AP purpose because AP may have different GDT + table than BSP. + + @param[in,out] Buffer The pointer to private data buffer. + +**/ +VOID +EFIAPI +GetGdtr ( + IN OUT VOID *Buffer + ) +{ + AsmReadGdtr ((IA32_DESCRIPTOR *)Buffer); +} + +/** + Initializes CPU exceptions handlers for the sake of stack switch requirement. + + This function is a wrapper of InitializeCpuExceptionHandlersEx. It's mainly + for the sake of AP's init because of EFI_AP_PROCEDURE API requirement. + + @param[in,out] Buffer The pointer to private data buffer. + +**/ +VOID +EFIAPI +InitializeExceptionStackSwitchHandlers ( + IN OUT VOID *Buffer + ) +{ + CPU_EXCEPTION_INIT_DATA *EssData; + IA32_DESCRIPTOR Idtr; + EFI_STATUS Status; + + EssData = Buffer; + // + // We don't plan to replace IDT table with a new one, but we should not assume + // the AP's IDT is the same as BSP's IDT either. + // + AsmReadIdtr (&Idtr); + EssData->Ia32.IdtTable = (VOID *)Idtr.Base; + EssData->Ia32.IdtTableSize = Idtr.Limit + 1; + Status = InitializeCpuExceptionHandlersEx (NULL, EssData); + ASSERT_EFI_ERROR (Status); +} + +/** + Initializes MP exceptions handlers for the sake of stack switch requirement. + + This function will allocate required resources required to setup stack switch + and pass them through CPU_EXCEPTION_INIT_DATA to each logic processor. + +**/ +VOID +InitializeMpExceptionStackSwitchHandlers ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN Bsp; + UINTN ExceptionNumber; + UINTN OldGdtSize; + UINTN NewGdtSize; + UINTN NewStackSize; + IA32_DESCRIPTOR Gdtr; + CPU_EXCEPTION_INIT_DATA EssData; + UINT8 *GdtBuffer; + UINT8 *StackTop; + UINTN NumberOfProcessors; + + if (!PcdGetBool (PcdCpuStackGuard)) { + return; + } + + MpInitLibGetNumberOfProcessors(&NumberOfProcessors, NULL); + MpInitLibWhoAmI (&Bsp); + + ExceptionNumber = FixedPcdGetSize (PcdCpuStackSwitchExceptionList); + NewStackSize = FixedPcdGet32 (PcdCpuKnownGoodStackSize) * ExceptionNumber; + + StackTop = AllocatePages (EFI_SIZE_TO_PAGES (NewStackSize * NumberOfProcessors)); + ASSERT(StackTop != NULL); + if (StackTop == NULL) { + return; + } + StackTop += NewStackSize * NumberOfProcessors; + + // + // The default exception handlers must have been initialized. Let's just skip + // it in this method. + // + EssData.Ia32.Revision = CPU_EXCEPTION_INIT_DATA_REV; + EssData.Ia32.InitDefaultHandlers = FALSE; + + EssData.Ia32.StackSwitchExceptions = FixedPcdGetPtr(PcdCpuStackSwitchExceptionList); + EssData.Ia32.StackSwitchExceptionNumber = ExceptionNumber; + EssData.Ia32.KnownGoodStackSize = FixedPcdGet32(PcdCpuKnownGoodStackSize); + + // + // Initialize Gdtr to suppress incorrect compiler/analyzer warnings. + // + Gdtr.Base = 0; + Gdtr.Limit = 0; + for (Index = 0; Index < NumberOfProcessors; ++Index) { + // + // To support stack switch, we need to re-construct GDT but not IDT. + // + if (Index == Bsp) { + GetGdtr(&Gdtr); + } else { + // + // AP might have different size of GDT from BSP. + // + MpInitLibStartupThisAP (GetGdtr, Index, NULL, 0, (VOID *)&Gdtr, NULL); + } + + // + // X64 needs only one TSS of current task working for all exceptions + // because of its IST feature. IA32 needs one TSS for each exception + // in addition to current task. Since AP is not supposed to allocate + // memory, we have to do it in BSP. To simplify the code, we allocate + // memory for IA32 case to cover both IA32 and X64 exception stack + // switch. + // + // Layout of memory to allocate for each processor: + // -------------------------------- + // | Alignment | (just in case) + // -------------------------------- + // | | + // | Original GDT | + // | | + // -------------------------------- + // | Current task descriptor | + // -------------------------------- + // | | + // | Exception task descriptors | X ExceptionNumber + // | | + // -------------------------------- + // | Current task-state segment | + // -------------------------------- + // | | + // | Exception task-state segment | X ExceptionNumber + // | | + // -------------------------------- + // + OldGdtSize = Gdtr.Limit + 1; + EssData.Ia32.ExceptionTssDescSize = sizeof (IA32_TSS_DESCRIPTOR) * + (ExceptionNumber + 1); + EssData.Ia32.ExceptionTssSize = sizeof (IA32_TASK_STATE_SEGMENT) * + (ExceptionNumber + 1); + NewGdtSize = sizeof (IA32_TSS_DESCRIPTOR) + + OldGdtSize + + EssData.Ia32.ExceptionTssDescSize + + EssData.Ia32.ExceptionTssSize; + + Status = PeiServicesAllocatePool ( + NewGdtSize, + (VOID **)&GdtBuffer + ); + ASSERT (GdtBuffer != NULL); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return; + } + + // + // Make sure GDT table alignment + // + EssData.Ia32.GdtTable = ALIGN_POINTER(GdtBuffer, sizeof (IA32_TSS_DESCRIPTOR)); + NewGdtSize -= ((UINT8 *)EssData.Ia32.GdtTable - GdtBuffer); + EssData.Ia32.GdtTableSize = NewGdtSize; + + EssData.Ia32.ExceptionTssDesc = ((UINT8 *)EssData.Ia32.GdtTable + OldGdtSize); + EssData.Ia32.ExceptionTss = ((UINT8 *)EssData.Ia32.GdtTable + OldGdtSize + + EssData.Ia32.ExceptionTssDescSize); + + EssData.Ia32.KnownGoodStackTop = (UINTN)StackTop; + DEBUG ((DEBUG_INFO, + "Exception stack top[cpu%lu]: 0x%lX\n", + (UINT64)(UINTN)Index, + (UINT64)(UINTN)StackTop)); + + if (Index == Bsp) { + InitializeExceptionStackSwitchHandlers (&EssData); + } else { + MpInitLibStartupThisAP ( + InitializeExceptionStackSwitchHandlers, + Index, + NULL, + 0, + (VOID *)&EssData, + NULL + ); + } + + StackTop -= NewStackSize; + } +} + +/** + Initializes MP and exceptions handlers. + + @param PeiServices The pointer to the PEI Services Table. + + @retval EFI_SUCCESS MP was successfully initialized. + @retval others Error occurred in MP initialization. + +**/ +EFI_STATUS +InitializeCpuMpWorker ( + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + EFI_VECTOR_HANDOFF_INFO *VectorInfo; + EFI_PEI_VECTOR_HANDOFF_INFO_PPI *VectorHandoffInfoPpi; + + // + // Get Vector Hand-off Info PPI + // + VectorInfo = NULL; + Status = PeiServicesLocatePpi ( + &gEfiVectorHandoffInfoPpiGuid, + 0, + NULL, + (VOID **)&VectorHandoffInfoPpi + ); + if (Status == EFI_SUCCESS) { + VectorInfo = VectorHandoffInfoPpi->Info; + } + + // + // Initialize default handlers + // + Status = InitializeCpuExceptionHandlers (VectorInfo); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = MpInitLibInitialize (); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Special initialization for the sake of Stack Guard + // + InitializeMpExceptionStackSwitchHandlers (); + + // + // Update and publish CPU BIST information + // + CollectBistDataFromPpi (PeiServices); + + // + // Install CPU MP PPI + // + Status = PeiServicesInstallPpi(mPeiCpuMpPpiList); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + The Entry point of the MP CPU PEIM. + + This function will wakeup APs and collect CPU AP count and install the + Mp Service Ppi. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS MpServicePpi is installed successfully. + +**/ +EFI_STATUS +EFIAPI +CpuMpPeimInit ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + + // + // For the sake of special initialization needing to be done right after + // memory discovery. + // + Status = PeiServicesNotifyPpi (&mPostMemNotifyList[0]); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.h new file mode 100644 index 00000000..1a6068f4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.h @@ -0,0 +1,450 @@ +/** @file + Definitions to install Multiple Processor PPI. + + Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_MP_PEI_H_ +#define _CPU_MP_PEI_H_ + +#include <PiPei.h> + +#include <Ppi/MpServices.h> +#include <Ppi/SecPlatformInformation.h> +#include <Ppi/SecPlatformInformation2.h> +#include <Ppi/EndOfPeiPhase.h> +#include <Ppi/MpServices2.h> + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/HobLib.h> +#include <Library/LocalApicLib.h> +#include <Library/PeimEntryPoint.h> +#include <Library/PeiServicesLib.h> +#include <Library/ReportStatusCodeLib.h> +#include <Library/CpuExceptionHandlerLib.h> +#include <Library/MpInitLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> + +extern EFI_PEI_PPI_DESCRIPTOR mPeiCpuMpPpiDesc; + +/** + This service retrieves the number of logical processor in the platform + and the number of those logical processors that are enabled on this boot. + This service may only be called from the BSP. + + This function is used to retrieve the following information: + - The number of logical processors that are present in the system. + - The number of enabled logical processors in the system at the instant + this call is made. + + Because MP Service Ppi provides services to enable and disable processors + dynamically, the number of enabled logical processors may vary during the + course of a boot session. + + If this service is called from an AP, then EFI_DEVICE_ERROR is returned. + If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then + EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors + is returned in NumberOfProcessors, the number of currently enabled processor + is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to this instance of the PPI. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors + Number of processors in the system that are enabled. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL. + NumberOfEnabledProcessors is NULL. +**/ +EFI_STATUS +EFIAPI +PeiGetNumberOfProcessors ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ); + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + This service retrieves detailed MP-related information about any processor + on the platform. Note the following: + - The processor information may change during the course of a boot session. + - The information presented here is entirely MP related. + + Information regarding the number of caches and their sizes, frequency of operation, + slot numbers is all considered platform-related information and is not provided + by this service. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to this instance of the PPI. + @param[in] ProcessorNumber Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] ProcessorInfoBuffer Number of processors in the system that are enabled. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. +**/ +EFI_STATUS +EFIAPI +PeiGetProcessorInfo ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ); + +/** + This service executes a caller provided function on all enabled APs. APs can + run either simultaneously or one at a time in sequence. This service supports + both blocking requests only. This service may only + be called from the BSP. + + This function is used to dispatch all the enabled APs to the function specified + by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned + immediately and Procedure is not started on any AP. + + If SingleThread is TRUE, all the enabled APs execute the function specified by + Procedure one by one, in ascending order of processor handle number. Otherwise, + all the enabled APs execute the function specified by Procedure simultaneously. + + If the timeout specified by TimeoutInMicroSeconds expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. All enabled APs + are always available for further calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + and EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If FailedCpuList is not NULL, its + content points to the list of processor handle numbers in which Procedure was + terminated. + + Note: It is the responsibility of the consumer of the EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + to make sure that the nature of the code that is executed on the BSP and the + dispatched APs is well controlled. The MP Services Ppi does not guarantee + that the Procedure function is MP-safe. Hence, the tasks that can be run in + parallel are limited to certain independent tasks and well-controlled exclusive + code. PEI services and Ppis may not be called by APs unless otherwise + specified. + + In blocking execution mode, BSP waits until all APs finish or + TimeoutInMicroSeconds expires. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute the function + specified by Procedure one by one, in ascending order + of processor handle number. If FALSE, then all the + enabled APs execute the function specified by Procedure + simultaneously. + @param[in] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before the + timeout expired. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before all + enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +PeiStartupAllAPs ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL + ); + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. The caller can request the BSP to wait for the completion + of the AP. This service may only be called from the BSP. + + This function is used to dispatch one enabled AP to the function specified by + Procedure passing in the argument specified by ProcedureArgument. + The execution is in blocking mode. The BSP waits until the AP finishes or + TimeoutInMicroSecondss expires. + + If the timeout specified by TimeoutInMicroseconds expires before the AP returns + from Procedure, then execution of Procedure by the AP is terminated. The AP is + available for subsequent calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() and + EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] TimeoutInMicroseconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before the + timeout expires. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the + specified AP has finished. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +PeiStartupThisAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ); + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. The new BSP can take over the + execution of the old BSP and continue seamlessly from where the old one left + off. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an enabled + AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to this + service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or a disabled + AP. + @retval EFI_NOT_READY The specified AP is busy. +**/ +EFI_STATUS +EFIAPI +PeiSwitchBSP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ); + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + This service allows the caller enable or disable an AP from this point onward. + The caller can optionally specify the health status of the AP by Health. If + an AP is being disabled, then the state of the disabled AP is implementation + dependent. If an AP is enabled, then the implementation must guarantee that a + complete initialization sequence is performed on the AP, so the AP is in a state + that is compatible with an MP operating system. + + If the enable or disable AP operation cannot be completed prior to the return + from this service, then EFI_UNSUPPORTED must be returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for enabled, + FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies the + new health status of the AP. This flag corresponds to + StatusFlag defined in EFI_PEI_MP_SERVICES_PPI.GetProcessorInfo(). + Only the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter is + ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed prior + to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. +**/ +EFI_STATUS +EFIAPI +PeiEnableDisableAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ); + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + This service returns the processor handle number for the calling processor. + The returned value is in the range from 0 to the total number of logical + processors minus 1. The total number of logical processors can be retrieved + with EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). This service may be + called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER + is returned. Otherwise, the current processors handle number is returned in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[out] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned in + ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. +**/ +EFI_STATUS +EFIAPI +PeiWhoAmI ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + OUT UINTN *ProcessorNumber + ); + +/** + Collects BIST data from PPI. + + This function collects BIST data from Sec Platform Information2 PPI + or SEC Platform Information PPI. + + @param PeiServices Pointer to PEI Services Table + +**/ +VOID +CollectBistDataFromPpi ( + IN CONST EFI_PEI_SERVICES **PeiServices + ); + +/** + Implementation of the PlatformInformation2 service in EFI_SEC_PLATFORM_INFORMATION2_PPI. + + @param PeiServices The pointer to the PEI Services Table. + @param StructureSize The pointer to the variable describing size of the input buffer. + @param PlatformInformationRecord2 The pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD2. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. The current buffer size needed to + hold the record is returned in StructureSize. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformation2 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD2 *PlatformInformationRecord2 + ); + +/** + Migrates the Global Descriptor Table (GDT) to permanent memory. + + @retval EFI_SUCCESS The GDT was migrated successfully. + @retval EFI_OUT_OF_RESOURCES The GDT could not be migrated due to lack of available memory. + +**/ +EFI_STATUS +MigrateGdt ( + VOID + ); + +/** + Initializes MP and exceptions handlers. + + @param PeiServices The pointer to the PEI Services Table. + + @retval EFI_SUCCESS MP was successfully initialized. + @retval others Error occurred in MP initialization. + +**/ +EFI_STATUS +InitializeCpuMpWorker ( + IN CONST EFI_PEI_SERVICES **PeiServices + ); + +/** + Enable/setup stack guard for each processor if PcdCpuStackGuard is set to TRUE. + + Doing this in the memory-discovered callback is to make sure the Stack Guard + feature to cover as most PEI code as possible. + + @param[in] PeiServices General purpose services available to every PEIM. + @param[in] NotifyDescriptor The notification structure this PEIM registered on install. + @param[in] Ppi The memory discovered PPI. Not used. + + @retval EFI_SUCCESS The function completed successfully. + @retval others There's error in MP initialization. +**/ +EFI_STATUS +EFIAPI +MemoryDiscoveredPpiNotifyCallback ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ); + +extern EFI_PEI_NOTIFY_DESCRIPTOR mPostMemNotifyList[]; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.inf new file mode 100644 index 00000000..c56d1e01 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.inf @@ -0,0 +1,76 @@ +## @file +# CPU driver installs CPU PI Multi-processor PPI. +# +# Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuMpPei + MODULE_UNI_FILE = CpuMpPei.uni + FILE_GUID = EDADEB9D-DDBA-48BD-9D22-C1C169C8C5C6 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = CpuMpPeimInit + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + CpuMpPei.h + CpuMpPei.c + CpuBist.c + CpuPaging.c + CpuMp2Pei.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + HobLib + LocalApicLib + PeimEntryPoint + PeiServicesLib + ReportStatusCodeLib + CpuExceptionHandlerLib + MpInitLib + BaseMemoryLib + CpuLib + MemoryAllocationLib + +[Guids] + gEdkiiMigratedFvInfoGuid ## SOMETIMES_CONSUMES ## HOB + +[Ppis] + gEfiPeiMpServicesPpiGuid ## PRODUCES + gEfiSecPlatformInformationPpiGuid ## SOMETIMES_CONSUMES + ## SOMETIMES_CONSUMES + ## PRODUCES + ## UNDEFINED # HOB + gEfiSecPlatformInformation2PpiGuid + gEfiVectorHandoffInfoPpiGuid ## SOMETIMES_CONSUMES + gEfiPeiMemoryDiscoveredPpiGuid ## CONSUMES + gEdkiiPeiMpServices2PpiGuid ## PRODUCES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## SOMETIMES_CONSUMES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + CpuMpPeiExtra.uni + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.uni new file mode 100644 index 00000000..bbb395fd --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPei.uni @@ -0,0 +1,16 @@ +// /** @file
+// CPU driver installs CPU PI Multi-processor PPI.
+//
+// CPU driver installs CPU PI Multi-processor PPI.
+//
+// Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Installs CPU PI Multi-processor PPI"
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU driver installs CPU PI Multi-processor PPI."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPeiExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPeiExtra.uni new file mode 100644 index 00000000..f84e1841 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuMpPeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// CpuMpPei Localized Strings and Content
+//
+// Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"CPU Multi-processor PEIM Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuPaging.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuPaging.c new file mode 100644 index 00000000..23c5cca4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuMpPei/CpuPaging.c @@ -0,0 +1,711 @@ +/** @file + Basic paging support for the CPU to enable Stack Guard. + +Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Register/Intel/Cpuid.h> +#include <Register/Intel/Msr.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/CpuLib.h> +#include <Library/BaseLib.h> +#include <Guid/MigratedFvInfo.h> +#ifdef VBOX +# define IN_RING0 +# include <iprt/asm.h> +#endif + +#include "CpuMpPei.h" + +#define IA32_PG_P BIT0 +#define IA32_PG_RW BIT1 +#define IA32_PG_U BIT2 +#define IA32_PG_A BIT5 +#define IA32_PG_D BIT6 +#define IA32_PG_PS BIT7 +#define IA32_PG_NX BIT63 + +#define PAGE_ATTRIBUTE_BITS (IA32_PG_RW | IA32_PG_P) +#define PAGE_PROGATE_BITS (IA32_PG_D | IA32_PG_A | IA32_PG_NX | IA32_PG_U |\ + PAGE_ATTRIBUTE_BITS) + +#define PAGING_PAE_INDEX_MASK 0x1FF +#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull +#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull +#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull +#define PAGING_512G_ADDRESS_MASK_64 0x000FFF8000000000ull + +typedef enum { + PageNone = 0, + PageMin = 1, + Page4K = PageMin, + Page2M = 2, + Page1G = 3, + Page512G = 4, + PageMax = Page512G +} PAGE_ATTRIBUTE; + +typedef struct { + PAGE_ATTRIBUTE Attribute; + UINT64 Length; + UINT64 AddressMask; + UINTN AddressBitOffset; + UINTN AddressBitLength; +} PAGE_ATTRIBUTE_TABLE; + +PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = { + {PageNone, 0, 0, 0, 0}, + {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64, 12, 9}, + {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64, 21, 9}, + {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64, 30, 9}, + {Page512G, SIZE_512GB, PAGING_512G_ADDRESS_MASK_64, 39, 9}, +}; + +EFI_PEI_NOTIFY_DESCRIPTOR mPostMemNotifyList[] = { + { + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiMemoryDiscoveredPpiGuid, + MemoryDiscoveredPpiNotifyCallback + } +}; + +#ifdef VBOX +/** + Safe page table entry write function, make 104% sure the compiler won't + split up the access (fatal if modifying entries for current code or data). + + @param[in] PageEntry The page table entry to modify.* + @param[in] CurrentPageEntry The old page table value (for cmpxchg8b). + @param[in] NewPageEntry What to write. +**/ +static VOID SafePageTableEntryWrite64 (UINT64 volatile *PageEntry, UINT64 CurrentPageEntry, UINT64 NewPageEntry) +{ +# ifdef VBOX + ASMAtomicWriteU64(PageEntry, NewPageEntry); RT_NOREF(CurrentPageEntry); +# else + for (;;) { + UINT64 CurValue = InterlockedCompareExchange64(PageEntry, CurrentPageEntry, NewPageEntry); + if (CurValue == CurrentPageEntry) + return; + CurrentPageEntry = CurValue; + } +# endif +} +#endif + +/** + The function will check if IA32 PAE is supported. + + @retval TRUE IA32 PAE is supported. + @retval FALSE IA32 PAE is not supported. + +**/ +BOOLEAN +IsIa32PaeSupported ( + VOID + ) +{ + UINT32 RegEax; + CPUID_VERSION_INFO_EDX RegEdx; + + AsmCpuid (CPUID_SIGNATURE, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_VERSION_INFO) { + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx.Uint32); + if (RegEdx.Bits.PAE != 0) { + return TRUE; + } + } + + return FALSE; +} + +/** + This API provides a way to allocate memory for page table. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +AllocatePageTableMemory ( + IN UINTN Pages + ) +{ + VOID *Address; + + Address = AllocatePages(Pages); + if (Address != NULL) { + ZeroMem(Address, EFI_PAGES_TO_SIZE (Pages)); + } + + return Address; +} + +/** + Get the address width supported by current processor. + + @retval 32 If processor is in 32-bit mode. + @retval 36-48 If processor is in 64-bit mode. + +**/ +UINTN +GetPhysicalAddressWidth ( + VOID + ) +{ + UINT32 RegEax; + + if (sizeof(UINTN) == 4) { + return 32; + } + + AsmCpuid(CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_VIR_PHY_ADDRESS_SIZE) { + AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &RegEax, NULL, NULL, NULL); + RegEax &= 0xFF; + if (RegEax > 48) { + return 48; + } + + return (UINTN)RegEax; + } + + return 36; +} + +/** + Get the type of top level page table. + + @retval Page512G PML4 paging. + @retval Page1G PAE paging. + +**/ +PAGE_ATTRIBUTE +GetPageTableTopLevelType ( + VOID + ) +{ + MSR_IA32_EFER_REGISTER MsrEfer; + + MsrEfer.Uint64 = AsmReadMsr64 (MSR_CORE_IA32_EFER); + + return (MsrEfer.Bits.LMA == 1) ? Page512G : Page1G; +} + +/** + Return page table entry matching the address. + + @param[in] Address The address to be checked. + @param[out] PageAttributes The page attribute of the page entry. + + @return The page entry. +**/ +VOID * +GetPageTableEntry ( + IN PHYSICAL_ADDRESS Address, + OUT PAGE_ATTRIBUTE *PageAttribute + ) +{ + INTN Level; + UINTN Index; + UINT64 *PageTable; + UINT64 AddressEncMask; + + AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask); + PageTable = (UINT64 *)(UINTN)(AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64); + for (Level = (INTN)GetPageTableTopLevelType (); Level > 0; --Level) { + Index = (UINTN)RShiftU64 (Address, mPageAttributeTable[Level].AddressBitOffset); + Index &= PAGING_PAE_INDEX_MASK; + + // + // No mapping? + // + if (PageTable[Index] == 0) { + *PageAttribute = PageNone; + return NULL; + } + + // + // Page memory? + // + if ((PageTable[Index] & IA32_PG_PS) != 0 || Level == PageMin) { + *PageAttribute = (PAGE_ATTRIBUTE)Level; + return &PageTable[Index]; + } + + // + // Page directory or table + // + PageTable = (UINT64 *)(UINTN)(PageTable[Index] & + ~AddressEncMask & + PAGING_4K_ADDRESS_MASK_64); + } + + *PageAttribute = PageNone; + return NULL; +} + +/** + This function splits one page entry to smaller page entries. + + @param[in] PageEntry The page entry to be splitted. + @param[in] PageAttribute The page attribute of the page entry. + @param[in] SplitAttribute How to split the page entry. + @param[in] Recursively Do the split recursively or not. + + @retval RETURN_SUCCESS The page entry is splitted. + @retval RETURN_INVALID_PARAMETER If target page attribute is invalid + @retval RETURN_OUT_OF_RESOURCES No resource to split page entry. +**/ +RETURN_STATUS +SplitPage ( +#ifdef VBOX + IN UINT64 volatile *PageEntry, +#else + IN UINT64 *PageEntry, +#endif + IN PAGE_ATTRIBUTE PageAttribute, + IN PAGE_ATTRIBUTE SplitAttribute, + IN BOOLEAN Recursively + ) +{ +#ifdef VBOX + UINT64 CurrentPageEntry; +#endif + UINT64 BaseAddress; + UINT64 *NewPageEntry; + UINTN Index; + UINT64 AddressEncMask; + PAGE_ATTRIBUTE SplitTo; + + if (SplitAttribute == PageNone || SplitAttribute >= PageAttribute) { + ASSERT (SplitAttribute != PageNone); + ASSERT (SplitAttribute < PageAttribute); + return RETURN_INVALID_PARAMETER; + } + + NewPageEntry = AllocatePageTableMemory (1); + if (NewPageEntry == NULL) { + ASSERT (NewPageEntry != NULL); + return RETURN_OUT_OF_RESOURCES; + } + + // + // One level down each step to achieve more compact page table. + // + SplitTo = PageAttribute - 1; + AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & + mPageAttributeTable[SplitTo].AddressMask; +#ifdef VBOX + CurrentPageEntry = *PageEntry; + BaseAddress = CurrentPageEntry & +#else + BaseAddress = *PageEntry & +#endif + ~PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & + mPageAttributeTable[PageAttribute].AddressMask; + for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { + NewPageEntry[Index] = BaseAddress | AddressEncMask | +#ifdef VBOX + (CurrentPageEntry & PAGE_PROGATE_BITS); +#else + ((*PageEntry) & PAGE_PROGATE_BITS); +#endif + + if (SplitTo != PageMin) { + NewPageEntry[Index] |= IA32_PG_PS; + } + + if (Recursively && SplitTo > SplitAttribute) { + SplitPage (&NewPageEntry[Index], SplitTo, SplitAttribute, Recursively); + } + + BaseAddress += mPageAttributeTable[SplitTo].Length; + } + +#ifdef VBOX + SafePageTableEntryWrite64 (PageEntry, CurrentPageEntry, + (UINT64)(UINTN)NewPageEntry | AddressEncMask | PAGE_ATTRIBUTE_BITS); +#else + (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | PAGE_ATTRIBUTE_BITS; +#endif + + return RETURN_SUCCESS; +} + +/** + This function modifies the page attributes for the memory region specified + by BaseAddress and Length from their current attributes to the attributes + specified by Attributes. + + Caller should make sure BaseAddress and Length is at page boundary. + + @param[in] BaseAddress Start address of a memory region. + @param[in] Length Size in bytes of the memory region. + @param[in] Attributes Bit mask of attributes to modify. + + @retval RETURN_SUCCESS The attributes were modified for the memory + region. + @retval RETURN_INVALID_PARAMETER Length is zero; or, + Attributes specified an illegal combination + of attributes that cannot be set together; or + Addressis not 4KB aligned. + @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify + the attributes. + @retval RETURN_UNSUPPORTED Cannot modify the attributes of given memory. + +**/ +RETURN_STATUS +EFIAPI +ConvertMemoryPageAttributes ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ) +{ +#ifdef VBOX + UINT64 volatile *PageEntry; + UINT64 CurrentPageEntry; +#else + UINT64 *PageEntry; +#endif + PAGE_ATTRIBUTE PageAttribute; + RETURN_STATUS Status; + EFI_PHYSICAL_ADDRESS MaximumAddress; + + if (Length == 0 || + (BaseAddress & (SIZE_4KB - 1)) != 0 || + (Length & (SIZE_4KB - 1)) != 0) { + + ASSERT (Length > 0); + ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0); + ASSERT ((Length & (SIZE_4KB - 1)) == 0); + + return RETURN_INVALID_PARAMETER; + } + + MaximumAddress = (EFI_PHYSICAL_ADDRESS)MAX_UINT32; + if (BaseAddress > MaximumAddress || + Length > MaximumAddress || + (BaseAddress > MaximumAddress - (Length - 1))) { + return RETURN_UNSUPPORTED; + } + + // + // Below logic is to check 2M/4K page to make sure we do not waste memory. + // + while (Length != 0) { + PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute); + if (PageEntry == NULL) { + return RETURN_UNSUPPORTED; + } + + if (PageAttribute != Page4K) { + Status = SplitPage (PageEntry, PageAttribute, Page4K, FALSE); + if (RETURN_ERROR (Status)) { + return Status; + } + // + // Do it again until the page is 4K. + // + continue; + } + + // + // Just take care of 'present' bit for Stack Guard. + // +#ifdef VBOX + CurrentPageEntry = *PageEntry; + if ((CurrentPageEntry & IA32_PG_P) != (Attributes & IA32_PG_P)) + SafePageTableEntryWrite64 (PageEntry, CurrentPageEntry, + (CurrentPageEntry & ~(UINT64)IA32_PG_P) | (Attributes & IA32_PG_P)); +#else + if ((Attributes & IA32_PG_P) != 0) { + *PageEntry |= (UINT64)IA32_PG_P; + } else { + *PageEntry &= ~((UINT64)IA32_PG_P); + } +#endif + + // + // Convert success, move to next + // + BaseAddress += SIZE_4KB; + Length -= SIZE_4KB; + } + + return RETURN_SUCCESS; +} + +/** + Get maximum size of page memory supported by current processor. + + @param[in] TopLevelType The type of top level page entry. + + @retval Page1G If processor supports 1G page and PML4. + @retval Page2M For all other situations. + +**/ +PAGE_ATTRIBUTE +GetMaxMemoryPage ( + IN PAGE_ATTRIBUTE TopLevelType + ) +{ + UINT32 RegEax; + UINT32 RegEdx; + + if (TopLevelType == Page512G) { + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_EXTENDED_CPU_SIG) { + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT26) != 0) { + return Page1G; + } + } + } + + return Page2M; +} + +/** + Create PML4 or PAE page table. + + @return The address of page table. + +**/ +UINTN +CreatePageTable ( + VOID + ) +{ + RETURN_STATUS Status; + UINTN PhysicalAddressBits; + UINTN NumberOfEntries; + PAGE_ATTRIBUTE TopLevelPageAttr; + UINTN PageTable; + PAGE_ATTRIBUTE MaxMemoryPage; + UINTN Index; + UINT64 AddressEncMask; + UINT64 *PageEntry; + EFI_PHYSICAL_ADDRESS PhysicalAddress; + + TopLevelPageAttr = (PAGE_ATTRIBUTE)GetPageTableTopLevelType (); + PhysicalAddressBits = GetPhysicalAddressWidth (); + NumberOfEntries = (UINTN)1 << (PhysicalAddressBits - + mPageAttributeTable[TopLevelPageAttr].AddressBitOffset); + + PageTable = (UINTN) AllocatePageTableMemory (1); + if (PageTable == 0) { + return 0; + } + + AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask); + AddressEncMask &= mPageAttributeTable[TopLevelPageAttr].AddressMask; + MaxMemoryPage = GetMaxMemoryPage (TopLevelPageAttr); + PageEntry = (UINT64 *)PageTable; + + PhysicalAddress = 0; + for (Index = 0; Index < NumberOfEntries; ++Index) { + *PageEntry = PhysicalAddress | AddressEncMask | PAGE_ATTRIBUTE_BITS; + + // + // Split the top page table down to the maximum page size supported + // + if (MaxMemoryPage < TopLevelPageAttr) { + Status = SplitPage(PageEntry, TopLevelPageAttr, MaxMemoryPage, TRUE); + ASSERT_EFI_ERROR (Status); + } + + if (TopLevelPageAttr == Page1G) { + // + // PDPTE[2:1] (PAE Paging) must be 0. SplitPage() might change them to 1. + // + *PageEntry &= ~(UINT64)(IA32_PG_RW | IA32_PG_U); + } + + PageEntry += 1; + PhysicalAddress += mPageAttributeTable[TopLevelPageAttr].Length; + } + + + return PageTable; +} + +/** + Setup page tables and make them work. + +**/ +VOID +EnablePaging ( + VOID + ) +{ + UINTN PageTable; + + PageTable = CreatePageTable (); + ASSERT (PageTable != 0); + if (PageTable != 0) { + AsmWriteCr3(PageTable); + AsmWriteCr4 (AsmReadCr4 () | BIT5); // CR4.PAE + AsmWriteCr0 (AsmReadCr0 () | BIT31); // CR0.PG + } +} + +/** + Get the base address of current AP's stack. + + This function is called in AP's context and assumes that whole calling stacks + (till this function) consumed by AP's wakeup procedure will not exceed 4KB. + + PcdCpuApStackSize must be configured with value taking the Guard page into + account. + + @param[in,out] Buffer The pointer to private data buffer. + +**/ +VOID +EFIAPI +GetStackBase ( + IN OUT VOID *Buffer + ) +{ + EFI_PHYSICAL_ADDRESS StackBase; + + StackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)&StackBase; + StackBase += BASE_4KB; + StackBase &= ~((EFI_PHYSICAL_ADDRESS)BASE_4KB - 1); + StackBase -= PcdGet32(PcdCpuApStackSize); + + *(EFI_PHYSICAL_ADDRESS *)Buffer = StackBase; +} + +/** + Setup stack Guard page at the stack base of each processor. BSP and APs have + different way to get stack base address. + +**/ +VOID +SetupStackGuardPage ( + VOID + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_PHYSICAL_ADDRESS StackBase; + UINTN NumberOfProcessors; + UINTN Bsp; + UINTN Index; + + // + // One extra page at the bottom of the stack is needed for Guard page. + // + if (PcdGet32(PcdCpuApStackSize) <= EFI_PAGE_SIZE) { + DEBUG ((DEBUG_ERROR, "PcdCpuApStackSize is not big enough for Stack Guard!\n")); + ASSERT (FALSE); + } + + MpInitLibGetNumberOfProcessors(&NumberOfProcessors, NULL); + MpInitLibWhoAmI (&Bsp); + for (Index = 0; Index < NumberOfProcessors; ++Index) { + StackBase = 0; + + if (Index == Bsp) { + Hob.Raw = GetHobList (); + while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) { + if (CompareGuid (&gEfiHobMemoryAllocStackGuid, + &(Hob.MemoryAllocationStack->AllocDescriptor.Name))) { + StackBase = Hob.MemoryAllocationStack->AllocDescriptor.MemoryBaseAddress; + break; + } + Hob.Raw = GET_NEXT_HOB (Hob); + } + } else { + // + // Ask AP to return is stack base address. + // + MpInitLibStartupThisAP(GetStackBase, Index, NULL, 0, (VOID *)&StackBase, NULL); + } + ASSERT (StackBase != 0); + // + // Set Guard page at stack base address. + // + ConvertMemoryPageAttributes(StackBase, EFI_PAGE_SIZE, 0); + DEBUG ((DEBUG_INFO, "Stack Guard set at %lx [cpu%lu]!\n", + (UINT64)StackBase, (UINT64)Index)); + } + + // + // Publish the changes of page table. + // + CpuFlushTlb (); +} + +/** + Enable/setup stack guard for each processor if PcdCpuStackGuard is set to TRUE. + + Doing this in the memory-discovered callback is to make sure the Stack Guard + feature to cover as most PEI code as possible. + + @param[in] PeiServices General purpose services available to every PEIM. + @param[in] NotifyDescriptor The notification structure this PEIM registered on install. + @param[in] Ppi The memory discovered PPI. Not used. + + @retval EFI_SUCCESS The function completed successfully. + @retval others There's error in MP initialization. +**/ +EFI_STATUS +EFIAPI +MemoryDiscoveredPpiNotifyCallback ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ) +{ + EFI_STATUS Status; + BOOLEAN InitStackGuard; + EDKII_MIGRATED_FV_INFO *MigratedFvInfo; + EFI_PEI_HOB_POINTERS Hob; + + // + // Paging must be setup first. Otherwise the exception TSS setup during MP + // initialization later will not contain paging information and then fail + // the task switch (for the sake of stack switch). + // + InitStackGuard = FALSE; + Hob.Raw = NULL; + if (IsIa32PaeSupported ()) { + Hob.Raw = GetFirstGuidHob (&gEdkiiMigratedFvInfoGuid); + InitStackGuard = PcdGetBool (PcdCpuStackGuard); + } + + if (InitStackGuard || Hob.Raw != NULL) { + EnablePaging (); + } + + Status = InitializeCpuMpWorker ((CONST EFI_PEI_SERVICES **)PeiServices); + ASSERT_EFI_ERROR (Status); + + if (InitStackGuard) { + SetupStackGuardPage (); + } + + while (Hob.Raw != NULL) { + MigratedFvInfo = GET_GUID_HOB_DATA (Hob); + + // + // Enable #PF exception, so if the code access SPI after disable NEM, it will generate + // the exception to avoid potential vulnerability. + // + ConvertMemoryPageAttributes (MigratedFvInfo->FvOrgBase, MigratedFvInfo->FvLength, 0); + + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextGuidHob (&gEdkiiMigratedFvInfoGuid, Hob.Raw); + } + CpuFlushTlb (); + + return Status; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuS3DataDxe/CpuS3Data.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuS3DataDxe/CpuS3Data.c new file mode 100644 index 00000000..040d23e8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuS3DataDxe/CpuS3Data.c @@ -0,0 +1,277 @@ +/** @file +ACPI CPU Data initialization module + +This module initializes the ACPI_CPU_DATA structure and registers the address +of this structure in the PcdCpuS3DataAddress PCD. This is a generic/simple +version of this module. It does not provide a machine check handler or CPU +register initialization tables for ACPI S3 resume. It also only supports the +number of CPUs reported by the MP Services Protocol, so this module does not +support hot plug CPUs. This module can be copied into a CPU specific package +and customized if these additional features are required. + +Copyright (c) 2013 - 2017, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2015, Red Hat, Inc. + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> + +#include <AcpiCpuData.h> + +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/DebugLib.h> +#include <Library/MtrrLib.h> +#include <Library/MemoryAllocationLib.h> + +#include <Protocol/MpService.h> +#include <Guid/EventGroup.h> + +// +// Data structure used to allocate ACPI_CPU_DATA and its supporting structures +// +typedef struct { + ACPI_CPU_DATA AcpiCpuData; + MTRR_SETTINGS MtrrTable; + IA32_DESCRIPTOR GdtrProfile; + IA32_DESCRIPTOR IdtrProfile; +} ACPI_CPU_DATA_EX; + +/** + Allocate EfiACPIMemoryNVS memory. + + @param[in] Size Size of memory to allocate. + + @return Allocated address for output. + +**/ +VOID * +AllocateAcpiNvsMemory ( + IN UINTN Size + ) +{ + EFI_PHYSICAL_ADDRESS Address; + EFI_STATUS Status; + VOID *Buffer; + + Status = gBS->AllocatePages ( + AllocateAnyPages, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES (Size), + &Address + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + Buffer = (VOID *)(UINTN)Address; + ZeroMem (Buffer, Size); + + return Buffer; +} + +/** + Allocate memory and clean it with zero. + + @param[in] Size Size of memory to allocate. + + @return Allocated address for output. + +**/ +VOID * +AllocateZeroPages ( + IN UINTN Size + ) +{ + VOID *Buffer; + + Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Size)); + if (Buffer != NULL) { + ZeroMem (Buffer, Size); + } + + return Buffer; +} +/** + Callback function executed when the EndOfDxe event group is signaled. + + We delay allocating StartupVector and saving the MTRR settings until BDS signals EndOfDxe. + + @param[in] Event Event whose notification function is being invoked. + @param[out] Context Pointer to the MTRR_SETTINGS buffer to fill in. +**/ +VOID +EFIAPI +CpuS3DataOnEndOfDxe ( + IN EFI_EVENT Event, + OUT VOID *Context + ) +{ + EFI_STATUS Status; + ACPI_CPU_DATA_EX *AcpiCpuDataEx; + + AcpiCpuDataEx = (ACPI_CPU_DATA_EX *) Context; + // + // Allocate a 4KB reserved page below 1MB + // + AcpiCpuDataEx->AcpiCpuData.StartupVector = BASE_1MB - 1; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiReservedMemoryType, + 1, + &AcpiCpuDataEx->AcpiCpuData.StartupVector + ); + ASSERT_EFI_ERROR (Status); + + DEBUG ((EFI_D_VERBOSE, "%a\n", __FUNCTION__)); + MtrrGetAllMtrrs (&AcpiCpuDataEx->MtrrTable); + + // + // Close event, so it will not be invoked again. + // + gBS->CloseEvent (Event); +} + +/** + The entry function of the CpuS3Data driver. + + Allocate and initialize all fields of the ACPI_CPU_DATA structure except the + MTRR settings. Register an event notification on gEfiEndOfDxeEventGroupGuid + to capture the ACPI_CPU_DATA MTRR settings. The PcdCpuS3DataAddress is set + to the address that ACPI_CPU_DATA is allocated at. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval EFI_UNSUPPORTED Do not support ACPI S3. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +CpuS3DataInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + ACPI_CPU_DATA_EX *AcpiCpuDataEx; + ACPI_CPU_DATA *AcpiCpuData; + EFI_MP_SERVICES_PROTOCOL *MpServices; + UINTN NumberOfCpus; + UINTN NumberOfEnabledProcessors; + VOID *Stack; + UINTN GdtSize; + UINTN IdtSize; + VOID *Gdt; + VOID *Idt; + EFI_EVENT Event; + ACPI_CPU_DATA *OldAcpiCpuData; + + if (!PcdGetBool (PcdAcpiS3Enable)) { + return EFI_UNSUPPORTED; + } + + // + // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure + // + OldAcpiCpuData = (ACPI_CPU_DATA *) (UINTN) PcdGet64 (PcdCpuS3DataAddress); + + AcpiCpuDataEx = AllocateZeroPages (sizeof (ACPI_CPU_DATA_EX)); + ASSERT (AcpiCpuDataEx != NULL); + AcpiCpuData = &AcpiCpuDataEx->AcpiCpuData; + + // + // Get MP Services Protocol + // + Status = gBS->LocateProtocol ( + &gEfiMpServiceProtocolGuid, + NULL, + (VOID **)&MpServices + ); + ASSERT_EFI_ERROR (Status); + + // + // Get the number of CPUs + // + Status = MpServices->GetNumberOfProcessors ( + MpServices, + &NumberOfCpus, + &NumberOfEnabledProcessors + ); + ASSERT_EFI_ERROR (Status); + AcpiCpuData->NumberOfCpus = (UINT32)NumberOfCpus; + + // + // Initialize ACPI_CPU_DATA fields + // + AcpiCpuData->StackSize = PcdGet32 (PcdCpuApStackSize); + AcpiCpuData->ApMachineCheckHandlerBase = 0; + AcpiCpuData->ApMachineCheckHandlerSize = 0; + AcpiCpuData->GdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->GdtrProfile; + AcpiCpuData->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->IdtrProfile; + AcpiCpuData->MtrrTable = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->MtrrTable; + + // + // Allocate stack space for all CPUs. + // Use ACPI NVS memory type because this data will be directly used by APs + // in S3 resume phase in long mode. Also during S3 resume, the stack buffer + // will only be used as scratch space. i.e. we won't read anything from it + // before we write to it, in PiSmmCpuDxeSmm. + // + Stack = AllocateAcpiNvsMemory (NumberOfCpus * AcpiCpuData->StackSize); + ASSERT (Stack != NULL); + AcpiCpuData->StackAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Stack; + + // + // Get the boot processor's GDT and IDT + // + AsmReadGdtr (&AcpiCpuDataEx->GdtrProfile); + AsmReadIdtr (&AcpiCpuDataEx->IdtrProfile); + + // + // Allocate GDT and IDT and copy current GDT and IDT contents + // + GdtSize = AcpiCpuDataEx->GdtrProfile.Limit + 1; + IdtSize = AcpiCpuDataEx->IdtrProfile.Limit + 1; + Gdt = AllocateZeroPages (GdtSize + IdtSize); + ASSERT (Gdt != NULL); + Idt = (VOID *)((UINTN)Gdt + GdtSize); + CopyMem (Gdt, (VOID *)AcpiCpuDataEx->GdtrProfile.Base, GdtSize); + CopyMem (Idt, (VOID *)AcpiCpuDataEx->IdtrProfile.Base, IdtSize); + AcpiCpuDataEx->GdtrProfile.Base = (UINTN)Gdt; + AcpiCpuDataEx->IdtrProfile.Base = (UINTN)Idt; + + if (OldAcpiCpuData != NULL) { + AcpiCpuData->RegisterTable = OldAcpiCpuData->RegisterTable; + AcpiCpuData->PreSmmInitRegisterTable = OldAcpiCpuData->PreSmmInitRegisterTable; + AcpiCpuData->ApLocation = OldAcpiCpuData->ApLocation; + CopyMem (&AcpiCpuData->CpuStatus, &OldAcpiCpuData->CpuStatus, sizeof (CPU_STATUS_INFORMATION)); + } + + // + // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure + // + Status = PcdSet64S (PcdCpuS3DataAddress, (UINT64)(UINTN)AcpiCpuData); + ASSERT_EFI_ERROR (Status); + + // + // Register EFI_END_OF_DXE_EVENT_GROUP_GUID event. + // The notification function allocates StartupVector and saves MTRRs for ACPI_CPU_DATA + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + CpuS3DataOnEndOfDxe, + AcpiCpuData, + &gEfiEndOfDxeEventGroupGuid, + &Event + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxe.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxe.inf new file mode 100644 index 00000000..b0645a7c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxe.inf @@ -0,0 +1,65 @@ +## @file +# ACPI CPU Data initialization module +# +# This module initializes the ACPI_CPU_DATA structure and registers the address +# of this structure in the PcdCpuS3DataAddress PCD. This is a generic/simple +# version of this module. It does not provide a machine check handler or CPU +# register initialization tables for ACPI S3 resume. It also only supports the +# number of CPUs reported by the MP Services Protocol, so this module does not +# support hot plug CPUs. This module can be copied into a CPU specific package +# and customized if these additional features are required. +# +# Copyright (c) 2013-2016, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2015, Red Hat, Inc. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuS3DataDxe + MODULE_UNI_FILE = CpuS3DataDxe.uni + FILE_GUID = 4D2E57EE-0E3F-44DD-93C4-D3B57E96945D + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = CpuS3DataInitialize + +# The following information is for reference only and not required by the build +# tools. +# +# VALID_ARCHITECTURES = IA32 X64 + +[Sources] + CpuS3Data.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + BaseMemoryLib + DebugLib + BaseLib + MtrrLib + MemoryAllocationLib + +[Guids] + gEfiEndOfDxeEventGroupGuid ## CONSUMES ## Event + +[Protocols] + gEfiMpServiceProtocolGuid ## CONSUMES + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuS3DataAddress ## PRODUCES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable ## CONSUMES + +[Depex] + gEfiMpServiceProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + CpuS3DataDxeExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxe.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxe.uni new file mode 100644 index 00000000..d16c03fc --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxe.uni @@ -0,0 +1,34 @@ +// /** @file
+// ACPI CPU Data initialization module
+//
+// This module initializes the ACPI_CPU_DATA structure and registers the address
+// of this structure in the PcdCpuS3DataAddress PCD. This is a generic/simple
+// version of this module. It does not provide a machine check handler or CPU
+// register initialization tables for ACPI S3 resume. It also only supports the
+// number of CPUs reported by the MP Services Protocol, so this module does not
+// support hot plug CPUs. This module can be copied into a CPU specific package
+// and customized if these additional features are required.
+//
+// Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+//
+// Copyright (c) 2015, Red Hat, Inc.
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT
+#language en-US
+"ACPI CPU Data initialization module"
+
+#string STR_MODULE_DESCRIPTION
+#language en-US
+"This module initializes the ACPI_CPU_DATA structure and registers the address "
+"of this structure in the PcdCpuS3DataAddress PCD. This is a generic/simple "
+"version of this module. It does not provide a machine check handler or CPU "
+"register initialization tables for ACPI S3 resume. It also only supports the "
+"number of CPUs reported by the MP Services Protocol, so this module does not "
+"support hot plug CPUs. This module can be copied into a CPU specific package "
+"and customized if these additional features are required."
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxeExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxeExtra.uni new file mode 100644 index 00000000..42653f20 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// CpuS3DataDxe Localized Strings and Content
+//
+// Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+//
+// Copyright (c) 2015, Red Hat, Inc.
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME #language en-US "CpuS3DataDxe module"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/AcpiCpuData.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/AcpiCpuData.h new file mode 100644 index 00000000..6265a47a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/AcpiCpuData.h @@ -0,0 +1,226 @@ +/** @file +Definitions for CPU S3 data. + +Copyright (c) 2013 - 2020, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _ACPI_CPU_DATA_H_ +#define _ACPI_CPU_DATA_H_ + +// +// Register types in register table +// +typedef enum { + Msr, + ControlRegister, + MemoryMapped, + CacheControl, + + // + // Semaphore type used to control the execute sequence of the Msr. + // It will be insert between two Msr which has execute dependence. + // + Semaphore, + InvalidReg +} REGISTER_TYPE; + +// +// Describe the dependency type for different features. +// The value set to CPU_REGISTER_TABLE_ENTRY.Value when the REGISTER_TYPE is Semaphore. +// +typedef enum { + NoneDepType, + ThreadDepType, + CoreDepType, + PackageDepType, + InvalidDepType +} CPU_FEATURE_DEPENDENCE_TYPE; + +// +// CPU information. +// +typedef struct { + // + // Record the package count in this CPU. + // + UINT32 PackageCount; + // + // Record the max core count in this CPU. + // Different packages may have different core count, this value + // save the max core count in all the packages. + // + UINT32 MaxCoreCount; + // + // Record the max thread count in this CPU. + // Different cores may have different thread count, this value + // save the max thread count in all the cores. + // + UINT32 MaxThreadCount; + // + // This field points to an array. + // This array saves thread count (type UINT32) of each package. + // The array has PackageCount elements. + // + // If the platform does not support MSR setting at S3 resume, and + // therefore it doesn't need the dependency semaphores, it should set + // this field to 0. + // + EFI_PHYSICAL_ADDRESS ThreadCountPerPackage; + // + // This field points to an array. + // This array saves thread count (type UINT8) of each core. + // The array has PackageCount * MaxCoreCount elements. + // + // If the platform does not support MSR setting at S3 resume, and + // therefore it doesn't need the dependency semaphores, it should set + // this field to 0. + // + EFI_PHYSICAL_ADDRESS ThreadCountPerCore; +} CPU_STATUS_INFORMATION; + +// +// Element of register table entry +// +typedef struct { + REGISTER_TYPE RegisterType; // offset 0 - 3 + UINT32 Index; // offset 4 - 7 + UINT8 ValidBitStart; // offset 8 + UINT8 ValidBitLength; // offset 9 + BOOLEAN TestThenWrite; // offset 10 + UINT8 Reserved1; // offset 11 + UINT32 HighIndex; // offset 12-15, only valid for MemoryMapped + UINT64 Value; // offset 16-23 +} CPU_REGISTER_TABLE_ENTRY; + +// +// Register table definition, including current table length, +// allocated size of this table, and pointer to the list of table entries. +// +typedef struct { + // + // The number of valid entries in the RegisterTableEntry buffer + // + UINT32 TableLength; + UINT32 NumberBeforeReset; + // + // The size, in bytes, of the RegisterTableEntry buffer + // + UINT32 AllocatedSize; + // + // The initial APIC ID of the CPU this register table applies to + // + UINT32 InitialApicId; + // + // Physical address of CPU_REGISTER_TABLE_ENTRY structures. + // + EFI_PHYSICAL_ADDRESS RegisterTableEntry; +} CPU_REGISTER_TABLE; + +// +// Data structure that is required for ACPI S3 resume. The PCD +// PcdCpuS3DataAddress must be set to the physical address where this structure +// is allocated +// +typedef struct { + // + // Physical address of 4KB buffer allocated below 1MB from memory of type + // EfiReservedMemoryType. The buffer is not required to be initialized, but + // it is recommended that the buffer be zero-filled. This buffer is used to + // wake APs during an ACPI S3 resume. + // + EFI_PHYSICAL_ADDRESS StartupVector; + // + // Physical address of structure of type IA32_DESCRIPTOR. The + // IA32_DESCRIPTOR structure provides the base address and length of a GDT + // The GDT must be filled in with the GDT contents that are + // used during an ACPI S3 resume. This is typically the contents of the GDT + // used by the boot processor when the platform is booted. + // + EFI_PHYSICAL_ADDRESS GdtrProfile; + // + // Physical address of structure of type IA32_DESCRIPTOR. The + // IA32_DESCRIPTOR structure provides the base address and length of an IDT. + // The IDT must be filled in with the IDT contents that are + // used during an ACPI S3 resume. This is typically the contents of the IDT + // used by the boot processor when the platform is booted. + // + EFI_PHYSICAL_ADDRESS IdtrProfile; + // + // Physical address of a buffer that is used as stacks during ACPI S3 resume. + // The total size of this buffer, in bytes, is NumberOfCpus * StackSize. This + // structure must be allocated from memory of type EfiACPIMemoryNVS. + // + EFI_PHYSICAL_ADDRESS StackAddress; + // + // The size, in bytes, of the stack provided to each CPU during ACPI S3 resume. + // + UINT32 StackSize; + // + // The number of CPUs. If a platform does not support hot plug CPUs, then + // this is the number of CPUs detected when the platform is booted, regardless + // of being enabled or disabled. If a platform does support hot plug CPUs, + // then this is the maximum number of CPUs that the platform supports. + // + UINT32 NumberOfCpus; + // + // Physical address of structure of type MTRR_SETTINGS that contains a copy + // of the MTRR settings that are compatible with the MTRR settings used by + // the boot processor when the platform was booted. These MTRR settings are + // used during an ACPI S3 resume. + // + EFI_PHYSICAL_ADDRESS MtrrTable; + // + // Physical address of an array of CPU_REGISTER_TABLE structures, with + // NumberOfCpus entries. If a register table is not required, then the + // TableLength and AllocatedSize fields of CPU_REGISTER_TABLE are set to 0. + // If TableLength is > 0, then elements of RegisterTableEntry are used to + // initialize the CPU that matches InitialApicId, during an ACPI S3 resume, + // before SMBASE relocation is performed. + // If a register table is not required for any one of the CPUs, then + // PreSmmInitRegisterTable may be set to 0. + // + EFI_PHYSICAL_ADDRESS PreSmmInitRegisterTable; + // + // Physical address of an array of CPU_REGISTER_TABLE structures, with + // NumberOfCpus entries. If a register table is not required, then the + // TableLength and AllocatedSize fields of CPU_REGISTER_TABLE are set to 0. + // If TableLength is > 0, then elements of RegisterTableEntry are used to + // initialize the CPU that matches InitialApicId, during an ACPI S3 resume, + // after SMBASE relocation is performed. + // If a register table is not required for any one of the CPUs, then + // RegisterTable may be set to 0. + // + EFI_PHYSICAL_ADDRESS RegisterTable; + // + // Physical address of a buffer that contains the machine check handler that + // is used during an ACPI S3 Resume. In order for this machine check + // handler to be active on an AP during an ACPI S3 resume, the machine check + // vector in the IDT provided by IdtrProfile must be initialized to transfer + // control to this physical address. + // + EFI_PHYSICAL_ADDRESS ApMachineCheckHandlerBase; + // + // The size, in bytes, of the machine check handler that is used during an + // ACPI S3 Resume. If this field is 0, then a machine check handler is not + // provided. + // + UINT32 ApMachineCheckHandlerSize; + // + // CPU information which is required when set the register table. + // + CPU_STATUS_INFORMATION CpuStatus; + // + // Location info for each AP. + // It points to an array which saves all APs location info. + // The array count is the AP count in this CPU. + // + // If the platform does not support MSR setting at S3 resume, and + // therefore it doesn't need the dependency semaphores, it should set + // this field to 0. + // + EFI_PHYSICAL_ADDRESS ApLocation; +} ACPI_CPU_DATA; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/CpuHotPlugData.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/CpuHotPlugData.h new file mode 100644 index 00000000..317d9257 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/CpuHotPlugData.h @@ -0,0 +1,27 @@ +/** @file +Definition for a structure sharing information for CPU hot plug. + +Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_HOT_PLUG_DATA_H_ +#define _CPU_HOT_PLUG_DATA_H_ + +#define CPU_HOT_PLUG_DATA_REVISION_1 0x00000001 + +typedef struct { + UINT32 Revision; // Used for version identification for this structure + UINT32 ArrayLength; // The entries number of the following ApicId array and SmBase array + // + // Data required for SMBASE relocation + // + UINT64 *ApicId; // Pointer to ApicId array + UINTN *SmBase; // Pointer to SmBase array + UINT32 Reserved; + UINT32 SmrrBase; + UINT32 SmrrSize; +} CPU_HOT_PLUG_DATA; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Guid/CpuFeaturesInitDone.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Guid/CpuFeaturesInitDone.h new file mode 100644 index 00000000..6922dae4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Guid/CpuFeaturesInitDone.h @@ -0,0 +1,20 @@ +/** @file + CPU Features Init Done PPI/Protocol should be installed after CPU features + are initialized. + +Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_FEATURES_INIT_DONE_H_ +#define _CPU_FEATURES_INIT_DONE_H_ + +#define EDKII_CPU_FEATURES_INIT_DONE_GUID \ + { \ + { 0xc77c3a41, 0x61ab, 0x4143, { 0x98, 0x3e, 0x33, 0x39, 0x28, 0x6, 0x28, 0xe5 } \ + } + +extern EFI_GUID gEdkiiCpuFeaturesInitDoneGuid; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Guid/CpuFeaturesSetDone.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Guid/CpuFeaturesSetDone.h new file mode 100644 index 00000000..66a2507e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Guid/CpuFeaturesSetDone.h @@ -0,0 +1,20 @@ +/** @file + CPU Features Set Done PPI/Protocol should be installed after CPU features + configuration are set. + +Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_FEATURES_INIT_DONE_H_ +#define _CPU_FEATURES_INIT_DONE_H_ + +#define EDKII_CPU_FEATURES_SET_DONE_GUID \ + { \ + { 0xa82485ce, 0xad6b, 0x4101, { 0x99, 0xd3, 0xe1, 0x35, 0x8c, 0x9e, 0x7e, 0x37 } \ + } + +extern EFI_GUID gEdkiiCpuFeaturesSetDoneGuid; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Guid/MicrocodePatchHob.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Guid/MicrocodePatchHob.h new file mode 100644 index 00000000..d20ffd58 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Guid/MicrocodePatchHob.h @@ -0,0 +1,44 @@ +/** @file + The microcode patch HOB is used to store the information of: + A. Base address and size of the loaded microcode patches data; + B. Detected microcode patch for each processor within system. + + Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _MICROCODE_PATCH_HOB_H_ +#define _MICROCODE_PATCH_HOB_H_ + +extern EFI_GUID gEdkiiMicrocodePatchHobGuid; + +// +// The EDKII microcode patch HOB will be produced by MpInitLib and it can be +// consumed by modules that want to detect/apply microcode patches. +// +typedef struct { + // + // The base address of the microcode patches data after being loaded into + // memory. + // + UINT64 MicrocodePatchAddress; + // + // The total size of the loaded microcode patches. + // + UINT64 MicrocodePatchRegionSize; + // + // The number of processors within the system. + // + UINT32 ProcessorCount; + // + // An array with 'ProcessorCount' elements that stores the offset (with + // regard to 'MicrocodePatchAddress') of the detected microcode patch + // (including the CPU_MICROCODE_HEADER data structure) for each processor. + // If no microcode patch is detected for certain processor, the relating + // element will be set to MAX_UINT64. + // + UINT64 ProcessorSpecificPatchOffset[0]; +} EDKII_MICROCODE_PATCH_HOB; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Guid/MsegSmram.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Guid/MsegSmram.h new file mode 100644 index 00000000..d2ad33f9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Guid/MsegSmram.h @@ -0,0 +1,24 @@ +/** @file + + Defines the HOB GUID used to describe the MSEG memory region allocated in PEI. + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _MSEG_SMRAM_H_ +#define _MSEG_SMRAM_H_ + +#define MSEG_SMRAM_GUID \ + { \ + 0x5802bce4, 0xeeee, 0x4e33, { 0xa1, 0x30, 0xeb, 0xad, 0x27, 0xf0, 0xe4, 0x39 } \ + } + +extern EFI_GUID gMsegSmramGuid; + +// +// The data portion of this HOB is type EFI_SMRAM_DESCRIPTOR +// + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/CpuCacheInfoLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/CpuCacheInfoLib.h new file mode 100644 index 00000000..67b0fb40 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/CpuCacheInfoLib.h @@ -0,0 +1,85 @@ +/** @file + Header file for CPU Cache info Library. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_CACHE_INFO_LIB_H_ +#define _CPU_CACHE_INFO_LIB_H_ + +typedef struct { + // + // Package number. + // + UINT32 Package; + // + // Core type of logical processor. + // Value = CPUID.1Ah:EAX[31:24] + // + UINT8 CoreType; + // + // Level of the cache that this package's this type of logical processor corresponds to. + // Value = CPUID.04h:EAX[07:05] + // + UINT8 CacheLevel : 3; + // + // Type of the cache that this package's this type of logical processor corresponds to. + // Value = CPUID.04h:EAX[04:00] + // + UINT8 CacheType : 5; + // + // Ways of associativity. + // Value = CPUID.04h:EBX[31:22] + // + UINT16 CacheWays : 10; + // + // Fully associative cache. + // Value = CPUID.04h:EAX[09] + // + UINT16 FullyAssociativeCache : 1; + // + // Direct mapped cache. + // Value = CPUID.04h:EDX[02] + // + UINT16 DirectMappedCache : 1; + UINT16 Reserved : 4; + // + // Size of single cache that this package's this type of logical processor corresponds to. + // Value = (CPUID.04h:EBX[31:22] + 1) * (CPUID.04h:EBX[21:12] + 1) * + // (CPUID.04h:EBX[11:00] + 1) * (CPUID.04h:ECX[31:00] + 1) + // + UINT32 CacheSizeinKB; + // + // Number of the cache that this package's this type of logical processor corresponds to. + // Have subtracted the number of caches that are shared. + // + UINT16 CacheCount; +} CPU_CACHE_INFO; + +/** + Get CpuCacheInfo data array. + + @param[in, out] CpuCacheInfo Pointer to the CpuCacheInfo array. + @param[in, out] CpuCacheInfoCount As input, point to the length of response CpuCacheInfo array. + As output, point to the actual length of response CpuCacheInfo array. + + @retval EFI_SUCCESS Function completed successfully. + @retval EFI_INVALID_PARAMETER CpuCacheInfoCount is NULL. + @retval EFI_INVALID_PARAMETER CpuCacheInfo is NULL while CpuCacheInfoCount contains the value + greater than zero. + @retval EFI_UNSUPPORTED Processor does not support CPUID_CACHE_PARAMS Leaf. + @retval EFI_OUT_OF_RESOURCES Required resources could not be allocated. + @retval EFI_BUFFER_TOO_SMALL CpuCacheInfoCount is too small to hold the response CpuCacheInfo + array. CpuCacheInfoCount has been updated with the length needed + to complete the request. +**/ +EFI_STATUS +EFIAPI +GetCpuCacheInfo ( + IN OUT CPU_CACHE_INFO *CpuCacheInfo, + IN OUT UINTN *CpuCacheInfoCount + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/LocalApicLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/LocalApicLib.h new file mode 100644 index 00000000..41dc66c1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/LocalApicLib.h @@ -0,0 +1,457 @@ +/** @file + Public include file for Local APIC library. + + Local APIC library assumes local APIC is enabled. It does not + handles cases where local APIC is disabled. + + Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __LOCAL_APIC_LIB_H__ +#define __LOCAL_APIC_LIB_H__ + +#define LOCAL_APIC_MODE_XAPIC 0x1 ///< xAPIC mode. +#define LOCAL_APIC_MODE_X2APIC 0x2 ///< x2APIC mode. + +/** + Retrieve the base address of local APIC. + + @return The base address of local APIC. + +**/ +UINTN +EFIAPI +GetLocalApicBaseAddress ( + VOID + ); + +/** + Set the base address of local APIC. + + If BaseAddress is not aligned on a 4KB boundary, then ASSERT(). + + @param[in] BaseAddress Local APIC base address to be set. + +**/ +VOID +EFIAPI +SetLocalApicBaseAddress ( + IN UINTN BaseAddress + ); + +/** + Get the current local APIC mode. + + If local APIC is disabled, then ASSERT. + + @retval LOCAL_APIC_MODE_XAPIC current APIC mode is xAPIC. + @retval LOCAL_APIC_MODE_X2APIC current APIC mode is x2APIC. +**/ +UINTN +EFIAPI +GetApicMode ( + VOID + ); + +/** + Set the current local APIC mode. + + If the specified local APIC mode is not valid, then ASSERT. + If the specified local APIC mode can't be set as current, then ASSERT. + + @param ApicMode APIC mode to be set. + + @note This API must not be called from an interrupt handler or SMI handler. + It may result in unpredictable behavior. +**/ +VOID +EFIAPI +SetApicMode ( + IN UINTN ApicMode + ); + +/** + Get the initial local APIC ID of the executing processor assigned by hardware upon power on or reset. + + In xAPIC mode, the initial local APIC ID may be different from current APIC ID. + In x2APIC mode, the local APIC ID can't be changed and there is no concept of initial APIC ID. In this case, + the 32-bit local APIC ID is returned as initial APIC ID. + + @return 32-bit initial local APIC ID of the executing processor. +**/ +UINT32 +EFIAPI +GetInitialApicId ( + VOID + ); + +/** + Get the local APIC ID of the executing processor. + + @return 32-bit local APIC ID of the executing processor. +**/ +UINT32 +EFIAPI +GetApicId ( + VOID + ); + +/** + Get the value of the local APIC version register. + + @return the value of the local APIC version register. +**/ +UINT32 +EFIAPI +GetApicVersion ( + VOID + ); + +/** + Send a Fixed IPI to a specified target processor. + + This function returns after the IPI has been accepted by the target processor. + + @param ApicId The local APIC ID of the target processor. + @param Vector The vector number of the interrupt being sent. +**/ +VOID +EFIAPI +SendFixedIpi ( + IN UINT32 ApicId, + IN UINT8 Vector + ); + +/** + Send a Fixed IPI to all processors excluding self. + + This function returns after the IPI has been accepted by the target processors. + + @param Vector The vector number of the interrupt being sent. +**/ +VOID +EFIAPI +SendFixedIpiAllExcludingSelf ( + IN UINT8 Vector + ); + +/** + Send a SMI IPI to a specified target processor. + + This function returns after the IPI has been accepted by the target processor. + + @param ApicId Specify the local APIC ID of the target processor. +**/ +VOID +EFIAPI +SendSmiIpi ( + IN UINT32 ApicId + ); + +/** + Send a SMI IPI to all processors excluding self. + + This function returns after the IPI has been accepted by the target processors. +**/ +VOID +EFIAPI +SendSmiIpiAllExcludingSelf ( + VOID + ); + +/** + Send an INIT IPI to a specified target processor. + + This function returns after the IPI has been accepted by the target processor. + + @param ApicId Specify the local APIC ID of the target processor. +**/ +VOID +EFIAPI +SendInitIpi ( + IN UINT32 ApicId + ); + +/** + Send an INIT IPI to all processors excluding self. + + This function returns after the IPI has been accepted by the target processors. +**/ +VOID +EFIAPI +SendInitIpiAllExcludingSelf ( + VOID + ); + +/** + Send an INIT-Start-up-Start-up IPI sequence to a specified target processor. + + This function returns after the IPI has been accepted by the target processor. + + if StartupRoutine >= 1M, then ASSERT. + if StartupRoutine is not multiple of 4K, then ASSERT. + + @param ApicId Specify the local APIC ID of the target processor. + @param StartupRoutine Points to a start-up routine which is below 1M physical + address and 4K aligned. +**/ +VOID +EFIAPI +SendInitSipiSipi ( + IN UINT32 ApicId, + IN UINT32 StartupRoutine + ); + +/** + Send an INIT-Start-up-Start-up IPI sequence to all processors excluding self. + + This function returns after the IPI has been accepted by the target processors. + + if StartupRoutine >= 1M, then ASSERT. + if StartupRoutine is not multiple of 4K, then ASSERT. + + @param StartupRoutine Points to a start-up routine which is below 1M physical + address and 4K aligned. +**/ +VOID +EFIAPI +SendInitSipiSipiAllExcludingSelf ( + IN UINT32 StartupRoutine + ); + +/** + Initialize the state of the SoftwareEnable bit in the Local APIC + Spurious Interrupt Vector register. + + @param Enable If TRUE, then set SoftwareEnable to 1 + If FALSE, then set SoftwareEnable to 0. + +**/ +VOID +EFIAPI +InitializeLocalApicSoftwareEnable ( + IN BOOLEAN Enable + ); + +/** + Programming Virtual Wire Mode. + + This function programs the local APIC for virtual wire mode following + the example described in chapter A.3 of the MP 1.4 spec. + + IOxAPIC is not involved in this type of virtual wire mode. +**/ +VOID +EFIAPI +ProgramVirtualWireMode ( + VOID + ); + +/** + Disable LINT0 & LINT1 interrupts. + + This function sets the mask flag in the LVT LINT0 & LINT1 registers. +**/ +VOID +EFIAPI +DisableLvtInterrupts ( + VOID + ); + +/** + Read the initial count value from the init-count register. + + @return The initial count value read from the init-count register. +**/ +UINT32 +EFIAPI +GetApicTimerInitCount ( + VOID + ); + +/** + Read the current count value from the current-count register. + + @return The current count value read from the current-count register. +**/ +UINT32 +EFIAPI +GetApicTimerCurrentCount ( + VOID + ); + +/** + Initialize the local APIC timer. + + The local APIC timer is initialized and enabled. + + @param DivideValue The divide value for the DCR. It is one of 1,2,4,8,16,32,64,128. + If it is 0, then use the current divide value in the DCR. + @param InitCount The initial count value. + @param PeriodicMode If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot. + @param Vector The timer interrupt vector number. +**/ +VOID +EFIAPI +InitializeApicTimer ( + IN UINTN DivideValue, + IN UINT32 InitCount, + IN BOOLEAN PeriodicMode, + IN UINT8 Vector + ); + +/** + Get the state of the local APIC timer. + + @param DivideValue Return the divide value for the DCR. It is one of 1,2,4,8,16,32,64,128. + @param PeriodicMode Return the timer mode. If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot. + @param Vector Return the timer interrupt vector number. +**/ +VOID +EFIAPI +GetApicTimerState ( + OUT UINTN *DivideValue OPTIONAL, + OUT BOOLEAN *PeriodicMode OPTIONAL, + OUT UINT8 *Vector OPTIONAL + ); + +/** + Enable the local APIC timer interrupt. +**/ +VOID +EFIAPI +EnableApicTimerInterrupt ( + VOID + ); + +/** + Disable the local APIC timer interrupt. +**/ +VOID +EFIAPI +DisableApicTimerInterrupt ( + VOID + ); + +/** + Get the local APIC timer interrupt state. + + @retval TRUE The local APIC timer interrupt is enabled. + @retval FALSE The local APIC timer interrupt is disabled. +**/ +BOOLEAN +EFIAPI +GetApicTimerInterruptState ( + VOID + ); + +/** + Send EOI to the local APIC. +**/ +VOID +EFIAPI +SendApicEoi ( + VOID + ); + +/** + Get the 32-bit address that a device should use to send a Message Signaled + Interrupt (MSI) to the Local APIC of the currently executing processor. + + @return 32-bit address used to send an MSI to the Local APIC. +**/ +UINT32 +EFIAPI +GetApicMsiAddress ( + VOID + ); + +/** + Get the 64-bit data value that a device should use to send a Message Signaled + Interrupt (MSI) to the Local APIC of the currently executing processor. + + If Vector is not in range 0x10..0xFE, then ASSERT(). + If DeliveryMode is not supported, then ASSERT(). + + @param Vector The 8-bit interrupt vector associated with the MSI. + Must be in the range 0x10..0xFE + @param DeliveryMode A 3-bit value that specifies how the recept of the MSI + is handled. The only supported values are: + 0: LOCAL_APIC_DELIVERY_MODE_FIXED + 1: LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY + 2: LOCAL_APIC_DELIVERY_MODE_SMI + 4: LOCAL_APIC_DELIVERY_MODE_NMI + 5: LOCAL_APIC_DELIVERY_MODE_INIT + 7: LOCAL_APIC_DELIVERY_MODE_EXTINT + + @param LevelTriggered TRUE specifies a level triggered interrupt. + FALSE specifies an edge triggered interrupt. + @param AssertionLevel Ignored if LevelTriggered is FALSE. + TRUE specifies a level triggered interrupt that active + when the interrupt line is asserted. + FALSE specifies a level triggered interrupt that active + when the interrupt line is deasserted. + + @return 64-bit data value used to send an MSI to the Local APIC. +**/ +UINT64 +EFIAPI +GetApicMsiValue ( + IN UINT8 Vector, + IN UINTN DeliveryMode, + IN BOOLEAN LevelTriggered, + IN BOOLEAN AssertionLevel + ); + +/** + Get Package ID/Core ID/Thread ID of a processor. + + The algorithm assumes the target system has symmetry across physical + package boundaries with respect to the number of logical processors + per package, number of cores per package. + + @param[in] InitialApicId Initial APIC ID of the target logical processor. + @param[out] Package Returns the processor package ID. + @param[out] Core Returns the processor core ID. + @param[out] Thread Returns the processor thread ID. +**/ +VOID +EFIAPI +GetProcessorLocationByApicId ( + IN UINT32 InitialApicId, + OUT UINT32 *Package OPTIONAL, + OUT UINT32 *Core OPTIONAL, + OUT UINT32 *Thread OPTIONAL + ); + +/** + Get Package ID/Module ID/Tile ID/Die ID/Core ID/Thread ID of a processor. + + The algorithm assumes the target system has symmetry across physical + package boundaries with respect to the number of threads per core, number of + cores per module, number of modules per tile, number of tiles per die, number + of dies per package. + + @param[in] InitialApicId Initial APIC ID of the target logical processor. + @param[out] Package Returns the processor package ID. + @param[out] Die Returns the processor die ID. + @param[out] Tile Returns the processor tile ID. + @param[out] Module Returns the processor module ID. + @param[out] Core Returns the processor core ID. + @param[out] Thread Returns the processor thread ID. +**/ +VOID +EFIAPI +GetProcessorLocation2ByApicId ( + IN UINT32 InitialApicId, + OUT UINT32 *Package OPTIONAL, + OUT UINT32 *Die OPTIONAL, + OUT UINT32 *Tile OPTIONAL, + OUT UINT32 *Module OPTIONAL, + OUT UINT32 *Core OPTIONAL, + OUT UINT32 *Thread OPTIONAL + ); +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/MicrocodeLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/MicrocodeLib.h new file mode 100644 index 00000000..c23fe5bd --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/MicrocodeLib.h @@ -0,0 +1,120 @@ +/** @file + Public include file for Microcode library. + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef MICROCODE_LIB_H_ +#define MICROCODE_LIB_H_ + +#include <Register/Intel/Microcode.h> +#include <Ppi/ShadowMicrocode.h> + +/** + Get microcode update signature of currently loaded microcode update. + + @return Microcode signature. +**/ +UINT32 +EFIAPI +GetProcessorMicrocodeSignature ( + VOID + ); + +/** + Get the processor signature and platform ID for current processor. + + @param MicrocodeCpuId Return the processor signature and platform ID. +**/ +VOID +EFIAPI +GetProcessorMicrocodeCpuId ( + EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId + ); + +/** + Return the total size of the microcode entry. + + Logic follows pseudo code in SDM as below: + + N = 512 + If (Update.DataSize != 00000000H) + N = Update.TotalSize / 4 + + If Microcode is NULL, then ASSERT. + + @param Microcode Pointer to the microcode entry. + + @return The microcode total size. +**/ +UINT32 +EFIAPI +GetMicrocodeLength ( + IN CPU_MICROCODE_HEADER *Microcode + ); + +/** + Load the microcode to the processor. + + If Microcode is NULL, then ASSERT. + + @param Microcode Pointer to the microcode entry. +**/ +VOID +EFIAPI +LoadMicrocode ( + IN CPU_MICROCODE_HEADER *Microcode + ); + +/** + Detect whether specified processor can find matching microcode patch and load it. + + Microcode format is as below: + +----------------------------------------+-------------------------------------------------+ + | CPU_MICROCODE_HEADER | | + +----------------------------------------+ V + | Update Data | CPU_MICROCODE_HEADER.Checksum + +----------------------------------------+-------+ ^ + | CPU_MICROCODE_EXTENDED_TABLE_HEADER | | | + +----------------------------------------+ V | + | CPU_MICROCODE_EXTENDED_TABLE[0] | CPU_MICROCODE_EXTENDED_TABLE_HEADER.Checksum | + | CPU_MICROCODE_EXTENDED_TABLE[1] | ^ | + | ... | | | + +----------------------------------------+-------+-----------------------------------------+ + + There may by multiple CPU_MICROCODE_EXTENDED_TABLE in this format. + The count of CPU_MICROCODE_EXTENDED_TABLE is indicated by ExtendedSignatureCount + of CPU_MICROCODE_EXTENDED_TABLE_HEADER structure. + + If Microcode is NULL, then ASSERT. + + @param Microcode Pointer to a microcode entry. + @param MicrocodeLength The total length of the microcode entry. + @param MinimumRevision The microcode whose revision <= MinimumRevision is treated as invalid. + Caller can supply value get from GetProcessorMicrocodeSignature() to check + whether the microcode is newer than loaded one. + Caller can supply 0 to treat any revision (except 0) microcode as valid. + @param MicrocodeCpuIds Pointer to an array of processor signature and platform ID that represents + a set of processors. + Caller can supply zero-element array to skip the processor signature and + platform ID check. + @param MicrocodeCpuIdCount The number of elements in MicrocodeCpuIds. + @param VerifyChecksum FALSE to skip all the checksum verifications. + + @retval TRUE The microcode is valid. + @retval FALSE The microcode is invalid. +**/ +BOOLEAN +EFIAPI +IsValidMicrocode ( + IN CPU_MICROCODE_HEADER *Microcode, + IN UINTN MicrocodeLength, + IN UINT32 MinimumRevision, + IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuIds, + IN UINTN MicrocodeCpuIdCount, + IN BOOLEAN VerifyChecksum + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/MpInitLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/MpInitLib.h new file mode 100644 index 00000000..a4bc74ee --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/MpInitLib.h @@ -0,0 +1,381 @@ +/** @file + Multiple-Processor initialization Library. + + Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __MP_INIT_LIB_H__ +#define __MP_INIT_LIB_H__ + +#include <Ppi/SecPlatformInformation.h> +#include <Protocol/MpService.h> + +/** + MP Initialize Library initialization. + + This service will allocate AP reset vector and wakeup all APs to do APs + initialization. + + This service must be invoked before all other MP Initialize Library + service are invoked. + + @retval EFI_SUCCESS MP initialization succeeds. + @retval Others MP initialization fails. + +**/ +EFI_STATUS +EFIAPI +MpInitLibInitialize ( + VOID + ); + +/** + Retrieves the number of logical processor in the platform and the number of + those logical processors that are enabled on this boot. This service may only + be called from the BSP. + + @param[out] NumberOfProcessors Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors + is NULL. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibGetNumberOfProcessors ( + OUT UINTN *NumberOfProcessors, OPTIONAL + OUT UINTN *NumberOfEnabledProcessors OPTIONAL + ); + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + @param[out] HealthData Return processor health data. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibGetProcessorInfo ( + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer, + OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL + ); + +/** + This service executes a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] SingleThread If TRUE, then all the enabled APs execute + the function specified by Procedure one by + one, in ascending order of processor handle + number. If FALSE, then all the enabled APs + execute the function specified by Procedure + simultaneously. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until all APs finish + or TimeoutInMicroSeconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on all the enabled + APs, and go on executing immediately. If + all return from Procedure, or TimeoutInMicroSeconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + all APs return from Procedure, then Procedure + on the failed APs is terminated. All enabled + APs are available for next function assigned + by MpInitLibStartupAllAPs() or + MPInitLibStartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise, + if all APs finish successfully, then its + content is set to NULL. If not all APs + finish before timeout expires, then its + content is set to address of the buffer + holding handle numbers of the failed APs. + The buffer is allocated by MP Initialization + library, and it's the caller's responsibility to + free the buffer with FreePool() service. + In blocking mode, it is ready for consumption + when the call returns. In non-blocking mode, + it is ready when WaitEvent is signaled. The + list of failed CPU is terminated by + END_OF_CPU_LIST. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not + supported. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + all enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +MpInitLibStartupAllAPs ( + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT UINTN **FailedCpuList OPTIONAL + ); + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. + + @param[in] Procedure A pointer to the function to be run on the + designated AP of the system. See type + EFI_AP_PROCEDURE. + @param[in] ProcessorNumber The handle number of the AP. The range is + from 0 to the total number of logical + processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until this AP finish + or TimeoutInMicroSeconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on this AP, + and go on executing immediately. If this AP + return from Procedure or TimeoutInMicroSeconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + this AP to finish this Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + this AP returns from Procedure, then Procedure + on the AP is terminated. The + AP is available for next function assigned + by MpInitLibStartupAllAPs() or + MpInitLibStartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure on the + specified AP. + @param[out] Finished If NULL, this parameter is ignored. In + blocking mode, this parameter is ignored. + In non-blocking mode, if AP returns from + Procedure before the timeout expires, its + content is set to TRUE. Otherwise, the + value is set to FALSE. The caller can + determine if the AP returned from Procedure + by evaluating this value. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before + the timeout expires. + @retval EFI_SUCCESS In non-blocking mode, the function has been + dispatched to specified AP. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not + supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + the specified AP has finished. + @retval EFI_NOT_READY The specified AP is busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +MpInitLibStartupThisAP ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT BOOLEAN *Finished OPTIONAL + ); + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + @param[in] ProcessorNumber The handle number of AP that is to become the new + BSP. The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an + enabled AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to + this service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or + a disabled AP. + @retval EFI_NOT_READY The specified AP is busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibSwitchBSP ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ); + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of AP. + The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for + enabled, FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies + the new health status of the AP. This flag + corresponds to StatusFlag defined in + EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only + the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter + is ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed + prior to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibEnableDisableAP ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ); + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + @param[out] ProcessorNumber Pointer to the handle number of AP. + The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned + in ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibWhoAmI ( + OUT UINTN *ProcessorNumber + ); + +/** + This service executes a caller provided function on all enabled CPUs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. Zero means + infinity. TimeoutInMicroseconds is ignored + for BSP. + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + + @retval EFI_SUCCESS In blocking mode, all CPUs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled CPUs. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + all enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +MpInitLibStartupAllCPUs ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/MtrrLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/MtrrLib.h new file mode 100644 index 00000000..02c046a9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/MtrrLib.h @@ -0,0 +1,364 @@ +/** @file + MTRR setting library + + Copyright (c) 2008 - 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _MTRR_LIB_H_ +#define _MTRR_LIB_H_ + +// +// According to IA32 SDM, MTRRs number and MSR offset are always consistent +// for IA32 processor family +// + +// +// The semantics of below macro is MAX_MTRR_NUMBER_OF_VARIABLE_MTRR, the real number can be read out from MTRR_CAP register. +// +#define MTRR_NUMBER_OF_VARIABLE_MTRR 32 +// +// Firmware need reserve 2 MTRR for OS +// Note: It is replaced by PCD PcdCpuNumberOfReservedVariableMtrrs +// +#define RESERVED_FIRMWARE_VARIABLE_MTRR_NUMBER 2 + +#define MTRR_NUMBER_OF_FIXED_MTRR 11 + +// +// Structure to describe a fixed MTRR +// +typedef struct { + UINT32 Msr; + UINT32 BaseAddress; + UINT32 Length; +} FIXED_MTRR; + +// +// Structure to describe a variable MTRR +// +typedef struct { + UINT64 BaseAddress; + UINT64 Length; + UINT64 Type; + UINT32 Msr; + BOOLEAN Valid; + BOOLEAN Used; +} VARIABLE_MTRR; + +// +// Structure to hold base and mask pair for variable MTRR register +// +typedef struct _MTRR_VARIABLE_SETTING_ { + UINT64 Base; + UINT64 Mask; +} MTRR_VARIABLE_SETTING; + +// +// Array for variable MTRRs +// +typedef struct _MTRR_VARIABLE_SETTINGS_ { + MTRR_VARIABLE_SETTING Mtrr[MTRR_NUMBER_OF_VARIABLE_MTRR]; +} MTRR_VARIABLE_SETTINGS; + +// +// Array for fixed MTRRs +// +typedef struct _MTRR_FIXED_SETTINGS_ { + UINT64 Mtrr[MTRR_NUMBER_OF_FIXED_MTRR]; +} MTRR_FIXED_SETTINGS; + +// +// Structure to hold all MTRRs +// +typedef struct _MTRR_SETTINGS_ { + MTRR_FIXED_SETTINGS Fixed; + MTRR_VARIABLE_SETTINGS Variables; + UINT64 MtrrDefType; +} MTRR_SETTINGS; + +// +// Memory cache types +// +typedef enum { + CacheUncacheable = 0, + CacheWriteCombining = 1, + CacheWriteThrough = 4, + CacheWriteProtected = 5, + CacheWriteBack = 6, + CacheInvalid = 7 +} MTRR_MEMORY_CACHE_TYPE; + +#define MTRR_CACHE_UNCACHEABLE 0 +#define MTRR_CACHE_WRITE_COMBINING 1 +#define MTRR_CACHE_WRITE_THROUGH 4 +#define MTRR_CACHE_WRITE_PROTECTED 5 +#define MTRR_CACHE_WRITE_BACK 6 +#define MTRR_CACHE_INVALID_TYPE 7 + +typedef struct { + UINT64 BaseAddress; + UINT64 Length; + MTRR_MEMORY_CACHE_TYPE Type; +} MTRR_MEMORY_RANGE; + +/** + Returns the variable MTRR count for the CPU. + + @return Variable MTRR count + +**/ +UINT32 +EFIAPI +GetVariableMtrrCount ( + VOID + ); + +/** + Returns the firmware usable variable MTRR count for the CPU. + + @return Firmware usable variable MTRR count + +**/ +UINT32 +EFIAPI +GetFirmwareVariableMtrrCount ( + VOID + ); + +/** + This function attempts to set the attributes for a memory range. + + @param[in] BaseAddress The physical address that is the start + address of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attribute The bit mask of attributes to set for the + memory region. + + @retval RETURN_SUCCESS The attributes were set for the memory + region. + @retval RETURN_INVALID_PARAMETER Length is zero. + @retval RETURN_UNSUPPORTED The processor does not support one or + more bytes of the memory resource range + specified by BaseAddress and Length. + @retval RETURN_UNSUPPORTED The bit mask of attributes is not support + for the memory resource range specified + by BaseAddress and Length. + @retval RETURN_ACCESS_DENIED The attributes for the memory resource + range specified by BaseAddress and Length + cannot be modified. + @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to + modify the attributes of the memory + resource range. + Multiple memory range attributes setting by calling this API multiple + times may fail with status RETURN_OUT_OF_RESOURCES. It may not mean + the number of CPU MTRRs are too small to set such memory attributes. + Pass the multiple memory range attributes to one call of + MtrrSetMemoryAttributesInMtrrSettings() may succeed. + @retval RETURN_BUFFER_TOO_SMALL The fixed internal scratch buffer is too small for MTRR calculation. + Caller should use MtrrSetMemoryAttributesInMtrrSettings() to specify + external scratch buffer. +**/ +RETURN_STATUS +EFIAPI +MtrrSetMemoryAttribute ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN MTRR_MEMORY_CACHE_TYPE Attribute + ); + + +/** + This function will get the memory cache type of the specific address. + This function is mainly for debugging purposes. + + @param[in] Address The specific address + + @return The memory cache type of the specific address + +**/ +MTRR_MEMORY_CACHE_TYPE +EFIAPI +MtrrGetMemoryAttribute ( + IN PHYSICAL_ADDRESS Address + ); + + +/** + This function gets the content in fixed MTRRs + + @param[out] FixedSettings A buffer to hold fixed MTRRs content. + + @return The pointer of FixedSettings + +**/ +MTRR_FIXED_SETTINGS* +EFIAPI +MtrrGetFixedMtrr ( + OUT MTRR_FIXED_SETTINGS *FixedSettings + ); + + +/** + This function gets the content in all MTRRs (variable and fixed) + + @param[out] MtrrSetting A buffer to hold all MTRRs content. + + @return The pointer of MtrrSetting + +**/ +MTRR_SETTINGS * +EFIAPI +MtrrGetAllMtrrs ( + OUT MTRR_SETTINGS *MtrrSetting + ); + + +/** + This function sets all MTRRs (variable and fixed) + + @param[in] MtrrSetting A buffer to hold all MTRRs content. + + @return The pointer of MtrrSetting + +**/ +MTRR_SETTINGS * +EFIAPI +MtrrSetAllMtrrs ( + IN MTRR_SETTINGS *MtrrSetting + ); + + +/** + Get the attribute of variable MTRRs. + + This function shadows the content of variable MTRRs into + an internal array: VariableMtrr + + @param[in] MtrrValidBitsMask The mask for the valid bit of the MTRR + @param[in] MtrrValidAddressMask The valid address mask for MTRR since the base address in + MTRR must align to 4K, so valid address mask equal to + MtrrValidBitsMask & 0xfffffffffffff000ULL + @param[out] VariableMtrr The array to shadow variable MTRRs content + + @return The return value of this parameter indicates the number of + MTRRs which has been used. +**/ +UINT32 +EFIAPI +MtrrGetMemoryAttributeInVariableMtrr ( + IN UINT64 MtrrValidBitsMask, + IN UINT64 MtrrValidAddressMask, + OUT VARIABLE_MTRR *VariableMtrr + ); + + +/** + This function prints all MTRRs for debugging. +**/ +VOID +EFIAPI +MtrrDebugPrintAllMtrrs ( + VOID + ); + +/** + Checks if MTRR is supported. + + @retval TRUE MTRR is supported. + @retval FALSE MTRR is not supported. + +**/ +BOOLEAN +EFIAPI +IsMtrrSupported ( + VOID + ); + +/** + Returns the default MTRR cache type for the system. + + @return The default MTRR cache type. + +**/ +MTRR_MEMORY_CACHE_TYPE +EFIAPI +MtrrGetDefaultMemoryType ( + VOID + ); + +/** + This function attempts to set the attributes into MTRR setting buffer for a memory range. + + @param[in, out] MtrrSetting MTRR setting buffer to be set. + @param[in] BaseAddress The physical address that is the start address + of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attribute The bit mask of attributes to set for the + memory region. + + @retval RETURN_SUCCESS The attributes were set for the memory region. + @retval RETURN_INVALID_PARAMETER Length is zero. + @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the + memory resource range specified by BaseAddress and Length. + @retval RETURN_UNSUPPORTED The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. + @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + Multiple memory range attributes setting by calling this API multiple + times may fail with status RETURN_OUT_OF_RESOURCES. It may not mean + the number of CPU MTRRs are too small to set such memory attributes. + Pass the multiple memory range attributes to one call of + MtrrSetMemoryAttributesInMtrrSettings() may succeed. + @retval RETURN_BUFFER_TOO_SMALL The fixed internal scratch buffer is too small for MTRR calculation. + Caller should use MtrrSetMemoryAttributesInMtrrSettings() to specify + external scratch buffer. +**/ +RETURN_STATUS +EFIAPI +MtrrSetMemoryAttributeInMtrrSettings ( + IN OUT MTRR_SETTINGS *MtrrSetting, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN MTRR_MEMORY_CACHE_TYPE Attribute + ); + +/** + This function attempts to set the attributes into MTRR setting buffer for multiple memory ranges. + + @param[in, out] MtrrSetting MTRR setting buffer to be set. + @param[in] Scratch A temporary scratch buffer that is used to perform the calculation. + @param[in, out] ScratchSize Pointer to the size in bytes of the scratch buffer. + It may be updated to the actual required size when the calculation + needs more scratch buffer. + @param[in] Ranges Pointer to an array of MTRR_MEMORY_RANGE. + When range overlap happens, the last one takes higher priority. + When the function returns, either all the attributes are set successfully, + or none of them is set. + @param[in] RangeCount Count of MTRR_MEMORY_RANGE. + + @retval RETURN_SUCCESS The attributes were set for all the memory ranges. + @retval RETURN_INVALID_PARAMETER Length in any range is zero. + @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the + memory resource range specified by BaseAddress and Length in any range. + @retval RETURN_UNSUPPORTED The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length in any range. + @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource ranges. + @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation. +**/ +RETURN_STATUS +EFIAPI +MtrrSetMemoryAttributesInMtrrSettings ( + IN OUT MTRR_SETTINGS *MtrrSetting, + IN VOID *Scratch, + IN OUT UINTN *ScratchSize, + IN CONST MTRR_MEMORY_RANGE *Ranges, + IN UINTN RangeCount + ); +#endif // _MTRR_LIB_H_ diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/PlatformSecLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/PlatformSecLib.h new file mode 100644 index 00000000..bc418143 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/PlatformSecLib.h @@ -0,0 +1,64 @@ +/** @file +This library class defines interface for platform to perform platform +specific initialization in SEC phase. + +Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __PLATFORM_SEC_LIB_H__ +#define __PLATFORM_SEC_LIB_H__ + +/** + A developer supplied function to perform platform specific operations. + + It's a developer supplied function to perform any operations appropriate to a + given platform. It's invoked just before passing control to PEI core by SEC + core. Platform developer may modify the SecCoreData passed to PEI Core. + It returns a platform specific PPI list that platform wishes to pass to PEI core. + The Generic SEC core module will merge this list to join the final list passed to + PEI core. + + @param SecCoreData The same parameter as passing to PEI core. It + could be overridden by this function. + + @return The platform specific PPI list to be passed to PEI core or + NULL if there is no need of such platform specific PPI list. + +**/ +EFI_PEI_PPI_DESCRIPTOR * +EFIAPI +SecPlatformMain ( + IN OUT EFI_SEC_PEI_HAND_OFF *SecCoreData + ); + +/** + This interface conveys state information out of the Security (SEC) phase into PEI. + + @param PeiServices Pointer to the PEI Services Table. + @param StructureSize Pointer to the variable describing size of the input buffer. + @param PlatformInformationRecord Pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformation ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD *PlatformInformationRecord + ); + +/** + This interface disables temporary memory in SEC Phase. +**/ +VOID +EFIAPI +SecPlatformDisableTemporaryMemory ( + VOID + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/RegisterCpuFeaturesLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/RegisterCpuFeaturesLib.h new file mode 100644 index 00000000..4c1b14c3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/RegisterCpuFeaturesLib.h @@ -0,0 +1,611 @@ +/** @file + Register CPU Features Library to register and manage CPU features. + + Copyright (c) 2017 - 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __REGISTER_CPU_FEATURES_LIB_H__ +#define __REGISTER_CPU_FEATURES_LIB_H__ + +#include <AcpiCpuData.h> +#include <Register/Intel/Cpuid.h> +#include <Protocol/MpService.h> + +/// +/// Defines used to identify a CPU feature. The lower 16-bits are used to +/// identify a unique CPU feature and the value represents a bit number in +/// a bit mask. The upper 16-bits are bit mask values that are used as +/// modifiers of a CPU feature. When used in a list, the define value +/// CPU_FEATURE_END is used to terminate a list of CPU feature values. +/// @{ +#define CPU_FEATURE_AESNI 0 +#define CPU_FEATURE_TURBO_MODE 1 +#define CPU_FEATURE_MWAIT 2 +#define CPU_FEATURE_ACPI 3 +#define CPU_FEATURE_EIST 4 +#define CPU_FEATURE_RESERVED_5 5 +#define CPU_FEATURE_FASTSTRINGS 6 +#define CPU_FEATURE_VMX 7 +#define CPU_FEATURE_SMX 8 +#define CPU_FEATURE_LMCE 9 +#define CPU_FEATURE_LOCK_FEATURE_CONTROL_REGISTER 10 +#define CPU_FEATURE_LIMIT_CPUID_MAX_VAL 11 +#define CPU_FEATURE_MCE 12 +#define CPU_FEATURE_MCA 13 +#define CPU_FEATURE_MCG_CTL 14 +#define CPU_FEATURE_PENDING_BREAK 15 +#define CPU_FEATURE_C1E 16 +#define CPU_FEATURE_C1_AUTO_DEMOTION 17 +#define CPU_FEATURE_C3_AUTO_DEMOTION 18 +#define CPU_FEATURE_C1_UNDEMOTION 19 +#define CPU_FEATURE_C3_UNDEMOTION 20 +#define CPU_FEATURE_C_STATE 21 +#define CPU_FEATURE_TM 22 +#define CPU_FEATURE_TM2 23 +#define CPU_FEATURE_X2APIC 24 +#define CPU_FEATURE_RESERVED_25 25 +#define CPU_FEATURE_RESERVED_26 26 +#define CPU_FEATURE_RESERVED_27 27 +#define CPU_FEATURE_RESERVED_28 28 +#define CPU_FEATURE_RESERVED_29 29 +#define CPU_FEATURE_RESERVED_30 30 +#define CPU_FEATURE_RESERVED_31 31 + +#define CPU_FEATURE_L2_PREFETCHER (32+0) +#define CPU_FEATURE_L1_DATA_PREFETCHER (32+1) +#define CPU_FEATURE_HARDWARE_PREFETCHER (32+2) +#define CPU_FEATURE_ADJACENT_CACHE_LINE_PREFETCH (32+3) +#define CPU_FEATURE_DCU_PREFETCHER (32+4) +#define CPU_FEATURE_IP_PREFETCHER (32+5) +#define CPU_FEATURE_MLC_STREAMER_PREFETCHER (32+6) +#define CPU_FEATURE_MLC_SPATIAL_PREFETCHER (32+7) +#define CPU_FEATURE_THREE_STRIKE_COUNTER (32+8) +#define CPU_FEATURE_APIC_TPR_UPDATE_MESSAGE (32+9) +#define CPU_FEATURE_ENERGY_PERFORMANCE_BIAS (32+10) +#define CPU_FEATURE_PPIN (32+11) +#define CPU_FEATURE_PROC_TRACE (32+12) + +#define CPU_FEATURE_BEFORE_ALL BIT23 +#define CPU_FEATURE_AFTER_ALL BIT24 +#define CPU_FEATURE_THREAD_BEFORE BIT25 +#define CPU_FEATURE_THREAD_AFTER BIT26 +#define CPU_FEATURE_CORE_BEFORE BIT27 +#define CPU_FEATURE_CORE_AFTER BIT28 +#define CPU_FEATURE_PACKAGE_BEFORE BIT29 +#define CPU_FEATURE_PACKAGE_AFTER BIT30 +#define CPU_FEATURE_END MAX_UINT32 +/// @} + +/// +/// The bit field to indicate whether the processor is the first in its parent scope. +/// +typedef struct { + // + // Set to 1 when current processor is the first thread in the core it resides in. + // + UINT32 Thread : 1; + // + // Set to 1 when current processor is a thread of the first core in the module it resides in. + // + UINT32 Core : 1; + // + // Set to 1 when current processor is a thread of the first module in the tile it resides in. + // + UINT32 Module : 1; + // + // Set to 1 when current processor is a thread of the first tile in the die it resides in. + // + UINT32 Tile : 1; + // + // Set to 1 when current processor is a thread of the first die in the package it resides in. + // + UINT32 Die : 1; + // + // Set to 1 when current processor is a thread of the first package in the system. + // + UINT32 Package : 1; + UINT32 Reserved : 26; +} REGISTER_CPU_FEATURE_FIRST_PROCESSOR; + +/// +/// CPU Information passed into the SupportFunc and InitializeFunc of the +/// RegisterCpuFeature() library function. This structure contains information +/// that is commonly used during CPU feature detection and initialization. +/// +typedef struct { + /// + /// The package that the CPU resides + /// + EFI_PROCESSOR_INFORMATION ProcessorInfo; + + /// + /// The bit flag indicating whether the CPU is the first Thread/Core/Module/Tile/Die/Package in its parent scope. + /// + REGISTER_CPU_FEATURE_FIRST_PROCESSOR First; + /// + /// The Display Family of the CPU computed from CPUID leaf CPUID_VERSION_INFO + /// + UINT32 DisplayFamily; + /// + /// The Display Model of the CPU computed from CPUID leaf CPUID_VERSION_INFO + /// + UINT32 DisplayModel; + /// + /// The Stepping ID of the CPU computed from CPUID leaf CPUID_VERSION_INFO + /// + UINT32 SteppingId; + /// + /// The Processor Type of the CPU computed from CPUID leaf CPUID_VERSION_INFO + /// + UINT32 ProcessorType; + /// + /// Bit field structured returned in ECX from CPUID leaf CPUID_VERSION_INFO + /// + CPUID_VERSION_INFO_ECX CpuIdVersionInfoEcx; + /// + /// Bit field structured returned in EDX from CPUID leaf CPUID_VERSION_INFO + /// + CPUID_VERSION_INFO_EDX CpuIdVersionInfoEdx; +} REGISTER_CPU_FEATURE_INFORMATION; + +/** + Determines if a CPU feature is enabled in PcdCpuFeaturesSupport bit mask. + If a CPU feature is disabled in PcdCpuFeaturesSupport then all the code/data + associated with that feature should be optimized away if compiler + optimizations are enabled. + + @param[in] Feature The bit number of the CPU feature to check in the PCD + PcdCpuFeaturesSupport. + + @retval TRUE The CPU feature is set in PcdCpuFeaturesSupport. + @retval FALSE The CPU feature is not set in PcdCpuFeaturesSupport. + + @note This service could be called by BSP only. +**/ +BOOLEAN +EFIAPI +IsCpuFeatureSupported ( + IN UINT32 Feature + ); + +/** + Determines if a CPU feature is set in PcdCpuFeaturesSetting bit mask. + + @param[in] Feature The bit number of the CPU feature to check in the PCD + PcdCpuFeaturesSetting. + + @retval TRUE The CPU feature is set in PcdCpuFeaturesSetting. + @retval FALSE The CPU feature is not set in PcdCpuFeaturesSetting. + + @note This service could be called by BSP only. +**/ +BOOLEAN +EFIAPI +IsCpuFeatureInSetting ( + IN UINT32 Feature + ); + +/** + Prepares for the data used by CPU feature detection and initialization. + + @param[in] NumberOfProcessors The number of CPUs in the platform. + + @return Pointer to a buffer of CPU related configuration data. + + @note This service could be called by BSP only. +**/ +typedef +VOID * +(EFIAPI *CPU_FEATURE_GET_CONFIG_DATA)( + IN UINTN NumberOfProcessors + ); + +/** + Detects if CPU feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE CPU feature is supported. + @retval FALSE CPU feature is not supported. + + @note This service could be called by BSP/APs. +**/ +typedef +BOOLEAN +(EFIAPI *CPU_FEATURE_SUPPORT)( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes CPU feature to specific state. + + This service does not initialize hardware and only produces entries in the + Register Table for specified processor. Hardware initialization on BSP/APs + will be done in CpuFeaturesInitialize(). + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the CPU feature must be enabled. + If FALSE, then the CPU feature must be disabled. + + @retval RETURN_SUCCESS CPU feature is initialized. + + @note This service could be called by BSP only. +**/ +typedef +RETURN_STATUS +(EFIAPI *CPU_FEATURE_INITIALIZE)( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Registers a CPU Feature. + + @param[in] FeatureName A Null-terminated Ascii string indicates CPU feature + name. + @param[in] GetConfigDataFunc CPU feature get configuration data function. This + is an optional parameter that may be NULL. If NULL, + then the most recently registered function for the + CPU feature is used. If no functions are registered + for a CPU feature, then the CPU configuration data + for the registered feature is NULL. + @param[in] SupportFunc CPU feature support function. This is an optional + parameter that may be NULL. If NULL, then the most + recently registered function for the CPU feature is + used. If no functions are registered for a CPU + feature, then the CPU feature is assumed to be + supported by all CPUs. + @param[in] InitializeFunc CPU feature initialize function. This is an optional + parameter that may be NULL. If NULL, then the most + recently registered function for the CPU feature is + used. If no functions are registered for a CPU + feature, then the CPU feature initialization is + skipped. + @param[in] ... Variable argument list of UINT32 CPU feature value. + Values with no modifiers are the features provided + by the registered functions. + Values with CPU_FEATURE_BEFORE modifier are features + that must be initialized after the features provided + by the registered functions are used. + Values with CPU_FEATURE_AFTER modifier are features + that must be initialized before the features provided + by the registered functions are used. + The last argument in this variable argument list must + always be CPU_FEATURE_END. + + @retval RETURN_SUCCESS The CPU feature was successfully registered. + @retval RETURN_OUT_OF_RESOURCES There are not enough resources to register + the CPU feature. + @retval RETURN_UNSUPPORTED Registration of the CPU feature is not + supported due to a circular dependency between + BEFORE and AFTER features. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +RegisterCpuFeature ( + IN CHAR8 *FeatureName, OPTIONAL + IN CPU_FEATURE_GET_CONFIG_DATA GetConfigDataFunc, OPTIONAL + IN CPU_FEATURE_SUPPORT SupportFunc, OPTIONAL + IN CPU_FEATURE_INITIALIZE InitializeFunc, OPTIONAL + ... + ); + +/** + Performs CPU features detection. + + This service will invoke MP service to check CPU features' + capabilities on BSP/APs. + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +CpuFeaturesDetect ( + VOID + ); + +/** + Performs CPU features Initialization. + + This service will invoke MP service to perform CPU features + initialization on BSP/APs per user configuration. + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +CpuFeaturesInitialize ( + VOID + ); + +/** + Switches to assigned BSP after CPU features initialization. + + @param[in] ProcessorNumber The index of the CPU executing this function. + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +SwitchBspAfterFeaturesInitialize ( + IN UINTN ProcessorNumber + ); + +/** + Adds an entry in specified register table. + + This function adds an entry in specified register table, with given register type, + register index, bit section and value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry. + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] ValueMask Mask of bits in register to write + @param[in] Value Value to write + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +CpuRegisterTableWrite ( + IN UINTN ProcessorNumber, + IN REGISTER_TYPE RegisterType, + IN UINT64 Index, + IN UINT64 ValueMask, + IN UINT64 Value + ); + +/** + Adds an entry in specified register table. + + This function adds an entry in specified register table, with given register type, + register index, bit section and value. + + Driver will test the current value before setting new value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] ValueMask Mask of bits in register to write + @param[in] Value Value to write + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +CpuRegisterTableTestThenWrite ( + IN UINTN ProcessorNumber, + IN REGISTER_TYPE RegisterType, + IN UINT64 Index, + IN UINT64 ValueMask, + IN UINT64 Value + ); + +/** + Adds an entry in specified Pre-SMM register table. + + This function adds an entry in specified register table, with given register type, + register index, bit section and value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry. + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] ValueMask Mask of bits in register to write + @param[in] Value Value to write + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +PreSmmCpuRegisterTableWrite ( + IN UINTN ProcessorNumber, + IN REGISTER_TYPE RegisterType, + IN UINT64 Index, + IN UINT64 ValueMask, + IN UINT64 Value + ); + +/** + Adds a 32-bit register write entry in specified register table. + + This macro adds an entry in specified register table, with given register type, + register index, and value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry. + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] Value Value to write + + @note This service could be called by BSP only. +**/ +#define CPU_REGISTER_TABLE_WRITE32(ProcessorNumber, RegisterType, Index, Value) \ + do { \ + CpuRegisterTableWrite (ProcessorNumber, RegisterType, Index, MAX_UINT32, Value); \ + } while(FALSE); + +/** + Adds a 32-bit register write entry in specified register table. + + This macro adds an entry in specified register table, with given register type, + register index, and value. + + Driver will test the current value before setting new value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry. + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] Value Value to write + + @note This service could be called by BSP only. +**/ +#define CPU_REGISTER_TABLE_TEST_THEN_WRITE32(ProcessorNumber, RegisterType, Index, Value) \ + do { \ + CpuRegisterTableTestThenWrite (ProcessorNumber, RegisterType, Index, MAX_UINT32, Value); \ + } while(FALSE); + +/** + Adds a 64-bit register write entry in specified register table. + + This macro adds an entry in specified register table, with given register type, + register index, and value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry. + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] Value Value to write + + @note This service could be called by BSP only. +**/ +#define CPU_REGISTER_TABLE_WRITE64(ProcessorNumber, RegisterType, Index, Value) \ + do { \ + CpuRegisterTableWrite (ProcessorNumber, RegisterType, Index, MAX_UINT64, Value); \ + } while(FALSE); + +/** + Adds a 64-bit register write entry in specified register table. + + This macro adds an entry in specified register table, with given register type, + register index, and value. + + Driver will test the current value before setting new value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry. + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] Value Value to write + + @note This service could be called by BSP only. +**/ +#define CPU_REGISTER_TABLE_TEST_THEN_WRITE64(ProcessorNumber, RegisterType, Index, Value) \ + do { \ + CpuRegisterTableTestThenWrite (ProcessorNumber, RegisterType, Index, MAX_UINT64, Value); \ + } while(FALSE); + +/** + Adds a bit field write entry in specified register table. + + This macro adds an entry in specified register table, with given register type, + register index, bit field section, and value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry. + @param[in] RegisterType Type of the register to program. + @param[in] Index Index of the register to program. + @param[in] Type The data type name of a register structure. + @param[in] Field The bit fiel name in register structure to write. + @param[in] Value Value to write to the bit field. + + @note This service could be called by BSP only. +**/ +#define CPU_REGISTER_TABLE_WRITE_FIELD(ProcessorNumber, RegisterType, Index, Type, Field, Value) \ + do { \ + UINT64 ValueMask; \ + ValueMask = MAX_UINT64; \ + ((Type *)(&ValueMask))->Field = 0; \ + CpuRegisterTableWrite (ProcessorNumber, RegisterType, Index, ~ValueMask, Value); \ + } while(FALSE); + +/** + Adds a bit field write entry in specified register table. + + This macro adds an entry in specified register table, with given register type, + register index, bit field section, and value. + + Driver will test the current value before setting new value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry. + @param[in] RegisterType Type of the register to program. + @param[in] Index Index of the register to program. + @param[in] Type The data type name of a register structure. + @param[in] Field The bit fiel name in register structure to write. + @param[in] Value Value to write to the bit field. + + @note This service could be called by BSP only. +**/ +#define CPU_REGISTER_TABLE_TEST_THEN_WRITE_FIELD(ProcessorNumber, RegisterType, Index, Type, Field, Value) \ + do { \ + UINT64 ValueMask; \ + ValueMask = MAX_UINT64; \ + ((Type *)(&ValueMask))->Field = 0; \ + CpuRegisterTableTestThenWrite (ProcessorNumber, RegisterType, Index, ~ValueMask, Value); \ + } while(FALSE); + +/** + Adds a 32-bit register write entry in specified register table. + + This macro adds an entry in specified register table, with given register type, + register index, and value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry. + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] Value Value to write + + @note This service could be called by BSP only. +**/ +#define PRE_SMM_CPU_REGISTER_TABLE_WRITE32(ProcessorNumber, RegisterType, Index, Value) \ + do { \ + PreSmmCpuRegisterTableWrite (ProcessorNumber, RegisterType, Index, MAX_UINT32, Value); \ + } while(FALSE); + +/** + Adds a 64-bit register write entry in specified register table. + + This macro adds an entry in specified register table, with given register type, + register index, and value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry. + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] Value Value to write + + @note This service could be called by BSP only. +**/ +#define PRE_SMM_CPU_REGISTER_TABLE_WRITE64(ProcessorNumber, RegisterType, Index, Value) \ + do { \ + PreSmmCpuRegisterTableWrite (ProcessorNumber, RegisterType, Index, MAX_UINT64, Value); \ + } while(FALSE); + +/** + Adds a bit field write entry in specified register table. + + This macro adds an entry in specified register table, with given register type, + register index, bit field section, and value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry. + @param[in] RegisterType Type of the register to program. + @param[in] Index Index of the register to program. + @param[in] Type The data type name of a register structure. + @param[in] Field The bit fiel name in register structure to write. + @param[in] Value Value to write to the bit field. + + @note This service could be called by BSP only. +**/ +#define PRE_SMM_CPU_REGISTER_TABLE_WRITE_FIELD(ProcessorNumber, RegisterType, Index, Type, Field, Value) \ + do { \ + UINT64 ValueMask; \ + ValueMask = MAX_UINT64; \ + ((Type *)(&ValueMask))->Field = 0; \ + PreSmmCpuRegisterTableWrite (ProcessorNumber, RegisterType, Index, ~ValueMask, Value); \ + } while(FALSE); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/SmmCpuFeaturesLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/SmmCpuFeaturesLib.h new file mode 100644 index 00000000..d6d3e825 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/SmmCpuFeaturesLib.h @@ -0,0 +1,414 @@ +/** @file +Library that provides CPU specific functions to support the PiSmmCpuDxeSmm module. + +Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __SMM_FEATURES_LIB_H__ +#define __SMM_FEATURES_LIB_H__ + +#include <Protocol/MpService.h> +#include <Protocol/SmmCpu.h> +#include <Register/Intel/SmramSaveStateMap.h> +#include <CpuHotPlugData.h> + +/// +/// Enumeration of SMM registers that are accessed using the library functions +/// SmmCpuFeaturesIsSmmRegisterSupported (), SmmCpuFeaturesGetSmmRegister (), +/// and SmmCpuFeaturesSetSmmRegister (). +/// +typedef enum { + /// + /// Read-write register to provides access to MSR_SMM_FEATURE_CONTROL if the + /// CPU supports this MSR. + /// + SmmRegFeatureControl, + /// + /// Read-only register that returns a non-zero value if the CPU is able to + /// respond to SMIs. + /// + SmmRegSmmEnable, + /// + /// Read-only register that returns a non-zero value if the CPU is able to + /// respond to SMIs, but is busy with other actions that are causing a delay + /// in responding to an SMI. This register abstracts access to MSR_SMM_DELAYED + /// if the CPU supports this MSR. + /// + SmmRegSmmDelayed, + /// + /// Read-only register that returns a non-zero value if the CPU is able to + /// respond to SMIs, but is busy with other actions that are blocking its + /// ability to respond to an SMI. This register abstracts access to + /// MSR_SMM_BLOCKED if the CPU supports this MSR. + /// + SmmRegSmmBlocked +} SMM_REG_NAME; + +/** + Called during the very first SMI into System Management Mode to initialize + CPU features, including SMBASE, for the currently executing CPU. Since this + is the first SMI, the SMRAM Save State Map is at the default address of + SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET. The currently executing + CPU is specified by CpuIndex and CpuIndex can be used to access information + about the currently executing CPU in the ProcessorInfo array and the + HotPlugCpuData data structure. + + @param[in] CpuIndex The index of the CPU to initialize. The value + must be between 0 and the NumberOfCpus field in + the System Management System Table (SMST). + @param[in] IsMonarch TRUE if the CpuIndex is the index of the CPU that + was elected as monarch during System Management + Mode initialization. + FALSE if the CpuIndex is not the index of the CPU + that was elected as monarch during System + Management Mode initialization. + @param[in] ProcessorInfo Pointer to an array of EFI_PROCESSOR_INFORMATION + structures. ProcessorInfo[CpuIndex] contains the + information for the currently executing CPU. + @param[in] CpuHotPlugData Pointer to the CPU_HOT_PLUG_DATA structure that + contains the ApidId and SmBase arrays. +**/ +VOID +EFIAPI +SmmCpuFeaturesInitializeProcessor ( + IN UINTN CpuIndex, + IN BOOLEAN IsMonarch, + IN EFI_PROCESSOR_INFORMATION *ProcessorInfo, + IN CPU_HOT_PLUG_DATA *CpuHotPlugData + ); + +/** + This function updates the SMRAM save state on the currently executing CPU + to resume execution at a specific address after an RSM instruction. This + function must evaluate the SMRAM save state to determine the execution mode + the RSM instruction resumes and update the resume execution address with + either NewInstructionPointer32 or NewInstructionPoint. The auto HALT restart + flag in the SMRAM save state must always be cleared. This function returns + the value of the instruction pointer from the SMRAM save state that was + replaced. If this function returns 0, then the SMRAM save state was not + modified. + + This function is called during the very first SMI on each CPU after + SmmCpuFeaturesInitializeProcessor() to set a flag in normal execution mode + to signal that the SMBASE of each CPU has been updated before the default + SMBASE address is used for the first SMI to the next CPU. + + @param[in] CpuIndex The index of the CPU to hook. The value + must be between 0 and the NumberOfCpus + field in the System Management System Table + (SMST). + @param[in] CpuState Pointer to SMRAM Save State Map for the + currently executing CPU. + @param[in] NewInstructionPointer32 Instruction pointer to use if resuming to + 32-bit execution mode from 64-bit SMM. + @param[in] NewInstructionPointer Instruction pointer to use if resuming to + same execution mode as SMM. + + @retval 0 This function did modify the SMRAM save state. + @retval > 0 The original instruction pointer value from the SMRAM save state + before it was replaced. +**/ +UINT64 +EFIAPI +SmmCpuFeaturesHookReturnFromSmm ( + IN UINTN CpuIndex, + IN SMRAM_SAVE_STATE_MAP *CpuState, + IN UINT64 NewInstructionPointer32, + IN UINT64 NewInstructionPointer + ); + +/** + Hook point in normal execution mode that allows the one CPU that was elected + as monarch during System Management Mode initialization to perform additional + initialization actions immediately after all of the CPUs have processed their + first SMI and called SmmCpuFeaturesInitializeProcessor() relocating SMBASE + into a buffer in SMRAM and called SmmCpuFeaturesHookReturnFromSmm(). +**/ +VOID +EFIAPI +SmmCpuFeaturesSmmRelocationComplete ( + VOID + ); + +/** + Return the size, in bytes, of a custom SMI Handler in bytes. If 0 is + returned, then a custom SMI handler is not provided by this library, + and the default SMI handler must be used. + + @retval 0 Use the default SMI handler. + @retval > 0 Use the SMI handler installed by SmmCpuFeaturesInstallSmiHandler() + The caller is required to allocate enough SMRAM for each CPU to + support the size of the custom SMI handler. +**/ +UINTN +EFIAPI +SmmCpuFeaturesGetSmiHandlerSize ( + VOID + ); + +/** + Install a custom SMI handler for the CPU specified by CpuIndex. This function + is only called if SmmCpuFeaturesGetSmiHandlerSize() returns a size is greater + than zero and is called by the CPU that was elected as monarch during System + Management Mode initialization. + + // + // Append Shadow Stack after normal stack + // + // |= SmiStack + // +--------------------------------------------------+---------------------------------------------------------------+ + // | Known Good Stack | Guard Page | SMM Stack | Known Good Shadow Stack | Guard Page | SMM Shadow Stack | + // +--------------------------------------------------+---------------------------------------------------------------+ + // | |PcdCpuSmmStackSize| |PcdCpuSmmShadowStackSize| + // |<-------------------- StackSize ----------------->|<------------------------- ShadowStackSize ------------------->| + // | | + // |<-------------------------------------------- Processor N ------------------------------------------------------->| + // | low address (bottom) high address (top) | + // + + @param[in] CpuIndex The index of the CPU to install the custom SMI handler. + The value must be between 0 and the NumberOfCpus field + in the System Management System Table (SMST). + @param[in] SmBase The SMBASE address for the CPU specified by CpuIndex. + @param[in] SmiStack The bottom of stack to use when an SMI is processed by the + the CPU specified by CpuIndex. + @param[in] StackSize The size, in bytes, if the stack used when an SMI is + processed by the CPU specified by CpuIndex. + StackSize should be PcdCpuSmmStackSize, with 2 more pages + if PcdCpuSmmStackGuard is true. + If ShadowStack is enabled, the shadow stack is allocated + after the normal Stack. The size is PcdCpuSmmShadowStackSize. + with 2 more pages if PcdCpuSmmStackGuard is true. + @param[in] GdtBase The base address of the GDT to use when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] GdtSize The size, in bytes, of the GDT used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] IdtBase The base address of the IDT to use when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] IdtSize The size, in bytes, of the IDT used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] Cr3 The base address of the page tables to use when an SMI + is processed by the CPU specified by CpuIndex. +**/ +VOID +EFIAPI +SmmCpuFeaturesInstallSmiHandler ( + IN UINTN CpuIndex, + IN UINT32 SmBase, + IN VOID *SmiStack, + IN UINTN StackSize, + IN UINTN GdtBase, + IN UINTN GdtSize, + IN UINTN IdtBase, + IN UINTN IdtSize, + IN UINT32 Cr3 + ); + +/** + Determines if MTRR registers must be configured to set SMRAM cache-ability + when executing in System Management Mode. + + @retval TRUE MTRR registers must be configured to set SMRAM cache-ability. + @retval FALSE MTRR registers do not need to be configured to set SMRAM + cache-ability. +**/ +BOOLEAN +EFIAPI +SmmCpuFeaturesNeedConfigureMtrrs ( + VOID + ); + +/** + Disable SMRR register if SMRR is supported and SmmCpuFeaturesNeedConfigureMtrrs() + returns TRUE. +**/ +VOID +EFIAPI +SmmCpuFeaturesDisableSmrr ( + VOID + ); + +/** + Enable SMRR register if SMRR is supported and SmmCpuFeaturesNeedConfigureMtrrs() + returns TRUE. +**/ +VOID +EFIAPI +SmmCpuFeaturesReenableSmrr ( + VOID + ); + +/** + Processor specific hook point each time a CPU enters System Management Mode. + + @param[in] CpuIndex The index of the CPU that has entered SMM. The value + must be between 0 and the NumberOfCpus field in the + System Management System Table (SMST). +**/ +VOID +EFIAPI +SmmCpuFeaturesRendezvousEntry ( + IN UINTN CpuIndex + ); + +/** + Processor specific hook point each time a CPU exits System Management Mode. + + @param[in] CpuIndex The index of the CPU that is exiting SMM. The value must + be between 0 and the NumberOfCpus field in the System + Management System Table (SMST). +**/ +VOID +EFIAPI +SmmCpuFeaturesRendezvousExit ( + IN UINTN CpuIndex + ); + +/** + Check to see if an SMM register is supported by a specified CPU. + + @param[in] CpuIndex The index of the CPU to check for SMM register support. + The value must be between 0 and the NumberOfCpus field + in the System Management System Table (SMST). + @param[in] RegName Identifies the SMM register to check for support. + + @retval TRUE The SMM register specified by RegName is supported by the CPU + specified by CpuIndex. + @retval FALSE The SMM register specified by RegName is not supported by the + CPU specified by CpuIndex. +**/ +BOOLEAN +EFIAPI +SmmCpuFeaturesIsSmmRegisterSupported ( + IN UINTN CpuIndex, + IN SMM_REG_NAME RegName + ); + +/** + Returns the current value of the SMM register for the specified CPU. + If the SMM register is not supported, then 0 is returned. + + @param[in] CpuIndex The index of the CPU to read the SMM register. The + value must be between 0 and the NumberOfCpus field in + the System Management System Table (SMST). + @param[in] RegName Identifies the SMM register to read. + + @return The value of the SMM register specified by RegName from the CPU + specified by CpuIndex. +**/ +UINT64 +EFIAPI +SmmCpuFeaturesGetSmmRegister ( + IN UINTN CpuIndex, + IN SMM_REG_NAME RegName + ); + +/** + Sets the value of an SMM register on a specified CPU. + If the SMM register is not supported, then no action is performed. + + @param[in] CpuIndex The index of the CPU to write the SMM register. The + value must be between 0 and the NumberOfCpus field in + the System Management System Table (SMST). + @param[in] RegName Identifies the SMM register to write. + registers are read-only. + @param[in] Value The value to write to the SMM register. +**/ +VOID +EFIAPI +SmmCpuFeaturesSetSmmRegister ( + IN UINTN CpuIndex, + IN SMM_REG_NAME RegName, + IN UINT64 Value + ); + +/** + Read an SMM Save State register on the target processor. If this function + returns EFI_UNSUPPORTED, then the caller is responsible for reading the + SMM Save Sate register. + + @param[in] CpuIndex The index of the CPU to read the SMM Save State. The + value must be between 0 and the NumberOfCpus field in + the System Management System Table (SMST). + @param[in] Register The SMM Save State register to read. + @param[in] Width The number of bytes to read from the CPU save state. + @param[out] Buffer Upon return, this holds the CPU register value read + from the save state. + + @retval EFI_SUCCESS The register was read from Save State. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED This function does not support reading Register. + +**/ +EFI_STATUS +EFIAPI +SmmCpuFeaturesReadSaveStateRegister ( + IN UINTN CpuIndex, + IN EFI_SMM_SAVE_STATE_REGISTER Register, + IN UINTN Width, + OUT VOID *Buffer + ); + +/** + Writes an SMM Save State register on the target processor. If this function + returns EFI_UNSUPPORTED, then the caller is responsible for writing the + SMM Save Sate register. + + @param[in] CpuIndex The index of the CPU to write the SMM Save State. The + value must be between 0 and the NumberOfCpus field in + the System Management System Table (SMST). + @param[in] Register The SMM Save State register to write. + @param[in] Width The number of bytes to write to the CPU save state. + @param[in] Buffer Upon entry, this holds the new CPU register value. + + @retval EFI_SUCCESS The register was written to Save State. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED This function does not support writing Register. +**/ +EFI_STATUS +EFIAPI +SmmCpuFeaturesWriteSaveStateRegister ( + IN UINTN CpuIndex, + IN EFI_SMM_SAVE_STATE_REGISTER Register, + IN UINTN Width, + IN CONST VOID *Buffer + ); + +/** + This function is hook point called after the gEfiSmmReadyToLockProtocolGuid + notification is completely processed. +**/ +VOID +EFIAPI +SmmCpuFeaturesCompleteSmmReadyToLock ( + VOID + ); + +/** + This API provides a method for a CPU to allocate a specific region for storing page tables. + + This API can be called more once to allocate memory for page tables. + + Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the + allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL + is returned. If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + This function can also return NULL if there is no preference on where the page tables are allocated in SMRAM. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer for page tables. + @retval NULL Fail to allocate a specific region for storing page tables, + Or there is no preference on where the page tables are allocated in SMRAM. + +**/ +VOID * +EFIAPI +SmmCpuFeaturesAllocatePageTableMemory ( + IN UINTN Pages + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/SmmCpuPlatformHookLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/SmmCpuPlatformHookLib.h new file mode 100644 index 00000000..d5b49cc5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/SmmCpuPlatformHookLib.h @@ -0,0 +1,103 @@ +/** @file + Public include file for the SMM CPU Platform Hook Library. + + Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __SMM_CPU_PLATFORM_HOOK_LIB_H__ +#define __SMM_CPU_PLATFORM_HOOK_LIB_H__ + +/// +/// SMM Page Size Type +/// +typedef enum { + SmmPageSize4K, + SmmPageSize2M, + SmmPageSize1G, + MaxSmmPageSizeType +} SMM_PAGE_SIZE_TYPE; + +/** + Checks if platform produces a valid SMI. + + This function checks if platform produces a valid SMI. This function is + called at SMM entry to detect if this is a spurious SMI. This function + must be implemented in an MP safe way because it is called by multiple CPU + threads. + + @retval TRUE There is a valid SMI + @retval FALSE There is no valid SMI + +**/ +BOOLEAN +EFIAPI +PlatformValidSmi ( + VOID + ); + +/** + Clears platform top level SMI status bit. + + This function clears platform top level SMI status bit. + + @retval TRUE The platform top level SMI status is cleared. + @retval FALSE The platform top level SMI status cannot be cleared. + +**/ +BOOLEAN +EFIAPI +ClearTopLevelSmiStatus ( + VOID + ); + +/** + Performs platform specific way of SMM BSP election. + + This function performs platform specific way of SMM BSP election. + + @param IsBsp Output parameter. TRUE: the CPU this function executes + on is elected to be the SMM BSP. FALSE: the CPU this + function executes on is to be SMM AP. + + @retval EFI_SUCCESS The function executes successfully. + @retval EFI_NOT_READY The function does not determine whether this CPU should be + BSP or AP. This may occur if hardware init sequence to + enable the determination is yet to be done, or the function + chooses not to do BSP election and will let SMM CPU driver to + use its default BSP election process. + @retval EFI_DEVICE_ERROR The function cannot determine whether this CPU should be + BSP or AP due to hardware error. + +**/ +EFI_STATUS +EFIAPI +PlatformSmmBspElection ( + OUT BOOLEAN *IsBsp + ); + +/** + Get platform page table attribute . + + This function gets page table attribute of platform. + + @param Address Input parameter. Obtain the page table entries attribute on this address. + @param PageSize Output parameter. The size of the page. + @param NumOfPages Output parameter. Number of page. + @param PageAttribute Output parameter. Paging Attributes (WB, UC, etc). + + @retval EFI_SUCCESS The platform page table attribute from the address is determined. + @retval EFI_UNSUPPORTED The platform does not support getting page table attribute for the address. + +**/ +EFI_STATUS +EFIAPI +GetPlatformPageTableAttribute ( + IN UINT64 Address, + OUT SMM_PAGE_SIZE_TYPE *PageSize, + OUT UINTN *NumOfPages, + OUT UINTN *PageAttribute + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/UefiCpuLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/UefiCpuLib.h new file mode 100644 index 00000000..fb8dd8cd --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/UefiCpuLib.h @@ -0,0 +1,46 @@ +/** @file + Public header file for UEFI CPU library class. + + This library class defines some routines that are generic for IA32 family CPU + to be UEFI specification compliant. + + Copyright (c) 2009, Intel Corporation. All rights reserved.<BR> + Copyright (c) 2020, AMD Inc. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __UEFI_CPU_LIB_H__ +#define __UEFI_CPU_LIB_H__ + + + +/** + Initializes floating point units for requirement of UEFI specification. + + This function initializes floating-point control word to 0x027F (all exceptions + masked,double-precision, round-to-nearest) and multimedia-extensions control word + (if supported) to 0x1F80 (all exceptions masked, round-to-nearest, flush to zero + for masked underflow). + +**/ +VOID +EFIAPI +InitializeFloatingPointUnits ( + VOID + ); + +/** + Determine if the standard CPU signature is "AuthenticAMD". + + @retval TRUE The CPU signature matches. + @retval FALSE The CPU signature does not match. + +**/ +BOOLEAN +EFIAPI +StandardSignatureIsAuthenticAMD ( + VOID + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/VmgExitLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/VmgExitLib.h new file mode 100644 index 00000000..6df1682f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Library/VmgExitLib.h @@ -0,0 +1,146 @@ +/** @file + Public header file for the VMGEXIT Support library class. + + This library class defines some routines used when invoking the VMGEXIT + instruction in support of SEV-ES and to handle #VC exceptions. + + Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __VMG_EXIT_LIB_H__ +#define __VMG_EXIT_LIB_H__ + +#include <Protocol/DebugSupport.h> +#include <Register/Amd/Ghcb.h> + + +/** + Perform VMGEXIT. + + Sets the necessary fields of the GHCB, invokes the VMGEXIT instruction and + then handles the return actions. + + @param[in, out] Ghcb A pointer to the GHCB + @param[in] ExitCode VMGEXIT code to be assigned to the SwExitCode + field of the GHCB. + @param[in] ExitInfo1 VMGEXIT information to be assigned to the + SwExitInfo1 field of the GHCB. + @param[in] ExitInfo2 VMGEXIT information to be assigned to the + SwExitInfo2 field of the GHCB. + + @retval 0 VMGEXIT succeeded. + @return Exception number to be propagated, VMGEXIT + processing did not succeed. + +**/ +UINT64 +EFIAPI +VmgExit ( + IN OUT GHCB *Ghcb, + IN UINT64 ExitCode, + IN UINT64 ExitInfo1, + IN UINT64 ExitInfo2 + ); + +/** + Perform pre-VMGEXIT initialization/preparation. + + Performs the necessary steps in preparation for invoking VMGEXIT. Must be + called before setting any fields within the GHCB. + + @param[in, out] Ghcb A pointer to the GHCB + @param[in, out] InterruptState A pointer to hold the current interrupt + state, used for restoring in VmgDone () + +**/ +VOID +EFIAPI +VmgInit ( + IN OUT GHCB *Ghcb, + IN OUT BOOLEAN *InterruptState + ); + +/** + Perform post-VMGEXIT cleanup. + + Performs the necessary steps to cleanup after invoking VMGEXIT. Must be + called after obtaining needed fields within the GHCB. + + @param[in, out] Ghcb A pointer to the GHCB + @param[in] InterruptState An indicator to conditionally (re)enable + interrupts + +**/ +VOID +EFIAPI +VmgDone ( + IN OUT GHCB *Ghcb, + IN BOOLEAN InterruptState + ); + +/** + Marks a specified offset as valid in the GHCB. + + The ValidBitmap area represents the areas of the GHCB that have been marked + valid. Set the bit in ValidBitmap for the input offset. + + @param[in, out] Ghcb A pointer to the GHCB + @param[in] Offset Qword offset in the GHCB to mark valid + +**/ +VOID +EFIAPI +VmgSetOffsetValid ( + IN OUT GHCB *Ghcb, + IN GHCB_REGISTER Offset + ); + +/** + Checks if a specified offset is valid in the GHCB. + + The ValidBitmap area represents the areas of the GHCB that have been marked + valid. Return whether the bit in the ValidBitmap is set for the input offset. + + @param[in] Ghcb A pointer to the GHCB + @param[in] Offset Qword offset in the GHCB to mark valid + + @retval TRUE Offset is marked valid in the GHCB + @retval FALSE Offset is not marked valid in the GHCB + +**/ +BOOLEAN +EFIAPI +VmgIsOffsetValid ( + IN GHCB *Ghcb, + IN GHCB_REGISTER Offset + ); + +/** + Handle a #VC exception. + + Performs the necessary processing to handle a #VC exception. + + The base library function returns an error equal to VC_EXCEPTION, + to be propagated to the standard exception handling stack. + + @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set + as value to use on error. + @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT + + @retval EFI_SUCCESS Exception handled + @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to + propagate provided + @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to + propagate provided + +**/ +EFI_STATUS +EFIAPI +VmgExitHandleVc ( + IN OUT EFI_EXCEPTION_TYPE *ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Ppi/MpServices2.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Ppi/MpServices2.h new file mode 100644 index 00000000..8efa6fed --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Ppi/MpServices2.h @@ -0,0 +1,279 @@ +/** @file + This file declares EDKII Multi-processor service PPI. + + Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EDKII_PEI_MP_SERVICES2_PPI_H__ +#define __EDKII_PEI_MP_SERVICES2_PPI_H__ + +#include <Ppi/MpServices.h> + +#define EDKII_PEI_MP_SERVICES2_PPI_GUID \ + { \ + 0x5cb9cb3d, 0x31a4, 0x480c, { 0x94, 0x98, 0x29, 0xd2, 0x69, 0xba, 0xcf, 0xba} \ + } + +typedef struct _EDKII_PEI_MP_SERVICES2_PPI EDKII_PEI_MP_SERVICES2_PPI; + +/** + Get the number of CPU's. + + @param[in] This Pointer to this instance of the PPI. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors + Number of processors in the system that are enabled. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL. + NumberOfEnabledProcessors is NULL. +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_PEI_MP_SERVICES_GET_NUMBER_OF_PROCESSORS) ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ); + +/** + Get information on a specific CPU. + + @param[in] This Pointer to this instance of the PPI. + @param[in] ProcessorNumber Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] ProcessorInfoBuffer Number of processors in the system that are enabled. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_PEI_MP_SERVICES_GET_PROCESSOR_INFO) ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ); + +/** + Activate all of the application proessors. + + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute the function + specified by Procedure one by one, in ascending order + of processor handle number. If FALSE, then all the + enabled APs execute the function specified by Procedure + simultaneously. + @param[in] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before the + timeout expired. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before all + enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_PEI_MP_SERVICES_STARTUP_ALL_APS) ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL + ); + +/** + Activate a specific application processor. + + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before the + timeout expires. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the + specified AP has finished. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_PEI_MP_SERVICES_STARTUP_THIS_AP) ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ); + +/** + Switch the boot strap processor. + + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an enabled + AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to this + service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or a disabled + AP. + @retval EFI_NOT_READY The specified AP is busy. +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_PEI_MP_SERVICES_SWITCH_BSP) ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ); + +/** + Enable or disable an application processor. + + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for enabled, + FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies the + new health status of the AP. This flag corresponds to + StatusFlag defined in EFI_PEI_MP_SERVICES_PPI.GetProcessorInfo(). + Only the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter is + ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed prior + to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_PEI_MP_SERVICES_ENABLEDISABLEAP) ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ); + +/** + Identify the currently executing processor. + + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[out] ProcessorNumber The handle number of the AP. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned in + ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_PEI_MP_SERVICES_WHOAMI) ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + OUT UINTN *ProcessorNumber + ); + + +/** + Activate all of the application proessors. + + @param[in] This A pointer to the EDKII_PEI_MP_SERVICES2_PPI instance. + @param[in] Procedure A pointer to the function to be run on enabled APs of + the system. + @param[in] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires in blocking + mode, BSP returns EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all CPUs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before the + timeout expired. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before all + enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_PEI_MP_SERVICES_STARTUP_ALL_CPUS) ( + IN EDKII_PEI_MP_SERVICES2_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL + ); + +struct _EDKII_PEI_MP_SERVICES2_PPI { + EDKII_PEI_MP_SERVICES_GET_NUMBER_OF_PROCESSORS GetNumberOfProcessors; + EDKII_PEI_MP_SERVICES_GET_PROCESSOR_INFO GetProcessorInfo; + EDKII_PEI_MP_SERVICES_STARTUP_ALL_APS StartupAllAPs; + EDKII_PEI_MP_SERVICES_STARTUP_THIS_AP StartupThisAP; + EDKII_PEI_MP_SERVICES_SWITCH_BSP SwitchBSP; + EDKII_PEI_MP_SERVICES_ENABLEDISABLEAP EnableDisableAP; + EDKII_PEI_MP_SERVICES_WHOAMI WhoAmI; + EDKII_PEI_MP_SERVICES_STARTUP_ALL_CPUS StartupAllCPUs; +}; + +extern EFI_GUID gEdkiiPeiMpServices2PpiGuid; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Ppi/RepublishSecPpi.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Ppi/RepublishSecPpi.h new file mode 100644 index 00000000..5b5a0879 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Ppi/RepublishSecPpi.h @@ -0,0 +1,54 @@ +/** @file + This file declares Sec Platform Information PPI. + + This service is the primary handoff state into the PEI Foundation. + The Security (SEC) component creates the early, transitory memory + environment and also encapsulates knowledge of at least the + location of the Boot Firmware Volume (BFV). + + Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Revision Reference: + This PPI is introduced in PI Version 1.0. + +**/ + +#ifndef __REPUBLISH_SEC_PPI_H__ +#define __REPUBLISH_SEC_PPI_H__ + +#include <Pi/PiPeiCis.h> + +#define REPUBLISH_SEC_PPI_PPI_GUID \ + { \ + 0x27a71b1e, 0x73ee, 0x43d6, { 0xac, 0xe3, 0x52, 0x1a, 0x2d, 0xc5, 0xd0, 0x92 } \ + } + +typedef struct _REPUBLISH_SEC_PPI_PPI REPUBLISH_SEC_PPI_PPI; + +/** + This interface re-installs PPIs installed in SecCore from a post-memory PEIM. + + This is to allow a platform that may not support relocation of SecCore to update the PPI instance to a post-memory + copy from a PEIM that has been shadowed to permanent memory. + + @retval EFI_SUCCESS The SecCore PPIs were re-installed successfully. + @retval Others An error occurred re-installing the SecCore PPIs. + +**/ +typedef +EFI_STATUS +(EFIAPI *REPUBLISH_SEC_PPI_REPUBLISH_SEC_PPIS)( + VOID + ); + +/// +/// Republish SEC PPIs +/// +struct _REPUBLISH_SEC_PPI_PPI { + REPUBLISH_SEC_PPI_REPUBLISH_SEC_PPIS RepublishSecPpis; +}; + +extern EFI_GUID gRepublishSecPpiPpiGuid; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Ppi/ShadowMicrocode.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Ppi/ShadowMicrocode.h new file mode 100644 index 00000000..cd3f3da5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Ppi/ShadowMicrocode.h @@ -0,0 +1,66 @@ +/** @file + This file declares EDKII Shadow Microcode PPI. + + Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __PPI_SHADOW_MICROCODE_H__ +#define __PPI_SHADOW_MICROCODE_H__ + +#define EDKII_PEI_SHADOW_MICROCODE_PPI_GUID \ + { \ + 0x430f6965, 0x9a69, 0x41c5, { 0x93, 0xed, 0x8b, 0xf0, 0x64, 0x35, 0xc1, 0xc6 } \ + } + +typedef struct _EDKII_PEI_SHADOW_MICROCODE_PPI EDKII_PEI_SHADOW_MICROCODE_PPI; + +typedef struct { + UINT32 ProcessorSignature; + UINT8 PlatformId; +} EDKII_PEI_MICROCODE_CPU_ID; + +/** + Shadow microcode update patches to memory. + + The function is used for shadowing microcode update patches to a continuous memory. + It shall allocate memory buffer and only shadow the microcode patches for those + processors specified by MicrocodeCpuId array. The checksum verification may be + skiped in this function so the caller must perform checksum verification before + using the microcode patches in returned memory buffer. + + @param[in] This The PPI instance pointer. + @param[in] CpuIdCount Number of elements in MicrocodeCpuId array. + @param[in] MicrocodeCpuId A pointer to an array of EDKII_PEI_MICROCODE_CPU_ID + structures. + @param[out] BufferSize Pointer to receive the total size of Buffer. + @param[out] Buffer Pointer to receive address of allocated memory + with microcode patches data in it. + + @retval EFI_SUCCESS The microcode has been shadowed to memory. + @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_PEI_SHADOW_MICROCODE) ( + IN EDKII_PEI_SHADOW_MICROCODE_PPI *This, + IN UINTN CpuIdCount, + IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId, + OUT UINTN *BufferSize, + OUT VOID **Buffer + ); + +/// +/// This PPI is installed by some platform or chipset-specific PEIM that +/// abstracts handling microcode shadow support. +/// +struct _EDKII_PEI_SHADOW_MICROCODE_PPI { + EDKII_PEI_SHADOW_MICROCODE ShadowMicrocode; +}; + +extern EFI_GUID gEdkiiPeiShadowMicrocodePpiGuid; + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Protocol/SmMonitorInit.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Protocol/SmMonitorInit.h new file mode 100644 index 00000000..6522ca21 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Protocol/SmMonitorInit.h @@ -0,0 +1,135 @@ +/** @file + STM service protocol definition + + Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SM_MONITOR_INIT_PROTOCOL_H_ +#define _SM_MONITOR_INIT_PROTOCOL_H_ + +#include <PiSmm.h> +#include <Register/Intel/StmApi.h> + +#define EFI_SM_MONITOR_INIT_PROTOCOL_GUID \ + { 0x228f344d, 0xb3de, 0x43bb, 0xa4, 0xd7, 0xea, 0x20, 0xb, 0x1b, 0x14, 0x82} + +// +// STM service +// + +/** + + Load STM image to MSEG. + + @param StmImage STM image + @param StmImageSize STM image size + + @retval EFI_SUCCESS Load STM to MSEG successfully + @retval EFI_ALREADY_STARTED STM image is already loaded to MSEG + @retval EFI_BUFFER_TOO_SMALL MSEG is smaller than minimal requirement of STM image + @retval EFI_UNSUPPORTED MSEG is not enabled + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SM_MONITOR_LOAD_MONITOR) ( + IN EFI_PHYSICAL_ADDRESS StmImage, + IN UINTN StmImageSize + ); + +/** + + Add resources in list to database. + + @param ResourceList A pointer to resource list to be added + @param NumEntries Optional number of entries. + If 0, list must be terminated by END_OF_RESOURCES. + + @retval EFI_SUCCESS If resources are added + @retval EFI_INVALID_PARAMETER If nested procedure detected resource failer + @retval EFI_OUT_OF_RESOURCES If nested procedure returned it and we cannot allocate more areas. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SM_MONITOR_ADD_PI_RESOURCE) ( + IN STM_RSC *ResourceList, + IN UINT32 NumEntries OPTIONAL + ); + +/** + + Delete resources in list to database. + + @param ResourceList A pointer to resource list to be deleted + NULL means delete all resources. + @param NumEntries Optional number of entries. + If 0, list must be terminated by END_OF_RESOURCES. + + @retval EFI_SUCCESS If resources are deleted + @retval EFI_INVALID_PARAMETER If nested procedure detected resource failer + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SM_MONITOR_DELETE_PI_RESOURCE) ( + IN STM_RSC *ResourceList OPTIONAL, + IN UINT32 NumEntries OPTIONAL + ); + +/** + + Get BIOS resources. + + @param ResourceList A pointer to resource list to be filled + @param ResourceSize On input it means size of resource list input. + On output it means size of resource list filled, + or the size of resource list to be filled if size of too small. + + @retval EFI_SUCCESS If resources are returned. + @retval EFI_BUFFER_TOO_SMALL If resource list buffer is too small to hold the whole resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SM_MONITOR_GET_PI_RESOURCE) ( + OUT STM_RSC *ResourceList, + IN OUT UINT32 *ResourceSize + ); + +typedef UINT32 EFI_SM_MONITOR_STATE; +#define EFI_SM_MONITOR_STATE_ENABLED 0x1 +#define EFI_SM_MONITOR_STATE_ACTIVATED 0x2 + +/** + + Get STM state + + @return STM state + +**/ +typedef +EFI_SM_MONITOR_STATE +(EFIAPI *EFI_SM_MONITOR_GET_MONITOR_STATE) ( + VOID + ); + +typedef struct _EFI_SM_MONITOR_INIT_PROTOCOL { + // + // Valid at boot-time only + // + EFI_SM_MONITOR_LOAD_MONITOR LoadMonitor; + EFI_SM_MONITOR_ADD_PI_RESOURCE AddPiResource; + EFI_SM_MONITOR_DELETE_PI_RESOURCE DeletePiResource; + EFI_SM_MONITOR_GET_PI_RESOURCE GetPiResource; + // + // Valid at runtime + // + EFI_SM_MONITOR_GET_MONITOR_STATE GetMonitorState; +} EFI_SM_MONITOR_INIT_PROTOCOL; + +extern EFI_GUID gEfiSmMonitorInitProtocolGuid; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Protocol/SmmCpuService.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Protocol/SmmCpuService.h new file mode 100644 index 00000000..55ad76fb --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Protocol/SmmCpuService.h @@ -0,0 +1,203 @@ +/** @file +SMM CPU Service protocol definition. + +Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SMM_CPU_SERVICE_PROTOCOL_H_ +#define _SMM_CPU_SERVICE_PROTOCOL_H_ + +// +// Share some definitions with MP Services and CPU Arch Protocol +// +#include <Protocol/MpService.h> +#include <Protocol/Cpu.h> + +#define EFI_SMM_CPU_SERVICE_PROTOCOL_GUID \ + { \ + 0x1d202cab, 0xc8ab, 0x4d5c, { 0x94, 0xf7, 0x3c, 0xfc, 0xc0, 0xd3, 0xd3, 0x35 } \ + } + +typedef struct _EFI_SMM_CPU_SERVICE_PROTOCOL EFI_SMM_CPU_SERVICE_PROTOCOL; + +// +// Protocol functions +// + +/** + Gets processor information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL + instance. + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. +**/ +typedef +EFI_STATUS +(EFIAPI * EFI_SMM_GET_PROCESSOR_INFO) ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ); + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. The new BSP can take over the + execution of the old BSP and continue seamlessly from where the old one left + off. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of AP that is to become the new + BSP. The range is from 0 to the total number of + logical processors minus 1. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to + this service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_SUCCESS The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or + a disabled AP. + @retval EFI_NOT_READY The specified AP is busy. + +**/ +typedef +EFI_STATUS +(EFIAPI * EFI_SMM_SWITCH_BSP) ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN UINTN ProcessorNumber + ); + +/** + Notify that a new processor has been added to the system. + + The SMM CPU driver should add the processor to the SMM CPU list. + + If the processor is disabled it won't participate any SMI handler during subsequent SMIs. + + @param This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param ProcessorId The hardware ID of the processor. + @param ProcessorNumber The handle number of processor. + @param ProcessorResource A pointer to EFI_SMM_PROCESSOR_RESOURCE which holds the assigned resources. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Processor already present. + @retval EFI_NOT_READY Space for a new handle could not be allocated. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SMM_ADD_PROCESSOR) ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN UINT64 ProcessorId, + OUT UINTN *ProcessorNumber + ); + +/** + Notify that a processor is hot-removed. + + Remove a processor from the CPU list of the SMM CPU driver. After this API is called, the removed processor + must not respond to SMIs in the coherence domain. + + @param This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param ProcessorId The hardware ID of the processor. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND Processor with the hardware ID specified by ProcessorId does not exist. + @retval EFI_NOT_READY Specified AP is busy. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SMM_REMOVE_PROCESSOR) ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN UINTN ProcessorNumber + ); + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + This service returns the processor handle number for the calling processor. + The returned value is in the range from 0 to the total number of logical + processors minus 1. This service may be called from the BSP and APs. + If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER + is returned. Otherwise, the current processors handle number is returned in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of AP that is to become the new + BSP. The range is from 0 to the total number of + logical processors minus 1. + + @retval EFI_SUCCESS The current processor handle number was returned + in ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI * EFI_SMM_WHOAMI) ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + OUT UINTN *ProcessorNumber + ); + +/** + Register exception handler. + + @param This A pointer to the SMM_CPU_SERVICE_PROTOCOL instance. + @param ExceptionType Defines which interrupt or exception to hook. Type EFI_EXCEPTION_TYPE and + the valid values for this parameter are defined in EFI_DEBUG_SUPPORT_PROTOCOL + of the UEFI 2.0 specification. + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER + that is called when a processor interrupt occurs. + If this parameter is NULL, then the handler will be uninstalled. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SMM_REGISTER_EXCEPTION_HANDLER) ( + IN EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ); + +// +// This protocol provides CPU services from SMM. +// +struct _EFI_SMM_CPU_SERVICE_PROTOCOL { + EFI_SMM_GET_PROCESSOR_INFO GetProcessorInfo; + EFI_SMM_SWITCH_BSP SwitchBsp; + EFI_SMM_ADD_PROCESSOR AddProcessor; + EFI_SMM_REMOVE_PROCESSOR RemoveProcessor; + EFI_SMM_WHOAMI WhoAmI; + EFI_SMM_REGISTER_EXCEPTION_HANDLER RegisterExceptionHandler; +}; + +extern EFI_GUID gEfiSmmCpuServiceProtocolGuid; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/ArchitecturalMsr.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/ArchitecturalMsr.h new file mode 100644 index 00000000..9c49b9b9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/ArchitecturalMsr.h @@ -0,0 +1,13 @@ +/** @file + Wrapper header file to include <Register/Intel/ArchitecturalMsr.h> in MdePkg. + + Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef __ARCHITECTURAL_MSR_H__ +#define __ARCHITECTURAL_MSR_H__ + +#include <Register/Intel/ArchitecturalMsr.h> + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/Cpuid.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/Cpuid.h new file mode 100644 index 00000000..fc94aea9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/Cpuid.h @@ -0,0 +1,13 @@ +/** @file + Wrapper header file to include <Register/Intel/Cpuid.h> in MdePkg. + + Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef __CPUID_H__ +#define __CPUID_H__ + +#include <Register/Intel/Cpuid.h> + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/LocalApic.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/LocalApic.h new file mode 100644 index 00000000..29acab08 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/LocalApic.h @@ -0,0 +1,14 @@ +/** @file + Wrapper header file to include <Register/Intel/LocalApic.h> in MdePkg. + + Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef __LOCAL_APIC_H__ +#define __LOCAL_APIC_H__ + +#include <Register/Intel/LocalApic.h> + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/Microcode.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/Microcode.h new file mode 100644 index 00000000..f308ff74 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/Microcode.h @@ -0,0 +1,13 @@ +/** @file + Wrapper header file to include <Register/Intel/Microcode.h> in MdePkg. + + Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef __MICROCODE_H__ +#define __MICROCODE_H__ + +#include <Register/Intel/Microcode.h> + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/Msr.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/Msr.h new file mode 100644 index 00000000..bef85ad4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/Msr.h @@ -0,0 +1,14 @@ +/** @file + Wrapper header file to include <Register/Intel/Msr.h> in MdePkg. + + Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __MSR_H__ +#define __MSR_H__ + +#include <Register/Intel/Msr.h> + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/SmramSaveStateMap.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/SmramSaveStateMap.h new file mode 100644 index 00000000..918300d7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/SmramSaveStateMap.h @@ -0,0 +1,13 @@ +/** @file + Wrapper header file to include <Register/Intel/SmramSaveStateMap.h> in MdePkg. + + Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef __SMRAM_SAVE_STATE_MAP_H__ +#define __SMRAM_SAVE_STATE_MAP_H__ + +#include <Register/Intel/SmramSaveStateMap.h> + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/StmApi.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/StmApi.h new file mode 100644 index 00000000..1cc8c81f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/Register/StmApi.h @@ -0,0 +1,13 @@ +/** @file + Wrapper header file to include <Register/Intel/StmApi.h> in MdePkg. + + Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _STM_API_H_ +#define _STM_API_H_ + +#include <Register/Intel/StmApi.h> + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/StuffRsbNasm.inc b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/StuffRsbNasm.inc new file mode 100644 index 00000000..2f4bc49b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Include/StuffRsbNasm.inc @@ -0,0 +1,50 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Abstract: +; +; This file provides macro definitions for stuffing the Return Stack Buffer (RSB) +; for NASM files. +; +;------------------------------------------------------------------------------ + +%define RSB_STUFF_ENTRIES 0x20 + +; +; parameters: +; @param 1: register to use as counter (e.g. IA32:eax, X64:rax) +; @param 2: stack pointer to restore (IA32:esp, X64:rsp) +; @param 3: the size of a stack frame (IA32:4, X64:8) +; +%macro StuffRsb 3 + mov %1, RSB_STUFF_ENTRIES / 2 + %%Unroll1: + call %%Unroll2 + %%SpecTrap1: + pause + lfence + jmp %%SpecTrap1 + %%Unroll2: + call %%StuffLoop + %%SpecTrap2: + pause + lfence + jmp %%SpecTrap2 + %%StuffLoop: + dec %1 + jnz %%Unroll1 + add %2, RSB_STUFF_ENTRIES * %3 ; Restore the stack pointer +%endmacro + +; +; RSB stuffing macros for IA32 and X64 +; +%macro StuffRsb32 0 + StuffRsb eax, esp, 4 +%endmacro + +%macro StuffRsb64 0 + StuffRsb rax, rsp, 8 +%endmacro diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.c new file mode 100644 index 00000000..6b066671 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.c @@ -0,0 +1,38 @@ +/** @file + This library defines some routines that are generic for IA32 family CPU. + + The library routines are UEFI specification compliant. + + Copyright (c) 2020, AMD Inc. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Register/Intel/Cpuid.h> +#include <Register/Amd/Cpuid.h> + +#include <Library/BaseLib.h> +#include <Library/UefiCpuLib.h> + +/** + Determine if the standard CPU signature is "AuthenticAMD". + + @retval TRUE The CPU signature matches. + @retval FALSE The CPU signature does not match. + +**/ +BOOLEAN +EFIAPI +StandardSignatureIsAuthenticAMD ( + VOID + ) +{ + UINT32 RegEbx; + UINT32 RegEcx; + UINT32 RegEdx; + + AsmCpuid (CPUID_SIGNATURE, NULL, &RegEbx, &RegEcx, &RegEdx); + return (RegEbx == CPUID_SIGNATURE_AUTHENTIC_AMD_EBX && + RegEcx == CPUID_SIGNATURE_AUTHENTIC_AMD_ECX && + RegEdx == CPUID_SIGNATURE_AUTHENTIC_AMD_EDX); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.inf new file mode 100644 index 00000000..894917a3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.inf @@ -0,0 +1,41 @@ +## @file +# This library defines some routines that are generic for IA32 family CPU. +# +# The library routines are UEFI specification compliant. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2020, AMD Inc. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseUefiCpuLib + MODULE_UNI_FILE = BaseUefiCpuLib.uni + FILE_GUID = 34C24FD7-7A90-45c2-89FD-946473D9CE98 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = UefiCpuLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.IA32] + Ia32/InitializeFpu.nasm + +[Sources.X64] + X64/InitializeFpu.nasm + +[Sources] + BaseUefiCpuLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.uni new file mode 100644 index 00000000..83c96cea --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.uni @@ -0,0 +1,16 @@ +// /** @file
+// This library defines some routines that are generic for IA32 family CPU.
+//
+// The library routines are UEFI specification compliant.
+//
+// Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Defines generic routines for IA32 family CPUs."
+
+#string STR_MODULE_DESCRIPTION #language en-US "The library routines comply with the UEFI Specification."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/Ia32/InitializeFpu.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/Ia32/InitializeFpu.nasm new file mode 100644 index 00000000..4ba5953b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/Ia32/InitializeFpu.nasm @@ -0,0 +1,68 @@ +;------------------------------------------------------------------------------ +;* +;* Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR> +;* SPDX-License-Identifier: BSD-2-Clause-Patent +;* +;* +;------------------------------------------------------------------------------ + + SECTION .rodata + +; +; Float control word initial value: +; all exceptions masked, double-precision, round-to-nearest +; +mFpuControlWord: DW 0x27F +; +; Multimedia-extensions control word: +; all exceptions masked, round-to-nearest, flush to zero for masked underflow +; +mMmxControlWord: DD 0x1F80 + + SECTION .text + +; +; Initializes floating point units for requirement of UEFI specification. +; +; This function initializes floating-point control word to 0x027F (all exceptions +; masked,double-precision, round-to-nearest) and multimedia-extensions control word +; (if supported) to 0x1F80 (all exceptions masked, round-to-nearest, flush to zero +; for masked underflow). +; +global ASM_PFX(InitializeFloatingPointUnits) +ASM_PFX(InitializeFloatingPointUnits): + + push ebx + + ; + ; Initialize floating point units + ; + finit + fldcw [mFpuControlWord] + + ; + ; Use CpuId instructuion (CPUID.01H:EDX.SSE[bit 25] = 1) to test + ; whether the processor supports SSE instruction. + ; + mov eax, 1 + cpuid + bt edx, 25 + jnc Done + + ; + ; Set OSFXSR bit 9 in CR4 + ; + mov eax, cr4 + or eax, BIT9 + mov cr4, eax + + ; + ; The processor should support SSE instruction and we can use + ; ldmxcsr instruction + ; + ldmxcsr [mMmxControlWord] +Done: + pop ebx + + ret + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/X64/InitializeFpu.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/X64/InitializeFpu.nasm new file mode 100644 index 00000000..bec21a2d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseUefiCpuLib/X64/InitializeFpu.nasm @@ -0,0 +1,51 @@ +;------------------------------------------------------------------------------ +;* +;* Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR> +;* SPDX-License-Identifier: BSD-2-Clause-Patent +;* +;* +;------------------------------------------------------------------------------ + + SECTION .rodata +; +; Float control word initial value: +; all exceptions masked, double-extended-precision, round-to-nearest +; +mFpuControlWord: DW 0x37F +; +; Multimedia-extensions control word: +; all exceptions masked, round-to-nearest, flush to zero for masked underflow +; +mMmxControlWord: DD 0x1F80 + +DEFAULT REL +SECTION .text + +; +; Initializes floating point units for requirement of UEFI specification. +; +; This function initializes floating-point control word to 0x027F (all exceptions +; masked,double-precision, round-to-nearest) and multimedia-extensions control word +; (if supported) to 0x1F80 (all exceptions masked, round-to-nearest, flush to zero +; for masked underflow). +; +global ASM_PFX(InitializeFloatingPointUnits) +ASM_PFX(InitializeFloatingPointUnits): + + ; + ; Initialize floating point units + ; + finit + fldcw [mFpuControlWord] + + ; + ; Set OSFXSR bit 9 in CR4 + ; + mov rax, cr4 + or rax, BIT9 + mov cr4, rax + + ldmxcsr [mMmxControlWord] + + ret + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.c new file mode 100644 index 00000000..434647ad --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.c @@ -0,0 +1,1254 @@ +/** @file + Local APIC Library. + + This local APIC library instance supports xAPIC mode only. + + Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> + Copyright (c) 2017 - 2020, AMD Inc. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Register/Intel/Cpuid.h> +#include <Register/Amd/Cpuid.h> +#include <Register/Intel/Msr.h> +#include <Register/Intel/LocalApic.h> + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/LocalApicLib.h> +#include <Library/IoLib.h> +#include <Library/TimerLib.h> +#include <Library/PcdLib.h> +#include <Library/UefiCpuLib.h> + +// +// Library internal functions +// + +/** + Determine if the CPU supports the Local APIC Base Address MSR. + + @retval TRUE The CPU supports the Local APIC Base Address MSR. + @retval FALSE The CPU does not support the Local APIC Base Address MSR. + +**/ +BOOLEAN +LocalApicBaseAddressMsrSupported ( + VOID + ) +{ + UINT32 RegEax; + UINTN FamilyId; + + AsmCpuid (1, &RegEax, NULL, NULL, NULL); + FamilyId = BitFieldRead32 (RegEax, 8, 11); + if (FamilyId == 0x04 || FamilyId == 0x05) { + // + // CPUs with a FamilyId of 0x04 or 0x05 do not support the + // Local APIC Base Address MSR + // + return FALSE; + } + return TRUE; +} + +/** + Retrieve the base address of local APIC. + + @return The base address of local APIC. + +**/ +UINTN +EFIAPI +GetLocalApicBaseAddress ( + VOID + ) +{ + MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr; + + if (!LocalApicBaseAddressMsrSupported ()) { + // + // If CPU does not support Local APIC Base Address MSR, then retrieve + // Local APIC Base Address from PCD + // + return PcdGet32 (PcdCpuLocalApicBaseAddress); + } + + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + + return (UINTN)(LShiftU64 ((UINT64) ApicBaseMsr.Bits.ApicBaseHi, 32)) + + (((UINTN)ApicBaseMsr.Bits.ApicBase) << 12); +} + +/** + Set the base address of local APIC. + + If BaseAddress is not aligned on a 4KB boundary, then ASSERT(). + + @param[in] BaseAddress Local APIC base address to be set. + +**/ +VOID +EFIAPI +SetLocalApicBaseAddress ( + IN UINTN BaseAddress + ) +{ + MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr; + + ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0); + + if (!LocalApicBaseAddressMsrSupported ()) { + // + // Ignore set request if the CPU does not support APIC Base Address MSR + // + return; + } + + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + + ApicBaseMsr.Bits.ApicBase = (UINT32) (BaseAddress >> 12); + ApicBaseMsr.Bits.ApicBaseHi = (UINT32) (RShiftU64((UINT64) BaseAddress, 32)); + + AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); +} + +/** + Read from a local APIC register. + + This function reads from a local APIC register either in xAPIC or x2APIC mode. + It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be + accessed using multiple 32-bit loads or stores, so this function only performs + 32-bit read. + + @param MmioOffset The MMIO offset of the local APIC register in xAPIC mode. + It must be 16-byte aligned. + + @return 32-bit Value read from the register. +**/ +UINT32 +EFIAPI +ReadLocalApicReg ( + IN UINTN MmioOffset + ) +{ + ASSERT ((MmioOffset & 0xf) == 0); + ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC); + + return MmioRead32 (GetLocalApicBaseAddress() + MmioOffset); +} + +/** + Write to a local APIC register. + + This function writes to a local APIC register either in xAPIC or x2APIC mode. + It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be + accessed using multiple 32-bit loads or stores, so this function only performs + 32-bit write. + + if the register index is invalid or unsupported in current APIC mode, then ASSERT. + + @param MmioOffset The MMIO offset of the local APIC register in xAPIC mode. + It must be 16-byte aligned. + @param Value Value to be written to the register. +**/ +VOID +EFIAPI +WriteLocalApicReg ( + IN UINTN MmioOffset, + IN UINT32 Value + ) +{ + ASSERT ((MmioOffset & 0xf) == 0); + ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC); + + MmioWrite32 (GetLocalApicBaseAddress() + MmioOffset, Value); +} + +/** + Send an IPI by writing to ICR. + + This function returns after the IPI has been accepted by the target processor. + + @param IcrLow 32-bit value to be written to the low half of ICR. + @param ApicId APIC ID of the target processor if this IPI is targeted for a specific processor. +**/ +VOID +SendIpi ( + IN UINT32 IcrLow, + IN UINT32 ApicId + ) +{ + LOCAL_APIC_ICR_LOW IcrLowReg; + UINT32 IcrHigh; + BOOLEAN InterruptState; + + ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC); + ASSERT (ApicId <= 0xff); + + InterruptState = SaveAndDisableInterrupts (); + + // + // Save existing contents of ICR high 32 bits + // + IcrHigh = ReadLocalApicReg (XAPIC_ICR_HIGH_OFFSET); + + // + // Wait for DeliveryStatus clear in case a previous IPI + // is still being sent + // + do { + IcrLowReg.Uint32 = ReadLocalApicReg (XAPIC_ICR_LOW_OFFSET); + } while (IcrLowReg.Bits.DeliveryStatus != 0); + + // + // For xAPIC, the act of writing to the low doubleword of the ICR causes the IPI to be sent. + // + WriteLocalApicReg (XAPIC_ICR_HIGH_OFFSET, ApicId << 24); + WriteLocalApicReg (XAPIC_ICR_LOW_OFFSET, IcrLow); + + // + // Wait for DeliveryStatus clear again + // + do { + IcrLowReg.Uint32 = ReadLocalApicReg (XAPIC_ICR_LOW_OFFSET); + } while (IcrLowReg.Bits.DeliveryStatus != 0); + + // + // And restore old contents of ICR high + // + WriteLocalApicReg (XAPIC_ICR_HIGH_OFFSET, IcrHigh); + + SetInterruptState (InterruptState); + +} + +// +// Library API implementation functions +// + +/** + Get the current local APIC mode. + + If local APIC is disabled, then ASSERT. + + @retval LOCAL_APIC_MODE_XAPIC current APIC mode is xAPIC. + @retval LOCAL_APIC_MODE_X2APIC current APIC mode is x2APIC. +**/ +UINTN +EFIAPI +GetApicMode ( + VOID + ) +{ + DEBUG_CODE ( + { + MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr; + + // + // Check to see if the CPU supports the APIC Base Address MSR + // + if (LocalApicBaseAddressMsrSupported ()) { + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + // + // Local APIC should have been enabled + // + ASSERT (ApicBaseMsr.Bits.EN != 0); + ASSERT (ApicBaseMsr.Bits.EXTD == 0); + } + } + ); + return LOCAL_APIC_MODE_XAPIC; +} + +/** + Set the current local APIC mode. + + If the specified local APIC mode is not valid, then ASSERT. + If the specified local APIC mode can't be set as current, then ASSERT. + + @param ApicMode APIC mode to be set. + + @note This API must not be called from an interrupt handler or SMI handler. + It may result in unpredictable behavior. +**/ +VOID +EFIAPI +SetApicMode ( + IN UINTN ApicMode + ) +{ + ASSERT (ApicMode == LOCAL_APIC_MODE_XAPIC); + ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC); +} + +/** + Get the initial local APIC ID of the executing processor assigned by hardware upon power on or reset. + + In xAPIC mode, the initial local APIC ID may be different from current APIC ID. + In x2APIC mode, the local APIC ID can't be changed and there is no concept of initial APIC ID. In this case, + the 32-bit local APIC ID is returned as initial APIC ID. + + @return 32-bit initial local APIC ID of the executing processor. +**/ +UINT32 +EFIAPI +GetInitialApicId ( + VOID + ) +{ + UINT32 ApicId; + UINT32 MaxCpuIdIndex; + UINT32 RegEbx; + + ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC); + + // + // Get the max index of basic CPUID + // + AsmCpuid (CPUID_SIGNATURE, &MaxCpuIdIndex, NULL, NULL, NULL); + + // + // If CPUID Leaf B is supported, + // And CPUID.0BH:EBX[15:0] reports a non-zero value, + // Then the initial 32-bit APIC ID = CPUID.0BH:EDX + // Else the initial 8-bit APIC ID = CPUID.1:EBX[31:24] + // + if (MaxCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) { + AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, NULL, &RegEbx, NULL, &ApicId); + if ((RegEbx & (BIT16 - 1)) != 0) { + return ApicId; + } + } + + AsmCpuid (CPUID_VERSION_INFO, NULL, &RegEbx, NULL, NULL); + return RegEbx >> 24; +} + +/** + Get the local APIC ID of the executing processor. + + @return 32-bit local APIC ID of the executing processor. +**/ +UINT32 +EFIAPI +GetApicId ( + VOID + ) +{ + UINT32 ApicId; + + ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC); + + if ((ApicId = GetInitialApicId ()) < 0x100) { + // + // If the initial local APIC ID is less 0x100, read APIC ID from + // XAPIC_ID_OFFSET, otherwise return the initial local APIC ID. + // + ApicId = ReadLocalApicReg (XAPIC_ID_OFFSET); + ApicId >>= 24; + } + return ApicId; +} + +/** + Get the value of the local APIC version register. + + @return the value of the local APIC version register. +**/ +UINT32 +EFIAPI +GetApicVersion ( + VOID + ) +{ + return ReadLocalApicReg (XAPIC_VERSION_OFFSET); +} + +/** + Send a Fixed IPI to a specified target processor. + + This function returns after the IPI has been accepted by the target processor. + + @param ApicId The local APIC ID of the target processor. + @param Vector The vector number of the interrupt being sent. +**/ +VOID +EFIAPI +SendFixedIpi ( + IN UINT32 ApicId, + IN UINT8 Vector + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + IcrLow.Uint32 = 0; + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_FIXED; + IcrLow.Bits.Level = 1; + IcrLow.Bits.Vector = Vector; + SendIpi (IcrLow.Uint32, ApicId); +} + +/** + Send a Fixed IPI to all processors excluding self. + + This function returns after the IPI has been accepted by the target processors. + + @param Vector The vector number of the interrupt being sent. +**/ +VOID +EFIAPI +SendFixedIpiAllExcludingSelf ( + IN UINT8 Vector + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + IcrLow.Uint32 = 0; + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_FIXED; + IcrLow.Bits.Level = 1; + IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF; + IcrLow.Bits.Vector = Vector; + SendIpi (IcrLow.Uint32, 0); +} + +/** + Send a SMI IPI to a specified target processor. + + This function returns after the IPI has been accepted by the target processor. + + @param ApicId Specify the local APIC ID of the target processor. +**/ +VOID +EFIAPI +SendSmiIpi ( + IN UINT32 ApicId + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + IcrLow.Uint32 = 0; + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_SMI; + IcrLow.Bits.Level = 1; + SendIpi (IcrLow.Uint32, ApicId); +} + +/** + Send a SMI IPI to all processors excluding self. + + This function returns after the IPI has been accepted by the target processors. +**/ +VOID +EFIAPI +SendSmiIpiAllExcludingSelf ( + VOID + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + IcrLow.Uint32 = 0; + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_SMI; + IcrLow.Bits.Level = 1; + IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF; + SendIpi (IcrLow.Uint32, 0); +} + +/** + Send an INIT IPI to a specified target processor. + + This function returns after the IPI has been accepted by the target processor. + + @param ApicId Specify the local APIC ID of the target processor. +**/ +VOID +EFIAPI +SendInitIpi ( + IN UINT32 ApicId + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + IcrLow.Uint32 = 0; + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_INIT; + IcrLow.Bits.Level = 1; + SendIpi (IcrLow.Uint32, ApicId); +} + +/** + Send an INIT IPI to all processors excluding self. + + This function returns after the IPI has been accepted by the target processors. +**/ +VOID +EFIAPI +SendInitIpiAllExcludingSelf ( + VOID + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + IcrLow.Uint32 = 0; + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_INIT; + IcrLow.Bits.Level = 1; + IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF; + SendIpi (IcrLow.Uint32, 0); +} + +/** + Send an INIT-Start-up-Start-up IPI sequence to a specified target processor. + + This function returns after the IPI has been accepted by the target processor. + + if StartupRoutine >= 1M, then ASSERT. + if StartupRoutine is not multiple of 4K, then ASSERT. + + @param ApicId Specify the local APIC ID of the target processor. + @param StartupRoutine Points to a start-up routine which is below 1M physical + address and 4K aligned. +**/ +VOID +EFIAPI +SendInitSipiSipi ( + IN UINT32 ApicId, + IN UINT32 StartupRoutine + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + ASSERT (StartupRoutine < 0x100000); + ASSERT ((StartupRoutine & 0xfff) == 0); + + SendInitIpi (ApicId); + MicroSecondDelay (PcdGet32(PcdCpuInitIpiDelayInMicroSeconds)); + IcrLow.Uint32 = 0; + IcrLow.Bits.Vector = (StartupRoutine >> 12); + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP; + IcrLow.Bits.Level = 1; + SendIpi (IcrLow.Uint32, ApicId); + if (!StandardSignatureIsAuthenticAMD ()) { + MicroSecondDelay (200); + SendIpi (IcrLow.Uint32, ApicId); + } +} + +/** + Send an INIT-Start-up-Start-up IPI sequence to all processors excluding self. + + This function returns after the IPI has been accepted by the target processors. + + if StartupRoutine >= 1M, then ASSERT. + if StartupRoutine is not multiple of 4K, then ASSERT. + + @param StartupRoutine Points to a start-up routine which is below 1M physical + address and 4K aligned. +**/ +VOID +EFIAPI +SendInitSipiSipiAllExcludingSelf ( + IN UINT32 StartupRoutine + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + ASSERT (StartupRoutine < 0x100000); + ASSERT ((StartupRoutine & 0xfff) == 0); + + SendInitIpiAllExcludingSelf (); + MicroSecondDelay (PcdGet32(PcdCpuInitIpiDelayInMicroSeconds)); + IcrLow.Uint32 = 0; + IcrLow.Bits.Vector = (StartupRoutine >> 12); + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP; + IcrLow.Bits.Level = 1; + IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF; + SendIpi (IcrLow.Uint32, 0); + if (!StandardSignatureIsAuthenticAMD ()) { + MicroSecondDelay (200); + SendIpi (IcrLow.Uint32, 0); + } +} + +/** + Initialize the state of the SoftwareEnable bit in the Local APIC + Spurious Interrupt Vector register. + + @param Enable If TRUE, then set SoftwareEnable to 1 + If FALSE, then set SoftwareEnable to 0. + +**/ +VOID +EFIAPI +InitializeLocalApicSoftwareEnable ( + IN BOOLEAN Enable + ) +{ + LOCAL_APIC_SVR Svr; + + // + // Set local APIC software-enabled bit. + // + Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET); + if (Enable) { + if (Svr.Bits.SoftwareEnable == 0) { + Svr.Bits.SoftwareEnable = 1; + WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32); + } + } else { + if (Svr.Bits.SoftwareEnable == 1) { + Svr.Bits.SoftwareEnable = 0; + WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32); + } + } +} + +/** + Programming Virtual Wire Mode. + + This function programs the local APIC for virtual wire mode following + the example described in chapter A.3 of the MP 1.4 spec. + + IOxAPIC is not involved in this type of virtual wire mode. +**/ +VOID +EFIAPI +ProgramVirtualWireMode ( + VOID + ) +{ + LOCAL_APIC_SVR Svr; + LOCAL_APIC_LVT_LINT Lint; + + // + // Enable the APIC via SVR and set the spurious interrupt to use Int 00F. + // + Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET); + Svr.Bits.SpuriousVector = 0xf; + Svr.Bits.SoftwareEnable = 1; + WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32); + + // + // Program the LINT0 vector entry as ExtInt. Not masked, edge, active high. + // + Lint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT0_OFFSET); + Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_EXTINT; + Lint.Bits.InputPinPolarity = 0; + Lint.Bits.TriggerMode = 0; + Lint.Bits.Mask = 0; + WriteLocalApicReg (XAPIC_LVT_LINT0_OFFSET, Lint.Uint32); + + // + // Program the LINT0 vector entry as NMI. Not masked, edge, active high. + // + Lint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT1_OFFSET); + Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_NMI; + Lint.Bits.InputPinPolarity = 0; + Lint.Bits.TriggerMode = 0; + Lint.Bits.Mask = 0; + WriteLocalApicReg (XAPIC_LVT_LINT1_OFFSET, Lint.Uint32); +} + +/** + Disable LINT0 & LINT1 interrupts. + + This function sets the mask flag in the LVT LINT0 & LINT1 registers. +**/ +VOID +EFIAPI +DisableLvtInterrupts ( + VOID + ) +{ + LOCAL_APIC_LVT_LINT LvtLint; + + LvtLint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT0_OFFSET); + LvtLint.Bits.Mask = 1; + WriteLocalApicReg (XAPIC_LVT_LINT0_OFFSET, LvtLint.Uint32); + + LvtLint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT1_OFFSET); + LvtLint.Bits.Mask = 1; + WriteLocalApicReg (XAPIC_LVT_LINT1_OFFSET, LvtLint.Uint32); +} + +/** + Read the initial count value from the init-count register. + + @return The initial count value read from the init-count register. +**/ +UINT32 +EFIAPI +GetApicTimerInitCount ( + VOID + ) +{ + return ReadLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET); +} + +/** + Read the current count value from the current-count register. + + @return The current count value read from the current-count register. +**/ +UINT32 +EFIAPI +GetApicTimerCurrentCount ( + VOID + ) +{ + return ReadLocalApicReg (XAPIC_TIMER_CURRENT_COUNT_OFFSET); +} + +/** + Initialize the local APIC timer. + + The local APIC timer is initialized and enabled. + + @param DivideValue The divide value for the DCR. It is one of 1,2,4,8,16,32,64,128. + If it is 0, then use the current divide value in the DCR. + @param InitCount The initial count value. + @param PeriodicMode If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot. + @param Vector The timer interrupt vector number. +**/ +VOID +EFIAPI +InitializeApicTimer ( + IN UINTN DivideValue, + IN UINT32 InitCount, + IN BOOLEAN PeriodicMode, + IN UINT8 Vector + ) +{ + LOCAL_APIC_DCR Dcr; + LOCAL_APIC_LVT_TIMER LvtTimer; + UINT32 Divisor; + + // + // Ensure local APIC is in software-enabled state. + // + InitializeLocalApicSoftwareEnable (TRUE); + + // + // Program init-count register. + // + WriteLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET, InitCount); + + if (DivideValue != 0) { + ASSERT (DivideValue <= 128); + ASSERT (DivideValue == GetPowerOfTwo32((UINT32)DivideValue)); + Divisor = (UINT32)((HighBitSet32 ((UINT32)DivideValue) - 1) & 0x7); + + Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET); + Dcr.Bits.DivideValue1 = (Divisor & 0x3); + Dcr.Bits.DivideValue2 = (Divisor >> 2); + WriteLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET, Dcr.Uint32); + } + + // + // Enable APIC timer interrupt with specified timer mode. + // + LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); + if (PeriodicMode) { + LvtTimer.Bits.TimerMode = 1; + } else { + LvtTimer.Bits.TimerMode = 0; + } + LvtTimer.Bits.Mask = 0; + LvtTimer.Bits.Vector = Vector; + WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32); +} + +/** + Get the state of the local APIC timer. + + This function will ASSERT if the local APIC is not software enabled. + + @param DivideValue Return the divide value for the DCR. It is one of 1,2,4,8,16,32,64,128. + @param PeriodicMode Return the timer mode. If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot. + @param Vector Return the timer interrupt vector number. +**/ +VOID +EFIAPI +GetApicTimerState ( + OUT UINTN *DivideValue OPTIONAL, + OUT BOOLEAN *PeriodicMode OPTIONAL, + OUT UINT8 *Vector OPTIONAL + ) +{ + UINT32 Divisor; + LOCAL_APIC_DCR Dcr; + LOCAL_APIC_LVT_TIMER LvtTimer; + + // + // Check the APIC Software Enable/Disable bit (bit 8) in Spurious-Interrupt + // Vector Register. + // This bit will be 1, if local APIC is software enabled. + // + ASSERT ((ReadLocalApicReg(XAPIC_SPURIOUS_VECTOR_OFFSET) & BIT8) != 0); + + if (DivideValue != NULL) { + Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET); + Divisor = Dcr.Bits.DivideValue1 | (Dcr.Bits.DivideValue2 << 2); + Divisor = (Divisor + 1) & 0x7; + *DivideValue = ((UINTN)1) << Divisor; + } + + if (PeriodicMode != NULL || Vector != NULL) { + LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); + if (PeriodicMode != NULL) { + if (LvtTimer.Bits.TimerMode == 1) { + *PeriodicMode = TRUE; + } else { + *PeriodicMode = FALSE; + } + } + if (Vector != NULL) { + *Vector = (UINT8) LvtTimer.Bits.Vector; + } + } +} + +/** + Enable the local APIC timer interrupt. +**/ +VOID +EFIAPI +EnableApicTimerInterrupt ( + VOID + ) +{ + LOCAL_APIC_LVT_TIMER LvtTimer; + + LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); + LvtTimer.Bits.Mask = 0; + WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32); +} + +/** + Disable the local APIC timer interrupt. +**/ +VOID +EFIAPI +DisableApicTimerInterrupt ( + VOID + ) +{ + LOCAL_APIC_LVT_TIMER LvtTimer; + + LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); + LvtTimer.Bits.Mask = 1; + WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32); +} + +/** + Get the local APIC timer interrupt state. + + @retval TRUE The local APIC timer interrupt is enabled. + @retval FALSE The local APIC timer interrupt is disabled. +**/ +BOOLEAN +EFIAPI +GetApicTimerInterruptState ( + VOID + ) +{ + LOCAL_APIC_LVT_TIMER LvtTimer; + + LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); + return (BOOLEAN)(LvtTimer.Bits.Mask == 0); +} + +/** + Send EOI to the local APIC. +**/ +VOID +EFIAPI +SendApicEoi ( + VOID + ) +{ + WriteLocalApicReg (XAPIC_EOI_OFFSET, 0); +} + +/** + Get the 32-bit address that a device should use to send a Message Signaled + Interrupt (MSI) to the Local APIC of the currently executing processor. + + @return 32-bit address used to send an MSI to the Local APIC. +**/ +UINT32 +EFIAPI +GetApicMsiAddress ( + VOID + ) +{ + LOCAL_APIC_MSI_ADDRESS MsiAddress; + + // + // Return address for an MSI interrupt to be delivered only to the APIC ID + // of the currently executing processor. + // + MsiAddress.Uint32 = 0; + MsiAddress.Bits.BaseAddress = 0xFEE; + MsiAddress.Bits.DestinationId = GetApicId (); + return MsiAddress.Uint32; +} + +/** + Get the 64-bit data value that a device should use to send a Message Signaled + Interrupt (MSI) to the Local APIC of the currently executing processor. + + If Vector is not in range 0x10..0xFE, then ASSERT(). + If DeliveryMode is not supported, then ASSERT(). + + @param Vector The 8-bit interrupt vector associated with the MSI. + Must be in the range 0x10..0xFE + @param DeliveryMode A 3-bit value that specifies how the recept of the MSI + is handled. The only supported values are: + 0: LOCAL_APIC_DELIVERY_MODE_FIXED + 1: LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY + 2: LOCAL_APIC_DELIVERY_MODE_SMI + 4: LOCAL_APIC_DELIVERY_MODE_NMI + 5: LOCAL_APIC_DELIVERY_MODE_INIT + 7: LOCAL_APIC_DELIVERY_MODE_EXTINT + + @param LevelTriggered TRUE specifies a level triggered interrupt. + FALSE specifies an edge triggered interrupt. + @param AssertionLevel Ignored if LevelTriggered is FALSE. + TRUE specifies a level triggered interrupt that active + when the interrupt line is asserted. + FALSE specifies a level triggered interrupt that active + when the interrupt line is deasserted. + + @return 64-bit data value used to send an MSI to the Local APIC. +**/ +UINT64 +EFIAPI +GetApicMsiValue ( + IN UINT8 Vector, + IN UINTN DeliveryMode, + IN BOOLEAN LevelTriggered, + IN BOOLEAN AssertionLevel + ) +{ + LOCAL_APIC_MSI_DATA MsiData; + + ASSERT (Vector >= 0x10 && Vector <= 0xFE); + ASSERT (DeliveryMode < 8 && DeliveryMode != 6 && DeliveryMode != 3); + + MsiData.Uint64 = 0; + MsiData.Bits.Vector = Vector; + MsiData.Bits.DeliveryMode = (UINT32)DeliveryMode; + if (LevelTriggered) { + MsiData.Bits.TriggerMode = 1; + if (AssertionLevel) { + MsiData.Bits.Level = 1; + } + } + return MsiData.Uint64; +} + +/** + Get Package ID/Core ID/Thread ID of a processor. + + The algorithm assumes the target system has symmetry across physical + package boundaries with respect to the number of logical processors + per package, number of cores per package. + + @param[in] InitialApicId Initial APIC ID of the target logical processor. + @param[out] Package Returns the processor package ID. + @param[out] Core Returns the processor core ID. + @param[out] Thread Returns the processor thread ID. +**/ +VOID +EFIAPI +GetProcessorLocationByApicId ( + IN UINT32 InitialApicId, + OUT UINT32 *Package OPTIONAL, + OUT UINT32 *Core OPTIONAL, + OUT UINT32 *Thread OPTIONAL + ) +{ + BOOLEAN TopologyLeafSupported; + CPUID_VERSION_INFO_EBX VersionInfoEbx; + CPUID_VERSION_INFO_EDX VersionInfoEdx; + CPUID_CACHE_PARAMS_EAX CacheParamsEax; + CPUID_EXTENDED_TOPOLOGY_EAX ExtendedTopologyEax; + CPUID_EXTENDED_TOPOLOGY_EBX ExtendedTopologyEbx; + CPUID_EXTENDED_TOPOLOGY_ECX ExtendedTopologyEcx; + CPUID_AMD_EXTENDED_CPU_SIG_ECX AmdExtendedCpuSigEcx; + CPUID_AMD_PROCESSOR_TOPOLOGY_EBX AmdProcessorTopologyEbx; + CPUID_AMD_VIR_PHY_ADDRESS_SIZE_ECX AmdVirPhyAddressSizeEcx; + UINT32 MaxStandardCpuIdIndex; + UINT32 MaxExtendedCpuIdIndex; + UINT32 SubIndex; + UINTN LevelType; + UINT32 MaxLogicProcessorsPerPackage; + UINT32 MaxCoresPerPackage; + UINTN ThreadBits; + UINTN CoreBits; + + // + // Check if the processor is capable of supporting more than one logical processor. + // + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32); + if (VersionInfoEdx.Bits.HTT == 0) { + if (Thread != NULL) { + *Thread = 0; + } + if (Core != NULL) { + *Core = 0; + } + if (Package != NULL) { + *Package = 0; + } + return; + } + + // + // Assume three-level mapping of APIC ID: Package|Core|Thread. + // + ThreadBits = 0; + CoreBits = 0; + + // + // Get max index of CPUID + // + AsmCpuid (CPUID_SIGNATURE, &MaxStandardCpuIdIndex, NULL, NULL, NULL); + AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedCpuIdIndex, NULL, NULL, NULL); + + // + // If the extended topology enumeration leaf is available, it + // is the preferred mechanism for enumerating topology. + // + TopologyLeafSupported = FALSE; + if (MaxStandardCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) { + AsmCpuidEx( + CPUID_EXTENDED_TOPOLOGY, + 0, + &ExtendedTopologyEax.Uint32, + &ExtendedTopologyEbx.Uint32, + &ExtendedTopologyEcx.Uint32, + NULL + ); + // + // If CPUID.(EAX=0BH, ECX=0H):EBX returns zero and maximum input value for + // basic CPUID information is greater than 0BH, then CPUID.0BH leaf is not + // supported on that processor. + // + if (ExtendedTopologyEbx.Uint32 != 0) { + TopologyLeafSupported = TRUE; + + // + // Sub-leaf index 0 (ECX= 0 as input) provides enumeration parameters to extract + // the SMT sub-field of x2APIC ID. + // + LevelType = ExtendedTopologyEcx.Bits.LevelType; + ASSERT (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT); + ThreadBits = ExtendedTopologyEax.Bits.ApicIdShift; + + // + // Software must not assume any "level type" encoding + // value to be related to any sub-leaf index, except sub-leaf 0. + // + SubIndex = 1; + do { + AsmCpuidEx ( + CPUID_EXTENDED_TOPOLOGY, + SubIndex, + &ExtendedTopologyEax.Uint32, + NULL, + &ExtendedTopologyEcx.Uint32, + NULL + ); + LevelType = ExtendedTopologyEcx.Bits.LevelType; + if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE) { + CoreBits = ExtendedTopologyEax.Bits.ApicIdShift - ThreadBits; + break; + } + SubIndex++; + } while (LevelType != CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID); + } + } + + if (!TopologyLeafSupported) { + // + // Get logical processor count + // + AsmCpuid (CPUID_VERSION_INFO, NULL, &VersionInfoEbx.Uint32, NULL, NULL); + MaxLogicProcessorsPerPackage = VersionInfoEbx.Bits.MaximumAddressableIdsForLogicalProcessors; + + // + // Assume single-core processor + // + MaxCoresPerPackage = 1; + + // + // Check for topology extensions on AMD processor + // + if (StandardSignatureIsAuthenticAMD()) { + if (MaxExtendedCpuIdIndex >= CPUID_AMD_PROCESSOR_TOPOLOGY) { + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, &AmdExtendedCpuSigEcx.Uint32, NULL); + if (AmdExtendedCpuSigEcx.Bits.TopologyExtensions != 0) { + // + // Account for max possible thread count to decode ApicId + // + AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, NULL, NULL, &AmdVirPhyAddressSizeEcx.Uint32, NULL); + MaxLogicProcessorsPerPackage = 1 << AmdVirPhyAddressSizeEcx.Bits.ApicIdCoreIdSize; + + // + // Get cores per processor package + // + AsmCpuid (CPUID_AMD_PROCESSOR_TOPOLOGY, NULL, &AmdProcessorTopologyEbx.Uint32, NULL, NULL); + MaxCoresPerPackage = MaxLogicProcessorsPerPackage / (AmdProcessorTopologyEbx.Bits.ThreadsPerCore + 1); + } + } + } + else { + // + // Extract core count based on CACHE information + // + if (MaxStandardCpuIdIndex >= CPUID_CACHE_PARAMS) { + AsmCpuidEx (CPUID_CACHE_PARAMS, 0, &CacheParamsEax.Uint32, NULL, NULL, NULL); + if (CacheParamsEax.Uint32 != 0) { + MaxCoresPerPackage = CacheParamsEax.Bits.MaximumAddressableIdsForLogicalProcessors + 1; + } + } + } + + ThreadBits = (UINTN)(HighBitSet32(MaxLogicProcessorsPerPackage / MaxCoresPerPackage - 1) + 1); + CoreBits = (UINTN)(HighBitSet32(MaxCoresPerPackage - 1) + 1); + } + + if (Thread != NULL) { + *Thread = InitialApicId & ((1 << ThreadBits) - 1); + } + if (Core != NULL) { + *Core = (InitialApicId >> ThreadBits) & ((1 << CoreBits) - 1); + } + if (Package != NULL) { + *Package = (InitialApicId >> (ThreadBits + CoreBits)); + } +} + +/** + Get Package ID/Die ID/Tile ID/Module ID/Core ID/Thread ID of a processor. + + The algorithm assumes the target system has symmetry across physical + package boundaries with respect to the number of threads per core, number of + cores per module, number of modules per tile, number of tiles per die, number + of dies per package. + + @param[in] InitialApicId Initial APIC ID of the target logical processor. + @param[out] Package Returns the processor package ID. + @param[out] Die Returns the processor die ID. + @param[out] Tile Returns the processor tile ID. + @param[out] Module Returns the processor module ID. + @param[out] Core Returns the processor core ID. + @param[out] Thread Returns the processor thread ID. +**/ +VOID +EFIAPI +GetProcessorLocation2ByApicId ( + IN UINT32 InitialApicId, + OUT UINT32 *Package OPTIONAL, + OUT UINT32 *Die OPTIONAL, + OUT UINT32 *Tile OPTIONAL, + OUT UINT32 *Module OPTIONAL, + OUT UINT32 *Core OPTIONAL, + OUT UINT32 *Thread OPTIONAL + ) +{ + CPUID_EXTENDED_TOPOLOGY_EAX ExtendedTopologyEax; + CPUID_EXTENDED_TOPOLOGY_ECX ExtendedTopologyEcx; + UINT32 MaxStandardCpuIdIndex; + UINT32 Index; + UINTN LevelType; + UINT32 Bits[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 2]; + UINT32 *Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 2]; + + for (LevelType = 0; LevelType < ARRAY_SIZE (Bits); LevelType++) { + Bits[LevelType] = 0; + } + + // + // Get max index of CPUID + // + AsmCpuid (CPUID_SIGNATURE, &MaxStandardCpuIdIndex, NULL, NULL, NULL); + if (MaxStandardCpuIdIndex < CPUID_V2_EXTENDED_TOPOLOGY) { + if (Die != NULL) { + *Die = 0; + } + if (Tile != NULL) { + *Tile = 0; + } + if (Module != NULL) { + *Module = 0; + } + GetProcessorLocationByApicId (InitialApicId, Package, Core, Thread); + return; + } + + // + // If the V2 extended topology enumeration leaf is available, it + // is the preferred mechanism for enumerating topology. + // + for (Index = 0; ; Index++) { + AsmCpuidEx( + CPUID_V2_EXTENDED_TOPOLOGY, + Index, + &ExtendedTopologyEax.Uint32, + NULL, + &ExtendedTopologyEcx.Uint32, + NULL + ); + + LevelType = ExtendedTopologyEcx.Bits.LevelType; + + // + // first level reported should be SMT. + // + ASSERT ((Index != 0) || (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT)); + if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID) { + break; + } + ASSERT (LevelType < ARRAY_SIZE (Bits)); + Bits[LevelType] = ExtendedTopologyEax.Bits.ApicIdShift; + } + + for (LevelType = CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE; LevelType < ARRAY_SIZE (Bits); LevelType++) { + // + // If there are more levels between level-1 (low-level) and level-2 (high-level), the unknown levels will be ignored + // and treated as an extension of the last known level (i.e., level-1 in this case). + // + if (Bits[LevelType] == 0) { + Bits[LevelType] = Bits[LevelType - 1]; + } + } + + Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 1] = Package; + Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE ] = Die; + Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_TILE ] = Tile; + Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_MODULE ] = Module; + Location[CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE ] = Core; + Location[CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT ] = Thread; + + Bits[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 1] = 32; + + for ( LevelType = CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT + ; LevelType <= CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 1 + ; LevelType ++ + ) { + if (Location[LevelType] != NULL) { + // + // Bits[i] holds the number of bits to shift right on x2APIC ID to get a unique + // topology ID of the next level type. + // + *Location[LevelType] = InitialApicId >> Bits[LevelType - 1]; + + // + // Bits[i] - Bits[i-1] holds the number of bits for the next ONE level type. + // + *Location[LevelType] &= (1 << (Bits[LevelType] - Bits[LevelType - 1])) - 1; + } + } +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.inf new file mode 100644 index 00000000..c55a7382 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.inf @@ -0,0 +1,45 @@ +## @file +# The Local Apic library supports xAPIC mode only. +# +# Note: Local APIC library assumes local APIC is enabled. It does not handle cases +# where local APIC is disabled. +# +# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2020, AMD Inc. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseXApicLib + MODULE_UNI_FILE = BaseXApicLib.uni + FILE_GUID = D87CA0A8-1AC2-439b-90F8-EF4A2AC88DAF + MODULE_TYPE = BASE + VERSION_STRING = 1.1 + LIBRARY_CLASS = LocalApicLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + BaseXApicLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + TimerLib + IoLib + PcdLib + UefiCpuLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuInitIpiDelayInMicroSeconds ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress ## SOMETIMES_CONSUMES diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.uni new file mode 100644 index 00000000..1e645f59 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.uni @@ -0,0 +1,17 @@ +// /** @file
+// The Local Apic library supports xAPIC mode only.
+//
+// Note: Local APIC library assumes local APIC is enabled. It does not handle cases
+// where local APIC is disabled.
+//
+// Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Supports xAPIC mode only"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Note: Local APIC library assumes local APIC is enabled. It does not handle cases where local APIC is disabled."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c new file mode 100644 index 00000000..5bd4c2b7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c @@ -0,0 +1,1349 @@ +/** @file + Local APIC Library. + + This local APIC library instance supports x2APIC capable processors + which have xAPIC and x2APIC modes. + + Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> + Copyright (c) 2017 - 2020, AMD Inc. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Register/Intel/Cpuid.h> +#include <Register/Amd/Cpuid.h> +#include <Register/Intel/Msr.h> +#include <Register/Intel/LocalApic.h> + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/LocalApicLib.h> +#include <Library/IoLib.h> +#include <Library/TimerLib.h> +#include <Library/PcdLib.h> +#include <Library/UefiCpuLib.h> + +// +// Library internal functions +// + +/** + Determine if the CPU supports the Local APIC Base Address MSR. + + @retval TRUE The CPU supports the Local APIC Base Address MSR. + @retval FALSE The CPU does not support the Local APIC Base Address MSR. + +**/ +BOOLEAN +LocalApicBaseAddressMsrSupported ( + VOID + ) +{ + UINT32 RegEax; + UINTN FamilyId; + + AsmCpuid (1, &RegEax, NULL, NULL, NULL); + FamilyId = BitFieldRead32 (RegEax, 8, 11); + if (FamilyId == 0x04 || FamilyId == 0x05) { + // + // CPUs with a FamilyId of 0x04 or 0x05 do not support the + // Local APIC Base Address MSR + // + return FALSE; + } + return TRUE; +} + +/** + Retrieve the base address of local APIC. + + @return The base address of local APIC. + +**/ +UINTN +EFIAPI +GetLocalApicBaseAddress ( + VOID + ) +{ + MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr; + + if (!LocalApicBaseAddressMsrSupported ()) { + // + // If CPU does not support Local APIC Base Address MSR, then retrieve + // Local APIC Base Address from PCD + // + return PcdGet32 (PcdCpuLocalApicBaseAddress); + } + + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + + return (UINTN)(LShiftU64 ((UINT64) ApicBaseMsr.Bits.ApicBaseHi, 32)) + + (((UINTN)ApicBaseMsr.Bits.ApicBase) << 12); +} + +/** + Set the base address of local APIC. + + If BaseAddress is not aligned on a 4KB boundary, then ASSERT(). + + @param[in] BaseAddress Local APIC base address to be set. + +**/ +VOID +EFIAPI +SetLocalApicBaseAddress ( + IN UINTN BaseAddress + ) +{ + MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr; + + ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0); + + if (!LocalApicBaseAddressMsrSupported ()) { + // + // Ignore set request of the CPU does not support APIC Base Address MSR + // + return; + } + + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + + ApicBaseMsr.Bits.ApicBase = (UINT32) (BaseAddress >> 12); + ApicBaseMsr.Bits.ApicBaseHi = (UINT32) (RShiftU64((UINT64) BaseAddress, 32)); + + AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); +} + +/** + Read from a local APIC register. + + This function reads from a local APIC register either in xAPIC or x2APIC mode. + It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be + accessed using multiple 32-bit loads or stores, so this function only performs + 32-bit read. + + @param MmioOffset The MMIO offset of the local APIC register in xAPIC mode. + It must be 16-byte aligned. + + @return 32-bit Value read from the register. +**/ +UINT32 +EFIAPI +ReadLocalApicReg ( + IN UINTN MmioOffset + ) +{ + UINT32 MsrIndex; + + ASSERT ((MmioOffset & 0xf) == 0); + + if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { + return MmioRead32 (GetLocalApicBaseAddress() + MmioOffset); + } else { + // + // DFR is not supported in x2APIC mode. + // + ASSERT (MmioOffset != XAPIC_ICR_DFR_OFFSET); + // + // Note that in x2APIC mode, ICR is a 64-bit MSR that needs special treatment. It + // is not supported in this function for simplicity. + // + ASSERT (MmioOffset != XAPIC_ICR_HIGH_OFFSET); + + MsrIndex = (UINT32)(MmioOffset >> 4) + X2APIC_MSR_BASE_ADDRESS; + return AsmReadMsr32 (MsrIndex); + } +} + +/** + Write to a local APIC register. + + This function writes to a local APIC register either in xAPIC or x2APIC mode. + It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be + accessed using multiple 32-bit loads or stores, so this function only performs + 32-bit write. + + if the register index is invalid or unsupported in current APIC mode, then ASSERT. + + @param MmioOffset The MMIO offset of the local APIC register in xAPIC mode. + It must be 16-byte aligned. + @param Value Value to be written to the register. +**/ +VOID +EFIAPI +WriteLocalApicReg ( + IN UINTN MmioOffset, + IN UINT32 Value + ) +{ + UINT32 MsrIndex; + + ASSERT ((MmioOffset & 0xf) == 0); + + if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { + MmioWrite32 (GetLocalApicBaseAddress() + MmioOffset, Value); + } else { + // + // DFR is not supported in x2APIC mode. + // + ASSERT (MmioOffset != XAPIC_ICR_DFR_OFFSET); + // + // Note that in x2APIC mode, ICR is a 64-bit MSR that needs special treatment. It + // is not supported in this function for simplicity. + // + ASSERT (MmioOffset != XAPIC_ICR_HIGH_OFFSET); + ASSERT (MmioOffset != XAPIC_ICR_LOW_OFFSET); + + MsrIndex = (UINT32)(MmioOffset >> 4) + X2APIC_MSR_BASE_ADDRESS; + // + // The serializing semantics of WRMSR are relaxed when writing to the APIC registers. + // Use memory fence here to force the serializing semantics to be consisent with xAPIC mode. + // + MemoryFence (); + AsmWriteMsr32 (MsrIndex, Value); + } +} + +/** + Send an IPI by writing to ICR. + + This function returns after the IPI has been accepted by the target processor. + + @param IcrLow 32-bit value to be written to the low half of ICR. + @param ApicId APIC ID of the target processor if this IPI is targeted for a specific processor. +**/ +VOID +SendIpi ( + IN UINT32 IcrLow, + IN UINT32 ApicId + ) +{ + UINT64 MsrValue; + LOCAL_APIC_ICR_LOW IcrLowReg; + UINTN LocalApciBaseAddress; + UINT32 IcrHigh; + BOOLEAN InterruptState; + + // + // Legacy APIC or X2APIC? + // + if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { + ASSERT (ApicId <= 0xff); + + InterruptState = SaveAndDisableInterrupts (); + + // + // Get base address of this LAPIC + // + LocalApciBaseAddress = GetLocalApicBaseAddress(); + + // + // Save existing contents of ICR high 32 bits + // + IcrHigh = MmioRead32 (LocalApciBaseAddress + XAPIC_ICR_HIGH_OFFSET); + + // + // Wait for DeliveryStatus clear in case a previous IPI + // is still being sent + // + do { + IcrLowReg.Uint32 = MmioRead32 (LocalApciBaseAddress + XAPIC_ICR_LOW_OFFSET); + } while (IcrLowReg.Bits.DeliveryStatus != 0); + + // + // For xAPIC, the act of writing to the low doubleword of the ICR causes the IPI to be sent. + // + MmioWrite32 (LocalApciBaseAddress + XAPIC_ICR_HIGH_OFFSET, ApicId << 24); + MmioWrite32 (LocalApciBaseAddress + XAPIC_ICR_LOW_OFFSET, IcrLow); + + // + // Wait for DeliveryStatus clear again + // + do { + IcrLowReg.Uint32 = MmioRead32 (LocalApciBaseAddress + XAPIC_ICR_LOW_OFFSET); + } while (IcrLowReg.Bits.DeliveryStatus != 0); + + // + // And restore old contents of ICR high + // + MmioWrite32 (LocalApciBaseAddress + XAPIC_ICR_HIGH_OFFSET, IcrHigh); + + SetInterruptState (InterruptState); + + } else { + // + // For x2APIC, A single MSR write to the Interrupt Command Register is required for dispatching an + // interrupt in x2APIC mode. + // + MsrValue = LShiftU64 ((UINT64) ApicId, 32) | IcrLow; + AsmWriteMsr64 (X2APIC_MSR_ICR_ADDRESS, MsrValue); + } +} + +// +// Library API implementation functions +// + +/** + Get the current local APIC mode. + + If local APIC is disabled, then ASSERT. + + @retval LOCAL_APIC_MODE_XAPIC current APIC mode is xAPIC. + @retval LOCAL_APIC_MODE_X2APIC current APIC mode is x2APIC. +**/ +UINTN +EFIAPI +GetApicMode ( + VOID + ) +{ + MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr; + + if (!LocalApicBaseAddressMsrSupported ()) { + // + // If CPU does not support APIC Base Address MSR, then return XAPIC mode + // + return LOCAL_APIC_MODE_XAPIC; + } + + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + // + // Local APIC should have been enabled + // + ASSERT (ApicBaseMsr.Bits.EN != 0); + if (ApicBaseMsr.Bits.EXTD != 0) { + return LOCAL_APIC_MODE_X2APIC; + } else { + return LOCAL_APIC_MODE_XAPIC; + } +} + +/** + Set the current local APIC mode. + + If the specified local APIC mode is not valid, then ASSERT. + If the specified local APIC mode can't be set as current, then ASSERT. + + @param ApicMode APIC mode to be set. + + @note This API must not be called from an interrupt handler or SMI handler. + It may result in unpredictable behavior. +**/ +VOID +EFIAPI +SetApicMode ( + IN UINTN ApicMode + ) +{ + UINTN CurrentMode; + MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr; + + if (!LocalApicBaseAddressMsrSupported ()) { + // + // Ignore set request if the CPU does not support APIC Base Address MSR + // + return; + } + + CurrentMode = GetApicMode (); + if (CurrentMode == LOCAL_APIC_MODE_XAPIC) { + switch (ApicMode) { + case LOCAL_APIC_MODE_XAPIC: + break; + case LOCAL_APIC_MODE_X2APIC: + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Bits.EXTD = 1; + AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); + break; + default: + ASSERT (FALSE); + } + } else { + switch (ApicMode) { + case LOCAL_APIC_MODE_XAPIC: + // + // Transition from x2APIC mode to xAPIC mode is a two-step process: + // x2APIC -> Local APIC disabled -> xAPIC + // + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Bits.EXTD = 0; + ApicBaseMsr.Bits.EN = 0; + AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); + ApicBaseMsr.Bits.EN = 1; + AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); + break; + case LOCAL_APIC_MODE_X2APIC: + break; + default: + ASSERT (FALSE); + } + } +} + +/** + Get the initial local APIC ID of the executing processor assigned by hardware upon power on or reset. + + In xAPIC mode, the initial local APIC ID may be different from current APIC ID. + In x2APIC mode, the local APIC ID can't be changed and there is no concept of initial APIC ID. In this case, + the 32-bit local APIC ID is returned as initial APIC ID. + + @return 32-bit initial local APIC ID of the executing processor. +**/ +UINT32 +EFIAPI +GetInitialApicId ( + VOID + ) +{ + UINT32 ApicId; + UINT32 MaxCpuIdIndex; + UINT32 RegEbx; + + if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { + // + // Get the max index of basic CPUID + // + AsmCpuid (CPUID_SIGNATURE, &MaxCpuIdIndex, NULL, NULL, NULL); + // + // If CPUID Leaf B is supported, + // And CPUID.0BH:EBX[15:0] reports a non-zero value, + // Then the initial 32-bit APIC ID = CPUID.0BH:EDX + // Else the initial 8-bit APIC ID = CPUID.1:EBX[31:24] + // + if (MaxCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) { + AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, NULL, &RegEbx, NULL, &ApicId); + if ((RegEbx & (BIT16 - 1)) != 0) { + return ApicId; + } + } + AsmCpuid (CPUID_VERSION_INFO, NULL, &RegEbx, NULL, NULL); + return RegEbx >> 24; + } else { + return GetApicId (); + } +} + +/** + Get the local APIC ID of the executing processor. + + @return 32-bit local APIC ID of the executing processor. +**/ +UINT32 +EFIAPI +GetApicId ( + VOID + ) +{ + UINT32 ApicId; + UINT32 InitApicId; + + ApicId = ReadLocalApicReg (XAPIC_ID_OFFSET); + if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { + ApicId = ((InitApicId = GetInitialApicId ()) < 0x100) ? (ApicId >> 24) : InitApicId; + } + + return ApicId; +} + +/** + Get the value of the local APIC version register. + + @return the value of the local APIC version register. +**/ +UINT32 +EFIAPI +GetApicVersion ( + VOID + ) +{ + return ReadLocalApicReg (XAPIC_VERSION_OFFSET); +} + +/** + Send a Fixed IPI to a specified target processor. + + This function returns after the IPI has been accepted by the target processor. + + @param ApicId The local APIC ID of the target processor. + @param Vector The vector number of the interrupt being sent. +**/ +VOID +EFIAPI +SendFixedIpi ( + IN UINT32 ApicId, + IN UINT8 Vector + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + IcrLow.Uint32 = 0; + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_FIXED; + IcrLow.Bits.Level = 1; + IcrLow.Bits.Vector = Vector; + SendIpi (IcrLow.Uint32, ApicId); +} + +/** + Send a Fixed IPI to all processors excluding self. + + This function returns after the IPI has been accepted by the target processors. + + @param Vector The vector number of the interrupt being sent. +**/ +VOID +EFIAPI +SendFixedIpiAllExcludingSelf ( + IN UINT8 Vector + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + IcrLow.Uint32 = 0; + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_FIXED; + IcrLow.Bits.Level = 1; + IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF; + IcrLow.Bits.Vector = Vector; + SendIpi (IcrLow.Uint32, 0); +} + +/** + Send a SMI IPI to a specified target processor. + + This function returns after the IPI has been accepted by the target processor. + + @param ApicId Specify the local APIC ID of the target processor. +**/ +VOID +EFIAPI +SendSmiIpi ( + IN UINT32 ApicId + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + IcrLow.Uint32 = 0; + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_SMI; + IcrLow.Bits.Level = 1; + SendIpi (IcrLow.Uint32, ApicId); +} + +/** + Send a SMI IPI to all processors excluding self. + + This function returns after the IPI has been accepted by the target processors. +**/ +VOID +EFIAPI +SendSmiIpiAllExcludingSelf ( + VOID + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + IcrLow.Uint32 = 0; + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_SMI; + IcrLow.Bits.Level = 1; + IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF; + SendIpi (IcrLow.Uint32, 0); +} + +/** + Send an INIT IPI to a specified target processor. + + This function returns after the IPI has been accepted by the target processor. + + @param ApicId Specify the local APIC ID of the target processor. +**/ +VOID +EFIAPI +SendInitIpi ( + IN UINT32 ApicId + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + IcrLow.Uint32 = 0; + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_INIT; + IcrLow.Bits.Level = 1; + SendIpi (IcrLow.Uint32, ApicId); +} + +/** + Send an INIT IPI to all processors excluding self. + + This function returns after the IPI has been accepted by the target processors. +**/ +VOID +EFIAPI +SendInitIpiAllExcludingSelf ( + VOID + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + IcrLow.Uint32 = 0; + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_INIT; + IcrLow.Bits.Level = 1; + IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF; + SendIpi (IcrLow.Uint32, 0); +} + +/** + Send an INIT-Start-up-Start-up IPI sequence to a specified target processor. + + This function returns after the IPI has been accepted by the target processor. + + if StartupRoutine >= 1M, then ASSERT. + if StartupRoutine is not multiple of 4K, then ASSERT. + + @param ApicId Specify the local APIC ID of the target processor. + @param StartupRoutine Points to a start-up routine which is below 1M physical + address and 4K aligned. +**/ +VOID +EFIAPI +SendInitSipiSipi ( + IN UINT32 ApicId, + IN UINT32 StartupRoutine + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + ASSERT (StartupRoutine < 0x100000); + ASSERT ((StartupRoutine & 0xfff) == 0); + + SendInitIpi (ApicId); + MicroSecondDelay (PcdGet32(PcdCpuInitIpiDelayInMicroSeconds)); + IcrLow.Uint32 = 0; + IcrLow.Bits.Vector = (StartupRoutine >> 12); + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP; + IcrLow.Bits.Level = 1; + SendIpi (IcrLow.Uint32, ApicId); + if (!StandardSignatureIsAuthenticAMD ()) { + MicroSecondDelay (200); + SendIpi (IcrLow.Uint32, ApicId); + } +} + +/** + Send an INIT-Start-up-Start-up IPI sequence to all processors excluding self. + + This function returns after the IPI has been accepted by the target processors. + + if StartupRoutine >= 1M, then ASSERT. + if StartupRoutine is not multiple of 4K, then ASSERT. + + @param StartupRoutine Points to a start-up routine which is below 1M physical + address and 4K aligned. +**/ +VOID +EFIAPI +SendInitSipiSipiAllExcludingSelf ( + IN UINT32 StartupRoutine + ) +{ + LOCAL_APIC_ICR_LOW IcrLow; + + ASSERT (StartupRoutine < 0x100000); + ASSERT ((StartupRoutine & 0xfff) == 0); + + SendInitIpiAllExcludingSelf (); + MicroSecondDelay (PcdGet32(PcdCpuInitIpiDelayInMicroSeconds)); + IcrLow.Uint32 = 0; + IcrLow.Bits.Vector = (StartupRoutine >> 12); + IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP; + IcrLow.Bits.Level = 1; + IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF; + SendIpi (IcrLow.Uint32, 0); + if (!StandardSignatureIsAuthenticAMD ()) { + MicroSecondDelay (200); + SendIpi (IcrLow.Uint32, 0); + } +} + +/** + Initialize the state of the SoftwareEnable bit in the Local APIC + Spurious Interrupt Vector register. + + @param Enable If TRUE, then set SoftwareEnable to 1 + If FALSE, then set SoftwareEnable to 0. + +**/ +VOID +EFIAPI +InitializeLocalApicSoftwareEnable ( + IN BOOLEAN Enable + ) +{ + LOCAL_APIC_SVR Svr; + + // + // Set local APIC software-enabled bit. + // + Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET); + if (Enable) { + if (Svr.Bits.SoftwareEnable == 0) { + Svr.Bits.SoftwareEnable = 1; + WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32); + } + } else { + if (Svr.Bits.SoftwareEnable == 1) { + Svr.Bits.SoftwareEnable = 0; + WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32); + } + } +} + +/** + Programming Virtual Wire Mode. + + This function programs the local APIC for virtual wire mode following + the example described in chapter A.3 of the MP 1.4 spec. + + IOxAPIC is not involved in this type of virtual wire mode. +**/ +VOID +EFIAPI +ProgramVirtualWireMode ( + VOID + ) +{ + LOCAL_APIC_SVR Svr; + LOCAL_APIC_LVT_LINT Lint; + + // + // Enable the APIC via SVR and set the spurious interrupt to use Int 00F. + // + Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET); + Svr.Bits.SpuriousVector = 0xf; + Svr.Bits.SoftwareEnable = 1; + WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32); + + // + // Program the LINT0 vector entry as ExtInt. Not masked, edge, active high. + // + Lint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT0_OFFSET); + Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_EXTINT; + Lint.Bits.InputPinPolarity = 0; + Lint.Bits.TriggerMode = 0; + Lint.Bits.Mask = 0; + WriteLocalApicReg (XAPIC_LVT_LINT0_OFFSET, Lint.Uint32); + + // + // Program the LINT0 vector entry as NMI. Not masked, edge, active high. + // + Lint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT1_OFFSET); + Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_NMI; + Lint.Bits.InputPinPolarity = 0; + Lint.Bits.TriggerMode = 0; + Lint.Bits.Mask = 0; + WriteLocalApicReg (XAPIC_LVT_LINT1_OFFSET, Lint.Uint32); +} + +/** + Disable LINT0 & LINT1 interrupts. + + This function sets the mask flag in the LVT LINT0 & LINT1 registers. +**/ +VOID +EFIAPI +DisableLvtInterrupts ( + VOID + ) +{ + LOCAL_APIC_LVT_LINT LvtLint; + + LvtLint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT0_OFFSET); + LvtLint.Bits.Mask = 1; + WriteLocalApicReg (XAPIC_LVT_LINT0_OFFSET, LvtLint.Uint32); + + LvtLint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT1_OFFSET); + LvtLint.Bits.Mask = 1; + WriteLocalApicReg (XAPIC_LVT_LINT1_OFFSET, LvtLint.Uint32); +} + +/** + Read the initial count value from the init-count register. + + @return The initial count value read from the init-count register. +**/ +UINT32 +EFIAPI +GetApicTimerInitCount ( + VOID + ) +{ + return ReadLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET); +} + +/** + Read the current count value from the current-count register. + + @return The current count value read from the current-count register. +**/ +UINT32 +EFIAPI +GetApicTimerCurrentCount ( + VOID + ) +{ + return ReadLocalApicReg (XAPIC_TIMER_CURRENT_COUNT_OFFSET); +} + +/** + Initialize the local APIC timer. + + The local APIC timer is initialized and enabled. + + @param DivideValue The divide value for the DCR. It is one of 1,2,4,8,16,32,64,128. + If it is 0, then use the current divide value in the DCR. + @param InitCount The initial count value. + @param PeriodicMode If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot. + @param Vector The timer interrupt vector number. +**/ +VOID +EFIAPI +InitializeApicTimer ( + IN UINTN DivideValue, + IN UINT32 InitCount, + IN BOOLEAN PeriodicMode, + IN UINT8 Vector + ) +{ + LOCAL_APIC_DCR Dcr; + LOCAL_APIC_LVT_TIMER LvtTimer; + UINT32 Divisor; + + // + // Ensure local APIC is in software-enabled state. + // + InitializeLocalApicSoftwareEnable (TRUE); + + // + // Program init-count register. + // + WriteLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET, InitCount); + + if (DivideValue != 0) { + ASSERT (DivideValue <= 128); + ASSERT (DivideValue == GetPowerOfTwo32((UINT32)DivideValue)); + Divisor = (UINT32)((HighBitSet32 ((UINT32)DivideValue) - 1) & 0x7); + + Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET); + Dcr.Bits.DivideValue1 = (Divisor & 0x3); + Dcr.Bits.DivideValue2 = (Divisor >> 2); + WriteLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET, Dcr.Uint32); + } + + // + // Enable APIC timer interrupt with specified timer mode. + // + LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); + if (PeriodicMode) { + LvtTimer.Bits.TimerMode = 1; + } else { + LvtTimer.Bits.TimerMode = 0; + } + LvtTimer.Bits.Mask = 0; + LvtTimer.Bits.Vector = Vector; + WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32); +} + +/** + Get the state of the local APIC timer. + + This function will ASSERT if the local APIC is not software enabled. + + @param DivideValue Return the divide value for the DCR. It is one of 1,2,4,8,16,32,64,128. + @param PeriodicMode Return the timer mode. If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot. + @param Vector Return the timer interrupt vector number. +**/ +VOID +EFIAPI +GetApicTimerState ( + OUT UINTN *DivideValue OPTIONAL, + OUT BOOLEAN *PeriodicMode OPTIONAL, + OUT UINT8 *Vector OPTIONAL + ) +{ + UINT32 Divisor; + LOCAL_APIC_DCR Dcr; + LOCAL_APIC_LVT_TIMER LvtTimer; + + // + // Check the APIC Software Enable/Disable bit (bit 8) in Spurious-Interrupt + // Vector Register. + // This bit will be 1, if local APIC is software enabled. + // + ASSERT ((ReadLocalApicReg(XAPIC_SPURIOUS_VECTOR_OFFSET) & BIT8) != 0); + + if (DivideValue != NULL) { + Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET); + Divisor = Dcr.Bits.DivideValue1 | (Dcr.Bits.DivideValue2 << 2); + Divisor = (Divisor + 1) & 0x7; + *DivideValue = ((UINTN)1) << Divisor; + } + + if (PeriodicMode != NULL || Vector != NULL) { + LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); + if (PeriodicMode != NULL) { + if (LvtTimer.Bits.TimerMode == 1) { + *PeriodicMode = TRUE; + } else { + *PeriodicMode = FALSE; + } + } + if (Vector != NULL) { + *Vector = (UINT8) LvtTimer.Bits.Vector; + } + } +} + +/** + Enable the local APIC timer interrupt. +**/ +VOID +EFIAPI +EnableApicTimerInterrupt ( + VOID + ) +{ + LOCAL_APIC_LVT_TIMER LvtTimer; + + LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); + LvtTimer.Bits.Mask = 0; + WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32); +} + +/** + Disable the local APIC timer interrupt. +**/ +VOID +EFIAPI +DisableApicTimerInterrupt ( + VOID + ) +{ + LOCAL_APIC_LVT_TIMER LvtTimer; + + LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); + LvtTimer.Bits.Mask = 1; + WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32); +} + +/** + Get the local APIC timer interrupt state. + + @retval TRUE The local APIC timer interrupt is enabled. + @retval FALSE The local APIC timer interrupt is disabled. +**/ +BOOLEAN +EFIAPI +GetApicTimerInterruptState ( + VOID + ) +{ + LOCAL_APIC_LVT_TIMER LvtTimer; + + LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); + return (BOOLEAN)(LvtTimer.Bits.Mask == 0); +} + +/** + Send EOI to the local APIC. +**/ +VOID +EFIAPI +SendApicEoi ( + VOID + ) +{ + WriteLocalApicReg (XAPIC_EOI_OFFSET, 0); +} + +/** + Get the 32-bit address that a device should use to send a Message Signaled + Interrupt (MSI) to the Local APIC of the currently executing processor. + + @return 32-bit address used to send an MSI to the Local APIC. +**/ +UINT32 +EFIAPI +GetApicMsiAddress ( + VOID + ) +{ + LOCAL_APIC_MSI_ADDRESS MsiAddress; + + // + // Return address for an MSI interrupt to be delivered only to the APIC ID + // of the currently executing processor. + // + MsiAddress.Uint32 = 0; + MsiAddress.Bits.BaseAddress = 0xFEE; + MsiAddress.Bits.DestinationId = GetApicId (); + return MsiAddress.Uint32; +} + +/** + Get the 64-bit data value that a device should use to send a Message Signaled + Interrupt (MSI) to the Local APIC of the currently executing processor. + + If Vector is not in range 0x10..0xFE, then ASSERT(). + If DeliveryMode is not supported, then ASSERT(). + + @param Vector The 8-bit interrupt vector associated with the MSI. + Must be in the range 0x10..0xFE + @param DeliveryMode A 3-bit value that specifies how the recept of the MSI + is handled. The only supported values are: + 0: LOCAL_APIC_DELIVERY_MODE_FIXED + 1: LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY + 2: LOCAL_APIC_DELIVERY_MODE_SMI + 4: LOCAL_APIC_DELIVERY_MODE_NMI + 5: LOCAL_APIC_DELIVERY_MODE_INIT + 7: LOCAL_APIC_DELIVERY_MODE_EXTINT + + @param LevelTriggered TRUE specifies a level triggered interrupt. + FALSE specifies an edge triggered interrupt. + @param AssertionLevel Ignored if LevelTriggered is FALSE. + TRUE specifies a level triggered interrupt that active + when the interrupt line is asserted. + FALSE specifies a level triggered interrupt that active + when the interrupt line is deasserted. + + @return 64-bit data value used to send an MSI to the Local APIC. +**/ +UINT64 +EFIAPI +GetApicMsiValue ( + IN UINT8 Vector, + IN UINTN DeliveryMode, + IN BOOLEAN LevelTriggered, + IN BOOLEAN AssertionLevel + ) +{ + LOCAL_APIC_MSI_DATA MsiData; + + ASSERT (Vector >= 0x10 && Vector <= 0xFE); + ASSERT (DeliveryMode < 8 && DeliveryMode != 6 && DeliveryMode != 3); + + MsiData.Uint64 = 0; + MsiData.Bits.Vector = Vector; + MsiData.Bits.DeliveryMode = (UINT32)DeliveryMode; + if (LevelTriggered) { + MsiData.Bits.TriggerMode = 1; + if (AssertionLevel) { + MsiData.Bits.Level = 1; + } + } + return MsiData.Uint64; +} + +/** + Get Package ID/Core ID/Thread ID of a processor. + + The algorithm assumes the target system has symmetry across physical + package boundaries with respect to the number of logical processors + per package, number of cores per package. + + @param[in] InitialApicId Initial APIC ID of the target logical processor. + @param[out] Package Returns the processor package ID. + @param[out] Core Returns the processor core ID. + @param[out] Thread Returns the processor thread ID. +**/ +VOID +EFIAPI +GetProcessorLocationByApicId ( + IN UINT32 InitialApicId, + OUT UINT32 *Package OPTIONAL, + OUT UINT32 *Core OPTIONAL, + OUT UINT32 *Thread OPTIONAL + ) +{ + BOOLEAN TopologyLeafSupported; + CPUID_VERSION_INFO_EBX VersionInfoEbx; + CPUID_VERSION_INFO_EDX VersionInfoEdx; + CPUID_CACHE_PARAMS_EAX CacheParamsEax; + CPUID_EXTENDED_TOPOLOGY_EAX ExtendedTopologyEax; + CPUID_EXTENDED_TOPOLOGY_EBX ExtendedTopologyEbx; + CPUID_EXTENDED_TOPOLOGY_ECX ExtendedTopologyEcx; + CPUID_AMD_EXTENDED_CPU_SIG_ECX AmdExtendedCpuSigEcx; + CPUID_AMD_PROCESSOR_TOPOLOGY_EBX AmdProcessorTopologyEbx; + CPUID_AMD_VIR_PHY_ADDRESS_SIZE_ECX AmdVirPhyAddressSizeEcx; + UINT32 MaxStandardCpuIdIndex; + UINT32 MaxExtendedCpuIdIndex; + UINT32 SubIndex; + UINTN LevelType; + UINT32 MaxLogicProcessorsPerPackage; + UINT32 MaxCoresPerPackage; + UINTN ThreadBits; + UINTN CoreBits; + + // + // Check if the processor is capable of supporting more than one logical processor. + // + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32); + if (VersionInfoEdx.Bits.HTT == 0) { + if (Thread != NULL) { + *Thread = 0; + } + if (Core != NULL) { + *Core = 0; + } + if (Package != NULL) { + *Package = 0; + } + return; + } + + // + // Assume three-level mapping of APIC ID: Package|Core|Thread. + // + ThreadBits = 0; + CoreBits = 0; + + // + // Get max index of CPUID + // + AsmCpuid (CPUID_SIGNATURE, &MaxStandardCpuIdIndex, NULL, NULL, NULL); + AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedCpuIdIndex, NULL, NULL, NULL); + + // + // If the extended topology enumeration leaf is available, it + // is the preferred mechanism for enumerating topology. + // + TopologyLeafSupported = FALSE; + if (MaxStandardCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) { + AsmCpuidEx( + CPUID_EXTENDED_TOPOLOGY, + 0, + &ExtendedTopologyEax.Uint32, + &ExtendedTopologyEbx.Uint32, + &ExtendedTopologyEcx.Uint32, + NULL + ); + // + // If CPUID.(EAX=0BH, ECX=0H):EBX returns zero and maximum input value for + // basic CPUID information is greater than 0BH, then CPUID.0BH leaf is not + // supported on that processor. + // + if (ExtendedTopologyEbx.Uint32 != 0) { + TopologyLeafSupported = TRUE; + + // + // Sub-leaf index 0 (ECX= 0 as input) provides enumeration parameters to extract + // the SMT sub-field of x2APIC ID. + // + LevelType = ExtendedTopologyEcx.Bits.LevelType; + ASSERT (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT); + ThreadBits = ExtendedTopologyEax.Bits.ApicIdShift; + + // + // Software must not assume any "level type" encoding + // value to be related to any sub-leaf index, except sub-leaf 0. + // + SubIndex = 1; + do { + AsmCpuidEx ( + CPUID_EXTENDED_TOPOLOGY, + SubIndex, + &ExtendedTopologyEax.Uint32, + NULL, + &ExtendedTopologyEcx.Uint32, + NULL + ); + LevelType = ExtendedTopologyEcx.Bits.LevelType; + if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE) { + CoreBits = ExtendedTopologyEax.Bits.ApicIdShift - ThreadBits; + break; + } + SubIndex++; + } while (LevelType != CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID); + } + } + + if (!TopologyLeafSupported) { + // + // Get logical processor count + // + AsmCpuid (CPUID_VERSION_INFO, NULL, &VersionInfoEbx.Uint32, NULL, NULL); + MaxLogicProcessorsPerPackage = VersionInfoEbx.Bits.MaximumAddressableIdsForLogicalProcessors; + + // + // Assume single-core processor + // + MaxCoresPerPackage = 1; + + // + // Check for topology extensions on AMD processor + // + if (StandardSignatureIsAuthenticAMD()) { + if (MaxExtendedCpuIdIndex >= CPUID_AMD_PROCESSOR_TOPOLOGY) { + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, &AmdExtendedCpuSigEcx.Uint32, NULL); + if (AmdExtendedCpuSigEcx.Bits.TopologyExtensions != 0) { + // + // Account for max possible thread count to decode ApicId + // + AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, NULL, NULL, &AmdVirPhyAddressSizeEcx.Uint32, NULL); + MaxLogicProcessorsPerPackage = 1 << AmdVirPhyAddressSizeEcx.Bits.ApicIdCoreIdSize; + + // + // Get cores per processor package + // + AsmCpuid (CPUID_AMD_PROCESSOR_TOPOLOGY, NULL, &AmdProcessorTopologyEbx.Uint32, NULL, NULL); + MaxCoresPerPackage = MaxLogicProcessorsPerPackage / (AmdProcessorTopologyEbx.Bits.ThreadsPerCore + 1); + } + } + } + else { + // + // Extract core count based on CACHE information + // + if (MaxStandardCpuIdIndex >= CPUID_CACHE_PARAMS) { + AsmCpuidEx (CPUID_CACHE_PARAMS, 0, &CacheParamsEax.Uint32, NULL, NULL, NULL); + if (CacheParamsEax.Uint32 != 0) { + MaxCoresPerPackage = CacheParamsEax.Bits.MaximumAddressableIdsForLogicalProcessors + 1; + } + } + } + + ThreadBits = (UINTN)(HighBitSet32(MaxLogicProcessorsPerPackage / MaxCoresPerPackage - 1) + 1); + CoreBits = (UINTN)(HighBitSet32(MaxCoresPerPackage - 1) + 1); + } + + if (Thread != NULL) { + *Thread = InitialApicId & ((1 << ThreadBits) - 1); + } + if (Core != NULL) { + *Core = (InitialApicId >> ThreadBits) & ((1 << CoreBits) - 1); + } + if (Package != NULL) { + *Package = (InitialApicId >> (ThreadBits + CoreBits)); + } +} + +/** + Get Package ID/Die ID/Tile ID/Module ID/Core ID/Thread ID of a processor. + + The algorithm assumes the target system has symmetry across physical + package boundaries with respect to the number of threads per core, number of + cores per module, number of modules per tile, number of tiles per die, number + of dies per package. + + @param[in] InitialApicId Initial APIC ID of the target logical processor. + @param[out] Package Returns the processor package ID. + @param[out] Die Returns the processor die ID. + @param[out] Tile Returns the processor tile ID. + @param[out] Module Returns the processor module ID. + @param[out] Core Returns the processor core ID. + @param[out] Thread Returns the processor thread ID. +**/ +VOID +EFIAPI +GetProcessorLocation2ByApicId ( + IN UINT32 InitialApicId, + OUT UINT32 *Package OPTIONAL, + OUT UINT32 *Die OPTIONAL, + OUT UINT32 *Tile OPTIONAL, + OUT UINT32 *Module OPTIONAL, + OUT UINT32 *Core OPTIONAL, + OUT UINT32 *Thread OPTIONAL + ) +{ + CPUID_EXTENDED_TOPOLOGY_EAX ExtendedTopologyEax; + CPUID_EXTENDED_TOPOLOGY_ECX ExtendedTopologyEcx; + UINT32 MaxStandardCpuIdIndex; + UINT32 Index; + UINTN LevelType; + UINT32 Bits[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 2]; + UINT32 *Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 2]; + + for (LevelType = 0; LevelType < ARRAY_SIZE (Bits); LevelType++) { + Bits[LevelType] = 0; + } + + // + // Get max index of CPUID + // + AsmCpuid (CPUID_SIGNATURE, &MaxStandardCpuIdIndex, NULL, NULL, NULL); + if (MaxStandardCpuIdIndex < CPUID_V2_EXTENDED_TOPOLOGY) { + if (Die != NULL) { + *Die = 0; + } + if (Tile != NULL) { + *Tile = 0; + } + if (Module != NULL) { + *Module = 0; + } + GetProcessorLocationByApicId (InitialApicId, Package, Core, Thread); + return; + } + + // + // If the V2 extended topology enumeration leaf is available, it + // is the preferred mechanism for enumerating topology. + // + for (Index = 0; ; Index++) { + AsmCpuidEx( + CPUID_V2_EXTENDED_TOPOLOGY, + Index, + &ExtendedTopologyEax.Uint32, + NULL, + &ExtendedTopologyEcx.Uint32, + NULL + ); + + LevelType = ExtendedTopologyEcx.Bits.LevelType; + + // + // first level reported should be SMT. + // + ASSERT ((Index != 0) || (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT)); + if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID) { + break; + } + ASSERT (LevelType < ARRAY_SIZE (Bits)); + Bits[LevelType] = ExtendedTopologyEax.Bits.ApicIdShift; + } + + for (LevelType = CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE; LevelType < ARRAY_SIZE (Bits); LevelType++) { + // + // If there are more levels between level-1 (low-level) and level-2 (high-level), the unknown levels will be ignored + // and treated as an extension of the last known level (i.e., level-1 in this case). + // + if (Bits[LevelType] == 0) { + Bits[LevelType] = Bits[LevelType - 1]; + } + } + + Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 1] = Package; + Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE ] = Die; + Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_TILE ] = Tile; + Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_MODULE ] = Module; + Location[CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE ] = Core; + Location[CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT ] = Thread; + + Bits[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 1] = 32; + + for ( LevelType = CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT + ; LevelType <= CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 1 + ; LevelType ++ + ) { + if (Location[LevelType] != NULL) { + // + // Bits[i] holds the number of bits to shift right on x2APIC ID to get a unique + // topology ID of the next level type. + // + *Location[LevelType] = InitialApicId >> Bits[LevelType - 1]; + + // + // Bits[i] - Bits[i-1] holds the number of bits for the next ONE level type. + // + *Location[LevelType] &= (1 << (Bits[LevelType] - Bits[LevelType - 1])) - 1; + } + } +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf new file mode 100644 index 00000000..564c1a97 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf @@ -0,0 +1,45 @@ +## @file +# The Local Apic library supports x2APIC capable processors which have xAPIC and x2APIC modes. +# +# Note: Local APIC library assumes local APIC is enabled. It does not handle cases +# where local APIC is disabled. +# +# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2020, AMD Inc. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseXApicX2ApicLib + MODULE_UNI_FILE = BaseXApicX2ApicLib.uni + FILE_GUID = 967B6E05-F10D-4c10-8BF7-365291CA143F + MODULE_TYPE = BASE + VERSION_STRING = 1.1 + LIBRARY_CLASS = LocalApicLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + BaseXApicX2ApicLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + TimerLib + IoLib + PcdLib + UefiCpuLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuInitIpiDelayInMicroSeconds ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress ## SOMETIMES_CONSUMES diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.uni new file mode 100644 index 00000000..e614c938 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.uni @@ -0,0 +1,17 @@ +// /** @file
+// The Local Apic library supports x2APIC capable processors which have xAPIC and x2APIC modes.
+//
+// Note: Local APIC library assumes local APIC is enabled. It does not handle cases
+// where local APIC is disabled.
+//
+// Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Supports x2APIC capable processors that have xAPIC and x2APIC modes"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Note: Local APIC library assumes local APIC is enabled. It does not handle cases where local APIC is disabled."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/CpuCacheInfoLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/CpuCacheInfoLib.c new file mode 100644 index 00000000..8eee5b77 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/CpuCacheInfoLib.c @@ -0,0 +1,441 @@ +/** @file + Provides cache info for each package, core type, cache level and cache type. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "InternalCpuCacheInfoLib.h" + +/** + Print CpuCacheInfo array. + + @param[in] CpuCacheInfo Pointer to the CpuCacheInfo array. + @param[in] CpuCacheInfoCount The length of CpuCacheInfo array. + +**/ +VOID +CpuCacheInfoPrintCpuCacheInfoTable ( + IN CPU_CACHE_INFO *CpuCacheInfo, + IN UINTN CpuCacheInfoCount + ) +{ + UINTN Index; + + DEBUG ((DEBUG_INFO, "+-------+--------------------------------------------------------------------------------------+\n")); + DEBUG ((DEBUG_INFO, "| Index | Packge CoreType CacheLevel CacheType CacheWays (FA|DM) CacheSizeinKB CacheCount |\n")); + DEBUG ((DEBUG_INFO, "+-------+--------------------------------------------------------------------------------------+\n")); + + for (Index = 0; Index < CpuCacheInfoCount; Index++) { + DEBUG ((DEBUG_INFO, "| %4x | %4x %2x %2x %2x %4x ( %x| %x) %8x %4x |\n", + Index, CpuCacheInfo[Index].Package, CpuCacheInfo[Index].CoreType, CpuCacheInfo[Index].CacheLevel, + CpuCacheInfo[Index].CacheType, CpuCacheInfo[Index].CacheWays, CpuCacheInfo[Index].FullyAssociativeCache, + CpuCacheInfo[Index].DirectMappedCache, CpuCacheInfo[Index].CacheSizeinKB, CpuCacheInfo[Index].CacheCount)); + } + + DEBUG ((DEBUG_INFO, "+-------+--------------------------------------------------------------------------------------+\n")); +} + +/** + Get the total number of package and package ID in the platform. + + @param[in] ProcessorInfo Pointer to the ProcessorInfo array. + @param[in] NumberOfProcessors Total number of logical processors in the platform. + @param[in, out] Package Pointer to the Package array. + + @retval Return the total number of package and package ID in the platform. +**/ +UINT32 +CpuCacheInfoGetNumberOfPackages ( + IN CPUID_PROCESSOR_INFO *ProcessorInfo, + IN UINTN NumberOfProcessors, + IN OUT UINT32 *Package + ) +{ + UINTN ProcessorIndex; + UINT32 PackageIndex; + UINT32 PackageCount; + UINT32 CurrentPackage; + + PackageCount = 0; + + for (ProcessorIndex = 0; ProcessorIndex < NumberOfProcessors; ProcessorIndex++) { + CurrentPackage = ProcessorInfo[ProcessorIndex].Package; + + // + // For the package that already exists in Package array, break out the loop. + // + for (PackageIndex = 0; PackageIndex < PackageCount; PackageIndex++) { + if (CurrentPackage == Package[PackageIndex]) { + break; + } + } + + // + // For the new package, save it in Package array. + // + if (PackageIndex == PackageCount) { + ASSERT (PackageCount < MAX_NUM_OF_PACKAGE); + Package[PackageCount++] = CurrentPackage; + } + } + + return PackageCount; +} + +/** + Get the number of CoreType of requested package. + + @param[in] ProcessorInfo Pointer to the ProcessorInfo array. + @param[in] NumberOfProcessors Total number of logical processors in the platform. + @param[in] Package The requested package number. + + @retval Return the number of CoreType of requested package. +**/ +UINTN +CpuCacheInfoGetNumberOfCoreTypePerPackage( + IN CPUID_PROCESSOR_INFO *ProcessorInfo, + IN UINTN NumberOfProcessors, + IN UINTN Package + ) +{ + UINTN ProcessorIndex; + // + // Core Type value comes from CPUID.1Ah.EAX[31:24]. + // So max number of core types should be MAX_UINT8. + // + UINT8 CoreType[MAX_UINT8]; + UINTN CoreTypeIndex; + UINTN CoreTypeCount; + UINT8 CurrentCoreType; + + // + // CoreType array is empty. + // + CoreTypeCount = 0; + + for (ProcessorIndex = 0; ProcessorIndex < NumberOfProcessors; ProcessorIndex++) { + CurrentCoreType = ProcessorInfo[ProcessorIndex].CoreType; + + if (ProcessorInfo[ProcessorIndex].Package != Package) { + continue; + } + + // + // For the type that already exists in CoreType array, break out the loop. + // + for (CoreTypeIndex = 0; CoreTypeIndex < CoreTypeCount; CoreTypeIndex++) { + if (CurrentCoreType == CoreType[CoreTypeIndex]) { + break; + } + } + + // + // For the new type, save it in CoreType array. + // + if (CoreTypeIndex == CoreTypeCount) { + ASSERT (CoreTypeCount < MAX_UINT8); + CoreType[CoreTypeCount++] = CurrentCoreType; + } + } + + return CoreTypeCount; +} + +/** + Collect core and cache information of calling processor via CPUID instructions. + + @param[in, out] Buffer The pointer to private data buffer. +**/ +VOID +EFIAPI +CpuCacheInfoCollectCoreAndCacheData ( + IN OUT VOID *Buffer + ) +{ + UINTN ProcessorIndex; + UINT32 CpuidMaxInput; + UINT8 CacheParamLeafIndex; + CPUID_CACHE_PARAMS_EAX CacheParamEax; + CPUID_CACHE_PARAMS_EBX CacheParamEbx; + UINT32 CacheParamEcx; + CPUID_CACHE_PARAMS_EDX CacheParamEdx; + CPUID_NATIVE_MODEL_ID_AND_CORE_TYPE_EAX NativeModelIdAndCoreTypeEax; + COLLECT_CPUID_CACHE_DATA_CONTEXT *Context; + CPUID_CACHE_DATA *CacheData; + + Context = (COLLECT_CPUID_CACHE_DATA_CONTEXT *)Buffer; + ProcessorIndex = CpuCacheInfoWhoAmI (Context->MpServices); + CacheData = &Context->CacheData[MAX_NUM_OF_CACHE_PARAMS_LEAF * ProcessorIndex]; + + AsmCpuid (CPUID_SIGNATURE, &CpuidMaxInput, NULL, NULL, NULL); + + // + // get CoreType if CPUID_HYBRID_INFORMATION leaf is supported. + // + Context->ProcessorInfo[ProcessorIndex].CoreType = 0; + if (CpuidMaxInput >= CPUID_HYBRID_INFORMATION) { + AsmCpuidEx (CPUID_HYBRID_INFORMATION, CPUID_HYBRID_INFORMATION_MAIN_LEAF, &NativeModelIdAndCoreTypeEax.Uint32, NULL, NULL, NULL); + Context->ProcessorInfo[ProcessorIndex].CoreType = (UINT8) NativeModelIdAndCoreTypeEax.Bits.CoreType; + } + + // + // cache hierarchy starts with an index value of 0. + // + CacheParamLeafIndex = 0; + + while (CacheParamLeafIndex < MAX_NUM_OF_CACHE_PARAMS_LEAF) { + AsmCpuidEx (CPUID_CACHE_PARAMS, CacheParamLeafIndex, &CacheParamEax.Uint32, &CacheParamEbx.Uint32, &CacheParamEcx, &CacheParamEdx.Uint32); + + if (CacheParamEax.Bits.CacheType == 0) { + break; + } + + CacheData[CacheParamLeafIndex].CacheLevel = (UINT8)CacheParamEax.Bits.CacheLevel; + CacheData[CacheParamLeafIndex].CacheType = (UINT8)CacheParamEax.Bits.CacheType; + CacheData[CacheParamLeafIndex].CacheWays = (UINT16)CacheParamEbx.Bits.Ways; + CacheData[CacheParamLeafIndex].FullyAssociativeCache = (UINT8)CacheParamEax.Bits.FullyAssociativeCache; + CacheData[CacheParamLeafIndex].DirectMappedCache = (UINT8)CacheParamEdx.Bits.ComplexCacheIndexing; + CacheData[CacheParamLeafIndex].CacheShareBits = (UINT16)CacheParamEax.Bits.MaximumAddressableIdsForLogicalProcessors; + CacheData[CacheParamLeafIndex].CacheSizeinKB = (CacheParamEbx.Bits.Ways + 1) * + (CacheParamEbx.Bits.LinePartitions + 1) * (CacheParamEbx.Bits.LineSize + 1) * (CacheParamEcx + 1) / SIZE_1KB; + + CacheParamLeafIndex++; + } +} + +/** + Collect CacheInfo data from the CacheData. + + @param[in] CacheData Pointer to the CacheData array. + @param[in] ProcessorInfo Pointer to the ProcessorInfo array. + @param[in] NumberOfProcessors Total number of logical processors in the platform. + @param[in, out] CacheInfo Pointer to the CacheInfo array. + @param[in, out] CacheInfoCount As input, point to the length of response CacheInfo array. + As output, point to the actual length of response CacheInfo array. + + @retval EFI_SUCCESS Function completed successfully. + @retval EFI_OUT_OF_RESOURCES Required resources could not be allocated. + @retval EFI_BUFFER_TOO_SMALL CacheInfoCount is too small to hold the response CacheInfo + array. CacheInfoCount has been updated with the length needed + to complete the request. +**/ +EFI_STATUS +CpuCacheInfoCollectCpuCacheInfoData ( + IN CPUID_CACHE_DATA *CacheData, + IN CPUID_PROCESSOR_INFO *ProcessorInfo, + IN UINTN NumberOfProcessors, + IN OUT CPU_CACHE_INFO *CacheInfo, + IN OUT UINTN *CacheInfoCount + ) +{ + EFI_STATUS Status; + UINT32 NumberOfPackage; + UINT32 Package[MAX_NUM_OF_PACKAGE]; + UINTN PackageIndex; + UINTN TotalNumberOfCoreType; + UINTN MaxCacheInfoCount; + CPU_CACHE_INFO *LocalCacheInfo; + UINTN CacheInfoIndex; + UINTN LocalCacheInfoCount; + UINTN Index; + UINTN NextIndex; + + // + // Get number of Packages and Package ID. + // + NumberOfPackage = CpuCacheInfoGetNumberOfPackages (ProcessorInfo, NumberOfProcessors, Package); + + // + // Get number of core types for each package and count the total number. + // E.g. If Package1 and Package2 both have 2 core types, the total number is 4. + // + TotalNumberOfCoreType = 0; + for (PackageIndex = 0; PackageIndex < NumberOfPackage; PackageIndex++) { + TotalNumberOfCoreType += CpuCacheInfoGetNumberOfCoreTypePerPackage (ProcessorInfo, NumberOfProcessors, Package[PackageIndex]); + } + + MaxCacheInfoCount = TotalNumberOfCoreType * MAX_NUM_OF_CACHE_PARAMS_LEAF; + LocalCacheInfo = AllocatePages (EFI_SIZE_TO_PAGES (MaxCacheInfoCount * sizeof (*LocalCacheInfo))); + ASSERT (LocalCacheInfo != NULL); + if (LocalCacheInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + LocalCacheInfoCount = 0; + + for (Index = 0; Index < NumberOfProcessors * MAX_NUM_OF_CACHE_PARAMS_LEAF; Index++) { + if (CacheData[Index].CacheSizeinKB == 0) { + continue; + } + + // + // For the sharing caches, clear their CacheSize. + // + for (NextIndex = Index + 1; NextIndex < NumberOfProcessors * MAX_NUM_OF_CACHE_PARAMS_LEAF; NextIndex++) { + if (CacheData[NextIndex].CacheSizeinKB == 0) { + continue; + } + + if (CacheData[Index].CacheLevel == CacheData[NextIndex].CacheLevel && + CacheData[Index].CacheType == CacheData[NextIndex].CacheType && + ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].Package == ProcessorInfo[NextIndex / MAX_NUM_OF_CACHE_PARAMS_LEAF].Package && + ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].CoreType == ProcessorInfo[NextIndex / MAX_NUM_OF_CACHE_PARAMS_LEAF].CoreType && + (ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].ApicId & ~CacheData[Index].CacheShareBits) == + (ProcessorInfo[NextIndex / MAX_NUM_OF_CACHE_PARAMS_LEAF].ApicId & ~CacheData[NextIndex].CacheShareBits)) { + CacheData[NextIndex].CacheSizeinKB = 0; // uses the sharing cache + } + } + + // + // For the cache that already exists in LocalCacheInfo, increase its CacheCount. + // + for (CacheInfoIndex = 0; CacheInfoIndex < LocalCacheInfoCount; CacheInfoIndex++) { + if (LocalCacheInfo[CacheInfoIndex].Package == ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].Package && + LocalCacheInfo[CacheInfoIndex].CoreType == ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].CoreType && + LocalCacheInfo[CacheInfoIndex].CacheLevel == CacheData[Index].CacheLevel && + LocalCacheInfo[CacheInfoIndex].CacheType == CacheData[Index].CacheType) { + LocalCacheInfo[CacheInfoIndex].CacheCount++; + break; + } + } + + // + // For the new cache with different Package, CoreType, CacheLevel or CacheType, copy its + // data into LocalCacheInfo buffer. + // + if (CacheInfoIndex == LocalCacheInfoCount) { + ASSERT (LocalCacheInfoCount < MaxCacheInfoCount); + + LocalCacheInfo[LocalCacheInfoCount].Package = ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].Package; + LocalCacheInfo[LocalCacheInfoCount].CoreType = ProcessorInfo[Index / MAX_NUM_OF_CACHE_PARAMS_LEAF].CoreType; + LocalCacheInfo[LocalCacheInfoCount].CacheLevel = CacheData[Index].CacheLevel; + LocalCacheInfo[LocalCacheInfoCount].CacheType = CacheData[Index].CacheType; + LocalCacheInfo[LocalCacheInfoCount].CacheWays = CacheData[Index].CacheWays; + LocalCacheInfo[LocalCacheInfoCount].FullyAssociativeCache = CacheData[Index].FullyAssociativeCache; + LocalCacheInfo[LocalCacheInfoCount].DirectMappedCache = CacheData[Index].DirectMappedCache; + LocalCacheInfo[LocalCacheInfoCount].CacheSizeinKB = CacheData[Index].CacheSizeinKB; + LocalCacheInfo[LocalCacheInfoCount].CacheCount = 1; + + LocalCacheInfoCount++; + } + } + + if (*CacheInfoCount < LocalCacheInfoCount) { + Status = EFI_BUFFER_TOO_SMALL; + } else { + CopyMem (CacheInfo, LocalCacheInfo, sizeof (*CacheInfo) * LocalCacheInfoCount); + DEBUG_CODE ( + CpuCacheInfoPrintCpuCacheInfoTable (CacheInfo, LocalCacheInfoCount); + ); + Status = EFI_SUCCESS; + } + + *CacheInfoCount = LocalCacheInfoCount; + + FreePages (LocalCacheInfo, EFI_SIZE_TO_PAGES (MaxCacheInfoCount * sizeof (*LocalCacheInfo))); + + return Status; +} + +/** + Get CpuCacheInfo data array. + + @param[in, out] CpuCacheInfo Pointer to the CpuCacheInfo array. + @param[in, out] CpuCacheInfoCount As input, point to the length of response CpuCacheInfo array. + As output, point to the actual length of response CpuCacheInfo array. + + @retval EFI_SUCCESS Function completed successfully. + @retval EFI_INVALID_PARAMETER CpuCacheInfoCount is NULL. + @retval EFI_INVALID_PARAMETER CpuCacheInfo is NULL while CpuCacheInfoCount contains the value + greater than zero. + @retval EFI_UNSUPPORTED Processor does not support CPUID_CACHE_PARAMS Leaf. + @retval EFI_OUT_OF_RESOURCES Required resources could not be allocated. + @retval EFI_BUFFER_TOO_SMALL CpuCacheInfoCount is too small to hold the response CpuCacheInfo + array. CpuCacheInfoCount has been updated with the length needed + to complete the request. +**/ +EFI_STATUS +EFIAPI +GetCpuCacheInfo ( + IN OUT CPU_CACHE_INFO *CpuCacheInfo, + IN OUT UINTN *CpuCacheInfoCount + ) +{ + EFI_STATUS Status; + UINT32 CpuidMaxInput; + UINT32 NumberOfProcessors; + UINTN CacheDataCount; + UINTN ProcessorIndex; + EFI_PROCESSOR_INFORMATION ProcessorInfo; + COLLECT_CPUID_CACHE_DATA_CONTEXT Context; + + if (CpuCacheInfoCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (*CpuCacheInfoCount != 0 && CpuCacheInfo == NULL) { + return EFI_INVALID_PARAMETER; + } + + AsmCpuid (CPUID_SIGNATURE, &CpuidMaxInput, NULL, NULL, NULL); + if (CpuidMaxInput < CPUID_CACHE_PARAMS) { + return EFI_UNSUPPORTED; + } + + // + // Initialize COLLECT_CPUID_CACHE_DATA_CONTEXT.MpServices. + // + CpuCacheInfoGetMpServices (&Context.MpServices); + + NumberOfProcessors = CpuCacheInfoGetNumberOfProcessors (Context.MpServices); + + // + // Initialize COLLECT_CPUID_CACHE_DATA_CONTEXT.ProcessorInfo. + // + Context.ProcessorInfo = AllocatePages (EFI_SIZE_TO_PAGES (NumberOfProcessors * sizeof (*Context.ProcessorInfo))); + ASSERT (Context.ProcessorInfo != NULL); + if (Context.ProcessorInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Initialize COLLECT_CPUID_CACHE_DATA_CONTEXT.CacheData. + // CacheData array consists of CPUID_CACHE_DATA data structure for each Cpuid Cache Parameter Leaf + // per logical processor. The array begin with data of each Cache Parameter Leaf of processor 0, followed + // by data of each Cache Parameter Leaf of processor 1 ... + // + CacheDataCount = NumberOfProcessors * MAX_NUM_OF_CACHE_PARAMS_LEAF; + Context.CacheData = AllocatePages (EFI_SIZE_TO_PAGES (CacheDataCount * sizeof (*Context.CacheData))); + ASSERT (Context.CacheData != NULL); + if (Context.CacheData == NULL) { + FreePages (Context.ProcessorInfo, EFI_SIZE_TO_PAGES (NumberOfProcessors * sizeof (*Context.ProcessorInfo))); + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (Context.CacheData, CacheDataCount * sizeof (*Context.CacheData)); + + // + // Collect Package ID and APIC ID of all processors. + // + for (ProcessorIndex = 0; ProcessorIndex < NumberOfProcessors; ProcessorIndex++) { + CpuCacheInfoGetProcessorInfo (Context.MpServices, ProcessorIndex, &ProcessorInfo); + Context.ProcessorInfo[ProcessorIndex].Package = ProcessorInfo.Location.Package; + Context.ProcessorInfo[ProcessorIndex].ApicId = (UINT32) ProcessorInfo.ProcessorId; + } + + // + // Wakeup all processors for CacheData(core type and cache data) collection. + // + CpuCacheInfoStartupAllCPUs (Context.MpServices, CpuCacheInfoCollectCoreAndCacheData, &Context); + + // + // Collect CpuCacheInfo data from CacheData. + // + Status = CpuCacheInfoCollectCpuCacheInfoData (Context.CacheData, Context.ProcessorInfo, NumberOfProcessors, CpuCacheInfo, CpuCacheInfoCount); + + FreePages (Context.CacheData, EFI_SIZE_TO_PAGES (CacheDataCount * sizeof (*Context.CacheData))); + FreePages (Context.ProcessorInfo, EFI_SIZE_TO_PAGES (NumberOfProcessors * sizeof (*Context.ProcessorInfo))); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/CpuCacheInfoLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/CpuCacheInfoLib.uni new file mode 100644 index 00000000..1bc801f1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/CpuCacheInfoLib.uni @@ -0,0 +1,15 @@ +// /** @file
+// CPU Cache Info Library
+//
+// Provides cache info for each package, core type, cache level and cache type.
+//
+// Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU Cache Info Library"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Provides cache info for each package, core type, cache level and cache type."
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/DxeCpuCacheInfoLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/DxeCpuCacheInfoLib.c new file mode 100644 index 00000000..aa00ddeb --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/DxeCpuCacheInfoLib.c @@ -0,0 +1,127 @@ +/** @file + Provides cache info for each package, core type, cache level and cache type. + + Copyright (c) 2020 Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/CpuCacheInfoLib.h> +#include <InternalCpuCacheInfoLib.h> + +/** + Get EFI_MP_SERVICES_PROTOCOL pointer. + + @param[out] MpServices A pointer to the buffer where EFI_MP_SERVICES_PROTOCOL is stored + + @retval EFI_SUCCESS EFI_MP_SERVICES_PROTOCOL interface is returned + @retval EFI_NOT_FOUND EFI_MP_SERVICES_PROTOCOL interface is not found +**/ +EFI_STATUS +CpuCacheInfoGetMpServices ( + OUT MP_SERVICES *MpServices + ) +{ + EFI_STATUS Status; + + Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpServices->Protocol); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Activate all of the logical processors. + + @param[in] MpServices MP_SERVICES structure. + @param[in] Procedure A pointer to the function to be run on enabled logical processors. + @param[in] ProcedureArgument The parameter passed into Procedure for all enabled logical processors. +**/ +VOID +CpuCacheInfoStartupAllCPUs ( + IN MP_SERVICES MpServices, + IN EFI_AP_PROCEDURE Procedure, + IN VOID *ProcedureArgument + ) +{ + EFI_STATUS Status; + + Status = MpServices.Protocol->StartupAllAPs (MpServices.Protocol, Procedure, FALSE, NULL, 0, ProcedureArgument, NULL); + if (Status == EFI_NOT_STARTED) { + // + // EFI_NOT_STARTED is returned when there is no enabled AP. + // Treat this case as EFI_SUCCESS. + // + Status = EFI_SUCCESS; + } + ASSERT_EFI_ERROR (Status); + + Procedure (ProcedureArgument); +} + +/** + Get detailed information of the requested logical processor. + + @param[in] MpServices MP_SERVICES structure. + @param[in] ProcessorNum The requested logical processor number. + @param[out] ProcessorInfo A pointer to the buffer where the processor information is stored +**/ +VOID +CpuCacheInfoGetProcessorInfo ( + IN MP_SERVICES MpServices, + IN UINTN ProcessorNum, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfo + ) +{ + EFI_STATUS Status; + + Status = MpServices.Protocol->GetProcessorInfo (MpServices.Protocol, ProcessorNum, ProcessorInfo); + ASSERT_EFI_ERROR (Status); +} + +/** + Get the logical processor number. + + @param[in] MpServices MP_SERVICES structure. + + @retval Return the logical processor number. +**/ +UINT32 +CpuCacheInfoWhoAmI ( + IN MP_SERVICES MpServices + ) +{ + EFI_STATUS Status; + UINTN ProcessorNum; + + Status = MpServices.Protocol->WhoAmI (MpServices.Protocol, &ProcessorNum); + ASSERT_EFI_ERROR (Status); + + return (UINT32)ProcessorNum; +} + +/** + Get the total number of logical processors in the platform. + + @param[in] MpServices MP_SERVICES structure. + + @retval Return the total number of logical processors. +**/ +UINT32 +CpuCacheInfoGetNumberOfProcessors ( + IN MP_SERVICES MpServices + ) +{ + EFI_STATUS Status; + UINTN NumberOfProcessor; + UINTN NumberOfEnabledProcessor; + + Status = MpServices.Protocol->GetNumberOfProcessors (MpServices.Protocol, &NumberOfProcessor, &NumberOfEnabledProcessor); + ASSERT_EFI_ERROR (Status); + + return (UINT32)NumberOfProcessor; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/DxeCpuCacheInfoLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/DxeCpuCacheInfoLib.inf new file mode 100644 index 00000000..770e96e7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/DxeCpuCacheInfoLib.inf @@ -0,0 +1,43 @@ +## @file +# CPU Cache Info Library instance for DXE driver. +# +# Provides cache info for each package, core type, cache level and cache type. +# +# Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeCpuCacheInfoLib + FILE_GUID = B25C288F-C309-41F1-8325-37E64DC5EA3D + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = CpuCacheInfoLib|DXE_DRIVER UEFI_APPLICATION + MODULE_UNI_FILE = CpuCacheInfoLib.uni + +[Sources] + InternalCpuCacheInfoLib.h + CpuCacheInfoLib.c + DxeCpuCacheInfoLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + BaseMemoryLib + MemoryAllocationLib + UefiBootServicesTableLib + +[Protocols] + gEfiMpServiceProtocolGuid + +[Pcd] + +[Depex] + gEfiMpServiceProtocolGuid diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/InternalCpuCacheInfoLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/InternalCpuCacheInfoLib.h new file mode 100644 index 00000000..df132e8d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/InternalCpuCacheInfoLib.h @@ -0,0 +1,170 @@ +/** @file + Internal header file for CPU Cache info Library. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _INTERNAL_CPU_CACHE_INFO_LIB_H_ +#define _INTERNAL_CPU_CACHE_INFO_LIB_H_ + +#include <PiPei.h> +#include <Register/Cpuid.h> +#include <Ppi/MpServices2.h> +#include <Protocol/MpService.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/CpuCacheInfoLib.h> + +typedef struct { + // + // Package ID, the information comes from + // EFI_CPU_PHYSICAL_LOCATION.Package + // + UINT32 Package; + // + // APIC ID, the information comes from + // EFI_PROCESSOR_INFORMATION.ProcessorId + // + UINT32 ApicId; + // + // Core type of logical processor. + // Value = CPUID.1Ah:EAX[31:24] + // + UINT8 CoreType; +} CPUID_PROCESSOR_INFO; + +typedef struct { + // + // Level of the cache. + // Value = CPUID.04h:EAX[07:05] + // + UINT8 CacheLevel : 3; + // + // Type of the cache. + // Value = CPUID.04h:EAX[04:00] + // + UINT8 CacheType : 5; + // + // Ways of associativity. + // Value = CPUID.04h:EBX[31:22] + // + UINT16 CacheWays : 10; + // + // Fully associative cache. + // Value = CPUID.04h:EAX[09] + // + UINT16 FullyAssociativeCache : 1; + // + // Direct mapped cache. + // Value = CPUID.04h:EDX[02] + // + UINT16 DirectMappedCache : 1; + UINT16 Reserved : 4; + // + // Cache share bits. + // Value = CPUID.04h:EAX[25:14] + // + UINT16 CacheShareBits; + // + // Size of single cache. + // Value = (CPUID.04h:EBX[31:22] + 1) * (CPUID.04h:EBX[21:12] + 1) * + // (CPUID.04h:EBX[11:00] + 1) * (CPUID.04h:ECX[31:00] + 1) + // + UINT32 CacheSizeinKB; +} CPUID_CACHE_DATA; + +typedef union { + EDKII_PEI_MP_SERVICES2_PPI *Ppi; + EFI_MP_SERVICES_PROTOCOL *Protocol; +} MP_SERVICES; + +typedef struct { + MP_SERVICES MpServices; + CPUID_PROCESSOR_INFO *ProcessorInfo; + CPUID_CACHE_DATA *CacheData; +} COLLECT_CPUID_CACHE_DATA_CONTEXT; + + +/* + Defines the maximum count of Deterministic Cache Parameters Leaf of all APs and BSP. + To save boot time, skip starting up all APs to calculate each AP's count of Deterministic + Cache Parameters Leaf, so use a definition instead. + Anyway, definition value will be checked in CpuCacheInfoCollectCoreAndCacheData function. +*/ +#define MAX_NUM_OF_CACHE_PARAMS_LEAF 6 + +/* + Defines the maximum count of packages. +*/ +#define MAX_NUM_OF_PACKAGE 100 + +/** + Get EDKII_PEI_MP_SERVICES2_PPI or EFI_MP_SERVICES_PROTOCOL pointer. + + @param[out] MpServices A pointer to the buffer where EDKII_PEI_MP_SERVICES2_PPI or + EFI_MP_SERVICES_PROTOCOL is stored + + @retval EFI_SUCCESS EDKII_PEI_MP_SERVICES2_PPI or EFI_MP_SERVICES_PROTOCOL interface is returned + @retval EFI_NOT_FOUND EDKII_PEI_MP_SERVICES2_PPI or EFI_MP_SERVICES_PROTOCOL interface is not found +**/ +EFI_STATUS +CpuCacheInfoGetMpServices ( + OUT MP_SERVICES *MpServices + ); + +/** + Activate all of the logical processors. + + @param[in] MpServices MP_SERVICES structure. + @param[in] Procedure A pointer to the function to be run on enabled logical processors. + @param[in] ProcedureArgument The parameter passed into Procedure for all enabled logical processors. +**/ +VOID +CpuCacheInfoStartupAllCPUs ( + IN MP_SERVICES MpServices, + IN EFI_AP_PROCEDURE Procedure, + IN VOID *ProcedureArgument + ); + +/** + Get detailed information of the requested logical processor. + + @param[in] MpServices MP_SERVICES structure. + @param[in] ProcessorNum The requested logical processor number. + @param[out] ProcessorInfo A pointer to the buffer where the processor information is stored +**/ +VOID +CpuCacheInfoGetProcessorInfo ( + IN MP_SERVICES MpServices, + IN UINTN ProcessorNum, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfo + ); + +/** + Get the logical processor number. + + @param[in] MpServices MP_SERVICES structure. + + @retval Return the logical processor number. +**/ +UINT32 +CpuCacheInfoWhoAmI ( + IN MP_SERVICES MpServices + ); + +/** + Get the total number of logical processors in the platform. + + @param[in] MpServices MP_SERVICES structure. + + @retval Return the total number of logical processors. +**/ +UINT32 +CpuCacheInfoGetNumberOfProcessors ( + IN MP_SERVICES MpServices + ); +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/PeiCpuCacheInfoLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/PeiCpuCacheInfoLib.c new file mode 100644 index 00000000..382a54e2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/PeiCpuCacheInfoLib.c @@ -0,0 +1,119 @@ +/** @file + Provides cache info for each package, core type, cache level and cache type. + + Copyright (c) 2020 Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiPei.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/PeiServicesLib.h> +#include <Library/PeiServicesTablePointerLib.h> +#include <Library/CpuCacheInfoLib.h> +#include <InternalCpuCacheInfoLib.h> + +/** + Get EDKII_PEI_MP_SERVICES2_PPI pointer. + + @param[out] MpServices A pointer to the buffer where EDKII_PEI_MP_SERVICES2_PPI is stored + + @retval EFI_SUCCESS EDKII_PEI_MP_SERVICES2_PPI interface is returned + @retval EFI_NOT_FOUND EDKII_PEI_MP_SERVICES2_PPI interface is not found +**/ +EFI_STATUS +CpuCacheInfoGetMpServices ( + OUT MP_SERVICES *MpServices + ) +{ + EFI_STATUS Status; + + Status = PeiServicesLocatePpi (&gEdkiiPeiMpServices2PpiGuid, 0, NULL, (VOID **)&MpServices->Ppi); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Activate all of the logical processors. + + @param[in] MpServices MP_SERVICES structure. + @param[in] Procedure A pointer to the function to be run on enabled logical processors. + @param[in] ProcedureArgument The parameter passed into Procedure for all enabled logical processors. +**/ +VOID +CpuCacheInfoStartupAllCPUs ( + IN MP_SERVICES MpServices, + IN EFI_AP_PROCEDURE Procedure, + IN VOID *ProcedureArgument + ) +{ + EFI_STATUS Status; + + Status = MpServices.Ppi->StartupAllCPUs (MpServices.Ppi, Procedure, 0, ProcedureArgument); + ASSERT_EFI_ERROR (Status); +} + +/** + Get detailed information of the requested logical processor. + + @param[in] MpServices MP_SERVICES structure. + @param[in] ProcessorNum The requested logical processor number. + @param[out] ProcessorInfo A pointer to the buffer where the processor information is stored +**/ +VOID +CpuCacheInfoGetProcessorInfo ( + IN MP_SERVICES MpServices, + IN UINTN ProcessorNum, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfo + ) +{ + EFI_STATUS Status; + + Status = MpServices.Ppi->GetProcessorInfo (MpServices.Ppi, ProcessorNum, ProcessorInfo); + ASSERT_EFI_ERROR (Status); +} + +/** + Get the logical processor number. + + @param[in] MpServices MP_SERVICES structure. + + @retval Return the logical processor number. +**/ +UINT32 +CpuCacheInfoWhoAmI ( + IN MP_SERVICES MpServices + ) +{ + EFI_STATUS Status; + UINTN ProcessorNum; + + Status = MpServices.Ppi->WhoAmI (MpServices.Ppi, &ProcessorNum); + ASSERT_EFI_ERROR (Status); + + return (UINT32)ProcessorNum; +} + +/** + Get the total number of logical processors in the platform. + + @param[in] MpServices MP_SERVICES structure. + + @retval Return the total number of logical processors. +**/ +UINT32 +CpuCacheInfoGetNumberOfProcessors ( + IN MP_SERVICES MpServices + ) +{ + EFI_STATUS Status; + UINTN NumberOfProcessor; + UINTN NumberOfEnabledProcessor; + + Status = MpServices.Ppi->GetNumberOfProcessors (MpServices.Ppi, &NumberOfProcessor, &NumberOfEnabledProcessor); + ASSERT_EFI_ERROR (Status); + + return (UINT32)NumberOfProcessor; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/PeiCpuCacheInfoLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/PeiCpuCacheInfoLib.inf new file mode 100644 index 00000000..4b7bc54f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCacheInfoLib/PeiCpuCacheInfoLib.inf @@ -0,0 +1,43 @@ +## @file +# CPU Cache Info Library instance for PEI module. +# +# Provides cache info for each package, core type, cache level and cache type. +# +# Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PeiCpuCacheInfoLib + FILE_GUID = CFEE2DBE-53B2-4916-84CA-0BA83C3DDA6E + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + LIBRARY_CLASS = CpuCacheInfoLib|PEIM + MODULE_UNI_FILE = CpuCacheInfoLib.uni + +[Sources] + InternalCpuCacheInfoLib.h + CpuCacheInfoLib.c + PeiCpuCacheInfoLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + BaseMemoryLib + MemoryAllocationLib + PeiServicesTablePointerLib + +[Ppis] + gEdkiiPeiMpServices2PpiGuid + +[Pcd] + +[Depex] + gEdkiiPeiMpServices2PpiGuid diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/Aesni.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/Aesni.c new file mode 100644 index 00000000..1ba239b4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/Aesni.c @@ -0,0 +1,119 @@ +/** @file + AESNI feature. + + Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/** + Prepares for the data used by CPU feature detection and initialization. + + @param[in] NumberOfProcessors The number of CPUs in the platform. + + @return Pointer to a buffer of CPU related configuration data. + + @note This service could be called by BSP only. +**/ +VOID * +EFIAPI +AesniGetConfigData ( + IN UINTN NumberOfProcessors + ) +{ + UINT64 *ConfigData; + + ConfigData = AllocateZeroPool (sizeof (UINT64) * NumberOfProcessors); + ASSERT (ConfigData != NULL); + return ConfigData; +} + +/** + Detects if AESNI feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE AESNI feature is supported. + @retval FALSE AESNI feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +AesniSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + MSR_SANDY_BRIDGE_FEATURE_CONFIG_REGISTER *MsrFeatureConfig; + + if (CpuInfo->CpuIdVersionInfoEcx.Bits.AESNI == 1) { + MsrFeatureConfig = (MSR_SANDY_BRIDGE_FEATURE_CONFIG_REGISTER *) ConfigData; + ASSERT (MsrFeatureConfig != NULL); + MsrFeatureConfig[ProcessorNumber].Uint64 = AsmReadMsr64 (MSR_SANDY_BRIDGE_FEATURE_CONFIG); + return TRUE; + } + return FALSE; +} + +/** + Initializes AESNI feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the AESNI feature must be enabled. + If FALSE, then the AESNI feature must be disabled. + + @retval RETURN_SUCCESS AESNI feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +AesniInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + MSR_SANDY_BRIDGE_FEATURE_CONFIG_REGISTER *MsrFeatureConfig; + + // + // SANDY_BRIDGE, SILVERMONT, XEON_5600, XEON_7, and XEON_PHI have the same MSR index, + // Simply use MSR_SANDY_BRIDGE_FEATURE_CONFIG here + // + // The scope of the MSR_SANDY_BRIDGE_FEATURE_CONFIG is Core, only program MSR_FEATURE_CONFIG for thread 0 + // of each core. Otherwise, once a thread in the core disabled AES, the other thread will cause GP when + // programming it. + // + if (CpuInfo->ProcessorInfo.Location.Thread == 0) { + MsrFeatureConfig = (MSR_SANDY_BRIDGE_FEATURE_CONFIG_REGISTER *) ConfigData; + ASSERT (MsrFeatureConfig != NULL); + if ((MsrFeatureConfig[ProcessorNumber].Bits.AESConfiguration & BIT0) == 0) { + CPU_REGISTER_TABLE_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_SANDY_BRIDGE_FEATURE_CONFIG, + MSR_SANDY_BRIDGE_FEATURE_CONFIG_REGISTER, + Bits.AESConfiguration, + BIT0 | ((State) ? 0 : BIT1) + ); + } + } + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/C1e.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/C1e.c new file mode 100644 index 00000000..5969b2af --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/C1e.c @@ -0,0 +1,81 @@ +/** @file + C1E feature. + + Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/** + Detects if C1E feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE C1E feature is supported. + @retval FALSE C1E feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +C1eSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + return IS_NEHALEM_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel); +} + +/** + Initializes C1E feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the C1E feature must be enabled. + If FALSE, then the C1E feature must be disabled. + + @retval RETURN_SUCCESS C1E feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +C1eInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + // + // The scope of C1EEnable bit in the MSR_NEHALEM_POWER_CTL is Package, only program + // MSR_FEATURE_CONFIG for thread 0 core 0 in each package. + // + if ((CpuInfo->ProcessorInfo.Location.Thread != 0) || (CpuInfo->ProcessorInfo.Location.Core != 0)) { + return RETURN_SUCCESS; + } + + CPU_REGISTER_TABLE_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_NEHALEM_POWER_CTL, + MSR_NEHALEM_POWER_CTL_REGISTER, + Bits.C1EEnable, + (State) ? 1 : 0 + ); + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/ClockModulation.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/ClockModulation.c new file mode 100644 index 00000000..2af0ec22 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/ClockModulation.c @@ -0,0 +1,131 @@ +/** @file + Clock Modulation feature. + + Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +typedef struct { + CPUID_THERMAL_POWER_MANAGEMENT_EAX ThermalPowerManagementEax; + MSR_IA32_CLOCK_MODULATION_REGISTER ClockModulation; +} CLOCK_MODULATION_CONFIG_DATA; + +/** + Prepares for the data used by CPU feature detection and initialization. + + @param[in] NumberOfProcessors The number of CPUs in the platform. + + @return Pointer to a buffer of CPU related configuration data. + + @note This service could be called by BSP only. +**/ +VOID * +EFIAPI +ClockModulationGetConfigData ( + IN UINTN NumberOfProcessors + ) +{ + UINT32 *ConfigData; + + ConfigData = AllocateZeroPool (sizeof (CLOCK_MODULATION_CONFIG_DATA) * NumberOfProcessors); + ASSERT (ConfigData != NULL); + return ConfigData; +} + +/** + Detects if Clock Modulation feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Clock Modulation feature is supported. + @retval FALSE Clock Modulation feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +ClockModulationSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + CLOCK_MODULATION_CONFIG_DATA *CmConfigData; + + if (CpuInfo->CpuIdVersionInfoEdx.Bits.ACPI == 1) { + CmConfigData = (CLOCK_MODULATION_CONFIG_DATA *) ConfigData; + ASSERT (CmConfigData != NULL); + AsmCpuid ( + CPUID_THERMAL_POWER_MANAGEMENT, + &CmConfigData[ProcessorNumber].ThermalPowerManagementEax.Uint32, + NULL, + NULL, + NULL + ); + CmConfigData[ProcessorNumber].ClockModulation.Uint64 = AsmReadMsr64 (MSR_IA32_CLOCK_MODULATION); + return TRUE; + } + return FALSE; +} + +/** + Initializes Clock Modulation feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Clock Modulation feature must be enabled. + If FALSE, then the Clock Modulation feature must be disabled. + + @retval RETURN_SUCCESS Clock Modulation feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +ClockModulationInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + CLOCK_MODULATION_CONFIG_DATA *CmConfigData; + MSR_IA32_CLOCK_MODULATION_REGISTER *ClockModulation; + + CmConfigData = (CLOCK_MODULATION_CONFIG_DATA *) ConfigData; + ASSERT (CmConfigData != NULL); + ClockModulation = &CmConfigData[ProcessorNumber].ClockModulation; + + if (State) { + ClockModulation->Bits.OnDemandClockModulationEnable = 1; + ClockModulation->Bits.OnDemandClockModulationDutyCycle = PcdGet8 (PcdCpuClockModulationDutyCycle) >> 1; + if (CmConfigData[ProcessorNumber].ThermalPowerManagementEax.Bits.ECMD == 1) { + ClockModulation->Bits.ExtendedOnDemandClockModulationDutyCycle = PcdGet8 (PcdCpuClockModulationDutyCycle) & BIT0; + } + } else { + ClockModulation->Bits.OnDemandClockModulationEnable = 0; + } + + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_CLOCK_MODULATION, + ClockModulation->Uint64 + ); + + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeatures.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeatures.h new file mode 100644 index 00000000..fbefc726 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeatures.h @@ -0,0 +1,1039 @@ +/** @file + CPU Common features library header file. + + Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_COMMON_FEATURES_H_ +#define _CPU_COMMON_FEATURES_H_ + +#include <PiDxe.h> + +#include <Library/BaseLib.h> +#include <Library/PcdLib.h> +#include <Library/DebugLib.h> +#include <Library/RegisterCpuFeaturesLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/LocalApicLib.h> + +#include <Register/Intel/Cpuid.h> +#include <Register/Intel/Msr.h> + +/** + Prepares for the data used by CPU feature detection and initialization. + + @param[in] NumberOfProcessors The number of CPUs in the platform. + + @return Pointer to a buffer of CPU related configuration data. + + @note This service could be called by BSP only. +**/ +VOID * +EFIAPI +AesniGetConfigData ( + IN UINTN NumberOfProcessors + ); + +/** + Detects if AESNI feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE AESNI feature is supported. + @retval FALSE AESNI feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +AesniSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes AESNI feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the AESNI feature must be enabled. + If FALSE, then the AESNI feature must be disabled. + + @retval RETURN_SUCCESS AESNI feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +AesniInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Prepares for the data used by CPU feature detection and initialization. + + @param[in] NumberOfProcessors The number of CPUs in the platform. + + @return Pointer to a buffer of CPU related configuration data. + + @note This service could be called by BSP only. +**/ +VOID * +EFIAPI +ClockModulationGetConfigData ( + IN UINTN NumberOfProcessors + ); + +/** + Detects if Clock Modulation feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Clock Modulation feature is supported. + @retval FALSE Clock Modulation feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +ClockModulationSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes Clock Modulation feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Clock Modulation feature must be enabled. + If FALSE, then the Clock Modulation feature must be disabled. + + @retval RETURN_SUCCESS Clock Modulation feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +ClockModulationInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if Enhanced Intel SpeedStep feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Enhanced Intel SpeedStep feature is supported. + @retval FALSE Enhanced Intel SpeedStep feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +EistSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes Enhanced Intel SpeedStep feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Enhanced Intel SpeedStep feature + must be enabled. + If FALSE, then the Enhanced Intel SpeedStep feature + must be disabled. + + @retval RETURN_SUCCESS Enhanced Intel SpeedStep feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +EistInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if Execute Disable feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Execute Disable feature is supported. + @retval FALSE Execute Disable feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +ExecuteDisableSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes Execute Disable feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Execute Disable feature must be enabled. + If FALSE, then the Execute Disable feature must be disabled. + + @retval RETURN_SUCCESS Execute Disable feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +ExecuteDisableInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Initializes Fast-Strings feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Fast-Strings feature must be enabled. + If FALSE, then the Fast-Strings feature must be disabled. + + @retval RETURN_SUCCESS Fast-Strings feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +FastStringsInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if MONITOR/MWAIT feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE MONITOR/MWAIT feature is supported. + @retval FALSE MONITOR/MWAIT feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +MonitorMwaitSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes MONITOR/MWAIT feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the MONITOR/MWAIT feature must be enabled. + If FALSE, then the MONITOR/MWAIT feature must be disabled. + + @retval RETURN_SUCCESS MONITOR/MWAIT feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +MonitorMwaitInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if VMX feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE VMX feature is supported. + @retval FALSE VMX feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +VmxSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes VMX feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the VMX feature must be enabled. + If FALSE, then the VMX feature must be disabled. + + @retval RETURN_SUCCESS VMX feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +VmxInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if Lock Feature Control Register feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Lock Feature Control Register feature is supported. + @retval FALSE Lock Feature Control Register feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +LockFeatureControlRegisterSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes Lock Feature Control Register feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Lock Feature Control Register feature must be enabled. + If FALSE, then the Lock Feature Control Register feature must be disabled. + + @retval RETURN_SUCCESS Lock Feature Control Register feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +LockFeatureControlRegisterInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if SMX feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE SMX feature is supported. + @retval FALSE SMX feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +SmxSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes SMX feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then SMX feature must be enabled. + If FALSE, then SMX feature must be disabled. + + @retval RETURN_SUCCESS SMX feature is initialized. + @retval RETURN_UNSUPPORTED VMX not initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +SmxInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if LimitCpuidMaxval feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE LimitCpuidMaxval feature is supported. + @retval FALSE LimitCpuidMaxval feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +LimitCpuidMaxvalSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes LimitCpuidMaxval feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the LimitCpuidMaxval feature must be enabled. + If FALSE, then the LimitCpuidMaxval feature must be disabled. + + @retval RETURN_SUCCESS LimitCpuidMaxval feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +LimitCpuidMaxvalInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if Machine Check Exception feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Machine Check Exception feature is supported. + @retval FALSE Machine Check Exception feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +MceSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes Machine Check Exception feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Machine Check Exception feature must be enabled. + If FALSE, then the Machine Check Exception feature must be disabled. + + @retval RETURN_SUCCESS Machine Check Exception feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +MceInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if Machine Check Architecture feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Machine Check Architecture feature is supported. + @retval FALSE Machine Check Architecture feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +McaSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes Machine Check Architecture feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Machine Check Architecture feature must be enabled. + If FALSE, then the Machine Check Architecture feature must be disabled. + + @retval RETURN_SUCCESS Machine Check Architecture feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +McaInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if IA32_MCG_CTL feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE IA32_MCG_CTL feature is supported. + @retval FALSE IA32_MCG_CTL feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +McgCtlSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes IA32_MCG_CTL feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the IA32_MCG_CTL feature must be enabled. + If FALSE, then the IA32_MCG_CTL feature must be disabled. + + @retval RETURN_SUCCESS IA32_MCG_CTL feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +McgCtlInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if Pending Break feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Pending Break feature is supported. + @retval FALSE Pending Break feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +PendingBreakSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes Pending Break feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Pending Break feature must be enabled. + If FALSE, then the Pending Break feature must be disabled. + + @retval RETURN_SUCCESS Pending Break feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +PendingBreakInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if C1E feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE C1E feature is supported. + @retval FALSE C1E feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +C1eSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes C1E feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the C1E feature must be enabled. + If FALSE, then the C1E feature must be disabled. + + @retval RETURN_SUCCESS C1E feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +C1eInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Prepares for the data used by CPU feature detection and initialization. + + @param[in] NumberOfProcessors The number of CPUs in the platform. + + @return Pointer to a buffer of CPU related configuration data. + + @note This service could be called by BSP only. +**/ +VOID * +EFIAPI +X2ApicGetConfigData ( + IN UINTN NumberOfProcessors + ); + +/** + Detects if X2Apci feature supported on current processor. + + Detect if X2Apci has been already enabled. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE X2Apci feature is supported. + @retval FALSE X2Apci feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +X2ApicSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes X2Apci feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the X2Apci feature must be enabled. + If FALSE, then the X2Apci feature must be disabled. + + @retval RETURN_SUCCESS X2Apci feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +X2ApicInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Prepares for the data used by CPU feature detection and initialization. + + @param[in] NumberOfProcessors The number of CPUs in the platform. + + @return Pointer to a buffer of CPU related configuration data. + + @note This service could be called by BSP only. +**/ +VOID * +EFIAPI +PpinGetConfigData ( + IN UINTN NumberOfProcessors + ); + +/** + Detects if Protected Processor Inventory Number feature supported on current + processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Protected Processor Inventory Number feature is supported. + @retval FALSE Protected Processor Inventory Number feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +PpinSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes Protected Processor Inventory Number feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Protected Processor Inventory + Number feature must be enabled. + If FALSE, then the Protected Processor Inventory + Number feature must be disabled. + + @retval RETURN_SUCCESS Protected Processor Inventory Number feature is + initialized. + @retval RETURN_DEVICE_ERROR Device can't change state because it has been + locked. + +**/ +RETURN_STATUS +EFIAPI +PpinInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Detects if Local machine check exception feature supported on current + processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Local machine check exception feature is supported. + @retval FALSE Local machine check exception feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +LmceSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes Local machine check exception feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Local machine check exception + feature must be enabled. + If FALSE, then the Local machine check exception + feature must be disabled. + + @retval RETURN_SUCCESS Local machine check exception feature is initialized. + +**/ +RETURN_STATUS +EFIAPI +LmceInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +/** + Prepares for the data used by CPU feature detection and initialization. + + @param[in] NumberOfProcessors The number of CPUs in the platform. + + @return Pointer to a buffer of CPU related configuration data. + + @note This service could be called by BSP only. +**/ +VOID * +EFIAPI +ProcTraceGetConfigData ( + IN UINTN NumberOfProcessors + ); + +/** + Detects if Intel Processor Trace feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Processor Trace feature is supported. + @retval FALSE Processor Trace feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +ProcTraceSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ); + +/** + Initializes Intel Processor Trace feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Processor Trace feature must be + enabled. + If FALSE, then the Processor Trace feature must be + disabled. + + @retval RETURN_SUCCESS Intel Processor Trace feature is initialized. + +**/ +RETURN_STATUS +EFIAPI +ProcTraceInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeaturesLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeaturesLib.c new file mode 100644 index 00000000..654e08db --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeaturesLib.c @@ -0,0 +1,231 @@ +/** @file + This library registers CPU features defined in Intel(R) 64 and IA-32 + Architectures Software Developer's Manual. + + Copyright (c) 2017 - 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/** + Register CPU features. + + @retval RETURN_SUCCESS Register successfully +**/ +RETURN_STATUS +EFIAPI +CpuCommonFeaturesLibConstructor ( + VOID + ) +{ + RETURN_STATUS Status; + + if (IsCpuFeatureSupported (CPU_FEATURE_AESNI)) { + Status = RegisterCpuFeature ( + "AESNI", + AesniGetConfigData, + AesniSupport, + AesniInitialize, + CPU_FEATURE_AESNI, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_MWAIT)) { + Status = RegisterCpuFeature ( + "MWAIT", + NULL, + MonitorMwaitSupport, + MonitorMwaitInitialize, + CPU_FEATURE_MWAIT, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_ACPI)) { + Status = RegisterCpuFeature ( + "ACPI", + ClockModulationGetConfigData, + ClockModulationSupport, + ClockModulationInitialize, + CPU_FEATURE_ACPI, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_EIST)) { + Status = RegisterCpuFeature ( + "EIST", + NULL, + EistSupport, + EistInitialize, + CPU_FEATURE_EIST, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_FASTSTRINGS)) { + Status = RegisterCpuFeature ( + "FastStrings", + NULL, + NULL, + FastStringsInitialize, + CPU_FEATURE_FASTSTRINGS, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_LOCK_FEATURE_CONTROL_REGISTER)) { + Status = RegisterCpuFeature ( + "Lock Feature Control Register", + NULL, + LockFeatureControlRegisterSupport, + LockFeatureControlRegisterInitialize, + CPU_FEATURE_LOCK_FEATURE_CONTROL_REGISTER, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_SMX)) { + Status = RegisterCpuFeature ( + "SMX", + NULL, + SmxSupport, + SmxInitialize, + CPU_FEATURE_SMX, + CPU_FEATURE_LOCK_FEATURE_CONTROL_REGISTER | CPU_FEATURE_THREAD_BEFORE, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_VMX)) { + Status = RegisterCpuFeature ( + "VMX", + NULL, + VmxSupport, + VmxInitialize, + CPU_FEATURE_VMX, + CPU_FEATURE_LOCK_FEATURE_CONTROL_REGISTER | CPU_FEATURE_THREAD_BEFORE, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_LIMIT_CPUID_MAX_VAL)) { + Status = RegisterCpuFeature ( + "Limit CpuId Maximum Value", + NULL, + LimitCpuidMaxvalSupport, + LimitCpuidMaxvalInitialize, + CPU_FEATURE_LIMIT_CPUID_MAX_VAL, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_MCE)) { + Status = RegisterCpuFeature ( + "Machine Check Enable", + NULL, + MceSupport, + MceInitialize, + CPU_FEATURE_MCE, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_MCA)) { + Status = RegisterCpuFeature ( + "Machine Check Architect", + NULL, + McaSupport, + McaInitialize, + CPU_FEATURE_MCA, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_MCG_CTL)) { + Status = RegisterCpuFeature ( + "MCG_CTL", + NULL, + McgCtlSupport, + McgCtlInitialize, + CPU_FEATURE_MCG_CTL, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_PENDING_BREAK)) { + Status = RegisterCpuFeature ( + "Pending Break", + NULL, + PendingBreakSupport, + PendingBreakInitialize, + CPU_FEATURE_PENDING_BREAK, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_C1E)) { + Status = RegisterCpuFeature ( + "C1E", + NULL, + C1eSupport, + C1eInitialize, + CPU_FEATURE_C1E, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_X2APIC)) { + Status = RegisterCpuFeature ( + "X2Apic", + X2ApicGetConfigData, + X2ApicSupport, + X2ApicInitialize, + CPU_FEATURE_X2APIC, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_PPIN)) { + Status = RegisterCpuFeature ( + "PPIN", + PpinGetConfigData, + PpinSupport, + PpinInitialize, + CPU_FEATURE_PPIN, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_LMCE)) { + Status = RegisterCpuFeature ( + "LMCE", + NULL, + LmceSupport, + LmceInitialize, + CPU_FEATURE_LMCE, + CPU_FEATURE_LOCK_FEATURE_CONTROL_REGISTER | CPU_FEATURE_THREAD_BEFORE, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + if (IsCpuFeatureSupported (CPU_FEATURE_PROC_TRACE)) { + Status = RegisterCpuFeature ( + "Proc Trace", + ProcTraceGetConfigData, + ProcTraceSupport, + ProcTraceInitialize, + CPU_FEATURE_PROC_TRACE, + CPU_FEATURE_END + ); + ASSERT_EFI_ERROR (Status); + } + + return RETURN_SUCCESS; +} + + + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeaturesLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeaturesLib.inf new file mode 100644 index 00000000..b7522abe --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeaturesLib.inf @@ -0,0 +1,64 @@ +## @file +# NULL instance to register CPU features. +# +# This library registers CPU features defined in Intel(R) 64 and IA-32 +# Architectures Software Developer's Manual. +# +# Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuCommonFeaturesLib + MODULE_UNI_FILE = CpuCommonFeaturesLib.uni + FILE_GUID = 6D69F79F-9535-4893-9DD7-93929898252C + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = NULL + + CONSTRUCTOR = CpuCommonFeaturesLibConstructor + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + CpuCommonFeaturesLib.c + CpuCommonFeatures.h + Aesni.c + C1e.c + ClockModulation.c + Eist.c + FastStrings.c + FeatureControl.c + LimitCpuIdMaxval.c + MachineCheck.c + MonitorMwait.c + PendingBreak.c + X2Apic.c + Ppin.c + ProcTrace.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + PcdLib + DebugLib + RegisterCpuFeaturesLib + BaseMemoryLib + MemoryAllocationLib + LocalApicLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuClockModulationDutyCycle ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdIsPowerOnReset ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuProcTraceOutputScheme ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuProcTraceMemSize ## SOMETIMES_CONSUMES diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeaturesLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeaturesLib.uni new file mode 100644 index 00000000..7f799e89 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeaturesLib.uni @@ -0,0 +1,20 @@ +// /** @file
+// Dxe Crc32 Guided Section Extract library.
+//
+// This library doesn't produce any library class. The constructor function uses
+// ExtractGuidedSectionLib service to register CRC32 guided section handler
+// that parses CRC32 encapsulation section and extracts raw data.
+//
+// It uses UEFI boot service CalculateCrc32 to authenticate 32 bit CRC value.
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Dxe Crc32 Guided Section Extract library."
+
+#string STR_MODULE_DESCRIPTION #language en-US "This library doesn't produce any library class. The constructor function uses ExtractGuidedSectionLib service to register CRC32 guided section handler that parses CRC32 encapsulation section and extracts raw data. It uses UEFI boot service CalculateCrc32 to authenticate 32 bit CRC value."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/Eist.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/Eist.c new file mode 100644 index 00000000..8b208c7c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/Eist.c @@ -0,0 +1,87 @@ +/** @file + Enhanced Intel SpeedStep feature. + + Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/** + Detects if Enhanced Intel SpeedStep feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Enhanced Intel SpeedStep feature is supported. + @retval FALSE Enhanced Intel SpeedStep feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +EistSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + return (CpuInfo->CpuIdVersionInfoEcx.Bits.EIST == 1); +} + +/** + Initializes Enhanced Intel SpeedStep feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Enhanced Intel SpeedStep feature + must be enabled. + If FALSE, then the Enhanced Intel SpeedStep feature + must be disabled. + + @retval RETURN_SUCCESS Enhanced Intel SpeedStep feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +EistInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + // + // The scope of the MSR_IA32_MISC_ENABLE is core for below processor type, only program + // MSR_IA32_MISC_ENABLE for thread 0 in each core. + // + if (IS_ATOM_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_CORE_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_CORE2_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + if (CpuInfo->ProcessorInfo.Location.Thread != 0) { + return RETURN_SUCCESS; + } + } + + CPU_REGISTER_TABLE_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_IA32_MISC_ENABLE, + MSR_IA32_MISC_ENABLE_REGISTER, + Bits.EIST, + (State) ? 1 : 0 + ); + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/FastStrings.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/FastStrings.c new file mode 100644 index 00000000..e0a17a70 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/FastStrings.c @@ -0,0 +1,58 @@ +/** @file + Fast-Strings feature. + + Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/** + Initializes Fast-Strings feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Fast-Strings feature must be enabled. + If FALSE, then the Fast-Strings feature must be disabled. + + @retval RETURN_SUCCESS Fast-Strings feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +FastStringsInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + // + // The scope of FastStrings bit in the MSR_IA32_MISC_ENABLE is core for below processor type, only program + // MSR_IA32_MISC_ENABLE for thread 0 in each core. + // + if (IS_SILVERMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_GOLDMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_PENTIUM_4_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + if (CpuInfo->ProcessorInfo.Location.Thread != 0) { + return RETURN_SUCCESS; + } + } + + CPU_REGISTER_TABLE_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_IA32_MISC_ENABLE, + MSR_IA32_MISC_ENABLE_REGISTER, + Bits.FastStrings, + (State) ? 1 : 0 + ); + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/FeatureControl.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/FeatureControl.c new file mode 100644 index 00000000..1735fe7d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/FeatureControl.c @@ -0,0 +1,280 @@ +/** @file + Features in MSR_IA32_FEATURE_CONTROL register. + + Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/** + Detects if VMX feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE VMX feature is supported. + @retval FALSE VMX feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +VmxSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + return (CpuInfo->CpuIdVersionInfoEcx.Bits.VMX == 1); +} + +/** + Initializes VMX feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the VMX feature must be enabled. + If FALSE, then the VMX feature must be disabled. + + @retval RETURN_SUCCESS VMX feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +VmxInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + // + // The scope of EnableVmxOutsideSmx bit in the MSR_IA32_FEATURE_CONTROL is core for + // below processor type, only program MSR_IA32_FEATURE_CONTROL for thread 0 in each + // core. + // + if (IS_SILVERMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_GOLDMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_GOLDMONT_PLUS_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + if (CpuInfo->ProcessorInfo.Location.Thread != 0) { + return RETURN_SUCCESS; + } + } + + CPU_REGISTER_TABLE_TEST_THEN_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_IA32_FEATURE_CONTROL, + MSR_IA32_FEATURE_CONTROL_REGISTER, + Bits.EnableVmxOutsideSmx, + (State) ? 1 : 0 + ); + + return RETURN_SUCCESS; +} + +/** + Detects if Lock Feature Control Register feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Lock Feature Control Register feature is supported. + @retval FALSE Lock Feature Control Register feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +LockFeatureControlRegisterSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + return TRUE; +} + +/** + Initializes Lock Feature Control Register feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Lock Feature Control Register feature must be enabled. + If FALSE, then the Lock Feature Control Register feature must be disabled. + + @retval RETURN_SUCCESS Lock Feature Control Register feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +LockFeatureControlRegisterInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + // + // The scope of Lock bit in the MSR_IA32_FEATURE_CONTROL is core for + // below processor type, only program MSR_IA32_FEATURE_CONTROL for thread 0 in each + // core. + // + if (IS_SILVERMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_GOLDMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_GOLDMONT_PLUS_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + if (CpuInfo->ProcessorInfo.Location.Thread != 0) { + return RETURN_SUCCESS; + } + } + + CPU_REGISTER_TABLE_TEST_THEN_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_IA32_FEATURE_CONTROL, + MSR_IA32_FEATURE_CONTROL_REGISTER, + Bits.Lock, + 1 + ); + + return RETURN_SUCCESS; +} + +/** + Detects if SMX feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE SMX feature is supported. + @retval FALSE SMX feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +SmxSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + return (CpuInfo->CpuIdVersionInfoEcx.Bits.SMX == 1); +} + +/** + Initializes SMX feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then SMX feature must be enabled. + If FALSE, then SMX feature must be disabled. + + @retval RETURN_SUCCESS SMX feature is initialized. + @retval RETURN_UNSUPPORTED VMX not initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +SmxInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + RETURN_STATUS Status; + + // + // The scope of Lock bit in the MSR_IA32_FEATURE_CONTROL is core for + // below processor type, only program MSR_IA32_FEATURE_CONTROL for thread 0 in each + // core. + // + if (IS_GOLDMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_GOLDMONT_PLUS_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + if (CpuInfo->ProcessorInfo.Location.Thread != 0) { + return RETURN_SUCCESS; + } + } + + Status = RETURN_SUCCESS; + + if (State && (!IsCpuFeatureInSetting (CPU_FEATURE_VMX))) { + DEBUG ((DEBUG_WARN, "Warning :: Can't enable SMX feature when VMX feature not enabled, disable it.\n")); + State = FALSE; + Status = RETURN_UNSUPPORTED; + } + + CPU_REGISTER_TABLE_WRITE_FIELD ( + ProcessorNumber, + ControlRegister, + 4, + IA32_CR4, + Bits.SMXE, + (State) ? 1 : 0 + ) + + CPU_REGISTER_TABLE_TEST_THEN_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_IA32_FEATURE_CONTROL, + MSR_IA32_FEATURE_CONTROL_REGISTER, + Bits.SenterLocalFunctionEnables, + (State) ? 0x7F : 0 + ); + + CPU_REGISTER_TABLE_TEST_THEN_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_IA32_FEATURE_CONTROL, + MSR_IA32_FEATURE_CONTROL_REGISTER, + Bits.SenterGlobalEnable, + (State) ? 1 : 0 + ); + + CPU_REGISTER_TABLE_TEST_THEN_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_IA32_FEATURE_CONTROL, + MSR_IA32_FEATURE_CONTROL_REGISTER, + Bits.EnableVmxInsideSmx, + (State) ? 1 : 0 + ); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/LimitCpuIdMaxval.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/LimitCpuIdMaxval.c new file mode 100644 index 00000000..889a5f18 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/LimitCpuIdMaxval.c @@ -0,0 +1,90 @@ +/** @file + LimitCpuidMaxval Feature. + + Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/** + Detects if LimitCpuidMaxval feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE LimitCpuidMaxval feature is supported. + @retval FALSE LimitCpuidMaxval feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +LimitCpuidMaxvalSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + UINT32 Eax; + + AsmCpuid (CPUID_SIGNATURE, &Eax, NULL, NULL, NULL); + return (Eax > 2); +} + +/** + Initializes LimitCpuidMaxval feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the LimitCpuidMaxval feature must be enabled. + If FALSE, then the LimitCpuidMaxval feature must be disabled. + + @retval RETURN_SUCCESS LimitCpuidMaxval feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +LimitCpuidMaxvalInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + // + // The scope of LimitCpuidMaxval bit in the MSR_IA32_MISC_ENABLE is core for below + // processor type, only program MSR_IA32_MISC_ENABLE for thread 0 in each core. + // + if (IS_PENTIUM_4_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_SILVERMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_GOLDMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_CORE_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_CORE2_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + if (CpuInfo->ProcessorInfo.Location.Thread != 0) { + return RETURN_SUCCESS; + } + } + + CPU_REGISTER_TABLE_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_IA32_MISC_ENABLE, + MSR_IA32_MISC_ENABLE_REGISTER, + Bits.LimitCpuidMaxval, + (State) ? 1 : 0 + ); + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/MachineCheck.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/MachineCheck.c new file mode 100644 index 00000000..59e213ce --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/MachineCheck.c @@ -0,0 +1,344 @@ +/** @file + Machine Check features. + + Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/** + Detects if Machine Check Exception feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Machine Check Exception feature is supported. + @retval FALSE Machine Check Exception feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +MceSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + return (CpuInfo->CpuIdVersionInfoEdx.Bits.MCE == 1); +} + +/** + Initializes Machine Check Exception feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Machine Check Exception feature must be enabled. + If FALSE, then the Machine Check Exception feature must be disabled. + + @retval RETURN_SUCCESS Machine Check Exception feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +MceInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + // + // Set MCE bit in CR4 + // + CPU_REGISTER_TABLE_WRITE_FIELD ( + ProcessorNumber, + ControlRegister, + 4, + IA32_CR4, + Bits.MCE, + (State) ? 1 : 0 + ); + return RETURN_SUCCESS; +} + +/** + Detects if Machine Check Architecture feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Machine Check Architecture feature is supported. + @retval FALSE Machine Check Architecture feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +McaSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + if (!MceSupport (ProcessorNumber, CpuInfo, ConfigData)) { + return FALSE; + } + return (CpuInfo->CpuIdVersionInfoEdx.Bits.MCA == 1); +} + +/** + Initializes Machine Check Architecture feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Machine Check Architecture feature must be enabled. + If FALSE, then the Machine Check Architecture feature must be disabled. + + @retval RETURN_SUCCESS Machine Check Architecture feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +McaInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + MSR_IA32_MCG_CAP_REGISTER McgCap; + UINT32 BankIndex; + + // + // The scope of MSR_IA32_MC*_CTL/MSR_IA32_MC*_STATUS is core for below processor type, only program + // MSR_IA32_MC*_CTL/MSR_IA32_MC*_STATUS for thread 0 in each core. + // + if (IS_ATOM_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_SILVERMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_SANDY_BRIDGE_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_SKYLAKE_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_XEON_PHI_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_PENTIUM_4_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_CORE_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + if (CpuInfo->ProcessorInfo.Location.Thread != 0) { + return RETURN_SUCCESS; + } + } + + // + // The scope of MSR_IA32_MC*_CTL/MSR_IA32_MC*_STATUS is package for below processor type, only program + // MSR_IA32_MC*_CTL/MSR_IA32_MC*_STATUS for thread 0 core 0 in each package. + // + if (IS_NEHALEM_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + if ((CpuInfo->ProcessorInfo.Location.Thread != 0) || (CpuInfo->ProcessorInfo.Location.Core != 0)) { + return RETURN_SUCCESS; + } + } + + if (State) { + McgCap.Uint64 = AsmReadMsr64 (MSR_IA32_MCG_CAP); + for (BankIndex = 0; BankIndex < (UINT32) McgCap.Bits.Count; BankIndex++) { + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_MC0_CTL + BankIndex * 4, + MAX_UINT64 + ); + } + + if (PcdGetBool (PcdIsPowerOnReset)) { + for (BankIndex = 0; BankIndex < (UINTN) McgCap.Bits.Count; BankIndex++) { + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_MC0_STATUS + BankIndex * 4, + 0 + ); + } + } + } + + return RETURN_SUCCESS; +} + +/** + Detects if IA32_MCG_CTL feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE IA32_MCG_CTL feature is supported. + @retval FALSE IA32_MCG_CTL feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +McgCtlSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + MSR_IA32_MCG_CAP_REGISTER McgCap; + + if (!McaSupport (ProcessorNumber, CpuInfo, ConfigData)) { + return FALSE; + } + McgCap.Uint64 = AsmReadMsr64 (MSR_IA32_MCG_CAP); + return (McgCap.Bits.MCG_CTL_P == 1); +} + +/** + Initializes IA32_MCG_CTL feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the IA32_MCG_CTL feature must be enabled. + If FALSE, then the IA32_MCG_CTL feature must be disabled. + + @retval RETURN_SUCCESS IA32_MCG_CTL feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +McgCtlInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_MCG_CTL, + (State)? MAX_UINT64 : 0 + ); + return RETURN_SUCCESS; +} + +/** + Detects if Local machine check exception feature supported on current + processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Local machine check exception feature is supported. + @retval FALSE Local machine check exception feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +LmceSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + MSR_IA32_MCG_CAP_REGISTER McgCap; + + if (!McaSupport (ProcessorNumber, CpuInfo, ConfigData)) { + return FALSE; + } + + McgCap.Uint64 = AsmReadMsr64 (MSR_IA32_MCG_CAP); + if (ProcessorNumber == 0) { + DEBUG ((DEBUG_INFO, "LMCE enable = %x\n", (BOOLEAN) (McgCap.Bits.MCG_LMCE_P != 0))); + } + return (BOOLEAN) (McgCap.Bits.MCG_LMCE_P != 0); +} + +/** + Initializes Local machine check exception feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Local machine check exception + feature must be enabled. + If FALSE, then the Local machine check exception + feature must be disabled. + + @retval RETURN_SUCCESS Local machine check exception feature is initialized. + +**/ +RETURN_STATUS +EFIAPI +LmceInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + // + // The scope of LcmeOn bit in the MSR_IA32_MISC_ENABLE is core for below processor type, only program + // MSR_IA32_MISC_ENABLE for thread 0 in each core. + // + if (IS_SILVERMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_GOLDMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_PENTIUM_4_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + if (CpuInfo->ProcessorInfo.Location.Thread != 0) { + return RETURN_SUCCESS; + } + } + + CPU_REGISTER_TABLE_TEST_THEN_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_IA32_FEATURE_CONTROL, + MSR_IA32_FEATURE_CONTROL_REGISTER, + Bits.LmceOn, + (State) ? 1 : 0 + ); + + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/MonitorMwait.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/MonitorMwait.c new file mode 100644 index 00000000..fbff3da2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/MonitorMwait.c @@ -0,0 +1,88 @@ +/** @file + MonitorMwait feature. + + Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/** + Detects if MONITOR/MWAIT feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE MONITOR/MWAIT feature is supported. + @retval FALSE MONITOR/MWAIT feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +MonitorMwaitSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + return (CpuInfo->CpuIdVersionInfoEcx.Bits.MONITOR == 1); +} + +/** + Initializes MONITOR/MWAIT feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the MONITOR/MWAIT feature must be enabled. + If FALSE, then the MONITOR/MWAIT feature must be disabled. + + @retval RETURN_SUCCESS MONITOR/MWAIT feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +MonitorMwaitInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + // + // The scope of the MSR_IA32_MISC_ENABLE is core for below processor type, only program + // MSR_IA32_MISC_ENABLE for thread 0 in each core. + // + if (IS_CORE2_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_ATOM_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_GOLDMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_SILVERMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_PENTIUM_4_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_CORE_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + if (CpuInfo->ProcessorInfo.Location.Thread != 0) { + return RETURN_SUCCESS; + } + } + + CPU_REGISTER_TABLE_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_IA32_MISC_ENABLE, + MSR_IA32_MISC_ENABLE_REGISTER, + Bits.MONITOR, + (State) ? 1 : 0 + ); + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/PendingBreak.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/PendingBreak.c new file mode 100644 index 00000000..eb9c689e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/PendingBreak.c @@ -0,0 +1,95 @@ +/** @file + Pending Break feature. + + Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/** + Detects if Pending Break feature supported on current processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Pending Break feature is supported. + @retval FALSE Pending Break feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +PendingBreakSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + if (IS_ATOM_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_CORE2_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_CORE_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_PENTIUM_4_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_PENTIUM_M_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + return (CpuInfo->CpuIdVersionInfoEdx.Bits.PBE == 1); + } + return FALSE; +} + +/** + Initializes Pending Break feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Pending Break feature must be enabled. + If FALSE, then the Pending Break feature must be disabled. + + @retval RETURN_SUCCESS Pending Break feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +PendingBreakInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + // + // The scope of the MSR_ATOM_IA32_MISC_ENABLE is core for below processor type, only program + // MSR_ATOM_IA32_MISC_ENABLE for thread 0 in each core. + // + // Support function has check the processer type for this feature, no need to check again + // here. + // + if (CpuInfo->ProcessorInfo.Location.Thread != 0) { + return RETURN_SUCCESS; + } + + // + // ATOM, CORE2, CORE, PENTIUM_4 and IS_PENTIUM_M_PROCESSOR have the same MSR index, + // Simply use MSR_ATOM_IA32_MISC_ENABLE here + // + CPU_REGISTER_TABLE_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_ATOM_IA32_MISC_ENABLE, + MSR_ATOM_IA32_MISC_ENABLE_REGISTER, + Bits.FERR, + (State) ? 1 : 0 + ); + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/Ppin.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/Ppin.c new file mode 100644 index 00000000..4e3af46b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/Ppin.c @@ -0,0 +1,164 @@ +/** @file + Protected Processor Inventory Number(PPIN) feature. + + Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/** + Prepares for the data used by CPU feature detection and initialization. + + @param[in] NumberOfProcessors The number of CPUs in the platform. + + @return Pointer to a buffer of CPU related configuration data. + + @note This service could be called by BSP only. +**/ +VOID * +EFIAPI +PpinGetConfigData ( + IN UINTN NumberOfProcessors + ) +{ + VOID *ConfigData; + + ConfigData = AllocateZeroPool (sizeof (MSR_IVY_BRIDGE_PPIN_CTL_REGISTER) * NumberOfProcessors); + ASSERT (ConfigData != NULL); + return ConfigData; +} + +/** + Detects if Protected Processor Inventory Number feature supported on current + processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Protected Processor Inventory Number feature is supported. + @retval FALSE Protected Processor Inventory Number feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +PpinSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + MSR_IVY_BRIDGE_PLATFORM_INFO_1_REGISTER PlatformInfo; + MSR_IVY_BRIDGE_PPIN_CTL_REGISTER *MsrPpinCtrl; + + if ((CpuInfo->DisplayFamily == 0x06) && + ((CpuInfo->DisplayModel == 0x3E) || // Xeon E5 V2 + (CpuInfo->DisplayModel == 0x56) || // Xeon Processor D Product + (CpuInfo->DisplayModel == 0x4F) || // Xeon E5 v4, E7 v4 + (CpuInfo->DisplayModel == 0x55) || // Xeon Processor Scalable + (CpuInfo->DisplayModel == 0x57) || // Xeon Phi processor 3200, 5200, 7200 series. + (CpuInfo->DisplayModel == 0x85) // Future Xeon phi processor + )) { + // + // Check whether platform support this feature. + // + PlatformInfo.Uint64 = AsmReadMsr64 (MSR_IVY_BRIDGE_PLATFORM_INFO_1); + if (PlatformInfo.Bits.PPIN_CAP != 0) { + MsrPpinCtrl = (MSR_IVY_BRIDGE_PPIN_CTL_REGISTER *) ConfigData; + ASSERT (MsrPpinCtrl != NULL); + MsrPpinCtrl[ProcessorNumber].Uint64 = AsmReadMsr64 (MSR_IVY_BRIDGE_PPIN_CTL); + return TRUE; + } + } + + return FALSE; +} + +/** + Initializes Protected Processor Inventory Number feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Protected Processor Inventory + Number feature must be enabled. + If FALSE, then the Protected Processor Inventory + Number feature must be disabled. + + @retval RETURN_SUCCESS Protected Processor Inventory Number feature is + initialized. + @retval RETURN_DEVICE_ERROR Device can't change state because it has been + locked. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +PpinInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + MSR_IVY_BRIDGE_PPIN_CTL_REGISTER *MsrPpinCtrl; + + MsrPpinCtrl = (MSR_IVY_BRIDGE_PPIN_CTL_REGISTER *) ConfigData; + ASSERT (MsrPpinCtrl != NULL); + + // + // Check whether processor already lock this register. + // If already locked, just based on the request state and + // the current state to return the status. + // + if (MsrPpinCtrl[ProcessorNumber].Bits.LockOut != 0) { + return MsrPpinCtrl[ProcessorNumber].Bits.Enable_PPIN == State ? RETURN_SUCCESS : RETURN_DEVICE_ERROR; + } + + // + // Support function already check the processor which support PPIN feature, so this function not need + // to check the processor again. + // + // The scope of the MSR_IVY_BRIDGE_PPIN_CTL is package level, only program MSR_IVY_BRIDGE_PPIN_CTL for + // thread 0 core 0 in each package. + // + if ((CpuInfo->ProcessorInfo.Location.Thread != 0) || (CpuInfo->ProcessorInfo.Location.Core != 0)) { + return RETURN_SUCCESS; + } + + if (State) { + // + // Enable and Unlock. + // According to SDM, once Enable_PPIN is set, attempt to write 1 to LockOut will cause #GP. + // + MsrPpinCtrl[ProcessorNumber].Bits.Enable_PPIN = 1; + MsrPpinCtrl[ProcessorNumber].Bits.LockOut = 0; + } else { + // + // Disable and Lock. + // According to SDM, writing 1 to LockOut is permitted only if Enable_PPIN is clear. + // + MsrPpinCtrl[ProcessorNumber].Bits.Enable_PPIN = 0; + MsrPpinCtrl[ProcessorNumber].Bits.LockOut = 1; + } + + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IVY_BRIDGE_PPIN_CTL, + MsrPpinCtrl[ProcessorNumber].Uint64 + ); + + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/ProcTrace.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/ProcTrace.c new file mode 100644 index 00000000..f2ce9bcf --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/ProcTrace.c @@ -0,0 +1,473 @@ +/** @file + Intel Processor Trace feature. + + Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/// +/// This macro define the max entries in the Topa table. +/// Each entry in the table contains some attribute bits, a pointer to an output region, and the size of the region. +/// The last entry in the table may hold a pointer to the next table. This pointer can either point to the top of the +/// current table (for circular array) or to the base of another table. +/// At least 2 entries are needed because the list of entries must +/// be terminated by an entry with the END bit set to 1, so 2 +/// entries are required to use a single valid entry. +/// +#define MAX_TOPA_ENTRY_COUNT 2 + + +/// +/// Processor trace output scheme selection. +/// +typedef enum { + RtitOutputSchemeSingleRange = 0, + RtitOutputSchemeToPA +} RTIT_OUTPUT_SCHEME; + +typedef struct { + BOOLEAN TopaSupported; + BOOLEAN SingleRangeSupported; + MSR_IA32_RTIT_CTL_REGISTER RtitCtrl; + MSR_IA32_RTIT_OUTPUT_BASE_REGISTER RtitOutputBase; + MSR_IA32_RTIT_OUTPUT_MASK_PTRS_REGISTER RtitOutputMaskPtrs; +} PROC_TRACE_PROCESSOR_DATA; + +typedef struct { + UINT32 NumberOfProcessors; + + UINT8 ProcTraceOutputScheme; + UINT32 ProcTraceMemSize; + + UINTN *ThreadMemRegionTable; + UINTN AllocatedThreads; + + UINTN *TopaMemArray; + + PROC_TRACE_PROCESSOR_DATA *ProcessorData; +} PROC_TRACE_DATA; + +typedef struct { + RTIT_TOPA_TABLE_ENTRY TopaEntry[MAX_TOPA_ENTRY_COUNT]; +} PROC_TRACE_TOPA_TABLE; + +/** + Prepares for the data used by CPU feature detection and initialization. + + @param[in] NumberOfProcessors The number of CPUs in the platform. + + @return Pointer to a buffer of CPU related configuration data. + + @note This service could be called by BSP only. +**/ +VOID * +EFIAPI +ProcTraceGetConfigData ( + IN UINTN NumberOfProcessors + ) +{ + PROC_TRACE_DATA *ConfigData; + + ConfigData = AllocateZeroPool (sizeof (PROC_TRACE_DATA) + sizeof (PROC_TRACE_PROCESSOR_DATA) * NumberOfProcessors); + ASSERT (ConfigData != NULL); + ConfigData->ProcessorData = (PROC_TRACE_PROCESSOR_DATA *) ((UINT8*) ConfigData + sizeof (PROC_TRACE_DATA)); + + ConfigData->NumberOfProcessors = (UINT32) NumberOfProcessors; + ConfigData->ProcTraceMemSize = PcdGet32 (PcdCpuProcTraceMemSize); + ConfigData->ProcTraceOutputScheme = PcdGet8 (PcdCpuProcTraceOutputScheme); + + return ConfigData; +} + +/** + Detects if Intel Processor Trace feature supported on current + processor. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE Processor Trace feature is supported. + @retval FALSE Processor Trace feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +ProcTraceSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + PROC_TRACE_DATA *ProcTraceData; + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_EBX Ebx; + CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF_ECX Ecx; + + // + // Check if ProcTraceMemorySize option is enabled (0xFF means disable by user) + // + ProcTraceData = (PROC_TRACE_DATA *) ConfigData; + ASSERT (ProcTraceData != NULL); + if ((ProcTraceData->ProcTraceMemSize > RtitTopaMemorySize128M) || + (ProcTraceData->ProcTraceOutputScheme > RtitOutputSchemeToPA)) { + return FALSE; + } + + // + // Check if Processor Trace is supported + // + AsmCpuidEx (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, 0, NULL, &Ebx.Uint32, NULL, NULL); + if (Ebx.Bits.IntelProcessorTrace == 0) { + return FALSE; + } + + AsmCpuidEx (CPUID_INTEL_PROCESSOR_TRACE, CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF, NULL, NULL, &Ecx.Uint32, NULL); + ProcTraceData->ProcessorData[ProcessorNumber].TopaSupported = (BOOLEAN) (Ecx.Bits.RTIT == 1); + ProcTraceData->ProcessorData[ProcessorNumber].SingleRangeSupported = (BOOLEAN) (Ecx.Bits.SingleRangeOutput == 1); + if ((ProcTraceData->ProcessorData[ProcessorNumber].TopaSupported && (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeToPA)) || + (ProcTraceData->ProcessorData[ProcessorNumber].SingleRangeSupported && (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeSingleRange))) { + ProcTraceData->ProcessorData[ProcessorNumber].RtitCtrl.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_CTL); + ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputBase.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_OUTPUT_BASE); + ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputMaskPtrs.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_OUTPUT_MASK_PTRS); + return TRUE; + } + + return FALSE; +} + +/** + Initializes Intel Processor Trace feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the Processor Trace feature must be + enabled. + If FALSE, then the Processor Trace feature must be + disabled. + + @retval RETURN_SUCCESS Intel Processor Trace feature is initialized. + +**/ +RETURN_STATUS +EFIAPI +ProcTraceInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + UINT32 MemRegionSize; + UINTN Pages; + UINTN Alignment; + UINTN MemRegionBaseAddr; + UINTN *ThreadMemRegionTable; + UINTN Index; + UINTN TopaTableBaseAddr; + UINTN AlignedAddress; + UINTN *TopaMemArray; + PROC_TRACE_TOPA_TABLE *TopaTable; + PROC_TRACE_DATA *ProcTraceData; + BOOLEAN FirstIn; + MSR_IA32_RTIT_CTL_REGISTER CtrlReg; + MSR_IA32_RTIT_STATUS_REGISTER StatusReg; + MSR_IA32_RTIT_OUTPUT_BASE_REGISTER OutputBaseReg; + MSR_IA32_RTIT_OUTPUT_MASK_PTRS_REGISTER OutputMaskPtrsReg; + RTIT_TOPA_TABLE_ENTRY *TopaEntryPtr; + + // + // The scope of the MSR_IA32_RTIT_* is core for below processor type, only program + // MSR_IA32_RTIT_* for thread 0 in each core. + // + if (IS_GOLDMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel) || + IS_GOLDMONT_PLUS_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + if (CpuInfo->ProcessorInfo.Location.Thread != 0) { + return RETURN_SUCCESS; + } + } + + ProcTraceData = (PROC_TRACE_DATA *) ConfigData; + ASSERT (ProcTraceData != NULL); + + // + // Clear MSR_IA32_RTIT_CTL[0] and IA32_RTIT_STS only if MSR_IA32_RTIT_CTL[0]==1b + // + CtrlReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitCtrl.Uint64; + if (CtrlReg.Bits.TraceEn != 0) { + /// + /// Clear bit 0 in MSR IA32_RTIT_CTL (570) + /// + CtrlReg.Bits.TraceEn = 0; + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_RTIT_CTL, + CtrlReg.Uint64 + ); + + /// + /// Clear MSR IA32_RTIT_STS (571h) to all zeros + /// + StatusReg.Uint64 = 0x0; + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_RTIT_STATUS, + StatusReg.Uint64 + ); + } + + if (!State) { + return RETURN_SUCCESS; + } + + MemRegionBaseAddr = 0; + FirstIn = FALSE; + + if (ProcTraceData->ThreadMemRegionTable == NULL) { + FirstIn = TRUE; + DEBUG ((DEBUG_INFO, "Initialize Processor Trace\n")); + } + + /// + /// Refer to PROC_TRACE_MEM_SIZE Table for Size Encoding + /// + MemRegionSize = (UINT32) (1 << (ProcTraceData->ProcTraceMemSize + 12)); + if (FirstIn) { + DEBUG ((DEBUG_INFO, "ProcTrace: MemSize requested: 0x%X \n", MemRegionSize)); + } + + if (FirstIn) { + // + // Let BSP allocate and create the necessary memory region (Aligned to the size of + // the memory region from setup option(ProcTraceMemSize) which is an integral multiple of 4kB) + // for all the enabled threads to store Processor Trace debug data. Then Configure the trace + // address base in MSR, IA32_RTIT_OUTPUT_BASE (560h) bits 47:12. Note that all regions must be + // aligned based on their size, not just 4K. Thus a 2M region must have bits 20:12 cleared. + // + ThreadMemRegionTable = (UINTN *) AllocatePool (ProcTraceData->NumberOfProcessors * sizeof (UINTN *)); + if (ThreadMemRegionTable == NULL) { + DEBUG ((DEBUG_ERROR, "Allocate ProcTrace ThreadMemRegionTable Failed\n")); + return RETURN_OUT_OF_RESOURCES; + } + ProcTraceData->ThreadMemRegionTable = ThreadMemRegionTable; + + for (Index = 0; Index < ProcTraceData->NumberOfProcessors; Index++, ProcTraceData->AllocatedThreads++) { + Pages = EFI_SIZE_TO_PAGES (MemRegionSize); + Alignment = MemRegionSize; + AlignedAddress = (UINTN) AllocateAlignedReservedPages (Pages, Alignment); + if (AlignedAddress == 0) { + DEBUG ((DEBUG_ERROR, "ProcTrace: Out of mem, allocated only for %d threads\n", ProcTraceData->AllocatedThreads)); + if (Index == 0) { + // + // Could not allocate for BSP even + // + FreePool ((VOID *) ThreadMemRegionTable); + ThreadMemRegionTable = NULL; + return RETURN_OUT_OF_RESOURCES; + } + break; + } + + ThreadMemRegionTable[Index] = AlignedAddress; + DEBUG ((DEBUG_INFO, "ProcTrace: PT MemRegionBaseAddr(aligned) for thread %d: 0x%llX \n", Index, (UINT64) ThreadMemRegionTable[Index])); + } + + DEBUG ((DEBUG_INFO, "ProcTrace: Allocated PT mem for %d thread \n", ProcTraceData->AllocatedThreads)); + } + + if (ProcessorNumber < ProcTraceData->AllocatedThreads) { + MemRegionBaseAddr = ProcTraceData->ThreadMemRegionTable[ProcessorNumber]; + } else { + return RETURN_SUCCESS; + } + + /// + /// Check Processor Trace output scheme: Single Range output or ToPA table + /// + + // + // Single Range output scheme + // + if (ProcTraceData->ProcessorData[ProcessorNumber].SingleRangeSupported && + (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeSingleRange)) { + if (FirstIn) { + DEBUG ((DEBUG_INFO, "ProcTrace: Enabling Single Range Output scheme \n")); + } + + // + // Clear MSR IA32_RTIT_CTL (0x570) ToPA (Bit 8) + // + CtrlReg.Bits.ToPA = 0; + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_RTIT_CTL, + CtrlReg.Uint64 + ); + + // + // Program MSR IA32_RTIT_OUTPUT_BASE (0x560) bits[63:7] with the allocated Memory Region + // + OutputBaseReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputBase.Uint64; + OutputBaseReg.Bits.Base = (MemRegionBaseAddr >> 7) & 0x01FFFFFF; + OutputBaseReg.Bits.BaseHi = RShiftU64 ((UINT64) MemRegionBaseAddr, 32) & 0xFFFFFFFF; + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_RTIT_OUTPUT_BASE, + OutputBaseReg.Uint64 + ); + + // + // Program the Mask bits for the Memory Region to MSR IA32_RTIT_OUTPUT_MASK_PTRS (561h) + // + OutputMaskPtrsReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputMaskPtrs.Uint64; + OutputMaskPtrsReg.Bits.MaskOrTableOffset = ((MemRegionSize - 1) >> 7) & 0x01FFFFFF; + OutputMaskPtrsReg.Bits.OutputOffset = RShiftU64 (MemRegionSize - 1, 32) & 0xFFFFFFFF; + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_RTIT_OUTPUT_MASK_PTRS, + OutputMaskPtrsReg.Uint64 + ); + } + + // + // ToPA(Table of physical address) scheme + // + if (ProcTraceData->ProcessorData[ProcessorNumber].TopaSupported && + (ProcTraceData->ProcTraceOutputScheme == RtitOutputSchemeToPA)) { + // + // Create ToPA structure aligned at 4KB for each logical thread + // with at least 2 entries by 8 bytes size each. The first entry + // should have the trace output base address in bits 47:12, 6:9 + // for Size, bits 4,2 and 0 must be cleared. The second entry + // should have the base address of the table location in bits + // 47:12, bits 4 and 2 must be cleared and bit 0 must be set. + // + if (FirstIn) { + DEBUG ((DEBUG_INFO, "ProcTrace: Enabling ToPA scheme \n")); + // + // Let BSP allocate ToPA table mem for all threads + // + TopaMemArray = (UINTN *) AllocatePool (ProcTraceData->AllocatedThreads * sizeof (UINTN *)); + if (TopaMemArray == NULL) { + DEBUG ((DEBUG_ERROR, "ProcTrace: Allocate mem for ToPA Failed\n")); + return RETURN_OUT_OF_RESOURCES; + } + ProcTraceData->TopaMemArray = TopaMemArray; + + for (Index = 0; Index < ProcTraceData->AllocatedThreads; Index++) { + Pages = EFI_SIZE_TO_PAGES (sizeof (PROC_TRACE_TOPA_TABLE)); + Alignment = 0x1000; + AlignedAddress = (UINTN) AllocateAlignedReservedPages (Pages, Alignment); + if (AlignedAddress == 0) { + if (Index < ProcTraceData->AllocatedThreads) { + ProcTraceData->AllocatedThreads = Index; + } + DEBUG ((DEBUG_ERROR, "ProcTrace: Out of mem, allocated ToPA mem only for %d threads\n", ProcTraceData->AllocatedThreads)); + if (Index == 0) { + // + // Could not allocate for BSP even + // + FreePool ((VOID *) TopaMemArray); + TopaMemArray = NULL; + return RETURN_OUT_OF_RESOURCES; + } + break; + } + + TopaMemArray[Index] = AlignedAddress; + DEBUG ((DEBUG_INFO, "ProcTrace: Topa table address(aligned) for thread %d is 0x%llX \n", Index, (UINT64) TopaMemArray[Index])); + } + + DEBUG ((DEBUG_INFO, "ProcTrace: Allocated ToPA mem for %d thread \n", ProcTraceData->AllocatedThreads)); + } + + if (ProcessorNumber < ProcTraceData->AllocatedThreads) { + TopaTableBaseAddr = ProcTraceData->TopaMemArray[ProcessorNumber]; + } else { + return RETURN_SUCCESS; + } + + TopaTable = (PROC_TRACE_TOPA_TABLE *) TopaTableBaseAddr; + TopaEntryPtr = &TopaTable->TopaEntry[0]; + TopaEntryPtr->Uint64 = 0; + TopaEntryPtr->Bits.Base = (MemRegionBaseAddr >> 12) & 0x000FFFFF; + TopaEntryPtr->Bits.BaseHi = RShiftU64 ((UINT64) MemRegionBaseAddr, 32) & 0xFFFFFFFF; + TopaEntryPtr->Bits.Size = ProcTraceData->ProcTraceMemSize; + TopaEntryPtr->Bits.END = 0; + + TopaEntryPtr = &TopaTable->TopaEntry[1]; + TopaEntryPtr->Uint64 = 0; + TopaEntryPtr->Bits.Base = (TopaTableBaseAddr >> 12) & 0x000FFFFF; + TopaEntryPtr->Bits.BaseHi = RShiftU64 ((UINT64) TopaTableBaseAddr, 32) & 0xFFFFFFFF; + TopaEntryPtr->Bits.END = 1; + + // + // Program the MSR IA32_RTIT_OUTPUT_BASE (0x560) bits[63:7] with ToPA base + // + OutputBaseReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputBase.Uint64; + OutputBaseReg.Bits.Base = (TopaTableBaseAddr >> 7) & 0x01FFFFFF; + OutputBaseReg.Bits.BaseHi = RShiftU64 ((UINT64) TopaTableBaseAddr, 32) & 0xFFFFFFFF; + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_RTIT_OUTPUT_BASE, + OutputBaseReg.Uint64 + ); + + // + // Set the MSR IA32_RTIT_OUTPUT_MASK (0x561) bits[63:7] to 0 + // + OutputMaskPtrsReg.Uint64 = ProcTraceData->ProcessorData[ProcessorNumber].RtitOutputMaskPtrs.Uint64; + OutputMaskPtrsReg.Bits.MaskOrTableOffset = 0; + OutputMaskPtrsReg.Bits.OutputOffset = 0; + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_RTIT_OUTPUT_MASK_PTRS, + OutputMaskPtrsReg.Uint64 + ); + // + // Enable ToPA output scheme by enabling MSR IA32_RTIT_CTL (0x570) ToPA (Bit 8) + // + CtrlReg.Bits.ToPA = 1; + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_RTIT_CTL, + CtrlReg.Uint64 + ); + } + + /// + /// Enable the Processor Trace feature from MSR IA32_RTIT_CTL (570h) + /// + CtrlReg.Bits.OS = 1; + CtrlReg.Bits.User = 1; + CtrlReg.Bits.BranchEn = 1; + CtrlReg.Bits.TraceEn = 1; + CPU_REGISTER_TABLE_WRITE64 ( + ProcessorNumber, + Msr, + MSR_IA32_RTIT_CTL, + CtrlReg.Uint64 + ); + + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/X2Apic.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/X2Apic.c new file mode 100644 index 00000000..e0ca6b9e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuCommonFeaturesLib/X2Apic.c @@ -0,0 +1,137 @@ +/** @file + X2Apic feature. + + Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuCommonFeatures.h" + +/** + Prepares for the data used by CPU feature detection and initialization. + + @param[in] NumberOfProcessors The number of CPUs in the platform. + + @return Pointer to a buffer of CPU related configuration data. + + @note This service could be called by BSP only. +**/ +VOID * +EFIAPI +X2ApicGetConfigData ( + IN UINTN NumberOfProcessors + ) +{ + BOOLEAN *ConfigData; + + ConfigData = AllocateZeroPool (sizeof (BOOLEAN) * NumberOfProcessors); + ASSERT (ConfigData != NULL); + return ConfigData; +} + +/** + Detects if X2Apci feature supported on current processor. + + Detect if X2Apci has been already enabled. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + + @retval TRUE X2Apci feature is supported. + @retval FALSE X2Apci feature is not supported. + + @note This service could be called by BSP/APs. +**/ +BOOLEAN +EFIAPI +X2ApicSupport ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData OPTIONAL + ) +{ + BOOLEAN *X2ApicEnabled; + + ASSERT (ConfigData != NULL); + X2ApicEnabled = (BOOLEAN *) ConfigData; + // + // *ConfigData indicates if X2APIC enabled on current processor + // + X2ApicEnabled[ProcessorNumber] = (GetApicMode () == LOCAL_APIC_MODE_X2APIC) ? TRUE : FALSE; + + return (CpuInfo->CpuIdVersionInfoEcx.Bits.x2APIC == 1); +} + +/** + Initializes X2Apci feature to specific state. + + @param[in] ProcessorNumber The index of the CPU executing this function. + @param[in] CpuInfo A pointer to the REGISTER_CPU_FEATURE_INFORMATION + structure for the CPU executing this function. + @param[in] ConfigData A pointer to the configuration buffer returned + by CPU_FEATURE_GET_CONFIG_DATA. NULL if + CPU_FEATURE_GET_CONFIG_DATA was not provided in + RegisterCpuFeature(). + @param[in] State If TRUE, then the X2Apci feature must be enabled. + If FALSE, then the X2Apci feature must be disabled. + + @retval RETURN_SUCCESS X2Apci feature is initialized. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +X2ApicInitialize ( + IN UINTN ProcessorNumber, + IN REGISTER_CPU_FEATURE_INFORMATION *CpuInfo, + IN VOID *ConfigData, OPTIONAL + IN BOOLEAN State + ) +{ + BOOLEAN *X2ApicEnabled; + + // + // The scope of the MSR_IA32_APIC_BASE is core for below processor type, only program + // MSR_IA32_APIC_BASE for thread 0 in each core. + // + if (IS_SILVERMONT_PROCESSOR (CpuInfo->DisplayFamily, CpuInfo->DisplayModel)) { + if (CpuInfo->ProcessorInfo.Location.Thread != 0) { + return RETURN_SUCCESS; + } + } + + ASSERT (ConfigData != NULL); + X2ApicEnabled = (BOOLEAN *) ConfigData; + if (X2ApicEnabled[ProcessorNumber]) { + PRE_SMM_CPU_REGISTER_TABLE_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_IA32_APIC_BASE, + MSR_IA32_APIC_BASE_REGISTER, + Bits.EXTD, + 1 + ); + } else { + // + // Enable X2APIC mode only if X2APIC is not enabled, + // Needn't to disabe X2APIC mode again if X2APIC is not enabled + // + if (State) { + CPU_REGISTER_TABLE_WRITE_FIELD ( + ProcessorNumber, + Msr, + MSR_IA32_APIC_BASE, + MSR_IA32_APIC_BASE_REGISTER, + Bits.EXTD, + 1 + ); + } + } + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c new file mode 100644 index 00000000..82823bb6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c @@ -0,0 +1,183 @@ +/** @file + CPU Exception Handler Library common functions. + + Copyright (c) 2012 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuExceptionCommon.h" + +// +// Error code flag indicating whether or not an error code will be +// pushed on the stack if an exception occurs. +// +// 1 means an error code will be pushed, otherwise 0 +// +CONST UINT32 mErrorCodeFlag = 0x20227d00; + +// +// Define the maximum message length +// +#define MAX_DEBUG_MESSAGE_LENGTH 0x100 + +CONST CHAR8 mExceptionReservedStr[] = "Reserved"; +CONST CHAR8 *mExceptionNameStr[] = { + "#DE - Divide Error", + "#DB - Debug", + "NMI Interrupt", + "#BP - Breakpoint", + "#OF - Overflow", + "#BR - BOUND Range Exceeded", + "#UD - Invalid Opcode", + "#NM - Device Not Available", + "#DF - Double Fault", + "Coprocessor Segment Overrun", + "#TS - Invalid TSS", + "#NP - Segment Not Present", + "#SS - Stack Fault Fault", + "#GP - General Protection", + "#PF - Page-Fault", + "Reserved", + "#MF - x87 FPU Floating-Point Error", + "#AC - Alignment Check", + "#MC - Machine-Check", + "#XM - SIMD floating-point", + "#VE - Virtualization", + "#CP - Control Protection", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "#VC - VMM Communication", +}; + +#define EXCEPTION_KNOWN_NAME_NUM (sizeof (mExceptionNameStr) / sizeof (CHAR8 *)) + +/** + Get ASCII format string exception name by exception type. + + @param ExceptionType Exception type. + + @return ASCII format string exception name. +**/ +CONST CHAR8 * +GetExceptionNameStr ( + IN EFI_EXCEPTION_TYPE ExceptionType + ) +{ + if ((UINTN) ExceptionType < EXCEPTION_KNOWN_NAME_NUM) { + return mExceptionNameStr[ExceptionType]; + } else { + return mExceptionReservedStr; + } +} + +/** + Prints a message to the serial port. + + @param Format Format string for the message to print. + @param ... Variable argument list whose contents are accessed + based on the format string specified by Format. + +**/ +VOID +EFIAPI +InternalPrintMessage ( + IN CONST CHAR8 *Format, + ... + ) +{ + CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH]; + VA_LIST Marker; + + // + // Convert the message to an ASCII String + // + VA_START (Marker, Format); + AsciiVSPrint (Buffer, sizeof (Buffer), Format, Marker); + VA_END (Marker); + + // + // Send the print string to a Serial Port + // + SerialPortWrite ((UINT8 *)Buffer, AsciiStrLen (Buffer)); +} + +/** + Find and display image base address and return image base and its entry point. + + @param CurrentEip Current instruction pointer. + +**/ +VOID +DumpModuleImageInfo ( + IN UINTN CurrentEip + ) +{ + EFI_STATUS Status; + UINTN Pe32Data; + VOID *PdbPointer; + VOID *EntryPoint; + + Pe32Data = PeCoffSearchImageBase (CurrentEip); + if (Pe32Data == 0) { + InternalPrintMessage ("!!!! Can't find image information. !!!!\n"); + } else { + // + // Find Image Base entry point + // + Status = PeCoffLoaderGetEntryPoint ((VOID *) Pe32Data, &EntryPoint); + if (EFI_ERROR (Status)) { + EntryPoint = NULL; + } + InternalPrintMessage ("!!!! Find image based on IP(0x%x) ", CurrentEip); + PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *) Pe32Data); + if (PdbPointer != NULL) { + InternalPrintMessage ("%a", PdbPointer); + } else { + InternalPrintMessage ("(No PDB) " ); + } + InternalPrintMessage ( + " (ImageBase=%016lp, EntryPoint=%016p) !!!!\n", + (VOID *) Pe32Data, + EntryPoint + ); + } +} + +/** + Read and save reserved vector information + + @param[in] VectorInfo Pointer to reserved vector list. + @param[out] ReservedVector Pointer to reserved vector data buffer. + @param[in] VectorCount Vector number to be updated. + + @return EFI_SUCCESS Read and save vector info successfully. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + +**/ +EFI_STATUS +ReadAndVerifyVectorInfo ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo, + OUT RESERVED_VECTORS_DATA *ReservedVector, + IN UINTN VectorCount + ) +{ + while (VectorInfo->Attribute != EFI_VECTOR_HANDOFF_LAST_ENTRY) { + if (VectorInfo->Attribute > EFI_VECTOR_HANDOFF_HOOK_AFTER) { + // + // If vector attrubute is invalid + // + return EFI_INVALID_PARAMETER; + } + if (VectorInfo->VectorNumber < VectorCount) { + ReservedVector[VectorInfo->VectorNumber].Attribute = VectorInfo->Attribute; + } + VectorInfo ++; + } + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h new file mode 100644 index 00000000..800a2abf --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h @@ -0,0 +1,321 @@ +/** @file + Common header file for CPU Exception Handler Library. + + Copyright (c) 2012 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_EXCEPTION_COMMON_H_ +#define _CPU_EXCEPTION_COMMON_H_ + +#include <Ppi/VectorHandoffInfo.h> +#include <Protocol/Cpu.h> +#include <Library/BaseLib.h> +#include <Library/SerialPortLib.h> +#include <Library/PrintLib.h> +#include <Library/LocalApicLib.h> +#include <Library/PeCoffGetEntryPointLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/SynchronizationLib.h> +#include <Library/CpuExceptionHandlerLib.h> + +#define CPU_EXCEPTION_NUM 32 +#define CPU_INTERRUPT_NUM 256 +#define HOOKAFTER_STUB_SIZE 16 + +// +// Exception Error Code of Page-Fault Exception +// +#define IA32_PF_EC_P BIT0 +#define IA32_PF_EC_WR BIT1 +#define IA32_PF_EC_US BIT2 +#define IA32_PF_EC_RSVD BIT3 +#define IA32_PF_EC_ID BIT4 +#define IA32_PF_EC_PK BIT5 +#define IA32_PF_EC_SS BIT6 +#define IA32_PF_EC_SGX BIT15 + +#include "ArchInterruptDefs.h" + +#define CPU_STACK_SWITCH_EXCEPTION_NUMBER \ + FixedPcdGetSize (PcdCpuStackSwitchExceptionList) + +#define CPU_STACK_SWITCH_EXCEPTION_LIST \ + FixedPcdGetPtr (PcdCpuStackSwitchExceptionList) + +#define CPU_KNOWN_GOOD_STACK_SIZE \ + FixedPcdGet32 (PcdCpuKnownGoodStackSize) + +#define CPU_TSS_GDT_SIZE (SIZE_2KB + CPU_TSS_DESC_SIZE + CPU_TSS_SIZE) + +// +// Record exception handler information +// +typedef struct { + UINTN ExceptionStart; + UINTN ExceptionStubHeaderSize; + UINTN HookAfterStubHeaderStart; +} EXCEPTION_HANDLER_TEMPLATE_MAP; + +typedef struct { + UINTN IdtEntryCount; + SPIN_LOCK DisplayMessageSpinLock; + RESERVED_VECTORS_DATA *ReservedVectors; + EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler; +} EXCEPTION_HANDLER_DATA; + +extern CONST UINT32 mErrorCodeFlag; +extern CONST UINTN mDoFarReturnFlag; + +/** + Return address map of exception handler template so that C code can generate + exception tables. + + @param AddressMap Pointer to a buffer where the address map is returned. +**/ +VOID +EFIAPI +AsmGetTemplateAddressMap ( + OUT EXCEPTION_HANDLER_TEMPLATE_MAP *AddressMap + ); + +/** + Return address map of exception handler template so that C code can generate + exception tables. + + @param IdtEntry Pointer to IDT entry to be updated. + @param InterruptHandler IDT handler value. + +**/ +VOID +ArchUpdateIdtEntry ( + OUT IA32_IDT_GATE_DESCRIPTOR *IdtEntry, + IN UINTN InterruptHandler + ); + +/** + Read IDT handler value from IDT entry. + + @param IdtEntry Pointer to IDT entry to be read. + +**/ +UINTN +ArchGetIdtHandler ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtEntry + ); + +/** + Prints a message to the serial port. + + @param Format Format string for the message to print. + @param ... Variable argument list whose contents are accessed + based on the format string specified by Format. + +**/ +VOID +EFIAPI +InternalPrintMessage ( + IN CONST CHAR8 *Format, + ... + ); + +/** + Find and display image base address and return image base and its entry point. + + @param CurrentEip Current instruction pointer. + +**/ +VOID +DumpModuleImageInfo ( + IN UINTN CurrentEip + ); + +/** + Display CPU information. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +DumpImageAndCpuContent ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +/** + Internal worker function to initialize exception handler. + + @param[in] VectorInfo Pointer to reserved vector list. + @param[in, out] ExceptionHandlerData Pointer to exception handler data. + + @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized + with default exception handlers. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +InitializeCpuExceptionHandlersWorker ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL, + IN OUT EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ); + +/** + Registers a function to be called from the processor interrupt handler. + + @param[in] InterruptType Defines which interrupt or exception to hook. + @param[in] InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. If this parameter is NULL, then the handler + will be uninstalled + @param[in] ExceptionHandlerData Pointer to exception handler data. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was + previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not + previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported, + or this function is not supported. +**/ +EFI_STATUS +RegisterCpuInterruptHandlerWorker ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ); + +/** + Internal worker function to update IDT entries accordling to vector attributes. + + @param[in] IdtTable Pointer to IDT table. + @param[in] TemplateMap Pointer to a buffer where the address map is + returned. + @param[in] ExceptionHandlerData Pointer to exception handler data. + +**/ +VOID +UpdateIdtTable ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtTable, + IN EXCEPTION_HANDLER_TEMPLATE_MAP *TemplateMap, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ); + +/** + Save CPU exception context when handling EFI_VECTOR_HANDOFF_HOOK_AFTER case. + + @param[in] ExceptionType Exception type. + @param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT. + @param[in] ExceptionHandlerData Pointer to exception handler data. +**/ +VOID +ArchSaveExceptionContext ( + IN UINTN ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ); + +/** + Restore CPU exception context when handling EFI_VECTOR_HANDOFF_HOOK_AFTER case. + + @param[in] ExceptionType Exception type. + @param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT. + @param[in] ExceptionHandlerData Pointer to exception handler data. +**/ +VOID +ArchRestoreExceptionContext ( + IN UINTN ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ); + +/** + Fix up the vector number and function address in the vector code. + + @param[in] NewVectorAddr New vector handler address. + @param[in] VectorNum Index of vector. + @param[in] OldVectorAddr Old vector handler address. + +**/ +VOID +EFIAPI +AsmVectorNumFixup ( + IN VOID *NewVectorAddr, + IN UINT8 VectorNum, + IN VOID *OldVectorAddr + ); + +/** + Read and save reserved vector information + + @param[in] VectorInfo Pointer to reserved vector list. + @param[out] ReservedVector Pointer to reserved vector data buffer. + @param[in] VectorCount Vector number to be updated. + + @return EFI_SUCCESS Read and save vector info successfully. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + +**/ +EFI_STATUS +ReadAndVerifyVectorInfo ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo, + OUT RESERVED_VECTORS_DATA *ReservedVector, + IN UINTN VectorCount + ); + +/** + Get ASCII format string exception name by exception type. + + @param ExceptionType Exception type. + + @return ASCII format string exception name. +**/ +CONST CHAR8 * +GetExceptionNameStr ( + IN EFI_EXCEPTION_TYPE ExceptionType + ); + +/** + Internal worker function for common exception handler. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. + @param ExceptionHandlerData Pointer to exception handler data. +**/ +VOID +CommonExceptionHandlerWorker ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ); + +/** + Setup separate stack for specific exceptions. + + @param[in] StackSwitchData Pointer to data required for setuping up + stack switch. + + @retval EFI_SUCCESS The exceptions have been successfully + initialized with new stack. + @retval EFI_INVALID_PARAMETER StackSwitchData contains invalid content. +**/ +EFI_STATUS +ArchSetupExceptionStack ( + IN CPU_EXCEPTION_INIT_DATA *StackSwitchData + ); + +/** + Return address map of exception handler template so that C code can generate + exception tables. The template is only for exceptions using task gate instead + of interrupt gate. + + @param AddressMap Pointer to a buffer where the address map is returned. +**/ +VOID +EFIAPI +AsmGetTssTemplateMap ( + OUT EXCEPTION_HANDLER_TEMPLATE_MAP *AddressMap + ); + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf new file mode 100644 index 00000000..42cc04ea --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf @@ -0,0 +1,63 @@ +## @file +# CPU Exception Handler library instance for DXE modules. +# +# Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeCpuExceptionHandlerLib + MODULE_UNI_FILE = DxeCpuExceptionHandlerLib.uni + FILE_GUID = B6E9835A-EDCF-4748-98A8-27D3C722E02D + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.1 + LIBRARY_CLASS = CpuExceptionHandlerLib|DXE_CORE DXE_DRIVER UEFI_APPLICATION + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.Ia32] + Ia32/ExceptionHandlerAsm.nasm + Ia32/ExceptionTssEntryAsm.nasm + Ia32/ArchExceptionHandler.c + Ia32/ArchInterruptDefs.h + +[Sources.X64] + X64/Xcode5ExceptionHandlerAsm.nasm + X64/ArchExceptionHandler.c + X64/ArchInterruptDefs.h + +[Sources.common] + CpuExceptionCommon.h + CpuExceptionCommon.c + PeiDxeSmmCpuException.c + DxeException.c + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard + gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList + gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize + +[FeaturePcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStackGuard ## CONSUMES + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + SerialPortLib + PrintLib + SynchronizationLib + LocalApicLib + PeCoffGetEntryPointLib + MemoryAllocationLib + DebugLib + VmgExitLib diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.uni new file mode 100644 index 00000000..e1850eee --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.uni @@ -0,0 +1,16 @@ +// /** @file
+// CPU Exception Handler library instance for DXE modules.
+//
+// CPU Exception Handler library instance for DXE modules.
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU Exception Handler library instance for DXE modules."
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU Exception Handler library instance for DXE modules."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeException.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeException.c new file mode 100644 index 00000000..f27f005b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeException.c @@ -0,0 +1,287 @@ +/** @file + CPU exception handler library implemenation for DXE modules. + + Copyright (c) 2013 - 2017, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> +#include "CpuExceptionCommon.h" +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> + +CONST UINTN mDoFarReturnFlag = 0; + +RESERVED_VECTORS_DATA mReservedVectorsData[CPU_EXCEPTION_NUM]; +EFI_CPU_INTERRUPT_HANDLER mExternalInterruptHandlerTable[CPU_EXCEPTION_NUM]; +UINTN mEnabledInterruptNum = 0; + +EXCEPTION_HANDLER_DATA mExceptionHandlerData; + +UINT8 mNewStack[CPU_STACK_SWITCH_EXCEPTION_NUMBER * + CPU_KNOWN_GOOD_STACK_SIZE]; +UINT8 mNewGdt[CPU_TSS_GDT_SIZE]; + +/** + Common exception handler. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +CommonExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + CommonExceptionHandlerWorker (ExceptionType, SystemContext, &mExceptionHandlerData); +} + +/** + Initializes all CPU exceptions entries and provides the default exception handlers. + + Caller should try to get an array of interrupt and/or exception vectors that are in use and need to + persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification. + If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL. + If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly. + + @param[in] VectorInfo Pointer to reserved vector list. + + @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized + with default exception handlers. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +EFIAPI +InitializeCpuExceptionHandlers ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL + ) +{ + mExceptionHandlerData.ReservedVectors = mReservedVectorsData; + mExceptionHandlerData.ExternalInterruptHandler = mExternalInterruptHandlerTable; + InitializeSpinLock (&mExceptionHandlerData.DisplayMessageSpinLock); + return InitializeCpuExceptionHandlersWorker (VectorInfo, &mExceptionHandlerData); +} + +/** + Initializes all CPU interrupt/exceptions entries and provides the default interrupt/exception handlers. + + Caller should try to get an array of interrupt and/or exception vectors that are in use and need to + persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification. + If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL. + If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly. + + @param[in] VectorInfo Pointer to reserved vector list. + + @retval EFI_SUCCESS All CPU interrupt/exception entries have been successfully initialized + with default interrupt/exception handlers. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +EFIAPI +InitializeCpuInterruptHandlers ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL + ) +{ + EFI_STATUS Status; + IA32_IDT_GATE_DESCRIPTOR *IdtTable; + IA32_DESCRIPTOR IdtDescriptor; + UINTN IdtEntryCount; + EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap; + UINTN Index; + UINTN InterruptEntry; + UINT8 *InterruptEntryCode; + RESERVED_VECTORS_DATA *ReservedVectors; + EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler; + + Status = gBS->AllocatePool ( + EfiBootServicesCode, + sizeof (RESERVED_VECTORS_DATA) * CPU_INTERRUPT_NUM, + (VOID **)&ReservedVectors + ); + ASSERT (!EFI_ERROR (Status) && ReservedVectors != NULL); + SetMem ((VOID *) ReservedVectors, sizeof (RESERVED_VECTORS_DATA) * CPU_INTERRUPT_NUM, 0xff); + if (VectorInfo != NULL) { + Status = ReadAndVerifyVectorInfo (VectorInfo, ReservedVectors, CPU_INTERRUPT_NUM); + if (EFI_ERROR (Status)) { + FreePool (ReservedVectors); + return EFI_INVALID_PARAMETER; + } + } + + ExternalInterruptHandler = AllocateZeroPool (sizeof (EFI_CPU_INTERRUPT_HANDLER) * CPU_INTERRUPT_NUM); + ASSERT (ExternalInterruptHandler != NULL); + + // + // Read IDT descriptor and calculate IDT size + // + AsmReadIdtr (&IdtDescriptor); + IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR); + if (IdtEntryCount > CPU_INTERRUPT_NUM) { + IdtEntryCount = CPU_INTERRUPT_NUM; + } + // + // Create Interrupt Descriptor Table and Copy the old IDT table in + // + IdtTable = AllocateZeroPool (sizeof (IA32_IDT_GATE_DESCRIPTOR) * CPU_INTERRUPT_NUM); + ASSERT (IdtTable != NULL); + CopyMem (IdtTable, (VOID *)IdtDescriptor.Base, sizeof (IA32_IDT_GATE_DESCRIPTOR) * IdtEntryCount); + + AsmGetTemplateAddressMap (&TemplateMap); + ASSERT (TemplateMap.ExceptionStubHeaderSize <= HOOKAFTER_STUB_SIZE); + + Status = gBS->AllocatePool ( + EfiBootServicesCode, + TemplateMap.ExceptionStubHeaderSize * CPU_INTERRUPT_NUM, + (VOID **)&InterruptEntryCode + ); + ASSERT (!EFI_ERROR (Status) && InterruptEntryCode != NULL); + + InterruptEntry = (UINTN) InterruptEntryCode; + for (Index = 0; Index < CPU_INTERRUPT_NUM; Index ++) { + CopyMem ( + (VOID *) InterruptEntry, + (VOID *) TemplateMap.ExceptionStart, + TemplateMap.ExceptionStubHeaderSize + ); + AsmVectorNumFixup ((VOID *) InterruptEntry, (UINT8) Index, (VOID *) TemplateMap.ExceptionStart); + InterruptEntry += TemplateMap.ExceptionStubHeaderSize; + } + + TemplateMap.ExceptionStart = (UINTN) InterruptEntryCode; + mExceptionHandlerData.IdtEntryCount = CPU_INTERRUPT_NUM; + mExceptionHandlerData.ReservedVectors = ReservedVectors; + mExceptionHandlerData.ExternalInterruptHandler = ExternalInterruptHandler; + InitializeSpinLock (&mExceptionHandlerData.DisplayMessageSpinLock); + + UpdateIdtTable (IdtTable, &TemplateMap, &mExceptionHandlerData); + + // + // Load Interrupt Descriptor Table + // + IdtDescriptor.Base = (UINTN) IdtTable; + IdtDescriptor.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * CPU_INTERRUPT_NUM - 1); + AsmWriteIdtr ((IA32_DESCRIPTOR *) &IdtDescriptor); + + return EFI_SUCCESS; +} + +/** + Registers a function to be called from the processor interrupt handler. + + This function registers and enables the handler specified by InterruptHandler for a processor + interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the + handler for the processor interrupt or exception type specified by InterruptType is uninstalled. + The installed handler is called once for each processor interrupt or exception. + NOTE: This function should be invoked after InitializeCpuExceptionHandlers() or + InitializeCpuInterruptHandlers() invoked, otherwise EFI_UNSUPPORTED returned. + + @param[in] InterruptType Defines which interrupt or exception to hook. + @param[in] InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. If this parameter is NULL, then the handler + will be uninstalled. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was + previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not + previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported, + or this function is not supported. +**/ +EFI_STATUS +EFIAPI +RegisterCpuInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ) +{ + return RegisterCpuInterruptHandlerWorker (InterruptType, InterruptHandler, &mExceptionHandlerData); +} + +/** + Initializes CPU exceptions entries and setup stack switch for given exceptions. + + This method will call InitializeCpuExceptionHandlers() to setup default + exception handlers unless indicated not to do it explicitly. + + If InitData is passed with NULL, this method will use the resource reserved + by global variables to initialize it; Otherwise it will use data in InitData + to setup stack switch. This is for the different use cases in DxeCore and + Cpu MP exception initialization. + + @param[in] VectorInfo Pointer to reserved vector list. + @param[in] InitData Pointer to data required to setup stack switch for + given exceptions. + + @retval EFI_SUCCESS The exceptions have been successfully + initialized. + @retval EFI_INVALID_PARAMETER VectorInfo or InitData contains invalid + content. + +**/ +EFI_STATUS +EFIAPI +InitializeCpuExceptionHandlersEx ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL, + IN CPU_EXCEPTION_INIT_DATA *InitData OPTIONAL + ) +{ + EFI_STATUS Status; + CPU_EXCEPTION_INIT_DATA EssData; + IA32_DESCRIPTOR Idtr; + IA32_DESCRIPTOR Gdtr; + + // + // To avoid repeat initialization of default handlers, the caller should pass + // an extended init data with InitDefaultHandlers set to FALSE. There's no + // need to call this method to just initialize default handlers. Call non-ex + // version instead; or this method must be implemented as a simple wrapper of + // non-ex version of it, if this version has to be called. + // + if (InitData == NULL || InitData->X64.InitDefaultHandlers) { + Status = InitializeCpuExceptionHandlers (VectorInfo); + } else { + Status = EFI_SUCCESS; + } + + if (!EFI_ERROR (Status)) { + // + // Initializing stack switch is only necessary for Stack Guard functionality. + // + if (PcdGetBool (PcdCpuStackGuard)) { + if (InitData == NULL) { + SetMem (mNewGdt, sizeof (mNewGdt), 0); + + AsmReadIdtr (&Idtr); + AsmReadGdtr (&Gdtr); + + EssData.X64.Revision = CPU_EXCEPTION_INIT_DATA_REV; + EssData.X64.KnownGoodStackTop = (UINTN)mNewStack + sizeof (mNewStack); + EssData.X64.KnownGoodStackSize = CPU_KNOWN_GOOD_STACK_SIZE; + EssData.X64.StackSwitchExceptions = CPU_STACK_SWITCH_EXCEPTION_LIST; + EssData.X64.StackSwitchExceptionNumber = CPU_STACK_SWITCH_EXCEPTION_NUMBER; + EssData.X64.IdtTable = (VOID *)Idtr.Base; + EssData.X64.IdtTableSize = Idtr.Limit + 1; + EssData.X64.GdtTable = mNewGdt; + EssData.X64.GdtTableSize = sizeof (mNewGdt); + EssData.X64.ExceptionTssDesc = mNewGdt + Gdtr.Limit + 1; + EssData.X64.ExceptionTssDescSize = CPU_TSS_DESC_SIZE; + EssData.X64.ExceptionTss = mNewGdt + Gdtr.Limit + 1 + CPU_TSS_DESC_SIZE; + EssData.X64.ExceptionTssSize = CPU_TSS_SIZE; + + InitData = &EssData; + } + Status = ArchSetupExceptionStack (InitData); + } + } + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c new file mode 100644 index 00000000..2662551d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c @@ -0,0 +1,427 @@ +/** @file + IA32 CPU Exception Handler functons. + + Copyright (c) 2012 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuExceptionCommon.h" + +/** + Return address map of exception handler template so that C code can generate + exception tables. + + @param IdtEntry Pointer to IDT entry to be updated. + @param InterruptHandler IDT handler value. + +**/ +VOID +ArchUpdateIdtEntry ( + OUT IA32_IDT_GATE_DESCRIPTOR *IdtEntry, + IN UINTN InterruptHandler + ) +{ + IdtEntry->Bits.OffsetLow = (UINT16)(UINTN)InterruptHandler; + IdtEntry->Bits.OffsetHigh = (UINT16)((UINTN)InterruptHandler >> 16); + IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; +} + +/** + Read IDT handler value from IDT entry. + + @param IdtEntry Pointer to IDT entry to be read. + +**/ +UINTN +ArchGetIdtHandler ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtEntry + ) +{ + return (UINTN)IdtEntry->Bits.OffsetLow + (((UINTN)IdtEntry->Bits.OffsetHigh) << 16); +} + +/** + Save CPU exception context when handling EFI_VECTOR_HANDOFF_HOOK_AFTER case. + + @param[in] ExceptionType Exception type. + @param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT. + @param[in] ExceptionHandlerData Pointer to exception handler data. +**/ +VOID +ArchSaveExceptionContext ( + IN UINTN ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + IA32_EFLAGS32 Eflags; + RESERVED_VECTORS_DATA *ReservedVectors; + + ReservedVectors = ExceptionHandlerData->ReservedVectors; + // + // Save Exception context in global variable in first entry of the exception handler. + // So when original exception handler returns to the new exception handler (second entry), + // the Eflags/Cs/Eip/ExceptionData can be used. + // + ReservedVectors[ExceptionType].OldFlags = SystemContext.SystemContextIa32->Eflags; + ReservedVectors[ExceptionType].OldCs = SystemContext.SystemContextIa32->Cs; + ReservedVectors[ExceptionType].OldIp = SystemContext.SystemContextIa32->Eip; + ReservedVectors[ExceptionType].ExceptionData = SystemContext.SystemContextIa32->ExceptionData; + // + // Clear IF flag to avoid old IDT handler enable interrupt by IRET + // + Eflags.UintN = SystemContext.SystemContextIa32->Eflags; + Eflags.Bits.IF = 0; + SystemContext.SystemContextIa32->Eflags = Eflags.UintN; + // + // Modify the EIP in stack, then old IDT handler will return to HookAfterStubBegin. + // + SystemContext.SystemContextIa32->Eip = (UINTN) ReservedVectors[ExceptionType].HookAfterStubHeaderCode; +} + +/** + Restore CPU exception context when handling EFI_VECTOR_HANDOFF_HOOK_AFTER case. + + @param[in] ExceptionType Exception type. + @param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT. + @param[in] ExceptionHandlerData Pointer to exception handler data. +**/ +VOID +ArchRestoreExceptionContext ( + IN UINTN ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + RESERVED_VECTORS_DATA *ReservedVectors; + + ReservedVectors = ExceptionHandlerData->ReservedVectors; + SystemContext.SystemContextIa32->Eflags = ReservedVectors[ExceptionType].OldFlags; + SystemContext.SystemContextIa32->Cs = ReservedVectors[ExceptionType].OldCs; + SystemContext.SystemContextIa32->Eip = ReservedVectors[ExceptionType].OldIp; + SystemContext.SystemContextIa32->ExceptionData = ReservedVectors[ExceptionType].ExceptionData; +} + +/** + Setup separate stack for given exceptions. + + @param[in] StackSwitchData Pointer to data required for setuping up + stack switch. + + @retval EFI_SUCCESS The exceptions have been successfully + initialized with new stack. + @retval EFI_INVALID_PARAMETER StackSwitchData contains invalid content. + +**/ +EFI_STATUS +ArchSetupExceptionStack ( + IN CPU_EXCEPTION_INIT_DATA *StackSwitchData + ) +{ + IA32_DESCRIPTOR Gdtr; + IA32_DESCRIPTOR Idtr; + IA32_IDT_GATE_DESCRIPTOR *IdtTable; + IA32_TSS_DESCRIPTOR *TssDesc; + IA32_TASK_STATE_SEGMENT *Tss; + UINTN StackTop; + UINTN Index; + UINTN Vector; + UINTN TssBase; + UINTN GdtSize; + EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap; + + if (StackSwitchData == NULL || + StackSwitchData->Ia32.Revision != CPU_EXCEPTION_INIT_DATA_REV || + StackSwitchData->Ia32.KnownGoodStackTop == 0 || + StackSwitchData->Ia32.KnownGoodStackSize == 0 || + StackSwitchData->Ia32.StackSwitchExceptions == NULL || + StackSwitchData->Ia32.StackSwitchExceptionNumber == 0 || + StackSwitchData->Ia32.StackSwitchExceptionNumber > CPU_EXCEPTION_NUM || + StackSwitchData->Ia32.GdtTable == NULL || + StackSwitchData->Ia32.IdtTable == NULL || + StackSwitchData->Ia32.ExceptionTssDesc == NULL || + StackSwitchData->Ia32.ExceptionTss == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // The caller is responsible for that the GDT table, no matter the existing + // one or newly allocated, has enough space to hold descriptors for exception + // task-state segments. + // + if (((UINTN)StackSwitchData->Ia32.GdtTable & (IA32_GDT_ALIGNMENT - 1)) != 0) { + return EFI_INVALID_PARAMETER; + } + + if ((UINTN)StackSwitchData->Ia32.ExceptionTssDesc < (UINTN)(StackSwitchData->Ia32.GdtTable)) { + return EFI_INVALID_PARAMETER; + } + + if ((UINTN)StackSwitchData->Ia32.ExceptionTssDesc + StackSwitchData->Ia32.ExceptionTssDescSize > + ((UINTN)(StackSwitchData->Ia32.GdtTable) + StackSwitchData->Ia32.GdtTableSize)) { + return EFI_INVALID_PARAMETER; + } + + // + // We need one descriptor and one TSS for current task and every exception + // specified. + // + if (StackSwitchData->Ia32.ExceptionTssDescSize < + sizeof (IA32_TSS_DESCRIPTOR) * (StackSwitchData->Ia32.StackSwitchExceptionNumber + 1)) { + return EFI_INVALID_PARAMETER; + } + if (StackSwitchData->Ia32.ExceptionTssSize < + sizeof (IA32_TASK_STATE_SEGMENT) * (StackSwitchData->Ia32.StackSwitchExceptionNumber + 1)) { + return EFI_INVALID_PARAMETER; + } + + TssDesc = StackSwitchData->Ia32.ExceptionTssDesc; + Tss = StackSwitchData->Ia32.ExceptionTss; + + // + // Initialize new GDT table and/or IDT table, if any + // + AsmReadIdtr (&Idtr); + AsmReadGdtr (&Gdtr); + + GdtSize = (UINTN)TssDesc + + sizeof (IA32_TSS_DESCRIPTOR) * + (StackSwitchData->Ia32.StackSwitchExceptionNumber + 1) - + (UINTN)(StackSwitchData->Ia32.GdtTable); + if ((UINTN)StackSwitchData->Ia32.GdtTable != Gdtr.Base) { + CopyMem (StackSwitchData->Ia32.GdtTable, (VOID *)Gdtr.Base, Gdtr.Limit + 1); + Gdtr.Base = (UINTN)StackSwitchData->Ia32.GdtTable; + Gdtr.Limit = (UINT16)GdtSize - 1; + } + + if ((UINTN)StackSwitchData->Ia32.IdtTable != Idtr.Base) { + Idtr.Base = (UINTN)StackSwitchData->Ia32.IdtTable; + } + if (StackSwitchData->Ia32.IdtTableSize > 0) { + Idtr.Limit = (UINT16)(StackSwitchData->Ia32.IdtTableSize - 1); + } + + // + // Fixup current task descriptor. Task-state segment for current task will + // be filled by processor during task switching. + // + TssBase = (UINTN)Tss; + + TssDesc->Uint64 = 0; + TssDesc->Bits.LimitLow = sizeof(IA32_TASK_STATE_SEGMENT) - 1; + TssDesc->Bits.BaseLow = (UINT16)TssBase; + TssDesc->Bits.BaseMid = (UINT8)(TssBase >> 16); + TssDesc->Bits.Type = IA32_GDT_TYPE_TSS; + TssDesc->Bits.P = 1; + TssDesc->Bits.LimitHigh = 0; + TssDesc->Bits.BaseHigh = (UINT8)(TssBase >> 24); + + // + // Fixup exception task descriptor and task-state segment + // + AsmGetTssTemplateMap (&TemplateMap); + StackTop = StackSwitchData->Ia32.KnownGoodStackTop - CPU_STACK_ALIGNMENT; + StackTop = (UINTN)ALIGN_POINTER (StackTop, CPU_STACK_ALIGNMENT); + IdtTable = StackSwitchData->Ia32.IdtTable; + for (Index = 0; Index < StackSwitchData->Ia32.StackSwitchExceptionNumber; ++Index) { + TssDesc += 1; + Tss += 1; + + // + // Fixup TSS descriptor + // + TssBase = (UINTN)Tss; + + TssDesc->Uint64 = 0; + TssDesc->Bits.LimitLow = sizeof(IA32_TASK_STATE_SEGMENT) - 1; + TssDesc->Bits.BaseLow = (UINT16)TssBase; + TssDesc->Bits.BaseMid = (UINT8)(TssBase >> 16); + TssDesc->Bits.Type = IA32_GDT_TYPE_TSS; + TssDesc->Bits.P = 1; + TssDesc->Bits.LimitHigh = 0; + TssDesc->Bits.BaseHigh = (UINT8)(TssBase >> 24); + + // + // Fixup TSS + // + Vector = StackSwitchData->Ia32.StackSwitchExceptions[Index]; + if (Vector >= CPU_EXCEPTION_NUM || + Vector >= (Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR)) { + continue; + } + + ZeroMem (Tss, sizeof (*Tss)); + Tss->EIP = (UINT32)(TemplateMap.ExceptionStart + + Vector * TemplateMap.ExceptionStubHeaderSize); + Tss->EFLAGS = 0x2; + Tss->ESP = StackTop; + Tss->CR3 = AsmReadCr3 (); + Tss->ES = AsmReadEs (); + Tss->CS = AsmReadCs (); + Tss->SS = AsmReadSs (); + Tss->DS = AsmReadDs (); + Tss->FS = AsmReadFs (); + Tss->GS = AsmReadGs (); + + StackTop -= StackSwitchData->Ia32.KnownGoodStackSize; + + // + // Update IDT to use Task Gate for given exception + // + IdtTable[Vector].Bits.OffsetLow = 0; + IdtTable[Vector].Bits.Selector = (UINT16)((UINTN)TssDesc - Gdtr.Base); + IdtTable[Vector].Bits.Reserved_0 = 0; + IdtTable[Vector].Bits.GateType = IA32_IDT_GATE_TYPE_TASK; + IdtTable[Vector].Bits.OffsetHigh = 0; + } + + // + // Publish GDT + // + AsmWriteGdtr (&Gdtr); + + // + // Load current task + // + AsmWriteTr ((UINT16)((UINTN)StackSwitchData->Ia32.ExceptionTssDesc - Gdtr.Base)); + + // + // Publish IDT + // + AsmWriteIdtr (&Idtr); + + return EFI_SUCCESS; +} + +/** + Display processor context. + + @param[in] ExceptionType Exception type. + @param[in] SystemContext Processor context to be display. +**/ +VOID +EFIAPI +DumpCpuContext ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + InternalPrintMessage ( + "!!!! IA32 Exception Type - %02x(%a) CPU Apic ID - %08x !!!!\n", + ExceptionType, + GetExceptionNameStr (ExceptionType), + GetApicId () + ); + if ((mErrorCodeFlag & (1 << ExceptionType)) != 0) { + InternalPrintMessage ( + "ExceptionData - %08x", + SystemContext.SystemContextIa32->ExceptionData + ); + if (ExceptionType == EXCEPT_IA32_PAGE_FAULT) { + InternalPrintMessage ( + " I:%x R:%x U:%x W:%x P:%x PK:%x SS:%x SGX:%x", + (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0, + (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_RSVD) != 0, + (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_US) != 0, + (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_WR) != 0, + (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_P) != 0, + (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_PK) != 0, + (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_SS) != 0, + (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_SGX) != 0 + ); + } + InternalPrintMessage ("\n"); + } + InternalPrintMessage ( + "EIP - %08x, CS - %08x, EFLAGS - %08x\n", + SystemContext.SystemContextIa32->Eip, + SystemContext.SystemContextIa32->Cs, + SystemContext.SystemContextIa32->Eflags + ); + InternalPrintMessage ( + "EAX - %08x, ECX - %08x, EDX - %08x, EBX - %08x\n", + SystemContext.SystemContextIa32->Eax, + SystemContext.SystemContextIa32->Ecx, + SystemContext.SystemContextIa32->Edx, + SystemContext.SystemContextIa32->Ebx + ); + InternalPrintMessage ( + "ESP - %08x, EBP - %08x, ESI - %08x, EDI - %08x\n", + SystemContext.SystemContextIa32->Esp, + SystemContext.SystemContextIa32->Ebp, + SystemContext.SystemContextIa32->Esi, + SystemContext.SystemContextIa32->Edi + ); + InternalPrintMessage ( + "DS - %08x, ES - %08x, FS - %08x, GS - %08x, SS - %08x\n", + SystemContext.SystemContextIa32->Ds, + SystemContext.SystemContextIa32->Es, + SystemContext.SystemContextIa32->Fs, + SystemContext.SystemContextIa32->Gs, + SystemContext.SystemContextIa32->Ss + ); + InternalPrintMessage ( + "CR0 - %08x, CR2 - %08x, CR3 - %08x, CR4 - %08x\n", + SystemContext.SystemContextIa32->Cr0, + SystemContext.SystemContextIa32->Cr2, + SystemContext.SystemContextIa32->Cr3, + SystemContext.SystemContextIa32->Cr4 + ); + InternalPrintMessage ( + "DR0 - %08x, DR1 - %08x, DR2 - %08x, DR3 - %08x\n", + SystemContext.SystemContextIa32->Dr0, + SystemContext.SystemContextIa32->Dr1, + SystemContext.SystemContextIa32->Dr2, + SystemContext.SystemContextIa32->Dr3 + ); + InternalPrintMessage ( + "DR6 - %08x, DR7 - %08x\n", + SystemContext.SystemContextIa32->Dr6, + SystemContext.SystemContextIa32->Dr7 + ); + InternalPrintMessage ( + "GDTR - %08x %08x, IDTR - %08x %08x\n", + SystemContext.SystemContextIa32->Gdtr[0], + SystemContext.SystemContextIa32->Gdtr[1], + SystemContext.SystemContextIa32->Idtr[0], + SystemContext.SystemContextIa32->Idtr[1] + ); + InternalPrintMessage ( + "LDTR - %08x, TR - %08x\n", + SystemContext.SystemContextIa32->Ldtr, + SystemContext.SystemContextIa32->Tr + ); + InternalPrintMessage ( + "FXSAVE_STATE - %08x\n", + &SystemContext.SystemContextIa32->FxSaveState + ); +} + +/** + Display CPU information. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +DumpImageAndCpuContent ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + DumpCpuContext (ExceptionType, SystemContext); + // + // Dump module image base and module entry point by EIP + // + if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) && + ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0)) { + // + // The EIP in SystemContext could not be used + // if it is page fault with I/D set. + // + DumpModuleImageInfo ((*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp)); + } else { + DumpModuleImageInfo (SystemContext.SystemContextIa32->Eip); + } +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchInterruptDefs.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchInterruptDefs.h new file mode 100644 index 00000000..6e8f1a0d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchInterruptDefs.h @@ -0,0 +1,46 @@ +/** @file + Ia32 arch definition for CPU Exception Handler Library. + + Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _ARCH_CPU_INTERRUPT_DEFS_H_ +#define _ARCH_CPU_INTERRUPT_DEFS_H_ + +typedef struct { + EFI_SYSTEM_CONTEXT_IA32 SystemContext; + BOOLEAN ExceptionDataFlag; + UINTN OldIdtHandler; +} EXCEPTION_HANDLER_CONTEXT; + +// +// Register Structure Definitions +// +typedef struct { + EFI_STATUS_CODE_DATA Header; + EFI_SYSTEM_CONTEXT_IA32 SystemContext; +} CPU_STATUS_CODE_TEMPLATE; + +typedef struct { + SPIN_LOCK SpinLock; + UINT32 ApicId; + UINT32 Attribute; + UINTN ExceptonHandler; + UINTN OldFlags; + UINTN OldCs; + UINTN OldIp; + UINTN ExceptionData; + UINT8 HookAfterStubHeaderCode[HOOKAFTER_STUB_SIZE]; +} RESERVED_VECTORS_DATA; + +#define CPU_TSS_DESC_SIZE \ + (sizeof (IA32_TSS_DESCRIPTOR) * \ + (FixedPcdGetSize (PcdCpuStackSwitchExceptionList) + 1)) + +#define CPU_TSS_SIZE \ + (sizeof (IA32_TASK_STATE_SEGMENT) * \ + (FixedPcdGetSize (PcdCpuStackSwitchExceptionList) + 1)) + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ExceptionHandlerAsm.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ExceptionHandlerAsm.nasm new file mode 100644 index 00000000..c2b92d53 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ExceptionHandlerAsm.nasm @@ -0,0 +1,456 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; ExceptionHandlerAsm.Asm +; +; Abstract: +; +; IA32 CPU Exception Handler +; +; Notes: +; +;------------------------------------------------------------------------------ + +; +; CommonExceptionHandler() +; +extern ASM_PFX(CommonExceptionHandler) + +SECTION .data + +extern ASM_PFX(mErrorCodeFlag) ; Error code flags for exceptions +extern ASM_PFX(mDoFarReturnFlag) ; Do far return flag + +SECTION .text + +ALIGN 8 + +; +; exception handler stub table +; +AsmIdtVectorBegin: +%rep 32 + db 0x6a ; push #VectorNum + db ($ - AsmIdtVectorBegin) / ((AsmIdtVectorEnd - AsmIdtVectorBegin) / 32) ; VectorNum + push eax + mov eax, ASM_PFX(CommonInterruptEntry) + jmp eax +%endrep +AsmIdtVectorEnd: + +HookAfterStubBegin: + db 0x6a ; push +VectorNum: + db 0 ; 0 will be fixed + push eax + mov eax, HookAfterStubHeaderEnd + jmp eax +HookAfterStubHeaderEnd: + pop eax + sub esp, 8 ; reserve room for filling exception data later + push dword [esp + 8] + xchg ecx, [esp] ; get vector number + bt [ASM_PFX(mErrorCodeFlag)], ecx + jnc .0 + push dword [esp] ; addition push if exception data needed +.0: + xchg ecx, [esp] ; restore ecx + push eax + +;----------------------------------------------------------------------------; +; CommonInterruptEntry ; +;----------------------------------------------------------------------------; +; The follow algorithm is used for the common interrupt routine. +; Entry from each interrupt with a push eax and eax=interrupt number +; Stack: +; +---------------------+ +; + EFlags + +; +---------------------+ +; + CS + +; +---------------------+ +; + EIP + +; +---------------------+ +; + Error Code + +; +---------------------+ +; + Vector Number + +; +---------------------+ +; + EBP + +; +---------------------+ <-- EBP +global ASM_PFX(CommonInterruptEntry) +ASM_PFX(CommonInterruptEntry): + cli + pop eax + ; + ; All interrupt handlers are invoked through interrupt gates, so + ; IF flag automatically cleared at the entry point + ; + + ; + ; Get vector number from top of stack + ; + xchg ecx, [esp] + and ecx, 0xFF ; Vector number should be less than 256 + cmp ecx, 32 ; Intel reserved vector for exceptions? + jae NoErrorCode + bt [ASM_PFX(mErrorCodeFlag)], ecx + jc HasErrorCode + +NoErrorCode: + + ; + ; Stack: + ; +---------------------+ + ; + EFlags + + ; +---------------------+ + ; + CS + + ; +---------------------+ + ; + EIP + + ; +---------------------+ + ; + ECX + + ; +---------------------+ <-- ESP + ; + ; Registers: + ; ECX - Vector Number + ; + + ; + ; Put Vector Number on stack + ; + push ecx + + ; + ; Put 0 (dummy) error code on stack, and restore ECX + ; + xor ecx, ecx ; ECX = 0 + xchg ecx, [esp+4] + + jmp ErrorCodeAndVectorOnStack + +HasErrorCode: + + ; + ; Stack: + ; +---------------------+ + ; + EFlags + + ; +---------------------+ + ; + CS + + ; +---------------------+ + ; + EIP + + ; +---------------------+ + ; + Error Code + + ; +---------------------+ + ; + ECX + + ; +---------------------+ <-- ESP + ; + ; Registers: + ; ECX - Vector Number + ; + + ; + ; Put Vector Number on stack and restore ECX + ; + xchg ecx, [esp] + +ErrorCodeAndVectorOnStack: + push ebp + mov ebp, esp + + ; + ; Stack: + ; +---------------------+ + ; + EFlags + + ; +---------------------+ + ; + CS + + ; +---------------------+ + ; + EIP + + ; +---------------------+ + ; + Error Code + + ; +---------------------+ + ; + Vector Number + + ; +---------------------+ + ; + EBP + + ; +---------------------+ <-- EBP + ; + + ; + ; Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32 + ; is 16-byte aligned + ; + and esp, 0xfffffff0 + sub esp, 12 + + sub esp, 8 + push 0 ; clear EXCEPTION_HANDLER_CONTEXT.OldIdtHandler + push 0 ; clear EXCEPTION_HANDLER_CONTEXT.ExceptionDataFlag + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + push eax + push ecx + push edx + push ebx + lea ecx, [ebp + 6 * 4] + push ecx ; ESP + push dword [ebp] ; EBP + push esi + push edi + +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; + mov eax, ss + push eax + movzx eax, word [ebp + 4 * 4] + push eax + mov eax, ds + push eax + mov eax, es + push eax + mov eax, fs + push eax + mov eax, gs + push eax + +;; UINT32 Eip; + mov eax, [ebp + 3 * 4] + push eax + +;; UINT32 Gdtr[2], Idtr[2]; + sub esp, 8 + sidt [esp] + mov eax, [esp + 2] + xchg eax, [esp] + and eax, 0xFFFF + mov [esp+4], eax + + sub esp, 8 + sgdt [esp] + mov eax, [esp + 2] + xchg eax, [esp] + and eax, 0xFFFF + mov [esp+4], eax + +;; UINT32 Ldtr, Tr; + xor eax, eax + str ax + push eax + sldt ax + push eax + +;; UINT32 EFlags; + mov eax, [ebp + 5 * 4] + push eax + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + mov eax, 1 + push ebx ; temporarily save value of ebx on stack + cpuid ; use CPUID to determine if FXSAVE/FXRESTOR and DE + ; are supported + pop ebx ; retore value of ebx that was overwritten by CPUID + mov eax, cr4 + push eax ; push cr4 firstly + test edx, BIT24 ; Test for FXSAVE/FXRESTOR support + jz .1 + or eax, BIT9 ; Set CR4.OSFXSR +.1: + test edx, BIT2 ; Test for Debugging Extensions support + jz .2 + or eax, BIT3 ; Set CR4.DE +.2: + mov cr4, eax + mov eax, cr3 + push eax + mov eax, cr2 + push eax + xor eax, eax + push eax + mov eax, cr0 + push eax + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov eax, dr7 + push eax + mov eax, dr6 + push eax + mov eax, dr3 + push eax + mov eax, dr2 + push eax + mov eax, dr1 + push eax + mov eax, dr0 + push eax + +;; FX_SAVE_STATE_IA32 FxSaveState; + sub esp, 512 + mov edi, esp + test edx, BIT24 ; Test for FXSAVE/FXRESTOR support. + ; edx still contains result from CPUID above + jz .3 + db 0xf, 0xae, 0x7 ;fxsave [edi] +.3: + +;; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear + cld + +;; UINT32 ExceptionData; + push dword [ebp + 2 * 4] + +;; Prepare parameter and call + mov edx, esp + push edx + mov edx, dword [ebp + 1 * 4] + push edx + + ; + ; Call External Exception Handler + ; + mov eax, ASM_PFX(CommonExceptionHandler) + call eax + add esp, 8 + + cli +;; UINT32 ExceptionData; + add esp, 4 + +;; FX_SAVE_STATE_IA32 FxSaveState; + mov esi, esp + mov eax, 1 + cpuid ; use CPUID to determine if FXSAVE/FXRESTOR + ; are supported + test edx, BIT24 ; Test for FXSAVE/FXRESTOR support + jz .4 + db 0xf, 0xae, 0xe ; fxrstor [esi] +.4: + add esp, 512 + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; Skip restoration of DRx registers to support in-circuit emualators +;; or debuggers set breakpoint in interrupt/exception context + add esp, 4 * 6 + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + pop eax + mov cr0, eax + add esp, 4 ; not for Cr1 + pop eax + mov cr2, eax + pop eax + mov cr3, eax + pop eax + mov cr4, eax + +;; UINT32 EFlags; + pop dword [ebp + 5 * 4] + +;; UINT32 Ldtr, Tr; +;; UINT32 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add esp, 24 + +;; UINT32 Eip; + pop dword [ebp + 3 * 4] + +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; +;; NOTE - modified segment registers could hang the debugger... We +;; could attempt to insulate ourselves against this possibility, +;; but that poses risks as well. +;; + pop gs + pop fs + pop es + pop ds + pop dword [ebp + 4 * 4] + pop ss + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + pop edi + pop esi + add esp, 4 ; not for ebp + add esp, 4 ; not for esp + pop ebx + pop edx + pop ecx + pop eax + + pop dword [ebp - 8] + pop dword [ebp - 4] + mov esp, ebp + pop ebp + add esp, 8 + cmp dword [esp - 16], 0 ; check EXCEPTION_HANDLER_CONTEXT.OldIdtHandler + jz DoReturn + cmp dword [esp - 20], 1 ; check EXCEPTION_HANDLER_CONTEXT.ExceptionDataFlag + jz ErrorCode + jmp dword [esp - 16] +ErrorCode: + sub esp, 4 + jmp dword [esp - 12] + +DoReturn: + cmp dword [ASM_PFX(mDoFarReturnFlag)], 0 ; Check if need to do far return instead of IRET + jz DoIret + push dword [esp + 8] ; save EFLAGS + add esp, 16 + push dword [esp - 8] ; save CS in new location + push dword [esp - 8] ; save EIP in new location + push dword [esp - 8] ; save EFLAGS in new location + popfd ; restore EFLAGS + retf ; far return + +DoIret: + iretd + +;---------------------------------------; +; _AsmGetTemplateAddressMap ; +;----------------------------------------------------------------------------; +; +; Protocol prototype +; AsmGetTemplateAddressMap ( +; EXCEPTION_HANDLER_TEMPLATE_MAP *AddressMap +; ); +; +; Routine Description: +; +; Return address map of interrupt handler template so that C code can generate +; interrupt table. +; +; Arguments: +; +; +; Returns: +; +; Nothing +; +; +; Input: [ebp][0] = Original ebp +; [ebp][4] = Return address +; +; Output: Nothing +; +; Destroys: Nothing +;-----------------------------------------------------------------------------; +global ASM_PFX(AsmGetTemplateAddressMap) +ASM_PFX(AsmGetTemplateAddressMap): + push ebp ; C prolog + mov ebp, esp + pushad + + mov ebx, dword [ebp + 0x8] + mov dword [ebx], AsmIdtVectorBegin + mov dword [ebx + 0x4], (AsmIdtVectorEnd - AsmIdtVectorBegin) / 32 + mov dword [ebx + 0x8], HookAfterStubBegin + + popad + pop ebp + ret + +;------------------------------------------------------------------------------------- +; AsmVectorNumFixup (*NewVectorAddr, VectorNum, *OldVectorAddr); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmVectorNumFixup) +ASM_PFX(AsmVectorNumFixup): + mov eax, dword [esp + 8] + mov ecx, [esp + 4] + mov [ecx + (VectorNum - HookAfterStubBegin)], al + ret diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ExceptionTssEntryAsm.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ExceptionTssEntryAsm.nasm new file mode 100644 index 00000000..da2aa2ba --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ExceptionTssEntryAsm.nasm @@ -0,0 +1,390 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; ExceptionTssEntryAsm.Asm +; +; Abstract: +; +; IA32 CPU Exception Handler with Separate Stack +; +; Notes: +; +;------------------------------------------------------------------------------ + +; +; IA32 TSS Memory Layout Description +; +struc IA32_TSS + resw 1 + resw 1 + .ESP0: resd 1 + .SS0: resw 1 + resw 1 + .ESP1: resd 1 + .SS1: resw 1 + resw 1 + .ESP2: resd 1 + .SS2: resw 1 + resw 1 + ._CR3: resd 1 + .EIP: resd 1 + .EFLAGS: resd 1 + ._EAX: resd 1 + ._ECX: resd 1 + ._EDX: resd 1 + ._EBX: resd 1 + ._ESP: resd 1 + ._EBP: resd 1 + ._ESI: resd 1 + ._EDI: resd 1 + ._ES: resw 1 + resw 1 + ._CS: resw 1 + resw 1 + ._SS: resw 1 + resw 1 + ._DS: resw 1 + resw 1 + ._FS: resw 1 + resw 1 + ._GS: resw 1 + resw 1 + .LDT: resw 1 + resw 1 + resw 1 + resw 1 +endstruc + +; +; CommonExceptionHandler() +; +extern ASM_PFX(CommonExceptionHandler) + +SECTION .data + +SECTION .text + +ALIGN 8 + +; +; Exception handler stub table +; +AsmExceptionEntryBegin: +%assign Vector 0 +%rep 32 + +DoIret%[Vector]: + iretd +ASM_PFX(ExceptionTaskSwtichEntry%[Vector]): + db 0x6a ; push #VectorNum + db %[Vector] + mov eax, ASM_PFX(CommonTaskSwtichEntryPoint) + call eax + mov esp, eax ; Restore stack top + jmp DoIret%[Vector] + +%assign Vector Vector+1 +%endrep +AsmExceptionEntryEnd: + +; +; Common part of exception handler +; +global ASM_PFX(CommonTaskSwtichEntryPoint) +ASM_PFX(CommonTaskSwtichEntryPoint): + ; + ; Stack: + ; +---------------------+ <-- EBP - 8 + ; + TSS Base + + ; +---------------------+ <-- EBP - 4 + ; + CPUID.EDX + + ; +---------------------+ <-- EBP + ; + EIP + + ; +---------------------+ <-- EBP + 4 + ; + Vector Number + + ; +---------------------+ <-- EBP + 8 + ; + Error Code + + ; +---------------------+ + ; + + mov ebp, esp ; Stack frame + +; Use CPUID to determine if FXSAVE/FXRESTOR and DE are supported + mov eax, 1 + cpuid + push edx + +; Get TSS base of interrupted task through PreviousTaskLink field in +; current TSS base + sub esp, 8 + sgdt [esp + 2] + mov eax, [esp + 4] ; GDT base + add esp, 8 + + xor ebx, ebx + str bx ; Current TR + + mov ecx, [eax + ebx + 2] + shl ecx, 8 + mov cl, [eax + ebx + 7] + ror ecx, 8 ; ecx = Current TSS base + push ecx ; keep it in stack for later use + + movzx ebx, word [ecx] ; Previous Task Link + mov ecx, [eax + ebx + 2] + shl ecx, 8 + mov cl, [eax + ebx + 7] + ror ecx, 8 ; ecx = Previous TSS base + +; +; Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32 +; is 16-byte aligned +; + and esp, 0xfffffff0 + sub esp, 12 + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + push dword [ecx + IA32_TSS._EAX] + push dword [ecx + IA32_TSS._ECX] + push dword [ecx + IA32_TSS._EDX] + push dword [ecx + IA32_TSS._EBX] + push dword [ecx + IA32_TSS._ESP] + push dword [ecx + IA32_TSS._EBP] + push dword [ecx + IA32_TSS._ESI] + push dword [ecx + IA32_TSS._EDI] + +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; + movzx eax, word [ecx + IA32_TSS._SS] + push eax + movzx eax, word [ecx + IA32_TSS._CS] + push eax + movzx eax, word [ecx + IA32_TSS._DS] + push eax + movzx eax, word [ecx + IA32_TSS._ES] + push eax + movzx eax, word [ecx + IA32_TSS._FS] + push eax + movzx eax, word [ecx + IA32_TSS._GS] + push eax + +;; UINT32 Eip; + push dword [ecx + IA32_TSS.EIP] + +;; UINT32 Gdtr[2], Idtr[2]; + sub esp, 8 + sidt [esp] + mov eax, [esp + 2] + xchg eax, [esp] + and eax, 0xFFFF + mov [esp+4], eax + + sub esp, 8 + sgdt [esp] + mov eax, [esp + 2] + xchg eax, [esp] + and eax, 0xFFFF + mov [esp+4], eax + +;; UINT32 Ldtr, Tr; + mov eax, ebx ; ebx still keeps selector of interrupted task + push eax + movzx eax, word [ecx + IA32_TSS.LDT] + push eax + +;; UINT32 EFlags; + push dword [ecx + IA32_TSS.EFLAGS] + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + mov eax, cr4 + push eax ; push cr4 firstly + + mov edx, [ebp - 4] ; cpuid.edx + test edx, BIT24 ; Test for FXSAVE/FXRESTOR support + jz .1 + or eax, BIT9 ; Set CR4.OSFXSR +.1: + test edx, BIT2 ; Test for Debugging Extensions support + jz .2 + or eax, BIT3 ; Set CR4.DE +.2: + mov cr4, eax + + mov eax, cr3 + push eax + mov eax, cr2 + push eax + xor eax, eax + push eax + mov eax, cr0 + push eax + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov eax, dr7 + push eax + mov eax, dr6 + push eax + mov eax, dr3 + push eax + mov eax, dr2 + push eax + mov eax, dr1 + push eax + mov eax, dr0 + push eax + +;; FX_SAVE_STATE_IA32 FxSaveState; +;; Clear TS bit in CR0 to avoid Device Not Available Exception (#NM) +;; when executing fxsave/fxrstor instruction + test edx, BIT24 ; Test for FXSAVE/FXRESTOR support. + ; edx still contains result from CPUID above + jz .3 + clts + sub esp, 512 + mov edi, esp + db 0xf, 0xae, 0x7 ;fxsave [edi] +.3: + +;; UINT32 ExceptionData; + push dword [ebp + 8] + +;; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear + cld + +;; call into exception handler + mov esi, ecx ; Keep TSS base to avoid overwrite + mov eax, ASM_PFX(CommonExceptionHandler) + +;; Prepare parameter and call + mov edx, esp + push edx ; EFI_SYSTEM_CONTEXT + push dword [ebp + 4] ; EFI_EXCEPTION_TYPE (vector number) + + ; + ; Call External Exception Handler + ; + call eax + add esp, 8 ; Restore stack before calling + mov ecx, esi ; Restore TSS base + +;; UINT32 ExceptionData; + add esp, 4 + +;; FX_SAVE_STATE_IA32 FxSaveState; + mov edx, [ebp - 4] ; cpuid.edx + test edx, BIT24 ; Test for FXSAVE/FXRESTOR support + jz .4 + mov esi, esp + db 0xf, 0xae, 0xe ; fxrstor [esi] +.4: + add esp, 512 + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; Skip restoration of DRx registers to support debuggers +;; that set breakpoints in interrupt/exception context + add esp, 4 * 6 + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + pop eax + mov cr0, eax + add esp, 4 ; not for Cr1 + pop eax + mov cr2, eax + pop eax + mov dword [ecx + IA32_TSS._CR3], eax + pop eax + mov cr4, eax + +;; UINT32 EFlags; + pop dword [ecx + IA32_TSS.EFLAGS] + mov ebx, dword [ecx + IA32_TSS.EFLAGS] + btr ebx, 9 ; Do 'cli' + mov dword [ecx + IA32_TSS.EFLAGS], ebx + +;; UINT32 Ldtr, Tr; +;; UINT32 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add esp, 24 + +;; UINT32 Eip; + pop dword [ecx + IA32_TSS.EIP] + +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; +;; NOTE - modified segment registers could hang the debugger... We +;; could attempt to insulate ourselves against this possibility, +;; but that poses risks as well. +;; + pop eax +o16 mov [ecx + IA32_TSS._GS], ax + pop eax +o16 mov [ecx + IA32_TSS._FS], ax + pop eax +o16 mov [ecx + IA32_TSS._ES], ax + pop eax +o16 mov [ecx + IA32_TSS._DS], ax + pop eax +o16 mov [ecx + IA32_TSS._CS], ax + pop eax +o16 mov [ecx + IA32_TSS._SS], ax + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + pop dword [ecx + IA32_TSS._EDI] + pop dword [ecx + IA32_TSS._ESI] + add esp, 4 ; not for ebp + add esp, 4 ; not for esp + pop dword [ecx + IA32_TSS._EBX] + pop dword [ecx + IA32_TSS._EDX] + pop dword [ecx + IA32_TSS._ECX] + pop dword [ecx + IA32_TSS._EAX] + +; Set single step DB# to allow debugger to able to go back to the EIP +; where the exception is triggered. + +;; Create return context for iretd in stub function + mov eax, dword [ecx + IA32_TSS._ESP] ; Get old stack pointer + mov ebx, dword [ecx + IA32_TSS.EIP] + mov [eax - 0xc], ebx ; create EIP in old stack + movzx ebx, word [ecx + IA32_TSS._CS] + mov [eax - 0x8], ebx ; create CS in old stack + mov ebx, dword [ecx + IA32_TSS.EFLAGS] + bts ebx, 8 ; Set TF + mov [eax - 0x4], ebx ; create eflags in old stack + sub eax, 0xc ; minus 12 byte + mov dword [ecx + IA32_TSS._ESP], eax ; Set new stack pointer + +;; Replace the EIP of interrupted task with stub function + mov eax, ASM_PFX(SingleStepStubFunction) + mov dword [ecx + IA32_TSS.EIP], eax + + mov ecx, [ebp - 8] ; Get current TSS base + mov eax, dword [ecx + IA32_TSS._ESP] ; Return current stack top + mov esp, ebp + + ret + +global ASM_PFX(SingleStepStubFunction) +ASM_PFX(SingleStepStubFunction): +; +; we need clean TS bit in CR0 to execute +; x87 FPU/MMX/SSE/SSE2/SSE3/SSSE3/SSE4 instructions. +; + clts + iretd + +global ASM_PFX(AsmGetTssTemplateMap) +ASM_PFX(AsmGetTssTemplateMap): + push ebp ; C prolog + mov ebp, esp + pushad + + mov ebx, dword [ebp + 0x8] + mov dword [ebx], ASM_PFX(ExceptionTaskSwtichEntry0) + mov dword [ebx + 0x4], (AsmExceptionEntryEnd - AsmExceptionEntryBegin) / 32 + mov dword [ebx + 0x8], 0 + + popad + pop ebp + ret + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuException.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuException.c new file mode 100644 index 00000000..2bce3924 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuException.c @@ -0,0 +1,262 @@ +/** @file + CPU exception handler library implementation for PEIM module. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiPei.h> +#include "CpuExceptionCommon.h" +#include <Library/DebugLib.h> +#include <Library/HobLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/PcdLib.h> + +CONST UINTN mDoFarReturnFlag = 0; + +typedef struct { + UINT8 ExceptionStubHeader[HOOKAFTER_STUB_SIZE]; + EXCEPTION_HANDLER_DATA *ExceptionHandlerData; +} EXCEPTION0_STUB_HEADER; + +/** + Get exception handler data pointer from IDT[0]. + + The exception #0 stub header is duplicated in an allocated pool with extra 4-byte/8-byte to store the + exception handler data. The new allocated memory layout follows structure EXCEPTION0_STUB_HEADER. + The code assumes that all processors uses the same exception handler for #0 exception. + + @return pointer to exception handler data. +**/ +EXCEPTION_HANDLER_DATA * +GetExceptionHandlerData ( + VOID + ) +{ + IA32_DESCRIPTOR IdtDescriptor; + IA32_IDT_GATE_DESCRIPTOR *IdtTable; + EXCEPTION0_STUB_HEADER *Exception0StubHeader; + + AsmReadIdtr (&IdtDescriptor); + IdtTable = (IA32_IDT_GATE_DESCRIPTOR *)IdtDescriptor.Base; + + Exception0StubHeader = (EXCEPTION0_STUB_HEADER *)ArchGetIdtHandler (&IdtTable[0]); + return Exception0StubHeader->ExceptionHandlerData; +} + +/** + Set exception handler data pointer to IDT[0]. + + The exception #0 stub header is duplicated in an allocated pool with extra 4-byte/8-byte to store the + exception handler data. The new allocated memory layout follows structure EXCEPTION0_STUB_HEADER. + The code assumes that all processors uses the same exception handler for #0 exception. + + @param ExceptionHandlerData pointer to exception handler data. +**/ +VOID +SetExceptionHandlerData ( + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + EXCEPTION0_STUB_HEADER *Exception0StubHeader; + IA32_DESCRIPTOR IdtDescriptor; + IA32_IDT_GATE_DESCRIPTOR *IdtTable; + // + // Duplicate the exception #0 stub header in pool and cache the ExceptionHandlerData just after the stub header. + // So AP can get the ExceptionHandlerData by reading the IDT[0]. + // + AsmReadIdtr (&IdtDescriptor); + IdtTable = (IA32_IDT_GATE_DESCRIPTOR *)IdtDescriptor.Base; + + Exception0StubHeader = AllocatePool (sizeof (*Exception0StubHeader)); + ASSERT (Exception0StubHeader != NULL); + CopyMem ( + Exception0StubHeader->ExceptionStubHeader, + (VOID *)ArchGetIdtHandler (&IdtTable[0]), + sizeof (Exception0StubHeader->ExceptionStubHeader) + ); + Exception0StubHeader->ExceptionHandlerData = ExceptionHandlerData; + ArchUpdateIdtEntry (&IdtTable[0], (UINTN)Exception0StubHeader->ExceptionStubHeader); +} + +/** + Common exception handler. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +CommonExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EXCEPTION_HANDLER_DATA *ExceptionHandlerData; + + ExceptionHandlerData = GetExceptionHandlerData (); + CommonExceptionHandlerWorker (ExceptionType, SystemContext, ExceptionHandlerData); +} + +/** + Initializes all CPU exceptions entries and provides the default exception handlers. + + Caller should try to get an array of interrupt and/or exception vectors that are in use and need to + persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification. + If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL. + If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly. + Note: Before invoking this API, caller must allocate memory for IDT table and load + IDTR by AsmWriteIdtr(). + + @param[in] VectorInfo Pointer to reserved vector list. + + @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized + with default exception handlers. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +EFIAPI +InitializeCpuExceptionHandlers ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL + ) +{ + EFI_STATUS Status; + EXCEPTION_HANDLER_DATA *ExceptionHandlerData; + RESERVED_VECTORS_DATA *ReservedVectors; + + ReservedVectors = AllocatePool (sizeof (RESERVED_VECTORS_DATA) * CPU_EXCEPTION_NUM); + ASSERT (ReservedVectors != NULL); + + ExceptionHandlerData = AllocatePool (sizeof (EXCEPTION_HANDLER_DATA)); + ASSERT (ExceptionHandlerData != NULL); + ExceptionHandlerData->ReservedVectors = ReservedVectors; + ExceptionHandlerData->ExternalInterruptHandler = NULL; + InitializeSpinLock (&ExceptionHandlerData->DisplayMessageSpinLock); + + Status = InitializeCpuExceptionHandlersWorker (VectorInfo, ExceptionHandlerData); + if (EFI_ERROR (Status)) { + FreePool (ReservedVectors); + FreePool (ExceptionHandlerData); + return Status; + } + + SetExceptionHandlerData (ExceptionHandlerData); + return EFI_SUCCESS; +} + +/** + Initializes all CPU interrupt/exceptions entries and provides the default interrupt/exception handlers. + + Caller should try to get an array of interrupt and/or exception vectors that are in use and need to + persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification. + If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL. + If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly. + + @param[in] VectorInfo Pointer to reserved vector list. + + @retval EFI_SUCCESS All CPU interrupt/exception entries have been successfully initialized + with default interrupt/exception handlers. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +EFIAPI +InitializeCpuInterruptHandlers ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Registers a function to be called from the processor interrupt handler. + + This function registers and enables the handler specified by InterruptHandler for a processor + interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the + handler for the processor interrupt or exception type specified by InterruptType is uninstalled. + The installed handler is called once for each processor interrupt or exception. + NOTE: This function should be invoked after InitializeCpuExceptionHandlers() or + InitializeCpuInterruptHandlers() invoked, otherwise EFI_UNSUPPORTED returned. + + @param[in] InterruptType Defines which interrupt or exception to hook. + @param[in] InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. If this parameter is NULL, then the handler + will be uninstalled. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was + previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not + previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported, + or this function is not supported. +**/ +EFI_STATUS +EFIAPI +RegisterCpuInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Initializes all CPU exceptions entries with optional extra initializations. + + By default, this method should include all functionalities implemented by + InitializeCpuExceptionHandlers(), plus extra initialization works, if any. + This could be done by calling InitializeCpuExceptionHandlers() directly + in this method besides the extra works. + + InitData is optional and its use and content are processor arch dependent. + The typical usage of it is to convey resources which have to be reserved + elsewhere and are necessary for the extra initializations of exception. + + @param[in] VectorInfo Pointer to reserved vector list. + @param[in] InitData Pointer to data optional for extra initializations + of exception. + + @retval EFI_SUCCESS The exceptions have been successfully + initialized. + @retval EFI_INVALID_PARAMETER VectorInfo or InitData contains invalid + content. + +**/ +EFI_STATUS +EFIAPI +InitializeCpuExceptionHandlersEx ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL, + IN CPU_EXCEPTION_INIT_DATA *InitData OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // To avoid repeat initialization of default handlers, the caller should pass + // an extended init data with InitDefaultHandlers set to FALSE. There's no + // need to call this method to just initialize default handlers. Call non-ex + // version instead; or this method must be implemented as a simple wrapper of + // non-ex version of it, if this version has to be called. + // + if (InitData == NULL || InitData->Ia32.InitDefaultHandlers) { + Status = InitializeCpuExceptionHandlers (VectorInfo); + } else { + Status = EFI_SUCCESS; + } + + if (!EFI_ERROR (Status)) { + // + // Initializing stack switch is only necessary for Stack Guard functionality. + // + if (PcdGetBool (PcdCpuStackGuard) && InitData != NULL) { + Status = ArchSetupExceptionStack (InitData); + } + } + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf new file mode 100644 index 00000000..53fbee20 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf @@ -0,0 +1,62 @@ +## @file +# CPU Exception Handler library instance for PEI module. +# +# Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PeiCpuExceptionHandlerLib + MODULE_UNI_FILE = PeiCpuExceptionHandlerLib.uni + FILE_GUID = 980DDA67-44A6-4897-99E6-275290B71F9E + MODULE_TYPE = PEIM + VERSION_STRING = 1.1 + LIBRARY_CLASS = CpuExceptionHandlerLib|PEI_CORE PEIM + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.Ia32] + Ia32/ExceptionHandlerAsm.nasm + Ia32/ExceptionTssEntryAsm.nasm + Ia32/ArchExceptionHandler.c + Ia32/ArchInterruptDefs.h + +[Sources.X64] + X64/Xcode5ExceptionHandlerAsm.nasm + X64/ArchExceptionHandler.c + X64/ArchInterruptDefs.h + +[Sources.common] + CpuExceptionCommon.h + CpuExceptionCommon.c + PeiCpuException.c + PeiDxeSmmCpuException.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + SerialPortLib + PrintLib + LocalApicLib + PeCoffGetEntryPointLib + HobLib + MemoryAllocationLib + SynchronizationLib + VmgExitLib + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard # CONSUMES + +[FeaturePcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStackGuard ## CONSUMES + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.uni new file mode 100644 index 00000000..300356c8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.uni @@ -0,0 +1,16 @@ +// /** @file
+// CPU Exception Handler library instance for PEI module.
+//
+// CPU Exception Handler library instance for PEI module.
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU Exception Handler library instance for PEI module."
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU Exception Handler library instance for PEI module."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c new file mode 100644 index 00000000..a0a0860e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c @@ -0,0 +1,314 @@ +/** @file + CPU Exception Library provides PEI/DXE/SMM CPU common exception handler. + +Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/DebugLib.h> +#include <Library/VmgExitLib.h> +#include "CpuExceptionCommon.h" + +/** + Internal worker function for common exception handler. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. + @param ExceptionHandlerData Pointer to exception handler data. +**/ +VOID +CommonExceptionHandlerWorker ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + EXCEPTION_HANDLER_CONTEXT *ExceptionHandlerContext; + RESERVED_VECTORS_DATA *ReservedVectors; + EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler; + + if (ExceptionType == VC_EXCEPTION) { + EFI_STATUS Status; + // + // #VC needs to be handled immediately upon enabling exception handling + // and therefore can't use the RegisterCpuInterruptHandler() interface. + // + // Handle the #VC: + // On EFI_SUCCESS - Exception has been handled, return + // On other - ExceptionType contains (possibly new) exception + // value + // + Status = VmgExitHandleVc (&ExceptionType, SystemContext); + if (!EFI_ERROR (Status)) { + return; + } + } + + ExceptionHandlerContext = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32); + ReservedVectors = ExceptionHandlerData->ReservedVectors; + ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler; + + switch (ReservedVectors[ExceptionType].Attribute) { + case EFI_VECTOR_HANDOFF_HOOK_BEFORE: + // + // The new exception handler registered by RegisterCpuInterruptHandler() is executed BEFORE original handler. + // Save the original handler to stack so the assembly code can jump to it instead of returning from handler. + // + ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE; + ExceptionHandlerContext->OldIdtHandler = ReservedVectors[ExceptionType].ExceptonHandler; + break; + case EFI_VECTOR_HANDOFF_HOOK_AFTER: + while (TRUE) { + // + // If spin-lock can be acquired, it's the first time entering here. + // + if (AcquireSpinLockOrFail (&ReservedVectors[ExceptionType].SpinLock)) { + // + // The new exception handler registered by RegisterCpuInterruptHandler() is executed AFTER original handler. + // Save the original handler to stack but skip running the new handler so the original handler is executed + // firstly. + // + ReservedVectors[ExceptionType].ApicId = GetApicId (); + ArchSaveExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData); + ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE; + ExceptionHandlerContext->OldIdtHandler = ReservedVectors[ExceptionType].ExceptonHandler; + return; + } + // + // If spin-lock cannot be acquired, it's the second time entering here. + // 'break' instead of 'return' is used so the new exception handler can be executed. + // + if (ReservedVectors[ExceptionType].ApicId == GetApicId ()) { + // + // Old IDT handler has been executed, then restore CPU exception content to + // run new exception handler. + // + ArchRestoreExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData); + // + // Release spin lock for ApicId + // + ReleaseSpinLock (&ReservedVectors[ExceptionType].SpinLock); + break; + } + CpuPause (); + } + break; + case 0xffffffff: + break; + default: + // + // It should never reach here + // + CpuDeadLoop (); + break; + } + + if (ExternalInterruptHandler != NULL && + ExternalInterruptHandler[ExceptionType] != NULL) { + (ExternalInterruptHandler[ExceptionType]) (ExceptionType, SystemContext); + } else if (ExceptionType < CPU_EXCEPTION_NUM) { + // + // Get Spinlock to display CPU information + // + while (!AcquireSpinLockOrFail (&ExceptionHandlerData->DisplayMessageSpinLock)) { + CpuPause (); + } + // + // Initialize the serial port before dumping. + // + SerialPortInitialize (); + // + // Display ExceptionType, CPU information and Image information + // + DumpImageAndCpuContent (ExceptionType, SystemContext); + // + // Release Spinlock of output message + // + ReleaseSpinLock (&ExceptionHandlerData->DisplayMessageSpinLock); + // + // Enter a dead loop if needn't to execute old IDT handler further + // + if (ReservedVectors[ExceptionType].Attribute != EFI_VECTOR_HANDOFF_HOOK_BEFORE) { + CpuDeadLoop (); + } + } +} + +/** + Internal worker function to update IDT entries accordling to vector attributes. + + @param[in] IdtTable Pointer to IDT table. + @param[in] TemplateMap Pointer to a buffer where the address map is + returned. + @param[in] ExceptionHandlerData Pointer to exception handler data. + +**/ +VOID +UpdateIdtTable ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtTable, + IN EXCEPTION_HANDLER_TEMPLATE_MAP *TemplateMap, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + UINT16 CodeSegment; + UINTN Index; + UINTN InterruptHandler; + RESERVED_VECTORS_DATA *ReservedVectors; + + ReservedVectors = ExceptionHandlerData->ReservedVectors; + // + // Use current CS as the segment selector of interrupt gate in IDT + // + CodeSegment = AsmReadCs (); + + for (Index = 0; Index < ExceptionHandlerData->IdtEntryCount; Index ++) { + IdtTable[Index].Bits.Selector = CodeSegment; + // + // Check reserved vectors attributes + // + switch (ReservedVectors[Index].Attribute) { + case EFI_VECTOR_HANDOFF_DO_NOT_HOOK: + // + // Keep original IDT entry + // + continue; + case EFI_VECTOR_HANDOFF_HOOK_AFTER: + InitializeSpinLock (&ReservedVectors[Index].SpinLock); + CopyMem ( + (VOID *) ReservedVectors[Index].HookAfterStubHeaderCode, + (VOID *) TemplateMap->HookAfterStubHeaderStart, + TemplateMap->ExceptionStubHeaderSize + ); + AsmVectorNumFixup ( + (VOID *) ReservedVectors[Index].HookAfterStubHeaderCode, + (UINT8) Index, + (VOID *) TemplateMap->HookAfterStubHeaderStart + ); + // + // Go on the following code + // + case EFI_VECTOR_HANDOFF_HOOK_BEFORE: + // + // Save original IDT handler address + // + ReservedVectors[Index].ExceptonHandler = ArchGetIdtHandler (&IdtTable[Index]); + // + // Go on the following code + // + default: + // + // Update new IDT entry + // + InterruptHandler = TemplateMap->ExceptionStart + Index * TemplateMap->ExceptionStubHeaderSize; + ArchUpdateIdtEntry (&IdtTable[Index], InterruptHandler); + break; + } + } +} + +/** + Internal worker function to initialize exception handler. + + @param[in] VectorInfo Pointer to reserved vector list. + @param[in, out] ExceptionHandlerData Pointer to exception handler data. + + @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized + with default exception handlers. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +InitializeCpuExceptionHandlersWorker ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL, + IN OUT EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + EFI_STATUS Status; + IA32_DESCRIPTOR IdtDescriptor; + UINTN IdtEntryCount; + EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap; + IA32_IDT_GATE_DESCRIPTOR *IdtTable; + RESERVED_VECTORS_DATA *ReservedVectors; + + ReservedVectors = ExceptionHandlerData->ReservedVectors; + SetMem ((VOID *) ReservedVectors, sizeof (RESERVED_VECTORS_DATA) * CPU_EXCEPTION_NUM, 0xff); + if (VectorInfo != NULL) { + Status = ReadAndVerifyVectorInfo (VectorInfo, ReservedVectors, CPU_EXCEPTION_NUM); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Read IDT descriptor and calculate IDT size + // + AsmReadIdtr (&IdtDescriptor); + IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR); + if (IdtEntryCount > CPU_EXCEPTION_NUM) { + // + // CPU exception library only setup CPU_EXCEPTION_NUM exception handler at most + // + IdtEntryCount = CPU_EXCEPTION_NUM; + } + + IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base; + AsmGetTemplateAddressMap (&TemplateMap); + ASSERT (TemplateMap.ExceptionStubHeaderSize <= HOOKAFTER_STUB_SIZE); + + ExceptionHandlerData->IdtEntryCount = IdtEntryCount; + UpdateIdtTable (IdtTable, &TemplateMap, ExceptionHandlerData); + + return EFI_SUCCESS; +} + +/** + Registers a function to be called from the processor interrupt handler. + + @param[in] InterruptType Defines which interrupt or exception to hook. + @param[in] InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. If this parameter is NULL, then the handler + will be uninstalled + @param[in] ExceptionHandlerData Pointer to exception handler data. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was + previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not + previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported, + or this function is not supported. +**/ +EFI_STATUS +RegisterCpuInterruptHandlerWorker ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + UINTN EnabledInterruptNum; + RESERVED_VECTORS_DATA *ReservedVectors; + EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler; + + EnabledInterruptNum = ExceptionHandlerData->IdtEntryCount; + ReservedVectors = ExceptionHandlerData->ReservedVectors; + ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler; + + if (InterruptType < 0 || InterruptType >= (EFI_EXCEPTION_TYPE)EnabledInterruptNum || + ReservedVectors[InterruptType].Attribute == EFI_VECTOR_HANDOFF_DO_NOT_HOOK) { + return EFI_UNSUPPORTED; + } + + if (InterruptHandler == NULL && ExternalInterruptHandler[InterruptType] == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (InterruptHandler != NULL && ExternalInterruptHandler[InterruptType] != NULL) { + return EFI_ALREADY_STARTED; + } + + ExternalInterruptHandler[InterruptType] = InterruptHandler; + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c new file mode 100644 index 00000000..7cc87955 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c @@ -0,0 +1,228 @@ +/** @file + CPU exception handler library implemenation for SEC/PEIM modules. + +Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiPei.h> +#include <Library/VmgExitLib.h> +#include "CpuExceptionCommon.h" + +CONST UINTN mDoFarReturnFlag = 0; + +/** + Common exception handler. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +CommonExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + if (ExceptionType == VC_EXCEPTION) { + EFI_STATUS Status; + // + // #VC needs to be handled immediately upon enabling exception handling + // and therefore can't use the RegisterCpuInterruptHandler() interface + // (which isn't supported under Sec and Pei anyway). + // + // Handle the #VC: + // On EFI_SUCCESS - Exception has been handled, return + // On other - ExceptionType contains (possibly new) exception + // value + // + Status = VmgExitHandleVc (&ExceptionType, SystemContext); + if (!EFI_ERROR (Status)) { + return; + } + } + + // + // Initialize the serial port before dumping. + // + SerialPortInitialize (); + // + // Display ExceptionType, CPU information and Image information + // + DumpImageAndCpuContent (ExceptionType, SystemContext); + + // + // Enter a dead loop. + // + CpuDeadLoop (); +} + +/** + Initializes all CPU exceptions entries and provides the default exception handlers. + + Caller should try to get an array of interrupt and/or exception vectors that are in use and need to + persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification. + If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL. + If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly. + Note: Before invoking this API, caller must allocate memory for IDT table and load + IDTR by AsmWriteIdtr(). + + @param[in] VectorInfo Pointer to reserved vector list. + + @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized + with default exception handlers. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +EFIAPI +InitializeCpuExceptionHandlers ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL + ) +{ + EFI_STATUS Status; + RESERVED_VECTORS_DATA ReservedVectorData[CPU_EXCEPTION_NUM]; + IA32_DESCRIPTOR IdtDescriptor; + UINTN IdtEntryCount; + UINT16 CodeSegment; + EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap; + IA32_IDT_GATE_DESCRIPTOR *IdtTable; + UINTN Index; + UINTN InterruptHandler; + + if (VectorInfo != NULL) { + SetMem ((VOID *) ReservedVectorData, sizeof (RESERVED_VECTORS_DATA) * CPU_EXCEPTION_NUM, 0xff); + Status = ReadAndVerifyVectorInfo (VectorInfo, ReservedVectorData, CPU_EXCEPTION_NUM); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + } + // + // Read IDT descriptor and calculate IDT size + // + AsmReadIdtr (&IdtDescriptor); + IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR); + if (IdtEntryCount > CPU_EXCEPTION_NUM) { + // + // CPU exception library only setup CPU_EXCEPTION_NUM exception handler at most + // + IdtEntryCount = CPU_EXCEPTION_NUM; + } + // + // Use current CS as the segment selector of interrupt gate in IDT + // + CodeSegment = AsmReadCs (); + + AsmGetTemplateAddressMap (&TemplateMap); + IdtTable = (IA32_IDT_GATE_DESCRIPTOR *)IdtDescriptor.Base; + for (Index = 0; Index < IdtEntryCount; Index ++) { + IdtTable[Index].Bits.Selector = CodeSegment; + // + // Check reserved vectors attributes if has, only EFI_VECTOR_HANDOFF_DO_NOT_HOOK + // supported in this instance + // + if (VectorInfo != NULL) { + if (ReservedVectorData[Index].Attribute == EFI_VECTOR_HANDOFF_DO_NOT_HOOK) { + continue; + } + } + // + // Update IDT entry + // + InterruptHandler = TemplateMap.ExceptionStart + Index * TemplateMap.ExceptionStubHeaderSize; + ArchUpdateIdtEntry (&IdtTable[Index], InterruptHandler); + } + return EFI_SUCCESS; +} + +/** + Initializes all CPU interrupt/exceptions entries and provides the default interrupt/exception handlers. + + Caller should try to get an array of interrupt and/or exception vectors that are in use and need to + persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification. + If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL. + If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly. + + @param[in] VectorInfo Pointer to reserved vector list. + + @retval EFI_SUCCESS All CPU interrupt/exception entries have been successfully initialized + with default interrupt/exception handlers. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +EFIAPI +InitializeCpuInterruptHandlers ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Registers a function to be called from the processor interrupt handler. + + This function registers and enables the handler specified by InterruptHandler for a processor + interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the + handler for the processor interrupt or exception type specified by InterruptType is uninstalled. + The installed handler is called once for each processor interrupt or exception. + NOTE: This function should be invoked after InitializeCpuExceptionHandlers() or + InitializeCpuInterruptHandlers() invoked, otherwise EFI_UNSUPPORTED returned. + + @param[in] InterruptType Defines which interrupt or exception to hook. + @param[in] InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. If this parameter is NULL, then the handler + will be uninstalled. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was + previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not + previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported, + or this function is not supported. +**/ +EFI_STATUS +EFIAPI +RegisterCpuInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Initializes all CPU exceptions entries with optional extra initializations. + + By default, this method should include all functionalities implemented by + InitializeCpuExceptionHandlers(), plus extra initialization works, if any. + This could be done by calling InitializeCpuExceptionHandlers() directly + in this method besides the extra works. + + InitData is optional and its use and content are processor arch dependent. + The typical usage of it is to convey resources which have to be reserved + elsewhere and are necessary for the extra initializations of exception. + + @param[in] VectorInfo Pointer to reserved vector list. + @param[in] InitData Pointer to data optional for extra initializations + of exception. + + @retval EFI_SUCCESS The exceptions have been successfully + initialized. + @retval EFI_INVALID_PARAMETER VectorInfo or InitData contains invalid + content. + +**/ +EFI_STATUS +EFIAPI +InitializeCpuExceptionHandlersEx ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL, + IN CPU_EXCEPTION_INIT_DATA *InitData OPTIONAL + ) +{ + return InitializeCpuExceptionHandlers (VectorInfo); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf new file mode 100644 index 00000000..4eaad47b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf @@ -0,0 +1,55 @@ +## @file +# CPU Exception Handler library instance for SEC/PEI modules. +# +# Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SecPeiCpuExceptionHandlerLib + MODULE_UNI_FILE = SecPeiCpuExceptionHandlerLib.uni + FILE_GUID = CA4BBC99-DFC6-4234-B553-8B6586B7B113 + MODULE_TYPE = PEIM + VERSION_STRING = 1.1 + LIBRARY_CLASS = CpuExceptionHandlerLib|SEC PEI_CORE PEIM + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.Ia32] + Ia32/ExceptionHandlerAsm.nasm + Ia32/ExceptionTssEntryAsm.nasm + Ia32/ArchExceptionHandler.c + Ia32/ArchInterruptDefs.h + +[Sources.X64] + X64/ExceptionHandlerAsm.nasm + X64/ArchExceptionHandler.c + X64/ArchInterruptDefs.h + +[Sources.common] + CpuExceptionCommon.h + CpuExceptionCommon.c + SecPeiCpuException.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + SerialPortLib + PrintLib + LocalApicLib + PeCoffGetEntryPointLib + VmgExitLib + +[FeaturePcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStackGuard ## CONSUMES + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.uni new file mode 100644 index 00000000..72300e51 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.uni @@ -0,0 +1,16 @@ +// /** @file
+// CPU Exception Handler library instance for SEC/PEI modules.
+//
+// CPU Exception Handler library instance for SEC/PEI modules.
+//
+// Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU Exception Handler library instance for SEC/PEI modules."
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU Exception Handler library instance for SEC/PEI modules."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf new file mode 100644 index 00000000..3cdab318 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf @@ -0,0 +1,58 @@ +## @file +# CPU Exception Handler library instance for SMM modules. +# +# Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SmmCpuExceptionHandlerLib + MODULE_UNI_FILE = SmmCpuExceptionHandlerLib.uni + FILE_GUID = 8D2C439B-3981-42ff-9CE5-1B50ECA502D6 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.1 + LIBRARY_CLASS = CpuExceptionHandlerLib|DXE_SMM_DRIVER MM_STANDALONE MM_CORE_STANDALONE + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.Ia32] + Ia32/ExceptionHandlerAsm.nasm + Ia32/ExceptionTssEntryAsm.nasm + Ia32/ArchExceptionHandler.c + Ia32/ArchInterruptDefs.h + +[Sources.X64] + X64/Xcode5ExceptionHandlerAsm.nasm + X64/ArchExceptionHandler.c + X64/ArchInterruptDefs.h + +[Sources.common] + CpuExceptionCommon.h + CpuExceptionCommon.c + PeiDxeSmmCpuException.c + SmmException.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + SerialPortLib + PrintLib + SynchronizationLib + LocalApicLib + PeCoffGetEntryPointLib + DebugLib + VmgExitLib + +[FeaturePcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStackGuard ## CONSUMES + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.uni new file mode 100644 index 00000000..2ec2f793 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.uni @@ -0,0 +1,16 @@ +// /** @file
+// CPU Exception Handler library instance for SMM modules.
+//
+// CPU Exception Handler library instance for SMM modules.
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU Exception Handler library instance for SMM modules."
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU Exception Handler library instance for SMM modules."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmException.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmException.c new file mode 100644 index 00000000..8a4c540d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmException.c @@ -0,0 +1,154 @@ +/** @file + CPU exception handler library implementation for SMM modules. + + Copyright (c) 2013 - 2017, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiSmm.h> +#include "CpuExceptionCommon.h" + +CONST UINTN mDoFarReturnFlag = 1; + +// +// Spin lock for CPU information display +// +SPIN_LOCK mDisplayMessageSpinLock; + +RESERVED_VECTORS_DATA mReservedVectorsData[CPU_EXCEPTION_NUM]; +EFI_CPU_INTERRUPT_HANDLER mExternalInterruptHandlerTable[CPU_EXCEPTION_NUM]; +EXCEPTION_HANDLER_DATA mExceptionHandlerData; +/** + Common exception handler. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +CommonExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + CommonExceptionHandlerWorker (ExceptionType, SystemContext, &mExceptionHandlerData); +} + +/** + Initializes all CPU exceptions entries and provides the default exception handlers. + + Caller should try to get an array of interrupt and/or exception vectors that are in use and need to + persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification. + If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL. + If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly. + + @param[in] VectorInfo Pointer to reserved vector list. + + @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized + with default exception handlers. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +EFIAPI +InitializeCpuExceptionHandlers ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL + ) +{ + mExceptionHandlerData.ReservedVectors = mReservedVectorsData; + mExceptionHandlerData.ExternalInterruptHandler = mExternalInterruptHandlerTable; + InitializeSpinLock (&mExceptionHandlerData.DisplayMessageSpinLock); + return InitializeCpuExceptionHandlersWorker (VectorInfo, &mExceptionHandlerData); +} + +/** + Initializes all CPU interrupt/exceptions entries and provides the default interrupt/exception handlers. + + Caller should try to get an array of interrupt and/or exception vectors that are in use and need to + persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification. + If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL. + If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly. + + @param[in] VectorInfo Pointer to reserved vector list. + + @retval EFI_SUCCESS All CPU interrupt/exception entries have been successfully initialized + with default interrupt/exception handlers. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +EFIAPI +InitializeCpuInterruptHandlers ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Registers a function to be called from the processor interrupt handler. + + This function registers and enables the handler specified by InterruptHandler for a processor + interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the + handler for the processor interrupt or exception type specified by InterruptType is uninstalled. + The installed handler is called once for each processor interrupt or exception. + NOTE: This function should be invoked after InitializeCpuExceptionHandlers() or + InitializeCpuInterruptHandlers() invoked, otherwise EFI_UNSUPPORTED returned. + + @param[in] InterruptType Defines which interrupt or exception to hook. + @param[in] InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. If this parameter is NULL, then the handler + will be uninstalled. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was + previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not + previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported, + or this function is not supported. +**/ +EFI_STATUS +EFIAPI +RegisterCpuInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ) +{ + return RegisterCpuInterruptHandlerWorker (InterruptType, InterruptHandler, &mExceptionHandlerData); +} + +/** + Initializes all CPU exceptions entries with optional extra initializations. + + By default, this method should include all functionalities implemented by + InitializeCpuExceptionHandlers(), plus extra initialization works, if any. + This could be done by calling InitializeCpuExceptionHandlers() directly + in this method besides the extra works. + + InitData is optional and its use and content are processor arch dependent. + The typical usage of it is to convey resources which have to be reserved + elsewhere and are necessary for the extra initializations of exception. + + @param[in] VectorInfo Pointer to reserved vector list. + @param[in] InitData Pointer to data optional for extra initializations + of exception. + + @retval EFI_SUCCESS The exceptions have been successfully + initialized. + @retval EFI_INVALID_PARAMETER VectorInfo or InitData contains invalid + content. + +**/ +EFI_STATUS +EFIAPI +InitializeCpuExceptionHandlersEx ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL, + IN CPU_EXCEPTION_INIT_DATA *InitData OPTIONAL + ) +{ + return InitializeCpuExceptionHandlers (VectorInfo); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c new file mode 100644 index 00000000..a0257aa8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c @@ -0,0 +1,427 @@ +/** @file + x64 CPU Exception Handler. + + Copyright (c) 2012 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuExceptionCommon.h" + +/** + Return address map of exception handler template so that C code can generate + exception tables. + + @param IdtEntry Pointer to IDT entry to be updated. + @param InterruptHandler IDT handler value. +**/ +VOID +ArchUpdateIdtEntry ( + OUT IA32_IDT_GATE_DESCRIPTOR *IdtEntry, + IN UINTN InterruptHandler + ) +{ + IdtEntry->Bits.OffsetLow = (UINT16)(UINTN)InterruptHandler; + IdtEntry->Bits.OffsetHigh = (UINT16)((UINTN)InterruptHandler >> 16); + IdtEntry->Bits.OffsetUpper = (UINT32)((UINTN)InterruptHandler >> 32); + IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; +} + +/** + Read IDT handler value from IDT entry. + + @param IdtEntry Pointer to IDT entry to be read. + +**/ +UINTN +ArchGetIdtHandler ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtEntry + ) +{ + return IdtEntry->Bits.OffsetLow + (((UINTN) IdtEntry->Bits.OffsetHigh) << 16) + + (((UINTN) IdtEntry->Bits.OffsetUpper) << 32); +} + +/** + Save CPU exception context when handling EFI_VECTOR_HANDOFF_HOOK_AFTER case. + + @param[in] ExceptionType Exception type. + @param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT. + @param[in] ExceptionHandlerData Pointer to exception handler data. +**/ +VOID +ArchSaveExceptionContext ( + IN UINTN ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + IA32_EFLAGS32 Eflags; + RESERVED_VECTORS_DATA *ReservedVectors; + + ReservedVectors = ExceptionHandlerData->ReservedVectors; + // + // Save Exception context in global variable in first entry of the exception handler. + // So when original exception handler returns to the new exception handler (second entry), + // the Eflags/Cs/Eip/ExceptionData can be used. + // + ReservedVectors[ExceptionType].OldSs = SystemContext.SystemContextX64->Ss; + ReservedVectors[ExceptionType].OldSp = SystemContext.SystemContextX64->Rsp; + ReservedVectors[ExceptionType].OldFlags = SystemContext.SystemContextX64->Rflags; + ReservedVectors[ExceptionType].OldCs = SystemContext.SystemContextX64->Cs; + ReservedVectors[ExceptionType].OldIp = SystemContext.SystemContextX64->Rip; + ReservedVectors[ExceptionType].ExceptionData = SystemContext.SystemContextX64->ExceptionData; + // + // Clear IF flag to avoid old IDT handler enable interrupt by IRET + // + Eflags.UintN = SystemContext.SystemContextX64->Rflags; + Eflags.Bits.IF = 0; + SystemContext.SystemContextX64->Rflags = Eflags.UintN; + // + // Modify the EIP in stack, then old IDT handler will return to HookAfterStubBegin. + // + SystemContext.SystemContextX64->Rip = (UINTN) ReservedVectors[ExceptionType].HookAfterStubHeaderCode; +} + +/** + Restore CPU exception context when handling EFI_VECTOR_HANDOFF_HOOK_AFTER case. + + @param[in] ExceptionType Exception type. + @param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT. + @param[in] ExceptionHandlerData Pointer to exception handler data. +**/ +VOID +ArchRestoreExceptionContext ( + IN UINTN ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + RESERVED_VECTORS_DATA *ReservedVectors; + + ReservedVectors = ExceptionHandlerData->ReservedVectors; + SystemContext.SystemContextX64->Ss = ReservedVectors[ExceptionType].OldSs; + SystemContext.SystemContextX64->Rsp = ReservedVectors[ExceptionType].OldSp; + SystemContext.SystemContextX64->Rflags = ReservedVectors[ExceptionType].OldFlags; + SystemContext.SystemContextX64->Cs = ReservedVectors[ExceptionType].OldCs; + SystemContext.SystemContextX64->Rip = ReservedVectors[ExceptionType].OldIp; + SystemContext.SystemContextX64->ExceptionData = ReservedVectors[ExceptionType].ExceptionData; +} + +/** + Setup separate stack for given exceptions. + + @param[in] StackSwitchData Pointer to data required for setuping up + stack switch. + + @retval EFI_SUCCESS The exceptions have been successfully + initialized with new stack. + @retval EFI_INVALID_PARAMETER StackSwitchData contains invalid content. + +**/ +EFI_STATUS +ArchSetupExceptionStack ( + IN CPU_EXCEPTION_INIT_DATA *StackSwitchData + ) +{ + IA32_DESCRIPTOR Gdtr; + IA32_DESCRIPTOR Idtr; + IA32_IDT_GATE_DESCRIPTOR *IdtTable; + IA32_TSS_DESCRIPTOR *TssDesc; + IA32_TASK_STATE_SEGMENT *Tss; + UINTN StackTop; + UINTN Index; + UINTN Vector; + UINTN TssBase; + UINTN GdtSize; + + if (StackSwitchData == NULL || + StackSwitchData->Ia32.Revision != CPU_EXCEPTION_INIT_DATA_REV || + StackSwitchData->X64.KnownGoodStackTop == 0 || + StackSwitchData->X64.KnownGoodStackSize == 0 || + StackSwitchData->X64.StackSwitchExceptions == NULL || + StackSwitchData->X64.StackSwitchExceptionNumber == 0 || + StackSwitchData->X64.StackSwitchExceptionNumber > CPU_EXCEPTION_NUM || + StackSwitchData->X64.GdtTable == NULL || + StackSwitchData->X64.IdtTable == NULL || + StackSwitchData->X64.ExceptionTssDesc == NULL || + StackSwitchData->X64.ExceptionTss == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // The caller is responsible for that the GDT table, no matter the existing + // one or newly allocated, has enough space to hold descriptors for exception + // task-state segments. + // + if (((UINTN)StackSwitchData->X64.GdtTable & (IA32_GDT_ALIGNMENT - 1)) != 0) { + return EFI_INVALID_PARAMETER; + } + + if ((UINTN)StackSwitchData->X64.ExceptionTssDesc < (UINTN)(StackSwitchData->X64.GdtTable)) { + return EFI_INVALID_PARAMETER; + } + + if (((UINTN)StackSwitchData->X64.ExceptionTssDesc + StackSwitchData->X64.ExceptionTssDescSize) > + ((UINTN)(StackSwitchData->X64.GdtTable) + StackSwitchData->X64.GdtTableSize)) { + return EFI_INVALID_PARAMETER; + } + + // + // One task gate descriptor and one task-state segment are needed. + // + if (StackSwitchData->X64.ExceptionTssDescSize < sizeof (IA32_TSS_DESCRIPTOR)) { + return EFI_INVALID_PARAMETER; + } + if (StackSwitchData->X64.ExceptionTssSize < sizeof (IA32_TASK_STATE_SEGMENT)) { + return EFI_INVALID_PARAMETER; + } + + // + // Interrupt stack table supports only 7 vectors. + // + TssDesc = StackSwitchData->X64.ExceptionTssDesc; + Tss = StackSwitchData->X64.ExceptionTss; + if (StackSwitchData->X64.StackSwitchExceptionNumber > ARRAY_SIZE (Tss->IST)) { + return EFI_INVALID_PARAMETER; + } + + // + // Initialize new GDT table and/or IDT table, if any + // + AsmReadIdtr (&Idtr); + AsmReadGdtr (&Gdtr); + + GdtSize = (UINTN)TssDesc + sizeof (IA32_TSS_DESCRIPTOR) - + (UINTN)(StackSwitchData->X64.GdtTable); + if ((UINTN)StackSwitchData->X64.GdtTable != Gdtr.Base) { + CopyMem (StackSwitchData->X64.GdtTable, (VOID *)Gdtr.Base, Gdtr.Limit + 1); + Gdtr.Base = (UINTN)StackSwitchData->X64.GdtTable; + Gdtr.Limit = (UINT16)GdtSize - 1; + } + + if ((UINTN)StackSwitchData->X64.IdtTable != Idtr.Base) { + Idtr.Base = (UINTN)StackSwitchData->X64.IdtTable; + } + if (StackSwitchData->X64.IdtTableSize > 0) { + Idtr.Limit = (UINT16)(StackSwitchData->X64.IdtTableSize - 1); + } + + // + // Fixup current task descriptor. Task-state segment for current task will + // be filled by processor during task switching. + // + TssBase = (UINTN)Tss; + + TssDesc->Uint128.Uint64 = 0; + TssDesc->Uint128.Uint64_1= 0; + TssDesc->Bits.LimitLow = sizeof(IA32_TASK_STATE_SEGMENT) - 1; + TssDesc->Bits.BaseLow = (UINT16)TssBase; + TssDesc->Bits.BaseMidl = (UINT8)(TssBase >> 16); + TssDesc->Bits.Type = IA32_GDT_TYPE_TSS; + TssDesc->Bits.P = 1; + TssDesc->Bits.LimitHigh = 0; + TssDesc->Bits.BaseMidh = (UINT8)(TssBase >> 24); + TssDesc->Bits.BaseHigh = (UINT32)(TssBase >> 32); + + // + // Fixup exception task descriptor and task-state segment + // + ZeroMem (Tss, sizeof (*Tss)); + StackTop = StackSwitchData->X64.KnownGoodStackTop - CPU_STACK_ALIGNMENT; + StackTop = (UINTN)ALIGN_POINTER (StackTop, CPU_STACK_ALIGNMENT); + IdtTable = StackSwitchData->X64.IdtTable; + for (Index = 0; Index < StackSwitchData->X64.StackSwitchExceptionNumber; ++Index) { + // + // Fixup IST + // + Tss->IST[Index] = StackTop; + StackTop -= StackSwitchData->X64.KnownGoodStackSize; + + // + // Set the IST field to enable corresponding IST + // + Vector = StackSwitchData->X64.StackSwitchExceptions[Index]; + if (Vector >= CPU_EXCEPTION_NUM || + Vector >= (Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR)) { + continue; + } + IdtTable[Vector].Bits.Reserved_0 = (UINT8)(Index + 1); + } + + // + // Publish GDT + // + AsmWriteGdtr (&Gdtr); + + // + // Load current task + // + AsmWriteTr ((UINT16)((UINTN)StackSwitchData->X64.ExceptionTssDesc - Gdtr.Base)); + + // + // Publish IDT + // + AsmWriteIdtr (&Idtr); + + return EFI_SUCCESS; +} + +/** + Display CPU information. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +DumpCpuContext ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + InternalPrintMessage ( + "!!!! X64 Exception Type - %02x(%a) CPU Apic ID - %08x !!!!\n", + ExceptionType, + GetExceptionNameStr (ExceptionType), + GetApicId () + ); + if ((mErrorCodeFlag & (1 << ExceptionType)) != 0) { + InternalPrintMessage ( + "ExceptionData - %016lx", + SystemContext.SystemContextX64->ExceptionData + ); + if (ExceptionType == EXCEPT_IA32_PAGE_FAULT) { + InternalPrintMessage ( + " I:%x R:%x U:%x W:%x P:%x PK:%x SS:%x SGX:%x", + (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0, + (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_RSVD) != 0, + (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_US) != 0, + (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_WR) != 0, + (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_P) != 0, + (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_PK) != 0, + (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_SS) != 0, + (SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_SGX) != 0 + ); + } + InternalPrintMessage ("\n"); + } + InternalPrintMessage ( + "RIP - %016lx, CS - %016lx, RFLAGS - %016lx\n", + SystemContext.SystemContextX64->Rip, + SystemContext.SystemContextX64->Cs, + SystemContext.SystemContextX64->Rflags + ); + InternalPrintMessage ( + "RAX - %016lx, RCX - %016lx, RDX - %016lx\n", + SystemContext.SystemContextX64->Rax, + SystemContext.SystemContextX64->Rcx, + SystemContext.SystemContextX64->Rdx + ); + InternalPrintMessage ( + "RBX - %016lx, RSP - %016lx, RBP - %016lx\n", + SystemContext.SystemContextX64->Rbx, + SystemContext.SystemContextX64->Rsp, + SystemContext.SystemContextX64->Rbp + ); + InternalPrintMessage ( + "RSI - %016lx, RDI - %016lx\n", + SystemContext.SystemContextX64->Rsi, + SystemContext.SystemContextX64->Rdi + ); + InternalPrintMessage ( + "R8 - %016lx, R9 - %016lx, R10 - %016lx\n", + SystemContext.SystemContextX64->R8, + SystemContext.SystemContextX64->R9, + SystemContext.SystemContextX64->R10 + ); + InternalPrintMessage ( + "R11 - %016lx, R12 - %016lx, R13 - %016lx\n", + SystemContext.SystemContextX64->R11, + SystemContext.SystemContextX64->R12, + SystemContext.SystemContextX64->R13 + ); + InternalPrintMessage ( + "R14 - %016lx, R15 - %016lx\n", + SystemContext.SystemContextX64->R14, + SystemContext.SystemContextX64->R15 + ); + InternalPrintMessage ( + "DS - %016lx, ES - %016lx, FS - %016lx\n", + SystemContext.SystemContextX64->Ds, + SystemContext.SystemContextX64->Es, + SystemContext.SystemContextX64->Fs + ); + InternalPrintMessage ( + "GS - %016lx, SS - %016lx\n", + SystemContext.SystemContextX64->Gs, + SystemContext.SystemContextX64->Ss + ); + InternalPrintMessage ( + "CR0 - %016lx, CR2 - %016lx, CR3 - %016lx\n", + SystemContext.SystemContextX64->Cr0, + SystemContext.SystemContextX64->Cr2, + SystemContext.SystemContextX64->Cr3 + ); + InternalPrintMessage ( + "CR4 - %016lx, CR8 - %016lx\n", + SystemContext.SystemContextX64->Cr4, + SystemContext.SystemContextX64->Cr8 + ); + InternalPrintMessage ( + "DR0 - %016lx, DR1 - %016lx, DR2 - %016lx\n", + SystemContext.SystemContextX64->Dr0, + SystemContext.SystemContextX64->Dr1, + SystemContext.SystemContextX64->Dr2 + ); + InternalPrintMessage ( + "DR3 - %016lx, DR6 - %016lx, DR7 - %016lx\n", + SystemContext.SystemContextX64->Dr3, + SystemContext.SystemContextX64->Dr6, + SystemContext.SystemContextX64->Dr7 + ); + InternalPrintMessage ( + "GDTR - %016lx %016lx, LDTR - %016lx\n", + SystemContext.SystemContextX64->Gdtr[0], + SystemContext.SystemContextX64->Gdtr[1], + SystemContext.SystemContextX64->Ldtr + ); + InternalPrintMessage ( + "IDTR - %016lx %016lx, TR - %016lx\n", + SystemContext.SystemContextX64->Idtr[0], + SystemContext.SystemContextX64->Idtr[1], + SystemContext.SystemContextX64->Tr + ); + InternalPrintMessage ( + "FXSAVE_STATE - %016lx\n", + &SystemContext.SystemContextX64->FxSaveState + ); +} + +/** + Display CPU information. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +DumpImageAndCpuContent ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + DumpCpuContext (ExceptionType, SystemContext); + // + // Dump module image base and module entry point by RIP + // + if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) && + ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0)) { + // + // The RIP in SystemContext could not be used + // if it is page fault with I/D set. + // + DumpModuleImageInfo ((*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp)); + } else { + DumpModuleImageInfo (SystemContext.SystemContextX64->Rip); + } +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchInterruptDefs.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchInterruptDefs.h new file mode 100644 index 00000000..97271c04 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchInterruptDefs.h @@ -0,0 +1,43 @@ +/** @file + X64 arch definition for CPU Exception Handler Library. + + Copyright (c) 2013, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _ARCH_CPU_INTERRUPT_DEFS_H_ +#define _ARCH_CPU_INTERRUPT_DEFS_H_ + +typedef struct { + EFI_SYSTEM_CONTEXT_X64 SystemContext; + BOOLEAN ExceptionDataFlag; + UINTN OldIdtHandler; +} EXCEPTION_HANDLER_CONTEXT; + +// +// Register Structure Definitions +// +typedef struct { + EFI_STATUS_CODE_DATA Header; + EFI_SYSTEM_CONTEXT_X64 SystemContext; +} CPU_STATUS_CODE_TEMPLATE; + +typedef struct { + SPIN_LOCK SpinLock; + UINT32 ApicId; + UINT32 Attribute; + UINTN ExceptonHandler; + UINTN OldSs; + UINTN OldSp; + UINTN OldFlags; + UINTN OldCs; + UINTN OldIp; + UINTN ExceptionData; + UINT8 HookAfterStubHeaderCode[HOOKAFTER_STUB_SIZE]; +} RESERVED_VECTORS_DATA; + +#define CPU_TSS_DESC_SIZE sizeof (IA32_TSS_DESCRIPTOR) +#define CPU_TSS_SIZE sizeof (IA32_TASK_STATE_SEGMENT) + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm new file mode 100644 index 00000000..1f112909 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm @@ -0,0 +1,400 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; ExceptionHandlerAsm.Asm +; +; Abstract: +; +; x64 CPU Exception Handler +; +; Notes: +; +;------------------------------------------------------------------------------ + +; +; CommonExceptionHandler() +; + +%define VC_EXCEPTION 29 + +extern ASM_PFX(mErrorCodeFlag) ; Error code flags for exceptions +extern ASM_PFX(mDoFarReturnFlag) ; Do far return flag +extern ASM_PFX(CommonExceptionHandler) + +SECTION .data + +DEFAULT REL +SECTION .text + +ALIGN 8 + +AsmIdtVectorBegin: +%rep 32 + db 0x6a ; push #VectorNum + db ($ - AsmIdtVectorBegin) / ((AsmIdtVectorEnd - AsmIdtVectorBegin) / 32) ; VectorNum + push rax + mov rax, ASM_PFX(CommonInterruptEntry) + jmp rax +%endrep +AsmIdtVectorEnd: + +HookAfterStubHeaderBegin: + db 0x6a ; push +@VectorNum: + db 0 ; 0 will be fixed + push rax + mov rax, HookAfterStubHeaderEnd + jmp rax +HookAfterStubHeaderEnd: + mov rax, rsp + and sp, 0xfff0 ; make sure 16-byte aligned for exception context + sub rsp, 0x18 ; reserve room for filling exception data later + push rcx + mov rcx, [rax + 8] + bt [ASM_PFX(mErrorCodeFlag)], ecx + jnc .0 + push qword [rsp] ; push additional rcx to make stack alignment +.0: + xchg rcx, [rsp] ; restore rcx, save Exception Number in stack + push qword [rax] ; push rax into stack to keep code consistence + +;---------------------------------------; +; CommonInterruptEntry ; +;---------------------------------------; +; The follow algorithm is used for the common interrupt routine. +; Entry from each interrupt with a push eax and eax=interrupt number +; Stack frame would be as follows as specified in IA32 manuals: +; +; +---------------------+ <-- 16-byte aligned ensured by processor +; + Old SS + +; +---------------------+ +; + Old RSP + +; +---------------------+ +; + RFlags + +; +---------------------+ +; + CS + +; +---------------------+ +; + RIP + +; +---------------------+ +; + Error Code + +; +---------------------+ +; + Vector Number + +; +---------------------+ +; + RBP + +; +---------------------+ <-- RBP, 16-byte aligned +; The follow algorithm is used for the common interrupt routine. +global ASM_PFX(CommonInterruptEntry) +ASM_PFX(CommonInterruptEntry): + cli + pop rax + ; + ; All interrupt handlers are invoked through interrupt gates, so + ; IF flag automatically cleared at the entry point + ; + xchg rcx, [rsp] ; Save rcx into stack and save vector number into rcx + and rcx, 0xFF + cmp ecx, 32 ; Intel reserved vector for exceptions? + jae NoErrorCode + bt [ASM_PFX(mErrorCodeFlag)], ecx + jc HasErrorCode + +NoErrorCode: + + ; + ; Push a dummy error code on the stack + ; to maintain coherent stack map + ; + push qword [rsp] + mov qword [rsp + 8], 0 +HasErrorCode: + push rbp + mov rbp, rsp + push 0 ; clear EXCEPTION_HANDLER_CONTEXT.OldIdtHandler + push 0 ; clear EXCEPTION_HANDLER_CONTEXT.ExceptionDataFlag + + ; + ; Stack: + ; +---------------------+ <-- 16-byte aligned ensured by processor + ; + Old SS + + ; +---------------------+ + ; + Old RSP + + ; +---------------------+ + ; + RFlags + + ; +---------------------+ + ; + CS + + ; +---------------------+ + ; + RIP + + ; +---------------------+ + ; + Error Code + + ; +---------------------+ + ; + RCX / Vector Number + + ; +---------------------+ + ; + RBP + + ; +---------------------+ <-- RBP, 16-byte aligned + ; + + ; + ; Since here the stack pointer is 16-byte aligned, so + ; EFI_FX_SAVE_STATE_X64 of EFI_SYSTEM_CONTEXT_x64 + ; is 16-byte aligned + ; + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rax + push qword [rbp + 8] ; RCX + push rdx + push rbx + push qword [rbp + 48] ; RSP + push qword [rbp] ; RBP + push rsi + push rdi + +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero + movzx rax, word [rbp + 56] + push rax ; for ss + movzx rax, word [rbp + 32] + push rax ; for cs + mov rax, ds + push rax + mov rax, es + push rax + mov rax, fs + push rax + mov rax, gs + push rax + + mov [rbp + 8], rcx ; save vector number + +;; UINT64 Rip; + push qword [rbp + 24] + +;; UINT64 Gdtr[2], Idtr[2]; + xor rax, rax + push rax + push rax + sidt [rsp] + mov bx, word [rsp] + mov rax, qword [rsp + 2] + mov qword [rsp], rax + mov word [rsp + 8], bx + + xor rax, rax + push rax + push rax + sgdt [rsp] + mov bx, word [rsp] + mov rax, qword [rsp + 2] + mov qword [rsp], rax + mov word [rsp + 8], bx + +;; UINT64 Ldtr, Tr; + xor rax, rax + str ax + push rax + sldt ax + push rax + +;; UINT64 RFlags; + push qword [rbp + 40] + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; + mov rax, cr8 + push rax + mov rax, cr4 + or rax, 0x208 + mov cr4, rax + push rax + mov rax, cr3 + push rax + mov rax, cr2 + push rax + xor rax, rax + push rax + mov rax, cr0 + push rax + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + cmp qword [rbp + 8], VC_EXCEPTION + je VcDebugRegs ; For SEV-ES (#VC) Debug registers ignored + + mov rax, dr7 + push rax + mov rax, dr6 + push rax + mov rax, dr3 + push rax + mov rax, dr2 + push rax + mov rax, dr1 + push rax + mov rax, dr0 + push rax + jmp DrFinish + +VcDebugRegs: +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7 are skipped for #VC to avoid exception recursion + xor rax, rax + push rax + push rax + push rax + push rax + push rax + push rax + +DrFinish: +;; FX_SAVE_STATE_X64 FxSaveState; + sub rsp, 512 + mov rdi, rsp + db 0xf, 0xae, 0x7 ;fxsave [rdi] + +;; UEFI calling convention for x64 requires that Direction flag in EFLAGs is clear + cld + +;; UINT32 ExceptionData; + push qword [rbp + 16] + +;; Prepare parameter and call + mov rcx, [rbp + 8] + mov rdx, rsp + ; + ; Per X64 calling convention, allocate maximum parameter stack space + ; and make sure RSP is 16-byte aligned + ; + sub rsp, 4 * 8 + 8 + mov rax, ASM_PFX(CommonExceptionHandler) + call rax + add rsp, 4 * 8 + 8 + + cli +;; UINT64 ExceptionData; + add rsp, 8 + +;; FX_SAVE_STATE_X64 FxSaveState; + + mov rsi, rsp + db 0xf, 0xae, 0xE ; fxrstor [rsi] + add rsp, 512 + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; Skip restoration of DRx registers to support in-circuit emualators +;; or debuggers set breakpoint in interrupt/exception context + add rsp, 8 * 6 + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; + pop rax + mov cr0, rax + add rsp, 8 ; not for Cr1 + pop rax + mov cr2, rax + pop rax + mov cr3, rax + pop rax + mov cr4, rax + pop rax + mov cr8, rax + +;; UINT64 RFlags; + pop qword [rbp + 40] + +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add rsp, 48 + +;; UINT64 Rip; + pop qword [rbp + 24] + +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; + pop rax + ; mov gs, rax ; not for gs + pop rax + ; mov fs, rax ; not for fs + ; (X64 will not use fs and gs, so we do not restore it) + pop rax + mov es, rax + pop rax + mov ds, rax + pop qword [rbp + 32] ; for cs + pop qword [rbp + 56] ; for ss + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pop rdi + pop rsi + add rsp, 8 ; not for rbp + pop qword [rbp + 48] ; for rsp + pop rbx + pop rdx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + + mov rsp, rbp + pop rbp + add rsp, 16 + cmp qword [rsp - 32], 0 ; check EXCEPTION_HANDLER_CONTEXT.OldIdtHandler + jz DoReturn + cmp qword [rsp - 40], 1 ; check EXCEPTION_HANDLER_CONTEXT.ExceptionDataFlag + jz ErrorCode + jmp qword [rsp - 32] +ErrorCode: + sub rsp, 8 + jmp qword [rsp - 24] + +DoReturn: + cmp qword [ASM_PFX(mDoFarReturnFlag)], 0 ; Check if need to do far return instead of IRET + jz DoIret + push rax + mov rax, rsp ; save old RSP to rax + mov rsp, [rsp + 0x20] + push qword [rax + 0x10] ; save CS in new location + push qword [rax + 0x8] ; save EIP in new location + push qword [rax + 0x18] ; save EFLAGS in new location + mov rax, [rax] ; restore rax + popfq ; restore EFLAGS + DB 0x48 ; prefix to composite "retq" with next "retf" + retf ; far return +DoIret: + iretq + +;------------------------------------------------------------------------------------- +; GetTemplateAddressMap (&AddressMap); +;------------------------------------------------------------------------------------- +; comments here for definition of address map +global ASM_PFX(AsmGetTemplateAddressMap) +ASM_PFX(AsmGetTemplateAddressMap): + mov rax, AsmIdtVectorBegin + mov qword [rcx], rax + mov qword [rcx + 0x8], (AsmIdtVectorEnd - AsmIdtVectorBegin) / 32 + mov rax, HookAfterStubHeaderBegin + mov qword [rcx + 0x10], rax + ret + +;------------------------------------------------------------------------------------- +; AsmVectorNumFixup (*NewVectorAddr, VectorNum, *OldVectorAddr); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmVectorNumFixup) +ASM_PFX(AsmVectorNumFixup): + mov rax, rdx + mov [rcx + (@VectorNum - HookAfterStubHeaderBegin)], al + ret + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm new file mode 100644 index 00000000..a8edfc01 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/Xcode5ExceptionHandlerAsm.nasm @@ -0,0 +1,455 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; ExceptionHandlerAsm.Asm +; +; Abstract: +; +; x64 CPU Exception Handler +; +; Notes: +; +;------------------------------------------------------------------------------ +%include "Nasm.inc" + +; +; CommonExceptionHandler() +; + +%define VC_EXCEPTION 29 + +extern ASM_PFX(mErrorCodeFlag) ; Error code flags for exceptions +extern ASM_PFX(mDoFarReturnFlag) ; Do far return flag +extern ASM_PFX(CommonExceptionHandler) +extern ASM_PFX(FeaturePcdGet (PcdCpuSmmStackGuard)) + +SECTION .data + +DEFAULT REL +SECTION .text + +ALIGN 8 + +AsmIdtVectorBegin: +%rep 32 + db 0x6a ; push #VectorNum + db ($ - AsmIdtVectorBegin) / ((AsmIdtVectorEnd - AsmIdtVectorBegin) / 32) ; VectorNum + push rax + mov rax, strict qword 0 ; mov rax, ASM_PFX(CommonInterruptEntry) + jmp rax +%endrep +AsmIdtVectorEnd: + +HookAfterStubHeaderBegin: + db 0x6a ; push +@VectorNum: + db 0 ; 0 will be fixed + push rax + mov rax, strict qword 0 ; mov rax, HookAfterStubHeaderEnd +JmpAbsoluteAddress: + jmp rax +HookAfterStubHeaderEnd: + mov rax, rsp + and sp, 0xfff0 ; make sure 16-byte aligned for exception context + sub rsp, 0x18 ; reserve room for filling exception data later + push rcx + mov rcx, [rax + 8] + bt [ASM_PFX(mErrorCodeFlag)], ecx + jnc .0 + push qword [rsp] ; push additional rcx to make stack alignment +.0: + xchg rcx, [rsp] ; restore rcx, save Exception Number in stack + push qword [rax] ; push rax into stack to keep code consistence + +;---------------------------------------; +; CommonInterruptEntry ; +;---------------------------------------; +; The follow algorithm is used for the common interrupt routine. +; Entry from each interrupt with a push eax and eax=interrupt number +; Stack frame would be as follows as specified in IA32 manuals: +; +; +---------------------+ <-- 16-byte aligned ensured by processor +; + Old SS + +; +---------------------+ +; + Old RSP + +; +---------------------+ +; + RFlags + +; +---------------------+ +; + CS + +; +---------------------+ +; + RIP + +; +---------------------+ +; + Error Code + +; +---------------------+ +; + Vector Number + +; +---------------------+ +; + RBP + +; +---------------------+ <-- RBP, 16-byte aligned +; The follow algorithm is used for the common interrupt routine. +global ASM_PFX(CommonInterruptEntry) +ASM_PFX(CommonInterruptEntry): + cli + pop rax + ; + ; All interrupt handlers are invoked through interrupt gates, so + ; IF flag automatically cleared at the entry point + ; + xchg rcx, [rsp] ; Save rcx into stack and save vector number into rcx + and rcx, 0xFF + cmp ecx, 32 ; Intel reserved vector for exceptions? + jae NoErrorCode + bt [ASM_PFX(mErrorCodeFlag)], ecx + jc HasErrorCode + +NoErrorCode: + + ; + ; Push a dummy error code on the stack + ; to maintain coherent stack map + ; + push qword [rsp] + mov qword [rsp + 8], 0 +HasErrorCode: + push rbp + mov rbp, rsp + push 0 ; clear EXCEPTION_HANDLER_CONTEXT.OldIdtHandler + push 0 ; clear EXCEPTION_HANDLER_CONTEXT.ExceptionDataFlag + + ; + ; Stack: + ; +---------------------+ <-- 16-byte aligned ensured by processor + ; + Old SS + + ; +---------------------+ + ; + Old RSP + + ; +---------------------+ + ; + RFlags + + ; +---------------------+ + ; + CS + + ; +---------------------+ + ; + RIP + + ; +---------------------+ + ; + Error Code + + ; +---------------------+ + ; + RCX / Vector Number + + ; +---------------------+ + ; + RBP + + ; +---------------------+ <-- RBP, 16-byte aligned + ; + + ; + ; Since here the stack pointer is 16-byte aligned, so + ; EFI_FX_SAVE_STATE_X64 of EFI_SYSTEM_CONTEXT_x64 + ; is 16-byte aligned + ; + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rax + push qword [rbp + 8] ; RCX + push rdx + push rbx + push qword [rbp + 48] ; RSP + push qword [rbp] ; RBP + push rsi + push rdi + +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero + movzx rax, word [rbp + 56] + push rax ; for ss + movzx rax, word [rbp + 32] + push rax ; for cs + mov rax, ds + push rax + mov rax, es + push rax + mov rax, fs + push rax + mov rax, gs + push rax + + mov [rbp + 8], rcx ; save vector number + +;; UINT64 Rip; + push qword [rbp + 24] + +;; UINT64 Gdtr[2], Idtr[2]; + xor rax, rax + push rax + push rax + sidt [rsp] + mov bx, word [rsp] + mov rax, qword [rsp + 2] + mov qword [rsp], rax + mov word [rsp + 8], bx + + xor rax, rax + push rax + push rax + sgdt [rsp] + mov bx, word [rsp] + mov rax, qword [rsp + 2] + mov qword [rsp], rax + mov word [rsp + 8], bx + +;; UINT64 Ldtr, Tr; + xor rax, rax + str ax + push rax + sldt ax + push rax + +;; UINT64 RFlags; + push qword [rbp + 40] + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; + mov rax, cr8 + push rax + mov rax, cr4 + or rax, 0x208 + mov cr4, rax + push rax + mov rax, cr3 + push rax + mov rax, cr2 + push rax + xor rax, rax + push rax + mov rax, cr0 + push rax + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + cmp qword [rbp + 8], VC_EXCEPTION + je VcDebugRegs ; For SEV-ES (#VC) Debug registers ignored + + mov rax, dr7 + push rax + mov rax, dr6 + push rax + mov rax, dr3 + push rax + mov rax, dr2 + push rax + mov rax, dr1 + push rax + mov rax, dr0 + push rax + jmp DrFinish + +VcDebugRegs: +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7 are skipped for #VC to avoid exception recursion + xor rax, rax + push rax + push rax + push rax + push rax + push rax + push rax + +DrFinish: +;; FX_SAVE_STATE_X64 FxSaveState; + sub rsp, 512 + mov rdi, rsp + db 0xf, 0xae, 0x7 ;fxsave [rdi] + +;; UEFI calling convention for x64 requires that Direction flag in EFLAGs is clear + cld + +;; UINT32 ExceptionData; + push qword [rbp + 16] + +;; Prepare parameter and call + mov rcx, [rbp + 8] + mov rdx, rsp + ; + ; Per X64 calling convention, allocate maximum parameter stack space + ; and make sure RSP is 16-byte aligned + ; + sub rsp, 4 * 8 + 8 + call ASM_PFX(CommonExceptionHandler) + add rsp, 4 * 8 + 8 + + cli +;; UINT64 ExceptionData; + add rsp, 8 + +;; FX_SAVE_STATE_X64 FxSaveState; + + mov rsi, rsp + db 0xf, 0xae, 0xE ; fxrstor [rsi] + add rsp, 512 + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; Skip restoration of DRx registers to support in-circuit emualators +;; or debuggers set breakpoint in interrupt/exception context + add rsp, 8 * 6 + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; + pop rax + mov cr0, rax + add rsp, 8 ; not for Cr1 + pop rax + mov cr2, rax + pop rax + mov cr3, rax + pop rax + mov cr4, rax + pop rax + mov cr8, rax + +;; UINT64 RFlags; + pop qword [rbp + 40] + +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add rsp, 48 + +;; UINT64 Rip; + pop qword [rbp + 24] + +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; + pop rax + ; mov gs, rax ; not for gs + pop rax + ; mov fs, rax ; not for fs + ; (X64 will not use fs and gs, so we do not restore it) + pop rax + mov es, rax + pop rax + mov ds, rax + pop qword [rbp + 32] ; for cs + pop qword [rbp + 56] ; for ss + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pop rdi + pop rsi + add rsp, 8 ; not for rbp + pop qword [rbp + 48] ; for rsp + pop rbx + pop rdx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + + mov rsp, rbp + pop rbp + add rsp, 16 + cmp qword [rsp - 32], 0 ; check EXCEPTION_HANDLER_CONTEXT.OldIdtHandler + jz DoReturn + cmp qword [rsp - 40], 1 ; check EXCEPTION_HANDLER_CONTEXT.ExceptionDataFlag + jz ErrorCode + jmp qword [rsp - 32] +ErrorCode: + sub rsp, 8 + jmp qword [rsp - 24] + +DoReturn: + cmp qword [ASM_PFX(mDoFarReturnFlag)], 0 ; Check if need to do far return instead of IRET + jz DoIret + push rax + mov rax, rsp ; save old RSP to rax + mov rsp, [rsp + 0x20] + push qword [rax + 0x10] ; save CS in new location + push qword [rax + 0x8] ; save EIP in new location + push qword [rax + 0x18] ; save EFLAGS in new location + mov rax, [rax] ; restore rax + popfq ; restore EFLAGS + + ; The follow algorithm is used for clear shadow stack token busy bit. + ; The comment is based on the sample shadow stack. + ; The sample shadow stack layout : + ; Address | Context + ; +-------------------------+ + ; 0xFD0 | FREE | it is 0xFD8|0x02|(LMA & CS.L), after SAVEPREVSSP. + ; +-------------------------+ + ; 0xFD8 | Prev SSP | + ; +-------------------------+ + ; 0xFE0 | RIP | + ; +-------------------------+ + ; 0xFE8 | CS | + ; +-------------------------+ + ; 0xFF0 | 0xFF0 | BUSY | BUSY flag cleared after CLRSSBSY + ; +-------------------------+ + ; 0xFF8 | 0xFD8|0x02|(LMA & CS.L) | + ; +-------------------------+ + ; Instructions for Intel Control Flow Enforcement Technology (CET) are supported since NASM version 2.15.01. + push rax ; SSP should be 0xFD8 at this point + cmp byte [dword ASM_PFX(FeaturePcdGet (PcdCpuSmmStackGuard))], 0 + jz CetDone + mov rax, cr4 + and rax, 0x800000 ; check if CET is enabled + jz CetDone + mov rax, 0x04 ; advance past cs:lip:prevssp;supervisor shadow stack token + INCSSP_RAX ; After this SSP should be 0xFF8 + SAVEPREVSSP ; now the shadow stack restore token will be created at 0xFD0 + READSSP_RAX ; Read new SSP, SSP should be 0x1000 + push rax + sub rax, 0x10 + CLRSSBSY_RAX ; Clear token at 0xFF0, SSP should be 0 after this + sub rax, 0x20 + RSTORSSP_RAX ; Restore to token at 0xFD0, new SSP will be 0xFD0 + pop rax + mov rax, 0x01 ; Pop off the new save token created + INCSSP_RAX ; SSP should be 0xFD8 now +CetDone: + pop rax ; restore rax + + DB 0x48 ; prefix to composite "retq" with next "retf" + retf ; far return +DoIret: + iretq + +;------------------------------------------------------------------------------------- +; GetTemplateAddressMap (&AddressMap); +;------------------------------------------------------------------------------------- +; comments here for definition of address map +global ASM_PFX(AsmGetTemplateAddressMap) +ASM_PFX(AsmGetTemplateAddressMap): + lea rax, [AsmIdtVectorBegin] + mov qword [rcx], rax + mov qword [rcx + 0x8], (AsmIdtVectorEnd - AsmIdtVectorBegin) / 32 + lea rax, [HookAfterStubHeaderBegin] + mov qword [rcx + 0x10], rax + +; Fix up CommonInterruptEntry address + lea rax, [ASM_PFX(CommonInterruptEntry)] + lea rcx, [AsmIdtVectorBegin] +%rep 32 + mov qword [rcx + (JmpAbsoluteAddress - 8 - HookAfterStubHeaderBegin)], rax + add rcx, (AsmIdtVectorEnd - AsmIdtVectorBegin) / 32 +%endrep +; Fix up HookAfterStubHeaderEnd + lea rax, [HookAfterStubHeaderEnd] + lea rcx, [JmpAbsoluteAddress] + mov qword [rcx - 8], rax + + ret + +;------------------------------------------------------------------------------------- +; AsmVectorNumFixup (*NewVectorAddr, VectorNum, *OldVectorAddr); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmVectorNumFixup) +ASM_PFX(AsmVectorNumFixup): + mov rax, rdx + mov [rcx + (@VectorNum - HookAfterStubHeaderBegin)], al + ret + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf new file mode 100644 index 00000000..76616cc4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf @@ -0,0 +1,60 @@ +## @file +# CPU Exception Handler library instance for SEC/PEI modules. +# +# Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR> +# Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# This is the XCODE5 variant of the SEC/PEI CpuExceptionHandlerLib. This +# variant performs binary patching to fix up addresses that allow the +# XCODE5 toolchain to be used. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Xcode5SecPeiCpuExceptionHandlerLib + MODULE_UNI_FILE = Xcode5SecPeiCpuExceptionHandlerLib.uni + FILE_GUID = 49C481AF-1621-42F3-8FA1-27C64143E304 + MODULE_TYPE = PEIM + VERSION_STRING = 1.1 + LIBRARY_CLASS = CpuExceptionHandlerLib|SEC PEI_CORE PEIM + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.Ia32] + Ia32/ExceptionHandlerAsm.nasm + Ia32/ExceptionTssEntryAsm.nasm + Ia32/ArchExceptionHandler.c + Ia32/ArchInterruptDefs.h + +[Sources.X64] + X64/Xcode5ExceptionHandlerAsm.nasm + X64/ArchExceptionHandler.c + X64/ArchInterruptDefs.h + +[Sources.common] + CpuExceptionCommon.h + CpuExceptionCommon.c + SecPeiCpuException.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + SerialPortLib + PrintLib + LocalApicLib + PeCoffGetEntryPointLib + VmgExitLib + +[FeaturePcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStackGuard ## CONSUMES + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.uni new file mode 100644 index 00000000..a63b25f3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.uni @@ -0,0 +1,18 @@ +// /** @file
+// XCODE5 CPU Exception Handler library instance for SEC/PEI modules.
+//
+// CPU Exception Handler library instance for SEC/PEI modules when built
+// using the XCODE5 toolchain.
+//
+// Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+// Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU Exception Handler library instance for SEC/PEI modules with the XCODE5 toolchain."
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU Exception Handler library instance for SEC/PEI modules with the XCODE5 toolchain."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.c new file mode 100644 index 00000000..3b8d863a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.c @@ -0,0 +1,41 @@ +/** @file + CPUID Leaf 0x15 for Core Crystal Clock frequency instance as Base Timer Library. + + Copyright (c) 2019 Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Base.h> +#include <Library/TimerLib.h> +#include <Library/BaseLib.h> + +/** + CPUID Leaf 0x15 for Core Crystal Clock Frequency. + + The TSC counting frequency is determined by using CPUID leaf 0x15. Frequency in MHz = Core XTAL frequency * EBX/EAX. + In newer flavors of the CPU, core xtal frequency is returned in ECX or 0 if not supported. + @return The number of TSC counts per second. + +**/ +UINT64 +CpuidCoreClockCalculateTscFrequency ( + VOID + ); + +/** + Internal function to retrieves the 64-bit frequency in Hz. + + Internal function to retrieves the 64-bit frequency in Hz. + + @return The frequency in Hz. + +**/ +UINT64 +InternalGetPerformanceCounterFrequency ( + VOID + ) +{ + return CpuidCoreClockCalculateTscFrequency (); +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf new file mode 100644 index 00000000..658e28e8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf @@ -0,0 +1,35 @@ +## @file +# Base CPU Timer Library +# +# Provides basic timer support using CPUID Leaf 0x15 XTAL frequency. The performance +# counter features are provided by the processors time stamp counter. +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseCpuTimerLib + FILE_GUID = F10B5B91-D15A-496C-B044-B5235721AA08 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TimerLib + MODULE_UNI_FILE = BaseCpuTimerLib.uni + +[Sources] + CpuTimerLib.c + BaseCpuTimerLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + PcdLib + DebugLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuCoreCrystalClockFrequency ## CONSUMES diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.uni new file mode 100644 index 00000000..fcf2b0fb --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.uni @@ -0,0 +1,17 @@ +// /** @file
+// Base CPU Timer Library
+//
+// Provides basic timer support using CPUID Leaf 0x15 XTAL frequency. The performance
+// counter features are provided by the processors time stamp counter.
+//
+// Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU Timer Library"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Provides basic timer support using CPUID Leaf 0x15 XTAL frequency."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuTimerLib/CpuTimerLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuTimerLib/CpuTimerLib.c new file mode 100644 index 00000000..89135bd1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuTimerLib/CpuTimerLib.c @@ -0,0 +1,279 @@ +/** @file + CPUID Leaf 0x15 for Core Crystal Clock frequency instance of Timer Library. + + Copyright (c) 2019 Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Base.h> +#include <Library/TimerLib.h> +#include <Library/BaseLib.h> +#include <Library/PcdLib.h> +#include <Library/DebugLib.h> +#include <Register/Cpuid.h> + +GUID mCpuCrystalFrequencyHobGuid = { 0xe1ec5ad0, 0x8569, 0x46bd, { 0x8d, 0xcd, 0x3b, 0x9f, 0x6f, 0x45, 0x82, 0x7a } }; + +/** + Internal function to retrieves the 64-bit frequency in Hz. + + Internal function to retrieves the 64-bit frequency in Hz. + + @return The frequency in Hz. + +**/ +UINT64 +InternalGetPerformanceCounterFrequency ( + VOID + ); + +/** + CPUID Leaf 0x15 for Core Crystal Clock Frequency. + + The TSC counting frequency is determined by using CPUID leaf 0x15. Frequency in MHz = Core XTAL frequency * EBX/EAX. + In newer flavors of the CPU, core xtal frequency is returned in ECX or 0 if not supported. + @return The number of TSC counts per second. + +**/ +UINT64 +CpuidCoreClockCalculateTscFrequency ( + VOID + ) +{ + UINT64 TscFrequency; + UINT64 CoreXtalFrequency; + UINT32 RegEax; + UINT32 RegEbx; + UINT32 RegEcx; + + // + // Use CPUID leaf 0x15 Time Stamp Counter and Nominal Core Crystal Clock Information + // EBX returns 0 if not supported. ECX, if non zero, provides Core Xtal Frequency in hertz. + // TSC frequency = (ECX, Core Xtal Frequency) * EBX/EAX. + // + AsmCpuid (CPUID_TIME_STAMP_COUNTER, &RegEax, &RegEbx, &RegEcx, NULL); + + // + // If EAX or EBX returns 0, the XTAL ratio is not enumerated. + // + if (RegEax == 0 || RegEbx ==0 ) { + ASSERT (RegEax != 0); + ASSERT (RegEbx != 0); + return 0; + } + // + // If ECX returns 0, the XTAL frequency is not enumerated. + // And PcdCpuCoreCrystalClockFrequency defined should base on processor series. + // + if (RegEcx == 0) { + CoreXtalFrequency = PcdGet64 (PcdCpuCoreCrystalClockFrequency); + } else { + CoreXtalFrequency = (UINT64) RegEcx; + } + + // + // Calculate TSC frequency = (ECX, Core Xtal Frequency) * EBX/EAX + // + TscFrequency = DivU64x32 (MultU64x32 (CoreXtalFrequency, RegEbx) + (UINT64)(RegEax >> 1), RegEax); + + return TscFrequency; +} + +/** + Stalls the CPU for at least the given number of ticks. + + Stalls the CPU for at least the given number of ticks. It's invoked by + MicroSecondDelay() and NanoSecondDelay(). + + @param Delay A period of time to delay in ticks. + +**/ +VOID +InternalCpuDelay ( + IN UINT64 Delay + ) +{ + UINT64 Ticks; + + // + // The target timer count is calculated here + // + Ticks = AsmReadTsc() + Delay; + + // + // Wait until time out + // Timer wrap-arounds are NOT handled correctly by this function. + // Thus, this function must be called within 10 years of reset since + // Intel guarantees a minimum of 10 years before the TSC wraps. + // + while (AsmReadTsc() <= Ticks) { + CpuPause(); + } +} + +/** + Stalls the CPU for at least the given number of microseconds. + + Stalls the CPU for the number of microseconds specified by MicroSeconds. + + @param[in] MicroSeconds The minimum number of microseconds to delay. + + @return MicroSeconds + +**/ +UINTN +EFIAPI +MicroSecondDelay ( + IN UINTN MicroSeconds + ) +{ + + InternalCpuDelay ( + DivU64x32 ( + MultU64x64 ( + MicroSeconds, + InternalGetPerformanceCounterFrequency () + ), + 1000000u + ) + ); + + return MicroSeconds; +} + +/** + Stalls the CPU for at least the given number of nanoseconds. + + Stalls the CPU for the number of nanoseconds specified by NanoSeconds. + + @param NanoSeconds The minimum number of nanoseconds to delay. + + @return NanoSeconds + +**/ +UINTN +EFIAPI +NanoSecondDelay ( + IN UINTN NanoSeconds + ) +{ + + InternalCpuDelay ( + DivU64x32 ( + MultU64x64 ( + NanoSeconds, + InternalGetPerformanceCounterFrequency () + ), + 1000000000u + ) + ); + + return NanoSeconds; +} + +/** + Retrieves the current value of a 64-bit free running performance counter. + + Retrieves the current value of a 64-bit free running performance counter. The + counter can either count up by 1 or count down by 1. If the physical + performance counter counts by a larger increment, then the counter values + must be translated. The properties of the counter can be retrieved from + GetPerformanceCounterProperties(). + + @return The current value of the free running performance counter. + +**/ +UINT64 +EFIAPI +GetPerformanceCounter ( + VOID + ) +{ + return AsmReadTsc (); +} + +/** + Retrieves the 64-bit frequency in Hz and the range of performance counter + values. + + If StartValue is not NULL, then the value that the performance counter starts + with immediately after is it rolls over is returned in StartValue. If + EndValue is not NULL, then the value that the performance counter end with + immediately before it rolls over is returned in EndValue. The 64-bit + frequency of the performance counter in Hz is always returned. If StartValue + is less than EndValue, then the performance counter counts up. If StartValue + is greater than EndValue, then the performance counter counts down. For + example, a 64-bit free running counter that counts up would have a StartValue + of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter + that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0. + + @param StartValue The value the performance counter starts with when it + rolls over. + @param EndValue The value that the performance counter ends with before + it rolls over. + + @return The frequency in Hz. + +**/ +UINT64 +EFIAPI +GetPerformanceCounterProperties ( + OUT UINT64 *StartValue, OPTIONAL + OUT UINT64 *EndValue OPTIONAL + ) +{ + if (StartValue != NULL) { + *StartValue = 0; + } + + if (EndValue != NULL) { + *EndValue = 0xffffffffffffffffULL; + } + return InternalGetPerformanceCounterFrequency (); +} + +/** + Converts elapsed ticks of performance counter to time in nanoseconds. + + This function converts the elapsed ticks of running performance counter to + time value in unit of nanoseconds. + + @param Ticks The number of elapsed ticks of running performance counter. + + @return The elapsed time in nanoseconds. + +**/ +UINT64 +EFIAPI +GetTimeInNanoSecond ( + IN UINT64 Ticks + ) +{ + UINT64 Frequency; + UINT64 NanoSeconds; + UINT64 Remainder; + INTN Shift; + + Frequency = GetPerformanceCounterProperties (NULL, NULL); + + // + // Ticks + // Time = --------- x 1,000,000,000 + // Frequency + // + NanoSeconds = MultU64x32 (DivU64x64Remainder (Ticks, Frequency, &Remainder), 1000000000u); + + // + // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit. + // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34, + // i.e. highest bit set in Remainder should <= 33. + // + Shift = MAX (0, HighBitSet64 (Remainder) - 33); + Remainder = RShiftU64 (Remainder, (UINTN) Shift); + Frequency = RShiftU64 (Frequency, (UINTN) Shift); + NanoSeconds += DivU64x64Remainder (MultU64x32 (Remainder, 1000000000u), Frequency, NULL); + + return NanoSeconds; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.c new file mode 100644 index 00000000..e2f1cfc2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.c @@ -0,0 +1,322 @@ +/** @file + Implementation of MicrocodeLib. + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi/UefiBaseType.h> +#include <Register/Intel/Cpuid.h> +#include <Register/Intel/ArchitecturalMsr.h> +#include <Register/Intel/Microcode.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Ppi/ShadowMicrocode.h> + +/** + Get microcode update signature of currently loaded microcode update. + + @return Microcode signature. +**/ +UINT32 +EFIAPI +GetProcessorMicrocodeSignature ( + VOID + ) +{ + MSR_IA32_BIOS_SIGN_ID_REGISTER BiosSignIdMsr; + + AsmWriteMsr64 (MSR_IA32_BIOS_SIGN_ID, 0); + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL); + BiosSignIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_BIOS_SIGN_ID); + return BiosSignIdMsr.Bits.MicrocodeUpdateSignature; +} + +/** + Get the processor signature and platform ID for current processor. + + @param MicrocodeCpuId Return the processor signature and platform ID. +**/ +VOID +EFIAPI +GetProcessorMicrocodeCpuId ( + EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId + ) +{ + MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr; + + ASSERT (MicrocodeCpuId != NULL); + + PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID); + MicrocodeCpuId->PlatformId = (UINT8) PlatformIdMsr.Bits.PlatformId; + AsmCpuid (CPUID_VERSION_INFO, &MicrocodeCpuId->ProcessorSignature, NULL, NULL, NULL); +} + +/** + Return the total size of the microcode entry. + + Logic follows pseudo code in SDM as below: + + N = 512 + If (Update.DataSize != 00000000H) + N = Update.TotalSize / 4 + + If Microcode is NULL, then ASSERT. + + @param Microcode Pointer to the microcode entry. + + @return The microcode total size. +**/ +UINT32 +EFIAPI +GetMicrocodeLength ( + IN CPU_MICROCODE_HEADER *Microcode + ) +{ + UINT32 TotalSize; + + ASSERT (Microcode != NULL); + + TotalSize = 2048; + if (Microcode->DataSize != 0) { + TotalSize = Microcode->TotalSize; + } + return TotalSize; +} + +/** + Load the microcode to the processor. + + If Microcode is NULL, then ASSERT. + + @param Microcode Pointer to the microcode entry. +**/ +VOID +EFIAPI +LoadMicrocode ( + IN CPU_MICROCODE_HEADER *Microcode + ) +{ + ASSERT (Microcode != NULL); + + AsmWriteMsr64 (MSR_IA32_BIOS_UPDT_TRIG, (UINT64) (UINTN) (Microcode + 1)); +} + +/** + Determine if a microcode patch matchs the specific processor signature and flag. + + @param[in] ProcessorSignature The processor signature field value in a + microcode patch. + @param[in] ProcessorFlags The processor flags field value in a + microcode patch. + @param[in] MicrocodeCpuId A pointer to an array of EDKII_PEI_MICROCODE_CPU_ID + structures. + @param[in] MicrocodeCpuIdCount Number of elements in MicrocodeCpuId array. + + @retval TRUE The specified microcode patch matches to one of the MicrocodeCpuId. + @retval FALSE The specified microcode patch doesn't match to any of the MicrocodeCpuId. +**/ +BOOLEAN +IsProcessorMatchedMicrocode ( + IN UINT32 ProcessorSignature, + IN UINT32 ProcessorFlags, + IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId, + IN UINTN MicrocodeCpuIdCount + ) +{ + UINTN Index; + + if (MicrocodeCpuIdCount == 0) { + return TRUE; + } + + for (Index = 0; Index < MicrocodeCpuIdCount; Index++) { + if ((ProcessorSignature == MicrocodeCpuId[Index].ProcessorSignature) && + (ProcessorFlags & (1 << MicrocodeCpuId[Index].PlatformId)) != 0) { + return TRUE; + } + } + + return FALSE; +} + +/** + Detect whether specified processor can find matching microcode patch and load it. + + Microcode format is as below: + +----------------------------------------+-------------------------------------------------+ + | CPU_MICROCODE_HEADER | | + +----------------------------------------+ V + | Update Data | CPU_MICROCODE_HEADER.Checksum + +----------------------------------------+-------+ ^ + | CPU_MICROCODE_EXTENDED_TABLE_HEADER | | | + +----------------------------------------+ V | + | CPU_MICROCODE_EXTENDED_TABLE[0] | CPU_MICROCODE_EXTENDED_TABLE_HEADER.Checksum | + | CPU_MICROCODE_EXTENDED_TABLE[1] | ^ | + | ... | | | + +----------------------------------------+-------+-----------------------------------------+ + + There may by multiple CPU_MICROCODE_EXTENDED_TABLE in this format. + The count of CPU_MICROCODE_EXTENDED_TABLE is indicated by ExtendedSignatureCount + of CPU_MICROCODE_EXTENDED_TABLE_HEADER structure. + + If Microcode is NULL, then ASSERT. + + @param Microcode Pointer to a microcode entry. + @param MicrocodeLength The total length of the microcode entry. + @param MinimumRevision The microcode whose revision <= MinimumRevision is treated as invalid. + Caller can supply value get from GetProcessorMicrocodeSignature() to check + whether the microcode is newer than loaded one. + Caller can supply 0 to treat any revision (except 0) microcode as valid. + @param MicrocodeCpuIds Pointer to an array of processor signature and platform ID that represents + a set of processors. + Caller can supply zero-element array to skip the processor signature and + platform ID check. + @param MicrocodeCpuIdCount The number of elements in MicrocodeCpuIds. + @param VerifyChecksum FALSE to skip all the checksum verifications. + + @retval TRUE The microcode is valid. + @retval FALSE The microcode is invalid. +**/ +BOOLEAN +EFIAPI +IsValidMicrocode ( + IN CPU_MICROCODE_HEADER *Microcode, + IN UINTN MicrocodeLength, + IN UINT32 MinimumRevision, + IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuIds, + IN UINTN MicrocodeCpuIdCount, + IN BOOLEAN VerifyChecksum + ) +{ + UINTN Index; + UINT32 DataSize; + UINT32 TotalSize; + CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable; + CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader; + UINT32 ExtendedTableLength; + UINT32 Sum32; + BOOLEAN Match; + + ASSERT (Microcode != NULL); + + // + // It's invalid when: + // the input microcode buffer is so small that even cannot contain the header. + // the input microcode buffer is so large that exceeds MAX_ADDRESS. + // + if ((MicrocodeLength < sizeof (CPU_MICROCODE_HEADER)) || (MicrocodeLength > (MAX_ADDRESS - (UINTN) Microcode))) { + return FALSE; + } + + // + // Per SDM, HeaderVersion and LoaderRevision should both be 1. + // + if ((Microcode->HeaderVersion != 1) || (Microcode->LoaderRevision != 1)) { + return FALSE; + } + + // + // The microcode revision should be larger than the minimum revision. + // + if (Microcode->UpdateRevision <= MinimumRevision) { + return FALSE; + } + + DataSize = Microcode->DataSize; + if (DataSize == 0) { + DataSize = 2000; + } + + // + // Per SDM, DataSize should be multiple of DWORDs. + // + if ((DataSize % 4) != 0) { + return FALSE; + } + + TotalSize = GetMicrocodeLength (Microcode); + + // + // Check whether the whole microcode is within the buffer. + // TotalSize should be multiple of 1024. + // + if (((TotalSize % SIZE_1KB) != 0) || (TotalSize > MicrocodeLength)) { + return FALSE; + } + + // + // The summation of all DWORDs in microcode should be zero. + // + if (VerifyChecksum && (CalculateSum32 ((UINT32 *) Microcode, TotalSize) != 0)) { + return FALSE; + } + + Sum32 = Microcode->ProcessorSignature.Uint32 + Microcode->ProcessorFlags + Microcode->Checksum; + + // + // Check the processor signature and platform ID in the primary header. + // + Match = IsProcessorMatchedMicrocode ( + Microcode->ProcessorSignature.Uint32, + Microcode->ProcessorFlags, + MicrocodeCpuIds, + MicrocodeCpuIdCount + ); + if (Match) { + return TRUE; + } + + ExtendedTableLength = TotalSize - (DataSize + sizeof (CPU_MICROCODE_HEADER)); + if ((ExtendedTableLength < sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER)) || ((ExtendedTableLength % 4) != 0)) { + return FALSE; + } + // + // Extended Table exist, check if the CPU in support list + // + ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *) ((UINTN) (Microcode + 1) + DataSize); + if (ExtendedTableHeader->ExtendedSignatureCount > MAX_UINT32 / sizeof (CPU_MICROCODE_EXTENDED_TABLE)) { + return FALSE; + } + if (ExtendedTableHeader->ExtendedSignatureCount * sizeof (CPU_MICROCODE_EXTENDED_TABLE) + > ExtendedTableLength - sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER)) { + return FALSE; + } + // + // Check the extended table checksum + // + if (VerifyChecksum && (CalculateSum32 ((UINT32 *) ExtendedTableHeader, ExtendedTableLength) != 0)) { + return FALSE; + } + + ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *) (ExtendedTableHeader + 1); + for (Index = 0; Index < ExtendedTableHeader->ExtendedSignatureCount; Index ++) { + if (VerifyChecksum && + (ExtendedTable[Index].ProcessorSignature.Uint32 + ExtendedTable[Index].ProcessorFlag + + ExtendedTable[Index].Checksum != Sum32)) { + // + // The extended table entry is valid when the summation of Processor Signature, Processor Flags + // and Checksum equal to the coresponding summation from primary header. Because: + // CalculateSum32 (Header + Update Binary) == 0 + // CalculateSum32 (Header + Update Binary) + // - (Header.ProcessorSignature + Header.ProcessorFlag + Header.Checksum) + // + (Extended.ProcessorSignature + Extended.ProcessorFlag + Extended.Checksum) == 0 + // So, + // (Header.ProcessorSignature + Header.ProcessorFlag + Header.Checksum) + // == (Extended.ProcessorSignature + Extended.ProcessorFlag + Extended.Checksum) + // + continue; + } + Match = IsProcessorMatchedMicrocode ( + ExtendedTable[Index].ProcessorSignature.Uint32, + ExtendedTable[Index].ProcessorFlag, + MicrocodeCpuIds, + MicrocodeCpuIdCount + ); + if (Match) { + return TRUE; + } + } + return FALSE; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf new file mode 100644 index 00000000..f076eea7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf @@ -0,0 +1,32 @@ +## @file +# Library for microcode verification and load. +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = MicrocodeLib + FILE_GUID = EB8C72BC-8A48-4F80-996B-E52F68416D57 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MicrocodeLib + +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources.common] + MicrocodeLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf new file mode 100644 index 00000000..e260e17d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf @@ -0,0 +1,78 @@ +## @file +# MP Initialize Library instance for DXE driver. +# +# Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeMpInitLib + MODULE_UNI_FILE = DxeMpInitLib.uni + FILE_GUID = B88F7146-9834-4c55-BFAC-481CC0C33736 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.1 + LIBRARY_CLASS = MpInitLib|DXE_DRIVER + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.IA32] + Ia32/MpFuncs.nasm + +[Sources.X64] + X64/MpFuncs.nasm + +[Sources.common] + MpEqu.inc + DxeMpLib.c + MpLib.c + MpLib.h + Microcode.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + LocalApicLib + MemoryAllocationLib + HobLib + MtrrLib + CpuLib + UefiCpuLib + UefiBootServicesTableLib + DebugAgentLib + SynchronizationLib + PcdLib + VmgExitLib + MicrocodeLib + +[Protocols] + gEfiTimerArchProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + gEfiEventExitBootServicesGuid ## CONSUMES ## Event + gEfiEventLegacyBootGuid ## SOMETIMES_CONSUMES ## Event + gEdkiiMicrocodePatchHobGuid ## SOMETIMES_CONSUMES ## HOB + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuBootLogicalProcessorNumber ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApInitTimeOutInMicroSeconds ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchAddress ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchRegionSize ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase ## CONSUMES diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.uni new file mode 100644 index 00000000..27cfdd37 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.uni @@ -0,0 +1,16 @@ +// /** @file
+// MP Initialize Library instance for DXE driver.
+//
+// MP Initialize Library instance for DXE driver.
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "MP Initialize Library instance for DXE driver."
+
+#string STR_MODULE_DESCRIPTION #language en-US "MP Initialize Library instance for DXE driver."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c new file mode 100644 index 00000000..16c8e9ca --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c @@ -0,0 +1,956 @@ +/** @file + MP initialize support functions for DXE phase. + + Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "MpLib.h" + +#include <Library/UefiLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/DebugAgentLib.h> +#include <Library/DxeServicesTableLib.h> +#include <Library/VmgExitLib.h> +#include <Register/Amd/Fam17Msr.h> +#include <Register/Amd/Ghcb.h> + +#include <Protocol/Timer.h> + +#define AP_SAFE_STACK_SIZE 128 + +CPU_MP_DATA *mCpuMpData = NULL; +EFI_EVENT mCheckAllApsEvent = NULL; +EFI_EVENT mMpInitExitBootServicesEvent = NULL; +EFI_EVENT mLegacyBootEvent = NULL; +volatile BOOLEAN mStopCheckAllApsStatus = TRUE; +VOID *mReservedApLoopFunc = NULL; +UINTN mReservedTopOfApStack; +volatile UINT32 mNumberToFinish = 0; + +/** + Enable Debug Agent to support source debugging on AP function. + +**/ +VOID +EnableDebugAgent ( + VOID + ) +{ + // + // Initialize Debug Agent to support source level debug in DXE phase + // + InitializeDebugAgent (DEBUG_AGENT_INIT_DXE_AP, NULL, NULL); +} + +/** + Get the pointer to CPU MP Data structure. + + @return The pointer to CPU MP Data structure. +**/ +CPU_MP_DATA * +GetCpuMpData ( + VOID + ) +{ + ASSERT (mCpuMpData != NULL); + return mCpuMpData; +} + +/** + Save the pointer to CPU MP Data structure. + + @param[in] CpuMpData The pointer to CPU MP Data structure will be saved. +**/ +VOID +SaveCpuMpData ( + IN CPU_MP_DATA *CpuMpData + ) +{ + mCpuMpData = CpuMpData; +} + +/** + Get available system memory below 0x88000 by specified size. + + @param[in] WakeupBufferSize Wakeup buffer size required + + @retval other Return wakeup buffer address below 1MB. + @retval -1 Cannot find free memory below 1MB. +**/ +UINTN +GetWakeupBuffer ( + IN UINTN WakeupBufferSize + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS StartAddress; + EFI_MEMORY_TYPE MemoryType; + + if (PcdGetBool (PcdSevEsIsEnabled)) { + MemoryType = EfiReservedMemoryType; + } else { + MemoryType = EfiBootServicesData; + } + + // + // Try to allocate buffer below 1M for waking vector. + // LegacyBios driver only reports warning when page allocation in range + // [0x60000, 0x88000) fails. + // This library is consumed by CpuDxe driver to produce CPU Arch protocol. + // LagacyBios driver depends on CPU Arch protocol which guarantees below + // allocation runs earlier than LegacyBios driver. + // + StartAddress = 0x88000; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + MemoryType, + EFI_SIZE_TO_PAGES (WakeupBufferSize), + &StartAddress + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + StartAddress = (EFI_PHYSICAL_ADDRESS) -1; + } + + DEBUG ((DEBUG_INFO, "WakeupBufferStart = %x, WakeupBufferSize = %x\n", + (UINTN) StartAddress, WakeupBufferSize)); + + return (UINTN) StartAddress; +} + +/** + Get available EfiBootServicesCode memory below 4GB by specified size. + + This buffer is required to safely transfer AP from real address mode to + protected mode or long mode, due to the fact that the buffer returned by + GetWakeupBuffer() may be marked as non-executable. + + @param[in] BufferSize Wakeup transition buffer size. + + @retval other Return wakeup transition buffer address below 4GB. + @retval 0 Cannot find free memory below 4GB. +**/ +UINTN +GetModeTransitionBuffer ( + IN UINTN BufferSize + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS StartAddress; + + StartAddress = BASE_4GB - 1; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiBootServicesCode, + EFI_SIZE_TO_PAGES (BufferSize), + &StartAddress + ); + if (EFI_ERROR (Status)) { + StartAddress = 0; + } + + return (UINTN)StartAddress; +} + +/** + Return the address of the SEV-ES AP jump table. + + This buffer is required in order for an SEV-ES guest to transition from + UEFI into an OS. + + @return Return SEV-ES AP jump table buffer +**/ +UINTN +GetSevEsAPMemory ( + VOID + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS StartAddress; + MSR_SEV_ES_GHCB_REGISTER Msr; + GHCB *Ghcb; + BOOLEAN InterruptState; + + // + // Allocate 1 page for AP jump table page + // + StartAddress = BASE_4GB - 1; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiReservedMemoryType, + 1, + &StartAddress + ); + ASSERT_EFI_ERROR (Status); + + DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory = %lx\n", (UINTN) StartAddress)); + + // + // Save the SevEsAPMemory as the AP jump table. + // + Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB); + Ghcb = Msr.Ghcb; + + VmgInit (Ghcb, &InterruptState); + VmgExit (Ghcb, SVM_EXIT_AP_JUMP_TABLE, 0, (UINT64) (UINTN) StartAddress); + VmgDone (Ghcb, InterruptState); + + return (UINTN) StartAddress; +} + +/** + Checks APs status and updates APs status if needed. + +**/ +VOID +CheckAndUpdateApsStatus ( + VOID + ) +{ + UINTN ProcessorNumber; + EFI_STATUS Status; + CPU_MP_DATA *CpuMpData; + + CpuMpData = GetCpuMpData (); + + // + // First, check whether pending StartupAllAPs() exists. + // + if (CpuMpData->WaitEvent != NULL) { + + Status = CheckAllAPs (); + // + // If all APs finish for StartupAllAPs(), signal the WaitEvent for it. + // + if (Status != EFI_NOT_READY) { + Status = gBS->SignalEvent (CpuMpData->WaitEvent); + CpuMpData->WaitEvent = NULL; + } + } + + // + // Second, check whether pending StartupThisAPs() callings exist. + // + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { + + if (CpuMpData->CpuData[ProcessorNumber].WaitEvent == NULL) { + continue; + } + + Status = CheckThisAP (ProcessorNumber); + + if (Status != EFI_NOT_READY) { + gBS->SignalEvent (CpuMpData->CpuData[ProcessorNumber].WaitEvent); + CpuMpData->CpuData[ProcessorNumber].WaitEvent = NULL; + } + } +} + +/** + Checks APs' status periodically. + + This function is triggered by timer periodically to check the + state of APs for StartupAllAPs() and StartupThisAP() executed + in non-blocking mode. + + @param[in] Event Event triggered. + @param[in] Context Parameter passed with the event. + +**/ +VOID +EFIAPI +CheckApsStatus ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // If CheckApsStatus() is not stopped, otherwise return immediately. + // + if (!mStopCheckAllApsStatus) { + CheckAndUpdateApsStatus (); + } +} + +/** + Get Protected mode code segment with 16-bit default addressing + from current GDT table. + + @return Protected mode 16-bit code segment value. +**/ +UINT16 +GetProtectedMode16CS ( + VOID + ) +{ + IA32_DESCRIPTOR GdtrDesc; + IA32_SEGMENT_DESCRIPTOR *GdtEntry; + UINTN GdtEntryCount; + UINT16 Index; + + Index = (UINT16) -1; + AsmReadGdtr (&GdtrDesc); + GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR); + GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; + for (Index = 0; Index < GdtEntryCount; Index++) { + if (GdtEntry->Bits.L == 0) { + if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 0) { + break; + } + } + GdtEntry++; + } + ASSERT (Index != GdtEntryCount); + return Index * 8; +} + +/** + Get Protected mode code segment from current GDT table. + + @return Protected mode code segment value. +**/ +UINT16 +GetProtectedModeCS ( + VOID + ) +{ + IA32_DESCRIPTOR GdtrDesc; + IA32_SEGMENT_DESCRIPTOR *GdtEntry; + UINTN GdtEntryCount; + UINT16 Index; + + AsmReadGdtr (&GdtrDesc); + GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR); + GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; + for (Index = 0; Index < GdtEntryCount; Index++) { + if (GdtEntry->Bits.L == 0) { + if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) { + break; + } + } + GdtEntry++; + } + ASSERT (Index != GdtEntryCount); + return Index * 8; +} + +/** + Do sync on APs. + + @param[in, out] Buffer Pointer to private data buffer. +**/ +VOID +EFIAPI +RelocateApLoop ( + IN OUT VOID *Buffer + ) +{ + CPU_MP_DATA *CpuMpData; + BOOLEAN MwaitSupport; + ASM_RELOCATE_AP_LOOP AsmRelocateApLoopFunc; + UINTN ProcessorNumber; + UINTN StackStart; + + MpInitLibWhoAmI (&ProcessorNumber); + CpuMpData = GetCpuMpData (); + MwaitSupport = IsMwaitSupport (); + if (CpuMpData->SevEsIsEnabled) { + StackStart = CpuMpData->SevEsAPResetStackStart; + } else { + StackStart = mReservedTopOfApStack; + } + AsmRelocateApLoopFunc = (ASM_RELOCATE_AP_LOOP) (UINTN) mReservedApLoopFunc; + AsmRelocateApLoopFunc ( + MwaitSupport, + CpuMpData->ApTargetCState, + CpuMpData->PmCodeSegment, + StackStart - ProcessorNumber * AP_SAFE_STACK_SIZE, + (UINTN) &mNumberToFinish, + CpuMpData->Pm16CodeSegment, + CpuMpData->SevEsAPBuffer, + CpuMpData->WakeupBuffer + ); + // + // It should never reach here + // + ASSERT (FALSE); +} + +/** + Callback function for ExitBootServices. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context The pointer to the notification function's context, + which is implementation-dependent. + +**/ +VOID +EFIAPI +MpInitChangeApLoopCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + CPU_MP_DATA *CpuMpData; + + CpuMpData = GetCpuMpData (); + CpuMpData->PmCodeSegment = GetProtectedModeCS (); + CpuMpData->Pm16CodeSegment = GetProtectedMode16CS (); + CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode); + mNumberToFinish = CpuMpData->CpuCount - 1; + WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE); + while (mNumberToFinish > 0) { + CpuPause (); + } + + if (CpuMpData->SevEsIsEnabled && (CpuMpData->WakeupBuffer != (UINTN) -1)) { + // + // There are APs present. Re-use reserved memory area below 1MB from + // WakeupBuffer as the area to be used for transitioning to 16-bit mode + // in support of booting of the AP by an OS. + // + CopyMem ( + (VOID *) CpuMpData->WakeupBuffer, + (VOID *) (CpuMpData->AddressMap.RendezvousFunnelAddress + + CpuMpData->AddressMap.SwitchToRealPM16ModeOffset), + CpuMpData->AddressMap.SwitchToRealPM16ModeSize + ); + } + + DEBUG ((DEBUG_INFO, "%a() done!\n", __FUNCTION__)); +} + +/** + Initialize global data for MP support. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +InitMpGlobalData ( + IN CPU_MP_DATA *CpuMpData + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Address; + UINTN ApSafeBufferSize; + UINTN Index; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc; + UINTN StackBase; + CPU_INFO_IN_HOB *CpuInfoInHob; + + SaveCpuMpData (CpuMpData); + + if (CpuMpData->CpuCount == 1) { + // + // If only BSP exists, return + // + return; + } + + if (PcdGetBool (PcdCpuStackGuard)) { + // + // One extra page at the bottom of the stack is needed for Guard page. + // + if (CpuMpData->CpuApStackSize <= EFI_PAGE_SIZE) { + DEBUG ((DEBUG_ERROR, "PcdCpuApStackSize is not big enough for Stack Guard!\n")); + ASSERT (FALSE); + } + + // + // DXE will reuse stack allocated for APs at PEI phase if it's available. + // Let's check it here. + // + // Note: BSP's stack guard is set at DxeIpl phase. But for the sake of + // BSP/AP exchange, stack guard for ApTopOfStack of cpu 0 will still be + // set here. + // + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; + for (Index = 0; Index < CpuMpData->CpuCount; ++Index) { + if (CpuInfoInHob != NULL && CpuInfoInHob[Index].ApTopOfStack != 0) { + StackBase = (UINTN)CpuInfoInHob[Index].ApTopOfStack - CpuMpData->CpuApStackSize; + } else { + StackBase = CpuMpData->Buffer + Index * CpuMpData->CpuApStackSize; + } + + Status = gDS->GetMemorySpaceDescriptor (StackBase, &MemDesc); + ASSERT_EFI_ERROR (Status); + + Status = gDS->SetMemorySpaceAttributes ( + StackBase, + EFI_PAGES_TO_SIZE (1), + MemDesc.Attributes | EFI_MEMORY_RP + ); + ASSERT_EFI_ERROR (Status); + + DEBUG ((DEBUG_INFO, "Stack Guard set at %lx [cpu%lu]!\n", + (UINT64)StackBase, (UINT64)Index)); + } + } + + // + // Avoid APs access invalid buffer data which allocated by BootServices, + // so we will allocate reserved data for AP loop code. We also need to + // allocate this buffer below 4GB due to APs may be transferred to 32bit + // protected mode on long mode DXE. + // Allocating it in advance since memory services are not available in + // Exit Boot Services callback function. + // + ApSafeBufferSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES ( + CpuMpData->AddressMap.RelocateApLoopFuncSize + )); + Address = BASE_4GB - 1; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiReservedMemoryType, + EFI_SIZE_TO_PAGES (ApSafeBufferSize), + &Address + ); + ASSERT_EFI_ERROR (Status); + + mReservedApLoopFunc = (VOID *) (UINTN) Address; + ASSERT (mReservedApLoopFunc != NULL); + + // + // Make sure that the buffer memory is executable if NX protection is enabled + // for EfiReservedMemoryType. + // + // TODO: Check EFI_MEMORY_XP bit set or not once it's available in DXE GCD + // service. + // + Status = gDS->GetMemorySpaceDescriptor (Address, &MemDesc); + if (!EFI_ERROR (Status)) { + gDS->SetMemorySpaceAttributes ( + Address, + ApSafeBufferSize, + MemDesc.Attributes & (~EFI_MEMORY_XP) + ); + } + + ApSafeBufferSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES ( + CpuMpData->CpuCount * AP_SAFE_STACK_SIZE + )); + Address = BASE_4GB - 1; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiReservedMemoryType, + EFI_SIZE_TO_PAGES (ApSafeBufferSize), + &Address + ); + ASSERT_EFI_ERROR (Status); + + mReservedTopOfApStack = (UINTN) Address + ApSafeBufferSize; + ASSERT ((mReservedTopOfApStack & (UINTN)(CPU_STACK_ALIGNMENT - 1)) == 0); + CopyMem ( + mReservedApLoopFunc, + CpuMpData->AddressMap.RelocateApLoopFuncAddress, + CpuMpData->AddressMap.RelocateApLoopFuncSize + ); + + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + CheckApsStatus, + NULL, + &mCheckAllApsEvent + ); + ASSERT_EFI_ERROR (Status); + + // + // Set timer to check all APs status. + // + Status = gBS->SetTimer ( + mCheckAllApsEvent, + TimerPeriodic, + EFI_TIMER_PERIOD_MICROSECONDS ( + PcdGet32 (PcdCpuApStatusCheckIntervalInMicroSeconds) + ) + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEvent ( + EVT_SIGNAL_EXIT_BOOT_SERVICES, + TPL_CALLBACK, + MpInitChangeApLoopCallback, + NULL, + &mMpInitExitBootServicesEvent + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + MpInitChangeApLoopCallback, + NULL, + &gEfiEventLegacyBootGuid, + &mLegacyBootEvent + ); + ASSERT_EFI_ERROR (Status); +} + +/** + This service executes a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] SingleThread If TRUE, then all the enabled APs execute + the function specified by Procedure one by + one, in ascending order of processor handle + number. If FALSE, then all the enabled APs + execute the function specified by Procedure + simultaneously. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until all APs finish + or TimeoutInMicroSeconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on all the enabled + APs, and go on executing immediately. If + all return from Procedure, or TimeoutInMicroSeconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + all APs return from Procedure, then Procedure + on the failed APs is terminated. All enabled + APs are available for next function assigned + by MpInitLibStartupAllAPs() or + MPInitLibStartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise, + if all APs finish successfully, then its + content is set to NULL. If not all APs + finish before timeout expires, then its + content is set to address of the buffer + holding handle numbers of the failed APs. + The buffer is allocated by MP Initialization + library, and it's the caller's responsibility to + free the buffer with FreePool() service. + In blocking mode, it is ready for consumption + when the call returns. In non-blocking mode, + it is ready when WaitEvent is signaled. The + list of failed CPU is terminated by + END_OF_CPU_LIST. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not + supported. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + all enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +MpInitLibStartupAllAPs ( + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT UINTN **FailedCpuList OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Temporarily stop checkAllApsStatus for avoid resource dead-lock. + // + mStopCheckAllApsStatus = TRUE; + + Status = StartupAllCPUsWorker ( + Procedure, + SingleThread, + TRUE, + WaitEvent, + TimeoutInMicroseconds, + ProcedureArgument, + FailedCpuList + ); + + // + // Start checkAllApsStatus + // + mStopCheckAllApsStatus = FALSE; + + return Status; +} + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. + + @param[in] Procedure A pointer to the function to be run on the + designated AP of the system. See type + EFI_AP_PROCEDURE. + @param[in] ProcessorNumber The handle number of the AP. The range is + from 0 to the total number of logical + processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until this AP finish + or TimeoutInMicroSeconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on this AP, + and go on executing immediately. If this AP + return from Procedure or TimeoutInMicroSeconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + this AP to finish this Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + this AP returns from Procedure, then Procedure + on the AP is terminated. The + AP is available for next function assigned + by MpInitLibStartupAllAPs() or + MpInitLibStartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure on the + specified AP. + @param[out] Finished If NULL, this parameter is ignored. In + blocking mode, this parameter is ignored. + In non-blocking mode, if AP returns from + Procedure before the timeout expires, its + content is set to TRUE. Otherwise, the + value is set to FALSE. The caller can + determine if the AP returned from Procedure + by evaluating this value. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before + the timeout expires. + @retval EFI_SUCCESS In non-blocking mode, the function has been + dispatched to specified AP. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not + supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + the specified AP has finished. + @retval EFI_NOT_READY The specified AP is busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +MpInitLibStartupThisAP ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT BOOLEAN *Finished OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // temporarily stop checkAllApsStatus for avoid resource dead-lock. + // + mStopCheckAllApsStatus = TRUE; + + Status = StartupThisAPWorker ( + Procedure, + ProcessorNumber, + WaitEvent, + TimeoutInMicroseconds, + ProcedureArgument, + Finished + ); + + mStopCheckAllApsStatus = FALSE; + + return Status; +} + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + @param[in] ProcessorNumber The handle number of AP that is to become the new + BSP. The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an + enabled AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to + this service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or + a disabled AP. + @retval EFI_NOT_READY The specified AP is busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibSwitchBSP ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + EFI_STATUS Status; + EFI_TIMER_ARCH_PROTOCOL *Timer; + UINT64 TimerPeriod; + + TimerPeriod = 0; + // + // Locate Timer Arch Protocol + // + Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &Timer); + if (EFI_ERROR (Status)) { + Timer = NULL; + } + + if (Timer != NULL) { + // + // Save current rate of DXE Timer + // + Timer->GetTimerPeriod (Timer, &TimerPeriod); + // + // Disable DXE Timer and drain pending interrupts + // + Timer->SetTimerPeriod (Timer, 0); + } + + Status = SwitchBSPWorker (ProcessorNumber, EnableOldBSP); + + if (Timer != NULL) { + // + // Enable and restore rate of DXE Timer + // + Timer->SetTimerPeriod (Timer, TimerPeriod); + } + + return Status; +} + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of AP. + The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for + enabled, FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies + the new health status of the AP. This flag + corresponds to StatusFlag defined in + EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only + the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter + is ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed + prior to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibEnableDisableAP ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + EFI_STATUS Status; + BOOLEAN TempStopCheckState; + + TempStopCheckState = FALSE; + // + // temporarily stop checkAllAPsStatus for initialize parameters. + // + if (!mStopCheckAllApsStatus) { + mStopCheckAllApsStatus = TRUE; + TempStopCheckState = TRUE; + } + + Status = EnableDisableApWorker (ProcessorNumber, EnableAP, HealthFlag); + + if (TempStopCheckState) { + mStopCheckAllApsStatus = FALSE; + } + + return Status; +} + +/** + This funtion will try to invoke platform specific microcode shadow logic to + relocate microcode update patches into memory. + + @param[in, out] CpuMpData The pointer to CPU MP Data structure. + + @retval EFI_SUCCESS Shadow microcode success. + @retval EFI_OUT_OF_RESOURCES No enough resource to complete the operation. + @retval EFI_UNSUPPORTED Can't find platform specific microcode shadow + PPI/Protocol. +**/ +EFI_STATUS +PlatformShadowMicrocode ( + IN OUT CPU_MP_DATA *CpuMpData + ) +{ + // + // There is no DXE version of platform shadow microcode protocol so far. + // A platform which only uses DxeMpInitLib instance could only supports + // the PCD based microcode shadowing. + // + return EFI_UNSUPPORTED; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm new file mode 100644 index 00000000..352c7b3d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm @@ -0,0 +1,348 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; MpFuncs.nasm +; +; Abstract: +; +; This is the assembly code for MP support +; +;------------------------------------------------------------------------------- + +%include "MpEqu.inc" +extern ASM_PFX(InitializeFloatingPointUnits) + +SECTION .text + +;------------------------------------------------------------------------------------- +;RendezvousFunnelProc procedure follows. All APs execute their procedure. This +;procedure serializes all the AP processors through an Init sequence. It must be +;noted that APs arrive here very raw...ie: real mode, no stack. +;ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC +;IS IN MACHINE CODE. +;------------------------------------------------------------------------------------- +global ASM_PFX(RendezvousFunnelProc) +ASM_PFX(RendezvousFunnelProc): +RendezvousFunnelProcStart: +; At this point CS = 0x(vv00) and ip= 0x0. +BITS 16 + mov ebp, eax ; save BIST information + + mov ax, cs + mov ds, ax + mov es, ax + mov ss, ax + xor ax, ax + mov fs, ax + mov gs, ax + + mov si, MP_CPU_EXCHANGE_INFO_FIELD (BufferStart) + mov ebx, [si] + + mov si, MP_CPU_EXCHANGE_INFO_FIELD (DataSegment) + mov edx, [si] + + ; + ; Get start address of 32-bit code in low memory (<1MB) + ; + mov edi, MP_CPU_EXCHANGE_INFO_FIELD (ModeTransitionMemory) + + mov si, MP_CPU_EXCHANGE_INFO_FIELD (GdtrProfile) +o32 lgdt [cs:si] + + mov si, MP_CPU_EXCHANGE_INFO_FIELD (IdtrProfile) +o32 lidt [cs:si] + + ; + ; Switch to protected mode + ; + mov eax, cr0 ; Get control register 0 + or eax, 000000003h ; Set PE bit (bit #0) & MP + mov cr0, eax + + ; Switch to 32-bit code in executable memory (>1MB) +o32 jmp far [cs:di] + +; +; Following code may be copied to memory with type of EfiBootServicesCode. +; This is required at DXE phase if NX is enabled for EfiBootServicesCode of +; memory. +; +BITS 32 +Flat32Start: ; protected mode entry point + mov ds, dx + mov es, dx + mov fs, dx + mov gs, dx + mov ss, dx + + mov esi, ebx + + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (EnableExecuteDisable) + cmp byte [edi], 0 + jz SkipEnableExecuteDisable + + ; + ; Enable IA32 PAE execute disable + ; + + mov ecx, 0xc0000080 + rdmsr + bts eax, 11 + wrmsr + + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (Cr3) + mov eax, dword [edi] + mov cr3, eax + + mov eax, cr4 + bts eax, 5 + mov cr4, eax + + mov eax, cr0 + bts eax, 31 + mov cr0, eax + +SkipEnableExecuteDisable: + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (InitFlag) + cmp dword [edi], 1 ; 1 == ApInitConfig + jnz GetApicId + + ; Increment the number of APs executing here as early as possible + ; This is decremented in C code when AP is finished executing + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (NumApsExecuting) + lock inc dword [edi] + + ; AP init + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (ApIndex) + mov ebx, 1 + lock xadd dword [edi], ebx ; EBX = ApIndex++ + inc ebx ; EBX is CpuNumber + + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (StackSize) + mov eax, [edi] + mov ecx, ebx + inc ecx + mul ecx ; EAX = StackSize * (CpuNumber + 1) + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (StackStart) + add eax, [edi] + mov esp, eax + jmp CProcedureInvoke + +GetApicId: + mov eax, 0 + cpuid + cmp eax, 0bh + jb NoX2Apic ; CPUID level below CPUID_EXTENDED_TOPOLOGY + + mov eax, 0bh + xor ecx, ecx + cpuid + test ebx, 0ffffh + jz NoX2Apic ; CPUID.0BH:EBX[15:0] is zero + + ; Processor is x2APIC capable; 32-bit x2APIC ID is already in EDX + jmp GetProcessorNumber + +NoX2Apic: + ; Processor is not x2APIC capable, so get 8-bit APIC ID + mov eax, 1 + cpuid + shr ebx, 24 + mov edx, ebx + +GetProcessorNumber: + ; + ; Get processor number for this AP + ; Note that BSP may become an AP due to SwitchBsp() + ; + xor ebx, ebx + lea eax, [esi + MP_CPU_EXCHANGE_INFO_FIELD (CpuInfo)] + mov edi, [eax] + +GetNextProcNumber: + cmp dword [edi + CPU_INFO_IN_HOB.InitialApicId], edx ; APIC ID match? + jz ProgramStack + add edi, CPU_INFO_IN_HOB_size + inc ebx + jmp GetNextProcNumber + +ProgramStack: + mov esp, dword [edi + CPU_INFO_IN_HOB.ApTopOfStack] + +CProcedureInvoke: + push ebp ; push BIST data at top of AP stack + xor ebp, ebp ; clear ebp for call stack trace + push ebp + mov ebp, esp + + mov eax, ASM_PFX(InitializeFloatingPointUnits) + call eax ; Call assembly function to initialize FPU per UEFI spec + + push ebx ; Push ApIndex + mov eax, esi + add eax, MP_CPU_EXCHANGE_INFO_OFFSET + push eax ; push address of exchange info data buffer + + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (CFunction) + mov eax, [edi] + + call eax ; Invoke C function + + jmp $ ; Never reach here +RendezvousFunnelProcEnd: + +;------------------------------------------------------------------------------------- +;SwitchToRealProc procedure follows. +;NOT USED IN 32 BIT MODE. +;------------------------------------------------------------------------------------- +global ASM_PFX(SwitchToRealProc) +ASM_PFX(SwitchToRealProc): +SwitchToRealProcStart: + jmp $ ; Never reach here +SwitchToRealProcEnd: + +;------------------------------------------------------------------------------------- +; AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish, Pm16CodeSegment, SevEsAPJumpTable, WakeupBuffer); +; +; The last three parameters (Pm16CodeSegment, SevEsAPJumpTable and WakeupBuffer) are +; specific to SEV-ES support and are not applicable on IA32. +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmRelocateApLoop) +ASM_PFX(AsmRelocateApLoop): +AsmRelocateApLoopStart: + mov eax, esp + mov esp, [eax + 16] ; TopOfApStack + push dword [eax] ; push return address for stack trace + push ebp + mov ebp, esp + mov ebx, [eax + 8] ; ApTargetCState + mov ecx, [eax + 4] ; MwaitSupport + mov eax, [eax + 20] ; CountTofinish + lock dec dword [eax] ; (*CountTofinish)-- + cmp cl, 1 ; Check mwait-monitor support + jnz HltLoop +MwaitLoop: + cli + mov eax, esp + xor ecx, ecx + xor edx, edx + monitor + mov eax, ebx ; Mwait Cx, Target C-State per eax[7:4] + shl eax, 4 + mwait + jmp MwaitLoop +HltLoop: + cli + hlt + jmp HltLoop +AsmRelocateApLoopEnd: + +;------------------------------------------------------------------------------------- +; AsmGetAddressMap (&AddressMap); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmGetAddressMap) +ASM_PFX(AsmGetAddressMap): + pushad + mov ebp,esp + + mov ebx, [ebp + 24h] + mov dword [ebx + MP_ASSEMBLY_ADDRESS_MAP.RendezvousFunnelAddress], RendezvousFunnelProcStart + mov dword [ebx + MP_ASSEMBLY_ADDRESS_MAP.ModeEntryOffset], Flat32Start - RendezvousFunnelProcStart + mov dword [ebx + MP_ASSEMBLY_ADDRESS_MAP.RendezvousFunnelSize], RendezvousFunnelProcEnd - RendezvousFunnelProcStart + mov dword [ebx + MP_ASSEMBLY_ADDRESS_MAP.RelocateApLoopFuncAddress], AsmRelocateApLoopStart + mov dword [ebx + MP_ASSEMBLY_ADDRESS_MAP.RelocateApLoopFuncSize], AsmRelocateApLoopEnd - AsmRelocateApLoopStart + mov dword [ebx + MP_ASSEMBLY_ADDRESS_MAP.ModeTransitionOffset], Flat32Start - RendezvousFunnelProcStart + mov dword [ebx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealSize], SwitchToRealProcEnd - SwitchToRealProcStart + mov dword [ebx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealOffset], SwitchToRealProcStart - RendezvousFunnelProcStart + mov dword [ebx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealNoNxOffset], SwitchToRealProcStart - Flat32Start + mov dword [ebx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealPM16ModeOffset], 0 + mov dword [ebx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealPM16ModeSize], 0 + + popad + ret + +;------------------------------------------------------------------------------------- +;AsmExchangeRole procedure follows. This procedure executed by current BSP, that is +;about to become an AP. It switches it'stack with the current AP. +;AsmExchangeRole (IN CPU_EXCHANGE_INFO *MyInfo, IN CPU_EXCHANGE_INFO *OthersInfo); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmExchangeRole) +ASM_PFX(AsmExchangeRole): + ; DO NOT call other functions in this function, since 2 CPU may use 1 stack + ; at the same time. If 1 CPU try to call a function, stack will be corrupted. + pushad + mov ebp,esp + + ; esi contains MyInfo pointer + mov esi, [ebp + 24h] + + ; edi contains OthersInfo pointer + mov edi, [ebp + 28h] + + ;Store EFLAGS, GDTR and IDTR register to stack + pushfd + mov eax, cr4 + push eax ; push cr4 firstly + mov eax, cr0 + push eax + + sgdt [esi + CPU_EXCHANGE_ROLE_INFO.Gdtr] + sidt [esi + CPU_EXCHANGE_ROLE_INFO.Idtr] + + ; Store the its StackPointer + mov [esi + CPU_EXCHANGE_ROLE_INFO.StackPointer],esp + + ; update its switch state to STORED + mov byte [esi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_STORED + +WaitForOtherStored: + ; wait until the other CPU finish storing its state + cmp byte [edi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_STORED + jz OtherStored + pause + jmp WaitForOtherStored + +OtherStored: + ; Since another CPU already stored its state, load them + ; load GDTR value + lgdt [edi + CPU_EXCHANGE_ROLE_INFO.Gdtr] + + ; load IDTR value + lidt [edi + CPU_EXCHANGE_ROLE_INFO.Idtr] + + ; load its future StackPointer + mov esp, [edi + CPU_EXCHANGE_ROLE_INFO.StackPointer] + + ; update the other CPU's switch state to LOADED + mov byte [edi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_LOADED + +WaitForOtherLoaded: + ; wait until the other CPU finish loading new state, + ; otherwise the data in stack may corrupt + cmp byte [esi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_LOADED + jz OtherLoaded + pause + jmp WaitForOtherLoaded + +OtherLoaded: + ; since the other CPU already get the data it want, leave this procedure + pop eax + mov cr0, eax + pop eax + mov cr4, eax + popfd + + popad + ret diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/Microcode.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/Microcode.c new file mode 100644 index 00000000..a1043050 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/Microcode.c @@ -0,0 +1,374 @@ +/** @file + Implementation of loading microcode on processors. + + Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "MpLib.h" + +/** + Detect whether specified processor can find matching microcode patch and load it. + + @param[in] CpuMpData The pointer to CPU MP Data structure. + @param[in] ProcessorNumber The handle number of the processor. The range is + from 0 to the total number of logical processors + minus 1. +**/ +VOID +MicrocodeDetect ( + IN CPU_MP_DATA *CpuMpData, + IN UINTN ProcessorNumber + ) +{ + CPU_MICROCODE_HEADER *Microcode; + UINTN MicrocodeEnd; + CPU_AP_DATA *BspData; + UINT32 LatestRevision; + CPU_MICROCODE_HEADER *LatestMicrocode; + UINT32 ThreadId; + EDKII_PEI_MICROCODE_CPU_ID MicrocodeCpuId; + + if (CpuMpData->MicrocodePatchRegionSize == 0) { + // + // There is no microcode patches + // + return; + } + + GetProcessorLocationByApicId (GetInitialApicId (), NULL, NULL, &ThreadId); + if (ThreadId != 0) { + // + // Skip loading microcode if it is not the first thread in one core. + // + return; + } + + GetProcessorMicrocodeCpuId (&MicrocodeCpuId); + + if (ProcessorNumber != (UINTN) CpuMpData->BspNumber) { + // + // Direct use microcode of BSP if AP is the same as BSP. + // Assume BSP calls this routine() before AP. + // + BspData = &(CpuMpData->CpuData[CpuMpData->BspNumber]); + if ((BspData->ProcessorSignature == MicrocodeCpuId.ProcessorSignature) && + (BspData->PlatformId == MicrocodeCpuId.PlatformId) && + (BspData->MicrocodeEntryAddr != 0)) { + LatestMicrocode = (CPU_MICROCODE_HEADER *)(UINTN) BspData->MicrocodeEntryAddr; + LatestRevision = LatestMicrocode->UpdateRevision; + goto LoadMicrocode; + } + } + + // + // BSP or AP which is different from BSP runs here + // Use 0 as the starting revision to search for microcode because MicrocodePatchInfo HOB needs + // the latest microcode location even it's loaded to the processor. + // + LatestRevision = 0; + LatestMicrocode = NULL; + Microcode = (CPU_MICROCODE_HEADER *) (UINTN) CpuMpData->MicrocodePatchAddress; + MicrocodeEnd = (UINTN) Microcode + (UINTN) CpuMpData->MicrocodePatchRegionSize; + + do { + if (!IsValidMicrocode (Microcode, MicrocodeEnd - (UINTN) Microcode, LatestRevision, &MicrocodeCpuId, 1, TRUE)) { + // + // It is the padding data between the microcode patches for microcode patches alignment. + // Because the microcode patch is the multiple of 1-KByte, the padding data should not + // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode + // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to + // find the next possible microcode patch header. + // + Microcode = (CPU_MICROCODE_HEADER *) ((UINTN) Microcode + SIZE_1KB); + continue; + } + LatestMicrocode = Microcode; + LatestRevision = LatestMicrocode->UpdateRevision; + + Microcode = (CPU_MICROCODE_HEADER *) (((UINTN) Microcode) + GetMicrocodeLength (Microcode)); + } while ((UINTN) Microcode < MicrocodeEnd); + +LoadMicrocode: + if (LatestRevision != 0) { + // + // Save the detected microcode patch entry address (including the microcode + // patch header) for each processor even it's the same as the loaded one. + // It will be used when building the microcode patch cache HOB. + // + CpuMpData->CpuData[ProcessorNumber].MicrocodeEntryAddr = (UINTN) LatestMicrocode; + } + + if (LatestRevision > GetProcessorMicrocodeSignature ()) { + // + // BIOS only authenticate updates that contain a numerically larger revision + // than the currently loaded revision, where Current Signature < New Update + // Revision. A processor with no loaded update is considered to have a + // revision equal to zero. + // + LoadMicrocode (LatestMicrocode); + } + // + // It's possible that the microcode fails to load. Just capture the CPU microcode revision after loading. + // + CpuMpData->CpuData[ProcessorNumber].MicrocodeRevision = GetProcessorMicrocodeSignature (); +} + +/** + Actual worker function that shadows the required microcode patches into memory. + + @param[in, out] CpuMpData The pointer to CPU MP Data structure. + @param[in] Patches The pointer to an array of information on + the microcode patches that will be loaded + into memory. + @param[in] PatchCount The number of microcode patches that will + be loaded into memory. + @param[in] TotalLoadSize The total size of all the microcode patches + to be loaded. +**/ +VOID +ShadowMicrocodePatchWorker ( + IN OUT CPU_MP_DATA *CpuMpData, + IN MICROCODE_PATCH_INFO *Patches, + IN UINTN PatchCount, + IN UINTN TotalLoadSize + ) +{ + UINTN Index; + VOID *MicrocodePatchInRam; + UINT8 *Walker; + + ASSERT ((Patches != NULL) && (PatchCount != 0)); + + MicrocodePatchInRam = AllocatePages (EFI_SIZE_TO_PAGES (TotalLoadSize)); + if (MicrocodePatchInRam == NULL) { + return; + } + + // + // Load all the required microcode patches into memory + // + for (Walker = MicrocodePatchInRam, Index = 0; Index < PatchCount; Index++) { + CopyMem ( + Walker, + (VOID *) Patches[Index].Address, + Patches[Index].Size + ); + Walker += Patches[Index].Size; + } + + // + // Update the microcode patch related fields in CpuMpData + // + CpuMpData->MicrocodePatchAddress = (UINTN) MicrocodePatchInRam; + CpuMpData->MicrocodePatchRegionSize = TotalLoadSize; + + DEBUG (( + DEBUG_INFO, + "%a: Required microcode patches have been loaded at 0x%lx, with size 0x%lx.\n", + __FUNCTION__, CpuMpData->MicrocodePatchAddress, CpuMpData->MicrocodePatchRegionSize + )); + + return; +} + +/** + Shadow the required microcode patches data into memory according to PCD + PcdCpuMicrocodePatchAddress and PcdCpuMicrocodePatchRegionSize. + + @param[in, out] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +ShadowMicrocodePatchByPcd ( + IN OUT CPU_MP_DATA *CpuMpData + ) +{ + UINTN Index; + CPU_MICROCODE_HEADER *MicrocodeEntryPoint; + UINTN MicrocodeEnd; + UINTN TotalSize; + MICROCODE_PATCH_INFO *PatchInfoBuffer; + UINTN MaxPatchNumber; + UINTN PatchCount; + UINTN TotalLoadSize; + EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuIds; + BOOLEAN Valid; + + // + // Initialize the microcode patch related fields in CpuMpData as the values + // specified by the PCD pair. If the microcode patches are loaded into memory, + // these fields will be updated. + // + CpuMpData->MicrocodePatchAddress = PcdGet64 (PcdCpuMicrocodePatchAddress); + CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize); + + MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) CpuMpData->MicrocodePatchAddress; + MicrocodeEnd = (UINTN) MicrocodeEntryPoint + + (UINTN) CpuMpData->MicrocodePatchRegionSize; + if ((MicrocodeEntryPoint == NULL) || ((UINTN) MicrocodeEntryPoint == MicrocodeEnd)) { + // + // There is no microcode patches + // + return; + } + + PatchCount = 0; + MaxPatchNumber = DEFAULT_MAX_MICROCODE_PATCH_NUM; + TotalLoadSize = 0; + PatchInfoBuffer = AllocatePool (MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO)); + if (PatchInfoBuffer == NULL) { + return; + } + + MicrocodeCpuIds = AllocatePages ( + EFI_SIZE_TO_PAGES (CpuMpData->CpuCount * sizeof (EDKII_PEI_MICROCODE_CPU_ID)) + ); + if (MicrocodeCpuIds == NULL) { + FreePool (PatchInfoBuffer); + return; + } + + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + MicrocodeCpuIds[Index].PlatformId = CpuMpData->CpuData[Index].PlatformId; + MicrocodeCpuIds[Index].ProcessorSignature = CpuMpData->CpuData[Index].ProcessorSignature; + } + + // + // Process the header of each microcode patch within the region. + // The purpose is to decide which microcode patch(es) will be loaded into memory. + // Microcode checksum is not verified because it's slow when performing on flash. + // + do { + Valid = IsValidMicrocode ( + MicrocodeEntryPoint, + MicrocodeEnd - (UINTN) MicrocodeEntryPoint, + 0, + MicrocodeCpuIds, + CpuMpData->CpuCount, + FALSE + ); + if (!Valid) { + // + // Padding data between the microcode patches, skip 1KB to check next entry. + // + MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB); + continue; + } + + PatchCount++; + if (PatchCount > MaxPatchNumber) { + // + // Current 'PatchInfoBuffer' cannot hold the information, double the size + // and allocate a new buffer. + // + if (MaxPatchNumber > MAX_UINTN / 2 / sizeof (MICROCODE_PATCH_INFO)) { + // + // Overflow check for MaxPatchNumber + // + goto OnExit; + } + + PatchInfoBuffer = ReallocatePool ( + MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO), + 2 * MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO), + PatchInfoBuffer + ); + if (PatchInfoBuffer == NULL) { + goto OnExit; + } + MaxPatchNumber = MaxPatchNumber * 2; + } + + TotalSize = GetMicrocodeLength (MicrocodeEntryPoint); + + // + // Store the information of this microcode patch + // + PatchInfoBuffer[PatchCount - 1].Address = (UINTN) MicrocodeEntryPoint; + PatchInfoBuffer[PatchCount - 1].Size = TotalSize; + TotalLoadSize += TotalSize; + + // + // Process the next microcode patch + // + MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) ((UINTN) MicrocodeEntryPoint + TotalSize); + } while ((UINTN) MicrocodeEntryPoint < MicrocodeEnd); + + if (PatchCount != 0) { + DEBUG (( + DEBUG_INFO, + "%a: 0x%x microcode patches will be loaded into memory, with size 0x%x.\n", + __FUNCTION__, PatchCount, TotalLoadSize + )); + + ShadowMicrocodePatchWorker (CpuMpData, PatchInfoBuffer, PatchCount, TotalLoadSize); + } + +OnExit: + if (PatchInfoBuffer != NULL) { + FreePool (PatchInfoBuffer); + } + FreePages (MicrocodeCpuIds, EFI_SIZE_TO_PAGES (CpuMpData->CpuCount * sizeof (EDKII_PEI_MICROCODE_CPU_ID))); +} + +/** + Shadow the required microcode patches data into memory. + + @param[in, out] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +ShadowMicrocodeUpdatePatch ( + IN OUT CPU_MP_DATA *CpuMpData + ) +{ + EFI_STATUS Status; + + Status = PlatformShadowMicrocode (CpuMpData); + if (EFI_ERROR (Status)) { + ShadowMicrocodePatchByPcd (CpuMpData); + } +} + +/** + Get the cached microcode patch base address and size from the microcode patch + information cache HOB. + + @param[out] Address Base address of the microcode patches data. + It will be updated if the microcode patch + information cache HOB is found. + @param[out] RegionSize Size of the microcode patches data. + It will be updated if the microcode patch + information cache HOB is found. + + @retval TRUE The microcode patch information cache HOB is found. + @retval FALSE The microcode patch information cache HOB is not found. + +**/ +BOOLEAN +GetMicrocodePatchInfoFromHob ( + UINT64 *Address, + UINT64 *RegionSize + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + EDKII_MICROCODE_PATCH_HOB *MicrocodePathHob; + + GuidHob = GetFirstGuidHob (&gEdkiiMicrocodePatchHobGuid); + if (GuidHob == NULL) { + DEBUG((DEBUG_INFO, "%a: Microcode patch cache HOB is not found.\n", __FUNCTION__)); + return FALSE; + } + + MicrocodePathHob = GET_GUID_HOB_DATA (GuidHob); + + *Address = MicrocodePathHob->MicrocodePatchAddress; + *RegionSize = MicrocodePathHob->MicrocodePatchRegionSize; + + DEBUG(( + DEBUG_INFO, "%a: MicrocodeBase = 0x%lx, MicrocodeSize = 0x%lx\n", + __FUNCTION__, *Address, *RegionSize + )); + + return TRUE; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/MpEqu.inc b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/MpEqu.inc new file mode 100644 index 00000000..b2e5d27f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/MpEqu.inc @@ -0,0 +1,99 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; MpEqu.inc +; +; Abstract: +; +; This is the equates file for Multiple Processor support +; +;------------------------------------------------------------------------------- +%include "Nasm.inc" + +CPU_SWITCH_STATE_IDLE equ 0 +CPU_SWITCH_STATE_STORED equ 1 +CPU_SWITCH_STATE_LOADED equ 2 + +; +; Equivalent NASM structure of MP_ASSEMBLY_ADDRESS_MAP +; +struc MP_ASSEMBLY_ADDRESS_MAP + .RendezvousFunnelAddress CTYPE_UINTN 1 + .ModeEntryOffset CTYPE_UINTN 1 + .RendezvousFunnelSize CTYPE_UINTN 1 + .RelocateApLoopFuncAddress CTYPE_UINTN 1 + .RelocateApLoopFuncSize CTYPE_UINTN 1 + .ModeTransitionOffset CTYPE_UINTN 1 + .SwitchToRealSize CTYPE_UINTN 1 + .SwitchToRealOffset CTYPE_UINTN 1 + .SwitchToRealNoNxOffset CTYPE_UINTN 1 + .SwitchToRealPM16ModeOffset CTYPE_UINTN 1 + .SwitchToRealPM16ModeSize CTYPE_UINTN 1 +endstruc + +; +; Equivalent NASM structure of IA32_DESCRIPTOR +; +struc IA32_DESCRIPTOR + .Limit CTYPE_UINT16 1 + .Base CTYPE_UINTN 1 +endstruc + +; +; Equivalent NASM structure of CPU_EXCHANGE_ROLE_INFO +; +struc CPU_EXCHANGE_ROLE_INFO + ; State is defined as UINT8 in C header file + ; Define it as UINTN here to guarantee the fields that follow State + ; is naturally aligned. The structure layout doesn't change. + .State CTYPE_UINTN 1 + .StackPointer CTYPE_UINTN 1 + .Gdtr CTYPE_UINT8 IA32_DESCRIPTOR_size + .Idtr CTYPE_UINT8 IA32_DESCRIPTOR_size +endstruc + +; +; Equivalent NASM structure of CPU_INFO_IN_HOB +; +struc CPU_INFO_IN_HOB + .InitialApicId CTYPE_UINT32 1 + .ApicId CTYPE_UINT32 1 + .Health CTYPE_UINT32 1 + .ApTopOfStack CTYPE_UINT64 1 +endstruc + +; +; Equivalent NASM structure of MP_CPU_EXCHANGE_INFO +; +struc MP_CPU_EXCHANGE_INFO + .StackStart: CTYPE_UINTN 1 + .StackSize: CTYPE_UINTN 1 + .CFunction: CTYPE_UINTN 1 + .GdtrProfile: CTYPE_UINT8 IA32_DESCRIPTOR_size + .IdtrProfile: CTYPE_UINT8 IA32_DESCRIPTOR_size + .BufferStart: CTYPE_UINTN 1 + .ModeOffset: CTYPE_UINTN 1 + .ApIndex: CTYPE_UINTN 1 + .CodeSegment: CTYPE_UINTN 1 + .DataSegment: CTYPE_UINTN 1 + .EnableExecuteDisable: CTYPE_UINTN 1 + .Cr3: CTYPE_UINTN 1 + .InitFlag: CTYPE_UINTN 1 + .CpuInfo: CTYPE_UINTN 1 + .NumApsExecuting: CTYPE_UINTN 1 + .CpuMpData: CTYPE_UINTN 1 + .InitializeFloatingPointUnits: CTYPE_UINTN 1 + .ModeTransitionMemory: CTYPE_UINT32 1 + .ModeTransitionSegment: CTYPE_UINT16 1 + .ModeHighMemory: CTYPE_UINT32 1 + .ModeHighSegment: CTYPE_UINT16 1 + .Enable5LevelPaging: CTYPE_BOOLEAN 1 + .SevEsIsEnabled: CTYPE_BOOLEAN 1 + .GhcbBase: CTYPE_UINTN 1 +endstruc + +MP_CPU_EXCHANGE_INFO_OFFSET equ (SwitchToRealProcEnd - RendezvousFunnelProcStart) +%define MP_CPU_EXCHANGE_INFO_FIELD(Field) (MP_CPU_EXCHANGE_INFO_OFFSET + MP_CPU_EXCHANGE_INFO. %+ Field) diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/MpLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/MpLib.c new file mode 100644 index 00000000..630d404f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/MpLib.c @@ -0,0 +1,2938 @@ +/** @file + CPU MP Initialize Library common functions. + + Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.<BR> + Copyright (c) 2020, AMD Inc. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "MpLib.h" +#include <Library/VmgExitLib.h> +#include <Register/Amd/Fam17Msr.h> +#include <Register/Amd/Ghcb.h> +#ifdef VBOX +# include <Library/IoLib.h> +# include "../../../../DevEFI.h" +#endif + +EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID; + + +/** + The function will check if BSP Execute Disable is enabled. + + DxeIpl may have enabled Execute Disable for BSP, APs need to + get the status and sync up the settings. + If BSP's CR0.Paging is not set, BSP execute Disble feature is + not working actually. + + @retval TRUE BSP Execute Disable is enabled. + @retval FALSE BSP Execute Disable is not enabled. +**/ +BOOLEAN +IsBspExecuteDisableEnabled ( + VOID + ) +{ + UINT32 Eax; + CPUID_EXTENDED_CPU_SIG_EDX Edx; + MSR_IA32_EFER_REGISTER EferMsr; + BOOLEAN Enabled; + IA32_CR0 Cr0; + + Enabled = FALSE; + Cr0.UintN = AsmReadCr0 (); + if (Cr0.Bits.PG != 0) { + // + // If CR0 Paging bit is set + // + AsmCpuid (CPUID_EXTENDED_FUNCTION, &Eax, NULL, NULL, NULL); + if (Eax >= CPUID_EXTENDED_CPU_SIG) { + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &Edx.Uint32); + // + // CPUID 0x80000001 + // Bit 20: Execute Disable Bit available. + // + if (Edx.Bits.NX != 0) { + EferMsr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER); + // + // MSR 0xC0000080 + // Bit 11: Execute Disable Bit enable. + // + if (EferMsr.Bits.NXE != 0) { + Enabled = TRUE; + } + } + } + } + + return Enabled; +} + +/** + Worker function for SwitchBSP(). + + Worker function for SwitchBSP(), assigned to the AP which is intended + to become BSP. + + @param[in] Buffer Pointer to CPU MP Data +**/ +VOID +EFIAPI +FutureBSPProc ( + IN VOID *Buffer + ) +{ + CPU_MP_DATA *DataInHob; + + DataInHob = (CPU_MP_DATA *) Buffer; + AsmExchangeRole (&DataInHob->APInfo, &DataInHob->BSPInfo); +} + +/** + Get the Application Processors state. + + @param[in] CpuData The pointer to CPU_AP_DATA of specified AP + + @return The AP status +**/ +CPU_STATE +GetApState ( + IN CPU_AP_DATA *CpuData + ) +{ + return CpuData->State; +} + +/** + Set the Application Processors state. + + @param[in] CpuData The pointer to CPU_AP_DATA of specified AP + @param[in] State The AP status +**/ +VOID +SetApState ( + IN CPU_AP_DATA *CpuData, + IN CPU_STATE State + ) +{ + AcquireSpinLock (&CpuData->ApLock); + CpuData->State = State; + ReleaseSpinLock (&CpuData->ApLock); +} + +/** + Save BSP's local APIC timer setting. + + @param[in] CpuMpData Pointer to CPU MP Data +**/ +VOID +SaveLocalApicTimerSetting ( + IN CPU_MP_DATA *CpuMpData + ) +{ + // + // Record the current local APIC timer setting of BSP + // + GetApicTimerState ( + &CpuMpData->DivideValue, + &CpuMpData->PeriodicMode, + &CpuMpData->Vector + ); + CpuMpData->CurrentTimerCount = GetApicTimerCurrentCount (); + CpuMpData->TimerInterruptState = GetApicTimerInterruptState (); +} + +/** + Sync local APIC timer setting from BSP to AP. + + @param[in] CpuMpData Pointer to CPU MP Data +**/ +VOID +SyncLocalApicTimerSetting ( + IN CPU_MP_DATA *CpuMpData + ) +{ + // + // Sync local APIC timer setting from BSP to AP + // + InitializeApicTimer ( + CpuMpData->DivideValue, + CpuMpData->CurrentTimerCount, + CpuMpData->PeriodicMode, + CpuMpData->Vector + ); + // + // Disable AP's local APIC timer interrupt + // + DisableApicTimerInterrupt (); +} + +/** + Save the volatile registers required to be restored following INIT IPI. + + @param[out] VolatileRegisters Returns buffer saved the volatile resisters +**/ +VOID +SaveVolatileRegisters ( + OUT CPU_VOLATILE_REGISTERS *VolatileRegisters + ) +{ + CPUID_VERSION_INFO_EDX VersionInfoEdx; + + VolatileRegisters->Cr0 = AsmReadCr0 (); + VolatileRegisters->Cr3 = AsmReadCr3 (); + VolatileRegisters->Cr4 = AsmReadCr4 (); + + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32); + if (VersionInfoEdx.Bits.DE != 0) { + // + // If processor supports Debugging Extensions feature + // by CPUID.[EAX=01H]:EDX.BIT2 + // + VolatileRegisters->Dr0 = AsmReadDr0 (); + VolatileRegisters->Dr1 = AsmReadDr1 (); + VolatileRegisters->Dr2 = AsmReadDr2 (); + VolatileRegisters->Dr3 = AsmReadDr3 (); + VolatileRegisters->Dr6 = AsmReadDr6 (); + VolatileRegisters->Dr7 = AsmReadDr7 (); + } + + AsmReadGdtr (&VolatileRegisters->Gdtr); + AsmReadIdtr (&VolatileRegisters->Idtr); + VolatileRegisters->Tr = AsmReadTr (); +} + +/** + Restore the volatile registers following INIT IPI. + + @param[in] VolatileRegisters Pointer to volatile resisters + @param[in] IsRestoreDr TRUE: Restore DRx if supported + FALSE: Do not restore DRx +**/ +VOID +RestoreVolatileRegisters ( + IN CPU_VOLATILE_REGISTERS *VolatileRegisters, + IN BOOLEAN IsRestoreDr + ) +{ + CPUID_VERSION_INFO_EDX VersionInfoEdx; + IA32_TSS_DESCRIPTOR *Tss; + + AsmWriteCr3 (VolatileRegisters->Cr3); + AsmWriteCr4 (VolatileRegisters->Cr4); + AsmWriteCr0 (VolatileRegisters->Cr0); + + if (IsRestoreDr) { + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32); + if (VersionInfoEdx.Bits.DE != 0) { + // + // If processor supports Debugging Extensions feature + // by CPUID.[EAX=01H]:EDX.BIT2 + // + AsmWriteDr0 (VolatileRegisters->Dr0); + AsmWriteDr1 (VolatileRegisters->Dr1); + AsmWriteDr2 (VolatileRegisters->Dr2); + AsmWriteDr3 (VolatileRegisters->Dr3); + AsmWriteDr6 (VolatileRegisters->Dr6); + AsmWriteDr7 (VolatileRegisters->Dr7); + } + } + + AsmWriteGdtr (&VolatileRegisters->Gdtr); + AsmWriteIdtr (&VolatileRegisters->Idtr); + if (VolatileRegisters->Tr != 0 && + VolatileRegisters->Tr < VolatileRegisters->Gdtr.Limit) { + Tss = (IA32_TSS_DESCRIPTOR *)(VolatileRegisters->Gdtr.Base + + VolatileRegisters->Tr); + if (Tss->Bits.P == 1) { + Tss->Bits.Type &= 0xD; // 1101 - Clear busy bit just in case + AsmWriteTr (VolatileRegisters->Tr); + } + } +} + +/** + Detect whether Mwait-monitor feature is supported. + + @retval TRUE Mwait-monitor feature is supported. + @retval FALSE Mwait-monitor feature is not supported. +**/ +BOOLEAN +IsMwaitSupport ( + VOID + ) +{ + CPUID_VERSION_INFO_ECX VersionInfoEcx; + + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &VersionInfoEcx.Uint32, NULL); + return (VersionInfoEcx.Bits.MONITOR == 1) ? TRUE : FALSE; +} + +/** + Get AP loop mode. + + @param[out] MonitorFilterSize Returns the largest monitor-line size in bytes. + + @return The AP loop mode. +**/ +UINT8 +GetApLoopMode ( + OUT UINT32 *MonitorFilterSize + ) +{ + UINT8 ApLoopMode; + CPUID_MONITOR_MWAIT_EBX MonitorMwaitEbx; + + ASSERT (MonitorFilterSize != NULL); + + ApLoopMode = PcdGet8 (PcdCpuApLoopMode); + ASSERT (ApLoopMode >= ApInHltLoop && ApLoopMode <= ApInRunLoop); + if (ApLoopMode == ApInMwaitLoop) { + if (!IsMwaitSupport ()) { + // + // If processor does not support MONITOR/MWAIT feature, + // force AP in Hlt-loop mode + // + ApLoopMode = ApInHltLoop; + } + + if (PcdGetBool (PcdSevEsIsEnabled)) { + // + // For SEV-ES, force AP in Hlt-loop mode in order to use the GHCB + // protocol for starting APs + // + ApLoopMode = ApInHltLoop; + } + } + + if (ApLoopMode != ApInMwaitLoop) { + *MonitorFilterSize = sizeof (UINT32); + } else { + // + // CPUID.[EAX=05H]:EBX.BIT0-15: Largest monitor-line size in bytes + // CPUID.[EAX=05H].EDX: C-states supported using MWAIT + // + AsmCpuid (CPUID_MONITOR_MWAIT, NULL, &MonitorMwaitEbx.Uint32, NULL, NULL); + *MonitorFilterSize = MonitorMwaitEbx.Bits.LargestMonitorLineSize; + } + + return ApLoopMode; +} + +/** + Sort the APIC ID of all processors. + + This function sorts the APIC ID of all processors so that processor number is + assigned in the ascending order of APIC ID which eases MP debugging. + + @param[in] CpuMpData Pointer to PEI CPU MP Data +**/ +VOID +SortApicId ( + IN CPU_MP_DATA *CpuMpData + ) +{ + UINTN Index1; + UINTN Index2; + UINTN Index3; + UINT32 ApicId; + CPU_INFO_IN_HOB CpuInfo; + UINT32 ApCount; + CPU_INFO_IN_HOB *CpuInfoInHob; + volatile UINT32 *StartupApSignal; + + ApCount = CpuMpData->CpuCount - 1; + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + if (ApCount != 0) { + for (Index1 = 0; Index1 < ApCount; Index1++) { + Index3 = Index1; + // + // Sort key is the hardware default APIC ID + // + ApicId = CpuInfoInHob[Index1].ApicId; + for (Index2 = Index1 + 1; Index2 <= ApCount; Index2++) { + if (ApicId > CpuInfoInHob[Index2].ApicId) { + Index3 = Index2; + ApicId = CpuInfoInHob[Index2].ApicId; + } + } + if (Index3 != Index1) { + CopyMem (&CpuInfo, &CpuInfoInHob[Index3], sizeof (CPU_INFO_IN_HOB)); + CopyMem ( + &CpuInfoInHob[Index3], + &CpuInfoInHob[Index1], + sizeof (CPU_INFO_IN_HOB) + ); + CopyMem (&CpuInfoInHob[Index1], &CpuInfo, sizeof (CPU_INFO_IN_HOB)); + + // + // Also exchange the StartupApSignal. + // + StartupApSignal = CpuMpData->CpuData[Index3].StartupApSignal; + CpuMpData->CpuData[Index3].StartupApSignal = + CpuMpData->CpuData[Index1].StartupApSignal; + CpuMpData->CpuData[Index1].StartupApSignal = StartupApSignal; + } + } + + // + // Get the processor number for the BSP + // + ApicId = GetInitialApicId (); + for (Index1 = 0; Index1 < CpuMpData->CpuCount; Index1++) { + if (CpuInfoInHob[Index1].ApicId == ApicId) { + CpuMpData->BspNumber = (UINT32) Index1; + break; + } + } + } +} + +/** + Enable x2APIC mode on APs. + + @param[in, out] Buffer Pointer to private data buffer. +**/ +VOID +EFIAPI +ApFuncEnableX2Apic ( + IN OUT VOID *Buffer + ) +{ + SetApicMode (LOCAL_APIC_MODE_X2APIC); +} + +/** + Do sync on APs. + + @param[in, out] Buffer Pointer to private data buffer. +**/ +VOID +EFIAPI +ApInitializeSync ( + IN OUT VOID *Buffer + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN ProcessorNumber; + EFI_STATUS Status; + + CpuMpData = (CPU_MP_DATA *) Buffer; + Status = GetProcessorNumber (CpuMpData, &ProcessorNumber); + ASSERT_EFI_ERROR (Status); + // + // Load microcode on AP + // + MicrocodeDetect (CpuMpData, ProcessorNumber); + // + // Sync BSP's MTRR table to AP + // + MtrrSetAllMtrrs (&CpuMpData->MtrrTable); +} + +/** + Find the current Processor number by APIC ID. + + @param[in] CpuMpData Pointer to PEI CPU MP Data + @param[out] ProcessorNumber Return the pocessor number found + + @retval EFI_SUCCESS ProcessorNumber is found and returned. + @retval EFI_NOT_FOUND ProcessorNumber is not found. +**/ +EFI_STATUS +GetProcessorNumber ( + IN CPU_MP_DATA *CpuMpData, + OUT UINTN *ProcessorNumber + ) +{ + UINTN TotalProcessorNumber; + UINTN Index; + CPU_INFO_IN_HOB *CpuInfoInHob; + UINT32 CurrentApicId; + + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + + TotalProcessorNumber = CpuMpData->CpuCount; + CurrentApicId = GetApicId (); + for (Index = 0; Index < TotalProcessorNumber; Index ++) { + if (CpuInfoInHob[Index].ApicId == CurrentApicId) { + *ProcessorNumber = Index; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +#ifdef VBOX +/* + * @todo move this function to the library. + */ +UINT32 VBoxGetVmVariable(UINT32 Variable, CHAR8* Buffer, UINT32 Size) +{ + UINT32 VarLen, i; + + IoWrite32(EFI_INFO_PORT, Variable); + VarLen = IoRead32(EFI_INFO_PORT); + + for (i = 0; i < VarLen && i < Size; i++) + Buffer[i] = IoRead8(EFI_INFO_PORT); + + return VarLen; +} +#endif + +/** + This function will get CPU count in the system. + + @param[in] CpuMpData Pointer to PEI CPU MP Data + + @return CPU count detected +**/ +UINTN +CollectProcessorCount ( + IN CPU_MP_DATA *CpuMpData + ) +{ + UINTN Index; + CPU_INFO_IN_HOB *CpuInfoInHob; + BOOLEAN X2Apic; +#ifdef VBOX + CHAR8 u8ApicMode; +#endif + + // + // Send 1st broadcast IPI to APs to wakeup APs + // + CpuMpData->InitFlag = ApInitConfig; + WakeUpAP (CpuMpData, TRUE, 0, NULL, NULL, TRUE); + CpuMpData->InitFlag = ApInitDone; + // + // When InitFlag == ApInitConfig, WakeUpAP () guarantees all APs are checked in. + // FinishedCount is the number of check-in APs. + // + CpuMpData->CpuCount = CpuMpData->FinishedCount + 1; + ASSERT (CpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber)); + + // + // Enable x2APIC mode if + // 1. Number of CPU is greater than 255; or + // 2. There are any logical processors reporting an Initial APIC ID of 255 or greater. + // + X2Apic = FALSE; + if (CpuMpData->CpuCount > 255) { + // + // If there are more than 255 processor found, force to enable X2APIC + // + X2Apic = TRUE; + } else { + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + if (CpuInfoInHob[Index].InitialApicId >= 0xFF) { + X2Apic = TRUE; + break; + } + } + } +#ifdef VBOX + /* Force x2APIC mode if the VM config forces it. */ + VBoxGetVmVariable(EFI_INFO_INDEX_APIC_MODE, &u8ApicMode, sizeof(u8ApicMode)); + if (u8ApicMode == EFI_APIC_MODE_X2APIC) + X2Apic = TRUE; +#endif + + if (X2Apic) { + DEBUG ((DEBUG_INFO, "Force x2APIC mode!\n")); + // + // Wakeup all APs to enable x2APIC mode + // + WakeUpAP (CpuMpData, TRUE, 0, ApFuncEnableX2Apic, NULL, TRUE); + // + // Wait for all known APs finished + // + while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) { + CpuPause (); + } + // + // Enable x2APIC on BSP + // + SetApicMode (LOCAL_APIC_MODE_X2APIC); + // + // Set BSP/Aps state to IDLE + // + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + SetApState (&CpuMpData->CpuData[Index], CpuStateIdle); + } + } + DEBUG ((DEBUG_INFO, "APIC MODE is %d\n", GetApicMode ())); + // + // Sort BSP/Aps by CPU APIC ID in ascending order + // + SortApicId (CpuMpData); + + DEBUG ((DEBUG_INFO, "MpInitLib: Find %d processors in system.\n", CpuMpData->CpuCount)); + + return CpuMpData->CpuCount; +} + +/** + Initialize CPU AP Data when AP is wakeup at the first time. + + @param[in, out] CpuMpData Pointer to PEI CPU MP Data + @param[in] ProcessorNumber The handle number of processor + @param[in] BistData Processor BIST data + @param[in] ApTopOfStack Top of AP stack + +**/ +VOID +InitializeApData ( + IN OUT CPU_MP_DATA *CpuMpData, + IN UINTN ProcessorNumber, + IN UINT32 BistData, + IN UINT64 ApTopOfStack + ) +{ + CPU_INFO_IN_HOB *CpuInfoInHob; + MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr; + + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId (); + CpuInfoInHob[ProcessorNumber].ApicId = GetApicId (); + CpuInfoInHob[ProcessorNumber].Health = BistData; + CpuInfoInHob[ProcessorNumber].ApTopOfStack = ApTopOfStack; + + CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; + CpuMpData->CpuData[ProcessorNumber].CpuHealthy = (BistData == 0) ? TRUE : FALSE; + + // + // NOTE: PlatformId is not relevant on AMD platforms. + // + if (!StandardSignatureIsAuthenticAMD ()) { + PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID); + CpuMpData->CpuData[ProcessorNumber].PlatformId = (UINT8)PlatformIdMsr.Bits.PlatformId; + } + + AsmCpuid ( + CPUID_VERSION_INFO, + &CpuMpData->CpuData[ProcessorNumber].ProcessorSignature, + NULL, + NULL, + NULL + ); + + InitializeSpinLock(&CpuMpData->CpuData[ProcessorNumber].ApLock); + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); +} + +/** + Get Protected mode code segment with 16-bit default addressing + from current GDT table. + + @return Protected mode 16-bit code segment value. +**/ +STATIC +UINT16 +GetProtectedMode16CS ( + VOID + ) +{ + IA32_DESCRIPTOR GdtrDesc; + IA32_SEGMENT_DESCRIPTOR *GdtEntry; + UINTN GdtEntryCount; + UINT16 Index; + + Index = (UINT16) -1; + AsmReadGdtr (&GdtrDesc); + GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR); + GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; + for (Index = 0; Index < GdtEntryCount; Index++) { + if (GdtEntry->Bits.L == 0 && + GdtEntry->Bits.DB == 0 && + GdtEntry->Bits.Type > 8) { + break; + } + GdtEntry++; + } + ASSERT (Index != GdtEntryCount); + return Index * 8; +} + +/** + Get Protected mode code segment with 32-bit default addressing + from current GDT table. + + @return Protected mode 32-bit code segment value. +**/ +STATIC +UINT16 +GetProtectedMode32CS ( + VOID + ) +{ + IA32_DESCRIPTOR GdtrDesc; + IA32_SEGMENT_DESCRIPTOR *GdtEntry; + UINTN GdtEntryCount; + UINT16 Index; + + Index = (UINT16) -1; + AsmReadGdtr (&GdtrDesc); + GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR); + GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; + for (Index = 0; Index < GdtEntryCount; Index++) { + if (GdtEntry->Bits.L == 0 && + GdtEntry->Bits.DB == 1 && + GdtEntry->Bits.Type > 8) { + break; + } + GdtEntry++; + } + ASSERT (Index != GdtEntryCount); + return Index * 8; +} + +/** + Reset an AP when in SEV-ES mode. + + If successful, this function never returns. + + @param[in] Ghcb Pointer to the GHCB + @param[in] CpuMpData Pointer to CPU MP Data + +**/ +STATIC +VOID +MpInitLibSevEsAPReset ( + IN GHCB *Ghcb, + IN CPU_MP_DATA *CpuMpData + ) +{ + EFI_STATUS Status; + UINTN ProcessorNumber; + UINT16 Code16, Code32; + AP_RESET *APResetFn; + UINTN BufferStart; + UINTN StackStart; + + Status = GetProcessorNumber (CpuMpData, &ProcessorNumber); + ASSERT_EFI_ERROR (Status); + + Code16 = GetProtectedMode16CS (); + Code32 = GetProtectedMode32CS (); + + if (CpuMpData->WakeupBufferHigh != 0) { + APResetFn = (AP_RESET *) (CpuMpData->WakeupBufferHigh + CpuMpData->AddressMap.SwitchToRealNoNxOffset); + } else { + APResetFn = (AP_RESET *) (CpuMpData->MpCpuExchangeInfo->BufferStart + CpuMpData->AddressMap.SwitchToRealOffset); + } + + BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart; + StackStart = CpuMpData->SevEsAPResetStackStart - + (AP_RESET_STACK_SIZE * ProcessorNumber); + + // + // This call never returns. + // + APResetFn (BufferStart, Code16, Code32, StackStart); +} + +/** + This function will be called from AP reset code if BSP uses WakeUpAP. + + @param[in] ExchangeInfo Pointer to the MP exchange info buffer + @param[in] ApIndex Number of current executing AP +**/ +VOID +EFIAPI +ApWakeupFunction ( + IN MP_CPU_EXCHANGE_INFO *ExchangeInfo, + IN UINTN ApIndex + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN ProcessorNumber; + EFI_AP_PROCEDURE Procedure; + VOID *Parameter; + UINT32 BistData; + volatile UINT32 *ApStartupSignalBuffer; + CPU_INFO_IN_HOB *CpuInfoInHob; + UINT64 ApTopOfStack; + UINTN CurrentApicMode; + + // + // AP finished assembly code and begin to execute C code + // + CpuMpData = ExchangeInfo->CpuMpData; + + // + // AP's local APIC settings will be lost after received INIT IPI + // We need to re-initialize them at here + // + ProgramVirtualWireMode (); + // + // Mask the LINT0 and LINT1 so that AP doesn't enter the system timer interrupt handler. + // + DisableLvtInterrupts (); + SyncLocalApicTimerSetting (CpuMpData); + + CurrentApicMode = GetApicMode (); + while (TRUE) { + if (CpuMpData->InitFlag == ApInitConfig) { + ProcessorNumber = ApIndex; + // + // This is first time AP wakeup, get BIST information from AP stack + // + ApTopOfStack = CpuMpData->Buffer + (ProcessorNumber + 1) * CpuMpData->CpuApStackSize; + BistData = *(UINT32 *) ((UINTN) ApTopOfStack - sizeof (UINTN)); + // + // CpuMpData->CpuData[0].VolatileRegisters is initialized based on BSP environment, + // to initialize AP in InitConfig path. + // NOTE: IDTR.BASE stored in CpuMpData->CpuData[0].VolatileRegisters points to a different IDT shared by all APs. + // + RestoreVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters, FALSE); + InitializeApData (CpuMpData, ProcessorNumber, BistData, ApTopOfStack); + ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; + } else { + // + // Execute AP function if AP is ready + // + GetProcessorNumber (CpuMpData, &ProcessorNumber); + // + // Clear AP start-up signal when AP waken up + // + ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; + InterlockedCompareExchange32 ( + (UINT32 *) ApStartupSignalBuffer, + WAKEUP_AP_SIGNAL, + 0 + ); + + if (CpuMpData->InitFlag == ApInitReconfig) { + // + // ApInitReconfig happens when: + // 1. AP is re-enabled after it's disabled, in either PEI or DXE phase. + // 2. AP is initialized in DXE phase. + // In either case, use the volatile registers value derived from BSP. + // NOTE: IDTR.BASE stored in CpuMpData->CpuData[0].VolatileRegisters points to a + // different IDT shared by all APs. + // + RestoreVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters, FALSE); + } else { + if (CpuMpData->ApLoopMode == ApInHltLoop) { + // + // Restore AP's volatile registers saved before AP is halted + // + RestoreVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters, TRUE); + } else { + // + // The CPU driver might not flush TLB for APs on spot after updating + // page attributes. AP in mwait loop mode needs to take care of it when + // woken up. + // + CpuFlushTlb (); + } + } + + if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateReady) { + Procedure = (EFI_AP_PROCEDURE)CpuMpData->CpuData[ProcessorNumber].ApFunction; + Parameter = (VOID *) CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument; + if (Procedure != NULL) { + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateBusy); + // + // Enable source debugging on AP function + // + EnableDebugAgent (); + // + // Invoke AP function here + // + Procedure (Parameter); + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + if (CpuMpData->SwitchBspFlag) { + // + // Re-get the processor number due to BSP/AP maybe exchange in AP function + // + GetProcessorNumber (CpuMpData, &ProcessorNumber); + CpuMpData->CpuData[ProcessorNumber].ApFunction = 0; + CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument = 0; + ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; + CpuInfoInHob[ProcessorNumber].ApTopOfStack = CpuInfoInHob[CpuMpData->NewBspNumber].ApTopOfStack; + } else { + if (CpuInfoInHob[ProcessorNumber].ApicId != GetApicId () || + CpuInfoInHob[ProcessorNumber].InitialApicId != GetInitialApicId ()) { + if (CurrentApicMode != GetApicMode ()) { + // + // If APIC mode change happened during AP function execution, + // we do not support APIC ID value changed. + // + ASSERT (FALSE); + CpuDeadLoop (); + } else { + // + // Re-get the CPU APICID and Initial APICID if they are changed + // + CpuInfoInHob[ProcessorNumber].ApicId = GetApicId (); + CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId (); + } + } + } + } + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateFinished); + } + } + + if (CpuMpData->ApLoopMode == ApInHltLoop) { + // + // Save AP volatile registers + // + SaveVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters); + } + + // + // AP finished executing C code + // + InterlockedIncrement ((UINT32 *) &CpuMpData->FinishedCount); + + if (CpuMpData->InitFlag == ApInitConfig) { + // + // Delay decrementing the APs executing count when SEV-ES is enabled + // to allow the APs to issue an AP_RESET_HOLD before the BSP possibly + // performs another INIT-SIPI-SIPI sequence. + // + if (!CpuMpData->SevEsIsEnabled) { + InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting); + } + } + + // + // Place AP is specified loop mode + // + if (CpuMpData->ApLoopMode == ApInHltLoop) { + // + // Place AP in HLT-loop + // + while (TRUE) { + DisableInterrupts (); + if (CpuMpData->SevEsIsEnabled) { + MSR_SEV_ES_GHCB_REGISTER Msr; + GHCB *Ghcb; + UINT64 Status; + BOOLEAN DoDecrement; + BOOLEAN InterruptState; + + DoDecrement = (BOOLEAN) (CpuMpData->InitFlag == ApInitConfig); + + while (TRUE) { + Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB); + Ghcb = Msr.Ghcb; + + VmgInit (Ghcb, &InterruptState); + + if (DoDecrement) { + DoDecrement = FALSE; + + // + // Perform the delayed decrement just before issuing the first + // VMGEXIT with AP_RESET_HOLD. + // + InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting); + } + + Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0); + if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) { + VmgDone (Ghcb, InterruptState); + break; + } + + VmgDone (Ghcb, InterruptState); + } + + // + // Awakened in a new phase? Use the new CpuMpData + // + if (CpuMpData->NewCpuMpData != NULL) { + CpuMpData = CpuMpData->NewCpuMpData; + } + + MpInitLibSevEsAPReset (Ghcb, CpuMpData); + } else { + CpuSleep (); + } + CpuPause (); + } + } + while (TRUE) { + DisableInterrupts (); + if (CpuMpData->ApLoopMode == ApInMwaitLoop) { + // + // Place AP in MWAIT-loop + // + AsmMonitor ((UINTN) ApStartupSignalBuffer, 0, 0); + if (*ApStartupSignalBuffer != WAKEUP_AP_SIGNAL) { + // + // Check AP start-up signal again. + // If AP start-up signal is not set, place AP into + // the specified C-state + // + AsmMwait (CpuMpData->ApTargetCState << 4, 0); + } + } else if (CpuMpData->ApLoopMode == ApInRunLoop) { + // + // Place AP in Run-loop + // + CpuPause (); + } else { + ASSERT (FALSE); + } + + // + // If AP start-up signal is written, AP is waken up + // otherwise place AP in loop again + // + if (*ApStartupSignalBuffer == WAKEUP_AP_SIGNAL) { + break; + } + } + } +} + +/** + Wait for AP wakeup and write AP start-up signal till AP is waken up. + + @param[in] ApStartupSignalBuffer Pointer to AP wakeup signal +**/ +VOID +WaitApWakeup ( + IN volatile UINT32 *ApStartupSignalBuffer + ) +{ + // + // If AP is waken up, StartupApSignal should be cleared. + // Otherwise, write StartupApSignal again till AP waken up. + // + while (InterlockedCompareExchange32 ( + (UINT32 *) ApStartupSignalBuffer, + WAKEUP_AP_SIGNAL, + WAKEUP_AP_SIGNAL + ) != 0) { + CpuPause (); + } +} + +/** + This function will fill the exchange info structure. + + @param[in] CpuMpData Pointer to CPU MP Data + +**/ +VOID +FillExchangeInfoData ( + IN CPU_MP_DATA *CpuMpData + ) +{ + volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; + UINTN Size; + IA32_SEGMENT_DESCRIPTOR *Selector; + IA32_CR4 Cr4; + + ExchangeInfo = CpuMpData->MpCpuExchangeInfo; + ExchangeInfo->StackStart = CpuMpData->Buffer; + ExchangeInfo->StackSize = CpuMpData->CpuApStackSize; + ExchangeInfo->BufferStart = CpuMpData->WakeupBuffer; + ExchangeInfo->ModeOffset = CpuMpData->AddressMap.ModeEntryOffset; + + ExchangeInfo->CodeSegment = AsmReadCs (); + ExchangeInfo->DataSegment = AsmReadDs (); + + ExchangeInfo->Cr3 = AsmReadCr3 (); + + ExchangeInfo->CFunction = (UINTN) ApWakeupFunction; + ExchangeInfo->ApIndex = 0; + ExchangeInfo->NumApsExecuting = 0; + ExchangeInfo->InitFlag = (UINTN) CpuMpData->InitFlag; + ExchangeInfo->CpuInfo = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + ExchangeInfo->CpuMpData = CpuMpData; + + ExchangeInfo->EnableExecuteDisable = IsBspExecuteDisableEnabled (); + + ExchangeInfo->InitializeFloatingPointUnitsAddress = (UINTN)InitializeFloatingPointUnits; + + // + // We can check either CPUID(7).ECX[bit16] or check CR4.LA57[bit12] + // to determin whether 5-Level Paging is enabled. + // CPUID(7).ECX[bit16] shows CPU's capability, CR4.LA57[bit12] shows + // current system setting. + // Using latter way is simpler because it also eliminates the needs to + // check whether platform wants to enable it. + // + Cr4.UintN = AsmReadCr4 (); + ExchangeInfo->Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1); + DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName, ExchangeInfo->Enable5LevelPaging)); + + ExchangeInfo->SevEsIsEnabled = CpuMpData->SevEsIsEnabled; + ExchangeInfo->GhcbBase = (UINTN) CpuMpData->GhcbBase; + + // + // Get the BSP's data of GDT and IDT + // + AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile); + AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile); + + // + // Find a 32-bit code segment + // + Selector = (IA32_SEGMENT_DESCRIPTOR *)ExchangeInfo->GdtrProfile.Base; + Size = ExchangeInfo->GdtrProfile.Limit + 1; + while (Size > 0) { + if (Selector->Bits.L == 0 && Selector->Bits.Type >= 8) { + ExchangeInfo->ModeTransitionSegment = + (UINT16)((UINTN)Selector - ExchangeInfo->GdtrProfile.Base); + break; + } + Selector += 1; + Size -= sizeof (IA32_SEGMENT_DESCRIPTOR); + } + + // + // Copy all 32-bit code and 64-bit code into memory with type of + // EfiBootServicesCode to avoid page fault if NX memory protection is enabled. + // + if (CpuMpData->WakeupBufferHigh != 0) { + Size = CpuMpData->AddressMap.RendezvousFunnelSize + + CpuMpData->AddressMap.SwitchToRealSize - + CpuMpData->AddressMap.ModeTransitionOffset; + CopyMem ( + (VOID *)CpuMpData->WakeupBufferHigh, + CpuMpData->AddressMap.RendezvousFunnelAddress + + CpuMpData->AddressMap.ModeTransitionOffset, + Size + ); + + ExchangeInfo->ModeTransitionMemory = (UINT32)CpuMpData->WakeupBufferHigh; + } else { + ExchangeInfo->ModeTransitionMemory = (UINT32) + (ExchangeInfo->BufferStart + CpuMpData->AddressMap.ModeTransitionOffset); + } + + ExchangeInfo->ModeHighMemory = ExchangeInfo->ModeTransitionMemory + + (UINT32)ExchangeInfo->ModeOffset - + (UINT32)CpuMpData->AddressMap.ModeTransitionOffset; + ExchangeInfo->ModeHighSegment = (UINT16)ExchangeInfo->CodeSegment; +} + +/** + Helper function that waits until the finished AP count reaches the specified + limit, or the specified timeout elapses (whichever comes first). + + @param[in] CpuMpData Pointer to CPU MP Data. + @param[in] FinishedApLimit The number of finished APs to wait for. + @param[in] TimeLimit The number of microseconds to wait for. +**/ +VOID +TimedWaitForApFinish ( + IN CPU_MP_DATA *CpuMpData, + IN UINT32 FinishedApLimit, + IN UINT32 TimeLimit + ); + +/** + Get available system memory below 1MB by specified size. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +BackupAndPrepareWakeupBuffer( + IN CPU_MP_DATA *CpuMpData + ) +{ + CopyMem ( + (VOID *) CpuMpData->BackupBuffer, + (VOID *) CpuMpData->WakeupBuffer, + CpuMpData->BackupBufferSize + ); + CopyMem ( + (VOID *) CpuMpData->WakeupBuffer, + (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress, + CpuMpData->AddressMap.RendezvousFunnelSize + + CpuMpData->AddressMap.SwitchToRealSize + ); +} + +/** + Restore wakeup buffer data. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +RestoreWakeupBuffer( + IN CPU_MP_DATA *CpuMpData + ) +{ + CopyMem ( + (VOID *) CpuMpData->WakeupBuffer, + (VOID *) CpuMpData->BackupBuffer, + CpuMpData->BackupBufferSize + ); +} + +/** + Calculate the size of the reset vector. + + @param[in] AddressMap The pointer to Address Map structure. + + @return Total amount of memory required for the AP reset area +**/ +STATIC +UINTN +GetApResetVectorSize ( + IN MP_ASSEMBLY_ADDRESS_MAP *AddressMap + ) +{ + UINTN Size; + + Size = AddressMap->RendezvousFunnelSize + + AddressMap->SwitchToRealSize + + sizeof (MP_CPU_EXCHANGE_INFO); + + // + // The AP reset stack is only used by SEV-ES guests. Do not add to the + // allocation if SEV-ES is not enabled. + // + if (PcdGetBool (PcdSevEsIsEnabled)) { + // + // Stack location is based on APIC ID, so use the total number of + // processors for calculating the total stack area. + // + Size += AP_RESET_STACK_SIZE * PcdGet32 (PcdCpuMaxLogicalProcessorNumber); + + Size = ALIGN_VALUE (Size, CPU_STACK_ALIGNMENT); + } + + return Size; +} + +/** + Allocate reset vector buffer. + + @param[in, out] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +AllocateResetVector ( + IN OUT CPU_MP_DATA *CpuMpData + ) +{ + UINTN ApResetVectorSize; + + if (CpuMpData->WakeupBuffer == (UINTN) -1) { + ApResetVectorSize = GetApResetVectorSize (&CpuMpData->AddressMap); + + CpuMpData->WakeupBuffer = GetWakeupBuffer (ApResetVectorSize); + CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) + (CpuMpData->WakeupBuffer + + CpuMpData->AddressMap.RendezvousFunnelSize + + CpuMpData->AddressMap.SwitchToRealSize); + CpuMpData->WakeupBufferHigh = GetModeTransitionBuffer ( + CpuMpData->AddressMap.RendezvousFunnelSize + + CpuMpData->AddressMap.SwitchToRealSize - + CpuMpData->AddressMap.ModeTransitionOffset + ); + // + // The reset stack starts at the end of the buffer. + // + CpuMpData->SevEsAPResetStackStart = CpuMpData->WakeupBuffer + ApResetVectorSize; + } + BackupAndPrepareWakeupBuffer (CpuMpData); +} + +/** + Free AP reset vector buffer. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +FreeResetVector ( + IN CPU_MP_DATA *CpuMpData + ) +{ + // + // If SEV-ES is enabled, the reset area is needed for AP parking and + // and AP startup in the OS, so the reset area is reserved. Do not + // perform the restore as this will overwrite memory which has data + // needed by SEV-ES. + // + if (!CpuMpData->SevEsIsEnabled) { + RestoreWakeupBuffer (CpuMpData); + } +} + +/** + Allocate the SEV-ES AP jump table buffer. + + @param[in, out] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +AllocateSevEsAPMemory ( + IN OUT CPU_MP_DATA *CpuMpData + ) +{ + if (CpuMpData->SevEsAPBuffer == (UINTN) -1) { + CpuMpData->SevEsAPBuffer = + CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0; + } +} + +/** + Program the SEV-ES AP jump table buffer. + + @param[in] SipiVector The SIPI vector used for the AP Reset +**/ +VOID +SetSevEsJumpTable ( + IN UINTN SipiVector + ) +{ + SEV_ES_AP_JMP_FAR *JmpFar; + UINT32 Offset, InsnByte; + UINT8 LoNib, HiNib; + + JmpFar = (SEV_ES_AP_JMP_FAR *) (UINTN) FixedPcdGet32 (PcdSevEsWorkAreaBase); + ASSERT (JmpFar != NULL); + + // + // Obtain the address of the Segment/Rip location in the workarea. + // This will be set to a value derived from the SIPI vector and will + // be the memory address used for the far jump below. + // + Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase); + Offset += sizeof (JmpFar->InsnBuffer); + LoNib = (UINT8) Offset; + HiNib = (UINT8) (Offset >> 8); + + // + // Program the workarea (which is the initial AP boot address) with + // far jump to the SIPI vector (where XX and YY represent the + // address of where the SIPI vector is stored. + // + // JMP FAR [CS:XXYY] => 2E FF 2E YY XX + // + InsnByte = 0; + JmpFar->InsnBuffer[InsnByte++] = 0x2E; // CS override prefix + JmpFar->InsnBuffer[InsnByte++] = 0xFF; // JMP (FAR) + JmpFar->InsnBuffer[InsnByte++] = 0x2E; // ModRM (JMP memory location) + JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ... + JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ... + + // + // Program the Segment/Rip based on the SIPI vector (always at least + // 16-byte aligned, so Rip is set to 0). + // + JmpFar->Rip = 0; + JmpFar->Segment = (UINT16) (SipiVector >> 4); +} + +/** + This function will be called by BSP to wakeup AP. + + @param[in] CpuMpData Pointer to CPU MP Data + @param[in] Broadcast TRUE: Send broadcast IPI to all APs + FALSE: Send IPI to AP by ApicId + @param[in] ProcessorNumber The handle number of specified processor + @param[in] Procedure The function to be invoked by AP + @param[in] ProcedureArgument The argument to be passed into AP function + @param[in] WakeUpDisabledAps Whether need to wake up disabled APs in broadcast mode. +**/ +VOID +WakeUpAP ( + IN CPU_MP_DATA *CpuMpData, + IN BOOLEAN Broadcast, + IN UINTN ProcessorNumber, + IN EFI_AP_PROCEDURE Procedure, OPTIONAL + IN VOID *ProcedureArgument, OPTIONAL + IN BOOLEAN WakeUpDisabledAps + ) +{ + volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; + UINTN Index; + CPU_AP_DATA *CpuData; + BOOLEAN ResetVectorRequired; + CPU_INFO_IN_HOB *CpuInfoInHob; + + CpuMpData->FinishedCount = 0; + ResetVectorRequired = FALSE; + + if (CpuMpData->WakeUpByInitSipiSipi || + CpuMpData->InitFlag != ApInitDone) { + ResetVectorRequired = TRUE; + AllocateResetVector (CpuMpData); + AllocateSevEsAPMemory (CpuMpData); + FillExchangeInfoData (CpuMpData); + SaveLocalApicTimerSetting (CpuMpData); + } + + if (CpuMpData->ApLoopMode == ApInMwaitLoop) { + // + // Get AP target C-state each time when waking up AP, + // for it maybe updated by platform again + // + CpuMpData->ApTargetCState = PcdGet8 (PcdCpuApTargetCstate); + } + + ExchangeInfo = CpuMpData->MpCpuExchangeInfo; + + if (Broadcast) { + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + if (Index != CpuMpData->BspNumber) { + CpuData = &CpuMpData->CpuData[Index]; + // + // All AP(include disabled AP) will be woke up by INIT-SIPI-SIPI, but + // the AP procedure will be skipped for disabled AP because AP state + // is not CpuStateReady. + // + if (GetApState (CpuData) == CpuStateDisabled && !WakeUpDisabledAps) { + continue; + } + + CpuData->ApFunction = (UINTN) Procedure; + CpuData->ApFunctionArgument = (UINTN) ProcedureArgument; + SetApState (CpuData, CpuStateReady); + if (CpuMpData->InitFlag != ApInitConfig) { + *(UINT32 *) CpuData->StartupApSignal = WAKEUP_AP_SIGNAL; + } + } + } + if (ResetVectorRequired) { + // + // For SEV-ES, the initial AP boot address will be defined by + // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address + // from the original INIT-SIPI-SIPI. + // + if (CpuMpData->SevEsIsEnabled) { + SetSevEsJumpTable (ExchangeInfo->BufferStart); + } + + // + // Wakeup all APs + // + SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart); + } + if (CpuMpData->InitFlag == ApInitConfig) { + if (PcdGet32 (PcdCpuBootLogicalProcessorNumber) > 0) { + // + // The AP enumeration algorithm below is suitable only when the + // platform can tell us the *exact* boot CPU count in advance. + // + // The wait below finishes only when the detected AP count reaches + // (PcdCpuBootLogicalProcessorNumber - 1), regardless of how long that + // takes. If at least one AP fails to check in (meaning a platform + // hardware bug), the detection hangs forever, by design. If the actual + // boot CPU count in the system is higher than + // PcdCpuBootLogicalProcessorNumber (meaning a platform + // misconfiguration), then some APs may complete initialization after + // the wait finishes, and cause undefined behavior. + // + TimedWaitForApFinish ( + CpuMpData, + PcdGet32 (PcdCpuBootLogicalProcessorNumber) - 1, + MAX_UINT32 // approx. 71 minutes + ); + } else { + // + // The AP enumeration algorithm below is suitable for two use cases. + // + // (1) The check-in time for an individual AP is bounded, and APs run + // through their initialization routines strongly concurrently. In + // particular, the number of concurrently running APs + // ("NumApsExecuting") is never expected to fall to zero + // *temporarily* -- it is expected to fall to zero only when all + // APs have checked-in. + // + // In this case, the platform is supposed to set + // PcdCpuApInitTimeOutInMicroSeconds to a low-ish value (just long + // enough for one AP to start initialization). The timeout will be + // reached soon, and remaining APs are collected by watching + // NumApsExecuting fall to zero. If NumApsExecuting falls to zero + // mid-process, while some APs have not completed initialization, + // the behavior is undefined. + // + // (2) The check-in time for an individual AP is unbounded, and/or APs + // may complete their initializations widely spread out. In + // particular, some APs may finish initialization before some APs + // even start. + // + // In this case, the platform is supposed to set + // PcdCpuApInitTimeOutInMicroSeconds to a high-ish value. The AP + // enumeration will always take that long (except when the boot CPU + // count happens to be maximal, that is, + // PcdCpuMaxLogicalProcessorNumber). All APs are expected to + // check-in before the timeout, and NumApsExecuting is assumed zero + // at timeout. APs that miss the time-out may cause undefined + // behavior. + // + TimedWaitForApFinish ( + CpuMpData, + PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1, + PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds) + ); + + while (CpuMpData->MpCpuExchangeInfo->NumApsExecuting != 0) { + CpuPause(); + } + } + } else { + // + // Wait all APs waken up if this is not the 1st broadcast of SIPI + // + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + CpuData = &CpuMpData->CpuData[Index]; + if (Index != CpuMpData->BspNumber) { + WaitApWakeup (CpuData->StartupApSignal); + } + } + } + } else { + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + CpuData->ApFunction = (UINTN) Procedure; + CpuData->ApFunctionArgument = (UINTN) ProcedureArgument; + SetApState (CpuData, CpuStateReady); + // + // Wakeup specified AP + // + ASSERT (CpuMpData->InitFlag != ApInitConfig); + *(UINT32 *) CpuData->StartupApSignal = WAKEUP_AP_SIGNAL; + if (ResetVectorRequired) { + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + + // + // For SEV-ES, the initial AP boot address will be defined by + // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address + // from the original INIT-SIPI-SIPI. + // + if (CpuMpData->SevEsIsEnabled) { + SetSevEsJumpTable (ExchangeInfo->BufferStart); + } + + SendInitSipiSipi ( + CpuInfoInHob[ProcessorNumber].ApicId, + (UINT32) ExchangeInfo->BufferStart + ); + } + // + // Wait specified AP waken up + // + WaitApWakeup (CpuData->StartupApSignal); + } + + if (ResetVectorRequired) { + FreeResetVector (CpuMpData); + } + + // + // After one round of Wakeup Ap actions, need to re-sync ApLoopMode with + // WakeUpByInitSipiSipi flag. WakeUpByInitSipiSipi flag maybe changed by + // S3SmmInitDone Ppi. + // + CpuMpData->WakeUpByInitSipiSipi = (CpuMpData->ApLoopMode == ApInHltLoop); +} + +/** + Calculate timeout value and return the current performance counter value. + + Calculate the number of performance counter ticks required for a timeout. + If TimeoutInMicroseconds is 0, return value is also 0, which is recognized + as infinity. + + @param[in] TimeoutInMicroseconds Timeout value in microseconds. + @param[out] CurrentTime Returns the current value of the performance counter. + + @return Expected time stamp counter for timeout. + If TimeoutInMicroseconds is 0, return value is also 0, which is recognized + as infinity. + +**/ +UINT64 +CalculateTimeout ( + IN UINTN TimeoutInMicroseconds, + OUT UINT64 *CurrentTime + ) +{ + UINT64 TimeoutInSeconds; + UINT64 TimestampCounterFreq; + + // + // Read the current value of the performance counter + // + *CurrentTime = GetPerformanceCounter (); + + // + // If TimeoutInMicroseconds is 0, return value is also 0, which is recognized + // as infinity. + // + if (TimeoutInMicroseconds == 0) { + return 0; + } + + // + // GetPerformanceCounterProperties () returns the timestamp counter's frequency + // in Hz. + // + TimestampCounterFreq = GetPerformanceCounterProperties (NULL, NULL); + + // + // Check the potential overflow before calculate the number of ticks for the timeout value. + // + if (DivU64x64Remainder (MAX_UINT64, TimeoutInMicroseconds, NULL) < TimestampCounterFreq) { + // + // Convert microseconds into seconds if direct multiplication overflows + // + TimeoutInSeconds = DivU64x32 (TimeoutInMicroseconds, 1000000); + // + // Assertion if the final tick count exceeds MAX_UINT64 + // + ASSERT (DivU64x64Remainder (MAX_UINT64, TimeoutInSeconds, NULL) >= TimestampCounterFreq); + return MultU64x64 (TimestampCounterFreq, TimeoutInSeconds); + } else { + // + // No overflow case, multiply the return value with TimeoutInMicroseconds and then divide + // it by 1,000,000, to get the number of ticks for the timeout value. + // + return DivU64x32 ( + MultU64x64 ( + TimestampCounterFreq, + TimeoutInMicroseconds + ), + 1000000 + ); + } +} + +/** + Checks whether timeout expires. + + Check whether the number of elapsed performance counter ticks required for + a timeout condition has been reached. + If Timeout is zero, which means infinity, return value is always FALSE. + + @param[in, out] PreviousTime On input, the value of the performance counter + when it was last read. + On output, the current value of the performance + counter + @param[in] TotalTime The total amount of elapsed time in performance + counter ticks. + @param[in] Timeout The number of performance counter ticks required + to reach a timeout condition. + + @retval TRUE A timeout condition has been reached. + @retval FALSE A timeout condition has not been reached. + +**/ +BOOLEAN +CheckTimeout ( + IN OUT UINT64 *PreviousTime, + IN UINT64 *TotalTime, + IN UINT64 Timeout + ) +{ + UINT64 Start; + UINT64 End; + UINT64 CurrentTime; + INT64 Delta; + INT64 Cycle; + + if (Timeout == 0) { + return FALSE; + } + GetPerformanceCounterProperties (&Start, &End); + Cycle = End - Start; + if (Cycle < 0) { + Cycle = -Cycle; + } + Cycle++; + CurrentTime = GetPerformanceCounter(); + Delta = (INT64) (CurrentTime - *PreviousTime); + if (Start > End) { + Delta = -Delta; + } + if (Delta < 0) { + Delta += Cycle; + } + *TotalTime += Delta; + *PreviousTime = CurrentTime; + if (*TotalTime > Timeout) { + return TRUE; + } + return FALSE; +} + +/** + Helper function that waits until the finished AP count reaches the specified + limit, or the specified timeout elapses (whichever comes first). + + @param[in] CpuMpData Pointer to CPU MP Data. + @param[in] FinishedApLimit The number of finished APs to wait for. + @param[in] TimeLimit The number of microseconds to wait for. +**/ +VOID +TimedWaitForApFinish ( + IN CPU_MP_DATA *CpuMpData, + IN UINT32 FinishedApLimit, + IN UINT32 TimeLimit + ) +{ + // + // CalculateTimeout() and CheckTimeout() consider a TimeLimit of 0 + // "infinity", so check for (TimeLimit == 0) explicitly. + // + if (TimeLimit == 0) { + return; + } + + CpuMpData->TotalTime = 0; + CpuMpData->ExpectedTime = CalculateTimeout ( + TimeLimit, + &CpuMpData->CurrentTime + ); + while (CpuMpData->FinishedCount < FinishedApLimit && + !CheckTimeout ( + &CpuMpData->CurrentTime, + &CpuMpData->TotalTime, + CpuMpData->ExpectedTime + )) { + CpuPause (); + } + + if (CpuMpData->FinishedCount >= FinishedApLimit) { + DEBUG (( + DEBUG_VERBOSE, + "%a: reached FinishedApLimit=%u in %Lu microseconds\n", + __FUNCTION__, + FinishedApLimit, + DivU64x64Remainder ( + MultU64x32 (CpuMpData->TotalTime, 1000000), + GetPerformanceCounterProperties (NULL, NULL), + NULL + ) + )); + } +} + +/** + Reset an AP to Idle state. + + Any task being executed by the AP will be aborted and the AP + will be waiting for a new task in Wait-For-SIPI state. + + @param[in] ProcessorNumber The handle number of processor. +**/ +VOID +ResetProcessorToIdleState ( + IN UINTN ProcessorNumber + ) +{ + CPU_MP_DATA *CpuMpData; + + CpuMpData = GetCpuMpData (); + + CpuMpData->InitFlag = ApInitReconfig; + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, NULL, NULL, TRUE); + while (CpuMpData->FinishedCount < 1) { + CpuPause (); + } + CpuMpData->InitFlag = ApInitDone; + + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); +} + +/** + Searches for the next waiting AP. + + Search for the next AP that is put in waiting state by single-threaded StartupAllAPs(). + + @param[out] NextProcessorNumber Pointer to the processor number of the next waiting AP. + + @retval EFI_SUCCESS The next waiting AP has been found. + @retval EFI_NOT_FOUND No waiting AP exists. + +**/ +EFI_STATUS +GetNextWaitingProcessorNumber ( + OUT UINTN *NextProcessorNumber + ) +{ + UINTN ProcessorNumber; + CPU_MP_DATA *CpuMpData; + + CpuMpData = GetCpuMpData (); + + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { + if (CpuMpData->CpuData[ProcessorNumber].Waiting) { + *NextProcessorNumber = ProcessorNumber; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** Checks status of specified AP. + + This function checks whether the specified AP has finished the task assigned + by StartupThisAP(), and whether timeout expires. + + @param[in] ProcessorNumber The handle number of processor. + + @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs(). + @retval EFI_TIMEOUT The timeout expires. + @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired. +**/ +EFI_STATUS +CheckThisAP ( + IN UINTN ProcessorNumber + ) +{ + CPU_MP_DATA *CpuMpData; + CPU_AP_DATA *CpuData; + + CpuMpData = GetCpuMpData (); + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + + // + // Check the CPU state of AP. If it is CpuStateIdle, then the AP has finished its task. + // Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the + // value of state after setting the it to CpuStateIdle, so BSP can safely make use of its value. + // + // + // If the AP finishes for StartupThisAP(), return EFI_SUCCESS. + // + if (GetApState(CpuData) == CpuStateFinished) { + if (CpuData->Finished != NULL) { + *(CpuData->Finished) = TRUE; + } + SetApState (CpuData, CpuStateIdle); + return EFI_SUCCESS; + } else { + // + // If timeout expires for StartupThisAP(), report timeout. + // + if (CheckTimeout (&CpuData->CurrentTime, &CpuData->TotalTime, CpuData->ExpectedTime)) { + if (CpuData->Finished != NULL) { + *(CpuData->Finished) = FALSE; + } + // + // Reset failed AP to idle state + // + ResetProcessorToIdleState (ProcessorNumber); + + return EFI_TIMEOUT; + } + } + return EFI_NOT_READY; +} + +/** + Checks status of all APs. + + This function checks whether all APs have finished task assigned by StartupAllAPs(), + and whether timeout expires. + + @retval EFI_SUCCESS All APs have finished task assigned by StartupAllAPs(). + @retval EFI_TIMEOUT The timeout expires. + @retval EFI_NOT_READY APs have not finished task and timeout has not expired. +**/ +EFI_STATUS +CheckAllAPs ( + VOID + ) +{ + UINTN ProcessorNumber; + UINTN NextProcessorNumber; + UINTN ListIndex; + EFI_STATUS Status; + CPU_MP_DATA *CpuMpData; + CPU_AP_DATA *CpuData; + + CpuMpData = GetCpuMpData (); + + NextProcessorNumber = 0; + + // + // Go through all APs that are responsible for the StartupAllAPs(). + // + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { + if (!CpuMpData->CpuData[ProcessorNumber].Waiting) { + continue; + } + + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + // + // Check the CPU state of AP. If it is CpuStateIdle, then the AP has finished its task. + // Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the + // value of state after setting the it to CpuStateIdle, so BSP can safely make use of its value. + // + if (GetApState(CpuData) == CpuStateFinished) { + CpuMpData->RunningCount --; + CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; + SetApState(CpuData, CpuStateIdle); + + // + // If in Single Thread mode, then search for the next waiting AP for execution. + // + if (CpuMpData->SingleThread) { + Status = GetNextWaitingProcessorNumber (&NextProcessorNumber); + + if (!EFI_ERROR (Status)) { + WakeUpAP ( + CpuMpData, + FALSE, + (UINT32) NextProcessorNumber, + CpuMpData->Procedure, + CpuMpData->ProcArguments, + TRUE + ); + } + } + } + } + + // + // If all APs finish, return EFI_SUCCESS. + // + if (CpuMpData->RunningCount == 0) { + return EFI_SUCCESS; + } + + // + // If timeout expires, report timeout. + // + if (CheckTimeout ( + &CpuMpData->CurrentTime, + &CpuMpData->TotalTime, + CpuMpData->ExpectedTime) + ) { + // + // If FailedCpuList is not NULL, record all failed APs in it. + // + if (CpuMpData->FailedCpuList != NULL) { + *CpuMpData->FailedCpuList = + AllocatePool ((CpuMpData->RunningCount + 1) * sizeof (UINTN)); + ASSERT (*CpuMpData->FailedCpuList != NULL); + } + ListIndex = 0; + + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { + // + // Check whether this processor is responsible for StartupAllAPs(). + // + if (CpuMpData->CpuData[ProcessorNumber].Waiting) { + // + // Reset failed APs to idle state + // + ResetProcessorToIdleState (ProcessorNumber); + CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; + if (CpuMpData->FailedCpuList != NULL) { + (*CpuMpData->FailedCpuList)[ListIndex++] = ProcessorNumber; + } + } + } + if (CpuMpData->FailedCpuList != NULL) { + (*CpuMpData->FailedCpuList)[ListIndex] = END_OF_CPU_LIST; + } + return EFI_TIMEOUT; + } + return EFI_NOT_READY; +} + +/** + MP Initialize Library initialization. + + This service will allocate AP reset vector and wakeup all APs to do APs + initialization. + + This service must be invoked before all other MP Initialize Library + service are invoked. + + @retval EFI_SUCCESS MP initialization succeeds. + @retval Others MP initialization fails. + +**/ +EFI_STATUS +EFIAPI +MpInitLibInitialize ( + VOID + ) +{ + CPU_MP_DATA *OldCpuMpData; + CPU_INFO_IN_HOB *CpuInfoInHob; + UINT32 MaxLogicalProcessorNumber; + UINT32 ApStackSize; + MP_ASSEMBLY_ADDRESS_MAP AddressMap; + CPU_VOLATILE_REGISTERS VolatileRegisters; + UINTN BufferSize; + UINT32 MonitorFilterSize; + VOID *MpBuffer; + UINTN Buffer; + CPU_MP_DATA *CpuMpData; + UINT8 ApLoopMode; + UINT8 *MonitorBuffer; + UINTN Index; + UINTN ApResetVectorSize; + UINTN BackupBufferAddr; + UINTN ApIdtBase; + + OldCpuMpData = GetCpuMpDataFromGuidedHob (); + if (OldCpuMpData == NULL) { + MaxLogicalProcessorNumber = PcdGet32(PcdCpuMaxLogicalProcessorNumber); + } else { + MaxLogicalProcessorNumber = OldCpuMpData->CpuCount; + } + ASSERT (MaxLogicalProcessorNumber != 0); + + AsmGetAddressMap (&AddressMap); + ApResetVectorSize = GetApResetVectorSize (&AddressMap); + ApStackSize = PcdGet32(PcdCpuApStackSize); + ApLoopMode = GetApLoopMode (&MonitorFilterSize); + + // + // Save BSP's Control registers for APs. + // + SaveVolatileRegisters (&VolatileRegisters); + + BufferSize = ApStackSize * MaxLogicalProcessorNumber; + BufferSize += MonitorFilterSize * MaxLogicalProcessorNumber; + BufferSize += ApResetVectorSize; + BufferSize = ALIGN_VALUE (BufferSize, 8); + BufferSize += VolatileRegisters.Idtr.Limit + 1; + BufferSize += sizeof (CPU_MP_DATA); + BufferSize += (sizeof (CPU_AP_DATA) + sizeof (CPU_INFO_IN_HOB))* MaxLogicalProcessorNumber; + MpBuffer = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize)); + ASSERT (MpBuffer != NULL); + ZeroMem (MpBuffer, BufferSize); + Buffer = (UINTN) MpBuffer; + + // + // The layout of the Buffer is as below: + // + // +--------------------+ <-- Buffer + // AP Stacks (N) + // +--------------------+ <-- MonitorBuffer + // AP Monitor Filters (N) + // +--------------------+ <-- BackupBufferAddr (CpuMpData->BackupBuffer) + // Backup Buffer + // +--------------------+ + // Padding + // +--------------------+ <-- ApIdtBase (8-byte boundary) + // AP IDT All APs share one separate IDT. So AP can get address of CPU_MP_DATA from IDT Base. + // +--------------------+ <-- CpuMpData + // CPU_MP_DATA + // +--------------------+ <-- CpuMpData->CpuData + // CPU_AP_DATA (N) + // +--------------------+ <-- CpuMpData->CpuInfoInHob + // CPU_INFO_IN_HOB (N) + // +--------------------+ + // + MonitorBuffer = (UINT8 *) (Buffer + ApStackSize * MaxLogicalProcessorNumber); + BackupBufferAddr = (UINTN) MonitorBuffer + MonitorFilterSize * MaxLogicalProcessorNumber; + ApIdtBase = ALIGN_VALUE (BackupBufferAddr + ApResetVectorSize, 8); + CpuMpData = (CPU_MP_DATA *) (ApIdtBase + VolatileRegisters.Idtr.Limit + 1); + CpuMpData->Buffer = Buffer; + CpuMpData->CpuApStackSize = ApStackSize; + CpuMpData->BackupBuffer = BackupBufferAddr; + CpuMpData->BackupBufferSize = ApResetVectorSize; + CpuMpData->WakeupBuffer = (UINTN) -1; + CpuMpData->CpuCount = 1; + CpuMpData->BspNumber = 0; + CpuMpData->WaitEvent = NULL; + CpuMpData->SwitchBspFlag = FALSE; + CpuMpData->CpuData = (CPU_AP_DATA *) (CpuMpData + 1); + CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber); + InitializeSpinLock(&CpuMpData->MpLock); + CpuMpData->SevEsIsEnabled = PcdGetBool (PcdSevEsIsEnabled); + CpuMpData->SevEsAPBuffer = (UINTN) -1; + CpuMpData->GhcbBase = PcdGet64 (PcdGhcbBase); + + // + // Make sure no memory usage outside of the allocated buffer. + // + ASSERT ((CpuMpData->CpuInfoInHob + sizeof (CPU_INFO_IN_HOB) * MaxLogicalProcessorNumber) == + Buffer + BufferSize); + + // + // Duplicate BSP's IDT to APs. + // All APs share one separate IDT. So AP can get the address of CpuMpData by using IDTR.BASE + IDTR.LIMIT + 1 + // + CopyMem ((VOID *)ApIdtBase, (VOID *)VolatileRegisters.Idtr.Base, VolatileRegisters.Idtr.Limit + 1); + VolatileRegisters.Idtr.Base = ApIdtBase; + // + // Don't pass BSP's TR to APs to avoid AP init failure. + // + VolatileRegisters.Tr = 0; + CopyMem (&CpuMpData->CpuData[0].VolatileRegisters, &VolatileRegisters, sizeof (VolatileRegisters)); + // + // Set BSP basic information + // + InitializeApData (CpuMpData, 0, 0, CpuMpData->Buffer + ApStackSize); + // + // Save assembly code information + // + CopyMem (&CpuMpData->AddressMap, &AddressMap, sizeof (MP_ASSEMBLY_ADDRESS_MAP)); + // + // Finally set AP loop mode + // + CpuMpData->ApLoopMode = ApLoopMode; + DEBUG ((DEBUG_INFO, "AP Loop Mode is %d\n", CpuMpData->ApLoopMode)); + + CpuMpData->WakeUpByInitSipiSipi = (CpuMpData->ApLoopMode == ApInHltLoop); + + // + // Set up APs wakeup signal buffer + // + for (Index = 0; Index < MaxLogicalProcessorNumber; Index++) { + CpuMpData->CpuData[Index].StartupApSignal = + (UINT32 *)(MonitorBuffer + MonitorFilterSize * Index); + } + // + // Enable the local APIC for Virtual Wire Mode. + // + ProgramVirtualWireMode (); + + if (OldCpuMpData == NULL) { + if (MaxLogicalProcessorNumber > 1) { + // + // Wakeup all APs and calculate the processor count in system + // + CollectProcessorCount (CpuMpData); + } + } else { + // + // APs have been wakeup before, just get the CPU Information + // from HOB + // + OldCpuMpData->NewCpuMpData = CpuMpData; + CpuMpData->CpuCount = OldCpuMpData->CpuCount; + CpuMpData->BspNumber = OldCpuMpData->BspNumber; + CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob; + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + InitializeSpinLock(&CpuMpData->CpuData[Index].ApLock); + CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health == 0)? TRUE:FALSE; + CpuMpData->CpuData[Index].ApFunction = 0; + } + } + + if (!GetMicrocodePatchInfoFromHob ( + &CpuMpData->MicrocodePatchAddress, + &CpuMpData->MicrocodePatchRegionSize + )) { + // + // The microcode patch information cache HOB does not exist, which means + // the microcode patches data has not been loaded into memory yet + // + ShadowMicrocodeUpdatePatch (CpuMpData); + } + + // + // Detect and apply Microcode on BSP + // + MicrocodeDetect (CpuMpData, CpuMpData->BspNumber); + // + // Store BSP's MTRR setting + // + MtrrGetAllMtrrs (&CpuMpData->MtrrTable); + + // + // Wakeup APs to do some AP initialize sync (Microcode & MTRR) + // + if (CpuMpData->CpuCount > 1) { + if (OldCpuMpData != NULL) { + // + // Only needs to use this flag for DXE phase to update the wake up + // buffer. Wakeup buffer allocated in PEI phase is no longer valid + // in DXE. + // + CpuMpData->InitFlag = ApInitReconfig; + } + WakeUpAP (CpuMpData, TRUE, 0, ApInitializeSync, CpuMpData, TRUE); + // + // Wait for all APs finished initialization + // + while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) { + CpuPause (); + } + if (OldCpuMpData != NULL) { + CpuMpData->InitFlag = ApInitDone; + } + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + SetApState (&CpuMpData->CpuData[Index], CpuStateIdle); + } + } + + // + // Dump the microcode revision for each core. + // + DEBUG_CODE ( + UINT32 ThreadId; + UINT32 ExpectedMicrocodeRevision; + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + GetProcessorLocationByApicId (CpuInfoInHob[Index].InitialApicId, NULL, NULL, &ThreadId); + if (ThreadId == 0) { + // + // MicrocodeDetect() loads microcode in first thread of each core, so, + // CpuMpData->CpuData[Index].MicrocodeEntryAddr is initialized only for first thread of each core. + // + ExpectedMicrocodeRevision = 0; + if (CpuMpData->CpuData[Index].MicrocodeEntryAddr != 0) { + ExpectedMicrocodeRevision = ((CPU_MICROCODE_HEADER *)(UINTN)CpuMpData->CpuData[Index].MicrocodeEntryAddr)->UpdateRevision; + } + DEBUG (( + DEBUG_INFO, "CPU[%04d]: Microcode revision = %08x, expected = %08x\n", + Index, CpuMpData->CpuData[Index].MicrocodeRevision, ExpectedMicrocodeRevision + )); + } + } + ); + // + // Initialize global data for MP support + // + InitMpGlobalData (CpuMpData); + + return EFI_SUCCESS; +} + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + @param[out] HealthData Return processor health data. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibGetProcessorInfo ( + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer, + OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + CPU_INFO_IN_HOB *CpuInfoInHob; + UINTN OriginalProcessorNumber; + + CpuMpData = GetCpuMpData (); + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + + // + // Lower 24 bits contains the actual processor number. + // + OriginalProcessorNumber = ProcessorNumber; + ProcessorNumber &= BIT24 - 1; + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + if (ProcessorInfoBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (ProcessorNumber >= CpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + ProcessorInfoBuffer->ProcessorId = (UINT64) CpuInfoInHob[ProcessorNumber].ApicId; + ProcessorInfoBuffer->StatusFlag = 0; + if (ProcessorNumber == CpuMpData->BspNumber) { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT; + } + if (CpuMpData->CpuData[ProcessorNumber].CpuHealthy) { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_HEALTH_STATUS_BIT; + } + if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) { + ProcessorInfoBuffer->StatusFlag &= ~PROCESSOR_ENABLED_BIT; + } else { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT; + } + + // + // Get processor location information + // + GetProcessorLocationByApicId ( + CpuInfoInHob[ProcessorNumber].ApicId, + &ProcessorInfoBuffer->Location.Package, + &ProcessorInfoBuffer->Location.Core, + &ProcessorInfoBuffer->Location.Thread + ); + + if ((OriginalProcessorNumber & CPU_V2_EXTENDED_TOPOLOGY) != 0) { + GetProcessorLocation2ByApicId ( + CpuInfoInHob[ProcessorNumber].ApicId, + &ProcessorInfoBuffer->ExtendedInformation.Location2.Package, + &ProcessorInfoBuffer->ExtendedInformation.Location2.Die, + &ProcessorInfoBuffer->ExtendedInformation.Location2.Tile, + &ProcessorInfoBuffer->ExtendedInformation.Location2.Module, + &ProcessorInfoBuffer->ExtendedInformation.Location2.Core, + &ProcessorInfoBuffer->ExtendedInformation.Location2.Thread + ); + } + + if (HealthData != NULL) { + HealthData->Uint32 = CpuInfoInHob[ProcessorNumber].Health; + } + + return EFI_SUCCESS; +} + +/** + Worker function to switch the requested AP to be the BSP from that point onward. + + @param[in] ProcessorNumber The handle number of AP that is to become the new BSP. + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an + enabled AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval others Failed to switch BSP. + +**/ +EFI_STATUS +SwitchBSPWorker ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + CPU_STATE State; + MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr; + BOOLEAN OldInterruptState; + BOOLEAN OldTimerInterruptState; + + // + // Save and Disable Local APIC timer interrupt + // + OldTimerInterruptState = GetApicTimerInterruptState (); + DisableApicTimerInterrupt (); + // + // Before send both BSP and AP to a procedure to exchange their roles, + // interrupt must be disabled. This is because during the exchange role + // process, 2 CPU may use 1 stack. If interrupt happens, the stack will + // be corrupted, since interrupt return address will be pushed to stack + // by hardware. + // + OldInterruptState = SaveAndDisableInterrupts (); + + // + // Mask LINT0 & LINT1 for the old BSP + // + DisableLvtInterrupts (); + + CpuMpData = GetCpuMpData (); + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + if (ProcessorNumber >= CpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + // + // Check whether specified AP is disabled + // + State = GetApState (&CpuMpData->CpuData[ProcessorNumber]); + if (State == CpuStateDisabled) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether ProcessorNumber specifies the current BSP + // + if (ProcessorNumber == CpuMpData->BspNumber) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether specified AP is busy + // + if (State == CpuStateBusy) { + return EFI_NOT_READY; + } + + CpuMpData->BSPInfo.State = CPU_SWITCH_STATE_IDLE; + CpuMpData->APInfo.State = CPU_SWITCH_STATE_IDLE; + CpuMpData->SwitchBspFlag = TRUE; + CpuMpData->NewBspNumber = ProcessorNumber; + + // + // Clear the BSP bit of MSR_IA32_APIC_BASE + // + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Bits.BSP = 0; + AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); + + // + // Need to wakeUp AP (future BSP). + // + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, FutureBSPProc, CpuMpData, TRUE); + + AsmExchangeRole (&CpuMpData->BSPInfo, &CpuMpData->APInfo); + + // + // Set the BSP bit of MSR_IA32_APIC_BASE on new BSP + // + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Bits.BSP = 1; + AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); + ProgramVirtualWireMode (); + + // + // Wait for old BSP finished AP task + // + while (GetApState (&CpuMpData->CpuData[CallerNumber]) != CpuStateFinished) { + CpuPause (); + } + + CpuMpData->SwitchBspFlag = FALSE; + // + // Set old BSP enable state + // + if (!EnableOldBSP) { + SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateDisabled); + } else { + SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateIdle); + } + // + // Save new BSP number + // + CpuMpData->BspNumber = (UINT32) ProcessorNumber; + + // + // Restore interrupt state. + // + SetInterruptState (OldInterruptState); + + if (OldTimerInterruptState) { + EnableApicTimerInterrupt (); + } + + return EFI_SUCCESS; +} + +/** + Worker function to let the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of AP. + @param[in] EnableAP Specifies the new state for the processor for + enabled, FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies + the new health status of the AP. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval others Failed to Enable/Disable AP. + +**/ +EFI_STATUS +EnableDisableApWorker ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + + CpuMpData = GetCpuMpData (); + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + if (ProcessorNumber == CpuMpData->BspNumber) { + return EFI_INVALID_PARAMETER; + } + + if (ProcessorNumber >= CpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + if (!EnableAP) { + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateDisabled); + } else { + ResetProcessorToIdleState (ProcessorNumber); + } + + if (HealthFlag != NULL) { + CpuMpData->CpuData[ProcessorNumber].CpuHealthy = + (BOOLEAN) ((*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT) != 0); + } + + return EFI_SUCCESS; +} + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + @param[out] ProcessorNumber Pointer to the handle number of AP. + The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned + in ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibWhoAmI ( + OUT UINTN *ProcessorNumber + ) +{ + CPU_MP_DATA *CpuMpData; + + if (ProcessorNumber == NULL) { + return EFI_INVALID_PARAMETER; + } + + CpuMpData = GetCpuMpData (); + + return GetProcessorNumber (CpuMpData, ProcessorNumber); +} + +/** + Retrieves the number of logical processor in the platform and the number of + those logical processors that are enabled on this boot. This service may only + be called from the BSP. + + @param[out] NumberOfProcessors Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors + is NULL. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibGetNumberOfProcessors ( + OUT UINTN *NumberOfProcessors, OPTIONAL + OUT UINTN *NumberOfEnabledProcessors OPTIONAL + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + UINTN ProcessorNumber; + UINTN EnabledProcessorNumber; + UINTN Index; + + CpuMpData = GetCpuMpData (); + + if ((NumberOfProcessors == NULL) && (NumberOfEnabledProcessors == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + ProcessorNumber = CpuMpData->CpuCount; + EnabledProcessorNumber = 0; + for (Index = 0; Index < ProcessorNumber; Index++) { + if (GetApState (&CpuMpData->CpuData[Index]) != CpuStateDisabled) { + EnabledProcessorNumber ++; + } + } + + if (NumberOfProcessors != NULL) { + *NumberOfProcessors = ProcessorNumber; + } + if (NumberOfEnabledProcessors != NULL) { + *NumberOfEnabledProcessors = EnabledProcessorNumber; + } + + return EFI_SUCCESS; +} + + +/** + Worker function to execute a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute + the function specified by Procedure one by + one, in ascending order of processor handle + number. If FALSE, then all the enabled APs + execute the function specified by Procedure + simultaneously. + @param[in] ExcludeBsp Whether let BSP also trig this task. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] FailedCpuList If all APs finish successfully, then its + content is set to NULL. If not all APs + finish before timeout expires, then its + content is set to address of the buffer + holding handle numbers of the failed APs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval others Failed to Startup all APs. + +**/ +EFI_STATUS +StartupAllCPUsWorker ( + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN BOOLEAN ExcludeBsp, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT UINTN **FailedCpuList OPTIONAL + ) +{ + EFI_STATUS Status; + CPU_MP_DATA *CpuMpData; + UINTN ProcessorCount; + UINTN ProcessorNumber; + UINTN CallerNumber; + CPU_AP_DATA *CpuData; + BOOLEAN HasEnabledAp; + CPU_STATE ApState; + + CpuMpData = GetCpuMpData (); + + if (FailedCpuList != NULL) { + *FailedCpuList = NULL; + } + + if (CpuMpData->CpuCount == 1 && ExcludeBsp) { + return EFI_NOT_STARTED; + } + + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + // + // Update AP state + // + CheckAndUpdateApsStatus (); + + ProcessorCount = CpuMpData->CpuCount; + HasEnabledAp = FALSE; + // + // Check whether all enabled APs are idle. + // If any enabled AP is not idle, return EFI_NOT_READY. + // + for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) { + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + if (ProcessorNumber != CpuMpData->BspNumber) { + ApState = GetApState (CpuData); + if (ApState != CpuStateDisabled) { + HasEnabledAp = TRUE; + if (ApState != CpuStateIdle) { + // + // If any enabled APs are busy, return EFI_NOT_READY. + // + return EFI_NOT_READY; + } + } + } + } + + if (!HasEnabledAp && ExcludeBsp) { + // + // If no enabled AP exists and not include Bsp to do the procedure, return EFI_NOT_STARTED. + // + return EFI_NOT_STARTED; + } + + CpuMpData->RunningCount = 0; + for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) { + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + CpuData->Waiting = FALSE; + if (ProcessorNumber != CpuMpData->BspNumber) { + if (CpuData->State == CpuStateIdle) { + // + // Mark this processor as responsible for current calling. + // + CpuData->Waiting = TRUE; + CpuMpData->RunningCount++; + } + } + } + + CpuMpData->Procedure = Procedure; + CpuMpData->ProcArguments = ProcedureArgument; + CpuMpData->SingleThread = SingleThread; + CpuMpData->FinishedCount = 0; + CpuMpData->FailedCpuList = FailedCpuList; + CpuMpData->ExpectedTime = CalculateTimeout ( + TimeoutInMicroseconds, + &CpuMpData->CurrentTime + ); + CpuMpData->TotalTime = 0; + CpuMpData->WaitEvent = WaitEvent; + + if (!SingleThread) { + WakeUpAP (CpuMpData, TRUE, 0, Procedure, ProcedureArgument, FALSE); + } else { + for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) { + if (ProcessorNumber == CallerNumber) { + continue; + } + if (CpuMpData->CpuData[ProcessorNumber].Waiting) { + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument, TRUE); + break; + } + } + } + + if (!ExcludeBsp) { + // + // Start BSP. + // + Procedure (ProcedureArgument); + } + + Status = EFI_SUCCESS; + if (WaitEvent == NULL) { + do { + Status = CheckAllAPs (); + } while (Status == EFI_NOT_READY); + } + + return Status; +} + +/** + Worker function to let the caller get one enabled AP to execute a caller-provided + function. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] ProcessorNumber The handle number of the AP. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] Finished If AP returns from Procedure before the + timeout expires, its content is set to TRUE. + Otherwise, the value is set to FALSE. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before + the timeout expires. + @retval others Failed to Startup AP. + +**/ +EFI_STATUS +StartupThisAPWorker ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT BOOLEAN *Finished OPTIONAL + ) +{ + EFI_STATUS Status; + CPU_MP_DATA *CpuMpData; + CPU_AP_DATA *CpuData; + UINTN CallerNumber; + + CpuMpData = GetCpuMpData (); + + if (Finished != NULL) { + *Finished = FALSE; + } + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + // + // Check whether processor with the handle specified by ProcessorNumber exists + // + if (ProcessorNumber >= CpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + // + // Check whether specified processor is BSP + // + if (ProcessorNumber == CpuMpData->BspNumber) { + return EFI_INVALID_PARAMETER; + } + + // + // Check parameter Procedure + // + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Update AP state + // + CheckAndUpdateApsStatus (); + + // + // Check whether specified AP is disabled + // + if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) { + return EFI_INVALID_PARAMETER; + } + + // + // If WaitEvent is not NULL, execute in non-blocking mode. + // BSP saves data for CheckAPsStatus(), and returns EFI_SUCCESS. + // CheckAPsStatus() will check completion and timeout periodically. + // + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + CpuData->WaitEvent = WaitEvent; + CpuData->Finished = Finished; + CpuData->ExpectedTime = CalculateTimeout (TimeoutInMicroseconds, &CpuData->CurrentTime); + CpuData->TotalTime = 0; + + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument, TRUE); + + // + // If WaitEvent is NULL, execute in blocking mode. + // BSP checks AP's state until it finishes or TimeoutInMicrosecsond expires. + // + Status = EFI_SUCCESS; + if (WaitEvent == NULL) { + do { + Status = CheckThisAP (ProcessorNumber); + } while (Status == EFI_NOT_READY); + } + + return Status; +} + +/** + Get pointer to CPU MP Data structure from GUIDed HOB. + + @return The pointer to CPU MP Data structure. +**/ +CPU_MP_DATA * +GetCpuMpDataFromGuidedHob ( + VOID + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + VOID *DataInHob; + CPU_MP_DATA *CpuMpData; + + CpuMpData = NULL; + GuidHob = GetFirstGuidHob (&mCpuInitMpLibHobGuid); + if (GuidHob != NULL) { + DataInHob = GET_GUID_HOB_DATA (GuidHob); + CpuMpData = (CPU_MP_DATA *) (*(UINTN *) DataInHob); + } + return CpuMpData; +} + +/** + This service executes a caller provided function on all enabled CPUs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. Zero means + infinity. TimeoutInMicroseconds is ignored + for BSP. + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + + @retval EFI_SUCCESS In blocking mode, all CPUs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled CPUs. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + all enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +MpInitLibStartupAllCPUs ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + return StartupAllCPUsWorker ( + Procedure, + FALSE, + FALSE, + NULL, + TimeoutInMicroseconds, + ProcedureArgument, + NULL + ); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/MpLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/MpLib.h new file mode 100644 index 00000000..aea1d27e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/MpLib.h @@ -0,0 +1,745 @@ +/** @file + Common header file for MP Initialize Library. + + Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.<BR> + Copyright (c) 2020, AMD Inc. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _MP_LIB_H_ +#define _MP_LIB_H_ + +#include <PiPei.h> + +#include <Register/Intel/Cpuid.h> +#include <Register/Amd/Cpuid.h> +#include <Register/Intel/Msr.h> +#include <Register/Intel/LocalApic.h> +#include <Register/Intel/Microcode.h> + +#include <Library/MpInitLib.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DebugLib.h> +#include <Library/LocalApicLib.h> +#include <Library/CpuLib.h> +#include <Library/UefiCpuLib.h> +#include <Library/TimerLib.h> +#include <Library/SynchronizationLib.h> +#include <Library/MtrrLib.h> +#include <Library/HobLib.h> +#include <Library/PcdLib.h> +#include <Library/MicrocodeLib.h> + +#include <Guid/MicrocodePatchHob.h> + +#define WAKEUP_AP_SIGNAL SIGNATURE_32 ('S', 'T', 'A', 'P') + +#define CPU_INIT_MP_LIB_HOB_GUID \ + { \ + 0x58eb6a19, 0x3699, 0x4c68, { 0xa8, 0x36, 0xda, 0xcd, 0x8e, 0xdc, 0xad, 0x4a } \ + } + +// +// The MP data for switch BSP +// +#define CPU_SWITCH_STATE_IDLE 0 +#define CPU_SWITCH_STATE_STORED 1 +#define CPU_SWITCH_STATE_LOADED 2 + +// +// Default maximum number of entries to store the microcode patches information +// +#define DEFAULT_MAX_MICROCODE_PATCH_NUM 8 + +// +// Data structure for microcode patch information +// +typedef struct { + UINTN Address; + UINTN Size; +} MICROCODE_PATCH_INFO; + +// +// CPU exchange information for switch BSP +// +typedef struct { + UINT8 State; // offset 0 + UINTN StackPointer; // offset 4 / 8 + IA32_DESCRIPTOR Gdtr; // offset 8 / 16 + IA32_DESCRIPTOR Idtr; // offset 14 / 26 +} CPU_EXCHANGE_ROLE_INFO; + +// +// AP loop state when APs are in idle state +// It's value is the same with PcdCpuApLoopMode +// +typedef enum { + ApInHltLoop = 1, + ApInMwaitLoop = 2, + ApInRunLoop = 3 +} AP_LOOP_MODE; + +// +// AP initialization state during APs wakeup +// +typedef enum { + ApInitConfig = 1, + ApInitReconfig = 2, + ApInitDone = 3 +} AP_INIT_STATE; + +// +// AP state +// +// The state transitions for an AP when it process a procedure are: +// Idle ----> Ready ----> Busy ----> Idle +// [BSP] [AP] [AP] +// +typedef enum { + CpuStateIdle, + CpuStateReady, + CpuStateBusy, + CpuStateFinished, + CpuStateDisabled +} CPU_STATE; + +// +// CPU volatile registers around INIT-SIPI-SIPI +// +typedef struct { + UINTN Cr0; + UINTN Cr3; + UINTN Cr4; + UINTN Dr0; + UINTN Dr1; + UINTN Dr2; + UINTN Dr3; + UINTN Dr6; + UINTN Dr7; + IA32_DESCRIPTOR Gdtr; + IA32_DESCRIPTOR Idtr; + UINT16 Tr; +} CPU_VOLATILE_REGISTERS; + +// +// AP related data +// +typedef struct { + SPIN_LOCK ApLock; + volatile UINT32 *StartupApSignal; + volatile UINTN ApFunction; + volatile UINTN ApFunctionArgument; + BOOLEAN CpuHealthy; + volatile CPU_STATE State; + CPU_VOLATILE_REGISTERS VolatileRegisters; + BOOLEAN Waiting; + BOOLEAN *Finished; + UINT64 ExpectedTime; + UINT64 CurrentTime; + UINT64 TotalTime; + EFI_EVENT WaitEvent; + UINT32 ProcessorSignature; + UINT8 PlatformId; + UINT64 MicrocodeEntryAddr; + UINT32 MicrocodeRevision; +} CPU_AP_DATA; + +// +// Basic CPU information saved in Guided HOB. +// Because the contents will be shard between PEI and DXE, +// we need to make sure the each fields offset same in different +// architecture. +// +#pragma pack (1) +typedef struct { + UINT32 InitialApicId; + UINT32 ApicId; + UINT32 Health; + UINT64 ApTopOfStack; +} CPU_INFO_IN_HOB; +#pragma pack () + +// +// AP reset code information including code address and size, +// this structure will be shared be C code and assembly code. +// It is natural aligned by design. +// +typedef struct { + UINT8 *RendezvousFunnelAddress; + UINTN ModeEntryOffset; + UINTN RendezvousFunnelSize; + UINT8 *RelocateApLoopFuncAddress; + UINTN RelocateApLoopFuncSize; + UINTN ModeTransitionOffset; + UINTN SwitchToRealSize; + UINTN SwitchToRealOffset; + UINTN SwitchToRealNoNxOffset; + UINTN SwitchToRealPM16ModeOffset; + UINTN SwitchToRealPM16ModeSize; +} MP_ASSEMBLY_ADDRESS_MAP; + +typedef struct _CPU_MP_DATA CPU_MP_DATA; + +#pragma pack(1) + +// +// MP CPU exchange information for AP reset code +// This structure is required to be packed because fixed field offsets +// into this structure are used in assembly code in this module +// +typedef struct { + UINTN StackStart; + UINTN StackSize; + UINTN CFunction; + IA32_DESCRIPTOR GdtrProfile; + IA32_DESCRIPTOR IdtrProfile; + UINTN BufferStart; + UINTN ModeOffset; + UINTN ApIndex; + UINTN CodeSegment; + UINTN DataSegment; + UINTN EnableExecuteDisable; + UINTN Cr3; + UINTN InitFlag; + CPU_INFO_IN_HOB *CpuInfo; + UINTN NumApsExecuting; + CPU_MP_DATA *CpuMpData; + UINTN InitializeFloatingPointUnitsAddress; + UINT32 ModeTransitionMemory; + UINT16 ModeTransitionSegment; + UINT32 ModeHighMemory; + UINT16 ModeHighSegment; + // + // Enable5LevelPaging indicates whether 5-level paging is enabled in long mode. + // + BOOLEAN Enable5LevelPaging; + BOOLEAN SevEsIsEnabled; + UINTN GhcbBase; +} MP_CPU_EXCHANGE_INFO; + +#pragma pack() + +// +// CPU MP Data save in memory +// +struct _CPU_MP_DATA { + UINT64 CpuInfoInHob; + UINT32 CpuCount; + UINT32 BspNumber; + // + // The above fields data will be passed from PEI to DXE + // Please make sure the fields offset same in the different + // architecture. + // + SPIN_LOCK MpLock; + UINTN Buffer; + UINTN CpuApStackSize; + MP_ASSEMBLY_ADDRESS_MAP AddressMap; + UINTN WakeupBuffer; + UINTN WakeupBufferHigh; + UINTN BackupBuffer; + UINTN BackupBufferSize; + + volatile UINT32 FinishedCount; + UINT32 RunningCount; + BOOLEAN SingleThread; + EFI_AP_PROCEDURE Procedure; + VOID *ProcArguments; + BOOLEAN *Finished; + UINT64 ExpectedTime; + UINT64 CurrentTime; + UINT64 TotalTime; + EFI_EVENT WaitEvent; + UINTN **FailedCpuList; + + AP_INIT_STATE InitFlag; + BOOLEAN SwitchBspFlag; + UINTN NewBspNumber; + CPU_EXCHANGE_ROLE_INFO BSPInfo; + CPU_EXCHANGE_ROLE_INFO APInfo; + MTRR_SETTINGS MtrrTable; + UINT8 ApLoopMode; + UINT8 ApTargetCState; + UINT16 PmCodeSegment; + UINT16 Pm16CodeSegment; + CPU_AP_DATA *CpuData; + volatile MP_CPU_EXCHANGE_INFO *MpCpuExchangeInfo; + + UINT32 CurrentTimerCount; + UINTN DivideValue; + UINT8 Vector; + BOOLEAN PeriodicMode; + BOOLEAN TimerInterruptState; + UINT64 MicrocodePatchAddress; + UINT64 MicrocodePatchRegionSize; + + // + // Whether need to use Init-Sipi-Sipi to wake up the APs. + // Two cases need to set this value to TRUE. One is in HLT + // loop mode, the other is resume from S3 which loop mode + // will be hardcode change to HLT mode by PiSmmCpuDxeSmm + // driver. + // + BOOLEAN WakeUpByInitSipiSipi; + + BOOLEAN SevEsIsEnabled; + UINTN SevEsAPBuffer; + UINTN SevEsAPResetStackStart; + CPU_MP_DATA *NewCpuMpData; + + UINT64 GhcbBase; +}; + +#define AP_SAFE_STACK_SIZE 128 +#define AP_RESET_STACK_SIZE AP_SAFE_STACK_SIZE + +#pragma pack(1) + +typedef struct { + UINT8 InsnBuffer[8]; + UINT16 Rip; + UINT16 Segment; +} SEV_ES_AP_JMP_FAR; + +#pragma pack() + +/** + Assembly code to move an AP from long mode to real mode. + + Move an AP from long mode to real mode in preparation to invoking + the reset vector. This is used for SEV-ES guests where a hypervisor + is not allowed to set the CS and RIP to point to the reset vector. + + @param[in] BufferStart The reset vector target. + @param[in] Code16 16-bit protected mode code segment value. + @param[in] Code32 32-bit protected mode code segment value. + @param[in] StackStart The start of a stack to be used for transitioning + from long mode to real mode. +**/ +typedef +VOID +(EFIAPI AP_RESET) ( + IN UINTN BufferStart, + IN UINT16 Code16, + IN UINT16 Code32, + IN UINTN StackStart + ); + +extern EFI_GUID mCpuInitMpLibHobGuid; + +/** + Assembly code to place AP into safe loop mode. + + Place AP into targeted C-State if MONITOR is supported, otherwise + place AP into hlt state. + Place AP in protected mode if the current is long mode. Due to AP maybe + wakeup by some hardware event. It could avoid accessing page table that + may not available during booting to OS. + + @param[in] MwaitSupport TRUE indicates MONITOR is supported. + FALSE indicates MONITOR is not supported. + @param[in] ApTargetCState Target C-State value. + @param[in] PmCodeSegment Protected mode code segment value. +**/ +typedef +VOID +(EFIAPI * ASM_RELOCATE_AP_LOOP) ( + IN BOOLEAN MwaitSupport, + IN UINTN ApTargetCState, + IN UINTN PmCodeSegment, + IN UINTN TopOfApStack, + IN UINTN NumberToFinish, + IN UINTN Pm16CodeSegment, + IN UINTN SevEsAPJumpTable, + IN UINTN WakeupBuffer + ); + +/** + Assembly code to get starting address and size of the rendezvous entry for APs. + Information for fixing a jump instruction in the code is also returned. + + @param[out] AddressMap Output buffer for address map information. +**/ +VOID +EFIAPI +AsmGetAddressMap ( + OUT MP_ASSEMBLY_ADDRESS_MAP *AddressMap + ); + +/** + This function is called by both the BSP and the AP which is to become the BSP to + Exchange execution context including stack between them. After return from this + function, the BSP becomes AP and the AP becomes the BSP. + + @param[in] MyInfo Pointer to buffer holding the exchanging information for the executing processor. + @param[in] OthersInfo Pointer to buffer holding the exchanging information for the peer. + +**/ +VOID +EFIAPI +AsmExchangeRole ( + IN CPU_EXCHANGE_ROLE_INFO *MyInfo, + IN CPU_EXCHANGE_ROLE_INFO *OthersInfo + ); + +/** + Get the pointer to CPU MP Data structure. + + @return The pointer to CPU MP Data structure. +**/ +CPU_MP_DATA * +GetCpuMpData ( + VOID + ); + +/** + Save the pointer to CPU MP Data structure. + + @param[in] CpuMpData The pointer to CPU MP Data structure will be saved. +**/ +VOID +SaveCpuMpData ( + IN CPU_MP_DATA *CpuMpData + ); + + +/** + Get available system memory below 1MB by specified size. + + @param[in] WakeupBufferSize Wakeup buffer size required + + @retval other Return wakeup buffer address below 1MB. + @retval -1 Cannot find free memory below 1MB. +**/ +UINTN +GetWakeupBuffer ( + IN UINTN WakeupBufferSize + ); + +/** + Get available EfiBootServicesCode memory below 4GB by specified size. + + This buffer is required to safely transfer AP from real address mode to + protected mode or long mode, due to the fact that the buffer returned by + GetWakeupBuffer() may be marked as non-executable. + + @param[in] BufferSize Wakeup transition buffer size. + + @retval other Return wakeup transition buffer address below 4GB. + @retval 0 Cannot find free memory below 4GB. +**/ +UINTN +GetModeTransitionBuffer ( + IN UINTN BufferSize + ); + +/** + Return the address of the SEV-ES AP jump table. + + This buffer is required in order for an SEV-ES guest to transition from + UEFI into an OS. + + @return Return SEV-ES AP jump table buffer +**/ +UINTN +GetSevEsAPMemory ( + VOID + ); + +/** + This function will be called by BSP to wakeup AP. + + @param[in] CpuMpData Pointer to CPU MP Data + @param[in] Broadcast TRUE: Send broadcast IPI to all APs + FALSE: Send IPI to AP by ApicId + @param[in] ProcessorNumber The handle number of specified processor + @param[in] Procedure The function to be invoked by AP + @param[in] ProcedureArgument The argument to be passed into AP function + @param[in] WakeUpDisabledAps Whether need to wake up disabled APs in broadcast mode. +**/ +VOID +WakeUpAP ( + IN CPU_MP_DATA *CpuMpData, + IN BOOLEAN Broadcast, + IN UINTN ProcessorNumber, + IN EFI_AP_PROCEDURE Procedure, OPTIONAL + IN VOID *ProcedureArgument, OPTIONAL + IN BOOLEAN WakeUpDisabledAps OPTIONAL + ); + +/** + Initialize global data for MP support. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +InitMpGlobalData ( + IN CPU_MP_DATA *CpuMpData + ); + +/** + Worker function to execute a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute + the function specified by Procedure one by + one, in ascending order of processor handle + number. If FALSE, then all the enabled APs + execute the function specified by Procedure + simultaneously. + @param[in] ExcludeBsp Whether let BSP also trig this task. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] FailedCpuList If all APs finish successfully, then its + content is set to NULL. If not all APs + finish before timeout expires, then its + content is set to address of the buffer + holding handle numbers of the failed APs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval others Failed to Startup all APs. + +**/ +EFI_STATUS +StartupAllCPUsWorker ( + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN BOOLEAN ExcludeBsp, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT UINTN **FailedCpuList OPTIONAL + ); + +/** + Worker function to let the caller get one enabled AP to execute a caller-provided + function. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] ProcessorNumber The handle number of the AP. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] Finished If AP returns from Procedure before the + timeout expires, its content is set to TRUE. + Otherwise, the value is set to FALSE. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before + the timeout expires. + @retval others Failed to Startup AP. + +**/ +EFI_STATUS +StartupThisAPWorker ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT BOOLEAN *Finished OPTIONAL + ); + +/** + Worker function to switch the requested AP to be the BSP from that point onward. + + @param[in] ProcessorNumber The handle number of AP that is to become the new BSP. + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an + enabled AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval others Failed to switch BSP. + +**/ +EFI_STATUS +SwitchBSPWorker ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ); + +/** + Worker function to let the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of AP. + @param[in] EnableAP Specifies the new state for the processor for + enabled, FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies + the new health status of the AP. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval others Failed to Enable/Disable AP. + +**/ +EFI_STATUS +EnableDisableApWorker ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ); + +/** + Get pointer to CPU MP Data structure from GUIDed HOB. + + @return The pointer to CPU MP Data structure. +**/ +CPU_MP_DATA * +GetCpuMpDataFromGuidedHob ( + VOID + ); + +/** Checks status of specified AP. + + This function checks whether the specified AP has finished the task assigned + by StartupThisAP(), and whether timeout expires. + + @param[in] ProcessorNumber The handle number of processor. + + @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs(). + @retval EFI_TIMEOUT The timeout expires. + @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired. +**/ +EFI_STATUS +CheckThisAP ( + IN UINTN ProcessorNumber + ); + +/** + Checks status of all APs. + + This function checks whether all APs have finished task assigned by StartupAllAPs(), + and whether timeout expires. + + @retval EFI_SUCCESS All APs have finished task assigned by StartupAllAPs(). + @retval EFI_TIMEOUT The timeout expires. + @retval EFI_NOT_READY APs have not finished task and timeout has not expired. +**/ +EFI_STATUS +CheckAllAPs ( + VOID + ); + +/** + Checks APs status and updates APs status if needed. + +**/ +VOID +CheckAndUpdateApsStatus ( + VOID + ); + +/** + Detect whether specified processor can find matching microcode patch and load it. + + @param[in] CpuMpData The pointer to CPU MP Data structure. + @param[in] ProcessorNumber The handle number of the processor. The range is + from 0 to the total number of logical processors + minus 1. +**/ +VOID +MicrocodeDetect ( + IN CPU_MP_DATA *CpuMpData, + IN UINTN ProcessorNumber + ); + +/** + Shadow the required microcode patches data into memory. + + @param[in, out] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +ShadowMicrocodeUpdatePatch ( + IN OUT CPU_MP_DATA *CpuMpData + ); + +/** + Get the cached microcode patch base address and size from the microcode patch + information cache HOB. + + @param[out] Address Base address of the microcode patches data. + It will be updated if the microcode patch + information cache HOB is found. + @param[out] RegionSize Size of the microcode patches data. + It will be updated if the microcode patch + information cache HOB is found. + + @retval TRUE The microcode patch information cache HOB is found. + @retval FALSE The microcode patch information cache HOB is not found. + +**/ +BOOLEAN +GetMicrocodePatchInfoFromHob ( + UINT64 *Address, + UINT64 *RegionSize + ); + +/** + Detect whether Mwait-monitor feature is supported. + + @retval TRUE Mwait-monitor feature is supported. + @retval FALSE Mwait-monitor feature is not supported. +**/ +BOOLEAN +IsMwaitSupport ( + VOID + ); + +/** + Enable Debug Agent to support source debugging on AP function. + +**/ +VOID +EnableDebugAgent ( + VOID + ); + +/** + Find the current Processor number by APIC ID. + + @param[in] CpuMpData Pointer to PEI CPU MP Data + @param[out] ProcessorNumber Return the pocessor number found + + @retval EFI_SUCCESS ProcessorNumber is found and returned. + @retval EFI_NOT_FOUND ProcessorNumber is not found. +**/ +EFI_STATUS +GetProcessorNumber ( + IN CPU_MP_DATA *CpuMpData, + OUT UINTN *ProcessorNumber + ); + +/** + This funtion will try to invoke platform specific microcode shadow logic to + relocate microcode update patches into memory. + + @param[in, out] CpuMpData The pointer to CPU MP Data structure. + + @retval EFI_SUCCESS Shadow microcode success. + @retval EFI_OUT_OF_RESOURCES No enough resource to complete the operation. + @retval EFI_UNSUPPORTED Can't find platform specific microcode shadow + PPI/Protocol. +**/ +EFI_STATUS +PlatformShadowMicrocode ( + IN OUT CPU_MP_DATA *CpuMpData + ); + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf new file mode 100644 index 00000000..bd9a6b5f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf @@ -0,0 +1,74 @@ +## @file +# MP Initialize Library instance for PEI driver. +# +# Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PeiMpInitLib + MODULE_UNI_FILE = PeiMpInitLib.uni + FILE_GUID = B00F6090-7739-4830-B906-E0032D388987 + MODULE_TYPE = PEIM + VERSION_STRING = 1.1 + LIBRARY_CLASS = MpInitLib|PEIM + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.IA32] + Ia32/MpFuncs.nasm + +[Sources.X64] + X64/MpFuncs.nasm + +[Sources.common] + MpEqu.inc + PeiMpLib.c + MpLib.c + MpLib.h + Microcode.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + LocalApicLib + MemoryAllocationLib + HobLib + MtrrLib + CpuLib + UefiCpuLib + SynchronizationLib + PeiServicesLib + PcdLib + VmgExitLib + MicrocodeLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuBootLogicalProcessorNumber ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApInitTimeOutInMicroSeconds ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchAddress ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchRegionSize ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase ## CONSUMES + +[Ppis] + gEdkiiPeiShadowMicrocodePpiGuid ## SOMETIMES_CONSUMES + +[Guids] + gEdkiiS3SmmInitDoneGuid + gEdkiiMicrocodePatchHobGuid diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.uni new file mode 100644 index 00000000..81eca4e0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.uni @@ -0,0 +1,16 @@ +// /** @file
+// MP Initialize Library instance for PEI driver.
+//
+// MP Initialize Library instance for PEI driver.
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "MP Initialize Library instance for PEI driver."
+
+#string STR_MODULE_DESCRIPTION #language en-US "MP Initialize Library instance for PEI driver."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c new file mode 100644 index 00000000..4ee7ef6a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c @@ -0,0 +1,729 @@ +/** @file + MP initialize support functions for PEI phase. + + Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "MpLib.h" +#include <Library/PeiServicesLib.h> +#include <Guid/S3SmmInitDone.h> +#include <Ppi/ShadowMicrocode.h> + +/** + S3 SMM Init Done notification function. + + @param PeiServices Indirect reference to the PEI Services Table. + @param NotifyDesc Address of the notification descriptor data structure. + @param InvokePpi Address of the PPI that was invoked. + + @retval EFI_SUCCESS The function completes successfully. + +**/ +EFI_STATUS +EFIAPI +NotifyOnS3SmmInitDonePpi ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, + IN VOID *InvokePpi + ); + + +// +// Global function +// +EFI_PEI_NOTIFY_DESCRIPTOR mS3SmmInitDoneNotifyDesc = { + EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, + &gEdkiiS3SmmInitDoneGuid, + NotifyOnS3SmmInitDonePpi +}; + +/** + S3 SMM Init Done notification function. + + @param PeiServices Indirect reference to the PEI Services Table. + @param NotifyDesc Address of the notification descriptor data structure. + @param InvokePpi Address of the PPI that was invoked. + + @retval EFI_SUCCESS The function completes successfully. + +**/ +EFI_STATUS +EFIAPI +NotifyOnS3SmmInitDonePpi ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc, + IN VOID *InvokePpi + ) +{ + CPU_MP_DATA *CpuMpData; + + CpuMpData = GetCpuMpData (); + + // + // PiSmmCpuDxeSmm driver hardcode change the loop mode to HLT mode. + // So in this notify function, code need to check the current loop + // mode, if it is not HLT mode, code need to change loop mode back + // to the original mode. + // + if (CpuMpData->ApLoopMode != ApInHltLoop) { + CpuMpData->WakeUpByInitSipiSipi = TRUE; + } + + return EFI_SUCCESS; +} + + +/** + Enable Debug Agent to support source debugging on AP function. + +**/ +VOID +EnableDebugAgent ( + VOID + ) +{ +} + +/** + Get pointer to CPU MP Data structure. + For BSP, the pointer is retrieved from HOB. + For AP, the structure is just after IDT. + + @return The pointer to CPU MP Data structure. +**/ +CPU_MP_DATA * +GetCpuMpData ( + VOID + ) +{ + CPU_MP_DATA *CpuMpData; + MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr; + IA32_DESCRIPTOR Idtr; + + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + if (ApicBaseMsr.Bits.BSP == 1) { + CpuMpData = GetCpuMpDataFromGuidedHob (); + ASSERT (CpuMpData != NULL); + } else { + AsmReadIdtr (&Idtr); + CpuMpData = (CPU_MP_DATA *) (Idtr.Base + Idtr.Limit + 1); + } + return CpuMpData; +} + +/** + Save the pointer to CPU MP Data structure. + + @param[in] CpuMpData The pointer to CPU MP Data structure will be saved. +**/ +VOID +SaveCpuMpData ( + IN CPU_MP_DATA *CpuMpData + ) +{ + UINT64 Data64; + // + // Build location of CPU MP DATA buffer in HOB + // + Data64 = (UINT64) (UINTN) CpuMpData; + BuildGuidDataHob ( + &mCpuInitMpLibHobGuid, + (VOID *) &Data64, + sizeof (UINT64) + ); +} + +/** + Check if AP wakeup buffer is overlapped with existing allocated buffer. + + @param[in] WakeupBufferStart AP wakeup buffer start address. + @param[in] WakeupBufferEnd AP wakeup buffer end address. + + @retval TRUE There is overlap. + @retval FALSE There is no overlap. +**/ +BOOLEAN +CheckOverlapWithAllocatedBuffer ( + IN UINT64 WakeupBufferStart, + IN UINT64 WakeupBufferEnd + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_MEMORY_ALLOCATION *MemoryHob; + BOOLEAN Overlapped; + UINT64 MemoryStart; + UINT64 MemoryEnd; + + Overlapped = FALSE; + // + // Get the HOB list for processing + // + Hob.Raw = GetHobList (); + // + // Collect memory ranges + // + while (!END_OF_HOB_LIST (Hob)) { + if (Hob.Header->HobType == EFI_HOB_TYPE_MEMORY_ALLOCATION) { + MemoryHob = Hob.MemoryAllocation; + MemoryStart = MemoryHob->AllocDescriptor.MemoryBaseAddress; + MemoryEnd = MemoryHob->AllocDescriptor.MemoryBaseAddress + MemoryHob->AllocDescriptor.MemoryLength; + if (!((WakeupBufferStart >= MemoryEnd) || (WakeupBufferEnd <= MemoryStart))) { + Overlapped = TRUE; + break; + } + } + Hob.Raw = GET_NEXT_HOB (Hob); + } + return Overlapped; +} + +/** + Get available system memory below 1MB by specified size. + + @param[in] WakeupBufferSize Wakeup buffer size required + + @retval other Return wakeup buffer address below 1MB. + @retval -1 Cannot find free memory below 1MB. +**/ +UINTN +GetWakeupBuffer ( + IN UINTN WakeupBufferSize + ) +{ + EFI_PEI_HOB_POINTERS Hob; + UINT64 WakeupBufferStart; + UINT64 WakeupBufferEnd; + + WakeupBufferSize = (WakeupBufferSize + SIZE_4KB - 1) & ~(SIZE_4KB - 1); + + // + // Get the HOB list for processing + // + Hob.Raw = GetHobList (); + + // + // Collect memory ranges + // + while (!END_OF_HOB_LIST (Hob)) { + if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { + if ((Hob.ResourceDescriptor->PhysicalStart < BASE_1MB) && + (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) && + ((Hob.ResourceDescriptor->ResourceAttribute & + (EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED | + EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED | + EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED + )) == 0) + ) { + // + // Need memory under 1MB to be collected here + // + WakeupBufferEnd = Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength; + if (WakeupBufferEnd > BASE_1MB) { + // + // Wakeup buffer should be under 1MB + // + WakeupBufferEnd = BASE_1MB; + } + while (WakeupBufferEnd > WakeupBufferSize) { + // + // Wakeup buffer should be aligned on 4KB + // + WakeupBufferStart = (WakeupBufferEnd - WakeupBufferSize) & ~(SIZE_4KB - 1); + if (WakeupBufferStart < Hob.ResourceDescriptor->PhysicalStart) { + break; + } + if (CheckOverlapWithAllocatedBuffer (WakeupBufferStart, WakeupBufferEnd)) { + // + // If this range is overlapped with existing allocated buffer, skip it + // and find the next range + // + WakeupBufferEnd -= WakeupBufferSize; + continue; + } + DEBUG ((DEBUG_INFO, "WakeupBufferStart = %x, WakeupBufferSize = %x\n", + WakeupBufferStart, WakeupBufferSize)); + return (UINTN)WakeupBufferStart; + } + } + } + // + // Find the next HOB + // + Hob.Raw = GET_NEXT_HOB (Hob); + } + + return (UINTN) -1; +} + +/** + Get available EfiBootServicesCode memory below 4GB by specified size. + + This buffer is required to safely transfer AP from real address mode to + protected mode or long mode, due to the fact that the buffer returned by + GetWakeupBuffer() may be marked as non-executable. + + @param[in] BufferSize Wakeup transition buffer size. + + @retval other Return wakeup transition buffer address below 4GB. + @retval 0 Cannot find free memory below 4GB. +**/ +UINTN +GetModeTransitionBuffer ( + IN UINTN BufferSize + ) +{ + // + // PEI phase doesn't need to do such transition. So simply return 0. + // + return 0; +} + +/** + Return the address of the SEV-ES AP jump table. + + This buffer is required in order for an SEV-ES guest to transition from + UEFI into an OS. + + @return Return SEV-ES AP jump table buffer +**/ +UINTN +GetSevEsAPMemory ( + VOID + ) +{ + // + // PEI phase doesn't need to do such transition. So simply return 0. + // + return 0; +} + +/** + Checks APs status and updates APs status if needed. + +**/ +VOID +CheckAndUpdateApsStatus ( + VOID + ) +{ +} + +/** + Build the microcode patch HOB that contains the base address and size of the + microcode patch stored in the memory. + + @param[in] CpuMpData Pointer to the CPU_MP_DATA structure. + +**/ +VOID +BuildMicrocodeCacheHob ( + IN CPU_MP_DATA *CpuMpData + ) +{ + EDKII_MICROCODE_PATCH_HOB *MicrocodeHob; + UINTN HobDataLength; + UINT32 Index; + + HobDataLength = sizeof (EDKII_MICROCODE_PATCH_HOB) + + sizeof (UINT64) * CpuMpData->CpuCount; + + MicrocodeHob = AllocatePool (HobDataLength); + if (MicrocodeHob == NULL) { + ASSERT (FALSE); + return; + } + + // + // Store the information of the memory region that holds the microcode patches. + // + MicrocodeHob->MicrocodePatchAddress = CpuMpData->MicrocodePatchAddress; + MicrocodeHob->MicrocodePatchRegionSize = CpuMpData->MicrocodePatchRegionSize; + + // + // Store the detected microcode patch for each processor as well. + // + MicrocodeHob->ProcessorCount = CpuMpData->CpuCount; + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + if (CpuMpData->CpuData[Index].MicrocodeEntryAddr != 0) { + MicrocodeHob->ProcessorSpecificPatchOffset[Index] = + CpuMpData->CpuData[Index].MicrocodeEntryAddr - CpuMpData->MicrocodePatchAddress; + } else { + MicrocodeHob->ProcessorSpecificPatchOffset[Index] = MAX_UINT64; + } + } + + BuildGuidDataHob ( + &gEdkiiMicrocodePatchHobGuid, + MicrocodeHob, + HobDataLength + ); + + return; +} + +/** + Initialize global data for MP support. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +InitMpGlobalData ( + IN CPU_MP_DATA *CpuMpData + ) +{ + EFI_STATUS Status; + + BuildMicrocodeCacheHob (CpuMpData); + SaveCpuMpData (CpuMpData); + + /// + /// Install Notify + /// + Status = PeiServicesNotifyPpi (&mS3SmmInitDoneNotifyDesc); + ASSERT_EFI_ERROR (Status); +} + +/** + This service executes a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] SingleThread If TRUE, then all the enabled APs execute + the function specified by Procedure one by + one, in ascending order of processor handle + number. If FALSE, then all the enabled APs + execute the function specified by Procedure + simultaneously. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until all APs finish + or TimeoutInMicroSeconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on all the enabled + APs, and go on executing immediately. If + all return from Procedure, or TimeoutInMicroSeconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + all APs return from Procedure, then Procedure + on the failed APs is terminated. All enabled + APs are available for next function assigned + by MpInitLibStartupAllAPs() or + MPInitLibStartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise, + if all APs finish successfully, then its + content is set to NULL. If not all APs + finish before timeout expires, then its + content is set to address of the buffer + holding handle numbers of the failed APs. + The buffer is allocated by MP Initialization + library, and it's the caller's responsibility to + free the buffer with FreePool() service. + In blocking mode, it is ready for consumption + when the call returns. In non-blocking mode, + it is ready when WaitEvent is signaled. The + list of failed CPU is terminated by + END_OF_CPU_LIST. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not + supported. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + all enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +MpInitLibStartupAllAPs ( + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT UINTN **FailedCpuList OPTIONAL + ) +{ + if (WaitEvent != NULL) { + return EFI_UNSUPPORTED; + } + + return StartupAllCPUsWorker ( + Procedure, + SingleThread, + TRUE, + NULL, + TimeoutInMicroseconds, + ProcedureArgument, + FailedCpuList + ); +} + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. + + @param[in] Procedure A pointer to the function to be run on the + designated AP of the system. See type + EFI_AP_PROCEDURE. + @param[in] ProcessorNumber The handle number of the AP. The range is + from 0 to the total number of logical + processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until this AP finish + or TimeoutInMicroSeconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on this AP, + and go on executing immediately. If this AP + return from Procedure or TimeoutInMicroSeconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + this AP to finish this Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + this AP returns from Procedure, then Procedure + on the AP is terminated. The + AP is available for next function assigned + by MpInitLibStartupAllAPs() or + MpInitLibStartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure on the + specified AP. + @param[out] Finished If NULL, this parameter is ignored. In + blocking mode, this parameter is ignored. + In non-blocking mode, if AP returns from + Procedure before the timeout expires, its + content is set to TRUE. Otherwise, the + value is set to FALSE. The caller can + determine if the AP returned from Procedure + by evaluating this value. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before + the timeout expires. + @retval EFI_SUCCESS In non-blocking mode, the function has been + dispatched to specified AP. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not + supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + the specified AP has finished. + @retval EFI_NOT_READY The specified AP is busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +MpInitLibStartupThisAP ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT BOOLEAN *Finished OPTIONAL + ) +{ + if (WaitEvent != NULL) { + return EFI_UNSUPPORTED; + } + + return StartupThisAPWorker ( + Procedure, + ProcessorNumber, + NULL, + TimeoutInMicroseconds, + ProcedureArgument, + Finished + ); +} + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + @param[in] ProcessorNumber The handle number of AP that is to become the new + BSP. The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an + enabled AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to + this service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or + a disabled AP. + @retval EFI_NOT_READY The specified AP is busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibSwitchBSP ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + return SwitchBSPWorker (ProcessorNumber, EnableOldBSP); +} + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of AP. + The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for + enabled, FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies + the new health status of the AP. This flag + corresponds to StatusFlag defined in + EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only + the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter + is ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed + prior to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibEnableDisableAP ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + return EnableDisableApWorker (ProcessorNumber, EnableAP, HealthFlag); +} + +/** + This funtion will try to invoke platform specific microcode shadow logic to + relocate microcode update patches into memory. + + @param[in, out] CpuMpData The pointer to CPU MP Data structure. + + @retval EFI_SUCCESS Shadow microcode success. + @retval EFI_OUT_OF_RESOURCES No enough resource to complete the operation. + @retval EFI_UNSUPPORTED Can't find platform specific microcode shadow + PPI/Protocol. +**/ +EFI_STATUS +PlatformShadowMicrocode ( + IN OUT CPU_MP_DATA *CpuMpData + ) +{ + EFI_STATUS Status; + EDKII_PEI_SHADOW_MICROCODE_PPI *ShadowMicrocodePpi; + UINTN CpuCount; + EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId; + UINTN Index; + UINTN BufferSize; + VOID *Buffer; + + Status = PeiServicesLocatePpi ( + &gEdkiiPeiShadowMicrocodePpiGuid, + 0, + NULL, + (VOID **) &ShadowMicrocodePpi + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + CpuCount = CpuMpData->CpuCount; + MicrocodeCpuId = (EDKII_PEI_MICROCODE_CPU_ID *) AllocateZeroPool (sizeof (EDKII_PEI_MICROCODE_CPU_ID) * CpuCount); + if (MicrocodeCpuId == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + MicrocodeCpuId[Index].ProcessorSignature = CpuMpData->CpuData[Index].ProcessorSignature; + MicrocodeCpuId[Index].PlatformId = CpuMpData->CpuData[Index].PlatformId; + } + + Status = ShadowMicrocodePpi->ShadowMicrocode ( + ShadowMicrocodePpi, + CpuCount, + MicrocodeCpuId, + &BufferSize, + &Buffer + ); + FreePool (MicrocodeCpuId); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + CpuMpData->MicrocodePatchAddress = (UINTN) Buffer; + CpuMpData->MicrocodePatchRegionSize = BufferSize; + + DEBUG (( + DEBUG_INFO, + "%a: Required microcode patches have been loaded at 0x%lx, with size 0x%lx.\n", + __FUNCTION__, CpuMpData->MicrocodePatchAddress, CpuMpData->MicrocodePatchRegionSize + )); + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm new file mode 100644 index 00000000..cfc3085d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm @@ -0,0 +1,778 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; MpFuncs.nasm +; +; Abstract: +; +; This is the assembly code for MP support +; +;------------------------------------------------------------------------------- + +%include "MpEqu.inc" +extern ASM_PFX(InitializeFloatingPointUnits) + +DEFAULT REL + +SECTION .text + +;------------------------------------------------------------------------------------- +;RendezvousFunnelProc procedure follows. All APs execute their procedure. This +;procedure serializes all the AP processors through an Init sequence. It must be +;noted that APs arrive here very raw...ie: real mode, no stack. +;ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC +;IS IN MACHINE CODE. +;------------------------------------------------------------------------------------- +global ASM_PFX(RendezvousFunnelProc) +ASM_PFX(RendezvousFunnelProc): +RendezvousFunnelProcStart: +; At this point CS = 0x(vv00) and ip= 0x0. +; Save BIST information to ebp firstly + +BITS 16 + mov ebp, eax ; Save BIST information + + mov ax, cs + mov ds, ax + mov es, ax + mov ss, ax + xor ax, ax + mov fs, ax + mov gs, ax + + mov si, MP_CPU_EXCHANGE_INFO_FIELD (BufferStart) + mov ebx, [si] + + mov si, MP_CPU_EXCHANGE_INFO_FIELD (DataSegment) + mov edx, [si] + + ; + ; Get start address of 32-bit code in low memory (<1MB) + ; + mov edi, MP_CPU_EXCHANGE_INFO_FIELD (ModeTransitionMemory) + + mov si, MP_CPU_EXCHANGE_INFO_FIELD (GdtrProfile) +o32 lgdt [cs:si] + + mov si, MP_CPU_EXCHANGE_INFO_FIELD (IdtrProfile) +o32 lidt [cs:si] + + ; + ; Switch to protected mode + ; + mov eax, cr0 ; Get control register 0 + or eax, 000000003h ; Set PE bit (bit #0) & MP + mov cr0, eax + + ; Switch to 32-bit code (>1MB) +o32 jmp far [cs:di] + +; +; Following code must be copied to memory with type of EfiBootServicesCode. +; This is required if NX is enabled for EfiBootServicesCode of memory. +; +BITS 32 +Flat32Start: ; protected mode entry point + mov ds, dx + mov es, dx + mov fs, dx + mov gs, dx + mov ss, dx + + ; + ; Enable execute disable bit + ; + mov esi, MP_CPU_EXCHANGE_INFO_FIELD (EnableExecuteDisable) + cmp byte [ebx + esi], 0 + jz SkipEnableExecuteDisableBit + + mov ecx, 0c0000080h ; EFER MSR number + rdmsr ; Read EFER + bts eax, 11 ; Enable Execute Disable Bit + wrmsr ; Write EFER + +SkipEnableExecuteDisableBit: + ; + ; Enable PAE + ; + mov eax, cr4 + bts eax, 5 + + mov esi, MP_CPU_EXCHANGE_INFO_FIELD (Enable5LevelPaging) + cmp byte [ebx + esi], 0 + jz SkipEnable5LevelPaging + + ; + ; Enable 5 Level Paging + ; + bts eax, 12 ; Set LA57=1. + +SkipEnable5LevelPaging: + + mov cr4, eax + + ; + ; Load page table + ; + mov esi, MP_CPU_EXCHANGE_INFO_FIELD (Cr3) ; Save CR3 in ecx + mov ecx, [ebx + esi] + mov cr3, ecx ; Load CR3 + + ; + ; Enable long mode + ; + mov ecx, 0c0000080h ; EFER MSR number + rdmsr ; Read EFER + bts eax, 8 ; Set LME=1 + wrmsr ; Write EFER + + ; + ; Enable paging + ; + mov eax, cr0 ; Read CR0 + bts eax, 31 ; Set PG=1 + mov cr0, eax ; Write CR0 + + ; + ; Far jump to 64-bit code + ; + mov edi, MP_CPU_EXCHANGE_INFO_FIELD (ModeHighMemory) + add edi, ebx + jmp far [edi] + +BITS 64 +LongModeStart: + mov esi, ebx + lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (InitFlag)] + cmp qword [edi], 1 ; ApInitConfig + jnz GetApicId + + ; Increment the number of APs executing here as early as possible + ; This is decremented in C code when AP is finished executing + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (NumApsExecuting) + lock inc dword [edi] + + ; AP init + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (ApIndex) + mov ebx, 1 + lock xadd dword [edi], ebx ; EBX = ApIndex++ + inc ebx ; EBX is CpuNumber + + ; program stack + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (StackSize) + mov eax, dword [edi] + mov ecx, ebx + inc ecx + mul ecx ; EAX = StackSize * (CpuNumber + 1) + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (StackStart) + add rax, qword [edi] + mov rsp, rax + + lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevEsIsEnabled)] + cmp byte [edi], 1 ; SevEsIsEnabled + jne CProcedureInvoke + + ; + ; program GHCB + ; Each page after the GHCB is a per-CPU page, so the calculation programs + ; a GHCB to be every 8KB. + ; + mov eax, SIZE_4KB + shl eax, 1 ; EAX = SIZE_4K * 2 + mov ecx, ebx + mul ecx ; EAX = SIZE_4K * 2 * CpuNumber + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (GhcbBase) + add rax, qword [edi] + mov rdx, rax + shr rdx, 32 + mov rcx, 0xc0010130 + wrmsr + jmp CProcedureInvoke + +GetApicId: + lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevEsIsEnabled)] + cmp byte [edi], 1 ; SevEsIsEnabled + jne DoCpuid + + ; + ; Since we don't have a stack yet, we can't take a #VC + ; exception. Use the GHCB protocol to perform the CPUID + ; calls. + ; + mov rcx, 0xc0010130 + rdmsr + shl rdx, 32 + or rax, rdx + mov rdi, rax ; RDI now holds the original GHCB GPA + + mov rdx, 0 ; CPUID function 0 + mov rax, 0 ; RAX register requested + or rax, 4 + wrmsr + rep vmmcall + rdmsr + cmp edx, 0bh + jb NoX2ApicSevEs ; CPUID level below CPUID_EXTENDED_TOPOLOGY + + mov rdx, 0bh ; CPUID function 0x0b + mov rax, 040000000h ; RBX register requested + or rax, 4 + wrmsr + rep vmmcall + rdmsr + test edx, 0ffffh + jz NoX2ApicSevEs ; CPUID.0BH:EBX[15:0] is zero + + mov rdx, 0bh ; CPUID function 0x0b + mov rax, 0c0000000h ; RDX register requested + or rax, 4 + wrmsr + rep vmmcall + rdmsr + + ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX + jmp RestoreGhcb + +NoX2ApicSevEs: + ; Processor is not x2APIC capable, so get 8-bit APIC ID + mov rdx, 1 ; CPUID function 1 + mov rax, 040000000h ; RBX register requested + or rax, 4 + wrmsr + rep vmmcall + rdmsr + shr edx, 24 + +RestoreGhcb: + mov rbx, rdx ; Save x2APIC/APIC ID + + mov rdx, rdi ; RDI holds the saved GHCB GPA + shr rdx, 32 + mov eax, edi + wrmsr + + mov rdx, rbx + + ; x2APIC ID or APIC ID is in EDX + jmp GetProcessorNumber + +DoCpuid: + mov eax, 0 + cpuid + cmp eax, 0bh + jb NoX2Apic ; CPUID level below CPUID_EXTENDED_TOPOLOGY + + mov eax, 0bh + xor ecx, ecx + cpuid + test ebx, 0ffffh + jz NoX2Apic ; CPUID.0BH:EBX[15:0] is zero + + ; Processor is x2APIC capable; 32-bit x2APIC ID is already in EDX + jmp GetProcessorNumber + +NoX2Apic: + ; Processor is not x2APIC capable, so get 8-bit APIC ID + mov eax, 1 + cpuid + shr ebx, 24 + mov edx, ebx + +GetProcessorNumber: + ; + ; Get processor number for this AP + ; Note that BSP may become an AP due to SwitchBsp() + ; + xor ebx, ebx + lea eax, [esi + MP_CPU_EXCHANGE_INFO_FIELD (CpuInfo)] + mov rdi, [eax] + +GetNextProcNumber: + cmp dword [rdi + CPU_INFO_IN_HOB.InitialApicId], edx ; APIC ID match? + jz ProgramStack + add rdi, CPU_INFO_IN_HOB_size + inc ebx + jmp GetNextProcNumber + +ProgramStack: + mov rsp, qword [rdi + CPU_INFO_IN_HOB.ApTopOfStack] + +CProcedureInvoke: + push rbp ; Push BIST data at top of AP stack + xor rbp, rbp ; Clear ebp for call stack trace + push rbp + mov rbp, rsp + + mov rax, qword [esi + MP_CPU_EXCHANGE_INFO_FIELD (InitializeFloatingPointUnits)] + sub rsp, 20h + call rax ; Call assembly function to initialize FPU per UEFI spec + add rsp, 20h + + mov edx, ebx ; edx is ApIndex + mov ecx, esi + add ecx, MP_CPU_EXCHANGE_INFO_OFFSET ; rcx is address of exchange info data buffer + + mov edi, esi + add edi, MP_CPU_EXCHANGE_INFO_FIELD (CFunction) + mov rax, qword [edi] + + sub rsp, 20h + call rax ; Invoke C function + add rsp, 20h + jmp $ ; Should never reach here + +RendezvousFunnelProcEnd: + +;------------------------------------------------------------------------------------- +;SwitchToRealProc procedure follows. +;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT MODE. HENCE THIS PROC +;IS IN MACHINE CODE. +; SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32, UINTN StackStart) +; rcx - Buffer Start +; rdx - Code16 Selector Offset +; r8 - Code32 Selector Offset +; r9 - Stack Start +;------------------------------------------------------------------------------------- +global ASM_PFX(SwitchToRealProc) +ASM_PFX(SwitchToRealProc): +SwitchToRealProcStart: +BITS 64 + cli + + ; + ; Get RDX reset value before changing stacks since the + ; new stack won't be able to accomodate a #VC exception. + ; + push rax + push rbx + push rcx + push rdx + + mov rax, 1 + cpuid + mov rsi, rax ; Save off the reset value for RDX + + pop rdx + pop rcx + pop rbx + pop rax + + ; + ; Establish stack below 1MB + ; + mov rsp, r9 + + ; + ; Push ultimate Reset Vector onto the stack + ; + mov rax, rcx + shr rax, 4 + push word 0x0002 ; RFLAGS + push ax ; CS + push word 0x0000 ; RIP + push word 0x0000 ; For alignment, will be discarded + + ; + ; Get address of "16-bit operand size" label + ; + lea rbx, [PM16Mode] + + ; + ; Push addresses used to change to compatibility mode + ; + lea rax, [CompatMode] + push r8 + push rax + + ; + ; Clear R8 - R15, for reset, before going into 32-bit mode + ; + xor r8, r8 + xor r9, r9 + xor r10, r10 + xor r11, r11 + xor r12, r12 + xor r13, r13 + xor r14, r14 + xor r15, r15 + + ; + ; Far return into 32-bit mode + ; +o64 retf + +BITS 32 +CompatMode: + ; + ; Set up stack to prepare for exiting protected mode + ; + push edx ; Code16 CS + push ebx ; PM16Mode label address + + ; + ; Disable paging + ; + mov eax, cr0 ; Read CR0 + btr eax, 31 ; Set PG=0 + mov cr0, eax ; Write CR0 + + ; + ; Disable long mode + ; + mov ecx, 0c0000080h ; EFER MSR number + rdmsr ; Read EFER + btr eax, 8 ; Set LME=0 + wrmsr ; Write EFER + + ; + ; Disable PAE + ; + mov eax, cr4 ; Read CR4 + btr eax, 5 ; Set PAE=0 + mov cr4, eax ; Write CR4 + + mov edx, esi ; Restore RDX reset value + + ; + ; Switch to 16-bit operand size + ; + retf + +BITS 16 + ; + ; At entry to this label + ; - RDX will have its reset value + ; - On the top of the stack + ; - Alignment data (two bytes) to be discarded + ; - IP for Real Mode (two bytes) + ; - CS for Real Mode (two bytes) + ; + ; This label is also used with AsmRelocateApLoop. During MP finalization, + ; the code from PM16Mode to SwitchToRealProcEnd is copied to the start of + ; the WakeupBuffer, allowing a parked AP to be booted by an OS. + ; +PM16Mode: + mov eax, cr0 ; Read CR0 + btr eax, 0 ; Set PE=0 + mov cr0, eax ; Write CR0 + + pop ax ; Discard alignment data + + ; + ; Clear registers (except RDX and RSP) before going into 16-bit mode + ; + xor eax, eax + xor ebx, ebx + xor ecx, ecx + xor esi, esi + xor edi, edi + xor ebp, ebp + + iret + +SwitchToRealProcEnd: + +;------------------------------------------------------------------------------------- +; AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish, Pm16CodeSegment, SevEsAPJumpTable, WakeupBuffer); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmRelocateApLoop) +ASM_PFX(AsmRelocateApLoop): +AsmRelocateApLoopStart: +BITS 64 + cmp qword [rsp + 56], 0 ; SevEsAPJumpTable + je NoSevEs + + ; + ; Perform some SEV-ES related setup before leaving 64-bit mode + ; + push rcx + push rdx + + ; + ; Get the RDX reset value using CPUID + ; + mov rax, 1 + cpuid + mov rsi, rax ; Save off the reset value for RDX + + ; + ; Prepare the GHCB for the AP_HLT_LOOP VMGEXIT call + ; - Must be done while in 64-bit long mode so that writes to + ; the GHCB memory will be unencrypted. + ; - No NAE events can be generated once this is set otherwise + ; the AP_RESET_HOLD SW_EXITCODE will be overwritten. + ; + mov rcx, 0xc0010130 + rdmsr ; Retrieve current GHCB address + shl rdx, 32 + or rdx, rax + + mov rdi, rdx + xor rax, rax + mov rcx, 0x800 + shr rcx, 3 + rep stosq ; Clear the GHCB + + mov rax, 0x80000004 ; VMGEXIT AP_RESET_HOLD + mov [rdx + 0x390], rax + mov rax, 114 ; Set SwExitCode valid bit + bts [rdx + 0x3f0], rax + inc rax ; Set SwExitInfo1 valid bit + bts [rdx + 0x3f0], rax + inc rax ; Set SwExitInfo2 valid bit + bts [rdx + 0x3f0], rax + + pop rdx + pop rcx + +NoSevEs: + cli ; Disable interrupt before switching to 32-bit mode + mov rax, [rsp + 40] ; CountTofinish + lock dec dword [rax] ; (*CountTofinish)-- + + mov r10, [rsp + 48] ; Pm16CodeSegment + mov rax, [rsp + 56] ; SevEsAPJumpTable + mov rbx, [rsp + 64] ; WakeupBuffer + mov rsp, r9 ; TopOfApStack + + push rax ; Save SevEsAPJumpTable + push rbx ; Save WakeupBuffer + push r10 ; Save Pm16CodeSegment + push rcx ; Save MwaitSupport + push rdx ; Save ApTargetCState + + lea rax, [PmEntry] ; rax <- The start address of transition code + + push r8 + push rax + + ; + ; Clear R8 - R15, for reset, before going into 32-bit mode + ; + xor r8, r8 + xor r9, r9 + xor r10, r10 + xor r11, r11 + xor r12, r12 + xor r13, r13 + xor r14, r14 + xor r15, r15 + + ; + ; Far return into 32-bit mode + ; +o64 retf + +BITS 32 +PmEntry: + mov eax, cr0 + btr eax, 31 ; Clear CR0.PG + mov cr0, eax ; Disable paging and caches + + mov ecx, 0xc0000080 + rdmsr + and ah, ~ 1 ; Clear LME + wrmsr + mov eax, cr4 + and al, ~ (1 << 5) ; Clear PAE + mov cr4, eax + + pop edx + add esp, 4 + pop ecx, + add esp, 4 + +MwaitCheck: + cmp cl, 1 ; Check mwait-monitor support + jnz HltLoop + mov ebx, edx ; Save C-State to ebx +MwaitLoop: + cli + mov eax, esp ; Set Monitor Address + xor ecx, ecx ; ecx = 0 + xor edx, edx ; edx = 0 + monitor + mov eax, ebx ; Mwait Cx, Target C-State per eax[7:4] + shl eax, 4 + mwait + jmp MwaitLoop + +HltLoop: + pop edx ; PM16CodeSegment + add esp, 4 + pop ebx ; WakeupBuffer + add esp, 4 + pop eax ; SevEsAPJumpTable + add esp, 4 + cmp eax, 0 ; Check for SEV-ES + je DoHlt + + cli + ; + ; SEV-ES is enabled, use VMGEXIT (GHCB information already + ; set by caller) + ; +BITS 64 + rep vmmcall +BITS 32 + + ; + ; Back from VMGEXIT AP_HLT_LOOP + ; Push the FLAGS/CS/IP values to use + ; + push word 0x0002 ; EFLAGS + xor ecx, ecx + mov cx, [eax + 2] ; CS + push cx + mov cx, [eax] ; IP + push cx + push word 0x0000 ; For alignment, will be discarded + + push edx + push ebx + + mov edx, esi ; Restore RDX reset value + + retf + +DoHlt: + cli + hlt + jmp DoHlt + +BITS 64 +AsmRelocateApLoopEnd: + +;------------------------------------------------------------------------------------- +; AsmGetAddressMap (&AddressMap); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmGetAddressMap) +ASM_PFX(AsmGetAddressMap): + lea rax, [ASM_PFX(RendezvousFunnelProc)] + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RendezvousFunnelAddress], rax + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.ModeEntryOffset], LongModeStart - RendezvousFunnelProcStart + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RendezvousFunnelSize], RendezvousFunnelProcEnd - RendezvousFunnelProcStart + lea rax, [ASM_PFX(AsmRelocateApLoop)] + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RelocateApLoopFuncAddress], rax + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.RelocateApLoopFuncSize], AsmRelocateApLoopEnd - AsmRelocateApLoopStart + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.ModeTransitionOffset], Flat32Start - RendezvousFunnelProcStart + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealSize], SwitchToRealProcEnd - SwitchToRealProcStart + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealOffset], SwitchToRealProcStart - RendezvousFunnelProcStart + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealNoNxOffset], SwitchToRealProcStart - Flat32Start + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealPM16ModeOffset], PM16Mode - RendezvousFunnelProcStart + mov qword [rcx + MP_ASSEMBLY_ADDRESS_MAP.SwitchToRealPM16ModeSize], SwitchToRealProcEnd - PM16Mode + ret + +;------------------------------------------------------------------------------------- +;AsmExchangeRole procedure follows. This procedure executed by current BSP, that is +;about to become an AP. It switches its stack with the current AP. +;AsmExchangeRole (IN CPU_EXCHANGE_INFO *MyInfo, IN CPU_EXCHANGE_INFO *OthersInfo); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmExchangeRole) +ASM_PFX(AsmExchangeRole): + ; DO NOT call other functions in this function, since 2 CPU may use 1 stack + ; at the same time. If 1 CPU try to call a function, stack will be corrupted. + + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + mov rax, cr0 + push rax + + mov rax, cr4 + push rax + + ; rsi contains MyInfo pointer + mov rsi, rcx + + ; rdi contains OthersInfo pointer + mov rdi, rdx + + ;Store EFLAGS, GDTR and IDTR regiter to stack + pushfq + sgdt [rsi + CPU_EXCHANGE_ROLE_INFO.Gdtr] + sidt [rsi + CPU_EXCHANGE_ROLE_INFO.Idtr] + + ; Store the its StackPointer + mov [rsi + CPU_EXCHANGE_ROLE_INFO.StackPointer], rsp + + ; update its switch state to STORED + mov byte [rsi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_STORED + +WaitForOtherStored: + ; wait until the other CPU finish storing its state + cmp byte [rdi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_STORED + jz OtherStored + pause + jmp WaitForOtherStored + +OtherStored: + ; Since another CPU already stored its state, load them + ; load GDTR value + lgdt [rdi + CPU_EXCHANGE_ROLE_INFO.Gdtr] + + ; load IDTR value + lidt [rdi + CPU_EXCHANGE_ROLE_INFO.Idtr] + + ; load its future StackPointer + mov rsp, [rdi + CPU_EXCHANGE_ROLE_INFO.StackPointer] + + ; update the other CPU's switch state to LOADED + mov byte [rdi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_LOADED + +WaitForOtherLoaded: + ; wait until the other CPU finish loading new state, + ; otherwise the data in stack may corrupt + cmp byte [rsi + CPU_EXCHANGE_ROLE_INFO.State], CPU_SWITCH_STATE_LOADED + jz OtherLoaded + pause + jmp WaitForOtherLoaded + +OtherLoaded: + ; since the other CPU already get the data it want, leave this procedure + popfq + + pop rax + mov cr4, rax + + pop rax + mov cr0, rax + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + + ret diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLibUp/MpInitLibUp.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLibUp/MpInitLibUp.c new file mode 100644 index 00000000..df1e2ce5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLibUp/MpInitLibUp.c @@ -0,0 +1,442 @@ +/** @file + Multiple-Processor initialization Library for uniprocessor platforms. + + Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> +#include <Ppi/SecPlatformInformation.h> +#include <Protocol/MpService.h> +#include <Library/DebugLib.h> +#include <Library/LocalApicLib.h> +#include <Library/HobLib.h> + +/** + MP Initialize Library initialization. + + This service will allocate AP reset vector and wakeup all APs to do APs + initialization. + + This service must be invoked before all other MP Initialize Library + service are invoked. + + @retval EFI_SUCCESS MP initialization succeeds. + @retval Others MP initialization fails. + +**/ +EFI_STATUS +EFIAPI +MpInitLibInitialize ( + VOID + ) +{ + // + // Enable the local APIC for Virtual Wire Mode. + // + ProgramVirtualWireMode (); + + return EFI_SUCCESS; +} + +/** + Retrieves the number of logical processor in the platform and the number of + those logical processors that are enabled on this boot. This service may only + be called from the BSP. + + @param[out] NumberOfProcessors Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors + is NULL. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibGetNumberOfProcessors ( + OUT UINTN *NumberOfProcessors, OPTIONAL + OUT UINTN *NumberOfEnabledProcessors OPTIONAL + ) +{ + *NumberOfProcessors = 1; + *NumberOfEnabledProcessors = 1; + return EFI_SUCCESS; +} + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + @param[out] HealthData Return processor health data. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibGetProcessorInfo ( + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer, + OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + EFI_SEC_PLATFORM_INFORMATION_RECORD *SecPlatformInformation; + + if (ProcessorInfoBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + if (ProcessorNumber != 0) { + return EFI_NOT_FOUND; + } + ProcessorInfoBuffer->ProcessorId = 0; + ProcessorInfoBuffer->StatusFlag = PROCESSOR_AS_BSP_BIT | + PROCESSOR_ENABLED_BIT | + PROCESSOR_HEALTH_STATUS_BIT; + ProcessorInfoBuffer->Location.Package = 0; + ProcessorInfoBuffer->Location.Core = 0; + ProcessorInfoBuffer->Location.Thread = 0; + if (HealthData != NULL) { + GuidHob = GetFirstGuidHob (&gEfiSecPlatformInformationPpiGuid); + if (GuidHob != NULL) { + SecPlatformInformation = GET_GUID_HOB_DATA (GuidHob); + HealthData->Uint32 = SecPlatformInformation->IA32HealthFlags.Uint32; + } else { + DEBUG ((DEBUG_INFO, "Does not find any HOB stored CPU BIST information!\n")); + HealthData->Uint32 = 0; + } + } + return EFI_SUCCESS; +} + +/** + This service executes a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] SingleThread If TRUE, then all the enabled APs execute + the function specified by Procedure one by + one, in ascending order of processor handle + number. If FALSE, then all the enabled APs + execute the function specified by Procedure + simultaneously. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until all APs finish + or TimeoutInMicroSeconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on all the enabled + APs, and go on executing immediately. If + all return from Procedure, or TimeoutInMicroSeconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + all APs return from Procedure, then Procedure + on the failed APs is terminated. All enabled + APs are available for next function assigned + by MpInitLibStartupAllAPs() or + MPInitLibStartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise, + if all APs finish successfully, then its + content is set to NULL. If not all APs + finish before timeout expires, then its + content is set to address of the buffer + holding handle numbers of the failed APs. + The buffer is allocated by MP Initialization + library, and it's the caller's responsibility to + free the buffer with FreePool() service. + In blocking mode, it is ready for consumption + when the call returns. In non-blocking mode, + it is ready when WaitEvent is signaled. The + list of failed CPU is terminated by + END_OF_CPU_LIST. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not + supported. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + all enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +MpInitLibStartupAllAPs ( + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT UINTN **FailedCpuList OPTIONAL + ) +{ + return EFI_NOT_STARTED; +} + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. + + @param[in] Procedure A pointer to the function to be run on the + designated AP of the system. See type + EFI_AP_PROCEDURE. + @param[in] ProcessorNumber The handle number of the AP. The range is + from 0 to the total number of logical + processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until this AP finish + or TimeoutInMicroSeconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on this AP, + and go on executing immediately. If this AP + return from Procedure or TimeoutInMicroSeconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + this AP to finish this Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + this AP returns from Procedure, then Procedure + on the AP is terminated. The + AP is available for next function assigned + by MpInitLibStartupAllAPs() or + MpInitLibStartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure on the + specified AP. + @param[out] Finished If NULL, this parameter is ignored. In + blocking mode, this parameter is ignored. + In non-blocking mode, if AP returns from + Procedure before the timeout expires, its + content is set to TRUE. Otherwise, the + value is set to FALSE. The caller can + determine if the AP returned from Procedure + by evaluating this value. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before + the timeout expires. + @retval EFI_SUCCESS In non-blocking mode, the function has been + dispatched to specified AP. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not + supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + the specified AP has finished. + @retval EFI_NOT_READY The specified AP is busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +MpInitLibStartupThisAP ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT BOOLEAN *Finished OPTIONAL + ) +{ + return EFI_INVALID_PARAMETER; +} + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + @param[in] ProcessorNumber The handle number of AP that is to become the new + BSP. The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an + enabled AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to + this service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or + a disabled AP. + @retval EFI_NOT_READY The specified AP is busy. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibSwitchBSP ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + return EFI_UNSUPPORTED; +} + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of AP. + The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for + enabled, FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies + the new health status of the AP. This flag + corresponds to StatusFlag defined in + EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only + the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter + is ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed + prior to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibEnableDisableAP ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + @param[out] ProcessorNumber Pointer to the handle number of AP. + The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned + in ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibWhoAmI ( + OUT UINTN *ProcessorNumber + ) +{ + if (ProcessorNumber == NULL) { + return EFI_INVALID_PARAMETER; + } + *ProcessorNumber = 0; + return EFI_SUCCESS; +} + +/** + This service executes a caller provided function on all enabled CPUs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. Zero means + infinity. TimeoutInMicroseconds is ignored + for BSP. + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + + @retval EFI_SUCCESS CPU have finished the procedure. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +EFI_STATUS +EFIAPI +MpInitLibStartupAllCPUs ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + Procedure (ProcedureArgument); + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLibUp/MpInitLibUp.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLibUp/MpInitLibUp.inf new file mode 100644 index 00000000..7f2d68e6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLibUp/MpInitLibUp.inf @@ -0,0 +1,37 @@ +## @file +# MP Initialize Library instance for uniprocessor platforms. +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MpInitLibUp + MODULE_UNI_FILE = MpInitLibUp.uni + FILE_GUID = 70E9818C-A4F0-4061-9FA2-2DFFC7016D6E + MODULE_TYPE = BASE + VERSION_STRING = 1.1 + LIBRARY_CLASS = MpInitLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + MpInitLibUp.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + DebugLib + LocalApicLib + HobLib + +[Ppis] + gEfiSecPlatformInformationPpiGuid ## SOMETIMES_CONSUMES diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLibUp/MpInitLibUp.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLibUp/MpInitLibUp.uni new file mode 100644 index 00000000..ca1ab943 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MpInitLibUp/MpInitLibUp.uni @@ -0,0 +1,14 @@ +// /** @file
+// MP Initialize Library instance for uniprocessor platforms.
+//
+// MP Initialize Library instance for uniprocessor platforms.
+//
+// Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "MP Initialize Library instance for uniprocessor platforms."
+
+#string STR_MODULE_DESCRIPTION #language en-US "MP Initialize Library instance for uniprocessor platforms."
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.c new file mode 100644 index 00000000..85ae1700 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.c @@ -0,0 +1,2815 @@ +/** @file + MTRR setting library + + @par Note: + Most of services in this library instance are suggested to be invoked by BSP only, + except for MtrrSetAllMtrrs() which is used to sync BSP's MTRR setting to APs. + + Copyright (c) 2008 - 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi.h> +#include <Register/Intel/Cpuid.h> +#include <Register/Intel/Msr.h> + +#include <Library/MtrrLib.h> +#include <Library/BaseLib.h> +#include <Library/CpuLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> + +#define OR_SEED 0x0101010101010101ull +#define CLEAR_SEED 0xFFFFFFFFFFFFFFFFull +#define MAX_WEIGHT MAX_UINT8 +#define SCRATCH_BUFFER_SIZE (4 * SIZE_4KB) +#define MTRR_LIB_ASSERT_ALIGNED(B, L) ASSERT ((B & ~(L - 1)) == B); + +#define M(x,y) ((x) * VertexCount + (y)) +#define O(x,y) ((y) * VertexCount + (x)) + +// +// Context to save and restore when MTRRs are programmed +// +typedef struct { + UINTN Cr4; + BOOLEAN InterruptState; +} MTRR_CONTEXT; + +typedef struct { + UINT64 Address; + UINT64 Alignment; + UINT64 Length; + MTRR_MEMORY_CACHE_TYPE Type : 7; + + // + // Temprary use for calculating the best MTRR settings. + // + BOOLEAN Visited : 1; + UINT8 Weight; + UINT16 Previous; +} MTRR_LIB_ADDRESS; + +// +// This table defines the offset, base and length of the fixed MTRRs +// +CONST FIXED_MTRR mMtrrLibFixedMtrrTable[] = { + { + MSR_IA32_MTRR_FIX64K_00000, + 0, + SIZE_64KB + }, + { + MSR_IA32_MTRR_FIX16K_80000, + 0x80000, + SIZE_16KB + }, + { + MSR_IA32_MTRR_FIX16K_A0000, + 0xA0000, + SIZE_16KB + }, + { + MSR_IA32_MTRR_FIX4K_C0000, + 0xC0000, + SIZE_4KB + }, + { + MSR_IA32_MTRR_FIX4K_C8000, + 0xC8000, + SIZE_4KB + }, + { + MSR_IA32_MTRR_FIX4K_D0000, + 0xD0000, + SIZE_4KB + }, + { + MSR_IA32_MTRR_FIX4K_D8000, + 0xD8000, + SIZE_4KB + }, + { + MSR_IA32_MTRR_FIX4K_E0000, + 0xE0000, + SIZE_4KB + }, + { + MSR_IA32_MTRR_FIX4K_E8000, + 0xE8000, + SIZE_4KB + }, + { + MSR_IA32_MTRR_FIX4K_F0000, + 0xF0000, + SIZE_4KB + }, + { + MSR_IA32_MTRR_FIX4K_F8000, + 0xF8000, + SIZE_4KB + } +}; + +// +// Lookup table used to print MTRRs +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mMtrrMemoryCacheTypeShortName[] = { + "UC", // CacheUncacheable + "WC", // CacheWriteCombining + "R*", // Invalid + "R*", // Invalid + "WT", // CacheWriteThrough + "WP", // CacheWriteProtected + "WB", // CacheWriteBack + "R*" // Invalid +}; + + +/** + Worker function prints all MTRRs for debugging. + + If MtrrSetting is not NULL, print MTRR settings from input MTRR + settings buffer. + If MtrrSetting is NULL, print MTRR settings from MTRRs. + + @param MtrrSetting A buffer holding all MTRRs content. +**/ +VOID +MtrrDebugPrintAllMtrrsWorker ( + IN MTRR_SETTINGS *MtrrSetting + ); + +/** + Worker function returns the variable MTRR count for the CPU. + + @return Variable MTRR count + +**/ +UINT32 +GetVariableMtrrCountWorker ( + VOID + ) +{ + MSR_IA32_MTRRCAP_REGISTER MtrrCap; + + MtrrCap.Uint64 = AsmReadMsr64 (MSR_IA32_MTRRCAP); + ASSERT (MtrrCap.Bits.VCNT <= ARRAY_SIZE (((MTRR_VARIABLE_SETTINGS *) 0)->Mtrr)); + return MtrrCap.Bits.VCNT; +} + +/** + Returns the variable MTRR count for the CPU. + + @return Variable MTRR count + +**/ +UINT32 +EFIAPI +GetVariableMtrrCount ( + VOID + ) +{ + if (!IsMtrrSupported ()) { + return 0; + } + return GetVariableMtrrCountWorker (); +} + +/** + Worker function returns the firmware usable variable MTRR count for the CPU. + + @return Firmware usable variable MTRR count + +**/ +UINT32 +GetFirmwareVariableMtrrCountWorker ( + VOID + ) +{ + UINT32 VariableMtrrCount; + UINT32 ReservedMtrrNumber; + + VariableMtrrCount = GetVariableMtrrCountWorker (); + ReservedMtrrNumber = PcdGet32 (PcdCpuNumberOfReservedVariableMtrrs); + if (VariableMtrrCount < ReservedMtrrNumber) { + return 0; + } + + return VariableMtrrCount - ReservedMtrrNumber; +} + +/** + Returns the firmware usable variable MTRR count for the CPU. + + @return Firmware usable variable MTRR count + +**/ +UINT32 +EFIAPI +GetFirmwareVariableMtrrCount ( + VOID + ) +{ + if (!IsMtrrSupported ()) { + return 0; + } + return GetFirmwareVariableMtrrCountWorker (); +} + +/** + Worker function returns the default MTRR cache type for the system. + + If MtrrSetting is not NULL, returns the default MTRR cache type from input + MTRR settings buffer. + If MtrrSetting is NULL, returns the default MTRR cache type from MSR. + + @param[in] MtrrSetting A buffer holding all MTRRs content. + + @return The default MTRR cache type. + +**/ +MTRR_MEMORY_CACHE_TYPE +MtrrGetDefaultMemoryTypeWorker ( + IN MTRR_SETTINGS *MtrrSetting + ) +{ + MSR_IA32_MTRR_DEF_TYPE_REGISTER DefType; + + if (MtrrSetting == NULL) { + DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE); + } else { + DefType.Uint64 = MtrrSetting->MtrrDefType; + } + + return (MTRR_MEMORY_CACHE_TYPE) DefType.Bits.Type; +} + + +/** + Returns the default MTRR cache type for the system. + + @return The default MTRR cache type. + +**/ +MTRR_MEMORY_CACHE_TYPE +EFIAPI +MtrrGetDefaultMemoryType ( + VOID + ) +{ + if (!IsMtrrSupported ()) { + return CacheUncacheable; + } + return MtrrGetDefaultMemoryTypeWorker (NULL); +} + +/** + Preparation before programming MTRR. + + This function will do some preparation for programming MTRRs: + disable cache, invalid cache and disable MTRR caching functionality + + @param[out] MtrrContext Pointer to context to save + +**/ +VOID +MtrrLibPreMtrrChange ( + OUT MTRR_CONTEXT *MtrrContext + ) +{ + MSR_IA32_MTRR_DEF_TYPE_REGISTER DefType; + // + // Disable interrupts and save current interrupt state + // + MtrrContext->InterruptState = SaveAndDisableInterrupts(); + + // + // Enter no fill cache mode, CD=1(Bit30), NW=0 (Bit29) + // + AsmDisableCache (); + + // + // Save original CR4 value and clear PGE flag (Bit 7) + // + MtrrContext->Cr4 = AsmReadCr4 (); + AsmWriteCr4 (MtrrContext->Cr4 & (~BIT7)); + + // + // Flush all TLBs + // + CpuFlushTlb (); + + // + // Disable MTRRs + // + DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE); + DefType.Bits.E = 0; + AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, DefType.Uint64); +} + +/** + Cleaning up after programming MTRRs. + + This function will do some clean up after programming MTRRs: + Flush all TLBs, re-enable caching, restore CR4. + + @param[in] MtrrContext Pointer to context to restore + +**/ +VOID +MtrrLibPostMtrrChangeEnableCache ( + IN MTRR_CONTEXT *MtrrContext + ) +{ + // + // Flush all TLBs + // + CpuFlushTlb (); + + // + // Enable Normal Mode caching CD=NW=0, CD(Bit30), NW(Bit29) + // + AsmEnableCache (); + + // + // Restore original CR4 value + // + AsmWriteCr4 (MtrrContext->Cr4); + + // + // Restore original interrupt state + // + SetInterruptState (MtrrContext->InterruptState); +} + +/** + Cleaning up after programming MTRRs. + + This function will do some clean up after programming MTRRs: + enable MTRR caching functionality, and enable cache + + @param[in] MtrrContext Pointer to context to restore + +**/ +VOID +MtrrLibPostMtrrChange ( + IN MTRR_CONTEXT *MtrrContext + ) +{ + MSR_IA32_MTRR_DEF_TYPE_REGISTER DefType; + // + // Enable Cache MTRR + // + DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE); + DefType.Bits.E = 1; + DefType.Bits.FE = 1; + AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, DefType.Uint64); + + MtrrLibPostMtrrChangeEnableCache (MtrrContext); +} + +/** + Worker function gets the content in fixed MTRRs + + @param[out] FixedSettings A buffer to hold fixed MTRRs content. + + @retval The pointer of FixedSettings + +**/ +MTRR_FIXED_SETTINGS* +MtrrGetFixedMtrrWorker ( + OUT MTRR_FIXED_SETTINGS *FixedSettings + ) +{ + UINT32 Index; + + for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) { + FixedSettings->Mtrr[Index] = + AsmReadMsr64 (mMtrrLibFixedMtrrTable[Index].Msr); + } + + return FixedSettings; +} + + +/** + This function gets the content in fixed MTRRs + + @param[out] FixedSettings A buffer to hold fixed MTRRs content. + + @retval The pointer of FixedSettings + +**/ +MTRR_FIXED_SETTINGS* +EFIAPI +MtrrGetFixedMtrr ( + OUT MTRR_FIXED_SETTINGS *FixedSettings + ) +{ + if (!IsMtrrSupported ()) { + return FixedSettings; + } + + return MtrrGetFixedMtrrWorker (FixedSettings); +} + + +/** + Worker function will get the raw value in variable MTRRs + + If MtrrSetting is not NULL, gets the variable MTRRs raw value from input + MTRR settings buffer. + If MtrrSetting is NULL, gets the variable MTRRs raw value from MTRRs. + + @param[in] MtrrSetting A buffer holding all MTRRs content. + @param[in] VariableMtrrCount Number of variable MTRRs. + @param[out] VariableSettings A buffer to hold variable MTRRs content. + + @return The VariableSettings input pointer + +**/ +MTRR_VARIABLE_SETTINGS* +MtrrGetVariableMtrrWorker ( + IN MTRR_SETTINGS *MtrrSetting, + IN UINT32 VariableMtrrCount, + OUT MTRR_VARIABLE_SETTINGS *VariableSettings + ) +{ + UINT32 Index; + + ASSERT (VariableMtrrCount <= ARRAY_SIZE (VariableSettings->Mtrr)); + + for (Index = 0; Index < VariableMtrrCount; Index++) { + if (MtrrSetting == NULL) { + VariableSettings->Mtrr[Index].Base = + AsmReadMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1)); + VariableSettings->Mtrr[Index].Mask = + AsmReadMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1)); + } else { + VariableSettings->Mtrr[Index].Base = MtrrSetting->Variables.Mtrr[Index].Base; + VariableSettings->Mtrr[Index].Mask = MtrrSetting->Variables.Mtrr[Index].Mask; + } + } + + return VariableSettings; +} + +/** + Programs fixed MTRRs registers. + + @param[in] Type The memory type to set. + @param[in, out] Base The base address of memory range. + @param[in, out] Length The length of memory range. + @param[in, out] LastMsrIndex On input, the last index of the fixed MTRR MSR to program. + On return, the current index of the fixed MTRR MSR to program. + @param[out] ClearMask The bits to clear in the fixed MTRR MSR. + @param[out] OrMask The bits to set in the fixed MTRR MSR. + + @retval RETURN_SUCCESS The cache type was updated successfully + @retval RETURN_UNSUPPORTED The requested range or cache type was invalid + for the fixed MTRRs. + +**/ +RETURN_STATUS +MtrrLibProgramFixedMtrr ( + IN MTRR_MEMORY_CACHE_TYPE Type, + IN OUT UINT64 *Base, + IN OUT UINT64 *Length, + IN OUT UINT32 *LastMsrIndex, + OUT UINT64 *ClearMask, + OUT UINT64 *OrMask + ) +{ + UINT32 MsrIndex; + UINT32 LeftByteShift; + UINT32 RightByteShift; + UINT64 SubLength; + + // + // Find the fixed MTRR index to be programmed + // + for (MsrIndex = *LastMsrIndex + 1; MsrIndex < ARRAY_SIZE (mMtrrLibFixedMtrrTable); MsrIndex++) { + if ((*Base >= mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress) && + (*Base < + ( + mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress + + (8 * mMtrrLibFixedMtrrTable[MsrIndex].Length) + ) + ) + ) { + break; + } + } + + ASSERT (MsrIndex != ARRAY_SIZE (mMtrrLibFixedMtrrTable)); + + // + // Find the begin offset in fixed MTRR and calculate byte offset of left shift + // + if ((((UINT32)*Base - mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress) % mMtrrLibFixedMtrrTable[MsrIndex].Length) != 0) { + // + // Base address should be aligned to the begin of a certain Fixed MTRR range. + // + return RETURN_UNSUPPORTED; + } + LeftByteShift = ((UINT32)*Base - mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress) / mMtrrLibFixedMtrrTable[MsrIndex].Length; + ASSERT (LeftByteShift < 8); + + // + // Find the end offset in fixed MTRR and calculate byte offset of right shift + // + SubLength = mMtrrLibFixedMtrrTable[MsrIndex].Length * (8 - LeftByteShift); + if (*Length >= SubLength) { + RightByteShift = 0; + } else { + if (((UINT32)(*Length) % mMtrrLibFixedMtrrTable[MsrIndex].Length) != 0) { + // + // Length should be aligned to the end of a certain Fixed MTRR range. + // + return RETURN_UNSUPPORTED; + } + RightByteShift = 8 - LeftByteShift - (UINT32)(*Length) / mMtrrLibFixedMtrrTable[MsrIndex].Length; + // + // Update SubLength by actual length + // + SubLength = *Length; + } + + *ClearMask = CLEAR_SEED; + *OrMask = MultU64x32 (OR_SEED, (UINT32) Type); + + if (LeftByteShift != 0) { + // + // Clear the low bits by LeftByteShift + // + *ClearMask &= LShiftU64 (*ClearMask, LeftByteShift * 8); + *OrMask &= LShiftU64 (*OrMask, LeftByteShift * 8); + } + + if (RightByteShift != 0) { + // + // Clear the high bits by RightByteShift + // + *ClearMask &= RShiftU64 (*ClearMask, RightByteShift * 8); + *OrMask &= RShiftU64 (*OrMask, RightByteShift * 8); + } + + *Length -= SubLength; + *Base += SubLength; + + *LastMsrIndex = MsrIndex; + + return RETURN_SUCCESS; +} + + +/** + Worker function gets the attribute of variable MTRRs. + + This function shadows the content of variable MTRRs into an + internal array: VariableMtrr. + + @param[in] VariableSettings The variable MTRR values to shadow + @param[in] VariableMtrrCount The number of variable MTRRs + @param[in] MtrrValidBitsMask The mask for the valid bit of the MTRR + @param[in] MtrrValidAddressMask The valid address mask for MTRR + @param[out] VariableMtrr The array to shadow variable MTRRs content + + @return Number of MTRRs which has been used. + +**/ +UINT32 +MtrrGetMemoryAttributeInVariableMtrrWorker ( + IN MTRR_VARIABLE_SETTINGS *VariableSettings, + IN UINTN VariableMtrrCount, + IN UINT64 MtrrValidBitsMask, + IN UINT64 MtrrValidAddressMask, + OUT VARIABLE_MTRR *VariableMtrr + ) +{ + UINTN Index; + UINT32 UsedMtrr; + + ZeroMem (VariableMtrr, sizeof (VARIABLE_MTRR) * ARRAY_SIZE (VariableSettings->Mtrr)); + for (Index = 0, UsedMtrr = 0; Index < VariableMtrrCount; Index++) { + if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *) &VariableSettings->Mtrr[Index].Mask)->Bits.V != 0) { + VariableMtrr[Index].Msr = (UINT32)Index; + VariableMtrr[Index].BaseAddress = (VariableSettings->Mtrr[Index].Base & MtrrValidAddressMask); + VariableMtrr[Index].Length = + ((~(VariableSettings->Mtrr[Index].Mask & MtrrValidAddressMask)) & MtrrValidBitsMask) + 1; + VariableMtrr[Index].Type = (VariableSettings->Mtrr[Index].Base & 0x0ff); + VariableMtrr[Index].Valid = TRUE; + VariableMtrr[Index].Used = TRUE; + UsedMtrr++; + } + } + return UsedMtrr; +} + +/** + Convert variable MTRRs to a RAW MTRR_MEMORY_RANGE array. + One MTRR_MEMORY_RANGE element is created for each MTRR setting. + The routine doesn't remove the overlap or combine the near-by region. + + @param[in] VariableSettings The variable MTRR values to shadow + @param[in] VariableMtrrCount The number of variable MTRRs + @param[in] MtrrValidBitsMask The mask for the valid bit of the MTRR + @param[in] MtrrValidAddressMask The valid address mask for MTRR + @param[out] VariableMtrr The array to shadow variable MTRRs content + + @return Number of MTRRs which has been used. + +**/ +UINT32 +MtrrLibGetRawVariableRanges ( + IN MTRR_VARIABLE_SETTINGS *VariableSettings, + IN UINTN VariableMtrrCount, + IN UINT64 MtrrValidBitsMask, + IN UINT64 MtrrValidAddressMask, + OUT MTRR_MEMORY_RANGE *VariableMtrr + ) +{ + UINTN Index; + UINT32 UsedMtrr; + + ZeroMem (VariableMtrr, sizeof (MTRR_MEMORY_RANGE) * ARRAY_SIZE (VariableSettings->Mtrr)); + for (Index = 0, UsedMtrr = 0; Index < VariableMtrrCount; Index++) { + if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *) &VariableSettings->Mtrr[Index].Mask)->Bits.V != 0) { + VariableMtrr[Index].BaseAddress = (VariableSettings->Mtrr[Index].Base & MtrrValidAddressMask); + VariableMtrr[Index].Length = + ((~(VariableSettings->Mtrr[Index].Mask & MtrrValidAddressMask)) & MtrrValidBitsMask) + 1; + VariableMtrr[Index].Type = (MTRR_MEMORY_CACHE_TYPE)(VariableSettings->Mtrr[Index].Base & 0x0ff); + UsedMtrr++; + } + } + return UsedMtrr; +} + +/** + Gets the attribute of variable MTRRs. + + This function shadows the content of variable MTRRs into an + internal array: VariableMtrr. + + @param[in] MtrrValidBitsMask The mask for the valid bit of the MTRR + @param[in] MtrrValidAddressMask The valid address mask for MTRR + @param[out] VariableMtrr The array to shadow variable MTRRs content + + @return The return value of this parameter indicates the + number of MTRRs which has been used. + +**/ +UINT32 +EFIAPI +MtrrGetMemoryAttributeInVariableMtrr ( + IN UINT64 MtrrValidBitsMask, + IN UINT64 MtrrValidAddressMask, + OUT VARIABLE_MTRR *VariableMtrr + ) +{ + MTRR_VARIABLE_SETTINGS VariableSettings; + + if (!IsMtrrSupported ()) { + return 0; + } + + MtrrGetVariableMtrrWorker ( + NULL, + GetVariableMtrrCountWorker (), + &VariableSettings + ); + + return MtrrGetMemoryAttributeInVariableMtrrWorker ( + &VariableSettings, + GetFirmwareVariableMtrrCountWorker (), + MtrrValidBitsMask, + MtrrValidAddressMask, + VariableMtrr + ); +} + +/** + Return the biggest alignment (lowest set bit) of address. + The function is equivalent to: 1 << LowBitSet64 (Address). + + @param Address The address to return the alignment. + @param Alignment0 The alignment to return when Address is 0. + + @return The least alignment of the Address. +**/ +UINT64 +MtrrLibBiggestAlignment ( + UINT64 Address, + UINT64 Alignment0 +) +{ + if (Address == 0) { + return Alignment0; + } + + return Address & ((~Address) + 1); +} + +/** + Return whether the left MTRR type precedes the right MTRR type. + + The MTRR type precedence rules are: + 1. UC precedes any other type + 2. WT precedes WB + For further details, please refer the IA32 Software Developer's Manual, + Volume 3, Section "MTRR Precedences". + + @param Left The left MTRR type. + @param Right The right MTRR type. + + @retval TRUE Left precedes Right. + @retval FALSE Left doesn't precede Right. +**/ +BOOLEAN +MtrrLibTypeLeftPrecedeRight ( + IN MTRR_MEMORY_CACHE_TYPE Left, + IN MTRR_MEMORY_CACHE_TYPE Right +) +{ + return (BOOLEAN) (Left == CacheUncacheable || (Left == CacheWriteThrough && Right == CacheWriteBack)); +} + +/** + Initializes the valid bits mask and valid address mask for MTRRs. + + This function initializes the valid bits mask and valid address mask for MTRRs. + + @param[out] MtrrValidBitsMask The mask for the valid bit of the MTRR + @param[out] MtrrValidAddressMask The valid address mask for the MTRR + +**/ +VOID +MtrrLibInitializeMtrrMask ( + OUT UINT64 *MtrrValidBitsMask, + OUT UINT64 *MtrrValidAddressMask + ) +{ + UINT32 MaxExtendedFunction; + CPUID_VIR_PHY_ADDRESS_SIZE_EAX VirPhyAddressSize; + + + AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedFunction, NULL, NULL, NULL); + + if (MaxExtendedFunction >= CPUID_VIR_PHY_ADDRESS_SIZE) { + AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &VirPhyAddressSize.Uint32, NULL, NULL, NULL); + } else { + VirPhyAddressSize.Bits.PhysicalAddressBits = 36; + } + + *MtrrValidBitsMask = LShiftU64 (1, VirPhyAddressSize.Bits.PhysicalAddressBits) - 1; + *MtrrValidAddressMask = *MtrrValidBitsMask & 0xfffffffffffff000ULL; +} + + +/** + Determines the real attribute of a memory range. + + This function is to arbitrate the real attribute of the memory when + there are 2 MTRRs covers the same memory range. For further details, + please refer the IA32 Software Developer's Manual, Volume 3, + Section "MTRR Precedences". + + @param[in] MtrrType1 The first kind of Memory type + @param[in] MtrrType2 The second kind of memory type + +**/ +MTRR_MEMORY_CACHE_TYPE +MtrrLibPrecedence ( + IN MTRR_MEMORY_CACHE_TYPE MtrrType1, + IN MTRR_MEMORY_CACHE_TYPE MtrrType2 + ) +{ + if (MtrrType1 == MtrrType2) { + return MtrrType1; + } + + ASSERT ( + MtrrLibTypeLeftPrecedeRight (MtrrType1, MtrrType2) || + MtrrLibTypeLeftPrecedeRight (MtrrType2, MtrrType1) + ); + + if (MtrrLibTypeLeftPrecedeRight (MtrrType1, MtrrType2)) { + return MtrrType1; + } else { + return MtrrType2; + } +} + +/** + Worker function will get the memory cache type of the specific address. + + If MtrrSetting is not NULL, gets the memory cache type from input + MTRR settings buffer. + If MtrrSetting is NULL, gets the memory cache type from MTRRs. + + @param[in] MtrrSetting A buffer holding all MTRRs content. + @param[in] Address The specific address + + @return Memory cache type of the specific address + +**/ +MTRR_MEMORY_CACHE_TYPE +MtrrGetMemoryAttributeByAddressWorker ( + IN MTRR_SETTINGS *MtrrSetting, + IN PHYSICAL_ADDRESS Address + ) +{ + MSR_IA32_MTRR_DEF_TYPE_REGISTER DefType; + UINT64 FixedMtrr; + UINTN Index; + UINTN SubIndex; + MTRR_MEMORY_CACHE_TYPE MtrrType; + MTRR_MEMORY_RANGE VariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)]; + UINT64 MtrrValidBitsMask; + UINT64 MtrrValidAddressMask; + UINT32 VariableMtrrCount; + MTRR_VARIABLE_SETTINGS VariableSettings; + + // + // Check if MTRR is enabled, if not, return UC as attribute + // + if (MtrrSetting == NULL) { + DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE); + } else { + DefType.Uint64 = MtrrSetting->MtrrDefType; + } + + if (DefType.Bits.E == 0) { + return CacheUncacheable; + } + + // + // If address is less than 1M, then try to go through the fixed MTRR + // + if (Address < BASE_1MB) { + if (DefType.Bits.FE != 0) { + // + // Go through the fixed MTRR + // + for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) { + if (Address >= mMtrrLibFixedMtrrTable[Index].BaseAddress && + Address < mMtrrLibFixedMtrrTable[Index].BaseAddress + + (mMtrrLibFixedMtrrTable[Index].Length * 8)) { + SubIndex = + ((UINTN) Address - mMtrrLibFixedMtrrTable[Index].BaseAddress) / + mMtrrLibFixedMtrrTable[Index].Length; + if (MtrrSetting == NULL) { + FixedMtrr = AsmReadMsr64 (mMtrrLibFixedMtrrTable[Index].Msr); + } else { + FixedMtrr = MtrrSetting->Fixed.Mtrr[Index]; + } + return (MTRR_MEMORY_CACHE_TYPE) (RShiftU64 (FixedMtrr, SubIndex * 8) & 0xFF); + } + } + } + } + + VariableMtrrCount = GetVariableMtrrCountWorker (); + ASSERT (VariableMtrrCount <= ARRAY_SIZE (MtrrSetting->Variables.Mtrr)); + MtrrGetVariableMtrrWorker (MtrrSetting, VariableMtrrCount, &VariableSettings); + + MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask); + MtrrLibGetRawVariableRanges ( + &VariableSettings, + VariableMtrrCount, + MtrrValidBitsMask, + MtrrValidAddressMask, + VariableMtrr + ); + + // + // Go through the variable MTRR + // + MtrrType = CacheInvalid; + for (Index = 0; Index < VariableMtrrCount; Index++) { + if (VariableMtrr[Index].Length != 0) { + if (Address >= VariableMtrr[Index].BaseAddress && + Address < VariableMtrr[Index].BaseAddress + VariableMtrr[Index].Length) { + if (MtrrType == CacheInvalid) { + MtrrType = (MTRR_MEMORY_CACHE_TYPE) VariableMtrr[Index].Type; + } else { + MtrrType = MtrrLibPrecedence (MtrrType, (MTRR_MEMORY_CACHE_TYPE) VariableMtrr[Index].Type); + } + } + } + } + + // + // If there is no MTRR which covers the Address, use the default MTRR type. + // + if (MtrrType == CacheInvalid) { + MtrrType = (MTRR_MEMORY_CACHE_TYPE) DefType.Bits.Type; + } + + return MtrrType; +} + + +/** + This function will get the memory cache type of the specific address. + + This function is mainly for debug purpose. + + @param[in] Address The specific address + + @return Memory cache type of the specific address + +**/ +MTRR_MEMORY_CACHE_TYPE +EFIAPI +MtrrGetMemoryAttribute ( + IN PHYSICAL_ADDRESS Address + ) +{ + if (!IsMtrrSupported ()) { + return CacheUncacheable; + } + + return MtrrGetMemoryAttributeByAddressWorker (NULL, Address); +} + +/** + Update the Ranges array to change the specified range identified by + BaseAddress and Length to Type. + + @param Ranges Array holding memory type settings for all memory regions. + @param Capacity The maximum count of memory ranges the array can hold. + @param Count Return the new memory range count in the array. + @param BaseAddress The base address of the memory range to change type. + @param Length The length of the memory range to change type. + @param Type The new type of the specified memory range. + + @retval RETURN_SUCCESS The type of the specified memory range is + changed successfully. + @retval RETURN_ALREADY_STARTED The type of the specified memory range equals + to the desired type. + @retval RETURN_OUT_OF_RESOURCES The new type set causes the count of memory + range exceeds capacity. +**/ +RETURN_STATUS +MtrrLibSetMemoryType ( + IN MTRR_MEMORY_RANGE *Ranges, + IN UINTN Capacity, + IN OUT UINTN *Count, + IN UINT64 BaseAddress, + IN UINT64 Length, + IN MTRR_MEMORY_CACHE_TYPE Type + ) +{ + UINTN Index; + UINT64 Limit; + UINT64 LengthLeft; + UINT64 LengthRight; + UINTN StartIndex; + UINTN EndIndex; + UINTN DeltaCount; + + LengthRight = 0; + LengthLeft = 0; + Limit = BaseAddress + Length; + StartIndex = *Count; + EndIndex = *Count; + for (Index = 0; Index < *Count; Index++) { + if ((StartIndex == *Count) && + (Ranges[Index].BaseAddress <= BaseAddress) && + (BaseAddress < Ranges[Index].BaseAddress + Ranges[Index].Length)) { + StartIndex = Index; + LengthLeft = BaseAddress - Ranges[Index].BaseAddress; + } + + if ((EndIndex == *Count) && + (Ranges[Index].BaseAddress < Limit) && + (Limit <= Ranges[Index].BaseAddress + Ranges[Index].Length)) { + EndIndex = Index; + LengthRight = Ranges[Index].BaseAddress + Ranges[Index].Length - Limit; + break; + } + } + + ASSERT (StartIndex != *Count && EndIndex != *Count); + if (StartIndex == EndIndex && Ranges[StartIndex].Type == Type) { + return RETURN_ALREADY_STARTED; + } + + // + // The type change may cause merging with previous range or next range. + // Update the StartIndex, EndIndex, BaseAddress, Length so that following + // logic doesn't need to consider merging. + // + if (StartIndex != 0) { + if (LengthLeft == 0 && Ranges[StartIndex - 1].Type == Type) { + StartIndex--; + Length += Ranges[StartIndex].Length; + BaseAddress -= Ranges[StartIndex].Length; + } + } + if (EndIndex != (*Count) - 1) { + if (LengthRight == 0 && Ranges[EndIndex + 1].Type == Type) { + EndIndex++; + Length += Ranges[EndIndex].Length; + } + } + + // + // |- 0 -|- 1 -|- 2 -|- 3 -| StartIndex EndIndex DeltaCount Count (Count = 4) + // |++++++++++++++++++| 0 3 1=3-0-2 3 + // |+++++++| 0 1 -1=1-0-2 5 + // |+| 0 0 -2=0-0-2 6 + // |+++| 0 0 -1=0-0-2+1 5 + // + // + DeltaCount = EndIndex - StartIndex - 2; + if (LengthLeft == 0) { + DeltaCount++; + } + if (LengthRight == 0) { + DeltaCount++; + } + if (*Count - DeltaCount > Capacity) { + return RETURN_OUT_OF_RESOURCES; + } + + // + // Reserve (-DeltaCount) space + // + CopyMem (&Ranges[EndIndex + 1 - DeltaCount], &Ranges[EndIndex + 1], (*Count - EndIndex - 1) * sizeof (Ranges[0])); + *Count -= DeltaCount; + + if (LengthLeft != 0) { + Ranges[StartIndex].Length = LengthLeft; + StartIndex++; + } + if (LengthRight != 0) { + Ranges[EndIndex - DeltaCount].BaseAddress = BaseAddress + Length; + Ranges[EndIndex - DeltaCount].Length = LengthRight; + Ranges[EndIndex - DeltaCount].Type = Ranges[EndIndex].Type; + } + Ranges[StartIndex].BaseAddress = BaseAddress; + Ranges[StartIndex].Length = Length; + Ranges[StartIndex].Type = Type; + return RETURN_SUCCESS; +} + +/** + Return the number of memory types in range [BaseAddress, BaseAddress + Length). + + @param Ranges Array holding memory type settings for all memory regions. + @param RangeCount The count of memory ranges the array holds. + @param BaseAddress Base address. + @param Length Length. + @param Types Return bit mask to indicate all memory types in the specified range. + + @retval Number of memory types. +**/ +UINT8 +MtrrLibGetNumberOfTypes ( + IN CONST MTRR_MEMORY_RANGE *Ranges, + IN UINTN RangeCount, + IN UINT64 BaseAddress, + IN UINT64 Length, + IN OUT UINT8 *Types OPTIONAL + ) +{ + UINTN Index; + UINT8 TypeCount; + UINT8 LocalTypes; + + TypeCount = 0; + LocalTypes = 0; + for (Index = 0; Index < RangeCount; Index++) { + if ((Ranges[Index].BaseAddress <= BaseAddress) && + (BaseAddress < Ranges[Index].BaseAddress + Ranges[Index].Length) + ) { + if ((LocalTypes & (1 << Ranges[Index].Type)) == 0) { + LocalTypes |= (UINT8)(1 << Ranges[Index].Type); + TypeCount++; + } + + if (BaseAddress + Length > Ranges[Index].BaseAddress + Ranges[Index].Length) { + Length -= Ranges[Index].BaseAddress + Ranges[Index].Length - BaseAddress; + BaseAddress = Ranges[Index].BaseAddress + Ranges[Index].Length; + } else { + break; + } + } + } + + if (Types != NULL) { + *Types = LocalTypes; + } + return TypeCount; +} + +/** + Calculate the least MTRR number from vertex Start to Stop and update + the Previous of all vertices from Start to Stop is updated to reflect + how the memory range is covered by MTRR. + + @param VertexCount The count of vertices in the graph. + @param Vertices Array holding all vertices. + @param Weight 2-dimention array holding weights between vertices. + @param Start Start vertex. + @param Stop Stop vertex. + @param IncludeOptional TRUE to count the optional weight. +**/ +VOID +MtrrLibCalculateLeastMtrrs ( + IN UINT16 VertexCount, + IN MTRR_LIB_ADDRESS *Vertices, + IN OUT CONST UINT8 *Weight, + IN UINT16 Start, + IN UINT16 Stop, + IN BOOLEAN IncludeOptional + ) +{ + UINT16 Index; + UINT8 MinWeight; + UINT16 MinI; + UINT8 Mandatory; + UINT8 Optional; + + for (Index = Start; Index <= Stop; Index++) { + Vertices[Index].Visited = FALSE; + Mandatory = Weight[M(Start,Index)]; + Vertices[Index].Weight = Mandatory; + if (Mandatory != MAX_WEIGHT) { + Optional = IncludeOptional ? Weight[O(Start, Index)] : 0; + Vertices[Index].Weight += Optional; + ASSERT (Vertices[Index].Weight >= Optional); + } + } + + MinI = Start; + MinWeight = 0; + while (!Vertices[Stop].Visited) { + // + // Update the weight from the shortest vertex to other unvisited vertices + // + for (Index = Start + 1; Index <= Stop; Index++) { + if (!Vertices[Index].Visited) { + Mandatory = Weight[M(MinI, Index)]; + if (Mandatory != MAX_WEIGHT) { + Optional = IncludeOptional ? Weight[O(MinI, Index)] : 0; + if (MinWeight + Mandatory + Optional <= Vertices[Index].Weight) { + Vertices[Index].Weight = MinWeight + Mandatory + Optional; + Vertices[Index].Previous = MinI; // Previous is Start based. + } + } + } + } + + // + // Find the shortest vertex from Start + // + MinI = VertexCount; + MinWeight = MAX_WEIGHT; + for (Index = Start + 1; Index <= Stop; Index++) { + if (!Vertices[Index].Visited && MinWeight > Vertices[Index].Weight) { + MinI = Index; + MinWeight = Vertices[Index].Weight; + } + } + + // + // Mark the shortest vertex from Start as visited + // + Vertices[MinI].Visited = TRUE; + } +} + +/** + Append the MTRR setting to MTRR setting array. + + @param Mtrrs Array holding all MTRR settings. + @param MtrrCapacity Capacity of the MTRR array. + @param MtrrCount The count of MTRR settings in array. + @param BaseAddress Base address. + @param Length Length. + @param Type Memory type. + + @retval RETURN_SUCCESS MTRR setting is appended to array. + @retval RETURN_OUT_OF_RESOURCES Array is full. +**/ +RETURN_STATUS +MtrrLibAppendVariableMtrr ( + IN OUT MTRR_MEMORY_RANGE *Mtrrs, + IN UINT32 MtrrCapacity, + IN OUT UINT32 *MtrrCount, + IN UINT64 BaseAddress, + IN UINT64 Length, + IN MTRR_MEMORY_CACHE_TYPE Type + ) +{ + if (*MtrrCount == MtrrCapacity) { + return RETURN_OUT_OF_RESOURCES; + } + + Mtrrs[*MtrrCount].BaseAddress = BaseAddress; + Mtrrs[*MtrrCount].Length = Length; + Mtrrs[*MtrrCount].Type = Type; + (*MtrrCount)++; + return RETURN_SUCCESS; +} + +/** + Return the memory type that has the least precedence. + + @param TypeBits Bit mask of memory type. + + @retval Memory type that has the least precedence. +**/ +MTRR_MEMORY_CACHE_TYPE +MtrrLibLowestType ( + IN UINT8 TypeBits +) +{ + INT8 Type; + + ASSERT (TypeBits != 0); + for (Type = 7; (INT8)TypeBits > 0; Type--, TypeBits <<= 1); + return (MTRR_MEMORY_CACHE_TYPE)Type; +} + +/** + Return TRUE when the Operand is exactly power of 2. + + @retval TRUE Operand is exactly power of 2. + @retval FALSE Operand is not power of 2. +**/ +BOOLEAN +MtrrLibIsPowerOfTwo ( + IN UINT64 Operand +) +{ + ASSERT (Operand != 0); + return (BOOLEAN) ((Operand & (Operand - 1)) == 0); +} + +/** + Calculate the subtractive path from vertex Start to Stop. + + @param DefaultType Default memory type. + @param A0 Alignment to use when base address is 0. + @param Ranges Array holding memory type settings for all memory regions. + @param RangeCount The count of memory ranges the array holds. + @param VertexCount The count of vertices in the graph. + @param Vertices Array holding all vertices. + @param Weight 2-dimention array holding weights between vertices. + @param Start Start vertex. + @param Stop Stop vertex. + @param Types Type bit mask of memory range from Start to Stop. + @param TypeCount Number of different memory types from Start to Stop. + @param Mtrrs Array holding all MTRR settings. + @param MtrrCapacity Capacity of the MTRR array. + @param MtrrCount The count of MTRR settings in array. + + @retval RETURN_SUCCESS The subtractive path is calculated successfully. + @retval RETURN_OUT_OF_RESOURCES The MTRR setting array is full. + +**/ +RETURN_STATUS +MtrrLibCalculateSubtractivePath ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT64 A0, + IN CONST MTRR_MEMORY_RANGE *Ranges, + IN UINTN RangeCount, + IN UINT16 VertexCount, + IN MTRR_LIB_ADDRESS *Vertices, + IN OUT UINT8 *Weight, + IN UINT16 Start, + IN UINT16 Stop, + IN UINT8 Types, + IN UINT8 TypeCount, + IN OUT MTRR_MEMORY_RANGE *Mtrrs, OPTIONAL + IN UINT32 MtrrCapacity, OPTIONAL + IN OUT UINT32 *MtrrCount OPTIONAL + ) +{ + RETURN_STATUS Status; + UINT64 Base; + UINT64 Length; + UINT8 PrecedentTypes; + UINTN Index; + UINT64 HBase; + UINT64 HLength; + UINT64 SubLength; + UINT16 SubStart; + UINT16 SubStop; + UINT16 Cur; + UINT16 Pre; + MTRR_MEMORY_CACHE_TYPE LowestType; + MTRR_MEMORY_CACHE_TYPE LowestPrecedentType; + + Base = Vertices[Start].Address; + Length = Vertices[Stop].Address - Base; + + LowestType = MtrrLibLowestType (Types); + + // + // Clear the lowest type (highest bit) to get the precedent types + // + PrecedentTypes = ~(1 << LowestType) & Types; + LowestPrecedentType = MtrrLibLowestType (PrecedentTypes); + + if (Mtrrs == NULL) { + Weight[M(Start, Stop)] = ((LowestType == DefaultType) ? 0 : 1); + Weight[O(Start, Stop)] = ((LowestType == DefaultType) ? 1 : 0); + } + + // Add all high level ranges + HBase = MAX_UINT64; + HLength = 0; + for (Index = 0; Index < RangeCount; Index++) { + if (Length == 0) { + break; + } + if ((Base < Ranges[Index].BaseAddress) || (Ranges[Index].BaseAddress + Ranges[Index].Length <= Base)) { + continue; + } + + // + // Base is in the Range[Index] + // + if (Base + Length > Ranges[Index].BaseAddress + Ranges[Index].Length) { + SubLength = Ranges[Index].BaseAddress + Ranges[Index].Length - Base; + } else { + SubLength = Length; + } + if (((1 << Ranges[Index].Type) & PrecedentTypes) != 0) { + // + // Meet a range whose types take precedence. + // Update the [HBase, HBase + HLength) to include the range, + // [HBase, HBase + HLength) may contain sub ranges with 2 different types, and both take precedence. + // + if (HBase == MAX_UINT64) { + HBase = Base; + } + HLength += SubLength; + } + + Base += SubLength; + Length -= SubLength; + + if (HLength == 0) { + continue; + } + + if ((Ranges[Index].Type == LowestType) || (Length == 0)) { // meet low type or end + + // + // Add the MTRRs for each high priority type range + // the range[HBase, HBase + HLength) contains only two types. + // We might use positive or subtractive, depending on which way uses less MTRR + // + for (SubStart = Start; SubStart <= Stop; SubStart++) { + if (Vertices[SubStart].Address == HBase) { + break; + } + } + + for (SubStop = SubStart; SubStop <= Stop; SubStop++) { + if (Vertices[SubStop].Address == HBase + HLength) { + break; + } + } + ASSERT (Vertices[SubStart].Address == HBase); + ASSERT (Vertices[SubStop].Address == HBase + HLength); + + if ((TypeCount == 2) || (SubStart == SubStop - 1)) { + // + // add subtractive MTRRs for [HBase, HBase + HLength) + // [HBase, HBase + HLength) contains only one type. + // while - loop is to split the range to MTRR - compliant aligned range. + // + if (Mtrrs == NULL) { + Weight[M (Start, Stop)] += (UINT8)(SubStop - SubStart); + } else { + while (SubStart != SubStop) { + Status = MtrrLibAppendVariableMtrr ( + Mtrrs, MtrrCapacity, MtrrCount, + Vertices[SubStart].Address, Vertices[SubStart].Length, Vertices[SubStart].Type + ); + if (RETURN_ERROR (Status)) { + return Status; + } + SubStart++; + } + } + } else { + ASSERT (TypeCount == 3); + MtrrLibCalculateLeastMtrrs (VertexCount, Vertices, Weight, SubStart, SubStop, TRUE); + + if (Mtrrs == NULL) { + Weight[M (Start, Stop)] += Vertices[SubStop].Weight; + } else { + // When we need to collect the optimal path from SubStart to SubStop + while (SubStop != SubStart) { + Cur = SubStop; + Pre = Vertices[Cur].Previous; + SubStop = Pre; + + if (Weight[M (Pre, Cur)] + Weight[O (Pre, Cur)] != 0) { + Status = MtrrLibAppendVariableMtrr ( + Mtrrs, MtrrCapacity, MtrrCount, + Vertices[Pre].Address, Vertices[Cur].Address - Vertices[Pre].Address, + (Pre != Cur - 1) ? LowestPrecedentType : Vertices[Pre].Type + ); + if (RETURN_ERROR (Status)) { + return Status; + } + } + if (Pre != Cur - 1) { + Status = MtrrLibCalculateSubtractivePath ( + DefaultType, A0, + Ranges, RangeCount, + VertexCount, Vertices, Weight, + Pre, Cur, PrecedentTypes, 2, + Mtrrs, MtrrCapacity, MtrrCount + ); + if (RETURN_ERROR (Status)) { + return Status; + } + } + } + } + + } + // + // Reset HBase, HLength + // + HBase = MAX_UINT64; + HLength = 0; + } + } + return RETURN_SUCCESS; +} + +/** + Calculate MTRR settings to cover the specified memory ranges. + + @param DefaultType Default memory type. + @param A0 Alignment to use when base address is 0. + @param Ranges Memory range array holding the memory type + settings for all memory address. + @param RangeCount Count of memory ranges. + @param Scratch A temporary scratch buffer that is used to perform the calculation. + This is an optional parameter that may be NULL. + @param ScratchSize Pointer to the size in bytes of the scratch buffer. + It may be updated to the actual required size when the calculation + needs more scratch buffer. + @param Mtrrs Array holding all MTRR settings. + @param MtrrCapacity Capacity of the MTRR array. + @param MtrrCount The count of MTRR settings in array. + + @retval RETURN_SUCCESS Variable MTRRs are allocated successfully. + @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity. + @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation. +**/ +RETURN_STATUS +MtrrLibCalculateMtrrs ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT64 A0, + IN CONST MTRR_MEMORY_RANGE *Ranges, + IN UINTN RangeCount, + IN VOID *Scratch, + IN OUT UINTN *ScratchSize, + IN OUT MTRR_MEMORY_RANGE *Mtrrs, + IN UINT32 MtrrCapacity, + IN OUT UINT32 *MtrrCount + ) +{ + UINT64 Base0; + UINT64 Base1; + UINTN Index; + UINT64 Base; + UINT64 Length; + UINT64 Alignment; + UINT64 SubLength; + MTRR_LIB_ADDRESS *Vertices; + UINT8 *Weight; + UINT32 VertexIndex; + UINT32 VertexCount; + UINTN RequiredScratchSize; + UINT8 TypeCount; + UINT16 Start; + UINT16 Stop; + UINT8 Type; + RETURN_STATUS Status; + + Base0 = Ranges[0].BaseAddress; + Base1 = Ranges[RangeCount - 1].BaseAddress + Ranges[RangeCount - 1].Length; + MTRR_LIB_ASSERT_ALIGNED (Base0, Base1 - Base0); + + // + // Count the number of vertices. + // + Vertices = (MTRR_LIB_ADDRESS*)Scratch; + for (VertexIndex = 0, Index = 0; Index < RangeCount; Index++) { + Base = Ranges[Index].BaseAddress; + Length = Ranges[Index].Length; + while (Length != 0) { + Alignment = MtrrLibBiggestAlignment (Base, A0); + SubLength = Alignment; + if (SubLength > Length) { + SubLength = GetPowerOfTwo64 (Length); + } + if (VertexIndex < *ScratchSize / sizeof (*Vertices)) { + Vertices[VertexIndex].Address = Base; + Vertices[VertexIndex].Alignment = Alignment; + Vertices[VertexIndex].Type = Ranges[Index].Type; + Vertices[VertexIndex].Length = SubLength; + } + Base += SubLength; + Length -= SubLength; + VertexIndex++; + } + } + // + // Vertices[VertexIndex] = Base1, so whole vertex count is (VertexIndex + 1). + // + VertexCount = VertexIndex + 1; + DEBUG (( + DEBUG_CACHE, " Count of vertices (%016llx - %016llx) = %d\n", + Ranges[0].BaseAddress, Ranges[RangeCount - 1].BaseAddress + Ranges[RangeCount - 1].Length, VertexCount + )); + ASSERT (VertexCount < MAX_UINT16); + + RequiredScratchSize = VertexCount * sizeof (*Vertices) + VertexCount * VertexCount * sizeof (*Weight); + if (*ScratchSize < RequiredScratchSize) { + *ScratchSize = RequiredScratchSize; + return RETURN_BUFFER_TOO_SMALL; + } + Vertices[VertexCount - 1].Address = Base1; + + Weight = (UINT8 *) &Vertices[VertexCount]; + for (VertexIndex = 0; VertexIndex < VertexCount; VertexIndex++) { + // + // Set optional weight between vertices and self->self to 0 + // + SetMem (&Weight[M(VertexIndex, 0)], VertexIndex + 1, 0); + // + // Set mandatory weight between vertices to MAX_WEIGHT + // + SetMem (&Weight[M (VertexIndex, VertexIndex + 1)], VertexCount - VertexIndex - 1, MAX_WEIGHT); + + // Final result looks like: + // 00 FF FF FF + // 00 00 FF FF + // 00 00 00 FF + // 00 00 00 00 + } + + // + // Set mandatory weight and optional weight for adjacent vertices + // + for (VertexIndex = 0; VertexIndex < VertexCount - 1; VertexIndex++) { + if (Vertices[VertexIndex].Type != DefaultType) { + Weight[M (VertexIndex, VertexIndex + 1)] = 1; + Weight[O (VertexIndex, VertexIndex + 1)] = 0; + } else { + Weight[M (VertexIndex, VertexIndex + 1)] = 0; + Weight[O (VertexIndex, VertexIndex + 1)] = 1; + } + } + + for (TypeCount = 2; TypeCount <= 3; TypeCount++) { + for (Start = 0; Start < VertexCount; Start++) { + for (Stop = Start + 2; Stop < VertexCount; Stop++) { + ASSERT (Vertices[Stop].Address > Vertices[Start].Address); + Length = Vertices[Stop].Address - Vertices[Start].Address; + if (Length > Vertices[Start].Alignment) { + // + // Pickup a new Start when [Start, Stop) cannot be described by one MTRR. + // + break; + } + if ((Weight[M(Start, Stop)] == MAX_WEIGHT) && MtrrLibIsPowerOfTwo (Length)) { + if (MtrrLibGetNumberOfTypes ( + Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type + ) == TypeCount) { + // + // Update the Weight[Start, Stop] using subtractive path. + // + MtrrLibCalculateSubtractivePath ( + DefaultType, A0, + Ranges, RangeCount, + (UINT16)VertexCount, Vertices, Weight, + Start, Stop, Type, TypeCount, + NULL, 0, NULL + ); + } else if (TypeCount == 2) { + // + // Pick up a new Start when we expect 2-type range, but 3-type range is met. + // Because no matter how Stop is increased, we always meet 3-type range. + // + break; + } + } + } + } + } + + Status = RETURN_SUCCESS; + MtrrLibCalculateLeastMtrrs ((UINT16) VertexCount, Vertices, Weight, 0, (UINT16) VertexCount - 1, FALSE); + Stop = (UINT16) VertexCount - 1; + while (Stop != 0) { + Start = Vertices[Stop].Previous; + TypeCount = MAX_UINT8; + Type = 0; + if (Weight[M(Start, Stop)] != 0) { + TypeCount = MtrrLibGetNumberOfTypes (Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type); + Status = MtrrLibAppendVariableMtrr ( + Mtrrs, MtrrCapacity, MtrrCount, + Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, + MtrrLibLowestType (Type) + ); + if (RETURN_ERROR (Status)) { + break; + } + } + + if (Start != Stop - 1) { + // + // substractive path + // + if (TypeCount == MAX_UINT8) { + TypeCount = MtrrLibGetNumberOfTypes ( + Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type + ); + } + Status = MtrrLibCalculateSubtractivePath ( + DefaultType, A0, + Ranges, RangeCount, + (UINT16) VertexCount, Vertices, Weight, Start, Stop, + Type, TypeCount, + Mtrrs, MtrrCapacity, MtrrCount + ); + if (RETURN_ERROR (Status)) { + break; + } + } + Stop = Start; + } + return Status; +} + + +/** + Apply the fixed MTRR settings to memory range array. + + @param Fixed The fixed MTRR settings. + @param Ranges Return the memory range array holding memory type + settings for all memory address. + @param RangeCapacity The capacity of memory range array. + @param RangeCount Return the count of memory range. + + @retval RETURN_SUCCESS The memory range array is returned successfully. + @retval RETURN_OUT_OF_RESOURCES The count of memory ranges exceeds capacity. +**/ +RETURN_STATUS +MtrrLibApplyFixedMtrrs ( + IN MTRR_FIXED_SETTINGS *Fixed, + IN OUT MTRR_MEMORY_RANGE *Ranges, + IN UINTN RangeCapacity, + IN OUT UINTN *RangeCount + ) +{ + RETURN_STATUS Status; + UINTN MsrIndex; + UINTN Index; + MTRR_MEMORY_CACHE_TYPE MemoryType; + UINT64 Base; + + Base = 0; + for (MsrIndex = 0; MsrIndex < ARRAY_SIZE (mMtrrLibFixedMtrrTable); MsrIndex++) { + ASSERT (Base == mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress); + for (Index = 0; Index < sizeof (UINT64); Index++) { + MemoryType = (MTRR_MEMORY_CACHE_TYPE)((UINT8 *)(&Fixed->Mtrr[MsrIndex]))[Index]; + Status = MtrrLibSetMemoryType ( + Ranges, RangeCapacity, RangeCount, Base, mMtrrLibFixedMtrrTable[MsrIndex].Length, MemoryType + ); + if (Status == RETURN_OUT_OF_RESOURCES) { + return Status; + } + Base += mMtrrLibFixedMtrrTable[MsrIndex].Length; + } + } + ASSERT (Base == BASE_1MB); + return RETURN_SUCCESS; +} + +/** + Apply the variable MTRR settings to memory range array. + + @param VariableMtrr The variable MTRR array. + @param VariableMtrrCount The count of variable MTRRs. + @param Ranges Return the memory range array with new MTRR settings applied. + @param RangeCapacity The capacity of memory range array. + @param RangeCount Return the count of memory range. + + @retval RETURN_SUCCESS The memory range array is returned successfully. + @retval RETURN_OUT_OF_RESOURCES The count of memory ranges exceeds capacity. +**/ +RETURN_STATUS +MtrrLibApplyVariableMtrrs ( + IN CONST MTRR_MEMORY_RANGE *VariableMtrr, + IN UINT32 VariableMtrrCount, + IN OUT MTRR_MEMORY_RANGE *Ranges, + IN UINTN RangeCapacity, + IN OUT UINTN *RangeCount + ) +{ + RETURN_STATUS Status; + UINTN Index; + + // + // WT > WB + // UC > * + // UC > * (except WB, UC) > WB + // + + // + // 1. Set WB + // + for (Index = 0; Index < VariableMtrrCount; Index++) { + if ((VariableMtrr[Index].Length != 0) && (VariableMtrr[Index].Type == CacheWriteBack)) { + Status = MtrrLibSetMemoryType ( + Ranges, RangeCapacity, RangeCount, + VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, VariableMtrr[Index].Type + ); + if (Status == RETURN_OUT_OF_RESOURCES) { + return Status; + } + } + } + + // + // 2. Set other types than WB or UC + // + for (Index = 0; Index < VariableMtrrCount; Index++) { + if ((VariableMtrr[Index].Length != 0) && + (VariableMtrr[Index].Type != CacheWriteBack) && (VariableMtrr[Index].Type != CacheUncacheable)) { + Status = MtrrLibSetMemoryType ( + Ranges, RangeCapacity, RangeCount, + VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, VariableMtrr[Index].Type + ); + if (Status == RETURN_OUT_OF_RESOURCES) { + return Status; + } + } + } + + // + // 3. Set UC + // + for (Index = 0; Index < VariableMtrrCount; Index++) { + if (VariableMtrr[Index].Length != 0 && VariableMtrr[Index].Type == CacheUncacheable) { + Status = MtrrLibSetMemoryType ( + Ranges, RangeCapacity, RangeCount, + VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, VariableMtrr[Index].Type + ); + if (Status == RETURN_OUT_OF_RESOURCES) { + return Status; + } + } + } + return RETURN_SUCCESS; +} + +/** + Return the memory type bit mask that's compatible to first type in the Ranges. + + @param Ranges Memory range array holding the memory type + settings for all memory address. + @param RangeCount Count of memory ranges. + + @return Compatible memory type bit mask. +**/ +UINT8 +MtrrLibGetCompatibleTypes ( + IN CONST MTRR_MEMORY_RANGE *Ranges, + IN UINTN RangeCount + ) +{ + ASSERT (RangeCount != 0); + + switch (Ranges[0].Type) { + case CacheWriteBack: + case CacheWriteThrough: + return (1 << CacheWriteBack) | (1 << CacheWriteThrough) | (1 << CacheUncacheable); + break; + + case CacheWriteCombining: + case CacheWriteProtected: + return (1 << Ranges[0].Type) | (1 << CacheUncacheable); + break; + + case CacheUncacheable: + if (RangeCount == 1) { + return (1 << CacheUncacheable); + } + return MtrrLibGetCompatibleTypes (&Ranges[1], RangeCount - 1); + break; + + case CacheInvalid: + default: + ASSERT (FALSE); + break; + } + return 0; +} + +/** + Overwrite the destination MTRR settings with the source MTRR settings. + This routine is to make sure the modification to destination MTRR settings + is as small as possible. + + @param DstMtrrs Destination MTRR settings. + @param DstMtrrCount Count of destination MTRR settings. + @param SrcMtrrs Source MTRR settings. + @param SrcMtrrCount Count of source MTRR settings. + @param Modified Flag array to indicate which destination MTRR setting is modified. +**/ +VOID +MtrrLibMergeVariableMtrr ( + MTRR_MEMORY_RANGE *DstMtrrs, + UINT32 DstMtrrCount, + MTRR_MEMORY_RANGE *SrcMtrrs, + UINT32 SrcMtrrCount, + BOOLEAN *Modified + ) +{ + UINT32 DstIndex; + UINT32 SrcIndex; + + ASSERT (SrcMtrrCount <= DstMtrrCount); + + for (DstIndex = 0; DstIndex < DstMtrrCount; DstIndex++) { + Modified[DstIndex] = FALSE; + + if (DstMtrrs[DstIndex].Length == 0) { + continue; + } + for (SrcIndex = 0; SrcIndex < SrcMtrrCount; SrcIndex++) { + if (DstMtrrs[DstIndex].BaseAddress == SrcMtrrs[SrcIndex].BaseAddress && + DstMtrrs[DstIndex].Length == SrcMtrrs[SrcIndex].Length && + DstMtrrs[DstIndex].Type == SrcMtrrs[SrcIndex].Type) { + break; + } + } + + if (SrcIndex == SrcMtrrCount) { + // + // Remove the one from DstMtrrs which is not in SrcMtrrs + // + DstMtrrs[DstIndex].Length = 0; + Modified[DstIndex] = TRUE; + } else { + // + // Remove the one from SrcMtrrs which is also in DstMtrrs + // + SrcMtrrs[SrcIndex].Length = 0; + } + } + + // + // Now valid MTRR only exists in either DstMtrrs or SrcMtrrs. + // Merge MTRRs from SrcMtrrs to DstMtrrs + // + DstIndex = 0; + for (SrcIndex = 0; SrcIndex < SrcMtrrCount; SrcIndex++) { + if (SrcMtrrs[SrcIndex].Length != 0) { + + // + // Find the empty slot in DstMtrrs + // + while (DstIndex < DstMtrrCount) { + if (DstMtrrs[DstIndex].Length == 0) { + break; + } + DstIndex++; + } + ASSERT (DstIndex < DstMtrrCount); + CopyMem (&DstMtrrs[DstIndex], &SrcMtrrs[SrcIndex], sizeof (SrcMtrrs[0])); + Modified[DstIndex] = TRUE; + } + } +} + +/** + Calculate the variable MTRR settings for all memory ranges. + + @param DefaultType Default memory type. + @param A0 Alignment to use when base address is 0. + @param Ranges Memory range array holding the memory type + settings for all memory address. + @param RangeCount Count of memory ranges. + @param Scratch Scratch buffer to be used in MTRR calculation. + @param ScratchSize Pointer to the size of scratch buffer. + @param VariableMtrr Array holding all MTRR settings. + @param VariableMtrrCapacity Capacity of the MTRR array. + @param VariableMtrrCount The count of MTRR settings in array. + + @retval RETURN_SUCCESS Variable MTRRs are allocated successfully. + @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity. + @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation. + The required scratch buffer size is returned through ScratchSize. +**/ +RETURN_STATUS +MtrrLibSetMemoryRanges ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT64 A0, + IN MTRR_MEMORY_RANGE *Ranges, + IN UINTN RangeCount, + IN VOID *Scratch, + IN OUT UINTN *ScratchSize, + OUT MTRR_MEMORY_RANGE *VariableMtrr, + IN UINT32 VariableMtrrCapacity, + OUT UINT32 *VariableMtrrCount + ) +{ + RETURN_STATUS Status; + UINT32 Index; + UINT64 Base0; + UINT64 Base1; + UINT64 Alignment; + UINT8 CompatibleTypes; + UINT64 Length; + UINT32 End; + UINTN ActualScratchSize; + UINTN BiggestScratchSize; + + *VariableMtrrCount = 0; + + // + // Since the whole ranges need multiple calls of MtrrLibCalculateMtrrs(). + // Each call needs different scratch buffer size. + // When the provided scratch buffer size is not sufficient in any call, + // set the GetActualScratchSize to TRUE, and following calls will only + // calculate the actual scratch size for the caller. + // + BiggestScratchSize = 0; + + for (Index = 0; Index < RangeCount;) { + Base0 = Ranges[Index].BaseAddress; + + // + // Full step is optimal + // + while (Index < RangeCount) { + ASSERT (Ranges[Index].BaseAddress == Base0); + Alignment = MtrrLibBiggestAlignment (Base0, A0); + while (Base0 + Alignment <= Ranges[Index].BaseAddress + Ranges[Index].Length) { + if ((BiggestScratchSize <= *ScratchSize) && (Ranges[Index].Type != DefaultType)) { + Status = MtrrLibAppendVariableMtrr ( + VariableMtrr, VariableMtrrCapacity, VariableMtrrCount, + Base0, Alignment, Ranges[Index].Type + ); + if (RETURN_ERROR (Status)) { + return Status; + } + } + Base0 += Alignment; + Alignment = MtrrLibBiggestAlignment (Base0, A0); + } + + // + // Remove the above range from Ranges[Index] + // + Ranges[Index].Length -= Base0 - Ranges[Index].BaseAddress; + Ranges[Index].BaseAddress = Base0; + if (Ranges[Index].Length != 0) { + break; + } else { + Index++; + } + } + + if (Index == RangeCount) { + break; + } + + // + // Find continous ranges [Base0, Base1) which could be combined by MTRR. + // Per SDM, the compatible types between[B0, B1) are: + // UC, * + // WB, WT + // UC, WB, WT + // + CompatibleTypes = MtrrLibGetCompatibleTypes (&Ranges[Index], RangeCount - Index); + + End = Index; // End points to last one that matches the CompatibleTypes. + while (End + 1 < RangeCount) { + if (((1 << Ranges[End + 1].Type) & CompatibleTypes) == 0) { + break; + } + End++; + } + Alignment = MtrrLibBiggestAlignment (Base0, A0); + Length = GetPowerOfTwo64 (Ranges[End].BaseAddress + Ranges[End].Length - Base0); + Base1 = Base0 + MIN (Alignment, Length); + + // + // Base1 may not in Ranges[End]. Update End to the range Base1 belongs to. + // + End = Index; + while (End + 1 < RangeCount) { + if (Base1 <= Ranges[End + 1].BaseAddress) { + break; + } + End++; + } + + Length = Ranges[End].Length; + Ranges[End].Length = Base1 - Ranges[End].BaseAddress; + ActualScratchSize = *ScratchSize; + Status = MtrrLibCalculateMtrrs ( + DefaultType, A0, + &Ranges[Index], End + 1 - Index, + Scratch, &ActualScratchSize, + VariableMtrr, VariableMtrrCapacity, VariableMtrrCount + ); + if (Status == RETURN_BUFFER_TOO_SMALL) { + BiggestScratchSize = MAX (BiggestScratchSize, ActualScratchSize); + // + // Ignore this error, because we need to calculate the biggest + // scratch buffer size. + // + Status = RETURN_SUCCESS; + } + if (RETURN_ERROR (Status)) { + return Status; + } + + if (Length != Ranges[End].Length) { + Ranges[End].BaseAddress = Base1; + Ranges[End].Length = Length - Ranges[End].Length; + Index = End; + } else { + Index = End + 1; + } + } + + if (*ScratchSize < BiggestScratchSize) { + *ScratchSize = BiggestScratchSize; + return RETURN_BUFFER_TOO_SMALL; + } + return RETURN_SUCCESS; +} + +/** + Set the below-1MB memory attribute to fixed MTRR buffer. + Modified flag array indicates which fixed MTRR is modified. + + @param [in, out] ClearMasks The bits (when set) to clear in the fixed MTRR MSR. + @param [in, out] OrMasks The bits to set in the fixed MTRR MSR. + @param [in] BaseAddress Base address. + @param [in] Length Length. + @param [in] Type Memory type. + + @retval RETURN_SUCCESS The memory attribute is set successfully. + @retval RETURN_UNSUPPORTED The requested range or cache type was invalid + for the fixed MTRRs. +**/ +RETURN_STATUS +MtrrLibSetBelow1MBMemoryAttribute ( + IN OUT UINT64 *ClearMasks, + IN OUT UINT64 *OrMasks, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN MTRR_MEMORY_CACHE_TYPE Type + ) +{ + RETURN_STATUS Status; + UINT32 MsrIndex; + UINT64 ClearMask; + UINT64 OrMask; + + ASSERT (BaseAddress < BASE_1MB); + + MsrIndex = (UINT32)-1; + while ((BaseAddress < BASE_1MB) && (Length != 0)) { + Status = MtrrLibProgramFixedMtrr (Type, &BaseAddress, &Length, &MsrIndex, &ClearMask, &OrMask); + if (RETURN_ERROR (Status)) { + return Status; + } + ClearMasks[MsrIndex] = ClearMasks[MsrIndex] | ClearMask; + OrMasks[MsrIndex] = (OrMasks[MsrIndex] & ~ClearMask) | OrMask; + } + return RETURN_SUCCESS; +} + +/** + This function attempts to set the attributes into MTRR setting buffer for multiple memory ranges. + + @param[in, out] MtrrSetting MTRR setting buffer to be set. + @param[in] Scratch A temporary scratch buffer that is used to perform the calculation. + @param[in, out] ScratchSize Pointer to the size in bytes of the scratch buffer. + It may be updated to the actual required size when the calculation + needs more scratch buffer. + @param[in] Ranges Pointer to an array of MTRR_MEMORY_RANGE. + When range overlap happens, the last one takes higher priority. + When the function returns, either all the attributes are set successfully, + or none of them is set. + @param[in] RangeCount Count of MTRR_MEMORY_RANGE. + + @retval RETURN_SUCCESS The attributes were set for all the memory ranges. + @retval RETURN_INVALID_PARAMETER Length in any range is zero. + @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the + memory resource range specified by BaseAddress and Length in any range. + @retval RETURN_UNSUPPORTED The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length in any range. + @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource ranges. + @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation. +**/ +RETURN_STATUS +EFIAPI +MtrrSetMemoryAttributesInMtrrSettings ( + IN OUT MTRR_SETTINGS *MtrrSetting, + IN VOID *Scratch, + IN OUT UINTN *ScratchSize, + IN CONST MTRR_MEMORY_RANGE *Ranges, + IN UINTN RangeCount + ) +{ + RETURN_STATUS Status; + UINT32 Index; + UINT64 BaseAddress; + UINT64 Length; + BOOLEAN Above1MbExist; + + UINT64 MtrrValidBitsMask; + UINT64 MtrrValidAddressMask; + MTRR_MEMORY_CACHE_TYPE DefaultType; + MTRR_VARIABLE_SETTINGS VariableSettings; + MTRR_MEMORY_RANGE WorkingRanges[2 * ARRAY_SIZE (MtrrSetting->Variables.Mtrr) + 2]; + UINTN WorkingRangeCount; + BOOLEAN Modified; + MTRR_VARIABLE_SETTING VariableSetting; + UINT32 OriginalVariableMtrrCount; + UINT32 FirmwareVariableMtrrCount; + UINT32 WorkingVariableMtrrCount; + MTRR_MEMORY_RANGE OriginalVariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)]; + MTRR_MEMORY_RANGE WorkingVariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)]; + BOOLEAN VariableSettingModified[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)]; + + UINT64 ClearMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)]; + UINT64 OrMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)]; + + MTRR_CONTEXT MtrrContext; + BOOLEAN MtrrContextValid; + + Status = RETURN_SUCCESS; + MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask); + + // + // TRUE indicating the accordingly Variable setting needs modificaiton in OriginalVariableMtrr. + // + SetMem (VariableSettingModified, ARRAY_SIZE (VariableSettingModified), FALSE); + + // + // TRUE indicating the caller requests to set variable MTRRs. + // + Above1MbExist = FALSE; + OriginalVariableMtrrCount = 0; + + // + // 0. Dump the requests. + // + DEBUG_CODE ( + DEBUG ((DEBUG_CACHE, "Mtrr: Set Mem Attribute to %a, ScratchSize = %x%a", + (MtrrSetting == NULL) ? "Hardware" : "Buffer", *ScratchSize, + (RangeCount <= 1) ? "," : "\n" + )); + for (Index = 0; Index < RangeCount; Index++) { + DEBUG ((DEBUG_CACHE, " %a: [%016lx, %016lx)\n", + mMtrrMemoryCacheTypeShortName[MIN (Ranges[Index].Type, CacheInvalid)], + Ranges[Index].BaseAddress, Ranges[Index].BaseAddress + Ranges[Index].Length + )); + } + ); + + // + // 1. Validate the parameters. + // + if (!IsMtrrSupported ()) { + Status = RETURN_UNSUPPORTED; + goto Exit; + } + + for (Index = 0; Index < RangeCount; Index++) { + if (Ranges[Index].Length == 0) { + Status = RETURN_INVALID_PARAMETER; + goto Exit; + } + if (((Ranges[Index].BaseAddress & ~MtrrValidAddressMask) != 0) || + ((((Ranges[Index].BaseAddress + Ranges[Index].Length) & ~MtrrValidAddressMask) != 0) && + (Ranges[Index].BaseAddress + Ranges[Index].Length) != MtrrValidBitsMask + 1) + ) { + // + // Either the BaseAddress or the Limit doesn't follow the alignment requirement. + // Note: It's still valid if Limit doesn't follow the alignment requirement but equals to MAX Address. + // + Status = RETURN_UNSUPPORTED; + goto Exit; + } + if ((Ranges[Index].Type != CacheUncacheable) && + (Ranges[Index].Type != CacheWriteCombining) && + (Ranges[Index].Type != CacheWriteThrough) && + (Ranges[Index].Type != CacheWriteProtected) && + (Ranges[Index].Type != CacheWriteBack)) { + Status = RETURN_INVALID_PARAMETER; + goto Exit; + } + if (Ranges[Index].BaseAddress + Ranges[Index].Length > BASE_1MB) { + Above1MbExist = TRUE; + } + } + + // + // 2. Apply the above-1MB memory attribute settings. + // + if (Above1MbExist) { + // + // 2.1. Read all variable MTRRs and convert to Ranges. + // + OriginalVariableMtrrCount = GetVariableMtrrCountWorker (); + MtrrGetVariableMtrrWorker (MtrrSetting, OriginalVariableMtrrCount, &VariableSettings); + MtrrLibGetRawVariableRanges ( + &VariableSettings, OriginalVariableMtrrCount, + MtrrValidBitsMask, MtrrValidAddressMask, OriginalVariableMtrr + ); + + DefaultType = MtrrGetDefaultMemoryTypeWorker (MtrrSetting); + WorkingRangeCount = 1; + WorkingRanges[0].BaseAddress = 0; + WorkingRanges[0].Length = MtrrValidBitsMask + 1; + WorkingRanges[0].Type = DefaultType; + + Status = MtrrLibApplyVariableMtrrs ( + OriginalVariableMtrr, OriginalVariableMtrrCount, + WorkingRanges, ARRAY_SIZE (WorkingRanges), &WorkingRangeCount); + ASSERT_RETURN_ERROR (Status); + + ASSERT (OriginalVariableMtrrCount >= PcdGet32 (PcdCpuNumberOfReservedVariableMtrrs)); + FirmwareVariableMtrrCount = OriginalVariableMtrrCount - PcdGet32 (PcdCpuNumberOfReservedVariableMtrrs); + ASSERT (WorkingRangeCount <= 2 * FirmwareVariableMtrrCount + 1); + + // + // 2.2. Force [0, 1M) to UC, so that it doesn't impact subtraction algorithm. + // + Status = MtrrLibSetMemoryType ( + WorkingRanges, ARRAY_SIZE (WorkingRanges), &WorkingRangeCount, + 0, SIZE_1MB, CacheUncacheable + ); + ASSERT (Status != RETURN_OUT_OF_RESOURCES); + + // + // 2.3. Apply the new memory attribute settings to Ranges. + // + Modified = FALSE; + for (Index = 0; Index < RangeCount; Index++) { + BaseAddress = Ranges[Index].BaseAddress; + Length = Ranges[Index].Length; + if (BaseAddress < BASE_1MB) { + if (Length <= BASE_1MB - BaseAddress) { + continue; + } + Length -= BASE_1MB - BaseAddress; + BaseAddress = BASE_1MB; + } + Status = MtrrLibSetMemoryType ( + WorkingRanges, ARRAY_SIZE (WorkingRanges), &WorkingRangeCount, + BaseAddress, Length, Ranges[Index].Type + ); + if (Status == RETURN_ALREADY_STARTED) { + Status = RETURN_SUCCESS; + } else if (Status == RETURN_OUT_OF_RESOURCES) { + goto Exit; + } else { + ASSERT_RETURN_ERROR (Status); + Modified = TRUE; + } + } + + if (Modified) { + // + // 2.4. Calculate the Variable MTRR settings based on the Ranges. + // Buffer Too Small may be returned if the scratch buffer size is insufficient. + // + Status = MtrrLibSetMemoryRanges ( + DefaultType, LShiftU64 (1, (UINTN)HighBitSet64 (MtrrValidBitsMask)), WorkingRanges, WorkingRangeCount, + Scratch, ScratchSize, + WorkingVariableMtrr, FirmwareVariableMtrrCount + 1, &WorkingVariableMtrrCount + ); + if (RETURN_ERROR (Status)) { + goto Exit; + } + + // + // 2.5. Remove the [0, 1MB) MTRR if it still exists (not merged with other range) + // + for (Index = 0; Index < WorkingVariableMtrrCount; Index++) { + if (WorkingVariableMtrr[Index].BaseAddress == 0 && WorkingVariableMtrr[Index].Length == SIZE_1MB) { + ASSERT (WorkingVariableMtrr[Index].Type == CacheUncacheable); + WorkingVariableMtrrCount--; + CopyMem ( + &WorkingVariableMtrr[Index], &WorkingVariableMtrr[Index + 1], + (WorkingVariableMtrrCount - Index) * sizeof (WorkingVariableMtrr[0]) + ); + break; + } + } + + if (WorkingVariableMtrrCount > FirmwareVariableMtrrCount) { + Status = RETURN_OUT_OF_RESOURCES; + goto Exit; + } + + // + // 2.6. Merge the WorkingVariableMtrr to OriginalVariableMtrr + // Make sure least modification is made to OriginalVariableMtrr. + // + MtrrLibMergeVariableMtrr ( + OriginalVariableMtrr, OriginalVariableMtrrCount, + WorkingVariableMtrr, WorkingVariableMtrrCount, + VariableSettingModified + ); + } + } + + // + // 3. Apply the below-1MB memory attribute settings. + // + // (Value & ~0 | 0) still equals to (Value) + // + ZeroMem (ClearMasks, sizeof (ClearMasks)); + ZeroMem (OrMasks, sizeof (OrMasks)); + for (Index = 0; Index < RangeCount; Index++) { + if (Ranges[Index].BaseAddress >= BASE_1MB) { + continue; + } + + Status = MtrrLibSetBelow1MBMemoryAttribute ( + ClearMasks, OrMasks, + Ranges[Index].BaseAddress, Ranges[Index].Length, Ranges[Index].Type + ); + if (RETURN_ERROR (Status)) { + goto Exit; + } + } + + MtrrContextValid = FALSE; + // + // 4. Write fixed MTRRs that have been modified + // + for (Index = 0; Index < ARRAY_SIZE (ClearMasks); Index++) { + if (ClearMasks[Index] != 0) { + if (MtrrSetting != NULL) { + MtrrSetting->Fixed.Mtrr[Index] = (MtrrSetting->Fixed.Mtrr[Index] & ~ClearMasks[Index]) | OrMasks[Index]; + } else { + if (!MtrrContextValid) { + MtrrLibPreMtrrChange (&MtrrContext); + MtrrContextValid = TRUE; + } + AsmMsrAndThenOr64 (mMtrrLibFixedMtrrTable[Index].Msr, ~ClearMasks[Index], OrMasks[Index]); + } + } + } + + // + // 5. Write variable MTRRs that have been modified + // + for (Index = 0; Index < OriginalVariableMtrrCount; Index++) { + if (VariableSettingModified[Index]) { + if (OriginalVariableMtrr[Index].Length != 0) { + VariableSetting.Base = (OriginalVariableMtrr[Index].BaseAddress & MtrrValidAddressMask) + | (UINT8)OriginalVariableMtrr[Index].Type; + VariableSetting.Mask = ((~(OriginalVariableMtrr[Index].Length - 1)) & MtrrValidAddressMask) | BIT11; + } else { + VariableSetting.Base = 0; + VariableSetting.Mask = 0; + } + if (MtrrSetting != NULL) { + CopyMem (&MtrrSetting->Variables.Mtrr[Index], &VariableSetting, sizeof (VariableSetting)); + } else { + if (!MtrrContextValid) { + MtrrLibPreMtrrChange (&MtrrContext); + MtrrContextValid = TRUE; + } + AsmWriteMsr64 ( + MSR_IA32_MTRR_PHYSBASE0 + (Index << 1), + VariableSetting.Base + ); + AsmWriteMsr64 ( + MSR_IA32_MTRR_PHYSMASK0 + (Index << 1), + VariableSetting.Mask + ); + } + } + } + + if (MtrrSetting != NULL) { + ((MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&MtrrSetting->MtrrDefType)->Bits.E = 1; + ((MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&MtrrSetting->MtrrDefType)->Bits.FE = 1; + } else { + if (MtrrContextValid) { + MtrrLibPostMtrrChange (&MtrrContext); + } + } + +Exit: + DEBUG ((DEBUG_CACHE, " Result = %r\n", Status)); + if (!RETURN_ERROR (Status)) { + MtrrDebugPrintAllMtrrsWorker (MtrrSetting); + } + return Status; +} + +/** + This function attempts to set the attributes into MTRR setting buffer for a memory range. + + @param[in, out] MtrrSetting MTRR setting buffer to be set. + @param[in] BaseAddress The physical address that is the start address + of a memory range. + @param[in] Length The size in bytes of the memory range. + @param[in] Attribute The bit mask of attributes to set for the + memory range. + + @retval RETURN_SUCCESS The attributes were set for the memory range. + @retval RETURN_INVALID_PARAMETER Length is zero. + @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the + memory resource range specified by BaseAddress and Length. + @retval RETURN_UNSUPPORTED The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. + @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + Multiple memory range attributes setting by calling this API multiple + times may fail with status RETURN_OUT_OF_RESOURCES. It may not mean + the number of CPU MTRRs are too small to set such memory attributes. + Pass the multiple memory range attributes to one call of + MtrrSetMemoryAttributesInMtrrSettings() may succeed. + @retval RETURN_BUFFER_TOO_SMALL The fixed internal scratch buffer is too small for MTRR calculation. + Caller should use MtrrSetMemoryAttributesInMtrrSettings() to specify + external scratch buffer. +**/ +RETURN_STATUS +EFIAPI +MtrrSetMemoryAttributeInMtrrSettings ( + IN OUT MTRR_SETTINGS *MtrrSetting, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN MTRR_MEMORY_CACHE_TYPE Attribute + ) +{ + UINT8 Scratch[SCRATCH_BUFFER_SIZE]; + UINTN ScratchSize; + MTRR_MEMORY_RANGE Range; + + Range.BaseAddress = BaseAddress; + Range.Length = Length; + Range.Type = Attribute; + ScratchSize = sizeof (Scratch); + return MtrrSetMemoryAttributesInMtrrSettings (MtrrSetting, Scratch, &ScratchSize, &Range, 1); +} + +/** + This function attempts to set the attributes for a memory range. + + @param[in] BaseAddress The physical address that is the start + address of a memory range. + @param[in] Length The size in bytes of the memory range. + @param[in] Attributes The bit mask of attributes to set for the + memory range. + + @retval RETURN_SUCCESS The attributes were set for the memory + range. + @retval RETURN_INVALID_PARAMETER Length is zero. + @retval RETURN_UNSUPPORTED The processor does not support one or + more bytes of the memory resource range + specified by BaseAddress and Length. + @retval RETURN_UNSUPPORTED The bit mask of attributes is not support + for the memory resource range specified + by BaseAddress and Length. + @retval RETURN_ACCESS_DENIED The attributes for the memory resource + range specified by BaseAddress and Length + cannot be modified. + @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to + modify the attributes of the memory + resource range. + Multiple memory range attributes setting by calling this API multiple + times may fail with status RETURN_OUT_OF_RESOURCES. It may not mean + the number of CPU MTRRs are too small to set such memory attributes. + Pass the multiple memory range attributes to one call of + MtrrSetMemoryAttributesInMtrrSettings() may succeed. + @retval RETURN_BUFFER_TOO_SMALL The fixed internal scratch buffer is too small for MTRR calculation. + Caller should use MtrrSetMemoryAttributesInMtrrSettings() to specify + external scratch buffer. +**/ +RETURN_STATUS +EFIAPI +MtrrSetMemoryAttribute ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN MTRR_MEMORY_CACHE_TYPE Attribute + ) +{ + return MtrrSetMemoryAttributeInMtrrSettings (NULL, BaseAddress, Length, Attribute); +} + +/** + Worker function setting variable MTRRs + + @param[in] VariableSettings A buffer to hold variable MTRRs content. + +**/ +VOID +MtrrSetVariableMtrrWorker ( + IN MTRR_VARIABLE_SETTINGS *VariableSettings + ) +{ + UINT32 Index; + UINT32 VariableMtrrCount; + + VariableMtrrCount = GetVariableMtrrCountWorker (); + ASSERT (VariableMtrrCount <= ARRAY_SIZE (VariableSettings->Mtrr)); + + for (Index = 0; Index < VariableMtrrCount; Index++) { + AsmWriteMsr64 ( + MSR_IA32_MTRR_PHYSBASE0 + (Index << 1), + VariableSettings->Mtrr[Index].Base + ); + AsmWriteMsr64 ( + MSR_IA32_MTRR_PHYSMASK0 + (Index << 1), + VariableSettings->Mtrr[Index].Mask + ); + } +} + +/** + Worker function setting fixed MTRRs + + @param[in] FixedSettings A buffer to hold fixed MTRRs content. + +**/ +VOID +MtrrSetFixedMtrrWorker ( + IN MTRR_FIXED_SETTINGS *FixedSettings + ) +{ + UINT32 Index; + + for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) { + AsmWriteMsr64 ( + mMtrrLibFixedMtrrTable[Index].Msr, + FixedSettings->Mtrr[Index] + ); + } +} + + +/** + This function gets the content in all MTRRs (variable and fixed) + + @param[out] MtrrSetting A buffer to hold all MTRRs content. + + @retval the pointer of MtrrSetting + +**/ +MTRR_SETTINGS * +EFIAPI +MtrrGetAllMtrrs ( + OUT MTRR_SETTINGS *MtrrSetting + ) +{ + if (!IsMtrrSupported ()) { + return MtrrSetting; + } + + // + // Get fixed MTRRs + // + MtrrGetFixedMtrrWorker (&MtrrSetting->Fixed); + + // + // Get variable MTRRs + // + MtrrGetVariableMtrrWorker ( + NULL, + GetVariableMtrrCountWorker (), + &MtrrSetting->Variables + ); + + // + // Get MTRR_DEF_TYPE value + // + MtrrSetting->MtrrDefType = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE); + + return MtrrSetting; +} + + +/** + This function sets all MTRRs (variable and fixed) + + @param[in] MtrrSetting A buffer holding all MTRRs content. + + @retval The pointer of MtrrSetting + +**/ +MTRR_SETTINGS * +EFIAPI +MtrrSetAllMtrrs ( + IN MTRR_SETTINGS *MtrrSetting + ) +{ + MTRR_CONTEXT MtrrContext; + + if (!IsMtrrSupported ()) { + return MtrrSetting; + } + + MtrrLibPreMtrrChange (&MtrrContext); + + // + // Set fixed MTRRs + // + MtrrSetFixedMtrrWorker (&MtrrSetting->Fixed); + + // + // Set variable MTRRs + // + MtrrSetVariableMtrrWorker (&MtrrSetting->Variables); + + // + // Set MTRR_DEF_TYPE value + // + AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, MtrrSetting->MtrrDefType); + + MtrrLibPostMtrrChangeEnableCache (&MtrrContext); + + return MtrrSetting; +} + + +/** + Checks if MTRR is supported. + + @retval TRUE MTRR is supported. + @retval FALSE MTRR is not supported. + +**/ +BOOLEAN +EFIAPI +IsMtrrSupported ( + VOID + ) +{ + CPUID_VERSION_INFO_EDX Edx; + MSR_IA32_MTRRCAP_REGISTER MtrrCap; + + // + // Check CPUID(1).EDX[12] for MTRR capability + // + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &Edx.Uint32); + if (Edx.Bits.MTRR == 0) { + return FALSE; + } + + // + // Check number of variable MTRRs and fixed MTRRs existence. + // If number of variable MTRRs is zero, or fixed MTRRs do not + // exist, return false. + // + MtrrCap.Uint64 = AsmReadMsr64 (MSR_IA32_MTRRCAP); + if ((MtrrCap.Bits.VCNT == 0) || (MtrrCap.Bits.FIX == 0)) { + return FALSE; + } + return TRUE; +} + + +/** + Worker function prints all MTRRs for debugging. + + If MtrrSetting is not NULL, print MTRR settings from input MTRR + settings buffer. + If MtrrSetting is NULL, print MTRR settings from MTRRs. + + @param MtrrSetting A buffer holding all MTRRs content. +**/ +VOID +MtrrDebugPrintAllMtrrsWorker ( + IN MTRR_SETTINGS *MtrrSetting + ) +{ + DEBUG_CODE ( + MTRR_SETTINGS LocalMtrrs; + MTRR_SETTINGS *Mtrrs; + UINTN Index; + UINTN RangeCount; + UINT64 MtrrValidBitsMask; + UINT64 MtrrValidAddressMask; + UINT32 VariableMtrrCount; + BOOLEAN ContainVariableMtrr; + MTRR_MEMORY_RANGE Ranges[ + ARRAY_SIZE (mMtrrLibFixedMtrrTable) * sizeof (UINT64) + 2 * ARRAY_SIZE (Mtrrs->Variables.Mtrr) + 1 + ]; + MTRR_MEMORY_RANGE RawVariableRanges[ARRAY_SIZE (Mtrrs->Variables.Mtrr)]; + + if (!IsMtrrSupported ()) { + return; + } + + VariableMtrrCount = GetVariableMtrrCountWorker (); + + if (MtrrSetting != NULL) { + Mtrrs = MtrrSetting; + } else { + MtrrGetAllMtrrs (&LocalMtrrs); + Mtrrs = &LocalMtrrs; + } + + // + // Dump RAW MTRR contents + // + DEBUG ((DEBUG_CACHE, "MTRR Settings:\n")); + DEBUG ((DEBUG_CACHE, "=============\n")); + DEBUG ((DEBUG_CACHE, "MTRR Default Type: %016lx\n", Mtrrs->MtrrDefType)); + for (Index = 0; Index < ARRAY_SIZE (mMtrrLibFixedMtrrTable); Index++) { + DEBUG ((DEBUG_CACHE, "Fixed MTRR[%02d] : %016lx\n", Index, Mtrrs->Fixed.Mtrr[Index])); + } + ContainVariableMtrr = FALSE; + for (Index = 0; Index < VariableMtrrCount; Index++) { + if ((Mtrrs->Variables.Mtrr[Index].Mask & BIT11) == 0) { + // + // If mask is not valid, then do not display range + // + continue; + } + ContainVariableMtrr = TRUE; + DEBUG ((DEBUG_CACHE, "Variable MTRR[%02d]: Base=%016lx Mask=%016lx\n", + Index, + Mtrrs->Variables.Mtrr[Index].Base, + Mtrrs->Variables.Mtrr[Index].Mask + )); + } + if (!ContainVariableMtrr) { + DEBUG ((DEBUG_CACHE, "Variable MTRR : None.\n")); + } + DEBUG((DEBUG_CACHE, "\n")); + + // + // Dump MTRR setting in ranges + // + DEBUG((DEBUG_CACHE, "Memory Ranges:\n")); + DEBUG((DEBUG_CACHE, "====================================\n")); + MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask); + Ranges[0].BaseAddress = 0; + Ranges[0].Length = MtrrValidBitsMask + 1; + Ranges[0].Type = MtrrGetDefaultMemoryTypeWorker (Mtrrs); + RangeCount = 1; + + MtrrLibGetRawVariableRanges ( + &Mtrrs->Variables, VariableMtrrCount, + MtrrValidBitsMask, MtrrValidAddressMask, RawVariableRanges + ); + MtrrLibApplyVariableMtrrs ( + RawVariableRanges, VariableMtrrCount, + Ranges, ARRAY_SIZE (Ranges), &RangeCount + ); + + MtrrLibApplyFixedMtrrs (&Mtrrs->Fixed, Ranges, ARRAY_SIZE (Ranges), &RangeCount); + + for (Index = 0; Index < RangeCount; Index++) { + DEBUG ((DEBUG_CACHE, "%a:%016lx-%016lx\n", + mMtrrMemoryCacheTypeShortName[Ranges[Index].Type], + Ranges[Index].BaseAddress, Ranges[Index].BaseAddress + Ranges[Index].Length - 1 + )); + } + ); +} + +/** + This function prints all MTRRs for debugging. +**/ +VOID +EFIAPI +MtrrDebugPrintAllMtrrs ( + VOID + ) +{ + MtrrDebugPrintAllMtrrsWorker (NULL); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.inf new file mode 100644 index 00000000..f92b8d02 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.inf @@ -0,0 +1,40 @@ +## @file +# MTRR library provides APIs for MTRR operation. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MtrrLib + MODULE_UNI_FILE = MtrrLib.uni + FILE_GUID = 6826b408-f4f3-47ee-917f-af7047f9d937 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MtrrLib + + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + MtrrLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseMemoryLib + BaseLib + CpuLib + DebugLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuNumberOfReservedVariableMtrrs ## SOMETIMES_CONSUMES + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.uni new file mode 100644 index 00000000..646ec2a3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/MtrrLib.uni @@ -0,0 +1,16 @@ +// /** @file
+// MTRR library provides APIs for MTRR operation.
+//
+// MTRR library provides APIs for MTRR operation.
+//
+// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "MTRR library provides APIs for MTRR operation"
+
+#string STR_MODULE_DESCRIPTION #language en-US "MTRR library provides APIs for MTRR operation."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTest.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTest.c new file mode 100644 index 00000000..d978a1a8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTest.c @@ -0,0 +1,1163 @@ +/** @file + Unit tests of the MtrrLib instance of the MtrrLib class + + Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "MtrrLibUnitTest.h" + +STATIC CONST MTRR_LIB_SYSTEM_PARAMETER mDefaultSystemParameter = { + 42, TRUE, TRUE, CacheUncacheable, 12 +}; + +STATIC MTRR_LIB_SYSTEM_PARAMETER mSystemParameters[] = { + { 38, TRUE, TRUE, CacheUncacheable, 12 }, + { 38, TRUE, TRUE, CacheWriteBack, 12 }, + { 38, TRUE, TRUE, CacheWriteThrough, 12 }, + { 38, TRUE, TRUE, CacheWriteProtected, 12 }, + { 38, TRUE, TRUE, CacheWriteCombining, 12 }, + + { 42, TRUE, TRUE, CacheUncacheable, 12 }, + { 42, TRUE, TRUE, CacheWriteBack, 12 }, + { 42, TRUE, TRUE, CacheWriteThrough, 12 }, + { 42, TRUE, TRUE, CacheWriteProtected, 12 }, + { 42, TRUE, TRUE, CacheWriteCombining, 12 }, + + { 48, TRUE, TRUE, CacheUncacheable, 12 }, + { 48, TRUE, TRUE, CacheWriteBack, 12 }, + { 48, TRUE, TRUE, CacheWriteThrough, 12 }, + { 48, TRUE, TRUE, CacheWriteProtected, 12 }, + { 48, TRUE, TRUE, CacheWriteCombining, 12 }, +}; + +UINT32 mFixedMtrrsIndex[] = { + MSR_IA32_MTRR_FIX64K_00000, + MSR_IA32_MTRR_FIX16K_80000, + MSR_IA32_MTRR_FIX16K_A0000, + MSR_IA32_MTRR_FIX4K_C0000, + MSR_IA32_MTRR_FIX4K_C8000, + MSR_IA32_MTRR_FIX4K_D0000, + MSR_IA32_MTRR_FIX4K_D8000, + MSR_IA32_MTRR_FIX4K_E0000, + MSR_IA32_MTRR_FIX4K_E8000, + MSR_IA32_MTRR_FIX4K_F0000, + MSR_IA32_MTRR_FIX4K_F8000 +}; +STATIC_ASSERT ( + (ARRAY_SIZE (mFixedMtrrsIndex) == MTRR_NUMBER_OF_FIXED_MTRR), + "gFixedMtrrIndex does NOT contain all the fixed MTRRs!" + ); + +// +// Context structure to be used for most of the test cases. +// +typedef struct { + CONST MTRR_LIB_SYSTEM_PARAMETER *SystemParameter; +} MTRR_LIB_TEST_CONTEXT; + +// +// Context structure to be used for GetFirmwareVariableMtrrCount() test. +// +typedef struct { + UINT32 NumberOfReservedVariableMtrrs; + CONST MTRR_LIB_SYSTEM_PARAMETER *SystemParameter; +} MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT; + +STATIC CHAR8 *mCacheDescription[] = { "UC", "WC", "N/A", "N/A", "WT", "WP", "WB" }; + +/** + Compare the actual memory ranges against expected memory ranges and return PASS when they match. + + @param ExpectedMemoryRanges Expected memory ranges. + @param ExpectedMemoryRangeCount Count of expected memory ranges. + @param ActualRanges Actual memory ranges. + @param ActualRangeCount Count of actual memory ranges. + + @retval UNIT_TEST_PASSED Test passed. + @retval others Test failed. +**/ +UNIT_TEST_STATUS +VerifyMemoryRanges ( + IN MTRR_MEMORY_RANGE *ExpectedMemoryRanges, + IN UINTN ExpectedMemoryRangeCount, + IN MTRR_MEMORY_RANGE *ActualRanges, + IN UINTN ActualRangeCount + ) +{ + UINTN Index; + UT_ASSERT_EQUAL (ExpectedMemoryRangeCount, ActualRangeCount); + for (Index = 0; Index < ExpectedMemoryRangeCount; Index++) { + UT_ASSERT_EQUAL (ExpectedMemoryRanges[Index].BaseAddress, ActualRanges[Index].BaseAddress); + UT_ASSERT_EQUAL (ExpectedMemoryRanges[Index].Length, ActualRanges[Index].Length); + UT_ASSERT_EQUAL (ExpectedMemoryRanges[Index].Type, ActualRanges[Index].Type); + } + + return UNIT_TEST_PASSED; +} + +/** + Dump the memory ranges. + + @param Ranges Memory ranges to dump. + @param RangeCount Count of memory ranges. +**/ +VOID +DumpMemoryRanges ( + MTRR_MEMORY_RANGE *Ranges, + UINTN RangeCount + ) +{ + UINTN Index; + for (Index = 0; Index < RangeCount; Index++) { + UT_LOG_INFO ("\t{ 0x%016llx, 0x%016llx, %a },\n", Ranges[Index].BaseAddress, Ranges[Index].Length, mCacheDescription[Ranges[Index].Type]); + } +} + +/** +**/ + +/** + Generate random count of MTRRs for each cache type. + + @param TotalCount Total MTRR count. + @param UcCount Return count of Uncacheable type. + @param WtCount Return count of Write Through type. + @param WbCount Return count of Write Back type. + @param WpCount Return count of Write Protected type. + @param WcCount Return count of Write Combining type. +**/ +VOID +GenerateRandomMemoryTypeCombination ( + IN UINT32 TotalCount, + OUT UINT32 *UcCount, + OUT UINT32 *WtCount, + OUT UINT32 *WbCount, + OUT UINT32 *WpCount, + OUT UINT32 *WcCount + ) +{ + UINTN Index; + UINT32 TotalMtrrCount; + UINT32 *CountPerType[5]; + + CountPerType[0] = UcCount; + CountPerType[1] = WtCount; + CountPerType[2] = WbCount; + CountPerType[3] = WpCount; + CountPerType[4] = WcCount; + + // + // Initialize the count of each cache type to 0. + // + for (Index = 0; Index < ARRAY_SIZE (CountPerType); Index++) { + *(CountPerType[Index]) = 0; + } + + // + // Pick a random count of MTRRs + // + TotalMtrrCount = Random32 (1, TotalCount); + for (Index = 0; Index < TotalMtrrCount; Index++) { + // + // For each of them, pick a random cache type. + // + (*(CountPerType[Random32 (0, ARRAY_SIZE (CountPerType) - 1)]))++; + } +} + +/** + Unit test of MtrrLib service MtrrSetMemoryAttribute() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrSetMemoryAttributesInMtrrSettings ( + IN UNIT_TEST_CONTEXT Context + ) +{ + CONST MTRR_LIB_SYSTEM_PARAMETER *SystemParameter; + RETURN_STATUS Status; + UINT32 UcCount; + UINT32 WtCount; + UINT32 WbCount; + UINT32 WpCount; + UINT32 WcCount; + + UINT32 MtrrIndex; + UINT8 *Scratch; + UINTN ScratchSize; + MTRR_SETTINGS LocalMtrrs; + + MTRR_MEMORY_RANGE RawMtrrRange[MTRR_NUMBER_OF_VARIABLE_MTRR]; + MTRR_MEMORY_RANGE ExpectedMemoryRanges[MTRR_NUMBER_OF_FIXED_MTRR * sizeof (UINT64) + 2 * MTRR_NUMBER_OF_VARIABLE_MTRR + 1]; + UINT32 ExpectedVariableMtrrUsage; + UINTN ExpectedMemoryRangesCount; + + MTRR_MEMORY_RANGE ActualMemoryRanges[MTRR_NUMBER_OF_FIXED_MTRR * sizeof (UINT64) + 2 * MTRR_NUMBER_OF_VARIABLE_MTRR + 1]; + UINT32 ActualVariableMtrrUsage; + UINTN ActualMemoryRangesCount; + + MTRR_SETTINGS *Mtrrs[2]; + + SystemParameter = (MTRR_LIB_SYSTEM_PARAMETER *) Context; + GenerateRandomMemoryTypeCombination ( + SystemParameter->VariableMtrrCount - PatchPcdGet32 (PcdCpuNumberOfReservedVariableMtrrs), + &UcCount, &WtCount, &WbCount, &WpCount, &WcCount + ); + GenerateValidAndConfigurableMtrrPairs ( + SystemParameter->PhysicalAddressBits, RawMtrrRange, + UcCount, WtCount, WbCount, WpCount, WcCount + ); + + ExpectedVariableMtrrUsage = UcCount + WtCount + WbCount + WpCount + WcCount; + ExpectedMemoryRangesCount = ARRAY_SIZE (ExpectedMemoryRanges); + GetEffectiveMemoryRanges ( + SystemParameter->DefaultCacheType, + SystemParameter->PhysicalAddressBits, + RawMtrrRange, ExpectedVariableMtrrUsage, + ExpectedMemoryRanges, &ExpectedMemoryRangesCount + ); + + UT_LOG_INFO ( + "Total MTRR [%d]: UC=%d, WT=%d, WB=%d, WP=%d, WC=%d\n", + ExpectedVariableMtrrUsage, UcCount, WtCount, WbCount, WpCount, WcCount + ); + UT_LOG_INFO ("--- Expected Memory Ranges [%d] ---\n", ExpectedMemoryRangesCount); + DumpMemoryRanges (ExpectedMemoryRanges, ExpectedMemoryRangesCount); + + // + // Default cache type is always an INPUT + // + ZeroMem (&LocalMtrrs, sizeof (LocalMtrrs)); + LocalMtrrs.MtrrDefType = MtrrGetDefaultMemoryType (); + ScratchSize = SCRATCH_BUFFER_SIZE; + Mtrrs[0] = &LocalMtrrs; + Mtrrs[1] = NULL; + + for (MtrrIndex = 0; MtrrIndex < ARRAY_SIZE (Mtrrs); MtrrIndex++) { + Scratch = calloc (ScratchSize, sizeof (UINT8)); + Status = MtrrSetMemoryAttributesInMtrrSettings (Mtrrs[MtrrIndex], Scratch, &ScratchSize, ExpectedMemoryRanges, ExpectedMemoryRangesCount); + if (Status == RETURN_BUFFER_TOO_SMALL) { + Scratch = realloc (Scratch, ScratchSize); + Status = MtrrSetMemoryAttributesInMtrrSettings (Mtrrs[MtrrIndex], Scratch, &ScratchSize, ExpectedMemoryRanges, ExpectedMemoryRangesCount); + } + UT_ASSERT_STATUS_EQUAL (Status, RETURN_SUCCESS); + + if (Mtrrs[MtrrIndex] == NULL) { + ZeroMem (&LocalMtrrs, sizeof (LocalMtrrs)); + MtrrGetAllMtrrs (&LocalMtrrs); + } + ActualMemoryRangesCount = ARRAY_SIZE (ActualMemoryRanges); + CollectTestResult ( + SystemParameter->DefaultCacheType, SystemParameter->PhysicalAddressBits, SystemParameter->VariableMtrrCount, + &LocalMtrrs, ActualMemoryRanges, &ActualMemoryRangesCount, &ActualVariableMtrrUsage + ); + + UT_LOG_INFO ("--- Actual Memory Ranges [%d] ---\n", ActualMemoryRangesCount); + DumpMemoryRanges (ActualMemoryRanges, ActualMemoryRangesCount); + VerifyMemoryRanges (ExpectedMemoryRanges, ExpectedMemoryRangesCount, ActualMemoryRanges, ActualMemoryRangesCount); + UT_ASSERT_TRUE (ExpectedVariableMtrrUsage >= ActualVariableMtrrUsage); + + ZeroMem (&LocalMtrrs, sizeof (LocalMtrrs)); + } + + free (Scratch); + + return UNIT_TEST_PASSED; +} + +/** + Test routine to check whether invalid base/size can be rejected. + + @param Context Pointer to MTRR_LIB_SYSTEM_PARAMETER. + + @return Test status. +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestInvalidMemoryLayouts ( + IN UNIT_TEST_CONTEXT Context + ) +{ + CONST MTRR_LIB_SYSTEM_PARAMETER *SystemParameter; + MTRR_MEMORY_RANGE Ranges[MTRR_NUMBER_OF_VARIABLE_MTRR * 2 + 1]; + UINTN RangeCount; + UINT64 MaxAddress; + UINT32 Index; + UINT64 BaseAddress; + UINT64 Length; + RETURN_STATUS Status; + UINTN ScratchSize; + + SystemParameter = (MTRR_LIB_SYSTEM_PARAMETER *) Context; + + RangeCount = Random32 (1, ARRAY_SIZE (Ranges)); + MaxAddress = 1ull << SystemParameter->PhysicalAddressBits; + + for (Index = 0; Index < RangeCount; Index++) { + do { + BaseAddress = Random64 (0, MaxAddress); + Length = Random64 (1, MaxAddress - BaseAddress); + } while (((BaseAddress & 0xFFF) == 0) || ((Length & 0xFFF) == 0)); + + Ranges[Index].BaseAddress = BaseAddress; + Ranges[Index].Length = Length; + Ranges[Index].Type = GenerateRandomCacheType (); + + Status = MtrrSetMemoryAttribute ( + Ranges[Index].BaseAddress, Ranges[Index].Length, Ranges[Index].Type + ); + UT_ASSERT_TRUE (RETURN_ERROR (Status)); + } + + ScratchSize = 0; + Status = MtrrSetMemoryAttributesInMtrrSettings (NULL, NULL, &ScratchSize, Ranges, RangeCount); + UT_ASSERT_TRUE (RETURN_ERROR (Status)); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service IsMtrrSupported() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestIsMtrrSupported ( + IN UNIT_TEST_CONTEXT Context + ) +{ + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_LIB_TEST_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + // + // MTRR capability off in CPUID leaf. + // + SystemParameter.MtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + UT_ASSERT_FALSE (IsMtrrSupported ()); + + // + // MTRR capability on in CPUID leaf, but no variable or fixed MTRRs. + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = 0; + SystemParameter.FixedMtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + UT_ASSERT_FALSE (IsMtrrSupported ()); + + // + // MTRR capability on in CPUID leaf, but no variable MTRRs. + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = 0; + SystemParameter.FixedMtrrSupported = TRUE; + InitializeMtrrRegs (&SystemParameter); + UT_ASSERT_FALSE (IsMtrrSupported ()); + + // + // MTRR capability on in CPUID leaf, but no fixed MTRRs. + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = 7; + SystemParameter.FixedMtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + UT_ASSERT_FALSE (IsMtrrSupported ()); + + // + // MTRR capability on in CPUID leaf with both variable and fixed MTRRs. + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = 7; + SystemParameter.FixedMtrrSupported = TRUE; + InitializeMtrrRegs (&SystemParameter); + UT_ASSERT_TRUE (IsMtrrSupported ()); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service GetVariableMtrrCount() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestGetVariableMtrrCount ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT32 Result; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_LIB_TEST_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + // + // If MTRR capability off in CPUID leaf, then the count is always 0. + // + SystemParameter.MtrrSupported = FALSE; + for (SystemParameter.VariableMtrrCount = 1; SystemParameter.VariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR; SystemParameter.VariableMtrrCount++) { + InitializeMtrrRegs (&SystemParameter); + Result = GetVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, 0); + } + + // + // Try all supported variable MTRR counts. + // If variable MTRR count is > MTRR_NUMBER_OF_VARIABLE_MTRR, then an ASSERT() + // is generated. + // + SystemParameter.MtrrSupported = TRUE; + for (SystemParameter.VariableMtrrCount = 1; SystemParameter.VariableMtrrCount <= MTRR_NUMBER_OF_VARIABLE_MTRR; SystemParameter.VariableMtrrCount++) { + InitializeMtrrRegs (&SystemParameter); + Result = GetVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, SystemParameter.VariableMtrrCount); + } + + // + // Expect ASSERT() if variable MTRR count is > MTRR_NUMBER_OF_VARIABLE_MTRR + // + SystemParameter.VariableMtrrCount = MTRR_NUMBER_OF_VARIABLE_MTRR + 1; + InitializeMtrrRegs (&SystemParameter); + UT_EXPECT_ASSERT_FAILURE (GetVariableMtrrCount (), NULL); + + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = MAX_UINT8; + InitializeMtrrRegs (&SystemParameter); + UT_EXPECT_ASSERT_FAILURE (GetVariableMtrrCount (), NULL); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service GetFirmwareVariableMtrrCount() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestGetFirmwareVariableMtrrCount ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT32 Result; + UINT32 ReservedMtrrs; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + + InitializeMtrrRegs (&SystemParameter); + // + // Positive test cases for VCNT = 10 and Reserved PCD in range 0..10 + // + for (ReservedMtrrs = 0; ReservedMtrrs <= SystemParameter.VariableMtrrCount; ReservedMtrrs++) { + PatchPcdSet32 (PcdCpuNumberOfReservedVariableMtrrs, ReservedMtrrs); + Result = GetFirmwareVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, SystemParameter.VariableMtrrCount - ReservedMtrrs); + } + + // + // Negative test cases when Reserved PCD is larger than VCNT + // + for (ReservedMtrrs = SystemParameter.VariableMtrrCount + 1; ReservedMtrrs <= 255; ReservedMtrrs++) { + PatchPcdSet32 (PcdCpuNumberOfReservedVariableMtrrs, ReservedMtrrs); + Result = GetFirmwareVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, 0); + } + + // + // Negative test cases when Reserved PCD is larger than VCNT + // + PatchPcdSet32 (PcdCpuNumberOfReservedVariableMtrrs, MAX_UINT32); + Result = GetFirmwareVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, 0); + + // + // Negative test case when MTRRs are not supported + // + SystemParameter.MtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + PatchPcdSet32 (PcdCpuNumberOfReservedVariableMtrrs, 2); + Result = GetFirmwareVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, 0); + + // + // Negative test case when Fixed MTRRs are not supported + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.FixedMtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + PatchPcdSet32 (PcdCpuNumberOfReservedVariableMtrrs, 2); + Result = GetFirmwareVariableMtrrCount (); + UT_ASSERT_EQUAL (Result, 0); + + // + // Expect ASSERT() if variable MTRR count is > MTRR_NUMBER_OF_VARIABLE_MTRR + // + SystemParameter.FixedMtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = MTRR_NUMBER_OF_VARIABLE_MTRR + 1; + InitializeMtrrRegs (&SystemParameter); + UT_EXPECT_ASSERT_FAILURE (GetFirmwareVariableMtrrCount (), NULL); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrGetMemoryAttribute() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrGetMemoryAttribute ( + IN UNIT_TEST_CONTEXT Context + ) +{ + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrGetFixedMtrr() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrGetFixedMtrr ( + IN UNIT_TEST_CONTEXT Context + ) +{ + MTRR_FIXED_SETTINGS *Result; + MTRR_FIXED_SETTINGS ExpectedFixedSettings; + MTRR_FIXED_SETTINGS FixedSettings; + UINTN Index; + UINTN MsrIndex; + UINTN ByteIndex; + UINT64 MsrValue; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_LIB_TEST_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + InitializeMtrrRegs (&SystemParameter); + // + // Set random cache type to different ranges under 1MB and make sure + // the fixed MTRR settings are expected. + // Try 100 times. + // + for (Index = 0; Index < 100; Index++) { + for (MsrIndex = 0; MsrIndex < ARRAY_SIZE (mFixedMtrrsIndex); MsrIndex++) { + MsrValue = 0; + for (ByteIndex = 0; ByteIndex < sizeof (UINT64); ByteIndex++) { + MsrValue = MsrValue | LShiftU64 (GenerateRandomCacheType (), ByteIndex * 8); + } + ExpectedFixedSettings.Mtrr[MsrIndex] = MsrValue; + AsmWriteMsr64 (mFixedMtrrsIndex[MsrIndex], MsrValue); + } + + Result = MtrrGetFixedMtrr (&FixedSettings); + UT_ASSERT_EQUAL (Result, &FixedSettings); + UT_ASSERT_MEM_EQUAL (&FixedSettings, &ExpectedFixedSettings, sizeof (FixedSettings)); + } + + // + // Negative test case when MTRRs are not supported + // + SystemParameter.MtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + + ZeroMem (&FixedSettings, sizeof (FixedSettings)); + ZeroMem (&ExpectedFixedSettings, sizeof (ExpectedFixedSettings)); + Result = MtrrGetFixedMtrr (&FixedSettings); + UT_ASSERT_EQUAL (Result, &FixedSettings); + UT_ASSERT_MEM_EQUAL (&ExpectedFixedSettings, &FixedSettings, sizeof (ExpectedFixedSettings)); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrGetAllMtrrs() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrGetAllMtrrs ( + IN UNIT_TEST_CONTEXT Context + ) +{ + MTRR_SETTINGS *Result; + MTRR_SETTINGS Mtrrs; + MTRR_SETTINGS ExpectedMtrrs; + MTRR_VARIABLE_SETTING VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR]; + UINT32 Index; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_LIB_TEST_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + InitializeMtrrRegs (&SystemParameter); + + for (Index = 0; Index < SystemParameter.VariableMtrrCount; Index++) { + GenerateRandomMtrrPair (SystemParameter.PhysicalAddressBits, GenerateRandomCacheType (), &VariableMtrr[Index], NULL); + AsmWriteMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1), VariableMtrr[Index].Base); + AsmWriteMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1), VariableMtrr[Index].Mask); + } + Result = MtrrGetAllMtrrs (&Mtrrs); + UT_ASSERT_EQUAL (Result, &Mtrrs); + UT_ASSERT_MEM_EQUAL (Mtrrs.Variables.Mtrr, VariableMtrr, sizeof (MTRR_VARIABLE_SETTING) * SystemParameter.VariableMtrrCount); + + // + // Negative test case when MTRRs are not supported + // + ZeroMem (&ExpectedMtrrs, sizeof (ExpectedMtrrs)); + ZeroMem (&Mtrrs, sizeof (Mtrrs)); + + SystemParameter.MtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + Result = MtrrGetAllMtrrs (&Mtrrs); + UT_ASSERT_EQUAL (Result, &Mtrrs); + UT_ASSERT_MEM_EQUAL (&ExpectedMtrrs, &Mtrrs, sizeof (ExpectedMtrrs)); + + // + // Expect ASSERT() if variable MTRR count is > MTRR_NUMBER_OF_VARIABLE_MTRR + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = MTRR_NUMBER_OF_VARIABLE_MTRR + 1; + InitializeMtrrRegs (&SystemParameter); + UT_EXPECT_ASSERT_FAILURE (MtrrGetAllMtrrs (&Mtrrs), NULL); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrSetAllMtrrs() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrSetAllMtrrs ( + IN UNIT_TEST_CONTEXT Context + ) +{ + MTRR_SETTINGS *Result; + MTRR_SETTINGS Mtrrs; + UINT32 Index; + MSR_IA32_MTRR_DEF_TYPE_REGISTER Default; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_LIB_TEST_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + InitializeMtrrRegs (&SystemParameter); + + Default.Uint64 = 0; + Default.Bits.E = 1; + Default.Bits.FE = 1; + Default.Bits.Type = GenerateRandomCacheType (); + + ZeroMem (&Mtrrs, sizeof (Mtrrs)); + Mtrrs.MtrrDefType = Default.Uint64; + for (Index = 0; Index < SystemParameter.VariableMtrrCount; Index++) { + GenerateRandomMtrrPair (SystemParameter.PhysicalAddressBits, GenerateRandomCacheType (), &Mtrrs.Variables.Mtrr[Index], NULL); + } + Result = MtrrSetAllMtrrs (&Mtrrs); + UT_ASSERT_EQUAL (Result, &Mtrrs); + + UT_ASSERT_EQUAL (AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE), Mtrrs.MtrrDefType); + for (Index = 0; Index < SystemParameter.VariableMtrrCount; Index++) { + UT_ASSERT_EQUAL (AsmReadMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1)), Mtrrs.Variables.Mtrr[Index].Base); + UT_ASSERT_EQUAL (AsmReadMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1)), Mtrrs.Variables.Mtrr[Index].Mask); + } + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrGetMemoryAttributeInVariableMtrr() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrGetMemoryAttributeInVariableMtrr ( + IN UNIT_TEST_CONTEXT Context + ) +{ + MTRR_LIB_TEST_CONTEXT *LocalContext; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + UINT32 Result; + MTRR_VARIABLE_SETTING VariableSetting[MTRR_NUMBER_OF_VARIABLE_MTRR]; + VARIABLE_MTRR VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR]; + UINT64 ValidMtrrBitsMask; + UINT64 ValidMtrrAddressMask; + UINT32 Index; + MSR_IA32_MTRR_PHYSBASE_REGISTER Base; + MSR_IA32_MTRR_PHYSMASK_REGISTER Mask; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + + InitializeMtrrRegs (&SystemParameter); + + ValidMtrrBitsMask = (1ull << SystemParameter.PhysicalAddressBits) - 1; + ValidMtrrAddressMask = ValidMtrrBitsMask & 0xfffffffffffff000ULL; + + for (Index = 0; Index < SystemParameter.VariableMtrrCount; Index++) { + GenerateRandomMtrrPair (SystemParameter.PhysicalAddressBits, GenerateRandomCacheType (), &VariableSetting[Index], NULL); + AsmWriteMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1), VariableSetting[Index].Base); + AsmWriteMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1), VariableSetting[Index].Mask); + } + Result = MtrrGetMemoryAttributeInVariableMtrr (ValidMtrrBitsMask, ValidMtrrAddressMask, VariableMtrr); + UT_ASSERT_EQUAL (Result, SystemParameter.VariableMtrrCount); + + for (Index = 0; Index < SystemParameter.VariableMtrrCount; Index++) { + Base.Uint64 = VariableMtrr[Index].BaseAddress; + Base.Bits.Type = (UINT32) VariableMtrr[Index].Type; + UT_ASSERT_EQUAL (Base.Uint64, VariableSetting[Index].Base); + + Mask.Uint64 = ~(VariableMtrr[Index].Length - 1) & ValidMtrrBitsMask; + Mask.Bits.V = 1; + UT_ASSERT_EQUAL (Mask.Uint64, VariableSetting[Index].Mask); + } + + // + // Negative test case when MTRRs are not supported + // + SystemParameter.MtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + Result = MtrrGetMemoryAttributeInVariableMtrr (ValidMtrrBitsMask, ValidMtrrAddressMask, VariableMtrr); + UT_ASSERT_EQUAL (Result, 0); + + // + // Expect ASSERT() if variable MTRR count is > MTRR_NUMBER_OF_VARIABLE_MTRR + // + SystemParameter.MtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = MTRR_NUMBER_OF_VARIABLE_MTRR + 1; + InitializeMtrrRegs (&SystemParameter); + UT_EXPECT_ASSERT_FAILURE (MtrrGetMemoryAttributeInVariableMtrr (ValidMtrrBitsMask, ValidMtrrAddressMask, VariableMtrr), NULL); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrDebugPrintAllMtrrs() + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrDebugPrintAllMtrrs ( + IN UNIT_TEST_CONTEXT Context + ) +{ + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrGetDefaultMemoryType(). + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrGetDefaultMemoryType ( + IN UNIT_TEST_CONTEXT Context + ) +{ + MTRR_LIB_TEST_CONTEXT *LocalContext; + UINTN Index; + MTRR_MEMORY_CACHE_TYPE Result; + MTRR_LIB_SYSTEM_PARAMETER SystemParameter; + MTRR_MEMORY_CACHE_TYPE CacheType[5]; + + CacheType[0] = CacheUncacheable; + CacheType[1] = CacheWriteCombining; + CacheType[2] = CacheWriteThrough; + CacheType[3] = CacheWriteProtected; + CacheType[4] = CacheWriteBack; + + LocalContext = (MTRR_LIB_TEST_CONTEXT *) Context; + + CopyMem (&SystemParameter, LocalContext->SystemParameter, sizeof (SystemParameter)); + // + // If MTRRs are supported, then always return the cache type in the MSR + // MSR_IA32_MTRR_DEF_TYPE + // + for (Index = 0; Index < ARRAY_SIZE (CacheType); Index++) { + SystemParameter.DefaultCacheType = CacheType[Index]; + InitializeMtrrRegs (&SystemParameter); + Result = MtrrGetDefaultMemoryType (); + UT_ASSERT_EQUAL (Result, SystemParameter.DefaultCacheType); + } + + // + // If MTRRs are not supported, then always return CacheUncacheable + // + SystemParameter.MtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + Result = MtrrGetDefaultMemoryType (); + UT_ASSERT_EQUAL (Result, CacheUncacheable); + + SystemParameter.MtrrSupported = TRUE; + SystemParameter.FixedMtrrSupported = FALSE; + InitializeMtrrRegs (&SystemParameter); + Result = MtrrGetDefaultMemoryType (); + UT_ASSERT_EQUAL (Result, CacheUncacheable); + + SystemParameter.MtrrSupported = TRUE; + SystemParameter.FixedMtrrSupported = TRUE; + SystemParameter.VariableMtrrCount = 0; + InitializeMtrrRegs (&SystemParameter); + Result = MtrrGetDefaultMemoryType (); + UT_ASSERT_EQUAL (Result, CacheUncacheable); + + return UNIT_TEST_PASSED; +} + +/** + Unit test of MtrrLib service MtrrSetMemoryAttributeInMtrrSettings(). + + @param[in] Context Ignored + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. + +**/ +UNIT_TEST_STATUS +EFIAPI +UnitTestMtrrSetMemoryAttributeInMtrrSettings ( + IN UNIT_TEST_CONTEXT Context + ) +{ + CONST MTRR_LIB_SYSTEM_PARAMETER *SystemParameter; + RETURN_STATUS Status; + UINT32 UcCount; + UINT32 WtCount; + UINT32 WbCount; + UINT32 WpCount; + UINT32 WcCount; + + UINTN MtrrIndex; + UINTN Index; + MTRR_SETTINGS LocalMtrrs; + + MTRR_MEMORY_RANGE RawMtrrRange[MTRR_NUMBER_OF_VARIABLE_MTRR]; + MTRR_MEMORY_RANGE ExpectedMemoryRanges[MTRR_NUMBER_OF_FIXED_MTRR * sizeof (UINT64) + 2 * MTRR_NUMBER_OF_VARIABLE_MTRR + 1]; + UINT32 ExpectedVariableMtrrUsage; + UINTN ExpectedMemoryRangesCount; + + MTRR_MEMORY_RANGE ActualMemoryRanges[MTRR_NUMBER_OF_FIXED_MTRR * sizeof (UINT64) + 2 * MTRR_NUMBER_OF_VARIABLE_MTRR + 1]; + UINT32 ActualVariableMtrrUsage; + UINTN ActualMemoryRangesCount; + + MTRR_SETTINGS *Mtrrs[2]; + + SystemParameter = (MTRR_LIB_SYSTEM_PARAMETER *) Context; + GenerateRandomMemoryTypeCombination ( + SystemParameter->VariableMtrrCount - PatchPcdGet32 (PcdCpuNumberOfReservedVariableMtrrs), + &UcCount, &WtCount, &WbCount, &WpCount, &WcCount + ); + GenerateValidAndConfigurableMtrrPairs ( + SystemParameter->PhysicalAddressBits, RawMtrrRange, + UcCount, WtCount, WbCount, WpCount, WcCount + ); + + ExpectedVariableMtrrUsage = UcCount + WtCount + WbCount + WpCount + WcCount; + ExpectedMemoryRangesCount = ARRAY_SIZE (ExpectedMemoryRanges); + GetEffectiveMemoryRanges ( + SystemParameter->DefaultCacheType, + SystemParameter->PhysicalAddressBits, + RawMtrrRange, ExpectedVariableMtrrUsage, + ExpectedMemoryRanges, &ExpectedMemoryRangesCount + ); + + UT_LOG_INFO ("--- Expected Memory Ranges [%d] ---\n", ExpectedMemoryRangesCount); + DumpMemoryRanges (ExpectedMemoryRanges, ExpectedMemoryRangesCount); + // + // Default cache type is always an INPUT + // + ZeroMem (&LocalMtrrs, sizeof (LocalMtrrs)); + LocalMtrrs.MtrrDefType = MtrrGetDefaultMemoryType (); + Mtrrs[0] = &LocalMtrrs; + Mtrrs[1] = NULL; + + for (MtrrIndex = 0; MtrrIndex < ARRAY_SIZE (Mtrrs); MtrrIndex++) { + for (Index = 0; Index < ExpectedMemoryRangesCount; Index++) { + Status = MtrrSetMemoryAttributeInMtrrSettings ( + Mtrrs[MtrrIndex], + ExpectedMemoryRanges[Index].BaseAddress, + ExpectedMemoryRanges[Index].Length, + ExpectedMemoryRanges[Index].Type + ); + UT_ASSERT_TRUE (Status == RETURN_SUCCESS || Status == RETURN_OUT_OF_RESOURCES || Status == RETURN_BUFFER_TOO_SMALL); + if (Status == RETURN_OUT_OF_RESOURCES || Status == RETURN_BUFFER_TOO_SMALL) { + return UNIT_TEST_SKIPPED; + } + } + + if (Mtrrs[MtrrIndex] == NULL) { + ZeroMem (&LocalMtrrs, sizeof (LocalMtrrs)); + MtrrGetAllMtrrs (&LocalMtrrs); + } + ActualMemoryRangesCount = ARRAY_SIZE (ActualMemoryRanges); + CollectTestResult ( + SystemParameter->DefaultCacheType, SystemParameter->PhysicalAddressBits, SystemParameter->VariableMtrrCount, + &LocalMtrrs, ActualMemoryRanges, &ActualMemoryRangesCount, &ActualVariableMtrrUsage + ); + UT_LOG_INFO ("--- Actual Memory Ranges [%d] ---\n", ActualMemoryRangesCount); + DumpMemoryRanges (ActualMemoryRanges, ActualMemoryRangesCount); + VerifyMemoryRanges (ExpectedMemoryRanges, ExpectedMemoryRangesCount, ActualMemoryRanges, ActualMemoryRangesCount); + UT_ASSERT_TRUE (ExpectedVariableMtrrUsage >= ActualVariableMtrrUsage); + + ZeroMem (&LocalMtrrs, sizeof (LocalMtrrs)); + } + + return UNIT_TEST_PASSED; +} + + +/** + Prep routine for UnitTestGetFirmwareVariableMtrrCount(). + + @param Context Point to a UINT32 data to save the PcdCpuNumberOfReservedVariableMtrrs. +**/ +UNIT_TEST_STATUS +EFIAPI +SavePcdValue ( + UNIT_TEST_CONTEXT Context + ) +{ + MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT *) Context; + LocalContext->NumberOfReservedVariableMtrrs = PatchPcdGet32 (PcdCpuNumberOfReservedVariableMtrrs); + return UNIT_TEST_PASSED; +} + +/** + Clean up routine for UnitTestGetFirmwareVariableMtrrCount(). + + @param Context Point to a UINT32 data to save the PcdCpuNumberOfReservedVariableMtrrs. +**/ +VOID +EFIAPI +RestorePcdValue ( + UNIT_TEST_CONTEXT Context + ) +{ + MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT *LocalContext; + + LocalContext = (MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT *) Context; + PatchPcdSet32 (PcdCpuNumberOfReservedVariableMtrrs, LocalContext->NumberOfReservedVariableMtrrs); +} + +/** + Initialize the unit test framework, suite, and unit tests for the + ResetSystemLib and run the ResetSystemLib unit test. + + @param Iteration Iteration of testing MtrrSetMemoryAttributeInMtrrSettings + and MtrrSetMemoryAttributesInMtrrSettings using random inputs. + + @retval EFI_SUCCESS All test cases were dispatched. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to + initialize the unit tests. +**/ +STATIC +EFI_STATUS +EFIAPI +UnitTestingEntry ( + UINTN Iteration + ) +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework; + UNIT_TEST_SUITE_HANDLE MtrrApiTests; + UINTN Index; + UINTN SystemIndex; + MTRR_LIB_TEST_CONTEXT Context; + MTRR_LIB_GET_FIRMWARE_VARIABLE_MTRR_COUNT_CONTEXT GetFirmwareVariableMtrrCountContext; + + Context.SystemParameter = &mDefaultSystemParameter; + GetFirmwareVariableMtrrCountContext.SystemParameter = &mDefaultSystemParameter; + Framework = NULL; + + // + // Setup the test framework for running the tests. + // + Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); + goto EXIT; + } + + // + // --------------Suite-----------Description--------------Name----------Function--------Pre---Post-------------------Context----------- + // + + // + // Populate the MtrrLib API Unit Test Suite. + // + Status = CreateUnitTestSuite (&MtrrApiTests, Framework, "MtrrLib API Tests", "MtrrLib.MtrrLib", NULL, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for MtrrLib API Tests\n")); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + AddTestCase (MtrrApiTests, "Test IsMtrrSupported", "MtrrSupported", UnitTestIsMtrrSupported, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test GetVariableMtrrCount", "GetVariableMtrrCount", UnitTestGetVariableMtrrCount, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test GetFirmwareVariableMtrrCount", "GetFirmwareVariableMtrrCount", UnitTestGetFirmwareVariableMtrrCount, SavePcdValue, RestorePcdValue, &GetFirmwareVariableMtrrCountContext); + AddTestCase (MtrrApiTests, "Test MtrrGetMemoryAttribute", "MtrrGetMemoryAttribute", UnitTestMtrrGetMemoryAttribute, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test MtrrGetFixedMtrr", "MtrrGetFixedMtrr", UnitTestMtrrGetFixedMtrr, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test MtrrGetAllMtrrs", "MtrrGetAllMtrrs", UnitTestMtrrGetAllMtrrs, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test MtrrSetAllMtrrs", "MtrrSetAllMtrrs", UnitTestMtrrSetAllMtrrs, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test MtrrGetMemoryAttributeInVariableMtrr", "MtrrGetMemoryAttributeInVariableMtrr", UnitTestMtrrGetMemoryAttributeInVariableMtrr, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test MtrrDebugPrintAllMtrrs", "MtrrDebugPrintAllMtrrs", UnitTestMtrrDebugPrintAllMtrrs, NULL, NULL, &Context); + AddTestCase (MtrrApiTests, "Test MtrrGetDefaultMemoryType", "MtrrGetDefaultMemoryType", UnitTestMtrrGetDefaultMemoryType, NULL, NULL, &Context); + + for (SystemIndex = 0; SystemIndex < ARRAY_SIZE (mSystemParameters); SystemIndex++) { + for (Index = 0; Index < Iteration; Index++) { + AddTestCase (MtrrApiTests, "Test InvalidMemoryLayouts", "InvalidMemoryLayouts", UnitTestInvalidMemoryLayouts, InitializeSystem, NULL, &mSystemParameters[SystemIndex]); + AddTestCase (MtrrApiTests, "Test MtrrSetMemoryAttributeInMtrrSettings", "MtrrSetMemoryAttributeInMtrrSettings", UnitTestMtrrSetMemoryAttributeInMtrrSettings, InitializeSystem, NULL, &mSystemParameters[SystemIndex]); + AddTestCase (MtrrApiTests, "Test MtrrSetMemoryAttributesInMtrrSettings", "MtrrSetMemoryAttributesInMtrrSettings", UnitTestMtrrSetMemoryAttributesInMtrrSettings, InitializeSystem, NULL, &mSystemParameters[SystemIndex]); + } + } + // + // Execute the tests. + // + Status = RunAllTestSuites (Framework); + +EXIT: + if (Framework != NULL) { + FreeUnitTestFramework (Framework); + } + + return Status; +} + +/** + Standard POSIX C entry point for host based unit test execution. + + @param Argc Number of arguments. + @param Argv Array of arguments. + + @return Test application exit code. +**/ +INT32 +main ( + INT32 Argc, + CHAR8 *Argv[] + ) +{ + UINTN Count; + + DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION)); + srand ((unsigned int) time (NULL)); + + // + // MtrrLibUnitTest generate-random-numbers <path to MtrrLib/UnitTest/RandomNumber.c> <random-number count> + // + if ((Argc == 4) && (AsciiStriCmp ("generate-random-numbers", Argv[1]) == 0)) { + Count = atoi (Argv[3]); + DEBUG ((DEBUG_INFO, "Generate %d random numbers to %a.\n", Count, Argv[2])); + GenerateRandomNumbers (Argv[2], Count); + return 0; + } + + // + // MtrrLibUnitTest [<iterations>] + // <iterations> [fixed|random] + // Default <iterations> is 10. + // Default uses fixed inputs. + // + Count = 10; + mRandomInput = FALSE; + if ((Argc == 2) || (Argc == 3)) { + Count = atoi (Argv[1]); + if (Argc == 3) { + if (AsciiStriCmp ("fixed", Argv[2]) == 0) { + mRandomInput = FALSE; + } else if (AsciiStriCmp ("random", Argv[2]) == 0) { + mRandomInput = TRUE; + } + } + } + + DEBUG ((DEBUG_INFO, "Iterations = %d\n", Count)); + DEBUG ((DEBUG_INFO, "Input = %a\n", mRandomInput ? "random" : "fixed")); + + return UnitTestingEntry (Count); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTest.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTest.h new file mode 100644 index 00000000..ffde1681 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTest.h @@ -0,0 +1,195 @@ +/** @file + + Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _MTRR_SUPPORT_H_ +#define _MTRR_SUPPORT_H_ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <time.h> + +#include <Uefi.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/UnitTestLib.h> +#include <Library/MtrrLib.h> +#include <Library/UnitTestHostBaseLib.h> + +#include <Register/ArchitecturalMsr.h> +#include <Register/Cpuid.h> +#include <Register/Msr.h> + +#define UNIT_TEST_APP_NAME "MtrrLib Unit Tests" +#define UNIT_TEST_APP_VERSION "1.0" + +#define SCRATCH_BUFFER_SIZE SIZE_16KB + +typedef struct { + UINT8 PhysicalAddressBits; + BOOLEAN MtrrSupported; + BOOLEAN FixedMtrrSupported; + MTRR_MEMORY_CACHE_TYPE DefaultCacheType; + UINT32 VariableMtrrCount; +} MTRR_LIB_SYSTEM_PARAMETER; + +extern UINT32 mFixedMtrrsIndex[]; +extern BOOLEAN mRandomInput; + +/** + Initialize the MTRR registers. + + @param SystemParameter System parameter that controls the MTRR registers initialization. +**/ +UNIT_TEST_STATUS +EFIAPI +InitializeMtrrRegs ( + IN MTRR_LIB_SYSTEM_PARAMETER *SystemParameter + ); + +/** + Initialize the MTRR registers. + + @param Context System parameter that controls the MTRR registers initialization. +**/ +UNIT_TEST_STATUS +EFIAPI +InitializeSystem ( + IN UNIT_TEST_CONTEXT Context + ); + +/** + Return a random memory cache type. +**/ +MTRR_MEMORY_CACHE_TYPE +GenerateRandomCacheType ( + VOID + ); + +/** + Generate random MTRRs. + + @param PhysicalAddressBits Physical address bits. + @param RawMemoryRanges Return the randomly generated MTRRs. + @param UcCount Count of Uncacheable MTRRs. + @param WtCount Count of Write Through MTRRs. + @param WbCount Count of Write Back MTRRs. + @param WpCount Count of Write Protected MTRRs. + @param WcCount Count of Write Combining MTRRs. +**/ +VOID +GenerateValidAndConfigurableMtrrPairs ( + IN UINT32 PhysicalAddressBits, + IN OUT MTRR_MEMORY_RANGE *RawMemoryRanges, + IN UINT32 UcCount, + IN UINT32 WtCount, + IN UINT32 WbCount, + IN UINT32 WpCount, + IN UINT32 WcCount + ); + +/** + Convert the MTRR BASE/MASK array to memory ranges. + + @param DefaultType Default memory type. + @param PhysicalAddressBits Physical address bits. + @param RawMemoryRanges Raw memory ranges. + @param RawMemoryRangeCount Count of raw memory ranges. + @param MemoryRanges Memory ranges. + @param MemoryRangeCount Count of memory ranges. +**/ +VOID +GetEffectiveMemoryRanges ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT32 PhysicalAddressBits, + IN MTRR_MEMORY_RANGE *RawMemoryRanges, + IN UINT32 RawMemoryRangeCount, + OUT MTRR_MEMORY_RANGE *MemoryRanges, + OUT UINTN *MemoryRangeCount + ); + +/** + Generate random MTRR BASE/MASK for a specified type. + + @param PhysicalAddressBits Physical address bits. + @param CacheType Cache type. + @param MtrrPair Return the random MTRR. + @param MtrrMemoryRange Return the random memory range. +**/ +VOID +GenerateRandomMtrrPair ( + IN UINT32 PhysicalAddressBits, + IN MTRR_MEMORY_CACHE_TYPE CacheType, + OUT MTRR_VARIABLE_SETTING *MtrrPair, OPTIONAL + OUT MTRR_MEMORY_RANGE *MtrrMemoryRange OPTIONAL + ); + +/** + Collect the test result. + + @param DefaultType Default memory type. + @param PhysicalAddressBits Physical address bits. + @param VariableMtrrCount Count of variable MTRRs. + @param Mtrrs MTRR settings to collect from. + @param Ranges Return the memory ranges. + @param RangeCount Return the count of memory ranges. + @param MtrrCount Return the count of variable MTRRs being used. +**/ +VOID +CollectTestResult ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT32 PhysicalAddressBits, + IN UINT32 VariableMtrrCount, + IN MTRR_SETTINGS *Mtrrs, + OUT MTRR_MEMORY_RANGE *Ranges, + IN OUT UINTN *RangeCount, + OUT UINT32 *MtrrCount + ); + +/** + Return a 64bit random number. + + @param Start Start of the random number range. + @param Limit Limit of the random number range. + @return 64bit random number +**/ +UINT64 +Random64 ( + UINT64 Start, + UINT64 Limit + ); + +/** + Return a 32bit random number. + + @param Start Start of the random number range. + @param Limit Limit of the random number range. + @return 32bit random number +**/ +UINT32 +Random32 ( + UINT32 Start, + UINT32 Limit + ); + +/** + Generate Count random numbers in FilePath. + + @param FilePath The file path to put the generated random numbers. + @param Count Count of random numbers. +**/ +VOID +GenerateRandomNumbers ( + CHAR8 *FilePath, + UINTN Count + ); +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTestHost.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTestHost.inf new file mode 100644 index 00000000..c3ee21eb --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTestHost.inf @@ -0,0 +1,43 @@ +## @file +# Unit tests of the MtrrLib instance of the MtrrLib class +# +# Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = MtrrLibUnitTestHost + FILE_GUID = A1542D84-B64D-4847-885E-0509084376AB + MODULE_TYPE = HOST_APPLICATION + VERSION_STRING = 1.0 + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + MtrrLibUnitTest.c + MtrrLibUnitTest.h + Support.c + RandomNumber.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MtrrLib + UnitTestLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuNumberOfReservedVariableMtrrs ## SOMETIMES_CONSUMES + +[BuildOptions] + MSFT:*_*_*_CC_FLAGS = -D _CRT_SECURE_NO_WARNINGS diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/RandomNumber.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/RandomNumber.c new file mode 100644 index 00000000..e75859f7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/RandomNumber.c @@ -0,0 +1,5009 @@ +/** @file + Pre-generated random number used by MtrrLib test. + + Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +UINTN mNumberCount = 50000; +UINTN mNumbers[] = { + 7791, 9427, 6867, 23807, 10984, 16661, 1863, 16690, 14810, 14550, + 11382, 15755, 19806, 32737, 209, 14661, 4112, 13680, 2425, 15502, + 25268, 2882, 29953, 12346, 27936, 8942, 21365, 17849, 13910, 21879, + 3995, 6982, 2032, 6432, 3448, 28770, 8385, 31106, 4630, 32385, + 31417, 21979, 22661, 18947, 10249, 22953, 30752, 32613, 2952, 25464, + 17, 12473, 28757, 19960, 27672, 1875, 17598, 20306, 22152, 15814, + 31841, 25276, 2962, 30223, 23953, 28748, 25029, 16399, 15868, 9993, + 31702, 25663, 29322, 15688, 22429, 18079, 30344, 2870, 20065, 30191, + 3589, 13233, 27221, 15138, 26492, 20316, 13666, 15632, 180, 19954, + 25895, 3179, 21221, 474, 2111, 11252, 2588, 2556, 6338, 26862, + 19424, 3540, 6158, 6099, 20758, 32708, 6087, 2375, 22306, 22959, + 21532, 19274, 11343, 1987, 17228, 2737, 23409, 30102, 15977, 10455, + 15049, 29045, 10077, 22928, 25128, 23033, 12330, 23902, 19543, 18906, + 10102, 11240, 10167, 13832, 28230, 8871, 6693, 12792, 31498, 3043, + 31776, 20028, 3546, 4574, 20270, 17751, 31231, 29263, 29563, 27343, + 10421, 1553, 24772, 4228, 14639, 28957, 26227, 22079, 25052, 3766, + 14092, 6030, 30441, 28131, 17095, 27600, 14855, 29218, 9599, 27788, + 22382, 27739, 11210, 13831, 13345, 16403, 13162, 22037, 29044, 26850, + 27364, 7471, 18892, 29735, 13422, 1478, 10919, 17146, 12302, 9687, + 12252, 10338, 12545, 24256, 25635, 14280, 8794, 16210, 27351, 22444, + 7915, 19495, 30459, 27799, 16488, 8757, 13180, 12369, 27082, 10148, + 181, 2387, 4338, 16093, 11064, 30335, 19343, 12260, 32716, 25359, + 9025, 15335, 24754, 30412, 29951, 3863, 16429, 13952, 24502, 1206, + 9281, 2221, 1586, 29041, 6074, 29311, 10306, 26609, 11376, 8448, + 22295, 11181, 29386, 4747, 18670, 17922, 2689, 29827, 4079, 29061, + 12454, 2372, 14420, 20376, 28391, 10842, 4557, 7227, 20494, 15090, + 10275, 21541, 15818, 7821, 12060, 276, 27570, 2044, 26016, 8289, + 29575, 13448, 13337, 22487, 1721, 26481, 2592, 27845, 22829, 14746, + 16213, 12626, 10558, 4482, 31630, 23569, 2306, 22390, 27189, 32213, + 19662, 14853, 15595, 2503, 17148, 5813, 16753, 9141, 6379, 23284, + 3785, 10413, 4424, 29009, 3480, 9675, 28556, 3820, 24527, 12623, + 15558, 29995, 27530, 23026, 18843, 31663, 3385, 8315, 4491, 30343, + 13565, 12173, 25462, 19619, 29525, 17859, 29534, 21131, 14585, 1535, + 14702, 15697, 19974, 8702, 16955, 16835, 17500, 4912, 21579, 13286, + 26948, 21959, 3796, 15008, 31319, 23054, 26053, 27573, 8603, 8189, + 7946, 25589, 31982, 12554, 13580, 12211, 13480, 27713, 17016, 6008, + 25980, 22679, 23362, 9465, 24991, 29052, 30441, 29767, 3372, 8623, + 20476, 9542, 22984, 24662, 23773, 352, 9602, 9294, 17861, 10618, + 252, 8640, 31752, 24615, 8400, 1525, 22171, 17436, 32301, 9579, + 22483, 3813, 27329, 20520, 111, 11222, 7132, 16462, 21465, 15171, + 28882, 22256, 29097, 22004, 30881, 6948, 26731, 9427, 31443, 12121, + 9634, 8424, 1958, 12523, 9997, 3975, 21355, 13813, 20776, 3784, + 24876, 23911, 3830, 17976, 16529, 9560, 7934, 4046, 25348, 31645, + 12691, 11553, 19476, 15514, 15449, 23767, 8471, 5208, 9989, 25697, + 23572, 30168, 13668, 25062, 9696, 7335, 21151, 31356, 7046, 19635, + 8637, 14494, 748, 17501, 30569, 11995, 12980, 5327, 8438, 13003, + 12641, 21648, 7328, 17479, 20411, 24661, 10005, 29913, 17012, 7707, + 16948, 25458, 32523, 30322, 6420, 8283, 24561, 30748, 25928, 3438, + 21483, 22998, 14689, 3718, 7351, 20086, 21685, 7732, 24818, 24374, + 25695, 6836, 5433, 16464, 21895, 24351, 143, 21451, 7396, 31969, + 24574, 20116, 6014, 10606, 28033, 13264, 26809, 13361, 20002, 6769, + 31119, 6341, 11104, 28935, 32029, 360, 31699, 13273, 7777, 19885, + 16147, 18741, 15170, 29041, 12501, 30724, 20688, 16399, 14167, 11425, + 31950, 30680, 4351, 32748, 14446, 8637, 1343, 13466, 12379, 6479, + 7355, 7887, 24060, 20714, 9390, 10734, 17912, 9772, 393, 28848, + 32757, 23212, 5342, 7027, 27558, 1257, 14973, 30933, 14084, 3457, + 8413, 1154, 6619, 3236, 189, 20554, 27273, 7111, 30730, 887, + 28674, 28573, 3076, 25693, 5400, 20035, 5789, 8242, 24826, 13755, + 8993, 17696, 22778, 18075, 32284, 8857, 12179, 18182, 31865, 1003, + 25770, 20956, 15847, 30996, 15124, 1582, 28289, 8663, 10073, 19189, + 19373, 12047, 7095, 31491, 28740, 3652, 21866, 3385, 11252, 29564, + 3883, 3323, 27107, 13651, 15607, 30433, 27285, 12110, 13585, 19887, + 21776, 7789, 27210, 31624, 25406, 11263, 7040, 21342, 11666, 6842, + 7269, 18093, 23022, 18955, 7041, 9065, 12741, 939, 2427, 28213, + 25469, 18291, 1428, 11477, 29463, 2417, 29556, 13542, 7697, 5691, + 7721, 21196, 13963, 4483, 3181, 23789, 14762, 17132, 27175, 1821, + 9530, 9500, 11061, 2648, 20631, 27411, 19305, 29879, 19661, 28603, + 20433, 13725, 4094, 17344, 27964, 14897, 28676, 19873, 30713, 11794, + 12376, 12296, 226, 13382, 10034, 27223, 17306, 11379, 15858, 26499, + 18962, 22609, 20671, 28453, 26962, 30047, 12654, 11470, 31606, 7893, + 20901, 16795, 31478, 3034, 22592, 5130, 25387, 32248, 9715, 21004, + 31247, 29645, 3377, 22876, 26437, 29699, 16717, 31246, 15449, 1154, + 16608, 18283, 21064, 8675, 16234, 27388, 8259, 19240, 21052, 11749, + 28592, 30123, 25749, 11381, 4466, 32085, 14091, 26618, 30826, 25872, + 28204, 10970, 23869, 31615, 29798, 29407, 20312, 14316, 13092, 21971, + 22322, 25474, 26426, 5197, 3537, 25276, 5251, 20170, 19856, 13085, + 23752, 4351, 24607, 28962, 15844, 10094, 19915, 31349, 31526, 29496, + 12800, 99, 13815, 12338, 14604, 7824, 31165, 24890, 11045, 18921, + 16127, 22220, 10414, 11227, 10697, 976, 31670, 15810, 1874, 5302, + 8898, 14501, 20188, 31941, 30924, 11552, 10716, 28080, 7819, 22433, + 18267, 28011, 2293, 1240, 32412, 32316, 22787, 6760, 1279, 3349, + 24756, 6115, 15141, 6689, 31060, 9205, 21845, 20381, 3838, 31199, + 25374, 31465, 1376, 19239, 31294, 7135, 22974, 27022, 2243, 32355, + 9785, 19779, 4720, 4667, 27218, 19286, 23906, 29986, 15530, 4207, + 20980, 25367, 18955, 13248, 4575, 251, 17836, 30264, 2726, 13659, + 11953, 506, 1841, 907, 3826, 15885, 1207, 7412, 5885, 2645, + 20194, 7342, 15198, 30658, 10030, 8530, 14110, 20679, 31635, 14601, + 6025, 1550, 28771, 21481, 13620, 21354, 16757, 12418, 15935, 12557, + 16003, 30387, 13784, 25851, 26321, 29891, 3328, 18622, 26639, 12972, + 14482, 5979, 1153, 20410, 26753, 22406, 9309, 24149, 17505, 13842, + 5749, 12827, 20777, 25771, 17429, 21555, 25655, 20679, 23599, 4573, + 18324, 18138, 14773, 22861, 29562, 21961, 2375, 22713, 275, 1698, + 29198, 5041, 14980, 15498, 17896, 25263, 27690, 20727, 31941, 4335, + 23331, 16415, 32593, 3200, 1297, 31152, 1235, 23708, 3354, 10689, + 2132, 21045, 31284, 3592, 6405, 9936, 10911, 11086, 2523, 32150, + 8370, 30497, 17532, 31400, 3291, 18889, 17246, 25780, 14562, 11389, + 30509, 1222, 14445, 22486, 7022, 14432, 713, 30339, 15971, 27084, + 23015, 63, 46, 25265, 8869, 16640, 19768, 11493, 4687, 24689, + 6100, 30082, 21862, 32163, 21792, 9788, 5024, 13055, 16684, 10481, + 29999, 5760, 11871, 2434, 3398, 2591, 25622, 27106, 19930, 28814, + 20621, 31852, 26790, 6696, 4611, 30842, 16753, 32297, 6937, 15667, + 21907, 5146, 30332, 5796, 15542, 24976, 28099, 22803, 9946, 4118, + 6043, 18063, 31644, 8040, 4385, 29388, 32439, 2018, 26255, 6446, + 7627, 24887, 28443, 6436, 19293, 24934, 8220, 3255, 2847, 18410, + 7023, 11619, 4978, 2353, 32025, 31525, 10282, 20048, 11309, 22997, + 4753, 7413, 13041, 6326, 23214, 15261, 25148, 6220, 9671, 9735, + 17442, 621, 24100, 9704, 8659, 18587, 2070, 25568, 8385, 23697, + 26680, 1197, 15105, 22300, 24309, 9887, 5876, 300, 7498, 21323, + 550, 29483, 5837, 14724, 4982, 16013, 21743, 17287, 14456, 21929, + 3966, 23926, 12823, 9671, 11886, 8, 17535, 6010, 6360, 21374, + 7738, 26928, 30019, 32473, 15000, 6151, 16163, 30538, 17237, 19170, + 21919, 25578, 13686, 29629, 26519, 27051, 19828, 6973, 24806, 29582, + 32412, 2699, 15588, 19223, 31158, 18275, 25135, 5375, 1765, 5736, + 27564, 1576, 14030, 30651, 712, 25814, 12028, 20939, 12036, 10779, + 5507, 15983, 16048, 22419, 24548, 11826, 11510, 21842, 2353, 21997, + 10523, 27675, 29554, 6812, 8962, 12242, 30790, 28522, 242, 28212, + 27854, 23054, 25240, 30150, 7366, 4442, 5456, 18699, 245, 27153, + 7584, 4602, 13512, 1530, 31237, 27055, 28474, 31026, 755, 18529, + 758, 22982, 3095, 12692, 10688, 28033, 10975, 28816, 7953, 27139, + 17980, 30993, 19969, 31507, 21415, 16448, 5738, 823, 25954, 8674, + 27979, 19073, 28160, 9680, 23658, 4011, 23847, 14759, 9534, 12135, + 31125, 12992, 2827, 22000, 7265, 11363, 2127, 28389, 12675, 5687, + 29533, 18263, 8599, 1789, 22088, 5665, 2270, 10110, 32258, 24183, + 24454, 18882, 1221, 24770, 30507, 131, 23474, 3499, 29085, 19169, + 6073, 12150, 5348, 10328, 1464, 15762, 8321, 10478, 18919, 3694, + 2039, 19889, 3699, 23438, 13962, 19808, 1385, 19421, 16455, 4457, + 16854, 30789, 9284, 32531, 11309, 7805, 1065, 24829, 5608, 30242, + 29935, 1223, 12402, 23562, 19688, 24847, 27461, 18389, 4864, 22431, + 32079, 16324, 29506, 26596, 7579, 16079, 23710, 26019, 12077, 16625, + 11241, 2023, 25646, 27079, 2085, 25662, 7241, 31065, 21507, 10788, + 596, 824, 22694, 12992, 11571, 7113, 3498, 14542, 18425, 28640, + 8805, 19677, 1361, 31376, 11372, 22970, 25117, 19778, 8025, 19756, + 31580, 7685, 29132, 14561, 31569, 4133, 5040, 32681, 25058, 27391, + 6592, 25314, 11326, 13075, 16969, 1075, 8130, 22875, 14129, 12158, + 15390, 29916, 12600, 271, 6323, 14970, 24702, 15386, 12608, 10000, + 15942, 25956, 26046, 4032, 11257, 30272, 12680, 3208, 878, 15293, + 23999, 28950, 31848, 13854, 13037, 18398, 2912, 3210, 28136, 873, + 7557, 26885, 31738, 23024, 22018, 20639, 16958, 32553, 12415, 19435, + 29773, 12385, 11498, 29619, 15898, 18123, 26743, 13802, 17691, 17432, + 18405, 26338, 17578, 3399, 5892, 29376, 18858, 18367, 16756, 17196, + 20845, 3227, 2223, 11597, 20437, 26181, 23369, 31732, 24762, 19526, + 12663, 27159, 10380, 4444, 4815, 8578, 10298, 30669, 10574, 4553, + 2130, 24572, 1439, 13185, 8041, 17722, 30874, 2991, 14269, 31966, + 9740, 897, 22553, 27544, 13494, 26684, 5113, 9474, 16280, 10102, + 9967, 10616, 27759, 7045, 24389, 13866, 19324, 31799, 7140, 23407, + 25735, 18372, 24948, 19805, 5412, 14336, 19141, 8854, 9269, 6101, + 22638, 30052, 11952, 3313, 25524, 6921, 20898, 10684, 14509, 25936, + 13684, 23437, 7077, 8808, 24173, 2333, 15808, 20210, 26233, 30012, + 25825, 11865, 1065, 14786, 19147, 15714, 762, 28361, 30598, 13060, + 4314, 21017, 6917, 889, 1256, 740, 10757, 6549, 892, 9652, + 16952, 14935, 9063, 1185, 6563, 18110, 30844, 32711, 32068, 20445, + 10063, 25079, 8953, 12594, 26172, 28077, 21149, 9524, 4011, 13890, + 10868, 2091, 10643, 6499, 713, 15684, 1597, 27842, 13525, 27109, + 25163, 13323, 18659, 31550, 9208, 29466, 20402, 10785, 8097, 28779, + 27419, 28947, 1413, 31698, 18931, 10403, 23753, 3436, 6963, 15866, + 12223, 20693, 15446, 14260, 19815, 28827, 9987, 31982, 17462, 13811, + 25068, 4768, 31255, 20651, 30966, 17198, 1456, 23392, 16538, 25657, + 11494, 28657, 26763, 11963, 11427, 24627, 8601, 23891, 11734, 26343, + 23253, 1245, 3004, 25833, 23030, 23366, 20319, 32426, 13051, 24734, + 1116, 11743, 25362, 7791, 19176, 7035, 4793, 29006, 19221, 21979, + 17187, 4271, 27986, 14145, 27357, 18686, 31793, 4228, 17841, 7147, + 4974, 27654, 23499, 15109, 44, 31973, 1050, 4234, 30337, 3702, + 26105, 19069, 16437, 2031, 15887, 7162, 18631, 22627, 27251, 10439, + 10929, 24308, 532, 20267, 8685, 9028, 31577, 17411, 1976, 29228, + 9732, 13586, 15254, 12977, 31836, 26009, 7574, 1843, 3979, 31015, + 32215, 3816, 4933, 24755, 9547, 23433, 19626, 7656, 23483, 28491, + 26734, 20672, 3852, 22289, 31799, 17236, 32704, 13536, 14150, 26611, + 5824, 20115, 1626, 32113, 18520, 29904, 11985, 19905, 10371, 22074, + 9489, 15046, 5225, 4840, 8742, 3431, 5414, 9239, 15532, 15074, + 24525, 27675, 2288, 5218, 5804, 668, 1106, 5331, 12242, 4350, + 20462, 16629, 9423, 21281, 32379, 19887, 20411, 4171, 31090, 167, + 6522, 8290, 16755, 32527, 23869, 20831, 3558, 32450, 9548, 16639, + 4062, 3471, 9476, 32443, 23618, 25300, 5147, 2555, 16686, 28495, + 20268, 135, 2711, 7986, 24943, 4414, 31278, 629, 23495, 1802, + 30343, 20336, 7427, 19, 13691, 29609, 28509, 9523, 26059, 9963, + 8007, 30123, 7173, 13464, 28520, 31508, 1708, 28479, 3553, 31128, + 11667, 625, 28372, 28769, 16522, 5841, 12327, 32338, 14652, 31851, + 27269, 32501, 21592, 20295, 5030, 29621, 9711, 19005, 21969, 3406, + 19742, 15370, 159, 27009, 2796, 21156, 30465, 22796, 16501, 12491, + 16004, 16857, 6217, 481, 5076, 29046, 24335, 32763, 10367, 29637, + 20334, 8248, 28156, 16885, 21805, 22866, 4588, 23984, 2968, 14450, + 3999, 11414, 31526, 14409, 31450, 10219, 23130, 3676, 26584, 13452, + 21991, 25247, 5262, 14120, 9239, 9542, 14132, 14508, 26477, 30424, + 5632, 12031, 9354, 22149, 19509, 15856, 20301, 2745, 24668, 1133, + 13118, 30705, 4049, 18933, 15149, 22990, 29570, 8271, 23574, 4711, + 25076, 19837, 10056, 996, 12935, 4588, 18374, 10313, 17914, 7917, + 28398, 23439, 30769, 20841, 29784, 3065, 24374, 1231, 11521, 17573, + 31615, 4690, 15785, 18018, 15727, 20562, 27769, 8897, 1054, 9751, + 13801, 3439, 31069, 1989, 10465, 22465, 28405, 28300, 19998, 19089, + 8496, 8480, 7896, 21065, 667, 24681, 20130, 12605, 29961, 19579, + 27793, 11326, 5083, 5576, 8096, 25963, 29567, 20116, 595, 22859, + 31287, 28842, 24987, 12825, 13852, 27470, 25338, 25951, 21602, 19826, + 21991, 20423, 7645, 24621, 13428, 17197, 7364, 17768, 5934, 15096, + 20406, 28379, 8261, 10923, 3578, 312, 8685, 6839, 267, 30629, + 1027, 1940, 22356, 7024, 11508, 14095, 5176, 9214, 29147, 30694, + 8533, 4869, 20969, 32658, 1290, 8443, 266, 23140, 25142, 19919, + 1477, 8659, 9539, 27094, 28260, 27925, 612, 11016, 24918, 12268, + 10522, 8812, 3281, 31683, 20733, 9681, 14780, 22265, 953, 447, + 1483, 7805, 18519, 1965, 2284, 9627, 5054, 19572, 27106, 2069, + 8825, 11012, 9775, 25206, 8122, 24406, 4408, 25763, 15775, 19874, + 20132, 29363, 8885, 18897, 19461, 18432, 20667, 29493, 32186, 20606, + 26792, 30840, 25364, 11340, 28655, 22482, 5559, 4489, 31489, 28543, + 26691, 105, 24851, 29297, 22580, 32136, 23842, 5442, 22279, 18046, + 16437, 1900, 11935, 13491, 6371, 3260, 1254, 17013, 29716, 19204, + 17079, 26183, 6924, 8209, 25084, 14460, 29349, 5084, 26387, 5893, + 24539, 15108, 2423, 15240, 21902, 7548, 20380, 30642, 28548, 30081, + 8730, 3021, 16046, 23248, 6670, 19349, 10924, 11797, 3773, 1351, + 3218, 22694, 9851, 24316, 26349, 13425, 28364, 7733, 24321, 12340, + 16328, 3511, 23381, 1004, 28356, 30105, 27506, 2447, 5166, 22939, + 23783, 3581, 26809, 29113, 8592, 11828, 32186, 4352, 27416, 5316, + 21714, 28321, 8125, 11140, 6862, 1348, 3155, 5506, 2507, 31902, + 30604, 1344, 13982, 20806, 31350, 17181, 19827, 11972, 16965, 30737, + 435, 12267, 8019, 4253, 13612, 31082, 1381, 19458, 6764, 25245, + 24880, 19378, 22125, 8361, 26396, 14084, 25976, 10183, 16199, 18219, + 18243, 14048, 13005, 20329, 13917, 16731, 15943, 5504, 28363, 11059, + 11665, 3690, 18420, 2892, 9425, 21321, 9658, 26631, 15028, 25249, + 1258, 8724, 2043, 453, 9942, 391, 20535, 21135, 8256, 6446, + 10192, 29012, 20346, 29730, 15180, 12055, 32204, 16797, 7118, 15357, + 12310, 29451, 13533, 22017, 27318, 100, 20327, 20457, 27820, 18104, + 17926, 1145, 9724, 13580, 20097, 2534, 27988, 15204, 31894, 14734, + 14575, 12321, 25079, 24549, 24249, 8077, 28562, 10419, 8116, 14773, + 31765, 7037, 666, 21111, 5042, 15424, 16836, 10203, 29984, 9318, + 20554, 25255, 24546, 10226, 14956, 1325, 6091, 23294, 25905, 15464, + 16834, 2919, 29005, 18247, 32195, 711, 17736, 26697, 25126, 9379, + 733, 5450, 15396, 15590, 27553, 22941, 14743, 4750, 30949, 7770, + 24577, 4774, 10972, 19705, 29904, 10100, 21249, 32, 15072, 4083, + 23987, 14010, 10562, 19332, 24354, 15689, 32572, 28128, 26479, 16765, + 22928, 28497, 25103, 17413, 11521, 19746, 1137, 23717, 18964, 3667, + 4064, 1161, 19988, 146, 27306, 23257, 9560, 16743, 21634, 6029, + 713, 26543, 14883, 8323, 28830, 24907, 27702, 29902, 24170, 22809, + 15734, 25170, 20569, 14627, 19474, 14734, 5046, 7447, 31314, 29624, + 7345, 18680, 23166, 32527, 29024, 30738, 4604, 9581, 17923, 6195, + 30648, 16667, 11916, 17688, 29129, 13290, 2399, 6182, 22097, 21631, + 1878, 8283, 21412, 17903, 29960, 5763, 9133, 28044, 10571, 8623, + 3726, 31486, 15607, 16355, 135, 13973, 28653, 9999, 14105, 2525, + 24408, 12864, 18795, 30323, 28979, 12056, 4076, 28440, 6412, 8060, + 12090, 18180, 23047, 17292, 19011, 11132, 10789, 32728, 8106, 23463, + 6854, 29648, 5740, 4766, 27245, 21629, 32195, 22466, 25540, 24431, + 1098, 10578, 11744, 31223, 10850, 13629, 29125, 20630, 13269, 5172, + 31021, 2740, 23738, 15108, 10885, 26261, 21913, 5667, 1593, 20874, + 19151, 904, 2353, 20580, 31539, 7872, 6300, 19502, 8697, 4927, + 23276, 5916, 14295, 26868, 8737, 15806, 20327, 32544, 19275, 30841, + 4301, 10599, 14154, 20792, 6297, 9000, 16601, 20831, 15855, 19722, + 21451, 19457, 997, 26839, 2451, 6226, 14892, 18318, 28426, 20575, + 4339, 7603, 22381, 26620, 9224, 29162, 24741, 12286, 16704, 22980, + 3276, 6571, 28186, 30937, 5356, 20079, 28831, 30231, 22794, 28625, + 24460, 23385, 4451, 5028, 32179, 4923, 9461, 20610, 25759, 31021, + 18753, 28278, 17015, 12231, 20304, 4089, 12068, 12637, 16884, 26400, + 23734, 20178, 14672, 19219, 9988, 4273, 1946, 16833, 18067, 11897, + 8992, 20946, 10938, 4153, 29065, 12509, 16131, 3677, 22622, 8668, + 8147, 25544, 14025, 6109, 590, 1571, 11420, 28922, 21365, 22644, + 21140, 23946, 21137, 27634, 18400, 18329, 10965, 17176, 14557, 20118, + 20543, 19192, 17188, 11084, 9151, 32466, 23238, 4116, 8859, 27109, + 5334, 22856, 9490, 31621, 31528, 8698, 28736, 24942, 19801, 4326, + 8001, 23640, 21263, 29094, 10137, 17099, 9090, 28197, 31955, 9781, + 1596, 31830, 5993, 20026, 20765, 21542, 25316, 32331, 26529, 431, + 22340, 14714, 16230, 26542, 16802, 30860, 3560, 17975, 20, 1616, + 911, 6567, 13521, 30256, 14317, 26075, 13462, 26886, 11072, 16799, + 13384, 5169, 319, 13929, 31194, 16103, 22803, 18504, 5233, 10700, + 26275, 14871, 24518, 5316, 12564, 25078, 22220, 22278, 6800, 8699, + 31124, 15731, 5447, 22820, 19053, 8997, 10676, 24720, 9807, 30794, + 12834, 26863, 31866, 11059, 28526, 19512, 10586, 10152, 23310, 10138, + 30894, 24703, 23122, 20916, 4841, 5997, 14058, 264, 23000, 22299, + 169, 13997, 30854, 21372, 15497, 16709, 10845, 9311, 1262, 27695, + 32141, 3648, 32559, 12030, 5298, 19812, 19026, 28969, 11185, 5925, + 19223, 13827, 25570, 17802, 31033, 17646, 7029, 24858, 4592, 8652, + 21518, 31986, 21434, 27063, 27827, 6422, 10075, 26610, 13032, 32517, + 23074, 30495, 3864, 19600, 19823, 690, 20535, 8427, 16242, 15525, + 23206, 8197, 4405, 23406, 15959, 16405, 11741, 29742, 30769, 5493, + 30509, 17707, 25270, 4276, 23371, 1152, 6627, 25319, 30663, 1811, + 22103, 31807, 28121, 21448, 952, 22753, 19434, 30452, 5715, 4081, + 12374, 10773, 25602, 22768, 2417, 9152, 1107, 31561, 5832, 9178, + 31718, 23590, 4009, 4107, 14103, 7816, 4837, 13424, 6109, 2455, + 17364, 31888, 6898, 10551, 14490, 14482, 22581, 23771, 7593, 17069, + 25164, 18590, 12935, 6367, 18829, 23696, 978, 16098, 17951, 26189, + 1665, 31836, 19804, 24789, 30828, 19286, 5754, 30359, 10767, 2799, + 21099, 27087, 5514, 19025, 6913, 22290, 26349, 16357, 23894, 1575, + 22963, 16579, 17259, 15641, 3980, 19565, 24847, 18826, 17504, 188, + 10243, 13386, 912, 2586, 26459, 21289, 17023, 25586, 23079, 11529, + 1105, 32101, 2387, 25655, 27378, 5695, 1228, 6907, 22874, 26665, + 14308, 1645, 2832, 28704, 23800, 9099, 17645, 27685, 21584, 17033, + 803, 24886, 25158, 23656, 18558, 15742, 6315, 26314, 29417, 27337, + 28518, 6661, 14643, 25914, 20574, 23926, 13843, 10816, 6811, 19992, + 9322, 5062, 24744, 4948, 12489, 29610, 26171, 30235, 1075, 881, + 8471, 28211, 27684, 31801, 16690, 6606, 17361, 27374, 1043, 21345, + 690, 5313, 22399, 22963, 5182, 32726, 3924, 15925, 23798, 29757, + 12981, 3473, 4260, 2340, 9319, 26245, 15845, 4683, 10875, 29426, + 13029, 440, 6446, 30512, 8042, 4523, 9485, 27557, 6164, 19156, + 8285, 21654, 24291, 15044, 10169, 19516, 5369, 28322, 509, 29467, + 14753, 1567, 14179, 14991, 6801, 29355, 9196, 15784, 6508, 14058, + 2306, 7223, 27275, 14945, 25179, 3113, 12541, 1398, 544, 5396, + 7116, 31127, 14415, 10768, 26754, 22969, 2398, 5576, 28396, 14084, + 14369, 27857, 6686, 5580, 9965, 31290, 29367, 24260, 21502, 23140, + 7783, 28782, 29974, 14355, 30894, 25814, 30311, 24518, 10470, 24448, + 24755, 7492, 22915, 30623, 27314, 6008, 29850, 4743, 14772, 7806, + 2464, 16997, 5511, 8190, 16501, 32046, 5019, 13753, 32160, 16341, + 32100, 609, 7133, 29121, 30208, 20654, 24277, 7294, 8986, 10414, + 812, 19379, 23822, 20317, 4032, 10580, 10829, 25717, 19504, 10776, + 2564, 4743, 156, 24110, 20984, 1355, 25260, 14892, 9140, 6709, + 21460, 27720, 15591, 32584, 21718, 4554, 9992, 13359, 21128, 24769, + 2619, 8586, 12247, 6645, 10104, 14982, 12556, 22302, 15850, 23728, + 11342, 2387, 24469, 25099, 9064, 15301, 31036, 6495, 10957, 28333, + 4482, 6357, 16857, 7811, 847, 7873, 90, 19313, 14197, 29887, + 15695, 5383, 8596, 2305, 4848, 15920, 1083, 12775, 25582, 11621, + 16463, 20432, 17915, 15648, 13233, 16689, 30093, 28820, 27419, 23780, + 14047, 24731, 11083, 26306, 18489, 29936, 5476, 12915, 18802, 15774, + 27281, 26757, 15539, 184, 13035, 8636, 1149, 20555, 19357, 19625, + 750, 15505, 8261, 4478, 15936, 30311, 5022, 10273, 11922, 30209, + 23389, 21631, 6379, 31197, 21336, 32035, 7036, 29173, 3053, 4103, + 20261, 11065, 6050, 10666, 8763, 20284, 23561, 12898, 20617, 3118, + 27111, 26100, 7316, 30986, 8340, 31074, 11812, 2576, 8357, 21252, + 14847, 10614, 20937, 2266, 22264, 18523, 32753, 6433, 9256, 31740, + 21428, 4388, 9340, 31530, 10737, 5717, 29552, 4092, 3766, 1436, + 14105, 20929, 24416, 7407, 29545, 15160, 22532, 25379, 8879, 28932, + 4110, 16898, 14173, 19546, 10792, 1693, 27406, 32253, 15198, 15332, + 32455, 23916, 11647, 4979, 20605, 14425, 8390, 20655, 27096, 13918, + 4281, 27154, 27703, 9452, 16466, 21699, 32244, 16260, 12637, 11882, + 7401, 18473, 11041, 14658, 31283, 1045, 5186, 24176, 17205, 28843, + 9190, 10899, 31712, 17581, 11799, 30876, 20018, 4527, 3945, 16096, + 29437, 6603, 31708, 12907, 6122, 11671, 13571, 13463, 26301, 9812, + 27494, 16094, 4571, 20584, 20262, 16621, 2569, 23055, 5087, 9465, + 16758, 6733, 27602, 28246, 8188, 17447, 15940, 9798, 3235, 23058, + 7457, 2831, 12391, 30124, 15845, 13648, 22075, 14250, 31164, 10132, + 3149, 29713, 6376, 31784, 27276, 28330, 28004, 9514, 8808, 15279, + 199, 29411, 15320, 6347, 22890, 21591, 9122, 21058, 6861, 1561, + 28375, 2155, 1335, 9060, 30620, 26983, 6301, 19816, 14128, 13069, + 24261, 23778, 299, 4388, 8364, 20975, 1028, 11221, 2852, 30242, + 13014, 31327, 1729, 25812, 31844, 11789, 18166, 28091, 29530, 18790, + 21628, 1471, 19414, 14243, 5875, 14856, 21030, 30822, 8652, 22876, + 16084, 13224, 18534, 18344, 3039, 18931, 15735, 12252, 12108, 13522, + 16814, 2044, 24285, 19335, 9623, 7028, 16673, 16379, 8894, 4013, + 12205, 26619, 20503, 5853, 28328, 9888, 13725, 17699, 32141, 2575, + 17210, 15315, 25900, 29027, 12007, 7951, 9970, 20200, 32276, 2455, + 12651, 23178, 15946, 29409, 32261, 18573, 12559, 15213, 15383, 22654, + 28426, 21331, 11553, 20585, 18023, 12062, 5718, 14412, 22653, 32476, + 27479, 27084, 15022, 23414, 5698, 18471, 7823, 24126, 235, 13418, + 17737, 22215, 17257, 4580, 16960, 4857, 7125, 28378, 20722, 28004, + 4942, 9596, 16929, 5589, 29760, 2328, 17460, 18471, 30182, 29582, + 2779, 25948, 16654, 18180, 6642, 8941, 12829, 14298, 14248, 17501, + 31826, 29408, 18912, 4884, 4687, 29639, 8919, 2155, 30677, 8150, + 263, 24068, 24783, 30942, 17996, 30812, 16786, 31856, 10822, 24483, + 13411, 845, 27646, 15051, 22585, 13598, 18124, 223, 3269, 25702, + 11896, 21654, 12208, 26292, 5791, 15912, 16164, 15645, 18776, 14176, + 1421, 8425, 4376, 15858, 12961, 18573, 14803, 24592, 1438, 30938, + 11620, 30520, 27858, 13939, 12079, 1360, 15969, 11140, 31493, 26612, + 28227, 5553, 19272, 22031, 25996, 20557, 2639, 23464, 24304, 20173, + 13067, 18431, 27782, 7036, 9451, 16857, 2764, 11888, 28414, 30232, + 25836, 12476, 22104, 5262, 11837, 32743, 7094, 3859, 13581, 1437, + 17509, 20181, 4766, 14245, 27231, 21488, 7278, 21523, 22456, 29503, + 31419, 11711, 28984, 9841, 2119, 7413, 13386, 8799, 24409, 26734, + 19476, 10140, 14340, 14975, 488, 16718, 31659, 30353, 3291, 26173, + 12741, 721, 1203, 12330, 10718, 23081, 31751, 7522, 1758, 31342, + 1441, 4243, 3982, 731, 10535, 733, 26712, 5822, 2811, 7212, + 6735, 22641, 19295, 9670, 2927, 4236, 24797, 2848, 19493, 21849, + 29158, 23798, 24082, 18056, 29113, 2714, 22540, 12580, 1158, 17805, + 2917, 12230, 28371, 17228, 3696, 6921, 5152, 20844, 20546, 24125, + 18630, 28649, 6582, 1120, 31525, 26044, 8116, 4426, 30718, 939, + 2738, 31527, 19998, 2405, 6010, 22570, 5437, 24782, 13614, 5248, + 7205, 9112, 17546, 17539, 16919, 7955, 23658, 26417, 32618, 19014, + 14220, 11484, 24949, 25150, 12182, 5666, 32175, 8358, 26971, 32544, + 8213, 16536, 5722, 27363, 14540, 17843, 28551, 8991, 19720, 14702, + 19791, 36, 28460, 21163, 214, 27917, 563, 15248, 21123, 31803, + 19534, 27026, 18878, 8194, 7374, 15314, 8696, 23457, 2373, 21421, + 4332, 120, 1512, 23438, 16933, 19317, 23864, 12295, 11201, 30416, + 19170, 7969, 8308, 5112, 15843, 12784, 17104, 16845, 1519, 13418, + 15811, 4879, 28692, 28537, 26209, 14614, 5787, 18766, 10979, 18116, + 26671, 16377, 29424, 8121, 10817, 29238, 21788, 26565, 31402, 10890, + 17839, 1699, 5266, 8335, 8871, 5092, 23697, 25740, 16640, 16169, + 30741, 10569, 24370, 2197, 29700, 24610, 8444, 23938, 16034, 23385, + 30816, 28019, 30900, 27313, 8175, 19274, 25062, 9784, 2630, 11964, + 25706, 23396, 27353, 27137, 994, 20577, 15343, 26576, 13184, 20906, + 26094, 17802, 22379, 15831, 27609, 23990, 30053, 949, 16663, 22189, + 22700, 18076, 11382, 2454, 11549, 7421, 23663, 21909, 21919, 7483, + 4268, 13120, 447, 11452, 31943, 7251, 18072, 17727, 18517, 7076, + 16118, 9328, 20515, 6489, 30395, 6585, 3263, 14168, 9601, 26204, + 16954, 20991, 31872, 25535, 23853, 30998, 2994, 25606, 8442, 29953, + 566, 231, 2900, 11322, 19955, 19642, 4354, 31999, 24189, 6608, + 6111, 24392, 3900, 19462, 21030, 25340, 31947, 26778, 24300, 24164, + 18846, 22589, 21486, 24800, 5608, 3945, 5261, 15833, 11963, 4340, + 6679, 17398, 14449, 32486, 4424, 29843, 22604, 11656, 28945, 20335, + 8537, 27837, 28311, 1700, 18586, 31637, 29311, 11997, 25699, 23072, + 29545, 16323, 15109, 28032, 22970, 30904, 25770, 10818, 20639, 32200, + 17536, 14575, 10319, 16604, 7450, 18653, 11664, 2865, 17427, 1355, + 6780, 15113, 14580, 24749, 1157, 2465, 9047, 10821, 27575, 28002, + 31821, 8691, 20285, 27739, 26366, 32334, 27431, 29423, 31887, 6387, + 874, 715, 3913, 13598, 24616, 19996, 10487, 5239, 24370, 4862, + 18575, 20014, 16076, 19255, 8981, 7241, 1456, 1061, 27581, 4925, + 11349, 29429, 26448, 30652, 27504, 6422, 20176, 9935, 21436, 3309, + 17759, 17444, 4854, 15051, 25949, 8873, 2604, 12828, 10449, 7002, + 6456, 21217, 12554, 18006, 17288, 28512, 12678, 19001, 4640, 13849, + 530, 13734, 22241, 30717, 7219, 5923, 13579, 7899, 1989, 18218, + 4865, 26651, 31458, 6268, 22860, 14254, 12549, 25273, 8931, 5957, + 15727, 6958, 17065, 13761, 23754, 28654, 16952, 31467, 2573, 6436, + 15837, 870, 23199, 21671, 16867, 6370, 26047, 16335, 3349, 31377, + 15139, 904, 23678, 2527, 11487, 1700, 9331, 12126, 28809, 6774, + 23252, 3071, 23451, 21916, 13966, 19080, 16152, 4380, 2148, 9538, + 9843, 15283, 4336, 29682, 32172, 30569, 30692, 22027, 25276, 13673, + 18582, 19746, 9779, 17424, 6076, 23458, 16160, 836, 13939, 31681, + 13873, 13063, 5767, 691, 19483, 4171, 17390, 17437, 24380, 13320, + 6037, 2508, 21631, 26765, 1888, 13223, 1921, 26900, 14188, 5090, + 1522, 16280, 12439, 23413, 6717, 13547, 24642, 10957, 26987, 1795, + 17749, 6849, 10308, 22932, 28292, 20935, 11192, 5349, 11729, 31080, + 22654, 9948, 9554, 10225, 13819, 23130, 4405, 4716, 10341, 14543, + 29441, 6862, 5672, 10757, 542, 3698, 31881, 17984, 1432, 10438, + 14217, 3721, 31520, 11845, 268, 32583, 12116, 13221, 27450, 12559, + 11072, 25937, 23078, 31677, 6948, 25369, 16557, 20420, 4727, 1064, + 9378, 14786, 20781, 5943, 11816, 17496, 28214, 29441, 18014, 11320, + 2887, 27383, 2370, 6783, 30172, 31796, 3791, 22854, 22910, 18099, + 20329, 13939, 26433, 9920, 10107, 2261, 5778, 684, 15967, 2929, + 25391, 8389, 21630, 790, 11383, 6533, 15544, 25144, 6628, 14858, + 1286, 24315, 10454, 27334, 4701, 12173, 18322, 28280, 13270, 24761, + 8586, 3419, 28941, 31417, 223, 157, 6086, 26700, 26916, 12818, + 19671, 21513, 20325, 9258, 9282, 28659, 4967, 5665, 1185, 23373, + 7730, 14453, 22983, 6832, 18893, 1437, 23204, 22106, 30281, 4856, + 26217, 21193, 28672, 28164, 15597, 22409, 2847, 22992, 5711, 30794, + 6242, 180, 24030, 29749, 5919, 28223, 5173, 8064, 18566, 25969, + 7338, 2880, 2087, 26410, 31210, 24447, 3509, 31971, 15837, 7055, + 31753, 7068, 12496, 12445, 24937, 13077, 29650, 10889, 3339, 15681, + 8798, 23286, 24021, 25080, 14139, 15005, 28263, 748, 14497, 29926, + 16987, 17374, 28573, 20921, 22731, 25362, 11958, 29073, 18452, 30208, + 27601, 4051, 18138, 24140, 12626, 16223, 17997, 2808, 10884, 21860, + 426, 23001, 20283, 30328, 9988, 11591, 29368, 31208, 32068, 2585, + 14796, 5964, 11119, 20945, 15425, 4703, 1319, 27923, 21877, 22798, + 117, 31324, 26696, 18383, 14533, 21744, 6712, 2100, 9010, 2068, + 18891, 15808, 22532, 638, 17454, 29945, 5495, 7690, 32283, 2238, + 24511, 20398, 21503, 29147, 25160, 5040, 5469, 13531, 30274, 27158, + 24429, 25118, 29277, 30559, 2826, 23595, 10193, 7536, 12747, 22598, + 12450, 8401, 3695, 19782, 29474, 11801, 15486, 17569, 32439, 21415, + 6957, 16841, 9694, 17299, 22402, 13243, 31088, 29156, 25018, 9428, + 2341, 25348, 20968, 7399, 3414, 15985, 32734, 27022, 24071, 31156, + 21072, 16573, 23332, 12183, 2359, 29085, 10763, 30611, 1908, 6039, + 27723, 20872, 24502, 942, 21419, 31153, 10397, 20499, 14191, 14550, + 23584, 20979, 3798, 1219, 17964, 23956, 29927, 1274, 18061, 4256, + 6509, 20389, 27154, 5825, 17217, 29390, 7916, 7277, 18375, 7634, + 2136, 25604, 7099, 32531, 13694, 12568, 24878, 10734, 16280, 32267, + 5835, 6381, 4950, 12256, 19551, 8253, 27009, 15435, 153, 25698, + 13024, 30218, 3086, 10537, 31576, 29560, 15743, 3294, 2134, 17248, + 2911, 960, 13938, 25615, 32400, 10162, 23116, 12461, 14958, 12339, + 10426, 14541, 17934, 9872, 27174, 13890, 31431, 10840, 32049, 5428, + 15346, 23057, 27804, 17012, 23535, 13353, 7273, 13325, 21097, 1072, + 21053, 24604, 10202, 12356, 7433, 5601, 14929, 31436, 24553, 27925, + 16243, 3177, 1101, 16254, 10175, 23081, 5974, 24770, 18199, 15358, + 22147, 2169, 26236, 18287, 28556, 8837, 8986, 17038, 30606, 31142, + 16004, 17194, 23913, 28745, 17472, 20037, 23398, 15265, 10374, 3016, + 11294, 21753, 17477, 25206, 1687, 5449, 21095, 4127, 9647, 12039, + 21078, 4630, 24254, 31295, 29631, 29157, 2935, 18150, 28566, 19006, + 19215, 10604, 29274, 6197, 24226, 30096, 31642, 11437, 9426, 23416, + 22200, 21008, 30897, 23297, 4333, 31300, 30689, 340, 4237, 2266, + 29689, 9402, 12684, 3464, 4907, 4785, 2882, 729, 25636, 25663, + 17259, 25891, 26932, 27782, 4722, 13754, 24239, 30105, 23791, 15971, + 16531, 16270, 1646, 10507, 21354, 30164, 24946, 16313, 13426, 3783, + 24761, 26818, 13564, 18818, 28098, 13116, 1298, 15860, 2189, 1972, + 29020, 13111, 5282, 20772, 14405, 12316, 30548, 12831, 14937, 31243, + 27463, 6735, 2545, 30218, 11568, 20513, 7699, 8219, 26076, 17087, + 11412, 3275, 30068, 14399, 28543, 10504, 1023, 11289, 22971, 11315, + 31528, 29151, 32487, 28790, 19777, 28712, 12937, 9560, 11657, 1998, + 8808, 4238, 7594, 31577, 28726, 20756, 29830, 27322, 3698, 22653, + 16947, 14141, 25741, 3720, 13636, 25529, 32332, 21235, 25931, 17673, + 19053, 20884, 9884, 5824, 11405, 23717, 19283, 18457, 26185, 16736, + 30104, 5421, 7076, 19148, 24353, 16489, 30376, 26326, 14841, 62, + 11857, 8611, 3712, 29978, 29862, 27429, 15028, 15256, 4137, 23276, + 30382, 28404, 9329, 26175, 623, 12225, 28182, 15468, 24797, 734, + 30157, 15708, 17417, 7334, 32468, 15978, 3763, 27751, 13457, 4469, + 3856, 31765, 23414, 564, 22219, 30784, 2343, 15818, 21577, 29764, + 10207, 22683, 4401, 16064, 18704, 26205, 14892, 23113, 2889, 22642, + 21481, 28255, 5981, 5749, 16741, 17567, 30914, 1299, 32617, 9787, + 21815, 28444, 14676, 11688, 18345, 1241, 6938, 24726, 25176, 26017, + 1165, 9765, 7222, 26535, 475, 27990, 11663, 4630, 11608, 6079, + 8175, 32096, 24190, 31876, 5441, 3538, 18994, 6018, 2612, 15469, + 30793, 14541, 6115, 25073, 12875, 18525, 30900, 14394, 20343, 29188, + 21632, 1487, 519, 19875, 6133, 10169, 2485, 22430, 23353, 28049, + 28541, 19853, 7120, 31709, 11941, 17205, 19904, 10397, 28411, 950, + 2035, 1568, 13406, 4869, 20444, 4124, 10111, 25501, 18526, 12030, + 23529, 5234, 18409, 1407, 14143, 19239, 6630, 15480, 24005, 32384, + 28501, 16631, 18466, 17690, 17076, 10386, 26213, 31834, 13091, 12283, + 29765, 23459, 6855, 2005, 25261, 18380, 25708, 25542, 31352, 31441, + 25710, 32718, 7289, 21448, 14553, 4055, 17086, 25367, 29903, 19853, + 10005, 1765, 6954, 6255, 13008, 11214, 11363, 23590, 7110, 1499, + 19379, 12566, 21146, 21937, 16247, 27444, 9792, 3592, 20697, 2839, + 16572, 27999, 20141, 17745, 24877, 24558, 14233, 5119, 30475, 15837, + 20345, 1695, 685, 14771, 9217, 17442, 24334, 4881, 17572, 29506, + 22736, 24992, 12725, 32022, 13953, 3144, 18125, 24036, 5662, 16547, + 9324, 30774, 17365, 32433, 23779, 7728, 28175, 3277, 26737, 17696, + 23214, 26591, 9388, 5975, 403, 980, 25378, 22584, 12388, 28747, + 19265, 20110, 7052, 13713, 4980, 10531, 131, 32208, 21903, 4494, + 15695, 24195, 621, 24908, 331, 22061, 14551, 11538, 31527, 21067, + 936, 6754, 10288, 20871, 17199, 13149, 10449, 30254, 17877, 11480, + 15962, 7396, 21504, 21169, 22017, 19351, 13111, 3683, 17703, 3249, + 25734, 15321, 6876, 2706, 8740, 16512, 10916, 32573, 24659, 6304, + 13435, 19537, 21896, 20204, 19992, 20284, 19916, 4696, 29039, 17835, + 19810, 24106, 21625, 22084, 18090, 26708, 28215, 4504, 26437, 18945, + 19701, 32595, 16506, 5742, 8652, 2998, 20184, 21499, 9578, 26952, + 9501, 2946, 14870, 15048, 15959, 25063, 28398, 6501, 15177, 28656, + 18547, 19250, 3856, 19543, 29256, 15828, 24327, 31774, 15927, 6525, + 6631, 15651, 5237, 31, 2611, 24308, 14631, 19673, 26579, 7652, + 2852, 26550, 11518, 13407, 8189, 406, 12894, 5754, 24891, 19564, + 22127, 3022, 3815, 938, 13353, 7047, 19280, 22778, 11486, 32440, + 5128, 6447, 18789, 3314, 19504, 7547, 13944, 22292, 15349, 23778, + 24667, 25700, 10587, 12782, 29118, 17887, 28218, 10405, 14821, 29646, + 12165, 26808, 15878, 10650, 6610, 4093, 31219, 13281, 18199, 4383, + 5112, 25801, 12722, 27285, 2055, 8853, 22701, 18896, 3602, 17526, + 31039, 28345, 16313, 12707, 406, 25434, 16677, 30379, 8403, 23515, + 28980, 17674, 6231, 21169, 25814, 4347, 12861, 13586, 12727, 16782, + 295, 13228, 19486, 9978, 19774, 2966, 31765, 11060, 21108, 23431, + 11722, 28865, 13325, 22854, 8501, 21448, 18781, 31585, 15611, 363, + 10195, 27426, 2194, 6487, 3638, 21183, 4518, 3482, 5423, 23505, + 16572, 13494, 18696, 29191, 24854, 16883, 4275, 7708, 24534, 6137, + 1550, 3334, 1638, 10653, 9061, 6001, 13334, 23804, 27192, 23915, + 23303, 9414, 26431, 19041, 17481, 17373, 14252, 5450, 24384, 28447, + 29616, 4386, 8268, 16767, 8768, 24986, 12968, 13915, 6681, 26638, + 403, 7690, 29698, 31927, 23726, 16564, 29265, 18466, 15270, 9062, + 432, 20704, 25874, 9061, 14113, 4480, 31903, 23491, 28480, 10938, + 15466, 2353, 29384, 1517, 23981, 24596, 17497, 31692, 29854, 2267, + 31609, 21331, 17300, 13967, 20696, 11521, 18796, 2987, 16344, 11841, + 3883, 16687, 24908, 297, 30948, 17392, 25472, 31846, 7645, 15410, + 17275, 1911, 27629, 13176, 11136, 3866, 6696, 19611, 6875, 7684, + 11967, 16922, 15066, 28117, 7201, 7857, 1305, 1099, 14694, 25645, + 7938, 12470, 6638, 5246, 3733, 32172, 2117, 20750, 11365, 27389, + 10314, 1105, 30435, 1001, 1169, 18846, 10975, 9617, 2821, 1853, + 17740, 2476, 15570, 28353, 21060, 27003, 5506, 22423, 11596, 19711, + 28005, 10208, 5848, 23145, 23351, 16252, 17170, 14802, 22781, 6503, + 17879, 24876, 12336, 5961, 2, 23673, 28957, 11080, 29166, 26171, + 31116, 21537, 5942, 25643, 20128, 8055, 12223, 7213, 19966, 9835, + 6649, 13700, 31281, 14444, 8801, 23106, 26190, 10320, 27723, 3331, + 1235, 28580, 22494, 1755, 24021, 7233, 2400, 12685, 28763, 15392, + 30132, 4606, 17157, 15216, 6176, 14057, 26609, 26853, 13652, 32723, + 30686, 5202, 20392, 20546, 18578, 31610, 12454, 810, 25210, 31012, + 11208, 12968, 31121, 10861, 32386, 10226, 28034, 32354, 29059, 1935, + 5393, 25550, 7993, 3523, 22711, 2385, 915, 28494, 24716, 25522, + 14602, 12886, 22891, 28278, 1762, 5025, 5456, 23218, 9389, 19463, + 2820, 8221, 22290, 18983, 30973, 9321, 18892, 13402, 12159, 27860, + 19189, 18149, 17928, 3578, 19333, 25203, 16363, 19949, 18710, 24258, + 25659, 11399, 1258, 30108, 19690, 17919, 11310, 23049, 28978, 17339, + 21888, 20419, 12084, 20325, 30640, 22250, 6368, 15425, 15811, 16377, + 2068, 23230, 23614, 15353, 15880, 10827, 19361, 15604, 4585, 23915, + 3121, 15021, 29380, 25612, 22266, 12174, 16550, 28693, 24597, 25058, + 16642, 10302, 3623, 19780, 12882, 32036, 19242, 16471, 17153, 21661, + 21104, 696, 28553, 32584, 8164, 5937, 3033, 11369, 25748, 28208, + 31737, 20468, 9324, 7287, 24560, 12008, 24900, 27156, 14054, 1502, + 30494, 19860, 15750, 32226, 3496, 10100, 2982, 25473, 17161, 20952, + 13610, 21539, 29657, 22655, 29221, 21916, 17521, 27895, 4674, 3069, + 20326, 27405, 16227, 432, 7504, 392, 21383, 13764, 18896, 21271, + 17905, 25733, 31185, 9095, 29575, 15952, 1805, 25913, 18040, 165, + 1235, 15060, 289, 9598, 520, 16695, 12338, 7391, 11373, 13746, + 2060, 17583, 14623, 19573, 12654, 4806, 13924, 19272, 18769, 9543, + 10916, 15485, 6160, 11568, 9398, 20003, 17356, 26415, 8542, 12669, + 527, 5816, 27155, 3763, 3247, 14164, 20741, 24774, 24054, 22588, + 28254, 19913, 30719, 22054, 17096, 20303, 15203, 26463, 25392, 16160, + 4374, 11802, 4554, 7216, 27531, 17672, 26710, 7121, 25375, 31272, + 30625, 23335, 29108, 11233, 78, 17628, 4508, 31822, 10454, 32534, + 17338, 20185, 21710, 30118, 16611, 17419, 361, 21635, 23178, 10146, + 19261, 1654, 20125, 1252, 330, 3094, 4778, 21812, 26936, 16119, + 23110, 31971, 6368, 21791, 23727, 2632, 2318, 14521, 7600, 27411, + 17797, 2062, 15813, 3038, 24132, 20912, 5504, 5637, 32264, 24824, + 8235, 16312, 18962, 11955, 31151, 25166, 28458, 18969, 29621, 19164, + 32740, 19967, 1546, 30983, 2082, 13901, 7752, 16406, 31194, 10673, + 4787, 32332, 22298, 2965, 15457, 24180, 15689, 27215, 31902, 22461, + 13743, 7576, 12686, 24520, 28472, 10560, 24084, 768, 17780, 14368, + 2291, 22314, 14563, 17231, 5939, 31804, 15472, 14745, 9671, 8051, + 31803, 15148, 9836, 13472, 411, 15765, 17364, 22979, 22628, 11243, + 20740, 6362, 6501, 22346, 18427, 31739, 32309, 19331, 7060, 10145, + 25136, 23570, 24749, 28484, 21692, 32081, 4745, 6188, 8401, 15851, + 30323, 16006, 28088, 29842, 25252, 4280, 23826, 30197, 27751, 21746, + 18973, 26348, 16763, 27909, 3635, 32502, 12473, 19577, 28307, 20198, + 9718, 5337, 5988, 24537, 7467, 19538, 18423, 15979, 20692, 25213, + 5197, 17963, 10133, 30584, 24537, 16716, 9507, 6459, 30086, 12445, + 25186, 27433, 24828, 20411, 20683, 27622, 27935, 10628, 19729, 19817, + 27609, 20688, 17668, 4946, 11034, 4444, 29877, 27517, 15133, 11527, + 32475, 29911, 27116, 31041, 25717, 26736, 6182, 28864, 8113, 22738, + 19200, 3444, 26125, 31634, 8074, 32446, 21948, 12580, 820, 4061, + 4683, 16115, 21904, 21161, 5252, 28593, 22361, 25219, 12691, 3564, + 2741, 21388, 7527, 27875, 26807, 8725, 27739, 29591, 4136, 13911, + 22550, 24101, 20871, 20580, 19375, 10578, 15438, 28766, 31509, 23656, + 4471, 3815, 2479, 9537, 16191, 14808, 1087, 31393, 18986, 26934, + 12812, 14537, 18247, 8290, 320, 27720, 19381, 12175, 4751, 16149, + 30054, 10617, 10265, 15323, 28963, 24917, 31634, 23444, 8656, 1283, + 16288, 4441, 19698, 10123, 20612, 18552, 28782, 20423, 22525, 2248, + 9619, 24413, 26729, 13625, 13659, 26097, 21294, 6319, 16878, 13464, + 15960, 13585, 10600, 17807, 15748, 2204, 1500, 19501, 29180, 15742, + 28313, 278, 30243, 30624, 24236, 32434, 6164, 13507, 25766, 16861, + 10118, 29390, 32671, 8928, 23283, 4198, 19834, 23200, 4089, 7104, + 28267, 26646, 27392, 947, 16823, 27135, 6685, 24338, 24507, 16597, + 16182, 15928, 30000, 3325, 17767, 15076, 25517, 23874, 9637, 14920, + 19309, 8578, 24225, 5468, 25171, 13735, 9199, 7032, 15403, 6361, + 2152, 8994, 26675, 9459, 10189, 7461, 2010, 23091, 5037, 24035, + 17095, 4917, 1928, 4331, 15389, 13602, 10292, 2766, 27902, 12325, + 21120, 27951, 2126, 25630, 15640, 20762, 19385, 15771, 29096, 27543, + 25079, 7940, 24900, 24702, 14926, 27157, 6152, 14220, 31065, 15761, + 21499, 10167, 26377, 15157, 825, 11455, 32233, 27115, 27986, 6918, + 32678, 2113, 3053, 9633, 13006, 25369, 22193, 2735, 11736, 1186, + 1637, 23017, 21349, 20359, 4949, 2427, 25541, 23142, 29432, 19731, + 18365, 8067, 22737, 25399, 30855, 21694, 32403, 19675, 10523, 32706, + 14216, 9011, 5293, 6784, 21299, 32710, 5751, 23516, 4917, 10948, + 28344, 30885, 14445, 921, 22781, 28875, 27491, 22466, 22431, 15564, + 999, 7135, 22669, 28528, 29685, 15114, 5963, 8118, 23004, 28776, + 24635, 12046, 32159, 24317, 6428, 16328, 15772, 23493, 13008, 28621, + 11883, 7680, 26722, 11149, 29193, 18893, 28487, 11576, 2614, 2298, + 31972, 31793, 10779, 9755, 17745, 192, 19248, 24025, 22365, 7, + 2397, 23181, 31186, 5175, 2656, 3598, 28154, 29955, 29806, 24006, + 15201, 9301, 14365, 21772, 26845, 21312, 12673, 20910, 18124, 4919, + 25749, 24268, 11933, 10108, 22749, 12781, 23442, 2778, 22602, 32332, + 16579, 32151, 5470, 4723, 103, 12197, 31888, 29707, 26703, 23320, + 13554, 28147, 6067, 25309, 5137, 17551, 12849, 18856, 2333, 1123, + 17427, 5422, 17226, 7904, 15313, 29077, 6005, 18813, 7382, 10536, + 26891, 21258, 20359, 17488, 6695, 25409, 12930, 26047, 18170, 22687, + 20771, 14063, 20592, 14450, 25421, 30923, 31248, 25906, 31335, 3244, + 7973, 3860, 32676, 2438, 17179, 4829, 26504, 12644, 299, 15092, + 16040, 23313, 7995, 5820, 29617, 9240, 18431, 23584, 683, 32495, + 12389, 1904, 25983, 29040, 11312, 11627, 28087, 6024, 30178, 13213, + 27526, 15923, 11744, 25323, 17928, 10920, 21713, 2156, 26826, 25040, + 17735, 6266, 10893, 1021, 28833, 2057, 4423, 4313, 25392, 20536, + 5995, 10141, 21400, 20518, 22122, 6019, 16761, 263, 14970, 21629, + 26447, 12407, 9110, 9255, 919, 13581, 6086, 5882, 15668, 20788, + 27296, 8643, 10000, 14390, 32271, 27476, 9990, 27034, 83, 8826, + 17408, 22192, 19273, 28719, 8028, 1642, 18669, 23056, 7573, 11117, + 285, 10513, 543, 24680, 13472, 10351, 658, 32421, 10598, 20710, + 5387, 17118, 3089, 13349, 32207, 13393, 2741, 15487, 24626, 534, + 3292, 5347, 22200, 19121, 11470, 19726, 22137, 484, 22031, 3583, + 25034, 26823, 11560, 29355, 19993, 19482, 9386, 22771, 8473, 31961, + 25457, 16083, 18720, 14097, 24614, 32732, 13517, 27379, 22023, 1764, + 3565, 7206, 30443, 18428, 7071, 19298, 32384, 5794, 4023, 7739, + 2776, 11320, 17249, 14650, 24100, 29365, 9991, 50, 28095, 26523, + 26996, 12497, 25198, 11049, 6713, 2934, 28134, 4049, 19279, 32322, + 6542, 15852, 30207, 1650, 29319, 16301, 9983, 15340, 14058, 2387, + 13790, 15127, 26704, 20899, 2977, 24816, 26520, 20118, 15535, 3419, + 32228, 10831, 31210, 24502, 30437, 17487, 7455, 8628, 12377, 4844, + 21664, 21609, 17105, 14591, 12847, 5025, 10168, 9182, 8478, 17270, + 145, 17802, 1973, 15394, 25472, 16169, 6100, 26641, 20748, 4022, + 4825, 2107, 14115, 6903, 1220, 4784, 9566, 7343, 15002, 24436, + 5767, 23574, 30727, 6030, 29529, 13190, 12358, 6748, 12934, 16503, + 17990, 19753, 14847, 20397, 6149, 2305, 25967, 22960, 22848, 6852, + 22024, 9085, 335, 2426, 28547, 22395, 20555, 5862, 9826, 10928, + 12145, 11870, 889, 25141, 28875, 13786, 172, 19687, 12813, 30026, + 8954, 1376, 26301, 2093, 30139, 4134, 19862, 3423, 19237, 10785, + 21807, 2354, 26133, 23379, 13272, 11713, 28941, 8643, 14020, 32129, + 26232, 18618, 3182, 31769, 20022, 16434, 3637, 24948, 29780, 2188, + 19785, 788, 32027, 1665, 26315, 26537, 31309, 21339, 25392, 14562, + 26027, 23909, 9447, 1923, 9402, 27808, 10811, 5560, 31175, 24503, + 9850, 7080, 1577, 13621, 13911, 14926, 16838, 13558, 13197, 4228, + 3033, 19931, 27589, 9825, 14578, 31682, 16058, 8901, 16958, 18870, + 26523, 32562, 31240, 17506, 30129, 11742, 27008, 28876, 14401, 26118, + 15291, 6976, 8628, 2692, 23036, 4745, 26101, 24228, 7474, 9064, + 22555, 32052, 14529, 26686, 19272, 17882, 17029, 16652, 31440, 13483, + 31764, 1949, 15774, 22520, 16708, 21506, 2260, 22635, 16366, 25919, + 1458, 18259, 23793, 580, 16057, 10045, 27257, 26399, 10256, 25284, + 4993, 4719, 1830, 9642, 8342, 1820, 32153, 19495, 31198, 22085, + 15191, 23680, 2650, 1402, 7171, 16092, 26728, 9516, 13210, 10068, + 26508, 11105, 3670, 26185, 30575, 26284, 10194, 4858, 29736, 7517, + 23920, 17493, 19387, 11509, 24568, 19470, 7391, 15837, 26753, 10223, + 13061, 24407, 13461, 7535, 917, 8116, 3472, 13189, 4219, 29896, + 24393, 32446, 21474, 2203, 8692, 29582, 6463, 15969, 6983, 13027, + 26514, 940, 15068, 1280, 24809, 16865, 5591, 30345, 15141, 1000, + 17864, 19418, 13824, 9256, 11987, 16029, 12879, 5811, 28942, 14748, + 20101, 9149, 2346, 1747, 11385, 9811, 25686, 9162, 17513, 9034, + 27488, 12322, 26942, 19573, 18717, 10861, 25105, 17435, 28684, 28483, + 15599, 13479, 9014, 11922, 16089, 4258, 1624, 19253, 5391, 3807, + 31044, 22728, 11151, 14069, 17029, 11195, 19672, 4594, 19778, 12198, + 28403, 21326, 32357, 30684, 20916, 3614, 25851, 15939, 21054, 19082, + 18910, 17507, 6085, 3969, 25234, 19132, 32152, 13484, 11118, 4701, + 24158, 4256, 11516, 7346, 9375, 22359, 12450, 7582, 17543, 15585, + 10369, 21449, 743, 12375, 20125, 15164, 11645, 17169, 16190, 20011, + 10357, 4120, 4759, 18449, 29999, 14302, 14758, 7939, 5699, 17453, + 8795, 10045, 29891, 2142, 26364, 5378, 10944, 20939, 3272, 20643, + 22287, 31879, 5595, 20701, 24140, 16460, 27217, 28519, 15648, 18868, + 23726, 27596, 20416, 29612, 19838, 4215, 12982, 13606, 12856, 11788, + 8420, 5903, 872, 24831, 29382, 18049, 22029, 21855, 8951, 5945, + 9280, 31986, 31735, 2635, 2327, 27527, 21291, 11540, 29641, 19848, + 29989, 25713, 5651, 11471, 10022, 30766, 4570, 30650, 25057, 29378, + 10769, 32386, 10626, 10553, 31453, 23240, 9967, 32625, 31991, 30065, + 26190, 15497, 22611, 7860, 24263, 919, 18943, 27761, 10123, 17561, + 8525, 21189, 32277, 14767, 6196, 10971, 432, 17543, 2804, 7173, + 18489, 16408, 21241, 6923, 12326, 6803, 18690, 29635, 5063, 19350, + 12930, 4321, 16844, 31395, 7750, 30225, 24328, 27090, 29785, 8259, + 5492, 30914, 15653, 16454, 13431, 21461, 25562, 921, 9828, 9117, + 30740, 24446, 24389, 20625, 12984, 4687, 10455, 12037, 16926, 20245, + 28629, 10073, 8566, 18263, 30329, 11888, 32612, 1840, 4161, 8803, + 30702, 22436, 19183, 24439, 9624, 6964, 10451, 8525, 19917, 22704, + 20556, 1113, 11393, 26701, 21645, 15253, 8981, 30456, 31645, 20087, + 20091, 21481, 9633, 29472, 18586, 15482, 14686, 22742, 6388, 6714, + 14842, 30388, 6278, 26429, 27138, 12960, 19695, 11447, 8114, 29822, + 32469, 20425, 11888, 31139, 3973, 8347, 2457, 31885, 20953, 24491, + 1152, 1233, 27231, 11621, 21428, 29524, 31415, 6131, 13186, 7870, + 7229, 15190, 22459, 5164, 28137, 10166, 2232, 20518, 14707, 502, + 4506, 976, 29626, 4748, 25558, 14948, 7270, 13968, 5106, 29407, + 16588, 26386, 5001, 507, 9747, 19121, 19605, 20574, 24213, 1385, + 494, 18705, 20104, 15021, 6413, 3123, 25566, 30134, 12566, 14831, + 1385, 27674, 11030, 27051, 32067, 20128, 1928, 8578, 25197, 7251, + 15647, 25077, 17846, 11221, 27839, 6621, 9695, 24310, 5836, 5796, + 28412, 13445, 13736, 17589, 2333, 11367, 24951, 21553, 14894, 12773, + 24870, 5604, 24348, 74, 19627, 9335, 16442, 13742, 9358, 11751, + 1250, 2061, 680, 28982, 2655, 20636, 18475, 17504, 8714, 24924, + 27874, 13968, 31412, 22767, 21434, 8852, 14660, 26795, 18562, 17806, + 1185, 16621, 2799, 11757, 2259, 6063, 2787, 5788, 13669, 32093, + 14830, 633, 15117, 29669, 5654, 11273, 3215, 31758, 20947, 7530, + 26334, 3338, 15710, 1161, 9348, 10830, 21803, 6037, 24645, 2977, + 6580, 7603, 11007, 2931, 5241, 28659, 22741, 20337, 11845, 1666, + 10636, 24406, 15026, 26146, 20533, 7684, 23791, 472, 7693, 22739, + 10689, 25828, 21665, 24873, 10952, 14582, 25398, 25675, 9988, 31903, + 4967, 30066, 21656, 32262, 26896, 32290, 22477, 28124, 14699, 2231, + 13145, 7826, 15737, 6465, 3554, 17353, 9124, 2193, 28862, 17930, + 25762, 5524, 25597, 32439, 24476, 8416, 2985, 28737, 400, 18061, + 10828, 20124, 31732, 19014, 11443, 27632, 20124, 2463, 8946, 23954, + 10461, 26793, 19112, 27954, 26099, 27845, 3716, 10838, 10400, 31157, + 7944, 12295, 23062, 25849, 2189, 29385, 4119, 21336, 27272, 10961, + 7771, 5309, 13323, 11891, 22322, 29252, 2447, 20698, 16234, 8853, + 14320, 6395, 14707, 22641, 22713, 25803, 27585, 8074, 2978, 19632, + 26226, 31640, 26967, 23350, 19505, 32169, 4987, 27908, 602, 17336, + 1517, 19726, 11358, 5762, 3402, 24844, 30314, 6549, 1715, 31274, + 29507, 16810, 19664, 7681, 4469, 1332, 30228, 23494, 27564, 31545, + 18499, 449, 30818, 23649, 1330, 18177, 23738, 6733, 29903, 15695, + 26284, 297, 20576, 8032, 29283, 6021, 29456, 20303, 16099, 12384, + 29607, 6474, 30191, 25478, 2969, 3723, 27434, 24605, 28482, 3209, + 22140, 19834, 31600, 17520, 3511, 11833, 23586, 22223, 379, 31116, + 4239, 22417, 343, 24275, 28778, 20015, 2864, 26477, 5733, 1572, + 10727, 27342, 21492, 17312, 10038, 2258, 4691, 12416, 5339, 7761, + 17610, 5502, 28921, 26612, 20528, 13221, 27067, 18247, 2990, 21001, + 1082, 24079, 16069, 21428, 10913, 29661, 16530, 27107, 25999, 23011, + 7249, 13939, 17596, 16647, 24353, 29334, 15072, 13878, 29446, 28106, + 28880, 14587, 11060, 30988, 21482, 8272, 25995, 4680, 25524, 16820, + 25950, 25621, 704, 3709, 19320, 28063, 20179, 7024, 20321, 26340, + 17477, 18063, 7645, 13604, 12459, 28466, 182, 914, 14516, 29785, + 13243, 21584, 30033, 27969, 2825, 7578, 27673, 20533, 20105, 18242, + 26347, 31655, 28346, 7260, 28373, 21887, 30385, 1338, 28212, 29875, + 24414, 7579, 15901, 2328, 10256, 31519, 8956, 30072, 22587, 14334, + 31867, 20452, 20090, 5626, 11430, 15435, 208, 19858, 31308, 21836, + 4659, 23929, 29352, 5961, 24907, 28230, 3648, 445, 29815, 7789, + 7438, 25295, 26385, 19054, 15838, 295, 15347, 9548, 25847, 6144, + 14649, 13633, 13821, 22910, 12617, 2224, 13590, 27458, 23825, 14171, + 4940, 4413, 11152, 982, 6707, 31425, 10904, 12604, 31435, 26078, + 27858, 24864, 935, 3307, 4354, 3526, 591, 4707, 20108, 29843, + 21476, 24307, 11064, 1219, 26882, 9680, 15922, 9818, 16618, 846, + 9301, 27997, 8161, 27055, 2472, 3393, 1235, 20536, 27689, 8356, + 8764, 22667, 31690, 6459, 24137, 24112, 15290, 27660, 7738, 9333, + 18661, 19933, 24842, 5752, 27070, 5100, 27986, 19842, 19260, 3296, + 5739, 20886, 3874, 26112, 23762, 18506, 3972, 28835, 28113, 1494, + 31623, 23527, 18012, 13419, 12648, 13990, 32058, 1478, 31168, 19878, + 30821, 8964, 2054, 14864, 4530, 20152, 25685, 13348, 28340, 27963, + 8815, 32298, 4062, 26435, 2563, 31660, 23273, 6839, 5163, 18658, + 9333, 19906, 25145, 26222, 31987, 22567, 4531, 19786, 17602, 30420, + 4508, 32510, 19352, 32185, 23797, 26134, 32548, 23514, 16004, 6867, + 27890, 9635, 468, 24609, 7054, 6524, 13693, 27849, 32492, 21150, + 25253, 21736, 3079, 7717, 24007, 10144, 12233, 9830, 5300, 5389, + 11563, 26754, 32215, 12410, 9885, 10300, 3260, 8917, 22184, 22023, + 20436, 24929, 15314, 7011, 7026, 15531, 15298, 362, 26138, 23487, + 420, 26022, 14843, 30619, 7119, 12582, 24506, 27364, 7333, 1489, + 9841, 27096, 17083, 22010, 27896, 30340, 27707, 6708, 20579, 25911, + 22918, 6503, 9163, 1334, 797, 30499, 5458, 16817, 1729, 31797, + 9313, 20214, 28633, 19832, 19764, 14309, 24341, 1262, 3481, 24484, + 21841, 24847, 9972, 19348, 9660, 32353, 16775, 18819, 21305, 20297, + 21744, 17955, 23813, 17953, 31219, 26554, 16026, 25994, 2426, 16668, + 9940, 11012, 11482, 12503, 10835, 1980, 17240, 16557, 29984, 23460, + 16620, 6471, 16275, 9929, 27205, 24103, 2676, 25826, 4620, 1503, + 26208, 18212, 15213, 640, 19054, 16478, 17876, 20054, 15891, 5953, + 1524, 12498, 23107, 29439, 28686, 20424, 26073, 5657, 14192, 24795, + 4409, 15217, 1852, 3893, 5428, 18855, 14442, 27003, 14636, 20642, + 10512, 27647, 1144, 1389, 6369, 31544, 7945, 12357, 12020, 25200, + 9550, 16624, 3272, 10941, 29526, 32080, 4740, 24114, 11061, 32311, + 14467, 2902, 31400, 28285, 8719, 7381, 25596, 16464, 19821, 22902, + 3627, 19660, 1307, 8269, 24649, 4555, 30293, 13618, 27678, 511, + 4317, 12658, 27669, 6974, 5116, 27844, 22613, 14108, 27844, 29310, + 22317, 18879, 18841, 22280, 3253, 751, 1776, 401, 3506, 1389, + 22869, 15874, 31163, 10374, 22707, 11056, 154, 30695, 20688, 22870, + 22261, 26035, 31511, 11458, 24110, 25299, 1434, 22734, 903, 20878, + 2903, 1810, 6794, 1625, 25018, 32472, 13992, 19184, 24420, 24784, + 18464, 6053, 43, 19206, 34, 30456, 2658, 30992, 16436, 18281, + 6490, 13452, 6358, 20362, 12584, 10698, 7009, 22616, 16297, 29893, + 821, 16843, 11972, 31320, 31251, 17836, 27631, 25006, 31688, 23719, + 2293, 16891, 7436, 31137, 23030, 22633, 14761, 3916, 13487, 23126, + 19936, 5112, 13563, 14268, 32520, 30636, 10906, 25072, 6008, 16501, + 5170, 27280, 31143, 16638, 19347, 3766, 20114, 7521, 18239, 626, + 29534, 4669, 5418, 4216, 27199, 12809, 29374, 25109, 21144, 3239, + 9762, 872, 11364, 30012, 31932, 30670, 27348, 23104, 4853, 29342, + 6713, 25553, 18748, 2071, 7461, 19533, 2637, 27286, 17613, 10015, + 18099, 2060, 18610, 2521, 18827, 7088, 24961, 19332, 19084, 2033, + 2871, 20019, 24900, 3247, 12936, 1759, 15780, 11690, 20866, 14190, + 18879, 23867, 16238, 3861, 6351, 16194, 14327, 9440, 18297, 6239, + 23950, 143, 12626, 15717, 15116, 9636, 8097, 5899, 2201, 21690, + 21863, 3557, 14285, 30488, 7324, 29331, 2838, 26758, 14524, 13881, + 15450, 4960, 25218, 1839, 30429, 4840, 12531, 9721, 10340, 20448, + 5023, 1657, 28059, 19289, 16932, 4451, 7604, 3014, 17651, 6642, + 14673, 24660, 31575, 23513, 16548, 28286, 17075, 5501, 13524, 15835, + 8971, 4705, 18595, 13883, 9413, 14817, 1103, 17395, 13233, 30244, + 24757, 5749, 13128, 1868, 7612, 11556, 10289, 5871, 22513, 9830, + 6922, 8139, 8559, 1274, 6244, 32181, 8706, 24460, 29199, 14803, + 652, 11910, 9625, 26379, 10151, 11306, 3089, 8891, 32651, 31454, + 14096, 3499, 26419, 1478, 7931, 20800, 27323, 20376, 1666, 1266, + 25869, 9132, 14188, 2047, 17084, 23891, 31895, 9057, 27696, 14698, + 32058, 2046, 6916, 26159, 23918, 32032, 7852, 17747, 18413, 21284, + 21595, 5200, 17689, 28278, 22051, 28412, 832, 32445, 9567, 24670, + 29726, 13906, 21582, 30145, 25507, 5623, 29440, 2056, 27639, 8390, + 17270, 12097, 27324, 3175, 30981, 5245, 23443, 16774, 1787, 5598, + 15329, 16516, 17029, 28913, 23507, 15981, 23504, 26430, 16543, 3627, + 29295, 4382, 31757, 10745, 18685, 21197, 7479, 28954, 23555, 10176, + 7820, 28106, 24603, 14393, 29979, 14197, 27478, 27928, 9487, 17703, + 19932, 30196, 32629, 17476, 24018, 30058, 27974, 18297, 14977, 18357, + 27553, 25074, 19124, 6015, 7512, 717, 31329, 13559, 5550, 9423, + 31628, 25380, 16479, 9814, 3689, 3961, 2785, 23951, 5313, 10153, + 13034, 15593, 29846, 445, 24114, 7114, 3594, 2659, 24442, 30592, + 18557, 11657, 19661, 16506, 31478, 11562, 19206, 1976, 13252, 23144, + 12946, 8320, 9061, 25820, 29047, 2643, 11942, 13354, 20117, 14111, + 27660, 32728, 29228, 438, 4211, 6496, 22431, 26751, 3829, 7299, + 3192, 24683, 28685, 31201, 24747, 21547, 9212, 4039, 27679, 9121, + 21612, 17736, 3434, 14228, 31060, 23439, 17885, 32167, 13595, 26995, + 5824, 24716, 15360, 13644, 17374, 4213, 19129, 24837, 8005, 12235, + 28762, 7283, 16967, 14371, 12153, 20965, 22468, 11118, 14992, 253, + 23468, 25967, 20386, 15294, 29631, 8563, 30498, 31205, 17815, 2108, + 10768, 13949, 28137, 23102, 9061, 2990, 32008, 2574, 24753, 16598, + 7338, 23319, 5635, 32475, 17135, 22671, 6409, 7871, 5791, 14505, + 1912, 18036, 26803, 12, 11438, 18418, 26392, 31109, 27287, 30344, + 27823, 22008, 10701, 26223, 4760, 2205, 5379, 19109, 26288, 13160, + 9123, 25848, 27480, 14281, 14262, 15457, 17529, 16278, 21253, 3476, + 13765, 21062, 10109, 10709, 22079, 19039, 1752, 14349, 7421, 16177, + 30074, 3101, 11052, 14021, 25753, 2162, 30550, 13329, 22433, 15834, + 15840, 30061, 30935, 15766, 23255, 30539, 24892, 20912, 28908, 3874, + 6068, 3983, 444, 12971, 2919, 30920, 23381, 16208, 3432, 7735, + 32502, 21986, 3477, 31055, 25879, 7818, 7870, 28590, 17065, 2306, + 1545, 13556, 13215, 23812, 22932, 32470, 28333, 12035, 29277, 16238, + 26192, 3354, 13663, 22997, 31502, 30786, 30718, 1268, 8959, 32199, + 933, 20631, 10892, 1277, 31918, 195, 29303, 9519, 12418, 13130, + 20252, 6104, 3841, 924, 27894, 6933, 21284, 8416, 24164, 22619, + 7904, 2628, 13997, 9061, 3662, 26001, 22082, 9585, 1546, 9218, + 19524, 18604, 16601, 26966, 28998, 30699, 24718, 26916, 8020, 1691, + 1582, 11887, 11060, 21755, 13650, 378, 747, 1161, 1741, 24541, + 5508, 20665, 28303, 15065, 26636, 24557, 6345, 28649, 31713, 8354, + 8786, 12185, 27181, 1767, 12362, 18075, 22701, 15892, 11005, 32024, + 11191, 8888, 8201, 68, 6523, 6355, 4036, 2613, 649, 4678, + 10712, 15349, 13203, 22919, 15664, 24022, 29812, 29849, 24344, 31237, + 2162, 13372, 3487, 1378, 960, 17249, 20800, 2703, 6727, 19168, + 8642, 2887, 19367, 32109, 4701, 3058, 4216, 8137, 2334, 32118, + 27861, 24885, 1421, 31055, 3099, 6188, 14988, 994, 18926, 14161, + 32642, 18702, 19581, 3751, 23737, 1797, 5127, 11520, 14085, 7002, + 9119, 30136, 24231, 26203, 10028, 16034, 1392, 30880, 15027, 2627, + 24618, 16100, 14041, 12862, 30515, 8212, 9891, 5648, 27243, 16806, + 19073, 8701, 11344, 26156, 19595, 16970, 30824, 6697, 2570, 5805, + 2272, 8995, 5690, 31234, 20338, 25849, 23118, 12863, 13708, 12663, + 30389, 23722, 10158, 13864, 18844, 27210, 28691, 31851, 16052, 16539, + 9365, 12233, 19509, 21126, 11211, 7814, 16675, 17807, 7540, 10358, + 18640, 30545, 536, 4114, 18338, 30503, 13804, 25035, 29974, 18477, + 29431, 26015, 22277, 32615, 18035, 20201, 550, 4671, 30070, 7805, + 17365, 26933, 5329, 27380, 6418, 2770, 22584, 837, 5837, 29405, + 23716, 8086, 10081, 27090, 29174, 5201, 16734, 28816, 10205, 15225, + 31627, 5202, 8697, 2111, 8747, 16096, 30529, 30928, 12672, 22279, + 8354, 23154, 22926, 17734, 15080, 5071, 28449, 12490, 8162, 27024, + 28177, 2205, 6849, 28974, 28869, 11546, 15298, 9733, 16630, 19876, + 3853, 21675, 26037, 20119, 15068, 20607, 21168, 19327, 18655, 10117, + 7345, 2440, 7904, 18545, 22375, 28214, 27299, 20225, 105, 20054, + 1296, 27280, 7032, 10549, 4243, 4841, 25604, 9231, 22212, 32601, + 14322, 22986, 10563, 5128, 8093, 21220, 18918, 14728, 5915, 14763, + 7703, 21167, 31937, 7852, 12663, 17570, 18665, 24941, 19400, 17715, + 29426, 4447, 7689, 25453, 10121, 23310, 19170, 32364, 13351, 21915, + 20035, 7795, 19419, 17516, 2121, 25656, 2378, 8482, 28513, 642, + 20980, 459, 11695, 30198, 32441, 12121, 11697, 28462, 28059, 84, + 23071, 8972, 24773, 19403, 5398, 30030, 20173, 2192, 10870, 12642, + 2102, 27813, 16777, 17787, 22073, 8433, 4581, 13284, 30404, 23863, + 24467, 11925, 6596, 73, 624, 4356, 17312, 30841, 31956, 11325, + 14597, 4177, 25970, 6453, 22505, 29572, 10367, 1371, 458, 35, + 25623, 11633, 31543, 14356, 25564, 31386, 25746, 26305, 32736, 30132, + 12308, 20237, 25285, 1255, 14609, 30092, 31522, 10110, 24702, 23283, + 7209, 9206, 21168, 15053, 4487, 23637, 23142, 8816, 10892, 15550, + 25866, 29744, 140, 30785, 7220, 10962, 31877, 2039, 14448, 28996, + 24577, 8271, 28993, 25227, 21822, 4259, 11878, 1532, 19085, 16655, + 24155, 23471, 7416, 17026, 5667, 3377, 22055, 9493, 24133, 5753, + 389, 2204, 20757, 31517, 23710, 7035, 28212, 23494, 26315, 31676, + 18266, 32738, 5138, 11870, 27219, 10136, 10056, 8431, 11336, 14849, + 30584, 26973, 14912, 9109, 9353, 14345, 4838, 30947, 32538, 12317, + 19853, 12678, 10161, 6688, 21105, 3924, 10517, 9415, 26688, 1496, + 4600, 10254, 15065, 13827, 20953, 7816, 16211, 21022, 23849, 18675, + 6465, 32259, 23725, 12304, 25908, 32653, 27683, 30898, 3202, 21325, + 11304, 15892, 1606, 31102, 11097, 26223, 10664, 3899, 27321, 9676, + 30047, 2032, 339, 8983, 28788, 6927, 8992, 29558, 2120, 14852, + 3882, 24537, 4056, 8810, 10482, 10449, 17472, 24703, 16655, 15525, + 31358, 21973, 20761, 5027, 23161, 6219, 11973, 9908, 19266, 15676, + 31313, 13363, 29849, 25846, 19491, 18175, 31155, 6766, 26029, 13263, + 20794, 23346, 27027, 198, 24214, 23454, 24336, 27464, 12395, 23379, + 1174, 11, 2146, 7653, 13748, 31089, 4141, 23508, 26685, 22549, + 24517, 16318, 1191, 27043, 14582, 17362, 7601, 12782, 7198, 904, + 19272, 27196, 6706, 21903, 11436, 2788, 2049, 16903, 21967, 4892, + 30397, 10481, 11262, 780, 5559, 11492, 30146, 15399, 24938, 13825, + 8713, 15290, 17339, 8489, 2689, 25541, 2431, 28525, 13637, 17931, + 20798, 2501, 22971, 9573, 22326, 27909, 11452, 8539, 17059, 21006, + 14326, 16373, 22590, 9793, 16527, 18895, 13828, 26682, 267, 11372, + 31800, 15546, 14960, 22220, 1963, 20452, 24744, 17473, 8507, 22189, + 26150, 10622, 18074, 17550, 27341, 30705, 28753, 2427, 21005, 11091, + 30980, 27673, 1103, 5501, 2551, 21548, 24467, 7293, 12497, 6478, + 6999, 7913, 672, 10727, 9147, 29299, 28702, 7769, 12260, 12478, + 12083, 3195, 6046, 18032, 23994, 12105, 11333, 30857, 16776, 14042, + 6373, 21491, 18858, 21650, 3488, 15193, 13551, 22424, 14928, 1853, + 423, 28042, 23662, 27717, 2729, 23408, 25651, 7126, 30107, 17135, + 29948, 21220, 22125, 18368, 17651, 19209, 11303, 10592, 8172, 16852, + 6885, 9480, 4493, 2659, 1823, 13279, 31410, 23860, 30254, 31087, + 5647, 23096, 22540, 30293, 12312, 28969, 7772, 21309, 5661, 9246, + 17578, 22542, 186, 22621, 11763, 25442, 32110, 10608, 31592, 30265, + 7050, 29307, 16495, 10222, 24653, 460, 14315, 5738, 17997, 10932, + 24253, 9879, 19480, 10645, 6027, 10519, 22044, 3645, 15295, 14135, + 26564, 8306, 15827, 23582, 30692, 29390, 16589, 24139, 25687, 6094, + 18693, 15678, 18663, 11557, 27182, 14196, 15724, 12630, 5780, 27801, + 16184, 22401, 29603, 28666, 18867, 19837, 22441, 24377, 25155, 10280, + 21725, 20804, 7023, 27131, 30111, 12679, 21315, 22498, 3585, 23393, + 12534, 4170, 18055, 2517, 9674, 6267, 17451, 28547, 30004, 21544, + 4516, 2774, 2109, 6980, 11752, 2686, 4136, 25112, 25584, 22049, + 15445, 10131, 20411, 3917, 1893, 7649, 13461, 7858, 24479, 16703, + 2201, 8590, 11922, 20749, 22901, 19304, 1020, 24356, 28269, 4097, + 26603, 26987, 19363, 10632, 6178, 12709, 31381, 20237, 13502, 18317, + 15335, 12088, 8218, 31013, 1635, 827, 5615, 7499, 30526, 19973, + 15808, 27789, 6044, 7096, 15477, 23614, 6999, 21854, 2374, 12233, + 31996, 31960, 2522, 13382, 6935, 25878, 14768, 23115, 15025, 14990, + 24767, 8709, 4886, 28646, 7513, 3463, 18140, 25332, 17749, 3425, + 28192, 19945, 25197, 7029, 5651, 22729, 19941, 30726, 22966, 24351, + 12142, 15634, 30689, 15854, 25417, 15330, 2698, 15850, 2991, 22751, + 17220, 28136, 20849, 22951, 20742, 404, 6108, 22880, 21294, 2146, + 10207, 22349, 32197, 4295, 5744, 5022, 21253, 2984, 27179, 11917, + 5656, 1043, 17468, 15782, 23331, 32557, 4224, 19302, 22659, 6251, + 231, 29960, 23895, 11781, 22557, 6812, 4414, 12712, 18281, 21849, + 24930, 29036, 25557, 10292, 16053, 27909, 30351, 7858, 4474, 2877, + 11407, 19962, 1544, 17314, 27002, 27694, 5834, 4219, 16838, 29179, + 30603, 15617, 24608, 31343, 17904, 25548, 23050, 21771, 9030, 12684, + 14281, 32282, 7038, 16377, 13182, 28993, 32118, 7437, 29587, 2646, + 2171, 21104, 17496, 26741, 4875, 308, 5358, 1873, 3807, 26050, + 15205, 713, 3625, 22548, 17441, 14524, 11802, 7205, 20610, 15645, + 2727, 5798, 18628, 21578, 8077, 14835, 25891, 26521, 14994, 16197, + 21059, 10292, 25358, 41, 29904, 21544, 23211, 1073, 4193, 20920, + 7017, 5865, 21923, 5367, 30437, 31244, 7091, 918, 15066, 2040, + 18635, 19194, 32488, 23136, 11809, 23697, 32660, 18753, 23308, 1127, + 10897, 22039, 11113, 19956, 16446, 22352, 5437, 11065, 11887, 30721, + 31132, 4619, 2160, 24536, 31945, 942, 16455, 5970, 10654, 12965, + 1338, 31789, 25646, 15049, 27537, 23073, 28586, 8089, 16797, 31691, + 2945, 1233, 13817, 18410, 27595, 21822, 13072, 16077, 4328, 19408, + 5087, 27660, 16996, 4849, 5629, 8311, 12574, 28181, 11480, 2124, + 1194, 8594, 6721, 29101, 22515, 3251, 28715, 22352, 26917, 10759, + 24495, 889, 202, 27135, 31060, 24357, 21555, 7096, 17038, 16776, + 26393, 22550, 17540, 21434, 12420, 8978, 30742, 23726, 1772, 14165, + 13087, 2403, 6046, 12293, 31479, 1374, 28002, 1247, 24637, 30551, + 19035, 3206, 18387, 13875, 22830, 31002, 17705, 18994, 31079, 781, + 27071, 3690, 11032, 13874, 11749, 1652, 24002, 5399, 22214, 15461, + 9849, 1685, 32164, 15103, 8516, 16805, 7269, 30761, 451, 11125, + 17231, 17309, 5908, 23484, 23568, 3553, 18166, 15178, 7681, 7032, + 17455, 28899, 6059, 30809, 1068, 4173, 29067, 16288, 14312, 16296, + 19986, 2948, 19471, 18174, 27376, 8585, 13410, 15899, 26514, 12022, + 1629, 19615, 21737, 10634, 10007, 12369, 29294, 10347, 4203, 32182, + 15459, 18783, 29013, 20195, 12123, 30270, 11678, 21647, 25852, 7848, + 188, 32108, 28164, 14835, 18420, 10168, 3466, 9575, 14273, 4366, + 14727, 12974, 121, 30851, 1172, 31366, 9774, 9966, 21749, 18361, + 4050, 20871, 3287, 11855, 4511, 2740, 2006, 8962, 30390, 13599, + 4546, 9970, 19975, 11532, 15011, 23450, 32368, 7146, 11258, 4867, + 26153, 10791, 25418, 12343, 2399, 841, 30167, 27641, 5151, 24635, + 16814, 20915, 13174, 3044, 12261, 2224, 2519, 5630, 27684, 15755, + 17084, 10278, 13669, 16360, 2448, 12591, 11255, 10337, 26573, 23228, + 16585, 6852, 20066, 32014, 10725, 30843, 20055, 1306, 59, 5963, + 17587, 223, 27296, 20546, 32158, 17910, 31254, 31466, 4803, 26219, + 6990, 10435, 2296, 26571, 16747, 13552, 28961, 28798, 11785, 31793, + 4177, 24407, 31358, 20753, 21299, 362, 18015, 97, 4265, 23070, + 12103, 31204, 24498, 12675, 30163, 29004, 1169, 23781, 29681, 2917, + 14636, 30113, 18523, 27537, 21682, 29239, 23390, 13684, 5760, 21921, + 8416, 22256, 6214, 26851, 15265, 11860, 11109, 13028, 26049, 24784, + 9628, 8828, 17027, 29997, 24804, 7886, 32080, 1722, 29336, 29144, + 23039, 9354, 24025, 16712, 6739, 12508, 17713, 2449, 23749, 21723, + 24438, 20287, 22660, 23688, 13298, 31367, 6843, 3828, 25024, 4387, + 10514, 22194, 30329, 16989, 11639, 20108, 6161, 31081, 3870, 1653, + 1531, 27127, 7366, 5311, 13716, 19209, 11327, 18318, 3518, 13314, + 21289, 2701, 1483, 20480, 7422, 21727, 11166, 23404, 25171, 13145, + 21772, 8144, 9562, 17134, 136, 25064, 6060, 12138, 10496, 30203, + 11589, 2660, 4354, 28267, 24784, 3693, 18723, 6284, 3366, 8846, + 30113, 15282, 17, 12786, 5959, 20110, 10837, 11231, 23441, 5818, + 4337, 16363, 14353, 26258, 25506, 10860, 21777, 27974, 23721, 9520, + 2938, 19757, 19918, 20590, 31221, 27740, 32421, 10237, 30031, 13176, + 16400, 22724, 22562, 11976, 22789, 2674, 30130, 17806, 12433, 29039, + 13333, 31640, 22813, 15634, 4503, 28946, 24817, 13844, 6492, 5278, + 3062, 8516, 2887, 15271, 23232, 14973, 15131, 5361, 715, 23509, + 27360, 10613, 15365, 9496, 9353, 24429, 1933, 14197, 22227, 13227, + 21435, 22902, 14704, 7470, 8544, 5942, 15461, 20215, 3663, 13006, + 1713, 19481, 26908, 30992, 18479, 528, 12835, 20088, 25277, 23891, + 11724, 2143, 11934, 3117, 27662, 26226, 24093, 25544, 26930, 16483, + 17523, 7051, 11226, 15326, 1003, 21157, 1089, 7871, 26436, 5098, + 20676, 4482, 27223, 19423, 27554, 26106, 13818, 25872, 9728, 13894, + 4899, 32426, 29744, 28674, 6481, 28997, 12082, 8122, 7207, 5634, + 3963, 4260, 7258, 982, 17654, 5262, 16334, 14117, 20553, 2069, + 5372, 22419, 345, 26392, 9623, 4119, 16144, 1388, 7279, 11601, + 31838, 16337, 661, 30237, 32381, 14629, 22464, 29288, 22650, 30680, + 2543, 5186, 16369, 30624, 4176, 30517, 7960, 878, 11617, 10709, + 2046, 27552, 7253, 13721, 27925, 27768, 11705, 22761, 17135, 22733, + 5975, 10104, 28485, 23808, 31579, 1236, 10557, 28210, 27316, 11580, + 12012, 14772, 26301, 16561, 29722, 10431, 9757, 1103, 11846, 19558, + 8428, 19934, 25066, 17864, 10263, 19877, 21761, 32394, 3077, 10878, + 2757, 17797, 5754, 9264, 11453, 9147, 23484, 12280, 1271, 32144, + 9973, 17257, 28051, 21559, 1497, 16759, 22913, 15210, 14216, 15822, + 23178, 22366, 694, 2664, 18581, 30454, 14089, 17246, 28195, 7735, + 24002, 19710, 24093, 5372, 23968, 30751, 22223, 19946, 5633, 16561, + 22068, 14759, 13643, 23192, 17268, 23111, 20106, 187, 20907, 11015, + 29275, 14935, 32748, 12350, 20232, 2714, 23174, 17296, 7156, 15981, + 24965, 28773, 21562, 19601, 14460, 29849, 1169, 31725, 898, 14710, + 12403, 17446, 5499, 22751, 4734, 7281, 21401, 717, 15386, 11429, + 29218, 22272, 22620, 5269, 28275, 1774, 5674, 15772, 27205, 21504, + 32160, 27257, 19503, 15869, 13491, 31069, 18796, 30150, 30484, 3139, + 15583, 26693, 17751, 2944, 28500, 30112, 3925, 13979, 13577, 10418, + 24015, 14174, 1133, 699, 16212, 27437, 27175, 12118, 31844, 22167, + 32346, 13484, 22229, 30507, 16167, 29672, 22646, 26509, 29812, 3563, + 29939, 2505, 236, 64, 760, 11238, 17479, 3666, 20633, 23231, + 29388, 19164, 22258, 22286, 1556, 21922, 20746, 24778, 11024, 8543, + 14846, 19635, 20814, 22014, 30299, 6187, 16247, 8972, 11467, 28487, + 20765, 5742, 4596, 22822, 16819, 19279, 11165, 32385, 10461, 1373, + 20242, 16004, 3051, 3125, 24266, 1500, 10399, 23323, 2888, 7186, + 23863, 30959, 6975, 23319, 20903, 3164, 32505, 1312, 17959, 83, + 13049, 11443, 10989, 19640, 13792, 25346, 25916, 14171, 5517, 8313, + 26013, 8489, 22408, 7059, 29549, 13220, 16545, 497, 20887, 656, + 16340, 18349, 8355, 30191, 112, 236, 8688, 4144, 26489, 31154, + 16445, 25764, 74, 32763, 366, 21041, 7294, 5127, 17805, 22090, + 11025, 29429, 30480, 3102, 2755, 12179, 4186, 11280, 23485, 19829, + 6788, 8040, 5239, 4676, 2396, 31816, 24025, 345, 6318, 32225, + 31393, 13706, 3779, 19686, 15876, 3396, 2088, 11329, 21912, 23771, + 12464, 24070, 27249, 6941, 3516, 21669, 23946, 15405, 15802, 1123, + 9063, 11919, 20716, 26891, 761, 11903, 8043, 5088, 15298, 25479, + 16491, 21975, 22330, 31824, 1728, 23043, 10608, 15545, 9890, 17489, + 17652, 12838, 26797, 10357, 26101, 538, 3627, 27940, 6311, 1801, + 19094, 28548, 7911, 12135, 30297, 31457, 22818, 21253, 24279, 26785, + 13939, 29341, 22446, 31866, 28683, 5548, 28989, 20706, 5078, 14975, + 30820, 26567, 8796, 3217, 9098, 13185, 17381, 21780, 4325, 11269, + 22752, 22990, 31221, 17499, 27013, 7718, 16466, 21586, 14522, 14975, + 14248, 19058, 32307, 15812, 20736, 14898, 3038, 19780, 2881, 6763, + 25214, 30401, 19702, 18445, 31125, 26819, 2897, 8465, 16008, 1330, + 25200, 19374, 3838, 548, 736, 32208, 12937, 3316, 6240, 22726, + 6024, 9108, 3559, 11578, 22348, 6180, 21224, 15207, 17785, 29379, + 1492, 17286, 3759, 9637, 10209, 31369, 10858, 18277, 15882, 14092, + 9255, 18333, 854, 27273, 32713, 20917, 4190, 15051, 15674, 20984, + 7177, 20389, 16213, 20717, 14747, 10467, 6660, 6372, 14318, 4225, + 2981, 22991, 1534, 32027, 9651, 16211, 16556, 30885, 26786, 11551, + 19899, 24412, 22282, 5501, 10271, 2812, 17416, 7718, 16759, 15924, + 24802, 1883, 7384, 25687, 19945, 2353, 1740, 31060, 12964, 6957, + 3738, 11752, 8490, 23056, 9416, 7186, 18640, 32756, 735, 8136, + 15364, 8856, 16494, 10216, 19833, 11257, 14673, 4977, 18565, 16674, + 10547, 28403, 2247, 2884, 8274, 9796, 13580, 5897, 13698, 32373, + 8563, 15910, 6517, 27652, 7376, 20272, 28473, 6483, 11599, 26544, + 3586, 12633, 90, 13433, 4138, 17048, 4540, 3129, 21366, 20047, + 19974, 6223, 20123, 22512, 13361, 32732, 24577, 2260, 14982, 18717, + 31174, 15406, 31419, 25848, 20230, 1854, 26975, 6356, 26202, 4551, + 7288, 15022, 13726, 21186, 25962, 29545, 11000, 1364, 16922, 31590, + 18197, 27134, 26964, 1365, 11446, 10260, 30314, 19275, 18796, 25456, + 30750, 29204, 31647, 23143, 26306, 14026, 22997, 8188, 17822, 6822, + 32561, 18004, 29586, 279, 314, 3434, 28413, 29713, 29407, 27140, + 4317, 680, 6327, 30950, 823, 28125, 12941, 12869, 30393, 6072, + 32283, 29054, 23454, 26775, 22294, 9480, 8658, 7877, 6251, 15015, + 17494, 15438, 8905, 7139, 9191, 1810, 11359, 20153, 29914, 20249, + 15847, 3728, 17483, 28454, 227, 29039, 22646, 15247, 17190, 28012, + 28773, 21323, 21399, 21096, 9932, 1869, 7218, 3065, 31793, 19472, + 7503, 16296, 19535, 26996, 11815, 7005, 12989, 23752, 9876, 26647, + 8907, 8488, 22040, 13536, 13997, 21001, 27681, 11777, 31195, 23570, + 13707, 29810, 12983, 12334, 21351, 2452, 28528, 11487, 20219, 8994, + 23079, 12757, 12041, 31589, 6225, 4543, 29269, 29096, 14685, 22113, + 4961, 3820, 30870, 2790, 31099, 17117, 22090, 120, 31481, 27908, + 19500, 16655, 21700, 1597, 8439, 9350, 15123, 32364, 8020, 15348, + 26489, 8812, 13310, 1849, 18466, 7752, 14116, 10455, 13402, 28590, + 25871, 3076, 19022, 23712, 18948, 12541, 23063, 17005, 9580, 5020, + 500, 20704, 2098, 24621, 24535, 1463, 12909, 27262, 31780, 31830, + 29759, 6965, 20917, 26045, 5628, 18278, 5921, 15544, 2959, 9829, + 11582, 2645, 30490, 23001, 22244, 20123, 16093, 15378, 7668, 24953, + 14937, 26781, 2457, 24573, 15029, 13077, 2510, 31175, 29280, 59, + 15393, 12988, 32760, 13641, 28086, 27903, 8299, 12676, 32398, 20253, + 19548, 1553, 2569, 23175, 17608, 24585, 30890, 17990, 21225, 22736, + 24347, 29162, 16218, 8354, 20081, 1219, 17533, 14190, 31152, 3412, + 26, 10216, 21650, 13573, 10312, 30938, 3972, 23576, 1937, 18618, + 5362, 12766, 1105, 18306, 27588, 7467, 14175, 700, 9001, 2311, + 21999, 14300, 11155, 27046, 29367, 8403, 16643, 3483, 23121, 28385, + 19078, 3912, 3809, 26539, 21247, 31385, 9975, 22669, 32552, 31474, + 5047, 21553, 9428, 9573, 4663, 28257, 26487, 13097, 19496, 27161, + 32231, 16455, 27577, 3553, 1380, 25239, 9912, 4697, 1495, 11499, + 1274, 24342, 2903, 29322, 26110, 2476, 30961, 24288, 2509, 19212, + 30889, 13683, 8484, 24337, 11452, 19993, 10453, 597, 1576, 8553, + 5227, 585, 25710, 31087, 3085, 4997, 25239, 6820, 16805, 25180, + 30069, 31004, 8513, 4532, 16171, 19970, 24761, 7188, 15344, 8514, + 12946, 19199, 11279, 7045, 8226, 14465, 24370, 13410, 20213, 3847, + 17633, 4306, 13680, 31604, 26182, 9701, 692, 1392, 11571, 29620, + 15813, 16899, 7270, 27799, 24556, 5220, 23819, 23704, 28249, 28939, + 17794, 25895, 22285, 22285, 15715, 24847, 5367, 29609, 8895, 17576, + 24413, 9246, 5289, 20046, 26750, 18880, 18165, 6931, 28534, 9291, + 17216, 29617, 10593, 9847, 15153, 26051, 17859, 30925, 20477, 32052, + 4648, 7864, 29489, 12791, 263, 29151, 17580, 24056, 6124, 26546, + 4586, 20307, 2262, 21827, 23177, 19141, 31454, 9560, 32261, 4703, + 17946, 13502, 30407, 17538, 9921, 18656, 18014, 9991, 11152, 21656, + 30476, 12990, 25452, 32580, 22148, 29466, 3656, 8733, 19954, 3626, + 21833, 12228, 31548, 2251, 20008, 14878, 28551, 6941, 19929, 2658, + 14078, 10547, 20074, 32624, 3534, 13327, 27693, 9829, 26229, 22221, + 15897, 16748, 9533, 9246, 3438, 12503, 13571, 30712, 12440, 29294, + 22845, 4492, 3414, 11014, 24892, 2425, 1563, 28246, 8366, 30783, + 22197, 14599, 20838, 24822, 9260, 20746, 16992, 13376, 17389, 46, + 30719, 32636, 32295, 23794, 8602, 7228, 20342, 5527, 2103, 29689, + 28095, 29050, 23190, 11270, 5211, 31905, 31346, 19907, 23415, 4500, + 12035, 16147, 7866, 13678, 24775, 7952, 14121, 31283, 17026, 23293, + 3144, 23396, 2948, 3963, 16514, 18641, 10026, 2560, 31947, 21423, + 32167, 32390, 8068, 12778, 14852, 17792, 21151, 3005, 12646, 13842, + 19715, 18303, 20368, 28801, 17202, 18368, 11432, 11497, 19241, 20372, + 12350, 25134, 14216, 303, 25994, 22836, 11762, 10930, 25845, 83, + 31927, 21829, 30428, 4869, 29616, 19046, 21392, 27106, 17515, 896, + 9377, 1743, 11656, 14272, 11100, 12658, 30051, 31780, 12434, 14313, + 30708, 10709, 8603, 10171, 19443, 32438, 32411, 4878, 17215, 11325, + 32260, 13851, 19072, 18562, 2396, 4320, 27659, 21909, 30529, 18708, + 31239, 21269, 9898, 26720, 9166, 3582, 24431, 19148, 31382, 8899, + 7180, 20856, 10577, 26013, 8354, 11660, 25554, 9506, 830, 22385, + 20784, 31513, 2398, 1602, 30063, 8077, 32433, 15229, 24157, 29216, + 26911, 14079, 6075, 11682, 15881, 26415, 10909, 31202, 31954, 10617, + 11284, 20420, 21150, 28414, 7945, 25492, 11097, 6537, 11628, 2458, + 21500, 9413, 31887, 18671, 4619, 28590, 8396, 4239, 21602, 14918, + 29806, 17070, 24882, 11145, 21967, 2640, 9543, 29602, 4285, 12052, + 2188, 24159, 30498, 23376, 27600, 27107, 27876, 2147, 13258, 13723, + 27403, 1351, 1371, 23923, 15906, 19302, 5908, 5449, 991, 5551, + 15352, 6305, 11270, 28136, 2947, 17051, 138, 9286, 19179, 25123, + 30965, 25915, 31653, 7784, 23819, 24975, 13099, 32655, 31402, 15538, + 8189, 28584, 29429, 13649, 24549, 18401, 25266, 15399, 2944, 15865, + 27119, 27926, 6906, 17912, 22252, 7798, 2073, 15390, 30773, 31405, + 20861, 27417, 23055, 22527, 17190, 17019, 29461, 17064, 10294, 20842, + 1059, 27944, 18936, 22937, 12147, 31455, 24610, 3704, 2822, 22745, + 22103, 21672, 12870, 6379, 2800, 5554, 6889, 767, 27388, 31736, + 12568, 20652, 3973, 10988, 32465, 18186, 16406, 18303, 25226, 8900, + 32377, 25821, 6423, 2670, 32709, 10583, 7885, 31513, 13977, 16288, + 11240, 25381, 21154, 26685, 20522, 24407, 22883, 16183, 29125, 20011, + 15413, 2579, 13179, 11628, 3980, 8707, 1540, 30209, 18301, 20062, + 26800, 27146, 7549, 9038, 7501, 2348, 876, 14537, 21323, 7976, + 18386, 19906, 3614, 8371, 20328, 11168, 19172, 19074, 2010, 24047, + 10568, 13988, 30667, 4693, 7078, 23572, 4121, 13512, 25200, 11407, + 8803, 7167, 22224, 17571, 8393, 23078, 18574, 25097, 2803, 1494, + 29493, 5172, 11349, 3908, 20081, 23338, 4017, 30137, 15804, 10387, + 19421, 28994, 25401, 30479, 3526, 25768, 6624, 27597, 949, 11170, + 23260, 13914, 3537, 6474, 4657, 8886, 22502, 15054, 9890, 4525, + 16484, 12904, 14546, 19300, 12459, 16016, 23977, 24627, 19077, 16752, + 22521, 2994, 3828, 23704, 5474, 19991, 25135, 29939, 29299, 12223, + 4327, 13049, 2577, 25354, 15054, 21287, 5149, 14444, 18010, 23212, + 5969, 6337, 16838, 1126, 29291, 31201, 21851, 31244, 142, 373, + 10989, 11693, 23781, 18953, 28328, 26457, 2441, 32354, 19725, 23326, + 32165, 16145, 4663, 23350, 24280, 1608, 20067, 31358, 27098, 32237, + 9135, 13098, 20067, 31725, 8482, 26039, 584, 6355, 20205, 17442, + 20455, 5273, 20464, 32088, 9158, 32189, 15488, 15597, 23927, 20120, + 6760, 21264, 10175, 29932, 5677, 6335, 7159, 30652, 24983, 25655, + 13489, 12128, 23404, 8623, 21407, 17368, 25727, 25669, 24241, 25953, + 1513, 825, 14462, 29510, 19607, 5977, 6117, 1273, 32261, 27836, + 756, 2402, 2632, 15539, 28936, 28791, 24402, 17405, 22582, 4877, + 370, 2362, 10730, 19693, 3354, 26530, 718, 32043, 1509, 12791, + 4590, 24755, 5848, 440, 31513, 31525, 10217, 1688, 14474, 483, + 27837, 27183, 16328, 32104, 20662, 7150, 28106, 5339, 25645, 6488, + 32351, 14307, 12028, 1647, 4813, 6846, 3136, 10861, 3000, 14477, + 29533, 9397, 14918, 20466, 21058, 28169, 11393, 3549, 23790, 31229, + 6397, 23203, 30258, 3244, 11706, 32081, 5561, 6313, 17995, 2367, + 20295, 14649, 19768, 18920, 24382, 7359, 19618, 8440, 24678, 29559, + 29221, 2727, 3821, 13927, 2563, 15430, 15533, 23187, 13694, 4375, + 23214, 17521, 24709, 14690, 15340, 3996, 14995, 24517, 30042, 32460, + 14037, 14540, 32550, 10246, 22411, 12482, 31551, 12560, 18608, 11206, + 17685, 12328, 3549, 3105, 11936, 1269, 18283, 29398, 3747, 15806, + 21208, 28283, 1074, 23770, 19051, 3144, 4978, 4712, 10267, 32622, + 32672, 15936, 4014, 21161, 1620, 20009, 1054, 22553, 24583, 19532, + 3136, 19278, 10707, 12776, 2727, 22946, 7291, 21064, 19655, 1227, + 13483, 29170, 5462, 5040, 12417, 24098, 9720, 1404, 1611, 6262, + 3098, 15089, 14483, 27371, 13598, 18944, 11097, 30728, 3513, 12182, + 24223, 7001, 12442, 21432, 12843, 11807, 26701, 5074, 5768, 1094, + 2142, 18252, 20302, 13719, 25025, 29279, 10915, 1839, 22129, 6951, + 20994, 2352, 20568, 29945, 6203, 15542, 8047, 22006, 26811, 22029, + 806, 10976, 15374, 28449, 25350, 23853, 13730, 15925, 31381, 24410, + 20174, 9432, 31866, 21378, 13074, 11029, 28897, 47, 12705, 15083, + 21281, 1904, 26652, 12285, 12956, 5164, 2721, 3977, 23271, 29109, + 27908, 31902, 18220, 16800, 12636, 15498, 25804, 17365, 16322, 24466, + 26952, 25826, 27477, 25063, 16225, 1685, 26754, 22741, 20350, 14025, + 30850, 60, 10063, 18560, 28195, 15428, 17584, 818, 28620, 25421, + 2000, 23653, 6843, 25637, 4242, 28542, 27538, 7294, 2867, 10199, + 4114, 29599, 12334, 20753, 1574, 3417, 31580, 428, 11299, 10557, + 24160, 5076, 9806, 524, 29250, 8318, 15775, 16751, 27738, 12468, + 7108, 23528, 23152, 6445, 2153, 26889, 27169, 13345, 2999, 19428, + 16260, 27995, 6682, 16322, 7950, 21648, 11592, 7448, 28339, 10735, + 28287, 27318, 21070, 6393, 22029, 3906, 26435, 5523, 25622, 14053, + 25443, 3930, 22162, 16708, 6928, 28470, 149, 19378, 12961, 4914, + 9939, 11171, 32520, 15499, 1379, 28004, 22961, 9222, 25283, 11018, + 27566, 10375, 21533, 20393, 13330, 19424, 5832, 6854, 19866, 29159, + 8978, 30734, 14227, 5579, 23613, 32584, 9719, 27048, 455, 12424, + 7386, 5208, 17903, 16549, 23300, 397, 11636, 2542, 22496, 30748, + 7328, 29977, 22677, 27292, 13701, 28284, 24690, 13872, 27527, 15232, + 143, 17313, 28698, 17490, 19121, 16048, 23757, 9691, 27503, 22442, + 16008, 8179, 12179, 17212, 26129, 13265, 30915, 27785, 8661, 1734, + 25953, 3450, 17638, 23852, 12207, 8533, 8000, 16793, 2103, 26002, + 19280, 11352, 28522, 23786, 29879, 16153, 21127, 2224, 31819, 26855, + 28606, 19556, 7518, 1627, 17814, 12166, 30788, 21007, 4492, 29096, + 9743, 14678, 8700, 9415, 14749, 26922, 32581, 16859, 20949, 10086, + 12329, 27565, 8493, 19510, 7538, 21481, 3695, 28619, 22747, 30324, + 21182, 8275, 19245, 7935, 14548, 13352, 3497, 27869, 24973, 3143, + 29350, 18705, 12713, 23301, 379, 684, 19661, 17149, 27039, 1366, + 16844, 25165, 12559, 24106, 19493, 7682, 8688, 711, 20173, 16848, + 15222, 3747, 27694, 18439, 10756, 27299, 24431, 24002, 2709, 4841, + 31478, 30561, 32090, 28398, 28715, 2362, 19364, 28624, 17535, 14476, + 9477, 26861, 6062, 6324, 14258, 22192, 8000, 18596, 10284, 1963, + 31574, 1168, 11296, 23526, 18595, 32746, 19595, 3703, 5049, 7057, + 4897, 15699, 4486, 19526, 9968, 9060, 16670, 17759, 7233, 14556, + 23227, 15295, 31679, 11720, 12289, 21765, 17627, 29416, 24967, 5892, + 28526, 17598, 3476, 27508, 12448, 27877, 23590, 3046, 1802, 11684, + 3129, 27134, 21015, 3011, 26181, 26588, 30896, 27293, 14345, 29915, + 25368, 11895, 15751, 25214, 19476, 22800, 6992, 1447, 11600, 21625, + 19969, 30730, 19198, 24615, 13070, 3554, 8896, 21575, 3774, 21474, + 16840, 12113, 13175, 10607, 17981, 8672, 11836, 19212, 4065, 3265, + 7284, 24854, 25586, 9546, 3821, 26289, 2126, 13293, 7712, 32002, + 6618, 8461, 5047, 32415, 8739, 17423, 19788, 26435, 12358, 10421, + 15476, 25543, 30990, 16039, 10236, 7757, 16989, 10067, 4722, 6847, + 7629, 5623, 27111, 14200, 13974, 30204, 25844, 9792, 14003, 19334, + 12539, 29054, 1856, 585, 21935, 25001, 28711, 7838, 29143, 22272, + 23291, 1238, 13647, 27515, 15652, 19150, 15439, 28929, 27965, 18371, + 12271, 8155, 26554, 22120, 20459, 2409, 18126, 32054, 18852, 8979, + 29068, 28932, 104, 32131, 17996, 9687, 10995, 31761, 17488, 25083, + 27190, 8319, 30887, 1875, 15703, 16103, 12882, 1773, 30456, 11761, + 16806, 13017, 6076, 18249, 18161, 25148, 12807, 19537, 21600, 7072, + 15110, 787, 26400, 14734, 27888, 8057, 31684, 15711, 7819, 19648, + 16871, 17487, 13297, 27434, 13074, 21203, 3220, 22003, 4320, 27181, + 4736, 17799, 3679, 7229, 20192, 8690, 10426, 4506, 10180, 22936, + 29461, 13793, 11041, 4134, 22969, 32333, 2841, 14792, 8305, 32392, + 12157, 30365, 8243, 16321, 145, 17000, 2051, 3583, 20239, 19592, + 9493, 12213, 32110, 3114, 30258, 15254, 20067, 25789, 3676, 170, + 31913, 28326, 2741, 13474, 32766, 14389, 8093, 23657, 9070, 4824, + 283, 8955, 20691, 16527, 10878, 4730, 13725, 5536, 18756, 25471, + 9423, 25228, 20282, 22190, 23114, 14913, 28645, 19597, 846, 26769, + 3361, 3990, 12350, 11491, 22280, 1162, 5027, 3369, 8981, 13577, + 12916, 21778, 16009, 26720, 27029, 5504, 16398, 16034, 27805, 14396, + 7323, 506, 8668, 31721, 919, 8767, 32669, 27702, 17985, 3785, + 20136, 10840, 20047, 25219, 30219, 6584, 26581, 5698, 13338, 17640, + 25193, 11605, 26805, 3804, 19845, 4190, 21705, 1111, 21549, 2656, + 29465, 24995, 31697, 7192, 20031, 8325, 19060, 6545, 30701, 8816, + 6158, 17907, 1401, 19193, 3887, 24987, 13721, 7819, 8422, 24177, + 9067, 14684, 28641, 11894, 10936, 12959, 30421, 24733, 24953, 16380, + 28193, 32732, 19098, 11581, 13486, 26754, 9727, 31818, 16409, 1995, + 453, 16727, 22548, 14770, 28874, 3932, 8457, 14598, 2671, 2245, + 14849, 21952, 6044, 26731, 29125, 26004, 4357, 5828, 22338, 8902, + 21164, 8022, 10546, 27967, 10407, 6770, 26873, 8672, 6436, 24350, + 5325, 31766, 4010, 4435, 19738, 13743, 19874, 24681, 26917, 22764, + 17429, 2511, 16111, 30935, 15031, 29695, 8796, 17366, 26661, 30129, + 24468, 9345, 24427, 7503, 13482, 25777, 30409, 3242, 3708, 1753, + 2999, 15595, 3290, 18842, 18470, 16076, 4586, 2449, 19721, 19208, + 24778, 30068, 26132, 14095, 3960, 18525, 10947, 8703, 20463, 32357, + 12447, 28112, 21651, 14197, 12989, 22734, 21455, 15449, 21127, 19277, + 28413, 23062, 21950, 11672, 134, 1918, 1472, 23082, 5450, 2305, + 5127, 33, 5442, 1734, 10227, 2543, 24419, 18683, 31177, 23635, + 23750, 3689, 7931, 392, 20231, 12619, 27562, 5308, 24043, 3680, + 3433, 451, 10309, 25151, 17220, 27917, 23578, 4331, 22273, 22485, + 6659, 2983, 15269, 21660, 10195, 18699, 30010, 24534, 13946, 856, + 11728, 12515, 16169, 12553, 7106, 5952, 15287, 27562, 7482, 14335, + 15940, 14573, 11511, 16244, 11292, 27131, 5198, 23346, 8157, 1288, + 32367, 4150, 14045, 27946, 20714, 16737, 19910, 15467, 28018, 31276, + 28803, 1053, 16608, 20887, 352, 26477, 17275, 31074, 23404, 17467, + 20086, 19840, 29730, 24105, 27322, 30665, 16457, 16370, 31974, 31727, + 24601, 18174, 29332, 16307, 22175, 3379, 21727, 25432, 20579, 9485, + 25634, 32344, 15687, 23537, 8449, 5207, 16818, 30398, 26286, 22101, + 17134, 7334, 31333, 1063, 4815, 25660, 13369, 552, 32406, 14029, + 9491, 30508, 17684, 32244, 24056, 1717, 14355, 20329, 15498, 31594, + 3313, 2842, 24786, 30148, 8334, 8061, 12348, 29451, 12905, 26660, + 2531, 16513, 23301, 23186, 4111, 11997, 16786, 30029, 11004, 6650, + 21705, 9524, 10462, 6353, 2840, 5395, 7969, 27045, 28159, 7029, + 28917, 28285, 29882, 15033, 7946, 19927, 29216, 26472, 17560, 2971, + 6603, 2246, 26019, 11854, 24697, 26535, 29309, 17544, 15282, 32644, + 9356, 24232, 1195, 26062, 6221, 20678, 619, 15234, 18585, 20713, + 24364, 23806, 20264, 17786, 3332, 17427, 3667, 22521, 24004, 20571, + 10270, 8175, 21025, 27085, 26229, 31268, 25617, 12686, 30902, 24593, + 20600, 13578, 10864, 21400, 9220, 22340, 30571, 13043, 3925, 25706, + 8424, 2283, 18018, 941, 10281, 11594, 28440, 31568, 21044, 18834, + 25412, 31486, 6357, 5615, 18866, 24672, 11490, 25195, 19094, 10735, + 28098, 16481, 25388, 3079, 31280, 24628, 7324, 23613, 15933, 30528, + 10784, 18163, 8852, 16913, 12904, 810, 16642, 30419, 18494, 30871, + 16950, 15370, 22118, 3064, 18629, 30596, 712, 1448, 3441, 2443, + 19665, 18033, 4266, 18527, 27414, 16034, 1365, 20451, 11322, 23394, + 22733, 31126, 24338, 24132, 31783, 4293, 1544, 21276, 21179, 5887, + 22973, 6160, 27103, 11466, 24307, 11250, 24054, 29332, 5033, 14029, + 4266, 30188, 30599, 189, 5237, 32498, 29565, 19204, 29030, 21004, + 25768, 16219, 10018, 14811, 20085, 27014, 4137, 28014, 30728, 28459, + 2650, 9909, 30943, 31204, 2234, 32056, 30898, 1939, 29297, 22792, + 9886, 5366, 29461, 4598, 11139, 22139, 8235, 3844, 7083, 29525, + 6123, 31257, 11180, 30415, 32068, 23545, 11407, 27616, 29984, 13427, + 4913, 22272, 2281, 25309, 3818, 571, 4684, 29373, 29549, 20356, + 5181, 8393, 31215, 1355, 9531, 11282, 7564, 18770, 20181, 18300, + 5862, 15804, 1645, 16241, 15119, 29799, 9020, 10198, 16809, 11387, + 30102, 9790, 13899, 3651, 25455, 11358, 6439, 22844, 11807, 14018, + 29418, 14444, 17762, 25534, 24548, 12533, 19363, 30374, 22121, 24298, + 12154, 6977, 11383, 29793, 32371, 32124, 8344, 11978, 21777, 2396, + 27146, 13951, 4114, 26422, 5150, 7324, 8949, 2082, 10462, 27408, + 14266, 22189, 28402, 31476, 31721, 24526, 16069, 1308, 16085, 2076, + 13027, 8428, 17698, 8509, 14246, 9581, 30085, 29602, 15889, 8352, + 17099, 4209, 5628, 22803, 30493, 32707, 13169, 3505, 17538, 7952, + 25431, 26590, 16037, 17383, 13133, 21444, 8665, 30435, 4313, 13031, + 2906, 23258, 25538, 7424, 1749, 5207, 10170, 8968, 5419, 17739, + 18752, 18579, 4211, 28927, 18885, 10079, 4414, 23629, 28430, 28973, + 14622, 15888, 473, 17359, 1641, 21465, 24515, 899, 5434, 12004, + 25705, 12159, 29113, 19786, 28110, 25805, 24476, 21367, 32765, 857, + 15086, 31199, 19439, 10569, 22031, 749, 7598, 30860, 30973, 19772, + 30139, 23222, 26065, 22026, 20909, 14091, 1677, 16362, 26652, 20185, + 10260, 12728, 23485, 13324, 26234, 8658, 10702, 3580, 11902, 24965, + 22453, 8329, 30805, 31942, 9188, 18794, 8106, 16931, 15871, 26562, + 31970, 1785, 19009, 25076, 9124, 31718, 13340, 15801, 27289, 9601, + 5151, 32368, 8304, 19785, 11549, 15797, 4646, 32476, 17548, 12852, + 18052, 25962, 23083, 6335, 4841, 7528, 744, 13215, 27977, 20105, + 27392, 21544, 8054, 8682, 17256, 8849, 16286, 26764, 7788, 6146, + 4629, 1544, 32662, 24301, 14421, 29566, 8913, 3490, 20804, 28367, + 16078, 28446, 28341, 10764, 18087, 20004, 1072, 3886, 28354, 30352, + 13046, 27655, 30969, 24807, 5952, 6776, 3063, 15410, 31432, 30949, + 6629, 18957, 4234, 18150, 6727, 16635, 7628, 14911, 6944, 10492, + 13404, 24595, 27997, 26210, 25113, 7764, 28784, 26336, 31156, 27006, + 16700, 9444, 22712, 645, 20529, 29990, 20168, 20747, 8888, 21121, + 2600, 11735, 17361, 7171, 11648, 12675, 10234, 31040, 29404, 23412, + 2132, 22763, 32758, 9488, 30977, 15783, 20408, 21802, 16080, 19738, + 21757, 10177, 10268, 21827, 15000, 7659, 27889, 15374, 25751, 2572, + 23153, 3366, 5833, 11339, 19830, 6667, 25725, 29511, 16505, 24676, + 19853, 13573, 28851, 10902, 11419, 29742, 24193, 7829, 23195, 1399, + 11533, 12027, 11153, 10592, 9515, 8791, 10396, 28193, 24449, 9415, + 11060, 28129, 23019, 6089, 15792, 15658, 31774, 5198, 25867, 7578, + 20363, 21264, 14311, 18651, 28294, 28454, 26322, 26581, 28327, 14037, + 6601, 2593, 19056, 32426, 18170, 12737, 3633, 10949, 26318, 18191, + 19497, 25208, 10535, 2292, 7150, 8721, 4071, 19802, 10511, 7923, + 4785, 9700, 5367, 12780, 12275, 15580, 12308, 15298, 3876, 32365, + 20025, 14351, 6218, 25106, 14913, 29263, 9408, 26702, 3318, 4408, + 11574, 8350, 27441, 27722, 6393, 19372, 28482, 24486, 4725, 28101, + 16785, 4643, 1272, 25997, 9350, 24991, 10168, 23281, 30631, 27027, + 14872, 6750, 30208, 22506, 21799, 12336, 13256, 1044, 30313, 22638, + 8001, 31830, 10203, 5812, 2272, 32100, 26929, 2672, 30382, 2097, + 15970, 21329, 18158, 2566, 1894, 6653, 26112, 19154, 1342, 4355, + 7045, 5569, 12342, 528, 12996, 20495, 17255, 15205, 589, 29215, + 8575, 29064, 5851, 5831, 30405, 26938, 1407, 5358, 15447, 32170, + 10491, 22362, 182, 22248, 20076, 26307, 6533, 25756, 5421, 23084, + 8846, 28920, 1527, 8563, 28166, 24555, 9549, 24102, 18962, 28312, + 1303, 16024, 16266, 1798, 6541, 3892, 11772, 31769, 21846, 30226, + 26868, 16833, 20721, 3272, 14970, 30065, 341, 19587, 28381, 19155, + 16852, 86, 19887, 12474, 13637, 12753, 9233, 16984, 16994, 17366, + 17457, 22705, 12143, 12200, 7696, 20559, 8477, 6213, 21897, 22327, + 173, 10230, 31119, 22301, 3416, 27684, 16566, 20436, 7407, 9069, + 28404, 13832, 20415, 12791, 18601, 10799, 15287, 12292, 14046, 7101, + 21197, 18119, 202, 11415, 2205, 20343, 16027, 26827, 31571, 26785, + 26995, 9854, 9164, 30146, 11440, 8742, 20728, 15637, 23664, 11001, + 29315, 6058, 13652, 29604, 30388, 26552, 18821, 23361, 16277, 18740, + 16117, 17027, 29524, 12450, 17128, 16692, 11241, 27692, 22194, 20450, + 18937, 15089, 22978, 14805, 28410, 28319, 29919, 27460, 25241, 19268, + 19122, 28616, 26456, 6497, 4485, 13584, 17020, 7745, 31306, 26071, + 31526, 28296, 2271, 28947, 5312, 20239, 16784, 4463, 18586, 6127, + 20446, 26876, 725, 19932, 2686, 1315, 9857, 26037, 30885, 24287, + 23810, 26200, 15491, 11049, 29880, 23739, 1805, 4019, 4831, 22073, + 30976, 4056, 15539, 21801, 5411, 17875, 31704, 29621, 10188, 15408, + 8698, 10474, 18341, 31617, 28593, 25611, 21424, 8274, 8871, 29227, + 4745, 3423, 12028, 25082, 22252, 3588, 10217, 23008, 19830, 31988, + 7076, 5192, 22448, 17198, 26244, 30271, 6217, 17197, 16742, 29757, + 5283, 1119, 3919, 14658, 3986, 13017, 11859, 17445, 352, 18249, + 20405, 28457, 20756, 27436, 6991, 5586, 13043, 27969, 8756, 19703, + 20552, 23101, 25749, 24855, 7265, 3520, 28385, 3825, 31450, 20360, + 21040, 10830, 6942, 20709, 31714, 10018, 11562, 7788, 12419, 14547, + 21396, 23024, 11762, 31705, 30412, 20654, 14531, 23943, 23971, 16840, + 6369, 13864, 12041, 26869, 18808, 18766, 16667, 8566, 24594, 8295, + 22877, 13644, 28416, 29585, 16963, 11432, 26363, 5296, 23031, 12707, + 15371, 4285, 32342, 23599, 20904, 31585, 31291, 31309, 29215, 6338, + 22529, 10369, 21816, 22941, 15683, 20099, 31254, 23957, 13286, 21560, + 22622, 28618, 1114, 8928, 11420, 12718, 1308, 18065, 25570, 26644, + 896, 17380, 27314, 7818, 10669, 30190, 25373, 5353, 1513, 2162, + 1581, 11050, 5911, 25172, 13441, 16251, 637, 5201, 31287, 12946, + 22219, 8174, 2575, 1602, 89, 10926, 29644, 19020, 21169, 29064, + 6622, 19319, 19277, 4248, 10815, 14563, 29090, 29461, 7835, 13210, + 30813, 32643, 1971, 31848, 1004, 21546, 10243, 13293, 14912, 10182, + 8610, 24816, 32721, 8832, 15211, 12534, 1507, 17181, 346, 10003, + 3592, 9334, 17464, 11521, 1767, 14120, 15483, 14959, 26171, 17292, + 21608, 1691, 25631, 18410, 23930, 30755, 31624, 1769, 13575, 18201, + 21523, 23114, 7201, 1365, 30977, 7638, 18107, 271, 383, 18069, + 26124, 13741, 3627, 31297, 12857, 16089, 16341, 15098, 14126, 471, + 2946, 31547, 26828, 277, 4023, 11789, 16970, 3588, 20212, 10107, + 29909, 19751, 29566, 24901, 27002, 16490, 97, 30681, 6726, 19380, + 5046, 29566, 1708, 26559, 7714, 12755, 18214, 30504, 13215, 888, + 1055, 29122, 22911, 21003, 21521, 23088, 30126, 25174, 17643, 12723, + 17207, 1734, 5301, 24128, 28681, 22047, 23926, 14038, 18428, 14959, + 14422, 10766, 4367, 27713, 7168, 13044, 31987, 11794, 31306, 4117, + 21454, 23561, 24856, 28889, 15417, 9186, 8829, 3347, 17546, 31020, + 18468, 23255, 4441, 6580, 21999, 22156, 21821, 13889, 8157, 4417, + 30167, 4993, 14129, 31045, 530, 14148, 13009, 4337, 23976, 30184, + 22252, 1993, 17897, 29716, 24543, 27076, 5048, 28842, 29786, 22759, + 24278, 27382, 702, 1304, 26211, 31020, 3530, 22066, 25249, 25361, + 11383, 31829, 9212, 21752, 16672, 23168, 9031, 16112, 11238, 32609, + 26441, 13372, 31827, 8299, 25464, 24323, 26174, 19242, 13316, 23625, + 32238, 16595, 23319, 5150, 32642, 12320, 25436, 25109, 9615, 16730, + 3006, 8696, 21260, 10423, 24690, 29882, 5227, 13031, 25911, 20909, + 15369, 32746, 13532, 14325, 7624, 24612, 14851, 5181, 11770, 1971, + 25894, 11945, 1922, 3072, 983, 13579, 7477, 24468, 7500, 13657, + 6791, 16430, 30746, 7263, 14179, 19218, 11585, 3249, 858, 11171, + 27753, 8676, 27648, 6196, 21867, 29693, 659, 4380, 2835, 28458, + 17630, 5640, 25381, 21424, 27997, 17814, 22429, 29221, 3746, 6943, + 7483, 30687, 32247, 16083, 831, 24492, 20805, 7180, 114, 23235, + 29749, 12764, 4453, 17915, 6292, 20968, 19453, 32571, 1532, 16762, + 19503, 12082, 3249, 28881, 24073, 23368, 8137, 15548, 2290, 21460, + 23184, 28263, 24626, 21135, 29994, 28020, 15541, 23881, 27770, 22605, + 17419, 16602, 24899, 30393, 24756, 14428, 29325, 15022, 3995, 5271, + 5963, 17255, 9952, 30653, 15162, 5813, 31194, 31064, 8972, 1178, + 32349, 18048, 23494, 26900, 22328, 4689, 14695, 16439, 26626, 27292, + 10442, 16510, 22039, 17629, 22733, 28515, 6877, 22381, 32435, 25645, + 2892, 4857, 14791, 11530, 30893, 20531, 23106, 2653, 20129, 1479, + 3951, 29785, 21094, 6801, 16653, 7887, 26454, 10253, 6133, 28484, + 31920, 16655, 31229, 6471, 2203, 4276, 32027, 21762, 13416, 26008, + 5582, 25614, 7704, 32073, 1209, 22668, 21278, 9212, 18321, 21105, + 26726, 31871, 4410, 10017, 21397, 1135, 3373, 10048, 27027, 10411, + 29213, 30404, 9799, 32264, 10202, 11087, 8609, 29255, 1141, 9405, + 21982, 2372, 25765, 21496, 6919, 31343, 9065, 25620, 31675, 17024, + 13551, 31128, 18223, 32103, 26805, 31769, 1139, 7298, 5870, 7694, + 10999, 19731, 15655, 5855, 17485, 8945, 20010, 17215, 23344, 23422, + 14627, 8124, 23494, 19672, 27649, 20884, 18722, 21844, 21791, 12747, + 27661, 24360, 10276, 3706, 24326, 19559, 27176, 5738, 7047, 24371, + 2328, 8929, 27786, 29230, 603, 21436, 27212, 5425, 23733, 12734, + 25425, 25153, 31084, 19240, 22184, 7870, 170, 32394, 25456, 31966, + 27939, 26584, 20891, 4442, 31479, 9152, 6130, 20839, 32218, 23867, + 857, 11496, 25202, 22883, 25429, 6370, 24320, 30084, 24523, 7728, + 5955, 19317, 23563, 16504, 14234, 28713, 2828, 20829, 19304, 129, + 6393, 1014, 21180, 28772, 3565, 21631, 23346, 20498, 19964, 6223, + 8212, 31669, 23154, 18954, 30149, 26938, 14431, 26136, 1120, 2142, + 8275, 27478, 5532, 26892, 23398, 21515, 31219, 7142, 29051, 6941, + 18691, 290, 3270, 8581, 308, 31127, 13964, 2048, 19099, 11799, + 20778, 20743, 24862, 24832, 29529, 24322, 27097, 32270, 2224, 8889, + 14273, 23667, 23403, 3595, 17287, 26410, 27498, 7906, 23232, 8072, + 13949, 5734, 19010, 11796, 21982, 12401, 2001, 30104, 5375, 28483, + 7931, 23672, 25990, 20344, 25920, 16451, 21654, 30775, 14710, 18312, + 25937, 15275, 21100, 1515, 29851, 31671, 24782, 9847, 2592, 18149, + 2120, 4150, 15000, 16800, 29678, 14591, 32586, 12196, 2524, 6800, + 9934, 12722, 537, 32221, 17176, 2952, 25794, 4263, 14514, 1552, + 13156, 31055, 21321, 16856, 16809, 23789, 875, 3522, 311, 2973, + 10639, 5629, 17621, 18201, 4694, 32563, 16079, 22413, 2064, 16900, + 16028, 25254, 7554, 23220, 29119, 14572, 5607, 8480, 11243, 3411, + 3636, 23799, 31338, 19212, 19360, 27174, 1728, 32161, 21348, 404, + 3211, 32577, 12223, 31248, 26285, 24921, 1598, 5512, 30637, 17252, + 25402, 11899, 29019, 3075, 24226, 22960, 16307, 14416, 32251, 20521, + 6916, 13679, 30628, 13651, 1362, 30823, 15199, 3455, 14072, 18480, + 25613, 30869, 16017, 28174, 30393, 1287, 22553, 16413, 4824, 12037, + 16208, 14892, 30671, 24560, 351, 6350, 950, 528, 4542, 10927, + 3932, 31881, 9813, 19493, 19339, 11147, 7906, 16647, 909, 10442, + 31642, 24770, 26862, 30766, 10003, 18601, 19588, 27851, 5985, 2189, + 26797, 23059, 2688, 32142, 8408, 25930, 25074, 20272, 23111, 2875, + 25000, 17074, 21011, 11818, 22054, 22683, 5232, 16393, 26083, 26773, + 31026, 23791, 4590, 12158, 18832, 27077, 10711, 957, 29480, 2883, + 14029, 9170, 27716, 2331, 8708, 21297, 11612, 29043, 6746, 14673, + 25564, 4249, 32057, 14080, 16934, 4338, 18111, 9721, 4315, 22324, + 19740, 14008, 25943, 28373, 19260, 24717, 8570, 6852, 32423, 6128, + 8366, 24150, 23717, 16148, 13861, 4739, 8869, 1215, 24675, 23378, + 17887, 9560, 22013, 4498, 1593, 16962, 20384, 29899, 10343, 17256, + 13140, 14708, 6999, 30489, 1282, 18278, 3276, 25623, 16436, 15505, + 12617, 490, 32541, 2030, 17200, 19322, 4825, 12468, 32043, 24541, + 28918, 7631, 5067, 9582, 6706, 29070, 29295, 8991, 24198, 19868, + 16232, 1040, 8804, 4913, 27482, 31072, 2366, 29577, 20453, 28227, + 10567, 25750, 8581, 16980, 11389, 19954, 14519, 32301, 743, 535, + 30302, 13877, 2587, 22482, 18276, 4026, 19972, 30709, 2980, 7232, + 18146, 13133, 25608, 32536, 14482, 29907, 11246, 4404, 5904, 9254, + 8657, 30521, 5178, 3129, 805, 24022, 15063, 6729, 8066, 20565, + 15063, 2323, 9989, 23919, 19164, 19523, 14324, 32357, 17146, 20527, + 28576, 29772, 21691, 25029, 656, 4008, 1127, 21186, 8155, 5693, + 18306, 11814, 9174, 15138, 27797, 4324, 24047, 23831, 12338, 31453, + 28121, 32536, 9010, 11279, 31722, 12083, 17985, 25098, 20673, 20374, + 32413, 2468, 4139, 30188, 18458, 14574, 6984, 21738, 18538, 1383, + 24041, 25636, 10139, 2671, 2877, 14886, 20961, 13310, 3501, 19648, + 1686, 8597, 11780, 2977, 22655, 13685, 13779, 2343, 849, 9232, + 4372, 9590, 14963, 6077, 13069, 28616, 21766, 15535, 17015, 12804, + 31523, 18728, 20704, 23185, 14435, 21994, 8526, 14444, 6053, 6895, + 16933, 15736, 11275, 25689, 32330, 15126, 11078, 21420, 27017, 19702, + 32020, 16699, 3124, 26157, 25606, 20960, 29197, 13447, 9095, 5360, + 8294, 19998, 18202, 15982, 14215, 23851, 28387, 20863, 17232, 15901, + 17769, 20254, 32544, 17384, 666, 15633, 4341, 5792, 8208, 6428, + 32724, 24646, 8850, 3188, 26987, 15330, 7390, 29558, 15093, 6865, + 20389, 3481, 29022, 5217, 2776, 27042, 9383, 21403, 13346, 25111, + 25229, 5984, 5814, 15861, 7269, 30904, 24650, 18853, 11339, 27494, + 28515, 31789, 13691, 11456, 22228, 31908, 25599, 5309, 14977, 7144, + 20363, 32080, 26985, 5779, 1445, 13300, 22756, 13098, 32520, 10632, + 25896, 4248, 1099, 21598, 31506, 14555, 17854, 30863, 3876, 19454, + 10901, 19089, 1482, 22343, 6984, 8203, 28897, 11441, 19257, 8935, + 18041, 20124, 17816, 15017, 29914, 17348, 16771, 8192, 30662, 26480, + 370, 20123, 21246, 2540, 16851, 18750, 5640, 30008, 31473, 25621, + 19758, 10754, 31258, 2376, 970, 26834, 5633, 11215, 15297, 23429, + 609, 23688, 32334, 29436, 1763, 13703, 27137, 19733, 7490, 4013, + 25095, 14042, 15021, 13948, 17067, 7929, 18541, 3625, 16157, 3189, + 4039, 24563, 19570, 28058, 7460, 16564, 15853, 21302, 24175, 17846, + 30333, 27654, 191, 31687, 21382, 21615, 20790, 21648, 13540, 3694, + 9513, 32290, 17966, 13778, 3456, 22738, 27959, 17577, 22880, 32583, + 475, 31736, 11272, 5329, 13048, 17583, 10745, 19964, 15546, 7387, + 346, 26470, 21532, 25886, 20890, 32461, 27563, 8796, 13911, 620, + 13709, 1976, 31103, 10888, 1731, 23743, 12469, 12614, 32683, 5090, + 18934, 15551, 7625, 9514, 19324, 12118, 6854, 295, 19170, 21066, + 15545, 12330, 24458, 3719, 31654, 29108, 5180, 10178, 8260, 28717, + 7183, 11716, 16388, 23769, 20109, 6150, 14926, 2057, 12050, 16255, + 6926, 18830, 885, 10938, 1647, 19827, 15850, 24619, 4582, 6380, + 15911, 12995, 11891, 23433, 11718, 15554, 2084, 24751, 31723, 31949, + 32474, 13641, 15398, 18752, 7247, 21947, 27803, 3525, 11096, 30743, + 26464, 1830, 21608, 18068, 806, 29023, 25430, 7740, 10012, 23544, + 6752, 9700, 22032, 6289, 13935, 20833, 29911, 30655, 2205, 9465, + 20464, 18454, 18676, 19736, 9316, 19335, 1656, 24132, 12272, 5869, + 22632, 20819, 3174, 4066, 13506, 4060, 23121, 31959, 4101, 9451, + 32472, 23396, 10810, 26105, 30949, 28892, 11573, 10267, 4486, 18725, + 25, 21881, 27539, 6086, 29722, 30466, 13969, 12098, 11911, 1630, + 2262, 677, 29703, 7204, 9454, 25948, 14627, 20046, 11838, 32073, + 7172, 21226, 4327, 6935, 11134, 15122, 8429, 27851, 8014, 23069, + 13465, 16028, 26016, 11543, 15899, 15877, 10709, 22127, 10356, 29464, + 14466, 496, 31635, 25621, 25788, 17650, 3006, 5947, 21139, 20178, + 14388, 26334, 19164, 32159, 32017, 23482, 31796, 11030, 23665, 391, + 19777, 17873, 25304, 27086, 9275, 2747, 19154, 20730, 18347, 29383, + 982, 6578, 15475, 10466, 8304, 4710, 17679, 7206, 28576, 14632, + 26138, 592, 17789, 29118, 5140, 24180, 14109, 25077, 28258, 24559, + 19897, 26680, 11158, 11678, 29965, 24672, 6897, 29005, 10494, 10958, + 24358, 14194, 9855, 10679, 18017, 30492, 9141, 10432, 25480, 22054, + 21337, 25291, 19649, 32176, 14175, 6672, 9012, 4815, 1749, 30319, + 18450, 10116, 11021, 7684, 25302, 14315, 6142, 15357, 7371, 1973, + 1000, 27048, 13774, 29656, 16951, 11728, 22101, 3205, 17834, 1610, + 17072, 2907, 3508, 12235, 21391, 31194, 803, 8189, 22469, 7194, + 2047, 26065, 17227, 29629, 16053, 25622, 20360, 28450, 19572, 6731, + 28879, 6363, 6872, 28593, 17706, 23394, 23750, 13477, 24662, 11527, + 23191, 20013, 6414, 14381, 18808, 16337, 15245, 21430, 12816, 1964, + 17742, 24508, 10852, 194, 3606, 23170, 26360, 16276, 564, 16810, + 26423, 7410, 20999, 5032, 16661, 25361, 15042, 1398, 20467, 20701, + 19564, 16521, 19139, 26512, 20779, 15621, 1388, 13956, 14785, 3117, + 24944, 6469, 13143, 10013, 12748, 7147, 19066, 5563, 5664, 31143, + 16227, 491, 4367, 27035, 28539, 2818, 24041, 11759, 30750, 28030, + 18823, 6433, 30367, 21236, 4727, 32156, 2445, 24137, 5210, 15245, + 30517, 18469, 17033, 24818, 7528, 8101, 13823, 4083, 3711, 848, + 21107, 8054, 32667, 26189, 18116, 10734, 27698, 13779, 27802, 32371, + 4807, 29408, 19275, 10264, 12024, 5166, 16891, 32633, 5310, 17507, + 7432, 31379, 4337, 20615, 6821, 11993, 13066, 23228, 8900, 14724, + 25645, 9513, 13882, 554, 28519, 2360, 22813, 2962, 24372, 28417, + 31857, 18119, 21572, 22790, 11012, 7275, 14718, 22560, 32151, 8270, + 31046, 31673, 6718, 9023, 6139, 4668, 17162, 5922, 22468, 19914, + 14308, 31043, 18829, 8495, 28604, 14058, 28492, 26900, 27876, 16432, + 21385, 13685, 12470, 15632, 14877, 18262, 4733, 6337, 20026, 5810, + 32147, 22339, 29099, 29877, 23238, 26937, 4445, 7723, 10496, 29174, + 8991, 21047, 2344, 13274, 12454, 16712, 16707, 5855, 5076, 26697, + 31455, 32111, 18490, 11288, 16561, 23417, 30171, 18746, 32637, 17347, + 6450, 27597, 3638, 24450, 27767, 957, 359, 1064, 30239, 10555, + 19921, 23421, 24738, 9579, 32341, 20977, 32640, 29533, 277, 16176, + 9606, 19775, 20439, 31992, 12365, 6609, 30634, 24615, 27909, 19993, + 17355, 18457, 14960, 5975, 4041, 14384, 3121, 24891, 1256, 12212, + 13127, 3158, 18115, 13278, 4053, 26831, 21302, 16431, 4132, 18775, + 6523, 4358, 8426, 16990, 3219, 31858, 29389, 27015, 13544, 19781, + 14627, 10586, 1328, 21610, 17916, 32058, 30356, 9336, 10082, 10646, + 12306, 11142, 10719, 17507, 15648, 8002, 25664, 8649, 6034, 28477, + 12441, 31004, 2237, 12681, 13961, 22031, 9693, 15750, 27327, 10788, + 27572, 26873, 10173, 4711, 15732, 7006, 17372, 9564, 19167, 11136, + 18577, 23862, 8621, 15174, 12014, 29257, 28206, 1488, 17772, 14786, + 12080, 21442, 8935, 23702, 24867, 2528, 19178, 31416, 25723, 1500, + 26413, 3727, 9046, 15723, 14940, 9383, 14943, 21406, 12314, 15271, + 10482, 6256, 11846, 24707, 19585, 9884, 2941, 23282, 11062, 5041, + 20192, 32291, 20953, 26215, 7447, 29099, 18677, 18228, 30358, 1731, + 25910, 7963, 11038, 28080, 30486, 6341, 9863, 24450, 7608, 14290, + 31422, 4097, 16660, 31345, 19210, 31717, 30549, 14638, 31891, 26437, + 22911, 14604, 20516, 8274, 2901, 9172, 18736, 23109, 31354, 23212, + 1555, 13462, 6358, 22251, 28059, 17007, 5691, 3372, 21959, 9824, + 23881, 1698, 1998, 5716, 22415, 9387, 17763, 23492, 15602, 17063, + 13112, 9258, 29267, 10762, 19649, 13753, 15212, 3248, 17722, 23627, + 17403, 31089, 31211, 8859, 17443, 32502, 22955, 11188, 17423, 28874, + 26019, 16553, 17112, 692, 12116, 26247, 17577, 11845, 30684, 4232, + 3105, 5805, 21620, 17033, 760, 24275, 31806, 14270, 5606, 9636, + 9022, 24997, 1984, 7338, 1701, 21304, 32741, 27873, 3670, 27751, + 22488, 24007, 10147, 22981, 27694, 29363, 15703, 15490, 29867, 29287, + 28922, 3737, 10152, 3304, 31056, 5962, 11447, 5006, 18979, 971, + 8842, 30551, 26946, 30779, 15377, 384, 5322, 11479, 14563, 31319, + 7844, 27504, 15583, 12875, 8527, 285, 19219, 8517, 29152, 30765, + 26589, 4155, 29511, 11871, 9807, 258, 11987, 6998, 9432, 18531, + 22009, 23367, 15649, 3698, 4433, 20981, 27446, 10523, 16609, 31199, + 12262, 1278, 4510, 13239, 29292, 4221, 3120, 10775, 9551, 23826, + 246, 7536, 12934, 27366, 30026, 11331, 29408, 21152, 30214, 27539, + 13475, 16763, 29510, 25498, 5491, 31644, 2416, 17595, 13174, 22690, + 23410, 26808, 31158, 28286, 25163, 29733, 3015, 24197, 21965, 11292, + 5289, 30915, 4769, 21555, 31983, 10643, 8587, 10411, 19489, 25965, + 17391, 18666, 14688, 1790, 10645, 9771, 12104, 21374, 13225, 19061, + 18203, 6462, 26886, 3017, 32539, 7296, 32692, 32672, 12343, 18060, + 8110, 17045, 1770, 19200, 17129, 26718, 865, 14184, 22163, 10240, + 17628, 8462, 408, 6211, 9302, 25876, 13419, 4196, 25192, 11967, + 26115, 267, 27755, 14358, 8740, 22908, 31965, 30892, 19679, 19729, + 6917, 14010, 16220, 3304, 15834, 11888, 8663, 18905, 5666, 17293, + 9961, 4779, 17367, 28974, 3081, 25223, 31438, 27619, 850, 29612, + 23195, 10760, 27271, 10853, 32175, 18003, 29524, 11320, 963, 28841, + 8406, 30561, 7034, 24886, 15966, 3711, 20289, 444, 13170, 3212, + 6522, 16683, 22805, 13586, 987, 17779, 13385, 21660, 22032, 28566, + 14801, 1387, 6056, 26113, 32571, 11371, 26926, 6428, 32030, 16998, + 30870, 30240, 18081, 15734, 5340, 15401, 23100, 11968, 9447, 10124, + 28611, 26719, 1459, 26474, 26528, 16287, 5039, 4485, 16223, 16236, + 25444, 2131, 12145, 24330, 27992, 1873, 22057, 4358, 12594, 18842, + 5131, 15980, 5150, 32440, 32070, 570, 32371, 442, 1151, 14613, + 31892, 1670, 17104, 14651, 26388, 12657, 273, 18105, 22045, 19725, + 26198, 6110, 25618, 4674, 28600, 32239, 883, 13985, 5465, 1564, + 27649, 13556, 16546, 9907, 25379, 1050, 30671, 19604, 25481, 12306, + 25262, 23781, 26639, 2389, 3235, 32518, 24036, 24169, 2032, 17095, + 491, 29619, 22067, 30425, 11617, 11661, 14896, 17283, 21953, 8344, + 22225, 23958, 17380, 31827, 16235, 26019, 18029, 1005, 23875, 18283, + 25444, 2392, 20049, 11671, 19745, 7839, 8516, 23978, 7788, 29049, + 4806, 4147, 8674, 1310, 12288, 6471, 10395, 15961, 609, 29998, + 13321, 30175, 10395, 3421, 30499, 1247, 239, 18852, 29379, 32730, + 18603, 16809, 12621, 5045, 21745, 8853, 26002, 14605, 18617, 7437, + 28916, 12746, 27673, 28516, 5322, 1271, 2245, 24591, 21254, 27857, + 28626, 15449, 16917, 27421, 3263, 12120, 22534, 30655, 10677, 1311, + 28911, 32137, 18702, 11829, 5260, 6481, 14357, 5958, 16170, 20294, + 16140, 10521, 12739, 10884, 4724, 1218, 3435, 31461, 27016, 20505, + 15850, 9498, 24075, 9834, 30635, 14587, 31043, 366, 19669, 16924, + 4678, 11898, 7607, 1697, 24948, 9207, 26729, 10323, 27973, 19218, + 15280, 23539, 13381, 4532, 20771, 10958, 14428, 22709, 4748, 29329, + 20307, 4685, 3729, 5983, 23700, 9419, 8340, 7201, 22577, 25993, + 20439, 5405, 18216, 16415, 10724, 10443, 2278, 24023, 7212, 4835, + 7689, 24929, 4334, 23995, 19562, 2760, 21338, 17872, 17499, 22437, + 32503, 31650, 11053, 29587, 32322, 14278, 12022, 3298, 23704, 21814, + 1936, 24976, 28501, 26091, 2277, 6260, 6718, 30126, 10858, 15037, + 13084, 28459, 10218, 24987, 18602, 22343, 8069, 27708, 20657, 21171, + 20728, 20986, 370, 10689, 25119, 25811, 23108, 15991, 19640, 6169, + 8062, 10467, 22889, 8042, 19863, 28077, 19461, 31203, 18316, 755, + 9483, 15761, 17538, 5968, 7279, 14120, 16557, 20286, 14749, 12792, + 30064, 27807, 11452, 31772, 7366, 5270, 19759, 31477, 992, 10139, + 24930, 25559, 12560, 24527, 5059, 27905, 8421, 10636, 14606, 2771, + 24216, 27201, 19406, 3387, 31654, 10621, 16673, 12657, 10943, 20553, + 6843, 31716, 479, 29371, 23085, 28276, 10171, 22472, 14695, 28385, + 28766, 13146, 19580, 29059, 11540, 18082, 28838, 15420, 13191, 23216, + 11402, 21932, 24616, 18753, 26045, 29859, 6096, 2008, 3974, 988, + 3239, 26896, 14054, 3560, 19014, 2096, 4053, 9576, 19736, 4864, + 26575, 7551, 10502, 26075, 8591, 9863, 851, 11782, 7707, 8963, + 25540, 1600, 14080, 18645, 8453, 16794, 11743, 25266, 27720, 22665, + 13608, 17874, 16431, 11322, 30246, 20460, 14060, 5157, 14396, 353, + 2983, 27109, 3438, 1976, 19231, 1863, 10274, 19530, 30613, 27297, + 15912, 23358, 9190, 30053, 7417, 14008, 18935, 30965, 21308, 27214, + 24661, 1465, 5007, 29372, 29469, 12400, 13443, 19928, 17434, 16835, + 29160, 18120, 13870, 1056, 21324, 21106, 2035, 9157, 23486, 4148, + 8609, 21787, 26328, 17109, 31283, 13638, 22268, 21948, 22469, 6394, + 30941, 16992, 28481, 15760, 16449, 27136, 10042, 4185, 16902, 14228, + 536, 50, 3939, 7683, 29558, 3416, 22129, 18070, 9416, 2942, + 27626, 941, 27969, 18973, 8752, 23473, 244, 5900, 4497, 16457, + 6969, 20304, 13769, 17873, 10322, 29237, 1224, 28424, 12878, 27294, + 25270, 24840, 5422, 665, 5941, 4988, 15943, 13622, 10214, 20749, + 31463, 21146, 15406, 7197, 6715, 10831, 28218, 19384, 26224, 12254, + 11584, 29895, 14617, 19872, 5384, 29575, 11985, 22993, 24236, 10554, + 15905, 25062, 20124, 16925, 13732, 16416, 17380, 6041, 25960, 12130, + 19634, 701, 30616, 20800, 25618, 13036, 15448, 23128, 15613, 23763, + 27889, 22757, 16018, 21918, 10798, 18833, 5539, 28268, 14854, 28299, + 32627, 9216, 18554, 7386, 12734, 30393, 12225, 29681, 1018, 32452, + 2979, 13187, 15837, 4240, 7203, 9112, 12063, 1410, 15200, 17121, + 29825, 31979, 27325, 12104, 3598, 10551, 15168, 31948, 10913, 32103, + 5432, 28300, 19623, 20161, 8995, 17953, 11302, 16069, 25550, 7473, + 31231, 356, 10174, 32065, 26736, 21357, 32207, 10699, 7388, 13640, + 21275, 20625, 28367, 15846, 18973, 5168, 6205, 6648, 3351, 9464, + 15114, 2911, 8112, 23547, 24527, 31972, 29666, 3172, 20956, 32298, + 17939, 32523, 31865, 15328, 1154, 19526, 11634, 18491, 17893, 2049, + 24786, 18340, 4945, 29713, 31214, 21934, 2393, 11461, 3493, 13898, + 5242, 958, 8679, 10046, 19639, 32001, 1652, 26067, 13440, 8779, + 10982, 15566, 24280, 8357, 5618, 11938, 16567, 25655, 25067, 4068, + 12717, 9825, 25223, 9727, 24521, 8712, 22502, 7721, 15033, 25134, + 21200, 28822, 6706, 24262, 11839, 1003, 9107, 23494, 16925, 9461, + 12997, 15997, 13134, 9883, 19045, 19751, 1265, 15889, 20261, 3263, + 20195, 28495, 4719, 12564, 15749, 7618, 28748, 21279, 16064, 5441, + 1157, 24980, 70, 8002, 15112, 32695, 8986, 30798, 16268, 18033, + 18514, 14770, 4216, 7075, 23324, 2936, 14593, 5050, 15359, 12949, + 30355, 4484, 12643, 20324, 19827, 6210, 16724, 24659, 20642, 1022, + 22659, 23773, 8452, 7198, 25229, 31928, 26561, 13370, 14208, 32591, + 20460, 20267, 668, 1906, 17251, 30820, 1622, 24396, 28406, 18310, + 24995, 14486, 20939, 16942, 14962, 15627, 23552, 4113, 29050, 796, + 24723, 13644, 17207, 15076, 24191, 18165, 31279, 10078, 25896, 19958, + 5754, 14294, 6137, 19483, 3713, 21917, 5452, 5993, 3334, 14849, + 17546, 32690, 20581, 24700, 20371, 29286, 3865, 26924, 19296, 4268, + 1409, 32643, 21479, 27569, 4324, 6464, 7998, 23360, 31668, 8606, + 19460, 8852, 24335, 25803, 28413, 22378, 15755, 20379, 9212, 16218, + 15155, 22398, 9734, 5990, 5837, 7856, 12980, 31188, 18527, 10415, + 15577, 22073, 26865, 13042, 12901, 12247, 18323, 1496, 22921, 5672, + 20386, 20959, 2332, 3003, 9288, 27926, 16446, 20287, 5442, 1237, + 24891, 15070, 20710, 6059, 5042, 11724, 30564, 19352, 4598, 13343, + 29516, 29820, 26036, 24801, 15184, 24272, 22486, 3683, 24908, 12948, + 6056, 30933, 4739, 648, 23865, 26388, 27947, 23455, 2821, 21955, + 20269, 26801, 4816, 22685, 550, 32368, 8661, 9737, 26801, 9117, + 22560, 239, 4328, 27004, 3897, 29899, 20520, 10373, 29698, 6144, + 19477, 30044, 30690, 18542, 5342, 25091, 23843, 5702, 13507, 24399, + 25241, 20765, 22740, 21144, 21125, 27456, 30459, 13237, 30904, 10056, + 5436, 18215, 17404, 17390, 5692, 18581, 5807, 6688, 12032, 6521, + 2737, 24916, 27128, 18456, 11393, 3612, 23356, 695, 29090, 32506, + 27157, 19212, 28228, 27510, 21439, 32243, 3616, 18799, 19309, 866, + 21668, 7343, 23438, 15007, 26704, 25794, 27727, 30384, 28000, 7431, + 4961, 7956, 29611, 775, 4348, 18608, 31691, 5894, 7716, 23308, + 22398, 2776, 28715, 5567, 26956, 4854, 5495, 2600, 1898, 19921, + 3973, 23903, 1503, 13670, 20014, 26219, 11237, 20243, 28713, 31926, + 6290, 3497, 27858, 2750, 23223, 6594, 2857, 14604, 16393, 29781, + 18974, 17770, 8062, 10119, 7692, 23121, 8954, 31172, 10115, 31393, + 21547, 22287, 18250, 21908, 28849, 25750, 13220, 5877, 18819, 29322, + 29395, 1933, 12686, 16429, 24283, 4575, 16287, 17965, 5768, 32533, + 26498, 31536, 32690, 57, 11884, 18096, 16064, 22440, 32406, 5641, + 18283, 14020, 24107, 18384, 27333, 9194, 3428, 24935, 1657, 31908, + 20465, 17102, 14676, 26655, 26012, 19644, 16644, 19935, 30249, 11503, + 25888, 9393, 29824, 17600, 4969, 11745, 15630, 24058, 4432, 28846, + 29544, 8684, 26296, 2555, 25393, 1725, 26108, 9287, 26687, 9984, + 30004, 11231, 8697, 14216, 31924, 4946, 30211, 3966, 7894, 15398, + 9758, 30384, 7418, 22473, 32073, 11820, 3530, 21706, 22572, 26382, + 18627, 11337, 3255, 7062, 10993, 9133, 29959, 32382, 6393, 22165, + 8585, 29834, 25181, 7440, 30305, 19552, 27132, 8720, 14349, 10394, + 14296, 15900, 19269, 20506, 22556, 27206, 11302, 22685, 11102, 24413, + 24720, 24467, 3105, 13667, 27085, 17711, 28062, 5953, 31091, 7610, + 22468, 24250, 28043, 19756, 25725, 11422, 22514, 253, 8755, 20800, + 5376, 31801, 32659, 6441, 13672, 27091, 9360, 21006, 16223, 10555, + 23723, 7168, 2817, 18738, 30322, 23267, 15856, 9438, 15431, 23585, + 10814, 22297, 27235, 1184, 5261, 30735, 17947, 6897, 9588, 22792, + 14945, 458, 2597, 12930, 7565, 30390, 2125, 30851, 1118, 10507, + 2364, 18733, 17979, 15644, 24646, 10643, 5823, 4017, 20585, 1227, + 22645, 19892, 5424, 8517, 3296, 13357, 29398, 90, 1471, 3185, + 4527, 23592, 15246, 14485, 10436, 14238, 27984, 25641, 15293, 13523, + 28044, 11555, 28677, 9628, 16534, 22810, 15656, 2653, 28687, 27317, + 31872, 29471, 14255, 11106, 7184, 30435, 28482, 32075, 30896, 13622, + 22001, 2438, 9067, 5638, 8899, 7819, 5669, 15284, 32356, 21228, + 7040, 23402, 18980, 16448, 28161, 28591, 22594, 27232, 21103, 31033, + 31083, 9943, 12668, 16898, 894, 6291, 29864, 22174, 13714, 5089, + 10849, 10699, 7482, 32408, 13679, 5706, 24840, 147, 2959, 13037, + 5199, 6245, 18270, 11506, 30765, 26697, 4473, 7311, 19913, 20312, + 9443, 1998, 7642, 8959, 29351, 1947, 6505, 7343, 10430, 17179, + 30679, 20438, 8242, 13632, 30241, 15375, 30340, 28827, 23395, 20373, + 22092, 21873, 9883, 28158, 13087, 28176, 18864, 20262, 31925, 25955, + 5555, 19049, 3403, 19397, 15460, 25109, 22343, 7699, 2808, 8990, + 5659, 17157, 16921, 11540, 1643, 29374, 12079, 30291, 26994, 15521, + 27358, 8314, 22434, 8039, 24783, 28958, 9866, 26478, 1204, 17087, + 26109, 18260, 22662, 506, 14449, 24261, 10257, 2817, 2725, 9105, + 7603, 31561, 28295, 24834, 5217, 19330, 7508, 16748, 19178, 17868, + 25857, 5637, 23680, 11304, 1242, 20727, 8820, 9004, 15721, 26036, + 25409, 13564, 6041, 906, 29388, 117, 10011, 24183, 10218, 26795, + 19465, 31294, 19235, 22678, 14281, 11194, 25270, 28424, 14645, 17523, + 32732, 9954, 5112, 7279, 29009, 20360, 26427, 28235, 32529, 27026, + 26498, 14730, 4959, 15301, 28624, 7980, 29964, 13377, 11794, 27778, + 30454, 27131, 28346, 18498, 16591, 21102, 19371, 13783, 5408, 32654, + 16981, 25338, 14477, 31230, 29985, 29574, 24154, 2946, 7183, 16264, + 15918, 3511, 26781, 14230, 14103, 4354, 24812, 25443, 9167, 28614, + 30238, 11920, 17470, 8666, 23042, 30660, 24639, 25658, 22515, 5630, + 1959, 2300, 13565, 18474, 4996, 902, 23362, 17139, 19618, 1279, + 21660, 20697, 23380, 15619, 22660, 14467, 25712, 4763, 28979, 23894, + 30620, 26988, 31750, 20038, 13000, 18222, 2343, 14060, 16124, 28000, + 24170, 26329, 2644, 15162, 8565, 25387, 25417, 27620, 27940, 13918, + 22833, 1253, 22060, 29284, 1545, 2270, 8958, 6391, 9587, 17016, + 15086, 24110, 7419, 15259, 4244, 31054, 22942, 13537, 2930, 30691, + 9869, 12618, 13528, 18192, 24878, 27185, 11269, 26404, 11876, 16254, + 9615, 32109, 5392, 30750, 31237, 14417, 8664, 26288, 2002, 14746, + 30759, 18049, 27748, 31506, 29812, 25787, 22670, 24912, 17352, 27097, + 1067, 28881, 5242, 22366, 1285, 22859, 17469, 3527, 3317, 19261, + 12126, 28350, 21323, 19403, 1350, 28560, 11631, 31433, 17990, 12257, + 6857, 32534, 20116, 26783, 18399, 3371, 4752, 18228, 19645, 15347, + 28748, 2304, 10906, 7134, 14924, 27398, 19640, 17457, 24458, 16869, + 4236, 19104, 21693, 19979, 6431, 22814, 8923, 8229, 29108, 22004, + 16093, 8665, 17323, 21008, 3778, 5816, 9125, 12704, 16342, 10606, + 14825, 17120, 21044, 22844, 18951, 14128, 32138, 23465, 23995, 12906, + 21276, 18036, 5907, 25714, 16652, 5580, 28116, 24847, 23256, 25283, + 23579, 10745, 2852, 24065, 15936, 7987, 4794, 25269, 27135, 18842, + 30927, 13778, 16602, 13247, 20616, 17783, 16825, 17650, 30652, 31918, + 24020, 21151, 13052, 32602, 5330, 23518, 19152, 25195, 7218, 9029, + 3652, 13396, 5611, 6912, 24227, 27071, 6735, 22308, 10371, 639, + 6453, 31297, 28161, 31430, 22103, 20535, 17998, 23704, 1273, 2890, + 30315, 1407, 18124, 22392, 1840, 29825, 4625, 14345, 31891, 13391, + 17153, 25449, 4647, 13759, 23661, 4403, 8419, 11900, 15266, 28302, + 4645, 20476, 17295, 11452, 1014, 27315, 17194, 24048, 656, 20871, + 6927, 5216, 31518, 4483, 26218, 30620, 19394, 20943, 11032, 17438, + 5340, 28640, 21769, 26914, 12170, 11244, 233, 14520, 28011, 31137, + 23039, 24087, 1449, 18710, 3925, 3636, 21302, 23554, 9241, 9875, + 18517, 31532, 8003, 11869, 15197, 8226, 24184, 28084, 28857, 26414, + 4142, 3907, 1588, 23942, 6418, 25, 9076, 32707, 26790, 439, + 5059, 31892, 303, 8807, 11542, 4020, 14030, 4048, 10311, 2329, + 27117, 30664, 20134, 21615, 16604, 27497, 8368, 5316, 9404, 30823, + 23070, 10373, 1437, 27808, 30498, 10665, 10723, 30709, 7917, 15916, + 3310, 8281, 6004, 19647, 4346, 15399, 9844, 27070, 2115, 26297, + 947, 4194, 22893, 9014, 16522, 31800, 17188, 8619, 18592, 14033, + 768, 23134, 24568, 11707, 25515, 29535, 16942, 25777, 30277, 9311, + 18173, 6159, 31495, 3269, 12293, 25481, 15655, 12287, 971, 16667, + 26108, 3094, 6658, 29763, 2915, 32390, 15625, 13679, 32097, 27468, + 1204, 12721, 5898, 23104, 20047, 12986, 5793, 32421, 30176, 774, + 16640, 23807, 13485, 25172, 21562, 804, 18686, 7769, 3456, 1686, + 19236, 9141, 17985, 15632, 21434, 6950, 27797, 25439, 22315, 26364, + 23260, 9843, 23157, 5325, 21154, 17078, 25900, 5993, 7879, 11677, + 21697, 23824, 8412, 32124, 29956, 5603, 19919, 926, 488, 16025, + 24011, 19605, 30342, 17267, 13146, 31439, 19813, 20338, 15821, 12138, + 5625, 7111, 23261, 30025, 873, 21387, 31634, 25192, 22678, 24496, + 4196, 25452, 21848, 12088, 161, 25983, 2314, 20388, 12558, 21082, + 11850, 27056, 24017, 4771, 29690, 30269, 19342, 2682, 22410, 16271, + 5073, 17487, 29818, 696, 4268, 15862, 27930, 17748, 11169, 3069, + 20629, 13175, 5057, 18237, 8749, 6628, 7958, 30895, 26056, 20608, + 23359, 30593, 21932, 10502, 4442, 28009, 9607, 13819, 16679, 30010, + 30386, 32229, 22377, 1973, 25014, 28344, 9104, 10913, 14608, 9617, + 20630, 5662, 26929, 24230, 25895, 17462, 6601, 8573, 3844, 7861, + 29765, 32093, 15785, 16186, 13206, 27139, 20526, 30095, 24885, 8341, + 23147, 2102, 25444, 220, 17759, 25412, 16566, 22189, 20179, 10611, + 15569, 5032, 11322, 23410, 12915, 20774, 21724, 2431, 20147, 11884, + 14131, 17208, 4490, 24357, 23957, 14766, 14225, 32271, 17940, 18364, + 28940, 30708, 29091, 21655, 2600, 8924, 28282, 17573, 26753, 21889, + 25019, 27795, 487, 4867, 21589, 15010, 12386, 7901, 18216, 2625, + 1907, 3352, 21099, 21985, 3741, 8894, 21579, 15109, 1316, 6802, + 29397, 1176, 4885, 8702, 29974, 12164, 2950, 8781, 14209, 7552, + 9247, 10778, 16273, 27177, 17882, 2824, 12975, 5717, 23302, 32692, + 13397, 32164, 16018, 28136, 28532, 19032, 12424, 6418, 20243, 23398, + 29656, 22772, 10809, 20684, 31063, 10396, 26382, 12838, 15367, 8599, + 22845, 25468, 12953, 14178, 24476, 23647, 31280, 18209, 1295, 21812, + 7784, 20371, 10410, 24271, 8855, 25926, 28949, 6212, 12371, 14087, + 3893, 30457, 3892, 13981, 5128, 21760, 18906, 14775, 24017, 14756, + 9266, 10018, 24694, 4162, 18166, 1087, 19750, 16731, 17523, 30389, + 23870, 25062, 23183, 3683, 17518, 27883, 5642, 27545, 3608, 15181, + 15108, 5511, 27128, 11752, 5824, 5365, 11274, 15835, 7847, 10037, + 936, 26407, 144, 7510, 11763, 8570, 2110, 15259, 10101, 23365, + 32298, 11563, 8453, 11028, 25475, 24586, 15338, 11039, 12148, 23880, + 20803, 13084, 29093, 29049, 18598, 25688, 32260, 14693, 16903, 15366, + 1477, 26368, 5851, 8151, 16807, 21148, 30334, 6347, 4474, 11357, + 8658, 12358, 13534, 25069, 2602, 9062, 333, 3186, 22374, 24724, + 14684, 31421, 6495, 857, 26941, 11204, 10373, 17011, 27785, 19795, + 31174, 1065, 316, 4862, 27058, 3291, 3074, 31066, 17117, 31709, + 29105, 18797, 901, 2694, 10774, 28770, 9845, 16771, 28660, 32641, + 29481, 6257, 12874, 26719, 6451, 13228, 10965, 18770, 6878, 13409, + 15830, 24104, 19216, 20203, 1813, 26905, 25205, 7004, 14374, 10233, + 28633, 21593, 11243, 10675, 10198, 30764, 21338, 6130, 22448, 20, + 24448, 18721, 32637, 8671, 19654, 11926, 22409, 20411, 13172, 29450, + 17656, 27210, 371, 23885, 429, 22772, 343, 19255, 31595, 31873, + 21149, 22754, 11834, 12263, 14128, 2070, 32269, 19296, 23090, 31749, + 21409, 24000, 29690, 7481, 168, 4102, 28505, 14794, 12071, 26417, + 29730, 30164, 24605, 22613, 31465, 8232, 15717, 25325, 19978, 16194, + 17117, 9587, 7150, 27260, 712, 8238, 32393, 17281, 2417, 6732, + 7207, 9219, 9234, 23856, 17769, 21425, 3827, 14861, 13450, 23139, + 20182, 3508, 20967, 17048, 14739, 13994, 8639, 24611, 29184, 12670, + 16183, 28449, 23315, 20313, 2209, 7982, 17995, 15063, 24829, 24281, + 24907, 16149, 10944, 31573, 9929, 1292, 24533, 26737, 22182, 15133, + 27362, 17609, 14686, 23276, 9676, 28222, 12963, 370, 8573, 15013, + 26437, 3406, 6885, 10428, 31317, 30556, 16702, 7522, 9610, 6537, + 12693, 263, 12515, 22502, 19334, 20833, 3470, 24571, 17223, 31584, + 6153, 29277, 5352, 4453, 17799, 27267, 15576, 12719, 16533, 30933, + 22956, 12565, 14589, 17158, 6940, 24059, 27371, 24228, 8782, 28543, + 8526, 23021, 14168, 29939, 14209, 18149, 30720, 22928, 24107, 31909, + 25541, 20584, 10084, 758, 26916, 15517, 7705, 23582, 22548, 27089, + 27825, 8442, 24019, 10461, 22256, 9037, 28132, 9414, 2844, 2648, + 15449, 1005, 3679, 7595, 11598, 10629, 18541, 12858, 23990, 11082, + 29286, 28083, 10586, 20336, 27902, 21519, 21022, 9946, 16859, 21352, + 11398, 6029, 21567, 16906, 30074, 27492, 1254, 716, 13638, 14085, + 19762, 17313, 21477, 11539, 5509, 2666, 17023, 12710, 19182, 1038, + 24112, 16888, 30729, 18893, 28683, 1121, 25888, 4305, 28255, 5253, + 918, 30726, 25400, 24524, 10374, 26668, 7209, 9746, 23207, 14891, + 1865, 29344, 14195, 6054, 24956, 8539, 13192, 18898, 6644, 17468, + 14926, 27273, 3831, 32000, 28890, 11162, 2018, 7969, 8210, 10340, + 4206, 11780, 2578, 30115, 6450, 2932, 11983, 25481, 361, 27215, + 17402, 9745, 28150, 22911, 25640, 22416, 4517, 31532, 14639, 2444, + 19124, 15408, 9683, 22482, 14738, 8140, 12962, 12739, 13542, 29593, + 16924, 19951, 16083, 32705, 20503, 31717, 24442, 1902, 16482, 18504, + 2241, 11651, 169, 10022, 26451, 4491, 408, 6278, 17641, 14574, + 19267, 31552, 24519, 6251, 12486, 530, 23433, 20930, 26408, 32495, + 12063, 31153, 902, 30874, 13335, 31266, 9325, 15988, 15130, 25763, + 19079, 28307, 18874, 18710, 9338, 8723, 25963, 25770, 21005, 13233, + 4046, 15626, 25229, 21653, 22582, 31700, 4719, 25456, 9829, 7756, + 3126, 14791, 8854, 19794, 12439, 25487, 24257, 29073, 10715, 29945, + 9498, 22522, 20102, 29327, 4058, 31345, 5942, 32640, 20035, 16581, + 11213, 17976, 15070, 19942, 19410, 20276, 9992, 11602, 5653, 14869, + 32684, 29905, 29586, 3315, 12114, 11874, 10648, 3571, 24921, 7797, + 22315, 23324, 2688, 14938, 24998, 13414, 23212, 25338, 5285, 11284, + 31717, 14277, 31009, 12612, 17057, 5611, 10205, 10437, 13125, 6095, + 6913, 2088, 233, 10425, 17157, 10010, 26971, 202, 25297, 15089, + 4711, 7478, 30499, 12420, 19542, 2221, 11568, 22592, 23989, 2074, + 11406, 6264, 15448, 32656, 14417, 6653, 22430, 9089, 27509, 8943, + 22517, 18994, 5244, 5074, 30625, 29035, 29783, 15076, 22714, 19571, + 25738, 29102, 31860, 28220, 10380, 14554, 16292, 25562, 28268, 17988, + 127, 21305, 17082, 14676, 20900, 29200, 31653, 27018, 24157, 26897, + 24417, 858, 25633, 30563, 2508, 26427, 25950, 27217, 21840, 32507, + 31094, 15684, 19211, 32085, 24071, 30190, 24109, 15301, 11632, 4297, + 30370, 10907, 24209, 4544, 3826, 1871, 29138, 3371, 26981, 19732, + 28518, 15617, 8298, 5039, 6865, 30281, 5689, 20618, 32679, 4818, + 1976, 11780, 15255, 8996, 18075, 27916, 10665, 31926, 24272, 1262, + 21188, 30818, 11034, 30060, 18291, 29973, 7420, 32529, 18152, 29387, + 15320, 13762, 22338, 22971, 8024, 8598, 13067, 16846, 26893, 25064, + 12293, 20054, 342, 1706, 806, 29181, 7044, 5115, 1505, 12027, + 17723, 25596, 24743, 10125, 1307, 23264, 16046, 4770, 19616, 8515, + 28401, 26839, 16711, 22717, 5752, 23973, 8435, 32438, 22489, 30937, + 17806, 29052, 5119, 25669, 3059, 19346, 10282, 16822, 8716, 2380, + 19767, 7462, 55, 11416, 31778, 20274, 4591, 21279, 31689, 25550, + 20904, 29325, 31730, 22839, 23546, 12076, 24976, 19241, 15874, 184, + 10306, 24147, 774, 6511, 32029, 8, 17751, 17255, 30379, 20023, + 18044, 10276, 3635, 15261, 21132, 13930, 15931, 17465, 29404, 6736, + 27212, 17920, 13274, 31638, 29685, 20947, 569, 30264, 8466, 2222, + 25423, 323, 14159, 22818, 3259, 14312, 28903, 12549, 8010, 14543, + 11954, 30568, 2332, 4014, 2910, 25289, 21250, 23833, 13308, 32650, + 23851, 12444, 10687, 31394, 20224, 18695, 20737, 1171, 15948, 12041, + 29063, 24732, 20896, 24064, 3078, 29743, 25078, 28432, 10470, 27301, + 24468, 31346, 1093, 16689, 32307, 8975, 15167, 20242, 27745, 78, + 24173, 7693, 6692, 23082, 3499, 10011, 21216, 27527, 287, 18271, + 11898, 7878, 9079, 346, 7614, 30871, 8561, 24162, 5630, 9486, + 4769, 1104, 8188, 23290, 8047, 21370, 19979, 2418, 18527, 8417, + 23920, 944, 12949, 24747, 1806, 7971, 7576, 21715, 17219, 30655, + 26198, 5151, 1293, 20120, 15208, 32082, 12401, 13999, 26264, 27299, + 20509, 1035, 20384, 14703, 20044, 12556, 7221, 31275, 13546, 11773, + 4420, 23708, 14584, 20200, 6524, 11338, 5427, 19374, 5388, 28978, + 30704, 1622, 22190, 2235, 2123, 2982, 29156, 19179, 18048, 496, + 16001, 4059, 19353, 31279, 13676, 10600, 11338, 22355, 2721, 9516, + 793, 16344, 10360, 27309, 26049, 19869, 28714, 2515, 18905, 28660, + 25489, 19872, 7345, 13301, 7215, 26395, 4733, 26393, 13170, 17549, + 28853, 18436, 54, 26864, 22459, 14753, 19852, 22437, 20415, 11380, + 28346, 26725, 7392, 12844, 21635, 6010, 7827, 28536, 1960, 9775, + 15945, 24604, 11438, 8069, 12620, 8516, 29922, 5415, 4586, 13848, + 8347, 10400, 5078, 31376, 30399, 13516, 22475, 14474, 21896, 11148, + 8818, 4890, 16520, 31678, 31366, 15393, 4075, 4973, 29238, 10273, + 9469, 21060, 8139, 13391, 18412, 73, 2442, 29418, 4814, 7368, + 2268, 4556, 3557, 6540, 26796, 3903, 24866, 22101, 17169, 12561, + 16105, 28807, 25133, 29163, 27668, 15136, 10325, 22750, 2362, 29420, + 17265, 28479, 305, 764, 12198, 18613, 16593, 27988, 19763, 20880, + 7163, 11236, 3481, 22364, 986, 7610, 31743, 32694, 29289, 21621, + 9428, 8543, 12086, 22013, 8374, 13199, 20837, 9841, 18067, 18316, + 8951, 11873, 18702, 16506, 18765, 20798, 17188, 15733, 22247, 18446, + 30735, 27101, 10866, 5220, 19050, 10225, 16130, 8080, 11927, 18848, + 20892, 23059, 7262, 19355, 32759, 28502, 17366, 18095, 17539, 24987, + 2441, 2898, 29611, 8755, 3539, 7036, 7767, 31217, 17291, 30392, + 32182, 21984, 23013, 27770, 19514, 19505, 32151, 26803, 12260, 28023, + 13627, 28323, 6593, 13847, 23797, 13806, 16053, 13305, 1365, 7667, + 3447, 13444, 14719, 27481, 17885, 3786, 28809, 1002, 5466, 24063, + 11812, 7333, 23749, 16432, 5759, 3598, 859, 27953, 31080, 17097, + 31496, 14808, 6325, 29799, 23010, 23400, 11746, 10880, 11510, 32592, + 29435, 12290, 26696, 2838, 12988, 29384, 6165, 6286, 9208, 367, + 14171, 591, 5175, 7013, 20380, 5223, 9635, 1858, 17886, 13646, + 9253, 9052, 30966, 6937, 13771, 25749, 5382, 24816, 27245, 14200, + 16453, 4290, 28304, 30597, 1278, 30007, 25994, 28872, 7388, 18407, + 12007, 8113, 356, 4041, 6924, 8991, 26051, 17390, 21274, 5310, + 32202, 1647, 30741, 15251, 13879, 2980, 15686, 19669, 16950, 406, + 477, 12031, 8743, 2999, 11739, 29475, 1954, 9937, 22063, 30849, + 15032, 2365, 27679, 28248, 25724, 21333, 31722, 10236, 25283, 9259, + 16044, 30505, 2343, 26236, 897, 151, 29522, 27103, 13293, 16078, + 19577, 7234, 30588, 10044, 12326, 30615, 15013, 9605, 2107, 30667, + 7412, 24858, 3124, 11512, 2665, 269, 17836, 15835, 1051, 25124, + 15906, 28296, 23584, 29484, 14846, 32604, 26801, 11151, 9423, 30772, + 29322, 20521, 4823, 13276, 25232, 31320, 31689, 16331, 29366, 5458, + 4817, 25613, 26145, 17852, 24533, 26496, 3211, 28536, 32251, 25667, + 5349, 19902, 9067, 16492, 29504, 20846, 22102, 7929, 24045, 14756, + 29495, 23818, 9699, 6797, 26712, 28493, 28258, 18042, 19096, 13241, + 2006, 19763, 10093, 2943, 29182, 1422, 14373, 20797, 18765, 21824, + 23153, 32629, 9105, 10994, 29219, 6975, 8146, 24216, 6199, 14138, + 11645, 27646, 15120, 8904, 20162, 15267, 5125, 6523, 23098, 28107, + 5660, 1738, 32717, 23504, 14869, 12809, 5331, 17337, 19371, 26264, + 27513, 21005, 7190, 27738, 14694, 32234, 15828, 25704, 20895, 27473, + 11689, 24071, 13736, 18372, 3301, 14296, 16877, 21779, 20709, 8527, + 31072, 5747, 3036, 20582, 13410, 21456, 13012, 16275, 14055, 26468, + 1263, 2039, 29141, 2441, 30707, 11224, 29614, 3787, 19973, 25937, + 19567, 19522, 14517, 19784, 26808, 5663, 11579, 28321, 10284, 3204, + 19968, 6577, 13083, 10905, 3425, 3062, 21343, 19420, 28079, 16389, + 21635, 7096, 634, 31171, 12075, 9050, 5903, 25304, 943, 27072, + 7743, 19297, 10250, 23058, 20322, 25317, 19699, 25654, 5276, 32139, + 32329, 2975, 11871, 14730, 692, 22629, 12337, 6368, 24202, 24219, + 15024, 25415, 32239, 20445, 5843, 25701, 19009, 19240, 5746, 23107, + 11955, 24302, 24073, 10240, 20535, 29034, 31673, 8391, 31906, 818, + 28559, 32510, 5386, 11, 2313, 23488, 30579, 4608, 15830, 14990, + 11179, 10934, 24030, 32065, 22387, 14074, 18562, 21571, 15839, 12444, + 1829, 32511, 19968, 640, 28907, 28822, 1216, 29426, 15296, 11476, + 9740, 2106, 7837, 29026, 4409, 7024, 26886, 32573, 13472, 3474, + 13272, 6246, 25740, 21204, 3905, 1500, 6487, 18487, 21008, 14791, + 22735, 1885, 28207, 25078, 20190, 11420, 28269, 32395, 22891, 17233, + 385, 20725, 2939, 19575, 13900, 14111, 27548, 4984, 19807, 18151, + 29059, 17730, 17341, 15826, 1780, 32158, 31458, 6398, 4066, 16061, + 8708, 26968, 15832, 1812, 17560, 14350, 12951, 31083, 8728, 3912, + 13825, 19248, 13060, 31507, 821, 27920, 32551, 5789, 28924, 8649, + 10523, 31144, 31076, 30521, 21263, 6713, 25770, 21947, 16584, 30352, + 7005, 6580, 23270, 7005, 25537, 27497, 30569, 23089, 3698, 18708, + 2931, 17459, 22089, 30518, 10831, 15636, 30595, 19246, 30131, 14917, + 16928, 1035, 25410, 615, 11146, 9631, 31573, 259, 9079, 2516, + 29947, 635, 12304, 7236, 21311, 27712, 5459, 29699, 26537, 17734, + 2998, 16319, 24071, 8885, 18957, 14348, 13676, 19682, 26285, 3065, + 19066, 12413, 21458, 31719, 22237, 28684, 31550, 5787, 2584, 10647, + 15651, 19601, 8068, 17573, 22083, 19859, 5525, 23656, 29014, 25739, + 27232, 6709, 26540, 17814, 346, 5175, 29971, 601, 5731, 1123, + 25339, 6296, 25091, 27566, 28376, 20228, 21235, 30078, 28616, 21589, + 223, 12127, 32630, 21657, 15466, 26872, 10814, 30924, 24691, 20541, + 27282, 1192, 18582, 7654, 29596, 1726, 4342, 26143, 3876, 20166, + 826, 24961, 4789, 7732, 21718, 16961, 12283, 18188, 6796, 27047, + 9328, 11510, 5426, 4546, 3128, 23701, 15285, 16219, 23624, 11541, + 9822, 4135, 18382, 8195, 16212, 14656, 5325, 29538, 23651, 18902, + 9224, 6651, 9723, 1570, 12322, 3572, 28540, 28316, 2051, 12708, + 25776, 18286, 18558, 12966, 12441, 13352, 22832, 9004, 366, 15285, + 12459, 15766, 3552, 18574, 9172, 1604, 32673, 31021, 17545, 16296, + 26407, 21701, 4999, 25759, 7342, 14384, 2443, 7425, 12924, 3881, + 15288, 24694, 23656, 11707, 7993, 15244, 28829, 24057, 12327, 27399, + 26012, 30192, 32685, 16761, 29056, 2522, 28010, 2834, 11910, 3733, + 4118, 25528, 26828, 83, 4457, 13146, 31065, 24978, 312, 17538, + 21086, 9807, 25570, 10538, 8379, 24438, 32746, 25041, 9925, 15006, + 12005, 31438, 13383, 10739, 13719, 5091, 24519, 18547, 26457, 1557, + 25866, 20062, 6333, 7099, 19674, 10727, 9589, 3798, 16902, 4144, + 3441, 16800, 14251, 6773, 27976, 25960, 17586, 17288, 21977, 924, + 22682, 22909, 18226, 7464, 11639, 14246, 21169, 21502, 6971, 23746, + 10344, 21238, 12341, 12657, 5062, 26585, 13015, 4843, 11124, 952, + 28860, 30194, 14462, 5507, 32490, 13829, 6323, 29399, 20308, 25815, + 8270, 17174, 16291, 11775, 16086, 8381, 1333, 8945, 27458, 25842, + 6758, 813, 7009, 20829, 4367, 21686, 19065, 3129, 28237, 24774, + 21295, 8875, 29572, 26816, 8358, 1362, 30883, 10086, 31467, 13087, + 15273, 29260, 29329, 1639, 18792, 3980, 12184, 22582, 25480, 5052, + 10132, 28135, 2000, 23581, 32522, 25841, 31905, 30997, 6686, 4356, + 1731, 7392, 8299, 85, 11365, 25218, 30771, 6710, 10590, 256, + 31928, 3049, 30905, 16079, 29673, 30646, 6001, 14733, 1857, 6715, + 25721, 21999, 17389, 23044, 16895, 27643, 26157, 30043, 23538, 887, + 29692, 16993, 14274, 13473, 27379, 13662, 29031, 288, 1387, 8534, + 18852, 14651, 23266, 26333, 27021, 16713, 24480, 1323, 32583, 19304, + 1945, 16755, 6159, 22898, 4246, 22641, 23421, 6957, 14224, 28227, + 3082, 31938, 23900, 12409, 28881, 31089, 24206, 24175, 3218, 1666, + 8855, 11227, 17916, 10879, 9776, 12706, 25900, 5280, 1276, 24816, + 2673, 18567, 18024, 11399, 594, 12845, 29143, 23639, 6866, 13853, + 11772, 16207, 24050, 4576, 28550, 12148, 21880, 8376, 31491, 7216, + 15343, 22673, 15870, 3820, 16111, 4916, 17259, 24033, 13982, 30654, + 18246, 298, 30327, 6224, 11638, 15902, 6986, 6153, 24567, 7390, + 31364, 27953, 22584, 23594, 5809, 5047, 22466, 22393, 15679, 17374, + 5147, 4936, 12677, 307, 23018, 25522, 2308, 5570, 27483, 24897, + 7404, 19849, 7621, 8196, 13538, 1581, 31371, 23777, 20224, 26348, + 26432, 5421, 10525, 21715, 6414, 15482, 28495, 30513, 22360, 1200, + 13231, 24651, 20894, 27744, 4956, 11857, 3233, 1876, 11459, 14481, + 10952, 13675, 19229, 7403, 5059, 15268, 27448, 16773, 16238, 29560, + 32123, 7872, 10232, 1133, 2341, 25962, 10052, 23528, 5333, 21813, + 17295, 22338, 30799, 22375, 12116, 21535, 31613, 12756, 108, 6907, + 26292, 26121, 25210, 22534, 26681, 4299, 18566, 5682, 29444, 24000, + 25064, 28810, 31427, 25451, 22120, 28101, 12578, 31582, 3491, 14875, + 10314, 27306, 1457, 13143, 32423, 28315, 32004, 31534, 30703, 418, + 29587, 975, 10080, 27795, 18232, 19131, 3913, 30736, 28503, 13783, + 22987, 21061, 7105, 18111, 3809, 31959, 15064, 6238, 27550, 22294, + 10995, 23228, 16054, 6266, 6916, 16662, 28224, 20301, 24184, 5684, + 2746, 14245, 14430, 27383, 4942, 32405, 25036, 4914, 20307, 17828, + 20350, 25786, 24855, 31358, 31425, 27841, 30172, 22250, 12687, 3935, + 14112, 7420, 12796, 17778, 24866, 25484, 5329, 27816, 8865, 1767, + 19318, 24026, 10727, 4547, 13589, 15772, 774, 12921, 3836, 15122, + 11106, 4206, 17475, 12549, 32367, 5586, 24751, 23861, 24508, 398, + 10967, 8689, 17969, 21879, 10629, 1510, 3221, 29896, 6239, 26165, + 11090, 1743, 4039, 3784, 16253, 29158, 25599, 11493, 785, 677, + 32657, 20483, 6779, 4950, 15598, 27869, 23388, 18345, 853, 5830, + 28810, 12458, 10998, 25404, 32600, 27887, 27736, 11199, 12823, 6422, + 24312, 8676, 19420, 9327, 19251, 27045, 10628, 24053, 21349, 11771, + 21494, 19843, 14419, 25519, 1960, 11588, 15558, 11884, 21861, 5996, + 27769, 7194, 16461, 15397, 18997, 10855, 32029, 13260, 10555, 19110, + 14000, 16734, 13794, 22493, 31686, 6445, 29057, 13081, 14853, 32728, + 9615, 31237, 27197, 21881, 4617, 3003, 20976, 24864, 23316, 20442, + 32274, 28801, 31204, 29657, 23309, 8169, 13532, 4763, 3592, 24183, + 25335, 31299, 29545, 648, 32358, 1097, 26264, 627, 19067, 13262, + 14086, 9591, 4293, 3628, 28271, 16625, 14717, 25042, 19548, 21970, + 17184, 28495, 6891, 8785, 23902, 10412, 16594, 31709, 11059, 31487, + 13474, 1366, 28756, 22077, 13641, 5500, 11724, 26448, 27611, 25000, + 29159, 14441, 23186, 16570, 24399, 11160, 15635, 6738, 12149, 922, + 15858, 22083, 13362, 28402, 9593, 24644, 7343, 18747, 26021, 5097, + 386, 1724, 10098, 9426, 31769, 32317, 15704, 22307, 19804, 1305, + 7958, 7219, 22414, 4501, 21166, 26505, 28532, 22384, 26289, 20484, + 18618, 4795, 25020, 20825, 402, 29931, 19267, 16835, 4510, 26669, + 12685, 20469, 8208, 7417, 20736, 7508, 1296, 5610, 16543, 18531, + 31326, 9677, 18385, 31614, 16184, 5112, 16740, 21452, 13182, 23261, + 12241, 13423, 11796, 13534, 26306, 23037, 4744, 27491, 28113, 16948, + 27124, 7210, 147, 15337, 806, 26847, 20437, 13122, 24063, 17645, + 26661, 3621, 26137, 21907, 24981, 11504, 16556, 6202, 15547, 25451, + 12723, 17158, 4348, 32251, 23626, 5035, 28683, 10008, 25179, 130, + 8876, 16640, 10105, 32732, 8894, 1965, 15116, 22934, 30328, 3845, + 11695, 14061, 20723, 29010, 21974, 25748, 7144, 16881, 18356, 31492, + 20156, 6222, 1116, 24852, 14009, 18192, 9254, 23753, 5279, 5008, + 84, 9839, 13147, 400, 8181, 12776, 23574, 1834, 27658, 8773, + 19313, 15622, 28392, 7059, 3087, 24272, 8925, 26359, 23125, 12265, + 5142, 7422, 30061, 6237, 22976, 18990, 401, 3606, 10862, 16865, + 24454, 7759, 4027, 4947, 7526, 11083, 32048, 21753, 25763, 12870, + 20072, 14868, 2377, 11692, 28785, 9863, 7811, 26505, 14966, 13541, + 13409, 20544, 14747, 25295, 8920, 11649, 29737, 22397, 1547, 13685, + 26603, 30874, 20424, 27195, 18392, 16725, 4694, 18840, 15601, 4021, + 26734, 9450, 11885, 23596, 7815, 13662, 21780, 3952, 343, 16483, + 7657, 27270, 30102, 24671, 8203, 13599, 15621, 17824, 10717, 9768, + 3247, 917, 10597, 15005, 22638, 15934, 29682, 28318, 30642, 19425, + 12546, 3268, 1918, 7935, 27746, 1973, 11699, 3206, 5015, 4598, + 8593, 16968, 25213, 26625, 7547, 428, 814, 152, 7674, 29347, + 25893, 5976, 4613, 8926, 4004, 17809, 24704, 1343, 29522, 7509, + 26370, 1421, 32553, 31439, 21979, 1131, 31887, 32726, 3414, 20363, + 30403, 28098, 24635, 27167, 12667, 20863, 24472, 14736, 29388, 13792, + 5057, 27251, 9289, 20533, 18070, 8633, 3951, 23327, 17156, 20005, + 14657, 5027, 11727, 11780, 8495, 3198, 23438, 23832, 26428, 19384, + 18423, 32469, 7520, 26236, 4700, 22736, 18475, 3879, 9078, 29044, + 21164, 18847, 24674, 1782, 31121, 14822, 3817, 29324, 29071, 16623, + 20228, 19418, 19717, 23472, 19871, 24749, 25820, 30840, 5278, 21740, + 18609, 13955, 7198, 32460, 31286, 17356, 253, 21314, 30677, 19746, + 1180, 30398, 23191, 13438, 14819, 17942, 2885, 404, 902, 27584, + 21197, 16552, 18311, 21829, 18918, 17315, 31741, 7410, 27639, 31078, + 28947, 15313, 20789, 20318, 31827, 6104, 21516, 20824, 26917, 16991, + 9665, 31639, 21138, 31448, 5717, 29476, 19022, 18874, 5036, 6246, + 28918, 18366, 9199, 10226, 25494, 25934, 24366, 23972, 8814, 17978, + 11338, 19951, 21750, 1454, 17682, 26993, 3871, 30305, 17836, 24267, + 31708, 13070, 19255, 20143, 4321, 4904, 26030, 1595, 12202, 22474, + 24847, 13189, 22464, 9453, 10685, 18551, 20024, 6877, 20515, 32733, + 24188, 9198, 28571, 14524, 6222, 19050, 31886, 7141, 12041, 10959, + 15836, 16331, 24106, 22071, 30078, 16018, 30480, 14616, 31850, 29288, + 31744, 23085, 21942, 19480, 27756, 1460, 32001, 30684, 9845, 14554, + 29711, 17523, 430, 24948, 21427, 116, 1827, 17903, 25586, 16866, + 27290, 10634, 32613, 19456, 10520, 194, 17887, 1895, 28839, 18958, + 25168, 16469, 13655, 24269, 26601, 8008, 6019, 20605, 2689, 4044, + 32597, 9773, 13880, 2449, 4492, 3637, 19034, 28608, 24957, 22564, + 31622, 31375, 1112, 19194, 8463, 24340, 27053, 11160, 29090, 10582, + 7471, 1908, 29481, 31914, 21943, 6033, 8688, 11837, 30092, 20399, + 4870, 28141, 24545, 28095, 5607, 29021, 18335, 7577, 6487, 21832, + 23397, 1265, 22417, 10517, 29389, 19335, 3489, 22700, 8434, 29599, + 16911, 26761, 20527, 5809, 24253, 14350, 19241, 23137, 8722, 15902, + 22287, 12721, 11096, 29671, 17184, 15990, 9827, 25087, 4462, 26377, + 23156, 947, 7394, 29758, 8285, 29852, 24705, 19354, 997, 7283, + 17389, 6592, 5229, 19749, 4399, 301, 10452, 1834, 30643, 8485, + 6809, 22547, 16532, 2285, 15423, 1891, 6171, 27451, 3830, 1440, + 4226, 9707, 31607, 20906, 16893, 29171, 4656, 18507, 11259, 20377, + 5793, 4622, 15272, 740, 23046, 9045, 7095, 13392, 10550, 678, + 12640, 2501, 20939, 18758, 27300, 19402, 643, 4617, 16856, 27383, + 2790, 19048, 6753, 31144, 15913, 6612, 23229, 28153, 32083, 21322, + 26563, 16366, 11422, 9512, 25899, 14321, 6176, 31551, 24400, 386, + 2986, 18217, 16949, 22058, 8689, 8924, 16838, 21680, 3819, 384, + 5985, 22672, 27004, 25214, 14485, 26025, 14418, 15066, 27434, 31798, + 17934, 2242, 23137, 26879, 164, 17767, 4476, 26065, 26628, 10507, + 32641, 7723, 20474, 30043, 26684, 27706, 31058, 14848, 26243, 25095, + 17985, 18289, 25534, 29424, 3584, 17419, 10406, 20821, 21434, 5987, + 27468, 21651, 25768, 8155, 32349, 31542, 8571, 26227, 1445, 23914, + 30458, 17634, 21459, 9139, 18924, 12774, 21884, 21982, 23096, 10200, + 11618, 5688, 24907, 22915, 26101, 7421, 28602, 10550, 23578, 2067, + 20008, 5656, 18220, 21240, 25963, 5052, 576, 2810, 29162, 32757, + 31406, 5030, 1609, 10988, 26573, 3970, 8521, 9583, 12285, 16639, + 23273, 18828, 7915, 30633, 27858, 12639, 20752, 23969, 6641, 4136, + 30843, 14669, 14694, 8608, 24273, 30542, 26781, 9102, 18578, 4274, + 10426, 3477, 7581, 8555, 27451, 31568, 31479, 17152, 20952, 31905, + 4552, 32643, 9646, 16453, 30630, 28867, 18845, 19628, 19478, 24514, + 11247, 15112, 22352, 30826, 31546, 10405, 1603, 25979, 8839, 21112, + 26585, 26824, 20659, 3834, 3964, 17315, 3862, 6711, 217, 918, + 11107, 26657, 2276, 1876, 30927, 28971, 10268, 20462, 11382, 11539, + 29200, 29258, 22778, 12287, 1570, 15570, 19821, 7628, 1419, 25961, + 23409, 26531, 10911, 21411, 18589, 1997, 25938, 23349, 28451, 12232, + 8444, 31402, 20912, 7322, 5977, 29272, 9876, 1381, 21716, 18323, + 7715, 22588, 20997, 2080, 26715, 14645, 18040, 25068, 15815, 30655, + 6320, 25342, 28578, 28214, 3947, 30123, 17216, 32693, 10280, 27913, + 7338, 10284, 14055, 13734, 15497, 21170, 13104, 32377, 5445, 27164, + 22102, 28184, 31597, 22478, 28413, 14380, 13658, 25809, 3546, 10018, + 11791, 12582, 28602, 28196, 20324, 13658, 18630, 22344, 18276, 603, + 21765, 8711, 15603, 13194, 21026, 9712, 14260, 18193, 18032, 3485, + 16169, 20599, 14635, 15847, 29980, 14706, 16385, 15702, 12094, 1276, + 1488, 12251, 21519, 18600, 11954, 15042, 32509, 20535, 15671, 25826, + 12240, 8038, 23793, 18199, 2373, 17931, 13878, 8434, 25337, 14944, + 12065, 9646, 12937, 11679, 15834, 5478, 4088, 3791, 12311, 30342, + 22865, 12253, 3043, 896, 14733, 16622, 28063, 8384, 5814, 5620, + 1862, 28385, 11085, 27263, 9461, 19745, 20202, 6103, 23210, 24594, + 12834, 28999, 16203, 28819, 4525, 15035, 22763, 14079, 9416, 28046, + 7754, 26276, 19161, 16285, 12698, 3447, 15442, 8352, 14473, 7173, + 17111, 8634, 30075, 10669, 15523, 31566, 1559, 18701, 28456, 9493, + 27398, 186, 27893, 13421, 15607, 15412, 4829, 29940, 13031, 18713, + 24662, 28502, 23967, 13464, 12063, 21091, 15486, 7465, 3622, 29285, + 3960, 19579, 17320, 21168, 2655, 11694, 15002, 28026, 19809, 29635, + 24392, 11757, 18131, 1105, 12092, 20603, 25133, 31353, 97, 7538, + 10325, 22043, 17256, 12752, 13600, 11082, 17771, 19032, 9750, 26110, + 13520, 17637, 26229, 30217, 28017, 19455, 31865, 30541, 20473, 24748, + 15323, 10795, 10385, 13960, 407, 20543, 26813, 5656, 23795, 16714, + 14464, 14684, 18877, 19567, 8903, 22179, 24532, 14755, 24837, 19875, + 7146, 32607, 2586, 10953, 9607, 151, 10361, 29902, 13905, 26352, + 31626, 30859, 1676, 27950, 26701, 1806, 25309, 11755, 23873, 23021, + 20307, 19087, 11652, 4570, 23463, 32380, 10043, 888, 3267, 29321, + 26932, 18030, 32642, 25778, 15306, 15033, 10423, 30734, 28937, 30790, + 22128, 16641, 4115, 23744, 22140, 25010, 20418, 11175, 26154, 19135, + 9359, 29858, 20308, 29744, 26035, 13334, 22172, 8874, 19852, 25079, + 24310, 3301, 22849, 25310, 26215, 30048, 19369, 3932, 16396, 3156, + 22377, 17807, 12125, 11750, 13405, 14578, 18611, 20640, 9274, 29817, + 15112, 14370, 10865, 17118, 26366, 16405, 31574, 28981, 26403, 21345, + 26241, 8135, 28020, 25504, 16500, 5405, 6688, 29906, 3967, 18267, + 12174, 4434, 32737, 4048, 18336, 14857, 25392, 15562, 28218, 9953, + 16123, 30127, 17662, 27516, 11949, 9545, 5210, 21543, 7348, 24162, + 28101, 3849, 5859, 28305, 17221, 30112, 16917, 4947, 6197, 2671, + 12564, 1373, 12101, 32496, 8889, 20017, 22978, 21968, 27476, 7152, + 19480, 30043, 3405, 1903, 7061, 15926, 17676, 140, 18799, 31308, + 3095, 21699, 32521, 14346, 22394, 1020, 24434, 26063, 5484, 22439, + 4654, 16798, 11823, 28683, 27450, 28951, 3128, 15323, 21578, 21925, + 28560, 3595, 23381, 28920, 4218, 17889, 2643, 28086, 21846, 15695, + 27718, 8301, 8133, 29056, 25463, 5325, 29966, 6577, 13570, 31508, + 1473, 8258, 28052, 2505, 22682, 12813, 884, 10157, 7335, 22518, + 22976, 29167, 26285, 20163, 2298, 29343, 24009, 4399, 4291, 7013, + 6995, 18489, 22245, 22643, 16903, 5570, 6039, 20408, 4199, 27991, + 11005, 8278, 29275, 28492, 17391, 13911, 22043, 31242, 3067, 1691, + 18510, 26025, 16557, 18197, 7028, 7618, 13837, 30520, 7809, 3454, + 25194, 19627, 9930, 4595, 22358, 22596, 28965, 26289, 19442, 30516, + 7756, 16535, 7276, 25727, 20099, 2796, 25285, 32432, 8315, 31403, + 3288, 5578, 6005, 27677, 5016, 21889, 11434, 16045, 14233, 8106, + 4075, 8953, 26322, 27377, 23429, 3877, 12193, 31178, 16231, 19828, + 25871, 32170, 26503, 22809, 22614, 31350, 8577, 32074, 19360, 32316, + 1409, 27287, 530, 289, 9828, 13946, 4361, 410, 3354, 31023, + 22179, 12511, 847, 32197, 14722, 17309, 29002, 14454, 5580, 15083, + 21269, 30516, 31606, 14720, 2270, 132, 8643, 14627, 1693, 5263, + 13319, 13410, 9975, 1142, 23473, 6279, 9570, 21479, 11144, 5064, + 907, 6382, 20533, 5054, 24053, 3597, 10754, 2073, 29666, 2702, + 13281, 15761, 26230, 21784, 23472, 25888, 29466, 1153, 12219, 7022, + 11551, 25315, 1411, 1165, 31395, 1778, 32190, 20454, 22713, 6028, + 2462, 14451, 10948, 28050, 21846, 21807, 6436, 2039, 15323, 19915, + 17173, 1558, 10215, 18339, 18387, 7241, 1604, 7920, 27395, 14007, + 11591, 16447, 22606, 15981, 23963, 13858, 19023, 13489, 16447, 16165, + 6319, 6290, 1490, 3, 29070, 19140, 2831, 9754, 16933, 25515, + 9876, 3613, 4778, 12214, 999, 17161, 22900, 17902, 12218, 26970, + 2400, 27855, 14702, 27262, 24435, 16726, 3543, 29834, 13249, 21595, + 28479, 8748, 20877, 12833, 11949, 24288, 6833, 2092, 2663, 23399, + 31297, 23890, 21941, 9846, 27243, 4117, 19944, 15787, 4513, 3888, + 16539, 10948, 15099, 112, 26425, 275, 11734, 18885, 7103, 20269, + 10434, 12441, 19139, 22755, 923, 864, 18102, 11635, 15605, 369, + 24678, 28159, 30397, 29972, 26822, 22589, 9758, 13276, 32754, 26993, + 31394, 31031, 21870, 11672, 16538, 20291, 15790, 3814, 17087, 31203, + 16604, 14707, 28441, 6163, 1749, 26418, 17184, 28536, 17525, 10274, + 17186, 5691, 3604, 31432, 228, 28440, 13136, 18652, 30092, 11186, + 2061, 7235, 12990, 18730, 16059, 8253, 12038, 15087, 14296, 17120, + 12662, 14296, 27317, 2757, 8336, 23920, 19324, 29953, 14438, 7569, + 21106, 28502, 27647, 10804, 2170, 17067, 30205, 21383, 15579, 442, + 23677, 3136, 18498, 20489, 27711, 2097, 267, 5403, 28423, 10525, + 17596, 28647, 8514, 11723, 15058, 32393, 10989, 22360, 13505, 20024, + 8115, 30929, 20278, 1912, 19029, 9474, 14495, 10896, 18255, 28296, + 16621, 28735, 12985, 28206, 5784, 8875, 28011, 886, 21732, 666, + 15716, 885, 18276, 3300, 19821, 18916, 1724, 13353, 20473, 24693, + 21162, 25279, 18768, 12387, 19784, 22766, 17644, 29261, 31627, 15200, + 31267, 20548, 12048, 26067, 9223, 28195, 15640, 22033, 4235, 19486, + 9937, 17186, 10983, 14238, 5847, 13504, 1341, 15949, 22522, 12043, + 9596, 22380, 10562, 18046, 6533, 14189, 4912, 12322, 5772, 446, + 8775, 8705, 10177, 19683, 10955, 6110, 12336, 11463, 31177, 20515, + 17382, 14164, 5975, 5403, 7254, 14145, 32285, 9825, 11259, 299, + 27668, 22169, 5438, 7912, 12430, 12637, 2201, 17688, 7031, 15738, + 19965, 18172, 29146, 12125, 32113, 31884, 2982, 8136, 11005, 20163, + 32435, 6858, 2502, 24126, 31488, 6893, 19066, 30725, 15423, 17585, + 14574, 19625, 13459, 30265, 23814, 7567, 21656, 32581, 8918, 21643, + 5284, 5472, 19974, 14768, 9036, 22619, 15483, 15207, 23850, 31481, + 15411, 31935, 29150, 25580, 30833, 7684, 23123, 23296, 16923, 29587, + 1092, 5783, 14176, 7916, 22270, 15701, 3741, 15881, 30055, 12316, + 2178, 9455, 24895, 12347, 19825, 21890, 25510, 13794, 14886, 10653, + 24286, 2778, 24477, 25442, 27987, 32114, 14896, 6095, 29144, 16317, + 25601, 10130, 32052, 18170, 18071, 14879, 11485, 16212, 4044, 28415, + 26371, 26572, 28134, 14699, 6096, 27251, 22276, 6681, 21487, 1244, + 24508, 11094, 1099, 5329, 25858, 13141, 32537, 23877, 3403, 12487, + 12824, 6026, 25843, 5547, 16919, 9412, 6620, 9320, 17337, 29598, + 6745, 8985, 29045, 28219, 6986, 22824, 736, 1136, 18824, 2846, + 2351, 9776, 10883, 26809, 29943, 16967, 28713, 8512, 10552, 7499, + 17418, 4627, 16282, 5328, 19898, 17286, 25078, 8924, 30839, 10223, + 23832, 13723, 15372, 20734, 20676, 12115, 27893, 3917, 8438, 8857, + 22468, 12344, 3182, 7878, 11204, 19951, 3180, 8357, 30939, 26057, + 25017, 31598, 17589, 10460, 29414, 19960, 31518, 15050, 24536, 22570, + 15792, 21213, 28293, 17587, 22191, 29527, 13560, 15098, 21297, 11011, + 9371, 19800, 2178, 5392, 23221, 6022, 2246, 2847, 23090, 8529, + 19168, 15771, 4771, 22933, 2451, 9801, 22661, 15720, 7655, 2336, + 12998, 32662, 22735, 30554, 8581, 17486, 8200, 2251, 16922, 17690, + 21060, 16242, 29782, 28948, 13960, 18766, 25867, 23896, 31443, 5234, + 8655, 6581, 1536, 26972, 6654, 15784, 8429, 32643, 12542, 16778, + 15069, 16431, 17863, 30728, 19739, 11149, 21410, 15316, 8673, 5123, + 4144, 9626, 631, 9153, 10927, 24118, 4255, 24729, 4217, 24868, + 20273, 8720, 23245, 17207, 7683, 11701, 18898, 20878, 11531, 28081, + 28889, 7365, 10952, 30112, 30691, 17512, 12765, 18847, 5731, 5805, + 14848, 7078, 11077, 22626, 4114, 29465, 5678, 18724, 6543, 29337, + 13830, 14290, 26409, 12320, 12468, 15169, 23216, 21325, 26664, 25393, + 25653, 28156, 16543, 30080, 6767, 17178, 7202, 2974, 12894, 28600, + 12112, 9281, 6701, 10845, 4958, 32090, 15197, 3233, 14684, 19015, + 10272, 6903, 8890, 6730, 14339, 32459, 778, 826, 22480, 9933, + 12039, 25720, 4275, 16780, 31959, 22053, 2715, 17990, 31117, 6290, + 3555, 32766, 6130, 6136, 18482, 20176, 15493, 22634, 22688, 4994, + 19136, 24071, 26394, 31067, 6045, 25972, 19207, 4664, 25945, 138, + 1323, 30940, 19576, 27467, 4300, 28040, 13833, 11547, 20500, 8395, + 19243, 23085, 6071, 8348, 32208, 2363, 7638, 7078, 3777, 20858, + 20514, 28387, 13539, 25805, 21828, 12652, 18872, 6712, 15741, 12944, + 7976, 31896, 9865, 7715, 25218, 12690, 22218, 15128, 12478, 19658, + 5026, 30803, 16389, 25429, 14113, 15288, 17011, 29552, 18656, 10120, + 26139, 29810, 7724, 18925, 5707, 8959, 17767, 27347, 25106, 23935, + 16556, 27642, 11264, 9422, 21426, 28489, 1837, 5429, 26867, 14182, + 25980, 27546, 21005, 30339, 31521, 27379, 26472, 32635, 31585, 13825, + 19225, 3357, 4761, 28986, 21254, 5063, 16770, 16647, 24139, 20792, + 32179, 30767, 6621, 3743, 15208, 2707, 30829, 18679, 17537, 6173, + 24976, 13207, 206, 9203, 23580, 18693, 5001, 19440, 14293, 24076, + 29220, 5204, 31305, 27406, 22598, 7098, 4856, 32667, 15632, 19275, + 6840, 5185, 11684, 22322, 8792, 11942, 12520, 9039, 21636, 3029, + 6279, 24624, 28392, 17622, 25401, 29757, 22169, 8577, 5189, 1330, + 19376, 3475, 9284, 13779, 56, 25602, 28455, 27611, 5706, 1166, + 17275, 12853, 18806, 26521, 30444, 2926, 18277, 9272, 9224, 18539, + 31677, 7306, 30022, 14167, 21317, 17481, 8519, 16096, 14922, 13726, + 31835, 14755, 23105, 2106, 17223, 9700, 23213, 7842, 838, 32131, + 10563, 24010, 17229, 18987, 3404, 742, 21139, 18893, 2636, 2941, + 24657, 14157, 21985, 25535, 16582, 2939, 25837, 20248, 895, 19012, + 21495, 16133, 16085, 12330, 11983, 3111, 13495, 21489, 23409, 17814, + 13482, 4251, 11308, 2679, 24333, 19351, 23812, 8657, 11682, 27446, + 84, 17564, 25930, 4446, 7864, 19995, 21776, 28290, 3625, 18028, + 1641, 16593, 17514, 31582, 17082, 21422, 10404, 7042, 4635, 4961, + 10317, 1925, 5979, 2316, 4941, 19900, 27972, 21216, 868, 1025, + 26912, 27792, 21190, 29630, 26236, 23558, 26121, 19527, 43, 5718, + 19777, 20333, 23182, 12528, 106, 11344, 25363, 13081, 15110, 31860, + 10492, 7068, 15110, 5911, 13154, 28348, 26350, 11970, 30841, 24686, + 7989, 27935, 23061, 28357, 16106, 266, 19309, 174, 3998, 21226, + 1055, 23773, 13437, 21314, 25717, 3335, 20670, 6019, 28391, 338, + 27966, 14629, 31457, 2058, 24935, 10997, 1222, 28053, 17239, 30897, + 16180, 22697, 9022, 9426, 18826, 14012, 5640, 6313, 12199, 19644, + 21309, 23213, 5877, 16385, 27173, 32532, 15527, 11136, 21039, 1696, + 10558, 5441, 23187, 8124, 15698, 22547, 15671, 32199, 31631, 21886, + 22736, 15024, 13310, 1017, 22089, 30403, 29829, 11613, 15720, 10946, + 25266, 29082, 15535, 24632, 25268, 25000, 10368, 17418, 12202, 18113, + 30085, 18255, 18721, 27086, 12879, 16993, 22999, 28447, 17469, 20020, + 31685, 6750, 27552, 18407, 32093, 3782, 21276, 12953, 3089, 24464, + 11858, 27518, 26644, 1198, 11195, 25650, 1380, 1980, 15831, 22349, + 15880, 32052, 32648, 11063, 31126, 7649, 14264, 30526, 28153, 5270, + 16450, 24293, 23921, 21535, 21520, 14207, 30171, 2424, 2177, 11261, + 13619, 32317, 30221, 21253, 30551, 32610, 21547, 27460, 4366, 7204, + 3186, 12804, 7240, 20799, 2303, 2049, 22952, 29794, 16920, 22648, + 19263, 29664, 20238, 30338, 27803, 16040, 32360, 25760, 11264, 26743, + 14653, 14999, 2293, 24140, 13794, 7550, 31746, 16157, 15814, 32277, + 3001, 11464, 2713, 4440, 13043, 10572, 32436, 14164, 31836, 21561, + 15541, 17466, 30514, 7269, 25193, 24498, 26953, 14817, 27376, 28273, + 25171, 1067, 31771, 14862, 5214, 30885, 8985, 19217, 5534, 1350, + 17491, 10574, 28525, 27536, 17044, 30321, 7824, 20418, 8557, 5849, + 26812, 18510, 9944, 23722, 11096, 397, 16411, 26909, 1925, 18866, + 11759, 14363, 3932, 6397, 15330, 2571, 22646, 27185, 1786, 8168, + 6066, 30535, 6043, 32150, 10802, 11876, 21162, 9846, 12199, 29449, + 1523, 2104, 6607, 32649, 5064, 2032, 21820, 31425, 491, 25964, + 28502, 5635, 28591, 26821, 23971, 18259, 14169, 3637, 19014, 29654, + 16076, 9256, 2502, 7815, 15206, 3990, 7697, 28460, 27941, 10895, + 27329, 9609, 5390, 28952, 32413, 19667, 10959, 28725, 5731, 17255, + 5539, 11079, 15557, 27324, 27766, 27544, 11185, 5014, 330, 11829, + 21205, 31357, 15321, 9212, 10083, 26734, 1915, 30709, 8465, 19408, + 22603, 20454, 2149, 23977, 2949, 15137, 31279, 10002, 10035, 929, + 17495, 31465, 15625, 5461, 32728, 14277, 29837, 28604, 3085, 26159, + 123, 21938, 20268, 1807, 871, 12170, 24592, 9828, 10148, 21340, + 32059, 1592, 28261, 10144, 29979, 30164, 31266, 21500, 25484, 10544, + 22589, 29787, 26783, 9389, 3558, 575, 15513, 4358, 27708, 26712, + 17727, 2948, 28596, 29781, 4177, 18034, 11967, 6906, 22357, 3537, + 7079, 17011, 28821, 1101, 7610, 1768, 20843, 11375, 17835, 11601, + 1468, 4627, 8501, 14476, 16579, 10269, 29185, 21676, 27408, 11880, + 19402, 3221, 20944, 13311, 23999, 32198, 23374, 2448, 27433, 32373, + 25786, 14365, 14157, 27105, 11562, 20469, 26772, 1764, 29150, 9187, + 9961, 15559, 8136, 17573, 31075, 2651, 15947, 20639, 10020, 29509, + 30532, 27601, 8065, 22426, 24647, 29703, 11619, 27052, 26923, 17743, + 31212, 13859, 5064, 8880, 8798, 29263, 22783, 5913, 25913, 1212, + 24315, 25240, 10916, 31751, 25783, 18380, 27662, 26609, 1728, 19869, + 16149, 18610, 16701, 18730, 1959, 2192, 21292, 17411, 5479, 13231, + 26350, 4761, 12370, 25131, 9222, 12019, 22690, 2148, 3614, 11170, + 15858, 8601, 21834, 21238, 12708, 26809, 31633, 18034, 6272, 4981, + 29610, 12926, 8093, 17306, 7427, 27228, 14469, 8862, 16648, 93, + 8340, 23416, 30113, 11861, 30625, 26293, 2300, 8780, 10721, 23753, + 23182, 886, 12551, 17059, 17748, 17254, 28848, 1156, 32607, 3966, + 12799, 4880, 4751, 30378, 644, 22182, 21151, 5966, 31331, 27885, + 29793, 27004, 11698, 25775, 10827, 31083, 19746, 27627, 27187, 9764, + 14818, 9253, 16471, 14463, 4288, 4353, 21408, 26190, 28818, 21776, + 32552, 5592, 32724, 12522, 9465, 30089, 30098, 30845, 7704, 15335, + 10633, 29348, 27586, 9160, 28553, 22268, 3573, 30914, 16925, 24051, + 9010, 18404, 110, 5921, 13698, 2211, 17253, 31196, 22711, 21015, + 3600, 25606, 32244, 28776, 23879, 31530, 8303, 28291, 23183, 9159, + 12150, 26641, 13886, 748, 22960, 14249, 27872, 15877, 7590, 21828, + 31706, 27893, 17281, 30962, 18765, 29285, 9237, 21964, 17213, 11867, + 24863, 298, 31219, 16142, 29387, 23062, 14751, 17401, 11898, 1280, + 26434, 22318, 10945, 20403, 9419, 12525, 9397, 4487, 128, 10613, + 19046, 23512, 13216, 3354, 3753, 3768, 13383, 30360, 27163, 9697, + 7825, 9127, 11618, 20973, 14594, 30671, 18614, 12746, 6182, 8574, + 11425, 16488, 9588, 5464, 10242, 19124, 3448, 21454, 26456, 458, + 22834, 11506, 31037, 32102, 14009, 30345, 31793, 17052, 13603, 26722, + 22937, 8906, 30633, 4839, 12807, 1738, 9223, 9202, 24252, 31340, + 4130, 28130, 11372, 3107, 29421, 25837, 13121, 21758, 7309, 1720, + 28706, 15578, 31164, 24612, 820, 20183, 26219, 9579, 14241, 12808, + 5035, 4205, 14489, 6767, 1229, 31044, 6039, 17960, 26568, 23838, + 2022, 23268, 19676, 18252, 29539, 14233, 373, 20630, 19588, 3704, + 4690, 10450, 16793, 23328, 14566, 24245, 13650, 2321, 14335, 23016, + 8322, 24163, 27557, 15198, 16674, 32440, 4165, 6118, 15001, 6767, + 1295, 16636, 2875, 7706, 3484, 4069, 31503, 30258, 22228, 26363, + 26581, 15114, 17167, 23877, 4679, 22786, 16762, 18506, 20640, 2301, + 13501, 6183, 19035, 11541, 11482, 14816, 8509, 25997, 30808, 29684, + 11157, 29190, 20642, 28980, 9155, 13046, 9642, 28116, 28919, 24407, + 19665, 15043, 27963, 17052, 5899, 2872, 19636, 5865, 4670, 16526, + 2596, 20355, 31212, 10321, 31924, 26502, 6930, 24729, 31473, 2262, + 12112, 22166, 12052, 8023, 29513, 4973, 134, 11361, 31821, 19266, + 13987, 28270, 4406, 25238, 14527, 12566, 2703, 24020, 3942, 22846, + 32252, 28454, 2792, 396, 14549, 5773, 13077, 15134, 19989, 2097, + 25670, 18043, 15219, 19702, 15773, 22729, 27086, 20815, 17549, 16705, + 3278, 22103, 24988, 737, 9673, 31006, 6759, 13920, 11747, 17472, + 11495, 18535, 11130, 32701, 28660, 26622, 14017, 13060, 7431, 14661, + 12714, 3409, 26436, 8292, 14090, 10297, 952, 14532, 1272, 22660, + 1759, 19584, 26229, 314, 15209, 24598, 24659, 7424, 22077, 9827, + 9551, 29093, 11199, 28878, 5505, 8697, 19022, 28611, 19702, 4574, + 7697, 22371, 1435, 20641, 500, 15481, 20572, 32138, 25069, 15792, + 27022, 23217, 21124, 19716, 11402, 532, 16786, 11639, 27291, 28903, + 9289, 30981, 10523, 23851, 18036, 17113, 2776, 2434, 7936, 10689, + 26813, 2882, 19900, 28384, 23982, 14024, 9821, 25763, 18867, 9743, + 27845, 18934, 4185, 3457, 9861, 26039, 5717, 21260, 9632, 30314, + 17003, 7033, 12434, 18991, 1422, 16889, 27536, 4850, 11849, 28161, + 26386, 19170, 2909, 15192, 30607, 15652, 8620, 10351, 14983, 1736, + 31061, 16112, 27847, 9212, 17953, 10905, 30185, 23935, 28446, 12875, + 19738, 10841, 14729, 18270, 6944, 22986, 6897, 7992, 15771, 6528, + 30362, 2846, 3978, 11276, 18976, 585, 6930, 25495, 3420, 4669, + 6267, 13521, 18827, 10582, 24188, 30251, 11181, 28580, 10168, 8006, + 5676, 8697, 20872, 7339, 30142, 9023, 30715, 3004, 23948, 14351, + 4891, 29419, 11924, 8320, 31090, 18985, 20044, 1085, 27597, 26529, + 12424, 25819, 3648, 9383, 14493, 2665, 29290, 20203, 13767, 7752, + 5882, 14835, 32456, 26685, 114, 1224, 28882, 25028, 20268, 26607, + 21927, 5031, 30362, 8090, 29951, 29136, 31185, 17952, 21802, 10587, + 20809, 12971, 28406, 5360, 24030, 12336, 22739, 19280, 3355, 8357, + 11573, 31789, 4792, 24686, 12346, 3903, 23393, 32280, 30861, 14740, + 7354, 6729, 8074, 32040, 9887, 1015, 15523, 18101, 22960, 25817, + 12290, 7628, 26381, 4635, 111, 6949, 15710, 21846, 20597, 6452, + 18199, 16831, 11968, 21244, 24058, 27039, 17866, 25896, 6109, 2051, + 11959, 25944, 20653, 29364, 27517, 953, 5555, 26729, 19527, 27352, + 22038, 3900, 17436, 20920, 31905, 14171, 28859, 29730, 22215, 13824, + 22986, 25916, 27876, 27811, 11056, 28765, 12230, 20590, 28352, 31776, + 8044, 1521, 2327, 9143, 20237, 25879, 32199, 31362, 27229, 31064, + 16053, 11744, 8203, 21041, 2751, 31046, 5518, 14289, 8098, 8334, + 18465, 6166, 17179, 25745, 32524, 16311, 26445, 9719, 13094, 13251, + 28259, 10830, 3992, 16202, 20854, 14170, 28643, 2393, 11953, 30892, + 32549, 18220, 691, 25462, 23704, 24224, 4404, 3230, 26971, 3303, + 16620, 9489, 9077, 25983, 28336, 4186, 12823, 15717, 31711, 18803, + 1825, 22150, 7164, 2523, 14824, 24438, 9628, 26344, 32566, 7012, + 27267, 22879, 25235, 17998, 23306, 25410, 9411, 2417, 30916, 18409, + 25488, 5124, 23648, 2644, 28362, 11301, 21201, 9664, 28299, 19476, + 21933, 13432, 16063, 8095, 25299, 30958, 9188, 30173, 30545, 32010, + 15631, 31519, 12677, 15298, 22320, 20260, 24194, 2283, 1430, 19340, + 30388, 11087, 9142, 3727, 19912, 23807, 9256, 7663, 31174, 9707, + 20722, 13867, 13696, 17583, 24841, 6646, 16944, 6576, 1813, 30183, + 3165, 18536, 23925, 11378, 6142, 766, 21379, 7703, 30690, 13338, + 25, 16857, 21583, 31479, 32084, 8690, 17642, 11706, 12695, 4293, + 470, 22018, 10707, 10772, 12320, 23488, 26886, 29811, 19803, 21065, + 20738, 14077, 32391, 7081, 2160, 22341, 17976, 22074, 532, 21392, + 26100, 18550, 2387, 11585, 28380, 16138, 30706, 22420, 17806, 4395, + 31008, 32399, 7499, 16173, 32183, 14902, 11861, 24402, 17855, 511, + 11163, 19680, 9012, 29829, 15256, 19830, 23048, 18304, 30841, 8351, + 16748, 29685, 15230, 11688, 2636, 29354, 14242, 17327, 20248, 7389, + 10110, 8189, 20682, 1333, 4715, 11926, 1317, 23801, 27879, 14605, + 23098, 14720, 6202, 30621, 21297, 19945, 1369, 14454, 25158, 27916, + 32522, 4437, 9234, 26543, 24393, 20311, 24588, 6910, 13290, 6415, + 29263, 17205, 5761, 30422, 670, 26272, 15231, 13666, 11429, 27845, + 31901, 22171, 32129, 13792, 25214, 1193, 27267, 18063, 29071, 15186, + 9521, 23503, 15923, 7043, 28814, 31527, 3101, 7468, 23452, 21283, + 31996, 4945, 1310, 4, 4702, 13919, 19618, 2230, 1742, 10428, + 5311, 10242, 16960, 10657, 31388, 13829, 28080, 27180, 9905, 17889, + 27141, 14513, 6866, 19378, 11835, 30290, 8553, 24260, 20673, 2767, + 16899, 27651, 16416, 19103, 24334, 27826, 28382, 831, 24800, 30048, + 30370, 8650, 4604, 12568, 30360, 10186, 4241, 7385, 12567, 25165, + 31044, 5957, 11394, 5948, 12079, 6301, 23404, 7401, 25898, 9965, + 4095, 4071, 19529, 23172, 16030, 6942, 2982, 1703, 21449, 1566, + 25952, 23175, 8513, 32180, 11491, 3696, 9837, 19131, 29290, 28994, + 14333, 27445, 1987, 27173, 19149, 16795, 16978, 17017, 26124, 20081, + 7769, 19468, 157, 10268, 10103, 14224, 19901, 9464, 19054, 12108, + 15591, 27558, 29737, 2577, 12532, 23971, 8701, 10864, 12935, 4127, + 18223, 24106, 8616, 18188, 11190, 20968, 10927, 9845, 10307, 27948, + 612, 3601, 25471, 10888, 11620, 21272, 29843, 16398, 22215, 19541, + 3822, 11271, 31231, 25725, 6978, 31208, 25603, 9919, 25040, 21377, + 20398, 31132, 10726, 12920, 8545, 31737, 29350, 17910, 1520, 14450, + 10504, 24736, 30908, 8946, 32547, 713, 29980, 5248, 18600, 21733, + 22274, 14684, 175, 9793, 11995, 9547, 23734, 17394, 3254, 559, + 4326, 15372, 1705, 3290, 10649, 24852, 32368, 21540, 7706, 8631, + 27224, 28219, 171, 2235, 9564, 6203, 28621, 31031, 2701, 29695, + 11451, 31487, 24503, 22582, 31544, 11107, 21313, 26297, 11914, 9753, + 4437, 23836, 1991, 31351, 13377, 21378, 18767, 229, 5149, 23990, + 9114, 9363, 14766, 17016, 24372, 30055, 30859, 15637, 26629, 5690, + 29822, 32059, 7638, 23363, 5677, 22794, 3601, 11475, 13050, 25966, + 31432, 579, 27687, 26797, 21241, 2347, 2366, 23356, 17, 19669, + 20912, 27351, 11438, 29170, 10684, 13264, 26793, 23179, 30354, 9384, + 20052, 17978, 3513, 16348, 3328, 17049, 13230, 12289, 31939, 32763, + 28110, 25107, 8056, 24154, 11125, 6667, 13062, 18065, 2664, 13743, + 14566, 9163, 14960, 4526, 5855, 4412, 17480, 17644, 13588, 4111, + 14265, 9755, 15681, 22943, 1284, 20363, 24386, 8681, 1408, 5443, + 16085, 24130, 30596, 8528, 28320, 25669, 29063, 10845, 7286, 29968, + 4230, 30852, 21206, 2224, 26023, 11364, 29620, 28121, 26395, 31860, + 20604, 4570, 26696, 23702, 853, 1233, 23848, 17392, 28067, 3611, + 27884, 1271, 32719, 25871, 6118, 27596, 11850, 25068, 29861, 5836, + 14287, 15119, 18530, 15715, 8425, 8810, 32411, 22816, 2230, 22321, + 17405, 25871, 15082, 32726, 18567, 10051, 8982, 24632, 27885, 14514, + 1323, 16408, 24378, 16198, 7994, 5580, 27322, 3632, 6269, 18743, + 32535, 8320, 24987, 8952, 12994, 8690, 27176, 15330, 17556, 31243, + 20331, 172, 24758, 3429, 26725, 7658, 11454, 10568, 21262, 26684, + 24346, 19833, 26338, 5318, 26034, 18519, 12444, 21984, 24521, 22819, + 23362, 12302, 28452, 19616, 10849, 6962, 9327, 18396, 27034, 17408, + 2780, 13645, 30045, 435, 5901, 2079, 16629, 21502, 32052, 18893, + 32197, 32070, 12921, 9711, 18385, 3342, 7580, 26707, 31616, 28339, + 15067, 8526, 6304, 20569, 14102, 25684, 22186, 30245, 17969, 21703, + 20039, 19885, 14522, 29965, 23572, 21932, 5323, 27621, 8354, 11771, + 13874, 13307, 16834, 25532, 14800, 27066, 20735, 19928, 14399, 11105, + 31595, 3501, 5048, 16217, 26271, 2048, 23859, 29894, 24317, 27193, + 5327, 29276, 15160, 4047, 18805, 26483, 6423, 23553, 23480, 21342, + 22389, 23495, 26624, 22265, 29069, 17416, 7144, 17538, 8052, 8205, + 31460, 25002, 24551, 17462, 22055, 4075, 20295, 22763, 31684, 20251, + 28642, 4566, 24851, 20034, 18292, 25935, 19542, 32425, 30190, 22552, + 16796, 23859, 10613, 12619, 14054, 26096, 5038, 10896, 21385, 26096, + 17465, 28819, 16083, 8021, 3195, 1974, 16881, 29612, 20641, 26883, + 16656, 2174, 27105, 27492, 20419, 6689, 32259, 19734, 25765, 28884, + 28705, 29019, 28735, 11276, 20139, 9839, 23340, 22536, 25261, 1982, + 12154, 427, 17493, 20564, 29012, 15499, 6320, 12693, 27358, 20294, + 23732, 558, 28513, 31253, 24963, 22859, 22952, 2979, 24046, 27147, + 18858, 14367, 14648, 13104, 25961, 26255, 29469, 22300, 8473, 26728, + 19731, 12281, 21125, 20316, 6352, 29751, 5039, 21876, 20492, 32571, + 14518, 2910, 22920, 4693, 23805, 16648, 8015, 8691, 19338, 23662, + 23100, 789, 18092, 4784, 14012, 3329, 29409, 14183, 14037, 824, + 26870, 5689, 30056, 23464, 13168, 2731, 11955, 13603, 15147, 18999, + 12825, 4624, 26433, 5279, 8486, 11105, 29741, 16000, 8142, 28613, + 17969, 18986, 23029, 798, 15093, 687, 565, 21045, 15984, 19770, + 11739, 3756, 9995, 9378, 12850, 10037, 7428, 29503, 26373, 4098, + 4369, 16125, 29279, 28541, 1607, 23470, 405, 824, 13113, 6053, + 4998, 20775, 15972, 5760, 14370, 30989, 6603, 20037, 26921, 14900, + 16862, 23217, 24629, 353, 25504, 6860, 3579, 4871, 11785, 19649, + 5838, 9659, 21161, 7149, 30821, 232, 31119, 21814, 15581, 29636, + 11369, 13726, 24321, 14466, 31981, 1815, 17354, 6090, 27255, 32666, + 27938, 24678, 19187, 5747, 18925, 1854, 15101, 17375, 18250, 902, + 22855, 24280, 1814, 15158, 20795, 3376, 24499, 5050, 30946, 31197, + 2311, 31467, 1541, 17812, 6855, 31807, 7826, 26009, 16373, 13884, + 18496, 25813, 17319, 17459, 31606, 15145, 23307, 28714, 23914, 12034, + 20648, 16666, 25990, 29997, 29926, 14683, 29199, 749, 30673, 22876, + 4104, 17995, 4122, 14999, 31316, 14510, 4958, 22212, 5148, 32683, + 25515, 20301, 25200, 4126, 22081, 18691, 18737, 1914, 30613, 12303, + 3886, 12125, 20636, 30763, 28045, 24424, 4421, 11538, 11885, 12621, + 17480, 26420, 7881, 15145, 28702, 30895, 22431, 17429, 18768, 26235, + 8786, 9975, 23029, 484, 31822, 22431, 17014, 13886, 32234, 15887, + 30613, 24276, 10386, 23577, 8622, 14625, 24613, 28976, 316, 18243, + 5020, 4216, 31902, 13996, 22765, 23839, 15132, 7963, 46, 24816, + 28965, 11722, 25086, 12581, 16858, 4329, 27937, 21885, 22993, 14156, + 14186, 9615, 10002, 14433, 5598, 4190, 21094, 30820, 22, 557, + 24018, 23766, 17125, 23701, 3166, 9515, 17979, 13524, 30490, 15299, + 27209, 19560, 29539, 4891, 16358, 30685, 11875, 25413, 8242, 26595, + 8663, 23445, 26489, 2211, 17941, 7326, 22362, 27475, 9549, 3676, + 25640, 28168, 13327, 26291, 29072, 5580, 4877, 29720, 17020, 20209, + 7715, 143, 8248, 1957, 27796, 24789, 31955, 22726, 9879, 990, + 5321, 12535, 21522, 5762, 21470, 13811, 2213, 3955, 333, 20513, + 4330, 14194, 30498, 5225, 1275, 450, 26853, 71, 25653, 18653, + 32120, 15389, 7740, 17255, 16450, 22478, 11583, 17760, 11662, 13704, + 9598, 8605, 24109, 5857, 24073, 16249, 5731, 38, 5834, 14536, + 15361, 26095, 16325, 5132, 30670, 22775, 17823, 28396, 1579, 9259, + 1885, 11489, 31479, 4353, 22895, 32307, 31921, 17931, 14829, 21284, + 15068, 29644, 6688, 3946, 9128, 17354, 24900, 4579, 7374, 15844, + 3212, 25733, 17340, 6118, 4199, 10388, 30962, 9051, 24322, 4887, + 15020, 19875, 14350, 3468, 4801, 26181, 9667, 20187, 7115, 8070, + 19237, 137, 24968, 32058, 26502, 17454, 8320, 9690, 13132, 11561, + 10472, 18903, 31220, 658, 26823, 5422, 5463, 23920, 24919, 24381, + 11820, 30508, 2990, 3284, 26063, 15430, 28829, 22672, 19777, 30535, + 30681, 30052, 19823, 10231, 12888, 16249, 25783, 23247, 9214, 28472, + 23976, 5297, 20601, 677, 25353, 9943, 3946, 20718, 5405, 32522, + 27589, 31811, 1062, 18452, 4730, 13801, 13352, 28279, 5205, 2186, + 3567, 23903, 32546, 7376, 24915, 2006, 4630, 11314, 23739, 4663, + 4458, 5645, 7648, 12261, 32073, 19190, 16340, 2364, 12268, 591, + 2626, 15816, 9988, 28184, 9719, 22197, 31832, 24840, 31393, 15751, + 20911, 6876, 6615, 2828, 13296, 12016, 23386, 17038, 14728, 14604, + 3739, 19788, 24588, 21632, 25981, 18988, 27935, 2419, 31835, 1450, + 22167, 9336, 9037, 11796, 30171, 11362, 19623, 6137, 2703, 27226, + 17603, 9951, 20596, 14382, 5889, 11828, 11401, 2284, 17419, 24199, + 13687, 24496, 11614, 4856, 6955, 10426, 23046, 4845, 12741, 10435, + 30888, 7175, 9137, 30353, 17094, 18517, 3859, 13002, 9535, 9415, + 4713, 29190, 21013, 3045, 3180, 3367, 8051, 6336, 4918, 19437, + 22511, 17961, 26282, 7621, 24588, 3618, 25105, 21730, 21763, 7570, + 12545, 6031, 2243, 28733, 19331, 31684, 32566, 26866, 32116, 247, + 20678, 14854, 32044, 23604, 5664, 12802, 21854, 19056, 16695, 16537, + 9791, 23835, 1932, 18397, 491, 29940, 16814, 3506, 26628, 24353, + 25596, 24962, 9372, 18559, 29874, 18994, 5717, 31543, 8996, 20565, + 8134, 6802, 25668, 14496, 18465, 20750, 14442, 17054, 20243, 9393, + 32341, 16293, 14316, 26013, 30573, 5641, 4096, 27365, 24906, 1167, + 29773, 4511, 18554, 14885, 6750, 12858, 30847, 5584, 24687, 18, + 11544, 8933, 10449, 1858, 26818, 9596, 1168, 15545, 5245, 19234, + 29012, 21649, 19667, 21454, 11909, 21148, 23, 18199, 28082, 2367, + 16875, 2467, 25140, 31363, 30724, 26339, 29710, 20329, 17458, 25341, + 6680, 22910, 20378, 26890, 8029, 21168, 23366, 10626, 22465, 20320, + 14817, 15604, 2023, 5816, 5819, 24315, 1105, 21823, 11796, 21869, + 19401, 23978, 23190, 22485, 8448, 27638, 7203, 2852, 26486, 7859, + 31697, 26019, 18329, 6819, 28979, 3884, 24113, 3203, 11483, 4506, + 31483, 32041, 27676, 28030, 12948, 11215, 14869, 26388, 18329, 25263, + 16874, 8053, 12045, 31184, 5717, 19981, 13307, 31775, 4702, 11856, + 32348, 8897, 14182, 30121, 314, 6824, 11370, 26035, 5142, 18532, + 17030, 20140, 15240, 32316, 8888, 5671, 25893, 22654, 2279, 5632, + 6821, 13331, 12324, 26517, 28742, 29526, 21551, 28467, 12907, 1319, + 27709, 15311, 5769, 25146, 760, 3831, 29507, 29877, 20424, 8456, + 17364, 7675, 30033, 30112, 31406, 20162, 2365, 21590, 26464, 25964, + 18747, 14761, 13245, 18068, 8519, 21559, 15419, 25575, 26413, 25136, + 18329, 9525, 28488, 777, 25029, 26567, 24208, 20853, 24060, 2036, + 24020, 22929, 17459, 10479, 4675, 16222, 11702, 12003, 32561, 10613, + 21775, 8002, 13592, 17398, 23849, 23386, 29149, 28610, 5045, 26921, + 5717, 29162, 13005, 15986, 5384, 14183, 26071, 16912, 15925, 267, + 5549, 30603, 3350, 2999, 16379, 2281, 23140, 2255, 24040, 8902, + 6174, 31467, 7672, 22183, 31367, 19151, 30000, 30604, 12910, 10398, + 19324, 9412, 7345, 3402, 18255, 30469, 2820, 28751, 31881, 956, + 2543, 1441, 669, 10914, 18454, 6114, 13811, 24976, 701, 21209, + 10379, 29332, 7051, 4640, 1508, 24644, 15246, 2076, 28188, 25682, + 10112, 20786, 3160, 15457, 10569, 18232, 27750, 14615, 12996, 10538, + 21409, 17268, 7622, 27423, 8469, 19554, 11613, 17019, 30948, 23181, + 13397, 16997, 16105, 10868, 1281, 7292, 9370, 5313, 3640, 22981, + 20019, 11989, 18845, 8168, 21265, 22952, 24355, 22173, 7515, 13008, + 8657, 16737, 27880, 7135, 18883, 24941, 349, 19442, 3954, 13288, + 12182, 8071, 3125, 8284, 26184, 6799, 1031, 11111, 26084, 20677, + 31882, 16819, 12222, 10301, 17740, 12071, 28782, 10816, 21833, 5727, + 26998, 22103, 8626, 31834, 7718, 11174, 20973, 15475, 18365, 23675, + 29917, 30867, 21713, 24615, 7011, 22948, 30705, 22285, 31781, 13385, + 2440, 29944, 1166, 26060, 16644, 26034, 31510, 32088, 2360, 24530, + 5827, 1384, 29695, 22997, 12271, 383, 4387, 5488, 28237, 29722, + 11389, 29989, 6183, 226, 14173, 20501, 26345, 32551, 24876, 14357, + 1251, 4234, 9039, 26115, 6219, 4089, 17487, 16475, 32641, 27873, + 5687, 12293, 8855, 18078, 29758, 14193, 32273, 23607, 17318, 32742, + 19177, 9288, 31716, 20759, 26583, 8511, 31233, 9976, 26575, 17867, + 9024, 22991, 27082, 32146, 6913, 3214, 7625, 11811, 4069, 21856, + 6935, 17451, 25651, 21634, 27599, 8427, 13691, 11604, 29087, 31721, + 1913, 5552, 1995, 3302, 5691, 28538, 1653, 4625, 4998, 8312, + 2843, 1658, 21367, 7268, 10849, 7369, 11366, 21824, 28433, 21040, + 3599, 1514, 19040, 11678, 5108, 2800, 22899, 32566, 5885, 15781, + 9249, 13311, 610, 1397, 14587, 4573, 28618, 4981, 19081, 28608, + 20149, 31806, 12759, 15899, 6935, 9212, 31280, 29592, 26437, 12482, + 23082, 12810, 15637, 30497, 15843, 14242, 6721, 5368, 18057, 28775, + 3085, 11917, 25376, 15328, 11818, 477, 26239, 15123, 4313, 17457, + 16028, 15028, 19028, 32242, 12554, 3478, 14185, 22718, 16576, 27493, + 12136, 5051, 8476, 10135, 12095, 4652, 23080, 21664, 16356, 14402, + 539, 10085, 7155, 2612, 12323, 12020, 22681, 16786, 3169, 13426, + 5552, 19670, 8501, 22356, 31019, 27964, 20925, 14803, 31157, 26323, + 6552, 20889, 15509, 30420, 31216, 12481, 2773, 1075, 14263, 23294, + 8611, 15989, 20272, 31514, 25079, 7873, 16562, 3235, 9078, 32569, + 6825, 19937, 11927, 15499, 25821, 3594, 8708, 5570, 20727, 11193, + 18243, 15971, 21569, 22429, 6805, 22197, 3757, 28526, 2590, 15968, + 8985, 1318, 12359, 30009, 25809, 26221, 3425, 32349, 32341, 14421, + 1163, 31101, 30045, 31559, 8149, 26232, 12315, 2878, 24485, 18223, + 24512, 28722, 6490, 5013, 29761, 4824, 10544, 13317, 32091, 10632, + 31587, 30169, 7381, 29054, 12440, 11832, 28165, 2118, 24952, 32559, + 1685, 31090, 17822, 29443, 29831, 21646, 16819, 25030, 23927, 24406, + 8407, 21801, 8992, 14374, 32067, 24209, 12578, 31921, 7213, 4973, + 22531, 18678, 16620, 9158, 22899, 8816, 15197, 16151, 2516, 9101, + 13701, 15200, 11001, 31586, 1144, 12918, 25646, 5533, 121, 2461, + 31609, 32443, 10338, 5976, 13344, 22651, 5191, 23424, 30315, 29330, + 31574, 32148, 9387, 23456, 20737, 1360, 16763, 32355, 15712, 385, + 19270, 7978, 29904, 4375, 28938, 19853, 13025, 636, 1115, 7772, + 23663, 32036, 19626, 16853, 3342, 26982, 8135, 18970, 5036, 2698, + 25509, 13794, 8817, 32547, 23902, 4974, 13802, 7071, 29046, 20415, + 30567, 21276, 14692, 1542, 7841, 8829, 4686, 25436, 5792, 12733, + 2325, 29363, 27100, 8068, 18321, 25868, 14755, 11000, 26398, 10731, + 17594, 12246, 19543, 27656, 16711, 10433, 28705, 17992, 3566, 11226, + 14828, 17643, 32315, 5520, 20277, 1799, 17984, 21946, 31823, 5173, + 18815, 4835, 6756, 14986, 20746, 6116, 5815, 2249, 27880, 16313, + 32555, 14550, 1211, 21389, 14345, 7790, 23456, 3863, 16126, 28182, + 2903, 16169, 8035, 28753, 15012, 11910, 31681, 29974, 4852, 21749, + 20629, 7041, 14660, 31891, 21584, 16841, 20031, 20845, 6306, 12658, + 14758, 3918, 20728, 23492, 17653, 16854, 25156, 14971, 18756, 19148, + 8188, 10432, 29803, 8885, 23141, 18520, 26985, 1958, 8721, 18204, + 16391, 3670, 25766, 6313, 10168, 18807, 18146, 30808, 12740, 31559, + 24540, 18289, 1701, 1378, 14733, 7067, 2737, 2778, 14990, 4503, + 13818, 30857, 23843, 14382, 9075, 26534, 22393, 10088, 6996, 3735, + 6444, 6143, 7359, 13318, 16908, 3859, 10915, 22583, 30241, 29215, + 24545, 2201, 13037, 5932, 3757, 4507, 2544, 9541, 28471, 704, + 25714, 22826, 8587, 14582, 13436, 585, 1718, 25380, 14120, 19433, + 13611, 13570, 11290, 9897, 3173, 15405, 9807, 26153, 11213, 2939, + 23062, 26375, 20701, 12427, 3163, 20749, 24710, 10145, 1835, 18856, + 8143, 16817, 14197, 10908, 24512, 12474, 22542, 4343, 8318, 11862, + 19960, 22595, 28599, 28814, 6628, 22016, 1600, 24818, 757, 26962, + 26877, 18922, 24263, 13046, 4354, 28231, 6832, 2426, 24311, 16157, + 27206, 32174, 24001, 9173, 16161, 854, 6239, 2174, 28130, 4641, + 1809, 9828, 9331, 21215, 22464, 15076, 5521, 11502, 5530, 8318, + 32136, 18421, 29693, 32300, 25662, 21973, 16534, 242, 16499, 14557, + 16586, 21431, 24647, 23102, 32340, 18771, 15101, 17598, 18224, 27881, + 4094, 22448, 25784, 27767, 30497, 9763, 19982, 4523, 17961, 22106, + 22969, 15609, 28560, 5500, 12947, 5866, 16003, 29581, 5090, 27575, + 5165, 15261, 4293, 25878, 2616, 7231, 27346, 5716, 7825, 13373, + 15357, 3748, 19564, 22410, 30651, 26039, 13961, 8312, 1896, 30348, + 18101, 8491, 1510, 15498, 26259, 18766, 24200, 1740, 13724, 21508, + 21294, 30140, 24677, 21026, 11697, 30435, 32032, 9371, 8321, 13055, + 4454, 31548, 6796, 8790, 9524, 11161, 4706, 15179, 1843, 29767, + 3223, 28463, 9788, 75, 24937, 26969, 9243, 16083, 1443, 17529, + 1427, 25380, 23234, 10793, 9123, 8404, 19029, 4293, 16904, 30777, + 12458, 2941, 28419, 10017, 23206, 24744, 4188, 4547, 6901, 23131, + 16807, 14326, 14948, 1339, 26566, 2721, 18314, 20879, 7599, 13683, + 2478, 19098, 24176, 12500, 6885, 22567, 4208, 12315, 28027, 15973, + 22007, 16385, 16532, 20469, 18160, 10581, 18451, 1602, 30702, 27634, + 3003, 29713, 29294, 26095, 16788, 18009, 604, 31509, 10129, 18260, + 31960, 20630, 8303, 6102, 25657, 28923, 6112, 4830, 3410, 27743, + 29161, 3946, 10741, 18651, 2441, 29840, 23734, 22337, 667, 21451, + 14833, 16397, 29437, 32147, 23055, 23704, 9970, 17456, 24836, 12431, + 1221, 24783, 7964, 6052, 21424, 11990, 12024, 31619, 27131, 12661, + 13523, 9736, 1640, 6013, 8971, 943, 22735, 12662, 10204, 27212, + 32262, 3564, 10350, 14332, 29443, 12297, 27922, 24155, 17221, 5225, + 25288, 11767, 30320, 30761, 5638, 30213, 311, 5697, 19124, 19539, + 15742, 20244, 21883, 4478, 11712, 28230, 6719, 1381, 4015, 718, + 21482, 32733, 539, 32076, 2412, 8615, 16917, 2204, 21504, 4511, + 17236, 20651, 23216, 6709, 10200, 11145, 10011, 15137, 23244, 22720, + 14198, 851, 21169, 19173, 29137, 18217, 16057, 12140, 23707, 1765, + 9073, 10432, 16489, 6494, 10915, 28308, 5875, 28406, 10875, 18547, + 27487, 203, 10216, 5791, 4035, 16434, 22597, 23832, 19429, 6753, + 26169, 1275, 26949, 25707, 32220, 18445, 16477, 17266, 25627, 31384, + 11047, 25879, 7517, 10102, 224, 7222, 26471, 12907, 32524, 13777, + 2684, 29880, 17338, 6729, 17865, 2028, 15625, 3796, 23297, 13054, + 12779, 11051, 32069, 11673, 18473, 22403, 10054, 11703, 11175, 26303, + 7341, 29553, 15082, 8420, 6843, 18197, 23767, 20941, 31788, 7546, + 25533, 12320, 21602, 22160, 8598, 12416, 17855, 19003, 4490, 6404, + 5971, 5986, 15029, 26856, 19895, 26947, 12330, 26755, 19256, 9993, + 21048, 30073, 15160, 16800, 13548, 24370, 19666, 5064, 20438, 26507, + 189, 2152, 23312, 15307, 3699, 31525, 32125, 31192, 9362, 1844, + 12367, 5547, 2178, 14444, 31692, 13057, 22872, 21152, 29009, 18259, + 31226, 9003, 1687, 6153, 26244, 4300, 13877, 26859, 30955, 23989, + 2406, 28376, 24801, 20446, 28429, 23226, 21240, 13215, 1014, 4281, + 13065, 27774, 12383, 24495, 15831, 2118, 16055, 19676, 30192, 2137, + 20389, 18396, 22484, 21830, 20300, 23253, 6489, 4254, 20430, 10997, + 30798, 14596, 27918, 23836, 23647, 21636, 7611, 13013, 20536, 5414, + 5514, 29579, 5614, 11202, 7362, 30202, 23194, 15637, 254, 24035, + 19783, 11541, 814, 25092, 7438, 13442, 26336, 11847, 8831, 29886, + 1726, 13882, 14403, 32222, 4695, 3007, 1792, 31489, 22818, 3589, + 18557, 30944, 18111, 1206, 17927, 31399, 29825, 31602, 5651, 20740, + 29738, 29053, 23844, 26493, 14325, 12724, 5615, 31750, 15322, 26310, + 9405, 29907, 21517, 16288, 14866, 18539, 17608, 14377, 18572, 11262, + 1866, 23088, 4621, 31736, 24608, 2722, 4125, 4822, 10915, 14081, + 15241, 568, 32194, 30481, 30544, 25144, 3585, 30421, 8896, 11003, + 28866, 15545, 5853, 19589, 9411, 8209, 4866, 30930, 1001, 962, + 23755, 21538, 572, 10559, 5937, 32660, 24114, 12216, 5367, 12790, + 9776, 5006, 10822, 21212, 14158, 16905, 16788, 19439, 6491, 5453, + 14282, 1160, 23585, 19594, 17317, 15230, 23017, 21620, 27740, 13116, + 834, 29683, 21380, 25622, 6143, 12571, 22065, 10461, 29055, 606, + 18783, 6576, 16945, 8192, 21904, 2326, 28246, 2508, 13750, 22346, + 25097, 5382, 23890, 4054, 2173, 5792, 18910, 1379, 18567, 32176, + 873, 24528, 10844, 29218, 4225, 20609, 2715, 19370, 24917, 24826, + 32398, 29949, 30235, 4012, 19885, 3905, 17584, 12046, 9315, 27074, + 8574, 15088, 25621, 8678, 12195, 6157, 13409, 32611, 7248, 29151, + 14755, 24751, 10606, 10935, 5310, 19385, 19336, 4620, 4121, 30422, + 24293, 21779, 7655, 24062, 14324, 23953, 2988, 14392, 4846, 4846, + 3836, 24915, 9910, 12682, 3974, 3031, 24324, 11348, 21074, 20283, + 450, 19608, 27207, 26200, 17334, 1048, 4592, 12830, 4194, 9567, + 21120, 21638, 2344, 30290, 7187, 30663, 32690, 20744, 13110, 5581, + 28561, 18139, 6058, 21479, 15140, 31167, 18533, 18112, 12220, 8147, + 2266, 12944, 5373, 25910, 2938, 7059, 19336, 8220, 9595, 15035, + 16087, 2849, 28632, 10914, 11879, 31197, 31129, 24770, 4637, 15288, + 28500, 18152, 7651, 8447, 23540, 2278, 10386, 22072, 26297, 27272, + 31252, 2483, 12928, 14794, 17610, 3938, 16965, 12616, 20011, 30442, + 1766, 24310, 2734, 18426, 31273, 25361, 12522, 22748, 27393, 6998, + 2044, 29514, 24333, 3697, 29994, 15234, 11618, 11192, 3270, 3485, + 13962, 13081, 10836, 17692, 24012, 22865, 21487, 8900, 10158, 31839, + 27880, 17580, 8633, 31801, 31665, 25748, 30588, 3005, 1785, 20600, + 22740, 3562, 19993, 20837, 5482, 21328, 24751, 29836, 4476, 19397, + 22920, 27285, 30689, 90, 16580, 28981, 19783, 27022, 22836, 31491, + 18624, 30145, 23815, 2439, 5501, 21483, 28070, 28052, 25048, 22157, + 15068, 25048, 24472, 744, 9530, 13234, 6513, 8302, 24506, 28227, + 16843, 23670, 21666, 15304, 24261, 22067, 20907, 28967, 30584, 9959, + 16227, 30663, 9856, 14932, 26576, 2469, 13753, 2931, 4182, 29605, + 10054, 6505, 27354, 21030, 20440, 10423, 8744, 31704, 26156, 9393, + 1426, 3794, 11222, 13617, 6106, 21471, 4926, 23692, 1363, 27673, + 1768, 12741, 17730, 31930, 13816, 2817, 27274, 19758, 2549, 11323, + 26505, 2925, 10701, 20025, 18749, 10887, 11801, 4543, 11691, 3968, + 27299, 13058, 31105, 95, 28965, 4447, 4346, 2434, 12628, 24397, + 6146, 19764, 23069, 3729, 32155, 29058, 24623, 19633, 22143, 8266, + 19897, 9130, 19206, 19860, 16245, 4837, 7243, 14373, 13029, 13416, + 1162, 22610, 2525, 16970, 30413, 12563, 26433, 4201, 456, 14168, + 18704, 20063, 23041, 17312, 929, 9649, 20276, 16839, 557, 2122, + 12533, 4895, 28814, 17192, 26263, 22211, 12870, 10582, 15949, 3112, + 19139, 27418, 14223, 17397, 6882, 28015, 19647, 21269, 6065, 13749, + 13, 15370, 24185, 15784, 12184, 17028, 18940, 17295, 29909, 23858, + 27899, 27317, 4917, 31192, 5627, 16413, 14850, 18338, 16457, 30578, + 7562, 31672, 5692, 29757, 11136, 17426, 26810, 29531, 24223, 23694, + 16381, 26797, 30096, 31295, 27031, 25873, 4366, 264, 10164, 6289, + 32072, 14186, 26016, 17627, 10887, 27803, 5513, 13297, 6416, 17624, + 7045, 1826, 15436, 8187, 27095, 29241, 6438, 20048, 12910, 14068, + 9141, 1957, 20910, 29880, 8219, 22688, 22131, 8322, 2733, 31318, + 19283, 14625, 19284, 18456, 9508, 2979, 14386, 18320, 31422, 7181, + 11145, 30478, 27138, 1615, 12505, 22044, 19996, 27510, 3401, 23640, + 11012, 16164, 10056, 21123, 29259, 2365, 4468, 16853, 1300, 28644, + 15547, 28051, 5985, 7590, 5568, 2788, 22672, 22516, 4480, 10977, + 1317, 22999, 27957, 2795, 17865, 18143, 9595, 16193, 32659, 19037, + 26801, 27601, 31111, 14033, 2210, 32591, 31580, 14758, 12132, 9349, + 18819, 30493, 23425, 24042, 8804, 8840, 26453, 32629, 8427, 26296, + 8119, 31411, 10307, 12984, 11740, 12568, 15585, 16060, 22647, 16531, + 28357, 23892, 24613, 32356, 14588, 27877, 31497, 5170, 31338, 32090, + 13235, 29528, 27590, 32279, 12103, 4737, 14054, 2094, 7899, 10612, + 4925, 6358, 24608, 24369, 2978, 20505, 4421, 7740, 20818, 1996, + 7192, 825, 16441, 22212, 31855, 6585, 30829, 21575, 17862, 23380, + 12540, 12225, 11305, 9464, 10215, 28856, 5205, 5760, 21358, 29442, + 28442, 4232, 14478, 29494, 25603, 17536, 2761, 16224, 3897, 2816, + 31013, 24569, 22181, 29787, 19575, 19734, 6741, 18295, 15073, 991, + 17441, 25308, 5274, 4163, 31374, 13322, 11757, 24298, 31474, 14608, + 10885, 27499, 24281, 16474, 3559, 7872, 20648, 23243, 13632, 29175, + 29568, 13324, 7777, 10537, 613, 17699, 8200, 15024, 27296, 5447, + 30561, 13973, 25953, 31986, 20172, 16968, 21673, 7993, 8673, 31892, + 26471, 3417, 8881, 24017, 18658, 18578, 1103, 26716, 24406, 5688, + 20112, 15684, 24034, 23047, 7950, 26950, 11681, 27319, 31603, 32252, + 29208, 5328, 6388, 19281, 11472, 16320, 15643, 19768, 17475, 26922, + 7114, 8606, 29181, 27266, 22141, 6812, 23547, 15575, 207, 16626, + 23895, 12015, 20032, 23217, 16935, 16793, 19765, 18999, 31394, 7743, + 21312, 22880, 2436, 23483, 32418, 17659, 12724, 1387, 28942, 7291, + 10020, 6280, 10160, 18927, 18169, 27571, 5230, 22069, 6893, 13521, + 23662, 15722, 11249, 8473, 24536, 26532, 26646, 13163, 28381, 6628, + 8109, 23780, 20472, 29510, 23981, 943, 31603, 19067, 8022, 27678, + 12334, 32633, 12052, 14329, 17820, 5489, 9894, 28694, 24334, 10498, + 23679, 19497, 25652, 5804, 16493, 11486, 25312, 25991, 27512, 31781, + 15886, 18997, 26381, 26573, 20002, 22037, 28508, 10837, 22128, 9913, + 13566, 22015, 29697, 10651, 25051, 22053, 27028, 18443, 25931, 6864, + 27876, 3948, 19323, 8839, 22825, 10656, 9235, 22117, 27361, 14785, + 15381, 27759, 26832, 28946, 12040, 17444, 24134, 28725, 4951, 29013, + 19104, 18590, 17908, 26934, 27153, 12871, 6464, 1462, 31651, 18853, + 27439, 21260, 10360, 27100, 26279, 23442, 28758, 30511, 29738, 26071, + 16227, 11706, 7605, 22767, 4396, 18531, 27112, 15118, 17644, 5134, + 29286, 32342, 1804, 17168, 14719, 4076, 29670, 5350, 781, 23889, + 22478, 9768, 13467, 17392, 12371, 31928, 205, 7923, 21186, 9279, + 25084, 26459, 23532, 28378, 10272, 6736, 19753, 11792, 7667, 22965, + 31389, 30728, 2387, 31488, 11962, 19704, 2792, 30793, 9990, 12513, + 8456, 30624, 19999, 3450, 20951, 23716, 22268, 28777, 16563, 6226, + 7541, 31161, 13007, 11034, 28678, 3721, 18205, 3384, 9936, 9217, + 25970, 28993, 11232, 13963, 10469, 5372, 22708, 16703, 18808, 5236, + 19707, 18846, 18682, 29515, 8861, 21380, 26411, 20561, 8955, 20824, + 8482, 30549, 24952, 25734, 16364, 17724, 24376, 559, 14966, 4688, + 7208, 20511, 28332, 5850, 14654, 19967, 30179, 32242, 4098, 7883, + 6260, 6806, 25421, 1671, 9386, 29434, 3408, 28718, 11487, 5663, + 20185, 4076, 28897, 28583, 9170, 30092, 30363, 3560, 16650, 9012, + 5617, 3647, 7993, 31824, 31723, 9126, 14142, 8396, 26902, 27529, + 21418, 2182, 12071, 1238, 7042, 29443, 12770, 7508, 14913, 32609, + 28474, 26933, 18717, 140, 21178, 14574, 23947, 25013, 22563, 8518, + 24837, 12032, 16727, 27053, 5643, 5757, 25461, 13520, 28264, 30042, + 20294, 5994, 5814, 17751, 22744, 4445, 23332, 32377, 17000, 7425, + 18807, 16643, 3717, 18620, 13391, 23458, 15731, 3795, 31660, 488, + 31182, 24447, 16048, 18171, 30831, 4420, 23207, 12431, 3988, 11134, + 23364, 30225, 18746, 16189, 16429, 16421, 29902, 16978, 15291, 31827, + 31749, 8362, 20218, 6397, 32521, 30683, 26360, 15765, 31701, 12541, + 24380, 21261, 1250, 27835, 3476, 28814, 19203, 30121, 15124, 108, + 32397, 11481, 11867, 12634, 19388, 9385, 11032, 2010, 30290, 24030, + 2765, 9208, 22657, 23287, 21203, 3896, 18699, 32187, 27551, 9933, + 11432, 25365, 23875, 30219, 13992, 12577, 13623, 5407, 7385, 2951, + 31066, 24574, 17067, 8712, 19508, 6555, 8082, 3414, 9854, 20317, + 25772, 28201, 18916, 4304, 9636, 655, 20745, 20379, 2262, 9603, + 204, 31666, 17076, 27842, 16074, 12236, 24680, 22746, 20771, 536, + 9659, 19332, 19330, 19755, 565, 14387, 19799, 1341, 24965, 17133, + 30055, 28054, 23332, 16730, 11911, 16963, 1334, 21019, 14756, 10765, + 28337, 32348, 6407, 1196, 1216, 6573, 16599, 26935, 26474, 21517, + 12512, 26847, 29039, 12872, 23442, 27101, 30578, 24708, 32333, 11945, + 605, 4886, 20188, 23917, 2306, 27034, 8767, 7097, 27902, 2114, + 28845, 4728, 16099, 23406, 23257, 28195, 3478, 14717, 5237, 12084, + 17653, 22243, 22516, 24286, 24509, 16508, 18325, 31739, 28843, 20513, + 24477, 13382, 18146, 20344, 24035, 15818, 12685, 15632, 17042, 25754, + 9925, 26541, 20011, 8387, 20917, 21472, 8393, 18559, 19452, 8155, + 24233, 12971, 1618, 22916, 32233, 23931, 9327, 31432, 8592, 28013, + 20521, 50, 9679, 6267, 18396, 19900, 16775, 20885, 11278, 21989, + 24842, 18510, 20563, 14083, 7718, 4310, 4975, 17115, 31072, 2088, + 699, 9327, 14461, 18564, 19658, 6167, 3655, 16928, 18251, 27091, + 28557, 19483, 3121, 11744, 14700, 16818, 29202, 26505, 3277, 32694, + 25549, 4672, 815, 22077, 23408, 16621, 8359, 17049, 1113, 18310, + 10020, 1414, 18587, 8928, 12024, 27047, 19875, 4805, 10388, 20020, + 9243, 6726, 11155, 18925, 12046, 23122, 14075, 31314, 1613, 31368, + 18111, 21269, 22546, 5194, 25224, 464, 3556, 21523, 2484, 14836, + 14522, 17674, 8351, 32001, 2147, 31447, 17186, 13053, 21029, 3509, + 8282, 10015, 27301, 7368, 17323, 15563, 22278, 19122, 28420, 28867, + 6485, 26721, 27236, 31387, 7205, 16938, 27805, 8813, 374, 17180, + 344, 7887, 22748, 25230, 4185, 3842, 221, 4091, 27140, 25964, + 14234, 4368, 32032, 22088, 7858, 1538, 6620, 7837, 13923, 2085, + 26127, 14188, 5106, 5819, 26308, 18734, 22349, 17929, 17142, 29566, + 9200, 25777, 9112, 15634, 8453, 2875, 21721, 16367, 17615, 18933, + 3237, 26077, 28488, 26877, 31343, 18713, 6153, 28316, 1070, 3403, + 4971, 24533, 21669, 17591, 13036, 28478, 7912, 13751, 17620, 3599, + 15132, 5225, 14104, 4985, 126, 1189, 24958, 28217, 167, 30770, + 15106, 5228, 2062, 15673, 32376, 9637, 1998, 20729, 628, 29501, + 19625, 19133, 18535, 25322, 10061, 29451, 1802, 24467, 27095, 23327, + 13312, 19555, 24214, 2200, 20663, 11805, 7185, 2416, 32399, 32431, + 23115, 1073, 7742, 24329, 11589, 6246, 32371, 6959, 18629, 9040, + 23993, 13453, 12630, 15579, 17310, 26252, 32400, 208, 29973, 22882, + 4761, 30796, 2074, 30510, 27372, 14193, 23188, 5956, 28249, 859, + 28799, 29637, 23629, 5206, 32074, 7540, 16724, 18510, 7294, 25028, + 5340, 22919, 32161, 22326, 25660, 28046, 1882, 993, 12021, 4207, + 13782, 21074, 22583, 28712, 14441, 18583, 20903, 27742, 4729, 13719, + 25439, 20047, 17304, 18597, 13622, 15624, 15088, 18260, 32066, 16855, + 6628, 21900, 30620, 28932, 2548, 989, 2915, 2748, 10721, 6164, + 16644, 10318, 22034, 17420, 28088, 18842, 20535, 16071, 14414, 22213, + 2604, 29629, 23338, 27186, 6176, 15476, 18318, 30382, 3574, 22609, + 3608, 7659, 19435, 5806, 12428, 19248, 32378, 26413, 16596, 26190, + 21885, 17483, 28997, 13014, 13972, 11320, 31268, 12169, 28362, 7142, + 30825, 2576, 10102, 16479, 10690, 31470, 8800, 21085, 10715, 7602, + 293, 20500, 5341, 6513, 7133, 2387, 13109, 11635, 11002, 12747, + 4758, 30253, 18547, 28078, 12353, 32367, 3107, 3197, 6276, 28895, + 18284, 26139, 8064, 32166, 9247, 3621, 5936, 18537, 10912, 20870, + 9895, 29307, 29509, 12869, 9881, 5457, 1792, 21922, 10013, 16694, + 13118, 6242, 689, 21567, 21774, 15719, 8831, 15746, 24454, 27267, + 22495, 131, 8546, 26510, 28444, 3311, 3567, 21430, 24443, 16013, + 3368, 17687, 15481, 6513, 5918, 1676, 19541, 5747, 7837, 26564, + 26574, 5087, 3852, 21400, 1838, 15982, 24981, 8347, 8327, 1653, + 3462, 20467, 15165, 30045, 22519, 11880, 23748, 30547, 18094, 3003, + 12971, 27968, 14911, 31801, 7642, 6908, 29559, 22671, 792, 17855, + 2043, 28640, 1232, 5848, 2605, 8213, 2580, 5793, 26703, 18549, + 14050, 21426, 18155, 19061, 14758, 2645, 23154, 3281, 4609, 24634, + 1059, 13531, 13513, 2156, 13870, 2579, 12625, 25476, 25370, 14329, + 15338, 28701, 31844, 25119, 16809, 8071, 17513, 16002, 26987, 25280, + 30597, 10778, 9751, 927, 4221, 27568, 15907, 7763, 11290, 6384, + 27738, 20236, 20503, 20927, 32220, 26616, 6462, 17871, 4454, 17265, + 5325, 14448, 23507, 1653, 18718, 30377, 5603, 26454, 12679, 29575, + 30524, 19634, 12672, 32437, 25262, 22694, 17155, 18765, 10171, 13375, + 25657, 11502, 32087, 12081, 2130, 26895, 6636, 1996, 4020, 19090, + 3233, 28459, 2905, 29084, 15324, 7599, 20338, 10661, 11525, 2988, + 6481, 1995, 31118, 8801, 19589, 17260, 5424, 31022, 2887, 31987, + 18807, 29441, 11797, 26354, 2688, 20498, 24220, 1394, 2306, 2000, + 23003, 30599, 9611, 32538, 7558, 32551, 10508, 28339, 1192, 15163, + 26264, 3568, 6029, 18423, 10857, 21940, 4831, 14695, 6692, 28574, + 27151, 9291, 27229, 9601, 18678, 10533, 3422, 25877, 22717, 13712, + 16164, 11244, 19450, 28627, 4779, 28583, 30235, 182, 25708, 22121, + 29192, 12181, 22051, 21388, 7172, 1202, 14293, 31891, 25300, 26063, + 530, 10848, 13348, 24829, 17784, 15016, 2086, 6575, 13176, 1443, + 20604, 3090, 9309, 23560, 20056, 6817, 16009, 8504, 5090, 6644, + 8962, 21945, 13530, 20184, 17198, 9703, 27262, 4773, 17371, 20710, + 11950, 4556, 4482, 31707, 4686, 22704, 2509, 17756, 21228, 8887, + 24150, 11557, 29602, 8096, 5298, 9228, 23870, 8983, 18350, 22583, + 18849, 29318, 3544, 4492, 32357, 9519, 10562, 12905, 6851, 19064, + 25730, 12437, 31923, 19077, 16637, 6775, 340, 13580, 23142, 31878, + 9359, 29285, 9089, 15105, 17399, 2785, 24621, 13280, 2292, 30813, + 161, 15626, 15905, 13847, 16886, 21192, 5106, 704, 14310, 2682, + 6298, 2258, 9171, 7561, 26412, 24867, 15626, 8348, 20992, 9147, + 20042, 15119, 3160, 31458, 19584, 29345, 5955, 27774, 19157, 24428, + 30146, 19868, 28650, 23579, 27690, 5197, 20578, 1623, 20169, 31667, + 21154, 9090, 9164, 11867, 32724, 15784, 7264, 27341, 26162, 3250, + 10520, 22114, 28801, 32733, 11663, 29270, 22235, 31595, 5966, 26398, + 3250, 7833, 23536, 6652, 20498, 9680, 8836, 16592, 15910, 15647, + 7218, 26673, 4350, 22249, 20514, 8124, 27279, 23259, 24217, 16556, + 20732, 16673, 32613, 684, 16404, 28122, 16252, 21225, 29723, 24777, + 16864, 8554, 20941, 3405, 1989, 6308, 4000, 28307, 1014, 29513, + 1098, 11274, 32117, 25698, 16003, 25111, 1377, 17821, 18956, 25355, + 1053, 32744, 17904, 1106, 8623, 25150, 12298, 19436, 3430, 23233, + 2503, 4642, 26816, 17930, 14893, 27505, 22922, 18621, 20764, 28363, + 1865, 26464, 4052, 11263, 2009, 20589, 23224, 26229, 32596, 3237, + 30802, 26654, 30518, 2472, 16790, 171, 12478, 12995, 10091, 1277, + 30359, 3081, 12647, 25309, 12727, 933, 23002, 7406, 17800, 28014, + 27407, 17119, 30533, 23740, 16846, 14821, 25767, 20602, 6437, 26985, + 19906, 25981, 17257, 8190, 13865, 788, 29815, 17557, 8537, 13793, + 319, 10228, 28245, 11495, 14468, 2660, 5773, 3299, 14557, 10666, + 21508, 3552, 26624, 27449, 29362, 5894, 28479, 30374, 20776, 19635, + 24915, 21596, 21618, 17613, 27993, 25745, 21313, 21846, 17178, 17017, + 15434, 12608, 3215, 26553, 31721, 18307, 8357, 9246, 25866, 6254, + 21309, 15759, 8521, 30010, 1655, 19520, 27736, 24037, 24880, 10025, + 7841, 28990, 20169, 29879, 19245, 9020, 11306, 13249, 29893, 11667, + 17927, 20669, 7767, 28297, 3170, 6964, 15003, 10758, 8018, 12147, + 16117, 24811, 6402, 10372, 23966, 12965, 14588, 11868, 11007, 32002, + 21672, 13398, 12893, 11843, 12559, 31712, 15803, 32114, 10725, 12148, + 21756, 31990, 18887, 10699, 14409, 15204, 14273, 15693, 22962, 26264, + 7699, 4043, 6680, 9477, 28213, 24176, 19786, 6473, 9752, 15861, + 18798, 17412, 31407, 11029, 1443, 14315, 2990, 32577, 3044, 21528, + 1170, 7572, 16943, 30884, 20193, 27657, 20199, 27000, 14771, 27884, + 7288, 21344, 14961, 3214, 16441, 10082, 10973, 30525, 18582, 16461, + 10322, 2931, 13934, 1684, 30797, 15438, 9658, 1629, 5677, 30784, + 26992, 11222, 20023, 26632, 7847, 6600, 2525, 6625, 31483, 20078, + 12608, 27897, 23091, 2695, 17907, 20772, 20257, 11946, 6914, 11986, + 15306, 730, 20019, 2879, 13511, 7258, 26251, 15593, 13291, 21954, + 15380, 17479, 24102, 3411, 3294, 7352, 24745, 7560, 25412, 6586, + 10571, 17389, 12656, 9308, 32300, 27188, 17954, 27894, 14488, 1414, + 32428, 25879, 10030, 23, 23106, 5567, 23361, 24829, 21104, 9678, + 25982, 9794, 7404, 9405, 17871, 1336, 14990, 28044, 4736, 1292, + 30621, 13819, 6817, 10142, 16716, 7677, 1963, 5264, 29828, 21354, + 16998, 31390, 2532, 7668, 7945, 6957, 17159, 26460, 5283, 13350, + 15621, 5094, 955, 15364, 3132, 16138, 20531, 9260, 17744, 12396, + 21107, 5501, 6000, 7438, 25539, 23337, 23515, 2244, 2551, 28611, + 16035, 23529, 1618, 24729, 25777, 337, 19365, 31555, 13066, 8318, + 8119, 31100, 6993, 18455, 27625, 7985, 4492, 7317, 282, 32472, + 3883, 24384, 21193, 22531, 18246, 24922, 28025, 17461, 28710, 14234, + 11354, 9716, 17927, 16860, 29221, 3615, 11036, 23318, 26952, 2946, + 2260, 17806, 3907, 6312, 22108, 10058, 3922, 7152, 26725, 10952, + 12364, 11747, 28199, 6128, 22987, 6597, 16293, 31047, 29837, 19562, + 24883, 8549, 10778, 9548, 24630, 9385, 18343, 24026, 27948, 6955, + 18491, 31506, 22252, 16114, 18960, 32081, 19246, 14952, 29500, 17002, + 77, 15114, 2448, 19958, 11340, 7268, 16833, 27628, 27928, 16189, + 2501, 24946, 1548, 24847, 21120, 8178, 5818, 1978, 7328, 11887, + 12009, 21880, 19654, 15642, 25562, 22804, 2790, 5920, 19152, 11863, + 12528, 26311, 31950, 708, 3248, 32697, 1955, 21125, 30963, 9119, + 28708, 29092, 4587, 29938, 10681, 13694, 2260, 3086, 10011, 21109, + 23256, 25225, 26715, 16870, 28464, 2242, 10791, 17499, 24717, 22973, + 6729, 21505, 30134, 8494, 13680, 7004, 3442, 26579, 12851, 17465, + 424, 23047, 7507, 13028, 17861, 31893, 2880, 412, 17502, 9162, + 735, 20548, 16407, 24782, 1419, 15451, 15299, 22836, 22618, 5422, + 11724, 15567, 29345, 8268, 725, 19911, 13754, 571, 13215, 12013, + 20996, 8041, 29225, 27724, 32044, 2405, 12828, 29184, 23979, 19535, + 4197, 25932, 20368, 12430, 24100, 5094, 26240, 17600, 21189, 28053, + 20731, 17399, 10604, 15358, 224, 11298, 11671, 3259, 15668, 6345, + 31487, 430, 287, 13822, 29887, 31637, 19511, 24464, 14263, 21214, + 18434, 16513, 3123, 4796, 26293, 1722, 6856, 27258, 24386, 32510, + 26751, 18704, 10898, 11549, 24118, 22260, 26127, 13418, 24386, 16946, + 16230, 9326, 13145, 4287, 19851, 32222, 18352, 27914, 31485, 971, + 2912, 8405, 25931, 11545, 27406, 12867, 21975, 18994, 12235, 22322, + 14298, 13051, 17721, 13002, 12200, 4360, 10991, 4438, 14134, 6668, + 5407, 5715, 25561, 20115, 118, 4295, 14672, 4339, 15684, 27093, + 11781, 10367, 13789, 24973, 17552, 1279, 29759, 3593, 24189, 22109, + 10997, 20661, 11343, 19617, 17723, 13806, 24260, 5151, 16994, 29391, + 32091, 25071, 24694, 30023, 1556, 9374, 23071, 14959, 14967, 11555, + 15301, 24347, 15831, 25621, 5111, 7856, 26840, 24347, 782, 13365, + 30255, 27299, 28173, 20908, 27817, 23511, 22732, 8610, 27247, 16076, + 11094, 1797, 13704, 13565, 26255, 3894, 13727, 6137, 3098, 19039, + 15111, 13400, 15737, 32263, 14076, 12531, 22134, 26083, 18732, 30293, + 1730, 30447, 18038, 10860, 11997, 22718, 7409, 5252, 1077, 2318, + 14640, 32119, 16530, 23968, 20676, 24605, 24753, 22599, 13827, 32266, + 13871, 318, 6408, 11385, 20650, 15294, 23035, 28035, 546, 18640, + 20627, 10645, 677, 27745, 11480, 7456, 30507, 26772, 21806, 20114, + 24511, 17244, 25999, 8326, 12511, 2633, 13336, 21559, 7206, 2210, + 32455, 4155, 19496, 9141, 9859, 16238, 6046, 20843, 7004, 23596, + 28611, 23839, 24085, 31683, 3581, 28336, 15071, 11815, 9408, 2005, + 8552, 1512, 6063, 27569, 15892, 23650, 32517, 9384, 14474, 9780, + 31284, 7280, 15219, 27635, 5580, 31339, 10622, 15135, 18589, 15412, + 9128, 9045, 19488, 15480, 17594, 5063, 21523, 7175, 13843, 11498, + 26980, 16682, 423, 23150, 27728, 24888, 17736, 18803, 460, 18532, + 11045, 3834, 28708, 21465, 19344, 4761, 12002, 21001, 23556, 4468, + 11761, 15681, 20326, 19816, 6146, 1153, 19339, 15497, 5167, 22156, + 6139, 19663, 19166, 9019, 27124, 4108, 12777, 18198, 12722, 25633, + 25596, 5413, 19694, 27506, 1854, 13060, 17061, 27031, 1843, 31542, + 27674, 22613, 9697, 22971, 12062, 18636, 10289, 22245, 11663, 25841, + 8601, 17511, 3615, 9535, 8952, 16559, 16982, 3754, 20673, 1142, + 28123, 3285, 9308, 11925, 21231, 15492, 23753, 538, 26682, 14932, + 25223, 19099, 31690, 18858, 8884, 23242, 1881, 26601, 9589, 22858, + 6606, 8168, 25496, 20417, 31239, 12707, 8056, 12335, 26326, 25136, + 17697, 17534, 6217, 2037, 5557, 15450, 26159, 10009, 8719, 19778, + 14295, 12856, 22693, 6367, 8475, 17243, 10931, 19861, 20637, 24871, + 3222, 13657, 13137, 9300, 21877, 9922, 28988, 20833, 16935, 3170, + 10107, 14982, 8017, 30294, 902, 32235, 24271, 27164, 9142, 29719, + 7254, 25765, 24837, 25259, 21847, 17067, 26134, 18256, 22566, 837, + 14197, 20206, 11867, 6329, 4383, 19743, 1657, 15249, 15685, 18403, + 24781, 29681, 18509, 27541, 921, 19751, 13850, 31517, 31346, 26500, + 7309, 4200, 9447, 23658, 18251, 16661, 6243, 10407, 18625, 31601, + 22869, 4353, 7270, 12198, 12173, 13586, 15936, 6447, 6633, 17495, + 23466, 15503, 9522, 2435, 340, 27971, 10817, 16409, 27275, 28661, + 14182, 26681, 21947, 3314, 10506, 3258, 562, 9950, 13714, 12154, + 15142, 10663, 28807, 20466, 5114, 26039, 2292, 16398, 14781, 11118, + 18192, 15163, 32244, 15055, 3357, 29633, 32409, 23417, 24464, 2643, + 3939, 12505, 14450, 9188, 18504, 7839, 10758, 4541, 23873, 20846, + 5292, 16333, 9037, 16344, 29701, 23872, 26296, 14217, 1880, 29523, + 30365, 19138, 22837, 24166, 2474, 12810, 27224, 23890, 24611, 30240, + 975, 11006, 18217, 30259, 29792, 22061, 24893, 7765, 15653, 19354, + 22528, 25134, 29049, 17991, 23356, 20461, 19992, 6674, 4392, 20356, + 19329, 3209, 23528, 25109, 12191, 12287, 6228, 30119, 25257, 29920, + 14842, 8478, 2887, 25921, 26726, 23658, 13814, 16059, 417, 2302, + 2989, 24186, 9719, 24196, 23489, 13201, 12541, 4047, 32006, 11152, + 17363, 15377, 18999, 19801, 17575, 78, 15767, 30255, 30586, 15632, + 28633, 27920, 5159, 25693, 32317, 16453, 32161, 25574, 7165, 8503, + 32380, 32641, 21223, 12932, 11951, 12249, 14155, 31826, 26264, 22884, + 4887, 31460, 25408, 28566, 19521, 31595, 32737, 22613, 2661, 15134, + 10405, 9132, 24496, 31884, 27313, 27587, 30972, 1841, 17762, 28943, + 6460, 17736, 8406, 18118, 25627, 29665, 3503, 9110, 15328, 6451, + 12819, 10902, 13973, 13619, 2039, 16509, 28264, 24678, 20638, 16658, + 12625, 19699, 24924, 10467, 21493, 24060, 11680, 1850, 11211, 18734, + 12850, 19533, 29821, 30359, 9656, 18357, 9361, 4792, 4735, 27425, + 30024, 1756, 15736, 9548, 8646, 19368, 32385, 26924, 8564, 18930, + 28134, 13677, 24995, 15831, 2856, 12317, 17010, 20487, 16637, 10921, + 23281, 22884, 27287, 8712, 18566, 1228, 15913, 3894, 3472, 17202, + 16552, 7140, 17227, 17908, 9935, 3950, 8948, 29544, 18277, 2061, + 10797, 2488, 706, 21582, 24226, 25585, 17296, 194, 29933, 5462, + 31951, 28151, 24247, 20218, 18715, 680, 1056, 17294, 3890, 20017, + 11361, 32119, 20401, 12101, 22960, 23332, 30, 21219, 7269, 9630, + 5931, 8070, 8538, 30048, 11833, 24733, 32663, 22890, 23580, 17978, + 2351, 29841, 11550, 3342, 9061, 1305, 23622, 25237, 27155, 2693, + 12762, 22926, 8781, 32174, 11042, 686, 16449, 20243, 20335, 32149, + 1121, 8048, 22108, 5633, 29637, 26258, 852, 16706, 12234, 31849, + 23124, 4078, 28928, 14431, 4594, 26581, 10563, 6888, 26271, 6711, + 4391, 13658, 13097, 10805, 9370, 17408, 29316, 22406, 21188, 11881, + 3785, 16747, 25906, 25094, 8575, 13797, 25140, 15093, 2330, 293, + 28291, 28439, 30657, 20804, 29223, 12136, 24404, 564, 1176, 29584, + 24238, 26103, 25636, 9141, 5159, 23822, 31888, 5268, 31069, 21301, + 16642, 2512, 11317, 29773, 23839, 12937, 18716, 11674, 368, 21553, + 14995, 20270, 20234, 6043, 8682, 24997, 9838, 11349, 15691, 1043, + 23982, 13880, 28411, 26171, 8657, 14653, 18909, 13058, 1913, 30611, + 18232, 32547, 3779, 30190, 2489, 4131, 7791, 6689, 20924, 3976, + 14913, 6127, 1735, 32326, 30886, 8222, 31319, 31866, 31643, 1486, + 18400, 14682, 28875, 19977, 30223, 8044, 23799, 23963, 15698, 4343, + 13979, 14628, 6042, 18616, 5071, 2923, 32096, 13013, 28523, 26101, + 29670, 21015, 18798, 28631, 20671, 1363, 9889, 30408, 22161, 16681, + 13175, 5754, 3866, 4593, 3640, 27179, 1649, 31219, 31838, 31863, + 19342, 32726, 3904, 26992, 23053, 348, 31725, 26352, 20783, 7756, + 10843, 10030, 7202, 20399, 16321, 18832, 28546, 32574, 10305, 19904, + 16415, 13084, 25546, 15618, 5889, 8641, 6717, 32197, 26281, 16950, + 15054, 1399, 17818, 11400, 10926, 23710, 12824, 24183, 18151, 10533, + 17200, 14446, 15751, 13043, 27384, 18104, 512, 5827, 11800, 10992, + 24783, 6959, 29833, 14777, 30300, 23565, 22429, 8368, 3361, 16383, + 4416, 10399, 9999, 27718, 28797, 26612, 13869, 10967, 30532, 31089, + 18563, 30385, 1386, 2852, 11062, 8813, 13553, 22938, 18676, 19735, + 19338, 27472, 18322, 6460, 25944, 8298, 10484, 23108, 15607, 27303, + 29486, 11266, 13501, 16789, 9192, 10378, 1343, 9985, 29447, 12355, + 13591, 20402, 10378, 30781, 15845, 17495, 2481, 21188, 660, 3773, + 8549, 26165, 31455, 18245, 26580, 17936, 14355, 5782, 3420, 8783, + 10006, 32655, 27818, 246, 13224, 1407, 26659, 13804, 12521, 665, + 13954, 3337, 11035, 17058, 25283, 23152, 1706, 13805, 833, 25887, + 4201, 20617, 27675, 24516, 25375, 27065, 24795, 25554, 17986, 12196, + 2394, 8917, 27138, 846, 11742, 16899, 9354, 20679, 32390, 5139, + 5250, 14243, 27112, 9554, 19672, 6057, 18168, 19955, 28096, 21968, + 22060, 16038, 20217, 28007, 24029, 15213, 8018, 8595, 26097, 30891, + 7534, 25342, 22199, 20094, 19908, 15822, 32530, 13903, 20969, 25556, + 17882, 18088, 17682, 9990, 2687, 8517, 16853, 30768, 30959, 1901, + 29999, 15846, 21690, 2179, 25291, 6077, 32628, 32472, 16001, 25019, + 15964, 20163, 32542, 3083, 826, 7156, 3683, 6901, 15502, 26885, + 14928, 25358, 28902, 21842, 19362, 21932, 10640, 27915, 26010, 29085, + 28785, 16263, 4864, 11289, 25909, 12251, 9320, 26100, 11689, 13228, + 17332, 16664, 17764, 29761, 24794, 27187, 28845, 1500, 23527, 10925, + 22169, 10571, 4164, 22237, 1652, 19386, 9528, 27772, 27746, 14539, + 18351, 30042, 31046, 13033, 12602, 591, 483, 26073, 8278, 21109, + 6466, 26513, 9713, 31380, 27219, 15182, 27690, 8414, 21718, 2785, + 23129, 8413, 20860, 2633, 14927, 11177, 24906, 14727, 17119, 14430, + 3135, 30894, 11165, 22733, 13110, 8762, 11749, 18034, 14606, 8098, + 5919, 15271, 18183, 11313, 20546, 27926, 12399, 14865, 32407, 14196, + 14719, 11689, 8640, 21869, 17792, 25385, 21240, 12762, 12337, 12898, + 2460, 10810, 22125, 25820, 2283, 21856, 4617, 21026, 23306, 8809, + 11516, 5729, 32074, 25585, 4034, 5937, 8622, 7047, 12034, 19123, + 10664, 6493, 31854, 29916, 15908, 17476, 24298, 20240, 10439, 7640, + 30193, 19955, 21113, 22264, 11317, 31100, 2849, 2170, 23942, 2891, + 32431, 25628, 5109, 18236, 31584, 29117, 8249, 16956, 27965, 11469, + 4434, 2907, 13030, 5634, 20332, 27427, 12173, 3404, 24069, 11560, + 15941, 2060, 2386, 10209, 27261, 30593, 735, 30390, 4551, 29570, + 12663, 6746, 21215, 32115, 14403, 29531, 28136, 28530, 31029, 19192, + 31224, 27601, 5800, 24114, 5079, 24777, 14400, 7974, 27140, 29804, + 5912, 8345, 18937, 27868, 5404, 24533, 4784, 26811, 25523, 24756, + 12390, 31714, 32162, 20474, 1439, 1110, 30765, 8167, 25962, 19319, + 28542, 21261, 742, 18299, 4683, 26976, 31237, 4379, 4276, 9100, + 21904, 24972, 29290, 4235, 13932, 11441, 6166, 6518, 27, 11151, + 27641, 32667, 11909, 27393, 1487, 209, 14585, 12973, 17914, 5640, + 9839, 22582, 11228, 36, 14096, 21172, 27525, 5900, 18168, 8801, + 1678, 3837, 20026, 24724, 27716, 10277, 8740, 3060, 25866, 31853, + 23493, 25829, 19704, 24470, 1965, 28231, 25032, 1415, 756, 26208, + 10227, 25317, 10622, 19478, 11031, 31717, 14716, 17506, 7580, 5470, + 4150, 29697, 7063, 31779, 9225, 8557, 5358, 22159, 24491, 13067, + 8894, 13409, 17079, 23974, 19449, 20682, 27493, 5916, 32084, 15045, + 28056, 8535, 14432, 22439, 6943, 16767, 15862, 29687, 8500, 30574, + 14297, 22143, 8009, 18789, 9623, 2467, 6917, 30717, 4272, 20210, + 30089, 11872, 19450, 28923, 17911, 696, 16964, 26466, 15669, 23272, + 6202, 307, 30446, 2427, 9609, 2421, 23514, 14041, 2371, 6659, + 31040, 27329, 23940, 4247, 29113, 18931, 20054, 23080, 27446, 23597, + 6098, 2166, 7748, 28249, 12168, 26083, 27279, 25861, 7457, 31620, + 5285, 4883, 17987, 15736, 4349, 26802, 12725, 20856, 56, 3725, + 1476, 9136, 16550, 16706, 17893, 15821, 14604, 277, 5019, 5208, + 16549, 27110, 30363, 15938, 18534, 18272, 28719, 19136, 10630, 14708, + 24024, 10585, 15678, 32005, 28471, 22588, 8654, 11493, 13101, 19672, + 20672, 3841, 66, 19111, 27248, 16856, 15427, 7242, 23244, 24114, + 18223, 25307, 15193, 24846, 3413, 28543, 5751, 29148, 1206, 23354, + 28645, 22764, 6576, 20744, 13959, 14887, 20190, 15913, 871, 26621, + 5178, 23740, 18939, 25246, 6532, 19599, 23974, 22381, 8168, 11830, + 28800, 13489, 3803, 29786, 28506, 9614, 20550, 24404, 24317, 2613, + 14310, 22266, 7859, 16471, 31218, 5817, 13321, 20447, 29951, 24549, + 978, 20448, 21282, 9804, 7279, 6092, 18004, 27409, 30030, 4328, + 25626, 14776, 18353, 15356, 25701, 31613, 25777, 16207, 18358, 5354, + 11394, 4306, 10375, 424, 2808, 4252, 15335, 25238, 7498, 10134, + 17764, 12824, 25879, 15992, 557, 4786, 11430, 8436, 20344, 27605, + 114, 8496, 4130, 18680, 25062, 3962, 5525, 12011, 5989, 23096, + 4079, 23400, 15757, 27165, 18785, 8940, 32189, 3747, 12500, 24772, + 3540, 3765, 30675, 11627, 23868, 12715, 18341, 20243, 4904, 21628, + 20921, 6274, 16921, 24367, 16200, 10783, 29863, 11923, 17461, 18765, + 30590, 17968, 22840, 16887, 1191, 24189, 24891, 1644, 13321, 9044, + 15876, 366, 21807, 13986, 6165, 20101, 5707, 19299, 24016, 19610, + 7291, 27854, 15839, 16064, 29004, 17868, 8403, 28501, 27005, 31919, + 26123, 2378, 26885, 30497, 26584, 7103, 10326, 6746, 2782, 31699, + 24292, 23447, 23742, 30906, 30433, 13511, 1522, 5648, 11851, 20871, + 4856, 16492, 18729, 2128, 22706, 12345, 14610, 29679, 30733, 28353, + 24046, 16694, 17633, 12683, 17409, 1266, 5144, 32743, 805, 11805, + 13587, 22215, 9046, 26708, 6729, 16080, 10868, 651, 30001, 2225, + 14149, 11775, 18787, 9140, 19898, 10604, 15098, 17042, 14451, 11392, + 7228, 17036, 9092, 10128, 32381, 2231, 21626, 11147, 19008, 18840, + 9113, 3628, 8864, 28400, 6760, 7795, 6126, 4057, 23408, 32085, + 28060, 12050, 14144, 28339, 30553, 13208, 4788, 13961, 20862, 20342, + 631, 22120, 30889, 18653, 16327, 16166, 1191, 22903, 23160, 19156, + 2712, 6137, 19516, 4848, 27213, 8184, 20769, 30428, 2805, 27645, + 10587, 26255, 19193, 5595, 10308, 17803, 10020, 28992, 13473, 31246, + 23815, 13451, 7719, 31885, 3228, 9181, 27710, 31582, 7465, 29299, + 2564, 26293, 26983, 20019, 29589, 30929, 30436, 18820, 18570, 3703, + 15526, 5987, 26559, 25504, 15633, 19892, 30810, 2159, 30475, 8661, + 1032, 8805, 29277, 21369, 1674, 19928, 27484, 14772, 5745, 13217, + 25034, 9810, 2853, 10006, 3831, 4973, 17452, 24632, 1835, 20287, + 30121, 9773, 2632, 6708, 7715, 32240, 18348, 27459, 2440, 12615, + 6485, 20095, 1394, 16885, 31304, 4585, 10920, 7473, 15411, 13449, + 24552, 9048, 16465, 19389, 31204, 22495, 14222, 21200, 7306, 19851, + 11756, 6350, 16776, 8932, 25893, 16535, 2064, 8935, 17042, 1843, + 12760, 24059, 21579, 17509, 29030, 20262, 12688, 22915, 28042, 24961, + 14951, 644, 7724, 27113, 31367, 15707, 26183, 26872, 13173, 15236, + 3894, 10547, 20973, 4572, 2351, 3461, 21123, 6837, 5389, 9197, + 22922, 15914, 15186, 20595, 565, 5296, 13420, 9312, 10492, 6126, + 9783, 13742, 13633, 21967, 23236, 2750, 9780, 22997, 21593, 28233, + 26356, 16560, 25272, 16281, 11767, 10767, 7588, 4857, 27563, 31926, + 26786, 13930, 3165, 12496, 25597, 28311, 4443, 5255, 28428, 11, + 17749, 16083, 8119, 22690, 4105, 9359, 21304, 27965, 11552, 978, + 15391, 8122, 19026, 6524, 4505, 16676, 20689, 21775, 17393, 1062, + 24037, 32449, 23127, 29638, 11987, 975, 24033, 16976, 1028, 23827, + 30159, 1167, 16227, 17369, 6419, 11587, 16903, 10479, 30993, 23760, + 19730, 8699, 2467, 22836, 27097, 16657, 19040, 16955, 7493, 2693, + 13598, 22006, 27257, 31998, 30133, 22889, 26804, 24524, 20230, 31108, + 28793, 19324, 20796, 14421, 9198, 8361, 17746, 14971, 22147, 24853, + 29142, 2270, 6099, 7928, 7404, 6813, 26063, 30359, 28565, 15176, + 18969, 17945, 7729, 8528, 26286, 6038, 31299, 18619, 13768, 29042, + 20853, 26140, 259, 233, 15836, 1475, 21438, 21226, 19324, 11057, + 27844, 3881, 5903, 1231, 29484, 26992, 18946, 2015, 29309, 15669, + 6081, 2226, 25110, 1906, 8244, 8987, 15309, 12977, 23433, 1829, + 26071, 26982, 29493, 32257, 29672, 25218, 11317, 15170, 922, 23621, + 18074, 20045, 14355, 14676, 30276, 18442, 6370, 29707, 22766, 26848, + 22571, 23572, 3435, 27361, 17412, 21049, 20526, 15425, 26398, 30326, + 16656, 15885, 7336, 31827, 13902, 23321, 7024, 6513, 16593, 2875, + 15436, 16994, 17843, 5814, 32696, 10685, 20734, 11769, 5824, 13682, + 1463, 10634, 29473, 24156, 20251, 1597, 8875, 11797, 14120, 29097, + 31209, 20238, 1631, 31452, 20996, 25311, 4297, 15182, 24037, 4827, + 21560, 3092, 23194, 3816, 19407, 6998, 7931, 28302, 7364, 30211, + 20109, 10317, 101, 22979, 13222, 4916, 3929, 25390, 1234, 9903, + 21033, 27707, 6518, 27809, 15103, 12027, 20915, 22962, 27821, 135, + 13709, 32747, 29424, 14391, 3917, 20352, 20470, 5908, 1315, 23020, + 799, 17296, 30224, 19267, 27905, 11637, 7683, 3524, 13486, 28188, + 17731, 19374, 4665, 30943, 32524, 17238, 8822, 12240, 15229, 8565, + 10242, 12460, 12466, 21958, 23939, 28170, 24365, 5585, 15665, 26840, + 13690, 513, 24982, 7688, 5906, 8445, 25489, 31879, 19073, 9525, + 22945, 30575, 4804, 12475, 23648, 2173, 30709, 2901, 13390, 14021, + 810, 32464, 5328, 30116, 29082, 19863, 2169, 28663, 8961, 30596, + 29319, 5002, 18246, 16629, 18865, 28884, 3931, 12323, 28704, 17121, + 25958, 29871, 19643, 13199, 28702, 28030, 2471, 12285, 25975, 16583, + 7182, 24897, 28801, 14954, 31008, 7513, 11917, 25732, 7256, 25700, + 1939, 3648, 17153, 15891, 26064, 13552, 248, 2906, 4275, 860, + 4787, 8299, 12026, 23385, 4226, 15076, 13337, 7582, 26389, 10311, + 15579, 9172, 14655, 12460, 8011, 8428, 3833, 6578, 12757, 23477, + 30122, 21947, 3497, 30638, 30118, 13145, 20576, 16409, 244, 8451, + 8418, 10576, 16212, 22797, 31623, 31836, 23424, 2519, 2428, 24772, + 13747, 20036, 1897, 14499, 8040, 2669, 32592, 765, 1124, 151, + 22969, 14419, 9462, 12899, 2572, 21877, 17790, 25157, 23102, 2200, + 30508, 18743, 6575, 13480, 24074, 21679, 13139, 16096, 6205, 206, + 28433, 23191, 10888, 8922, 25192, 18269, 5649, 14318, 21592, 13638, + 31592, 3056, 6272, 17286, 28357, 19652, 9604, 19774, 24977, 23999, + 27313, 30870, 17704, 32607, 10468, 15372, 19166, 7993, 21159, 19980, + 25920, 25650, 14238, 12992, 28316, 19186, 32546, 23593, 6339, 26262, + 13867, 4207, 2573, 6565, 29811, 2677, 19358, 23366, 23768, 2225, + 27880, 23728, 32002, 15931, 113, 8929, 20551, 23075, 21974, 26771, + 27083, 6408, 5393, 23660, 2540, 5350, 7927, 3957, 8925, 18224, + 22276, 10189, 4240, 28959, 14939, 11167, 2010, 24137, 26633, 30801, + 210, 13685, 32394, 14846, 8314, 19374, 29326, 30503, 26017, 11670, + 17149, 1189, 7431, 21245, 11832, 30178, 24771, 12466, 19618, 23881, + 9954, 7321, 13407, 28923, 9041, 14334, 2651, 26564, 253, 21545, + 9358, 8781, 13880, 1331, 8575, 29843, 30815, 22296, 6407, 9111, + 4793, 22796, 19032, 27480, 9379, 29459, 27453, 8446, 2794, 14796, + 31908, 17752, 30841, 13453, 18521, 7048, 564, 18869, 27668, 6141, + 8739, 17726, 5558, 15266, 30163, 11666, 21712, 12117, 15353, 23200, + 9573, 13211, 21849, 814, 6129, 14482, 19686, 13425, 15017, 12839, + 11051, 26587, 10245, 31880, 6377, 11060, 24188, 25821, 5378, 3820, + 17806, 10803, 16789, 20144, 1032, 2744, 3179, 26683, 2948, 12590, + 13970, 5231, 32047, 5786, 32583, 22180, 19407, 11324, 30234, 5696, + 16132, 25028, 14412, 30343, 15309, 23491, 7548, 24757, 30287, 28256, + 6018, 29770, 28882, 12364, 18844, 20902, 27661, 14840, 6856, 29397, + 11427, 25861, 18288, 1708, 14829, 8188, 13900, 533, 31226, 10815, + 5253, 23463, 20180, 19519, 32022, 9594, 6405, 24560, 12152, 31033, + 17242, 15738, 6526, 27256, 15335, 24626, 14462, 29512, 32331, 22472, + 5553, 3849, 4918, 22977, 25052, 30592, 10536, 1832, 29744, 14378, + 10875, 14977, 12399, 3023, 29859, 29183, 6225, 14425, 5301, 10743, + 3110, 23783, 11558, 15251, 32444, 26142, 26837, 5067, 8323, 7359, + 8012, 22161, 17903, 639, 3651, 16972, 3358, 28947, 19558, 26504, + 1723, 12170, 22873, 2284, 26146, 29151, 28408, 14188, 31941, 30415, + 30691, 32448, 1230, 14312, 20282, 13844, 32518, 24547, 11685, 18689, + 10229, 31539, 26520, 3260, 18507, 5854, 24789, 30093, 25726, 16780, + 22834, 16515, 4021, 8925, 30364, 25959, 26085, 7424, 12380, 25838, + 28985, 11394, 32665, 18226, 32440, 31331, 31006, 24163, 9762, 21615, + 13406, 8634, 2330, 32715, 9141, 13487, 3466, 6607, 16881, 28268, + 5956, 31507, 14609, 30081, 1253, 20685, 26189, 17369, 29695, 24666, + 24406, 9965, 4215, 10522, 23774, 4151, 29483, 22657, 11867, 18922, + 21464, 17515, 6920, 25568, 4668, 24496, 21549, 27860, 23159, 23801, + 23713, 19409, 26807, 29650, 2445, 19946, 12308, 31744, 114, 15404, + 26809, 21268, 22126, 3537, 18757, 16448, 13499, 2742, 28529, 19050, + 9848, 29200, 15346, 13439, 29612, 31066, 9633, 5130, 11656, 14226, + 18393, 13829, 20460, 20644, 12320, 17902, 7213, 29991, 12042, 27422, + 4374, 2631, 12379, 20567, 9260, 3586, 24172, 18844, 8246, 2968, + 5328, 3141, 19709, 13372, 26166, 11602, 17489, 28688, 14737, 23040, + 6220, 31568, 17015, 32410, 11995, 25549, 22727, 20370, 30329, 31390, + 29583, 26832, 22590, 3602, 19245, 30538, 2841, 32745, 26292, 8405, + 11354, 3011, 6289, 7738, 16755, 20952, 11886, 15245, 1791, 31210, + 22614, 12986, 12547, 632, 2842, 5217, 116, 4149, 25076, 13028, + 20986, 20920, 17259, 17342, 2548, 19317, 19830, 11030, 444, 8172, + 19684, 9619, 23084, 10272, 17403, 17523, 22369, 28747, 17504, 28631, + 10686, 27856, 8247, 25441, 18484, 31601, 13962, 22785, 16321, 1218, + 24303, 22028, 7223, 20957, 14437, 20111, 20031, 27224, 24448, 4834, + 12957, 3700, 20422, 15232, 7862, 8599, 29784, 1813, 5711, 6397, + 11221, 24281, 3735, 17066, 17275, 29981, 25192, 30883, 11663, 12835, + 23498, 5213, 30463, 25395, 30393, 13942, 16455, 24555, 16680, 13987, + 15761, 6615, 31813, 20458, 544, 19010, 25343, 23825, 6894, 12875, + 14789, 14678, 15508, 8507, 2961, 6496, 2155, 13589, 14374, 28790, + 11505, 2415, 14230, 13807, 25966, 18561, 14086, 7913, 7383, 21116, + 29365, 8254, 14322, 28445, 28987, 19636, 19131, 15745, 1039, 25460, + 15623, 22075, 12620, 8894, 8807, 21412, 27317, 13018, 18767, 13426, + 13814, 15698, 17783, 19596, 10797, 1118, 31106, 14365, 26280, 3744, + 25566, 1255, 26352, 15121, 13035, 1499, 22535, 20796, 25695, 1254, + 21231, 25002, 29673, 18189, 8299, 2717, 23828, 5820, 27146, 29953, + 29807, 18523, 31840, 7583, 23531, 27939, 32407, 16020, 5764, 10288, + 17191, 27905, 32547, 22999, 5601, 16133, 9923, 5433, 11058, 3560, + 24019, 5144, 5286, 10393, 27868, 7402, 24235, 2651, 16484, 3884, + 15256, 27067, 3090, 2553, 24557, 13253, 10515, 24795, 29660, 18295, + 13243, 21217, 15881, 1398, 11376, 14322, 27535, 2048, 8132, 6240, + 6467, 5922, 6625, 25280, 572, 11563, 26922, 13306, 30611, 8032, + 29727, 26012, 1383, 16113, 9857, 22132, 26916, 1580, 14775, 8541, + 15621, 9713, 23665, 13913, 31960, 1894, 32139, 2528, 18434, 5611, + 15924, 20088, 1218, 25937, 20303, 27782, 18618, 2118, 15897, 11364, + 10923, 11028, 29709, 18099, 24960, 18060, 28165, 20211, 21646, 32572, + 28321, 11345, 282, 1257, 12268, 5311, 22811, 1593, 22589, 15946, + 32578, 3247, 13916, 15016, 1080, 21724, 2278, 12561, 2343, 30420, + 21747, 27217, 27515, 29408, 4085, 25370, 24042, 30322, 8420, 22686, + 4785, 27998, 31896, 861, 23329, 13126, 24950, 29756, 24667, 31062, + 30286, 18392, 22872, 13362, 20612, 9241, 25132, 9773, 9582, 13322, + 22667, 22972, 11006, 5608, 24972, 8234, 27553, 28537, 31917, 26765, + 4454, 7326, 18455, 760, 27747, 11308, 23076, 9202, 14421, 7928, + 21535, 15399, 25990, 7473, 4683, 30814, 26868, 5619, 7462, 32171, + 22447, 32024, 16366, 13798, 20867, 6994, 26725, 16475, 32471, 9335, + 13266, 23235, 9685, 2075, 19784, 22132, 6859, 12388, 298, 28116, + 4049, 8986, 10735, 3603, 1965, 14590, 23075, 26906, 28659, 24413, + 26542, 25534, 9841, 6116, 15449, 13370, 28105, 10934, 3982, 17628, + 5472, 16060, 27999, 12030, 22647, 19516, 11979, 25144, 14488, 7598, + 18236, 19412, 22324, 21195, 2899, 4738, 4746, 6630, 21736, 14925, + 27082, 4991, 10317, 17614, 27771, 5142, 16083, 8608, 14618, 14397, + 6800, 788, 20228, 32497, 1887, 27429, 14230, 24106, 11559, 1198, + 19090, 1457, 5985, 7386, 4548, 17458, 21972, 20700, 4070, 17042, + 6819, 17377, 28354, 2434, 31114, 28995, 22678, 1365, 11365, 28416, + 188, 31758, 242, 13418, 8074, 10686, 21463, 14624, 25976, 30477, + 15591, 60, 10624, 32278, 20641, 25550, 22219, 20856, 6905, 13450, + 23945, 14761, 14172, 445, 11041, 23694, 10734, 7558, 28042, 841, + 24086, 12610, 9720, 29267, 28220, 30280, 20156, 1994, 6353, 26327, + 6245, 5132, 1680, 31564, 556, 8324, 17924, 18328, 20879, 6340, + 8267, 3314, 11579, 2516, 11033, 5065, 24367, 27594, 15360, 25085, + 543, 10651, 8430, 8526, 7702, 23345, 16887, 18965, 30816, 26046, + 4180, 26162, 6823, 30287, 26284, 69, 21852, 2724, 16019, 4897, + 16729, 31807, 24401, 3453, 1220, 16827, 27108, 15853, 24374, 23135, + 26184, 15121, 16668, 6131, 13813, 7180, 29278, 15058, 4010, 5432, + 3135, 5185, 26729, 19737, 19077, 8959, 28098, 32143, 30681, 24094, + 30037, 13647, 31088, 8720, 1427, 19859, 15851, 29701, 23492, 26641, + 4988, 3957, 4942, 2346, 10571, 29176, 21044, 8805, 16408, 22878, + 7901, 8475, 20001, 26620, 17175, 21876, 6726, 25412, 24667, 17810, + 4715, 16537, 6483, 16651, 24862, 29793, 29849, 18589, 1657, 14171, + 23746, 7709, 30794, 13874, 24987, 4273, 3442, 24403, 7858, 22847, + 14475, 17148, 27416, 6949, 21966, 14742, 13926, 23851, 4147, 17477, + 14078, 3765, 8665, 23485, 19859, 27966, 29609, 8535, 16876, 24065, + 24631, 9828, 15588, 4089, 7656, 15809, 9142, 31244, 5210, 2988, + 14455, 2436, 8448, 18389, 9188, 5310, 3853, 26040, 19550, 26150, + 18068, 5931, 7731, 29539, 30445, 23859, 10527, 5326, 16474, 6940, + 14099, 20777, 8426, 24521, 16436, 4494, 10398, 22856, 24615, 20901, + 14539, 9897, 12428, 8218, 21183, 31761, 11464, 846, 14759, 19722, + 776, 5709, 27569, 28957, 30339, 29185, 13933, 30371, 14079, 13989, + 27257, 6104, 20886, 18811, 29277, 1585, 7866, 6850, 20660, 32752, + 20855, 2019, 18829, 25589, 3064, 11755, 15120, 14295, 7501, 16445, + 26981, 21555, 19563, 14217, 16849, 11076, 21620, 27896, 22818, 23988, + 30265, 30121, 10574, 4530, 17150, 21089, 21079, 16453, 1149, 18302, + 25201, 27872, 26157, 28973, 26983, 31542, 17347, 26589, 23355, 5967, + 16633, 2292, 1549, 25564, 3727, 14272, 7997, 25288, 144, 10928, + 11220, 19471, 22999, 6340, 18464, 18131, 28881, 23362, 4767, 14509, + 4485, 22443, 15903, 8001, 16593, 17901, 3874, 10261, 17654, 9946, + 18567, 31395, 5975, 32473, 19303, 26796, 9085, 10459, 28222, 30928, + 30559, 21905, 20299, 32604, 18656, 2781, 9785, 14691, 29850, 14214, + 22782, 7696, 23002, 12079, 22532, 23098, 14315, 8842, 1414, 10809, + 28824, 26569, 11326, 1528, 21484, 10069, 18749, 21324, 22844, 16579, + 11546, 4740, 26984, 31748, 14414, 14658, 7773, 23259, 14047, 12169, + 7134, 7828, 7996, 8982, 25989, 22004, 14440, 9460, 22020, 1900, + 27483, 17739, 7089, 19345, 17318, 8569, 24130, 27601, 25429, 6243, + 29140, 22878, 28185, 6413, 15155, 4207, 12199, 16233, 10581, 15272, + 347, 13165, 26663, 11222, 13089, 32360, 12429, 30688, 31637, 19094, + 22947, 22779, 13161, 16626, 3914, 5916, 24583, 31051, 20960, 6579, + 20856, 12442, 9038, 28822, 22405, 14739, 1352, 26939, 21020, 23418, + 11472, 1067, 8437, 4920, 3836, 24243, 13940, 27310, 11002, 25821, + 20946, 23113, 13649, 24479, 9336, 26839, 2153, 13137, 26283, 32153, + 32334, 25188, 8046, 6297, 6065, 10322, 9124, 25917, 5168, 16593, + 4214, 23325, 24535, 12108, 17652, 24850, 31206, 6803, 7935, 9871, + 14548, 4306, 32614, 10037, 3989, 18368, 22863, 3233, 14361, 11544, + 20697, 25225, 26389, 23373, 11519, 20743, 27909, 18042, 8384, 20788, + 26738, 13719, 12156, 4089, 28016, 23539, 12450, 29474, 18028, 9950, + 13960, 29876, 12836, 5030, 23706, 18561, 17143, 8646, 16567, 32761, + 17247, 13657, 11224, 8259, 17340, 32213, 11763, 24205, 13651, 13557, + 12575, 30368, 13974, 18935, 18424, 20407, 29862, 19720, 15173, 25489, + 17942, 12029, 6872, 7109, 25330, 10563, 21942, 6486, 24063, 25113, + 7544, 6791, 17221, 8444, 6385, 29424, 32715, 10083, 10769, 18898, + 30370, 429, 19209, 4776, 26404, 25688, 18096, 29694, 2174, 26995, + 20518, 4230, 32364, 16982, 15799, 26217, 2884, 12688, 27394, 14555, + 9014, 24865, 1626, 14322, 17618, 26858, 19820, 23224, 15050, 6497, + 32034, 8063, 11002, 24201, 30920, 9844, 5797, 20258, 27758, 23159, + 12290, 15388, 28301, 7210, 10487, 21586, 25837, 9032, 24356, 12405, + 12485, 2617, 2339, 23366, 26149, 14307, 18702, 10761, 4753, 7106, + 18498, 31030, 22254, 28731, 16483, 8561, 22691, 30509, 5537, 14524, + 19960, 25802, 26169, 29676, 10689, 27813, 13118, 15449, 10817, 9483, + 1689, 19930, 20297, 13463, 14927, 28913, 31648, 32257, 17666, 9538, + 17831, 15927, 12219, 20554, 15165, 20655, 6374, 17485, 7393, 3579, + 26782, 13200, 21526, 30878, 6699, 25545, 10508, 11294, 16073, 13780, + 31612, 16986, 7595, 21927, 724, 8990, 5440, 9864, 22932, 29173, + 8629, 16254, 22520, 3646, 11451, 3077, 471, 22914, 2698, 25861, + 3019, 14341, 18470, 13829, 15250, 32667, 12001, 668, 10164, 18621, + 8481, 24617, 6758, 21980, 13411, 14276, 7087, 1269, 11392, 25717, + 26313, 32614, 15407, 419, 20170, 7168, 11980, 32207, 22148, 10077, + 24408, 17794, 32681, 1652, 8384, 6787, 5427, 26474, 31586, 27470, + 29954, 29868, 25549, 16066, 7367, 15930, 20168, 2074, 32290, 19782, + 18071, 30494, 24071, 5629, 25977, 6298, 17254, 15269, 22901, 23312, + 14603, 23272, 21846, 19370, 26307, 20079, 4853, 8050, 17856, 8627, + 19126, 23749, 29758, 9744, 17493, 14597, 27751, 24092, 25827, 32399, + 8820, 13110, 24075, 3821, 19990, 10594, 25242, 21836, 25364, 1364, + 1087, 15664, 32053, 6346, 7822, 358, 17248, 10527, 29515, 26427, + 1538, 9245, 23526, 14827, 7344, 5908, 13378, 6748, 18190, 7208, + 25274, 30476, 5359, 12783, 8550, 16456, 6589, 12604, 27802, 11424, + 28444, 21813, 15024, 8599, 19146, 2476, 24840, 5279, 11209, 29612, + 19020, 30788, 25281, 26705, 25081, 2471, 31136, 17885, 22811, 4654, + 16003, 25353, 10328, 29050, 30169, 2201, 29487, 921, 30661, 10547, + 12418, 14205, 15557, 19313, 15442, 27053, 7570, 23093, 14027, 22331, + 19015, 20285, 21632, 15606, 15556, 27639, 1015, 30432, 10837, 29846, + 3176, 13283, 14479, 8549, 2498, 4543, 31099, 14939, 27119, 13639, + 29431, 4336, 20313, 13005, 13656, 19504, 31917, 23259, 17748, 15118, + 11743, 16164, 23840, 8442, 622, 953, 18645, 12470, 23785, 20608, + 25112, 10597, 23474, 30435, 28410, 18917, 20226, 8133, 26653, 3678, + 20525, 5562, 27161, 27675, 24103, 32631, 22045, 32665, 9031, 7815, + 26074, 24798, 18508, 26176, 13980, 4620, 30878, 32502, 4026, 15414, + 31702, 2414, 26673, 11890, 2175, 13973, 28556, 21296, 13342, 1906, + 7687, 4042, 9329, 18778, 4083, 16988, 26101, 8803, 12380, 20949, + 2986, 16923, 30442, 1102, 31770, 2681, 15621, 3995, 3992, 17683, + 28220, 6176, 25542, 14482, 6120, 4582, 2138, 12899, 7180, 14319, + 26913, 22704, 2944, 3526, 6468, 22671, 27471, 8025, 25546, 17529, + 26973, 29451, 17758, 14077, 17485, 25094, 13138, 3509, 27297, 19150, + 21477, 20602, 5411, 21020, 3009, 6356, 24708, 9705, 1224, 3508, + 10820, 30409, 4624, 18278, 13521, 7034, 5420, 12225, 22259, 4797, + 18606, 12961, 14951, 22372, 5097, 21143, 28107, 15021, 19452, 30174, + 19898, 14542, 1548, 32657, 2359, 31697, 24272, 2332, 17743, 162, + 23379, 20465, 9210, 4711, 18165, 11132, 12867, 4111, 12979, 8024, + 424, 12334, 31145, 24003, 28868, 14088, 28373, 20047, 26411, 21227, + 6174, 27867, 2198, 4803, 6805, 5314, 9024, 10984, 15354, 3154, + 23951, 31802, 7554, 25728, 26359, 4464, 5079, 217, 12316, 1709, + 16481, 23484, 28214, 3924, 20420, 23239, 28090, 3614, 426, 18319, + 30775, 29546, 24002, 16532, 20861, 21972, 14857, 13940, 4101, 30984, + 30280, 18782, 21645, 14041, 4317, 6858, 9475, 32397, 30911, 26471, + 28324, 24187, 16573, 32085, 29541, 19417, 6150, 25237, 9874, 7375, + 11246, 16318, 15541, 26831, 22255, 25329, 29369, 17193, 16622, 28430, + 20559, 986, 21696, 2856, 27025, 4773, 12013, 9783, 25573, 26726, + 27060, 8973, 16933, 15010, 27215, 140, 15357, 1378, 4984, 21013, + 9364, 26434, 299, 7235, 9326, 965, 12718, 27098, 19089, 24555, + 16335, 11514, 14566, 27910, 4717, 16537, 28643, 13769, 9529, 7728, + 32103, 24994, 20158, 7091, 4837, 586, 10143, 17863, 18117, 24474, + 30496, 10800, 1287, 13728, 18664, 9864, 29482, 6386, 4102, 3895, + 19276, 30018, 31537, 17055, 29330, 23091, 24148, 31895, 20170, 2667, + 21834, 32283, 8015, 233, 997, 23873, 23853, 24729, 5942, 4463, + 26246, 550, 27656, 4044, 31029, 5136, 32430, 3060, 16560, 254, + 2047, 12528, 31539, 4181, 27550, 26213, 31259, 29961, 23423, 30219, + 400, 16052, 21670, 9569, 19302, 24805, 22834, 6048, 12613, 17594, + 2971, 3053, 16904, 6321, 3896, 19410, 18020, 21179, 20996, 24047, + 30922, 6290, 9121, 20717, 29838, 589, 15773, 12980, 8264, 27442, + 8437, 2014, 6011, 17611, 27863, 22379, 22388, 8252, 4780, 7862, + 13173, 25043, 2902, 16807, 21523, 21217, 29110, 19243, 17885, 1480, + 9842, 4577, 23358, 16470, 24821, 5399, 26021, 24657, 20919, 9716, + 2530, 28243, 14782, 12534, 13268, 21208, 7013, 15264, 27969, 24982, + 26716, 27244, 7173, 12045, 20720, 8967, 27503, 25694, 6284, 7869, + 25832, 1376, 4992, 306, 31701, 23793, 12802, 18260, 4287, 906, + 15474, 8845, 12209, 13335, 9443, 5181, 3554, 25349, 4566, 23911, + 2708, 19356, 1608, 5803, 14140, 26511, 19184, 1140, 10979, 1233, + 31938, 31602, 9420, 28075, 25783, 26955, 16983, 22284, 31952, 21901, + 19493, 2336, 18009, 28392, 24189, 30243, 21867, 30874, 28911, 13176, + 13748, 11306, 311, 2293, 3194, 25433, 25956, 2690, 7134, 7766, + 2361, 28642, 2910, 14897, 6867, 7888, 6558, 27538, 27276, 6990, + 7016, 20907, 544, 21050, 27633, 13675, 14094, 29218, 12894, 18676, + 15425, 18009, 8326, 31568, 30448, 18506, 3834, 13407, 6851, 23309, + 6723, 3348, 20791, 25912, 9578, 22497, 28739, 15608, 26046, 3440, + 1084, 6752, 23505, 29076, 12534, 1024, 24004, 1151, 11820, 27066, + 5828, 5173, 25377, 25041, 8149, 577, 25992, 15427, 5582, 4518, + 24481, 24665, 20645, 20760, 8614, 10364, 8173, 30060, 25189, 12362, + 12445, 14414, 29486, 18665, 19, 3890, 27375, 1947, 89, 20109, + 3230, 8851, 20902, 3396, 27863, 4104, 13608, 23652, 6855, 7047, + 7424, 10005, 20552, 25997, 32143, 20169, 27981, 341, 17392, 22457, + 17025, 9455, 5573, 14738, 28459, 8082, 15927, 10347, 22101, 2611, + 27704, 27131, 28461, 26198, 9739, 2825, 29867, 7973, 5639, 2339, + 14452, 8383, 5272, 17555, 7423, 31186, 4998, 20111, 31478, 15212, + 20090, 26544, 30175, 2830, 25790, 17062, 1019, 6915, 16452, 24361, + 11630, 31983, 28138, 4402, 18081, 683, 19196, 2156, 10725, 8528, + 11258, 27621, 13401, 2706, 24732, 24074, 2573, 28752, 14612, 31954, + 25959, 9832, 28819, 12857, 30150, 5840, 8462, 26864, 4454, 15331, + 9282, 31059, 16004, 5368, 1526, 25243, 22466, 18745, 27520, 9301, + 20428, 27827, 5258, 19475, 22588, 7081, 22806, 2913, 28603, 24622, + 18624, 27130, 7017, 5619, 20291, 26684, 20352, 16477, 575, 9690, + 19252, 1754, 31802, 24785, 27613, 19318, 26852, 24733, 15191, 4009, + 9887, 8620, 15785, 7027, 8510, 21779, 16870, 6827, 18957, 2176, + 14929, 9365, 20754, 2671, 24516, 25010, 915, 24225, 9299, 17372, + 29657, 22218, 14636, 1170, 20019, 9099, 11201, 23952, 32573, 29087, + 24106, 12194, 3937, 26457, 24260, 3003, 22431, 5710, 25375, 21057, + 29578, 13647, 27159, 23724, 5267, 13978, 18752, 25935, 6794, 3664, + 10632, 8565, 5474, 15658, 14103, 16816, 5173, 29544, 2170, 19566, + 25991, 24587, 257, 1741, 11471, 68, 12901, 24497, 28980, 812, + 430, 4371, 2452, 5557, 4948, 27548, 2506, 10977, 17061, 114, + 5425, 5170, 10342, 7506, 14948, 32353, 29885, 20191, 15432, 9811, + 8271, 23865, 27725, 23112, 14501, 30418, 31216, 6998, 19412, 32125, + 23429, 29462, 14173, 10696, 13363, 20893, 24146, 30860, 23360, 1965, + 2746, 5826, 26766, 12589, 32311, 16280, 27238, 9175, 30182, 16581, + 10357, 13873, 9607, 6425, 6164, 29374, 28126, 25944, 26061, 2933, + 21080, 3912, 14362, 8562, 29170, 29449, 3951, 13548, 1879, 28325, + 26491, 20839, 13384, 4072, 32172, 18986, 7233, 21618, 24817, 6900, + 4698, 12298, 11971, 16619, 24171, 19765, 8550, 8149, 23923, 15367, + 21356, 18314, 315, 826, 23676, 11689, 29494, 18301, 11089, 12171, + 24236, 32014, 16645, 28789, 9751, 22411, 12688, 26642, 1998, 27654, + 3468, 13600, 30176, 14679, 6459, 2153, 9539, 2742, 29898, 15349, + 6190, 26761, 6615, 27262, 10179, 2938, 21042, 6948, 20721, 1485, + 3040, 21858, 18585, 5872, 11878, 12609, 6895, 23892, 21828, 14283, + 23076, 23164, 5055, 10179, 8258, 32084, 9699, 29331, 26711, 9923, + 13001, 1414, 725, 23683, 1046, 13111, 10553, 7729, 22212, 20810, + 29931, 4256, 10533, 24153, 15717, 29555, 10788, 17457, 8600, 5355, + 11727, 2623, 16523, 14333, 19218, 3141, 25926, 8360, 1733, 2385, + 10734, 14981, 12266, 7920, 22065, 3488, 20783, 1183, 31973, 30086, + 23483, 7042, 7403, 20127, 28454, 9817, 18778, 3518, 25289, 15791, + 7385, 30199, 29753, 21588, 31821, 24753, 16142, 24590, 27829, 19451, + 25065, 31372, 81, 8996, 29877, 3739, 7946, 30085, 13356, 26013, + 11790, 4002, 1693, 10550, 23227, 10866, 15853, 11592, 3262, 9466, + 15069, 5666, 17276, 31777, 21898, 12020, 25265, 29887, 12090, 1158, + 12886, 11255, 20821, 6186, 5577, 455, 15794, 16112, 15912, 4573, + 21524, 280, 23549, 11948, 12533, 3533, 5153, 4800, 27010, 5904, + 15322, 15438, 18662, 10738, 8263, 10000, 29387, 22278, 23806, 22607, + 3870, 16150, 1523, 28994, 25363, 22549, 10396, 15254, 31594, 32341, + 26706, 25678, 19440, 9722, 14843, 22733, 26696, 16559, 742, 31281, + 14827, 11159, 7926, 6883, 22382, 14726, 4611, 31340, 24836, 11634, + 26749, 13195, 1603, 14628, 2898, 20612, 6443, 21555, 2834, 31162, + 21092, 32563, 9810, 29312, 10951, 18218, 3918, 22573, 28192, 7491, + 13522, 22382, 27442, 22215, 4698, 19425, 25852, 26952, 19320, 32223, + 10555, 14112, 5750, 29337, 28720, 26, 14908, 614, 27043, 19054, + 9077, 10587, 6952, 22515, 21742, 17949, 29601, 31632, 14142, 15091, + 14340, 25331, 2988, 9503, 11615, 10374, 1613, 7791, 9241, 16799, + 21357, 15631, 18554, 7416, 3120, 17825, 23661, 1374, 24185, 17295, + 16005, 7341, 27659, 31648, 25151, 763, 9455, 27850, 7559, 32276, + 8469, 21320, 28541, 13607, 15678, 25068, 9336, 21694, 32536, 9653, + 31563, 12019, 1195, 18340, 12232, 11233, 757, 8396, 3715, 7770, + 6097, 21673, 30169, 20696, 29470, 26034, 26672, 24245, 6940, 30769, + 4991, 11678, 20505, 14163, 29202, 6981, 21326, 4219, 26759, 13142, + 5380, 14556, 1309, 429, 3579, 12615, 17454, 21702, 18443, 15425, + 32249, 1140, 23052, 31321, 24997, 4310, 23284, 8377, 8579, 11655, + 4159, 32250, 13460, 3350, 23444, 16701, 20811, 182, 11173, 14199, + 1427, 16438, 29783, 29452, 4116, 28211, 29219, 4512, 24242, 11404, + 15547, 5161, 22103, 21782, 27889, 32073, 18419, 10897, 22997, 10716, + 14017, 5595, 12574, 6093, 21451, 17252, 28364, 3885, 21168, 13538, + 30731, 24966, 31653, 6982, 23897, 13164, 7866, 3310, 2027, 1110, + 19551, 27676, 22093, 26796, 30502, 4519, 27722, 10785, 25290, 4080, + 14859, 16963, 1137, 8728, 32387, 25424, 14382, 7024, 18698, 844, + 28618, 27441, 15943, 7504, 16068, 2158, 9300, 7461, 26518, 23110, + 31261, 10514, 20026, 28766, 7689, 15857, 29677, 8079, 18397, 20872, + 24627, 22235, 23005, 28439, 6939, 16550, 19764, 558, 7364, 1058, + 15765, 17345, 22048, 15116, 22563, 32126, 14072, 28566, 12686, 5982, + 21808, 13988, 18303, 9083, 6477, 14906, 12599, 23585, 5172, 18485, + 17267, 18471, 25140, 14650, 11154, 31104, 16199, 17389, 17913, 6669, + 12437, 4195, 13239, 15029, 26426, 8153, 29541, 6328, 16457, 23718, + 336, 19776, 1553, 26222, 17115, 6970, 22175, 28313, 16670, 20323, + 21380, 14595, 30419, 26278, 32472, 5365, 13683, 28325, 27006, 25173, + 26012, 21654, 5802, 30489, 9910, 5712, 14497, 30977, 4566, 2469, + 10944, 32646, 9186, 11763, 19648, 4622, 4855, 15350, 9016, 22113, + 4871, 26592, 24931, 864, 19911, 2476, 16420, 27637, 4552, 8111, + 17991, 13894, 31971, 24432, 26122, 4910, 9691, 18617, 5084, 2541, + 2366, 28398, 14948, 11294, 15756, 27806, 12229, 17898, 12955, 13656, + 4129, 24188, 58, 14784, 8540, 7348, 2069, 4577, 9250, 25632, + 28377, 8645, 13867, 15912, 12440, 27715, 10685, 25480, 21614, 2073, + 30323, 31236, 869, 27451, 10046, 23126, 1686, 106, 27049, 29292, + 28817, 15111, 8801, 6096, 30207, 7466, 10996, 24168, 14726, 25583, + 7385, 18271, 3821, 19984, 19889, 23651, 31105, 12473, 24780, 2833, + 27063, 20119, 4591, 21677, 32274, 9523, 7508, 9751, 19271, 9342, + 26867, 27642, 28576, 1196, 21248, 14151, 30110, 11846, 18869, 1506, + 18914, 26747, 4177, 923, 18509, 16824, 21857, 22367, 9319, 26232, + 32318, 20655, 12541, 23132, 19415, 2386, 19215, 18302, 15536, 20295, + 27625, 22531, 20523, 27390, 9871, 19951, 3847, 5576, 5214, 19773, + 32038, 13451, 29937, 3120, 22675, 10938, 1346, 24036, 18883, 22916, + 21009, 19240, 26907, 8012, 2442, 11664, 15795, 27482, 4130, 197, + 6490, 14800, 23746, 24096, 30170, 3039, 24615, 22135, 14205, 12346, + 31625, 7735, 30927, 28619, 19066, 16307, 17318, 29350, 17426, 18364, + 23392, 28987, 30537, 8306, 31852, 1487, 26276, 3673, 13580, 12845, + 18196, 944, 15192, 28056, 9755, 15870, 25735, 20229, 15152, 25069, + 8554, 10119, 26725, 2395, 9273, 8459, 11847, 30639, 5010, 13637, + 5893, 26729, 28351, 4330, 23765, 11262, 1848, 14112, 20372, 1762, + 13160, 31027, 15788, 1766, 7860, 5769, 25659, 17092, 23226, 6360, + 10866, 14328, 5590, 16534, 11323, 15058, 6891, 5553, 22558, 11466, + 32352, 28801, 9317, 27923, 7272, 3835, 8062, 14195, 19591, 2599, + 24482, 25937, 28242, 16340, 5556, 17680, 13174, 1162, 18284, 31643, + 19195, 12736, 29876, 9281, 20938, 25679, 11346, 8900, 11373, 32551, + 21448, 23468, 11799, 17984, 9848, 18145, 12516, 9684, 12300, 4217, + 865, 31180, 17941, 15311, 20303, 27000, 17650, 31957, 23972, 11138, + 5108, 15275, 23507, 17957, 13504, 30320, 1613, 8090, 25076, 30040, + 23624, 29395, 269, 1278, 16575, 31852, 27528, 11004, 11450, 29663, + 21319, 2234, 22223, 9463, 17975, 24707, 29673, 15751, 5424, 28894, + 30024, 9051, 8732, 6683, 26569, 26223, 22876, 556, 22632, 9055, + 23143, 10934, 25091, 27167, 5044, 11624, 18529, 31980, 21979, 4748, + 17522, 31618, 8695, 30171, 26072, 20112, 14158, 28054, 11687, 26473, + 9268, 26578, 18025, 10236, 24108, 29204, 32102, 1798, 9553, 7965, + 24866, 17542, 14068, 32519, 10534, 17010, 4999, 23953, 19203, 21928, + 23481, 15032, 24725, 8999, 2275, 12679, 17285, 23935, 1187, 16687, + 529, 30342, 25542, 11354, 4368, 6477, 18540, 25277, 24368, 82, + 12134, 10476, 11651, 31735, 11491, 29299, 20794, 15667, 23628, 8738, + 17072, 14472, 1900, 9384, 7594, 13621, 18644, 6500, 30429, 23724, + 11646, 32119, 10928, 24134, 6576, 3358, 2372, 31542, 29987, 20287, + 945, 3772, 25189, 32577, 16019, 26267, 1404, 18721, 11718, 32716, + 7628, 3010, 7533, 31604, 30523, 5509, 19513, 24596, 30354, 3866, + 31464, 31487, 17702, 2198, 5387, 30226, 24505, 23749, 22285, 30111, + 3227, 24257, 5489, 21162, 32020, 1475, 2395, 17752, 7687, 2874, + 18002, 5028, 23503, 12058, 15684, 6228, 23917, 5751, 28874, 13867, + 31461, 30802, 16046, 2913, 29413, 3251, 25259, 31056, 9128, 29176, + 5747, 21616, 19226, 31852, 29431, 20613, 4669, 9243, 30434, 32023, + 26571, 1253, 22118, 26147, 28189, 9426, 30780, 11536, 75, 29861, + 17382, 11748, 4757, 16052, 9528, 1208, 29185, 30710, 26902, 1898, + 10054, 10078, 22399, 1937, 233, 22375, 11979, 28854, 2214, 17905, + 25648, 5825, 26641, 17995, 19851, 18609, 125, 2449, 6237, 31632, + 14877, 16696, 12820, 8614, 20073, 20455, 1421, 494, 4547, 31880, + 12758, 9158, 91, 23456, 7736, 8938, 4688, 25307, 3957, 32675, + 6936, 23276, 8782, 16188, 14369, 9107, 20296, 28854, 4792, 32255, + 23472, 27069, 31019, 20823, 23517, 20442, 4906, 7572, 10933, 16873, + 13683, 4241, 31347, 13774, 6319, 29029, 25255, 24164, 30614, 22000, + 3965, 11702, 22618, 22196, 30275, 18290, 1347, 13542, 13094, 30618, + 30870, 9925, 29756, 381, 16316, 15915, 5576, 23270, 6560, 7522, + 21433, 10131, 21593, 28216, 13427, 2280, 11785, 20310, 4306, 5605, + 15621, 11213, 23916, 3926, 9014, 12530, 6423, 9289, 4287, 22124, + 1196, 23237, 23007, 11403, 2058, 28857, 9241, 25820, 26717, 18739, + 28307, 18682, 13216, 14802, 31165, 5030, 1376, 19582, 21042, 24602, + 2243, 18774, 739, 9115, 23498, 7402, 12914, 25934, 14291, 28331, + 12252, 10316, 866, 31648, 12891, 15352, 12576, 2394, 20619, 26257, + 4132, 1321, 29387, 7683, 29042, 9104, 17638, 17155, 18113, 5111, + 27379, 4698, 7810, 7328, 32121, 16513, 8477, 13020, 4311, 29551, + 29566, 25471, 19427, 3759, 8912, 8081, 20946, 1461, 32136, 16710, + 856, 10354, 32166, 23856, 23311, 7718, 10295, 13236, 26909, 630, + 1567, 22203, 12818, 22718, 6677, 22441, 29967, 18856, 30703, 4499, + 7469, 24318, 24864, 25517, 25116, 12732, 2687, 28673, 23563, 15978, + 28490, 12903, 3784, 21416, 21866, 945, 12329, 20235, 26360, 23846, + 11690, 7207, 1281, 28722, 13655, 10733, 14214, 26203, 17901, 19000, + 24263, 23927, 2620, 12194, 2498, 29724, 975, 32156, 5622, 31321, + 18639, 7870, 31852, 6819, 22821, 21851, 28664, 3858, 20191, 14937, + 12477, 14030, 28117, 7743, 14575, 8132, 22156, 144, 9949, 22685, + 24666, 4440, 2094, 7920, 24267, 28932, 27242, 15798, 22084, 8822, + 21693, 12287, 25574, 25433, 3832, 23524, 23733, 24242, 23001, 31165, + 26618, 28088, 31884, 31636, 6374, 23270, 19313, 2265, 19319, 23374, + 3104, 25174, 18592, 12848, 23211, 16998, 30116, 13003, 29731, 3426, + 4803, 5235, 2653, 26205, 19736, 19684, 1269, 31761, 10178, 25814, + 21396, 8728, 66, 31442, 13679, 2656, 12836, 26691, 6672, 13017, + 7650, 15568, 32601, 23408, 5893, 1001, 28870, 6209, 32141, 2208, + 3058, 21512, 1683, 3003, 11869, 22233, 30765, 8199, 12396, 9490, + 23698, 8431, 20913, 19085, 28798, 30540, 14957, 26138, 20696, 8319, + 13907, 2348, 11155, 11198, 3215, 16338, 30674, 8619, 6407, 21737, + 31055, 2603, 2785, 9873, 4861, 1141, 27717, 7901, 11500, 9577, + 22372, 9423, 12653, 4132, 31929, 6531, 25976, 31953, 24206, 13738, + 8266, 8641, 26051, 23354, 9810, 12991, 13259, 12132, 11086, 27293, + 22007, 4295, 16306, 21267, 15863, 15843, 11593, 16074, 9118, 16336, + 1822, 10675, 13285, 27951, 28829, 29597, 12595, 6498, 24566, 10503, + 26843, 17400, 24514, 20018, 8678, 23659, 5097, 24699, 26900, 28174, + 1475, 488, 24093, 18497, 31613, 769, 26172, 30439, 24255, 16097, + 26606, 7962, 30347, 32140, 29688, 5149, 30825, 20611, 32679, 14481, + 13505, 7574, 5211, 32748, 25224, 19450, 6215, 9256, 19998, 18331, + 5234, 25528, 28535, 20147, 7169, 23488, 11130, 15772, 14521, 21125, + 17574, 19683, 192, 1571, 16123, 30109, 14755, 21374, 15128, 28670, + 11421, 18810, 3233, 39, 4119, 31855, 4067, 1344, 10859, 11735, + 699, 32455, 4589, 7242, 25779, 31783, 11820, 4013, 8815, 10893, + 24181, 30118, 4168, 29943, 27033, 8786, 30774, 27278, 6896, 2363, + 16115, 24373, 12446, 644, 25693, 31330, 32533, 28775, 25373, 10284, + 28124, 22754, 13190, 8358, 12686, 18348, 30143, 31084, 26405, 3182, + 1829, 30075, 29816, 10544, 1771, 1862, 20995, 1297, 3587, 14598, + 8679, 14140, 7746, 28497, 14385, 32279, 3263, 14830, 30815, 14014, + 27516, 9960, 11297, 26799, 13896, 19425, 14172, 14791, 18147, 31145, + 19828, 24173, 21604, 19983, 3019, 23053, 472, 6135, 7557, 9160, + 16428, 16813, 19599, 873, 1869, 13689, 9183, 15003, 10893, 28788, + 1789, 20058, 19529, 14233, 25282, 1192, 13512, 8991, 23391, 27265, + 4199, 29703, 7074, 30224, 22771, 26609, 10613, 6435, 28247, 32563, + 25512, 22493, 8823, 26924, 3312, 14250, 15451, 2774, 2095, 6776, + 13968, 21403, 10741, 23077, 15563, 21610, 17223, 14387, 11472, 19721, + 18848, 30147, 7826, 9133, 16438, 10177, 14236, 31432, 18046, 7004, + 19887, 22818, 28658, 24249, 7341, 6187, 18706, 16872, 5045, 3216, + 11689, 25623, 32144, 7436, 21585, 8453, 2124, 15233, 13113, 29370, + 28710, 29767, 8742, 7030, 20910, 1987, 23992, 11239, 26405, 16956, + 20463, 14750, 13610, 1515, 16877, 22980, 27286, 15185, 23294, 13321, + 30628, 7118, 4309, 16862, 8277, 2670, 27929, 3556, 3602, 28422, + 19488, 1956, 9363, 18304, 30970, 16256, 7681, 25459, 19121, 7913, + 12887, 32247, 9935, 28883, 19982, 10679, 3361, 20070, 7161, 17223, + 4283, 17399, 26222, 23992, 25148, 16950, 5030, 23914, 20307, 12201, + 16742, 13602, 23426, 11865, 17375, 31068, 22050, 27083, 3028, 15843, + 4520, 305, 14330, 29086, 8230, 25058, 21924, 11437, 14613, 9736, + 5279, 9397, 11345, 19352, 29275, 15223, 1637, 22408, 8268, 23464, + 18953, 14652, 10640, 6140, 25321, 577, 7122, 16517, 13736, 17981, + 14190, 25147, 28965, 14118, 27350, 25820, 1277, 20915, 7476, 15094, + 24913, 11792, 3153, 22593, 25545, 18646, 16821, 25139, 5592, 22586, + 25360, 20175, 8258, 389, 25048, 26046, 6108, 708, 20020, 22432, + 1939, 9408, 20761, 24484, 23472, 2352, 4530, 9046, 4699, 10542, + 2566, 17593, 21853, 6120, 3797, 23190, 24874, 23960, 3778, 17171, + 23838, 32438, 19640, 1159, 2311, 875, 6534, 12429, 25572, 4938, + 20684, 32088, 12268, 31035, 7451, 2374, 30674, 9143, 18264, 31927, + 31048, 30011, 5473, 32287, 6338, 30966, 31731, 11354, 17020, 20421, + 10188, 4928, 23742, 26978, 8716, 20090, 12127, 24025, 5089, 20116, + 25736, 31547, 28003, 29778, 1722, 23445, 29629, 30280, 8210, 5923, + 31374, 17573, 24268, 26306, 19599, 24400, 25351, 19108, 1909, 21046, + 6599, 2375, 28441, 19426, 18752, 6824, 31702, 590, 12882, 21428, + 10290, 23032, 27472, 26111, 16833, 825, 22533, 1976, 16711, 466, + 25337, 22493, 28189, 21192, 23536, 21163, 20694, 9331, 8116, 27225, + 14625, 21713, 27924, 6149, 23281, 10784, 2861, 30959, 18140, 31147, + 9448, 19429, 16833, 12516, 32196, 25342, 16302, 29875, 25558, 23947, + 1537, 19214, 782, 15947, 14541, 714, 32187, 12766, 22399, 19888, + 28241, 6524, 27841, 16596, 13930, 11751, 30110, 12066, 16140, 20126, + 17383, 4666, 3626, 20820, 24534, 30078, 21869, 26016, 11506, 5992, + 24116, 24822, 22066, 13602, 30425, 1750, 17765, 13392, 17718, 8515, + 10705, 7984, 8807, 32119, 21019, 9958, 25554, 8405, 23911, 8209, + 13669, 18975, 1983, 11682, 26911, 4841, 5561, 3566, 24530, 17742, + 9044, 15874, 28455, 14817, 23076, 26422, 26850, 20966, 21167, 19205, + 10087, 25027, 9494, 8475, 17277, 8113, 27896, 7088, 22355, 27319, + 17555, 1557, 29377, 32217, 31532, 12684, 9423, 25485, 2737, 9309, + 15995, 15726, 4254, 16166, 12983, 13364, 19997, 19571, 14258, 9188, + 29553, 11918, 3416, 22596, 14996, 8089, 3356, 28050, 5407, 14297, + 26357, 10613, 29035, 11861, 12198, 24628, 23286, 1952, 9283, 14886, + 31240, 9971, 18161, 28010, 13974, 30078, 14573, 16343, 614, 5120, + 30160, 25910, 3700, 19094, 21521, 3503, 28565, 10960, 28363, 14466, + 30298, 6154, 23744, 7928, 15927, 10746, 22687, 19352, 14308, 7557, + 16002, 12701, 13060, 18391, 699, 22696, 11013, 16306, 1488, 28851, + 23367, 12550, 27076, 25158, 24192, 3779, 19608, 12013, 23903, 5867, + 3090, 13801, 4207, 23787, 30397, 12582, 6463, 20011, 3633, 32365, + 30400, 25828, 16020, 2267, 6382, 32050, 12129, 26258, 22450, 106, + 4456, 32430, 20743, 23213, 3607, 19323, 29277, 28349, 15351, 3301, + 12602, 31760, 15481, 32559, 14119, 11043, 26355, 27112, 1226, 13329, + 9446, 6939, 20560, 21348, 25474, 8903, 8974, 299, 15980, 25230, + 31335, 7757, 18366, 17841, 28656, 3100, 4432, 13628, 11474, 25133, + 23350, 7294, 11376, 13928, 2953, 4540, 4682, 11099, 21355, 30503, + 5921, 14547, 9541, 10250, 20829, 25262, 24659, 1608, 23723, 20971, + 21859, 31750, 9450, 1027, 2265, 17829, 22600, 1148, 22007, 6131, + 17576, 25787, 22363, 30370, 9288, 26138, 14537, 20217, 7584, 2618, + 22059, 10953, 31373, 32155, 19414, 4003, 23214, 4201, 23567, 24136, + 25870, 12697, 19918, 18017, 15620, 8245, 10434, 8816, 15960, 18164, + 29332, 20487, 17575, 30222, 20848, 22584, 521, 28494, 14274, 31820, + 10480, 30825, 32061, 31415, 27988, 26510, 2338, 8268, 8461, 24126, + 29119, 31150, 28544, 9822, 13622, 14599, 31869, 25995, 28543, 9212, + 27240, 6829, 12720, 7648, 2967, 6607, 24188, 8743, 17148, 31278, + 12282, 17182, 22532, 10895, 19112, 218, 16830, 8150, 432, 3182, + 11584, 20949, 12394, 19298, 17223, 31330, 19745, 12128, 28957, 19116, + 6426, 13871, 14528, 2767, 4562, 9702, 19520, 22437, 1928, 22873, + 30825, 29407, 31995, 12504, 8866, 22869, 13429, 31513, 24085, 5019, + 3592, 27250, 23568, 29909, 6936, 27211, 15275, 15308, 2842, 26836, + 31916, 19009, 21914, 26867, 6567, 26241, 17795, 28801, 3434, 15161, + 18843, 9909, 18328, 2444, 11853, 14048, 21651, 16739, 9387, 19291, + 15519, 29700, 15027, 13502, 24133, 21735, 10449, 4435, 18422, 15556, + 6310, 2251, 21745, 25329, 18696, 28719, 4455, 16619, 13130, 27161, + 10716, 21555, 5565, 22307, 27088, 12891, 1910, 10912, 3487, 9056, + 31058, 30602, 27558, 13882, 18488, 18876, 14503, 946, 17566, 8033, + 27502, 4980, 21327, 5805, 949, 5989, 2923, 9163, 14406, 28982, + 30127, 25366, 20575, 13882, 20726, 23435, 11354, 9588, 0, 17790, + 16781, 17397, 1139, 10637, 3440, 4965, 11039, 27603, 17405, 5855, + 19846, 5982, 15709, 6658, 19612, 32226, 26304, 28228, 10443, 32686, + 14989, 4746, 23354, 4064, 955, 30966, 21583, 18645, 28798, 24320, + 17267, 9444, 14375, 27593, 3093, 12108, 22597, 2219, 3057, 16746, + 11537, 10261, 5778, 8240, 26889, 28310, 18965, 3538, 12250, 29239, + 8497, 9154, 22659, 1038, 26228, 2271, 6840, 14352, 27855, 22506, + 11140, 7975, 12629, 19001, 3379, 26501, 19008, 31407, 5743, 22327, + 18229, 21739, 29865, 21757, 26313, 1216, 11707, 29529, 17731, 31205, + 25803, 12774, 26787, 21486, 26226, 26859, 19940, 19067, 16413, 4912, + 23423, 26032, 26292, 27152, 23856, 6885, 12410, 2000, 27900, 8606, + 9781, 1071, 28019, 5309, 28558, 16190, 31027, 13302, 24250, 20082, + 27565, 22916, 24410, 16376, 26412, 30746, 19616, 10312, 31763, 13597, + 29364, 11773, 21861, 29456, 16214, 2020, 3723, 24656, 26052, 8531, + 5714, 24065, 31198, 22716, 17204, 21320, 18056, 3201, 31960, 16570, + 25424, 27146, 7900, 10261, 19793, 13078, 30929, 7214, 1878, 27982, + 3975, 3371, 1347, 12320, 907, 9458, 26289, 1226, 16623, 18402, + 17625, 3253, 23625, 3520, 6067, 25093, 16638, 18164, 30303, 26222, + 5313, 25077, 14019, 29769, 29782, 17666, 12406, 9703, 28060, 28691, + 9948, 10021, 23242, 31503, 7827, 17728, 761, 17471, 8718, 12523, + 9187, 25347, 14720, 9887, 18666, 12995, 6652, 28979, 4956, 2601, + 20442, 31404, 22517, 29325, 26716, 24406, 13733, 29617, 24210, 14100, + 2452, 18351, 20346, 2292, 25700, 2863, 14700, 7430, 25782, 15837, + 19125, 8303, 6895, 10011, 899, 20409, 23100, 29426, 12033, 7021, + 6867, 6991, 30658, 31460, 20860, 29036, 7011, 24922, 24656, 23436, + 364, 2372, 1217, 6891, 14712, 2531, 26331, 11765, 9366, 27418, + 11722, 8886, 19950, 11133, 15771, 28050, 13992, 27818, 583, 6198, + 6409, 14812, 27629, 23758, 26335, 7357, 95, 376, 1824, 29820, + 9696, 2041, 5838, 17184, 2860, 1456, 12573, 25884, 20665, 14280, + 728, 26403, 22333, 10161, 11521, 28221, 22384, 29648, 15032, 26139, + 10003, 4213, 11869, 27047, 20273, 5390, 27129, 6624, 22437, 27035, + 30426, 22245, 27612, 26722, 28798, 3919, 15580, 18404, 5811, 6596, + 17541, 24847, 19195, 19658, 19991, 9653, 32053, 18648, 26613, 23357, + 19322, 10419, 8873, 28057, 22854, 3064, 16679, 28280, 8664, 12276, + 15958, 5308, 18907, 2748, 19404, 20526, 13381, 7290, 11737, 17776, + 24192, 27398, 6344, 17972, 18376, 23478, 29537, 6572, 26580, 24438, + 8883, 10561, 30904, 9499, 17168, 25992, 6154, 17908, 29272, 30578, + 835, 8354, 3963, 4547, 29545, 22058, 4921, 30405, 23531, 7497, + 26156, 1597, 4301, 24584, 22537, 26526, 6148, 22415, 7203, 9341, + 10788, 6718, 28464, 18245, 9784, 20474, 19024, 21036, 31985, 197, + 18251, 8805, 22276, 12690, 6682, 5015, 31437, 17239, 17322, 30607, + 13624, 18531, 25052, 13874, 18913, 17394, 16592, 10677, 11898, 2047, + 4451, 2832, 5486, 9561, 26870, 687, 3011, 8697, 28274, 12651, + 11845, 27027, 22664, 17850, 14817, 11371, 8510, 28962, 31567, 8298, + 4775, 20980, 25076, 22641, 4337, 9526, 14854, 9709, 17830, 21729, + 20977, 14508, 4139, 13901, 3833, 28911, 24461, 3305, 13601, 6080, + 22908, 9519, 26188, 29061, 29336, 7127, 7331, 24278, 23532, 9547, + 9270, 32652, 27064, 26972, 22502, 3098, 22388, 18227, 9602, 22817, + 3345, 5514, 7472, 14654, 18818, 13084, 4111, 12583, 22139, 31577, + 538, 12790, 10617, 29535, 29520, 19908, 5634, 17190, 17426, 6778, + 29998, 20218, 14540, 14052, 26214, 1327, 24791, 9735, 3436, 13328, + 25758, 29877, 29083, 31768, 32512, 23902, 25201, 12901, 9718, 24105, + 1354, 18281, 9846, 30019, 28782, 1507, 14231, 30583, 30542, 15885, + 18677, 13568, 3083, 12567, 4183, 24731, 23096, 20693, 6799, 27872, + 32481, 10165, 25506, 21457, 460, 31987, 14889, 31924, 29694, 2320, + 29175, 13342, 22401, 20096, 10748, 27217, 13413, 27299, 10601, 11207, + 32517, 29430, 6165, 2063, 13196, 12939, 5664, 23256, 19127, 23478, + 30422, 11084, 27612, 522, 8918, 5535, 43, 28803, 25971, 29415, + 2868, 12293, 10586, 13102, 27817, 20997, 26741, 7621, 28517, 21382, + 21264, 27612, 18862, 11336, 29243, 29008, 29367, 24124, 18799, 18372, + 29033, 6458, 15220, 12275, 2147, 18166, 13953, 7492, 25408, 4913, + 27558, 8096, 10520, 17452, 5479, 8064, 6217, 30562, 4522, 8266, + 3959, 5750, 21203, 31965, 10193, 6251, 27693, 21098, 17558, 25363, + 15756, 3463, 15320, 24778, 560, 19838, 31873, 7780, 18157, 4587, + 21907, 1379, 2953, 26246, 17076, 1645, 4966, 317, 25065, 18430, + 10592, 1670, 30370, 11611, 23289, 26611, 7671, 18887, 28340, 4358, + 6193, 5112, 6590, 22088, 19340, 28108, 14547, 23189, 25659, 21561, + 4192, 24250, 13741, 19330, 22501, 12098, 16655, 23473, 5278, 9356, + 1298, 12609, 1890, 26913, 31470, 13851, 12747, 31301, 25767, 14020, + 13975, 5836, 20622, 26580, 6478, 4143, 30220, 24490, 8485, 12767, + 20379, 22622, 25379, 13017, 5653, 19959, 14210, 25979, 305, 19678, + 7768, 6550, 29002, 1171, 22391, 25796, 26211, 29343, 12689, 17660, + 26729, 14794, 21664, 20835, 17591, 31117, 2948, 28005, 17632, 14794, + 20718, 12805, 17064, 16059, 9086, 369, 15699, 22825, 25896, 30938, + 21539, 1758, 18908, 13442, 18972, 30556, 6502, 6679, 30474, 18639, + 18579, 7487, 18885, 4417, 11669, 12916, 16308, 2061, 21416, 25618, + 30096, 25494, 12790, 15790, 13674, 16184, 18872, 426, 9712, 7551, + 28575, 23814, 32492, 23191, 17522, 3449, 26381, 8964, 8440, 11713, + 24298, 17043, 31000, 7223, 23876, 24465, 31295, 12198, 139, 30266, + 26882, 11869, 31140, 10356, 517, 15235, 5098, 13758, 25115, 11947, + 32371, 12131, 14766, 2472, 14996, 15603, 20322, 163, 21415, 1267, + 5621, 18539, 17419, 3540, 22890, 7947, 11117, 11042, 18278, 17058, + 14918, 19198, 22234, 19055, 30373, 14286, 21218, 5676, 15919, 8755, + 3309, 18333, 10596, 1852, 31263, 31336, 13348, 18137, 16371, 11815, + 20337, 32402, 29733, 5820, 16502, 31864, 5071, 18148, 28872, 24156, + 30162, 26896, 5101, 12608, 15479, 31344, 17947, 9483, 2646, 15440, + 2113, 10369, 32375, 20391, 15692, 369, 15288, 12013, 3235, 13616, + 32622, 31278, 19955, 21831, 2895, 18781, 25138, 1129, 26256, 30954, + 7646, 18344, 14922, 9366, 10374, 30696, 19746, 17543, 30092, 26515, + 25129, 8343, 14889, 4238, 32513, 3570, 1201, 24172, 23874, 10976, + 22809, 26850, 2843, 3295, 30390, 12923, 32573, 5365, 11079, 16269, + 9204, 24132, 22326, 6508, 7575, 31455, 1819, 4573, 20515, 29404, + 18050, 23246, 26659, 14763, 4078, 17988, 27347, 3494, 32286, 17044, + 4484, 30184, 8846, 26326, 29762, 21091, 9537, 9111, 18357, 12845, + 4542, 27598, 12869, 25627, 30041, 27153, 31873, 30097, 12979, 5641, + 8386, 5016, 11908, 15908, 32267, 561, 14744, 22921, 12852, 7658, + 23576, 13905, 24392, 7086, 26123, 22516, 24619, 31457, 26908, 6657, + 12416, 23981, 32670, 27411, 3017, 15254, 1229, 13312, 2000, 8789, + 8791, 16211, 11847, 30947, 22299, 13565, 22955, 30932, 23344, 32327, + 22894, 20966, 1321, 9739, 8532, 11226, 5271, 2340, 2078, 31362, + 1488, 27427, 22886, 22556, 21231, 17219, 16074, 23006, 17948, 21920, + 17484, 4818, 16654, 27811, 31737, 14564, 7629, 18113, 1225, 8250, + 3261, 3173, 30919, 195, 5080, 25130, 1649, 14867, 4923, 27470, + 14690, 2414, 29412, 20580, 13870, 26146, 23994, 7752, 28046, 30746, + 11725, 4440, 2749, 23729, 28128, 8417, 8481, 11716, 5157, 24280, + 23854, 32523, 23111, 32715, 15496, 17833, 22189, 7958, 4455, 24065, + 15102, 4954, 21824, 9164, 23520, 26411, 3746, 20584, 26118, 18953, + 20062, 4979, 25705, 3474, 2026, 6553, 31701, 757, 18695, 30122, + 32680, 19626, 5282, 8176, 17367, 30817, 25848, 32703, 20529, 30008, + 6398, 13133, 22760, 14896, 20736, 2500, 8269, 12514, 32392, 25494, + 9691, 14446, 11137, 27921, 18039, 21685, 15516, 26419, 12790, 24008, + 13043, 29375, 23008, 17311, 12525, 4496, 15731, 14863, 18017, 21038, + 14059, 24822, 18430, 22943, 11777, 15545, 13802, 16256, 21426, 4848, + 30121, 28756, 19265, 32607, 25409, 2587, 25727, 21801, 32155, 26407, + 19274, 8256, 12845, 19243, 20564, 23328, 3030, 26261, 12070, 11453, + 18697, 9245, 30052, 17081, 7470, 29854, 28498, 7131, 8517, 3365, + 15767, 22205, 14285, 19272, 30785, 22724, 26596, 13242, 778, 24917, + 29615, 16156, 30425, 23878, 11772, 26901, 17548, 3731, 21286, 13732, + 29941, 23635, 26937, 8373, 10786, 24298, 27479, 11251, 17890, 31871, + 17499, 32478, 22916, 10673, 23739, 19741, 17122, 8837, 16307, 30485, + 6815, 12195, 7072, 6330, 22444, 19395, 8618, 31397, 19473, 15714, + 2213, 6504, 24298, 30905, 14137, 14110, 10159, 24047, 22849, 11078, + 20153, 10126, 25714, 25809, 22274, 7476, 20541, 21113, 13613, 24203, + 1270, 9557, 22470, 23162, 17973, 8618, 1226, 18077, 7293, 29189, + 12394, 10995, 539, 12422, 12556, 21975, 22353, 6783, 29843, 19506, + 25012, 8766, 6880, 2542, 23251, 21286, 6552, 9902, 21017, 15365, + 19752, 12988, 26101, 21409, 6919, 24642, 6199, 2964, 2363, 19646, + 16830, 1370, 28447, 32432, 26960, 7593, 9059, 27541, 457, 16896, + 19284, 6681, 21368, 17539, 12126, 13449, 5318, 8710, 14543, 8071, + 30232, 28264, 31965, 26881, 9463, 23330, 30955, 9060, 2671, 3103, + 25608, 268, 18612, 11983, 14777, 17408, 15372, 1911, 25080, 21980, + 744, 26827, 6780, 7182, 20820, 18878, 1339, 23621, 5010, 28057, + 46, 17703, 9218, 21066, 10836, 28483, 6214, 22783, 14841, 27206, + 27042, 23796, 2104, 10865, 31913, 11401, 30143, 27210, 19478, 6296, + 3282, 21650, 15096, 3589, 1171, 2680, 21010, 28055, 2432, 23052, + 27777, 5638, 28477, 14582, 6047, 21589, 12180, 22376, 10792, 11873, + 23226, 24401, 22171, 15291, 23763, 13268, 15587, 179, 15545, 17656, + 16183, 32693, 14329, 19361, 7743, 29913, 24607, 447, 8312, 28597, + 7476, 10949, 18649, 16246, 14724, 93, 24759, 25903, 23205, 22361, + 22758, 112, 2996, 4333, 17953, 27163, 15586, 11292, 31915, 29043, + 23599, 2929, 3895, 28534, 27510, 17135, 13574, 9325, 14250, 586, + 23375, 5057, 5471, 28504, 26083, 21008, 18060, 1218, 25361, 31104, + 28047, 18687, 14023, 14419, 12657, 29059, 251, 11708, 30572, 17971, + 17225, 2289, 21930, 22813, 2526, 7810, 9040, 26214, 7060, 26601, + 18875, 28803, 30046, 8546, 15113, 27820, 5351, 31229, 12764, 5006, + 22134, 2545, 7770, 10817, 27157, 819, 25721, 22393, 9202, 20551, + 20345, 22784, 11269, 5463, 24242, 11620, 20723, 18306, 20367, 1517, + 563, 20086, 2267, 5329, 13679, 25665, 19259, 2987, 15149, 10084, + 19196, 24106, 18094, 4905, 5903, 7430, 11542, 21047, 29010, 32057, + 8142, 8036, 31762, 24427, 26151, 10442, 24781, 27566, 23104, 10525, + 21564, 5483, 11561, 11068, 7056, 7068, 13896, 11269, 30461, 22844, + 20988, 3694, 5493, 31228, 4993, 5585, 30134, 26424, 19988, 23176, + 22662, 297, 14679, 27834, 10301, 30812, 13558, 6516, 31109, 23395, + 28555, 1532, 2398, 11466, 29887, 5474, 23653, 5453, 26290, 19543, + 25429, 1607, 26270, 10050, 18638, 24686, 28855, 5777, 19541, 29374, + 12117, 5453, 20125, 26035, 2388, 3570, 29444, 26299, 28603, 381, + 31329, 10219, 11933, 9414, 15101, 2033, 17725, 26640, 23007, 30282, + 2421, 7391, 10573, 26549, 6745, 11786, 20623, 28391, 26135, 7696, + 8438, 19840, 18210, 16303, 15389, 4414, 31887, 23731, 28527, 4464, + 447, 16141, 26857, 24750, 2366, 20516, 10529, 1736, 7915, 17008, + 22152, 5884, 13657, 31044, 21996, 30316, 8822, 8939, 19521, 26319, + 20330, 14583, 28064, 10598, 8130, 23254, 15760, 20017, 21454, 13990, + 9349, 868, 27207, 29770, 32422, 30997, 17717, 22206, 2003, 11575, + 5648, 1959, 18663, 13259, 21798, 16687, 27194, 20309, 18492, 18959, + 3947, 28045, 23672, 30701, 8284, 23640, 22399, 11241, 9085, 19190, + 9178, 32115, 3406, 27517, 15518, 1887, 10527, 18566, 31820, 2875, + 32538, 8998, 21238, 24288, 23428, 27936, 10147, 22599, 23360, 13144, + 26869, 11166, 8597, 3211, 4361, 28571, 6961, 24638, 8351, 15377, + 29293, 6644, 21883, 5825, 6354, 7332, 8203, 8818, 2524, 2666, + 7241, 17476, 11007, 13437, 4579, 13121, 27880, 18273, 16281, 9781, + 4613, 1886, 30183, 5055, 15943, 12592, 17637, 14505, 13351, 21248, + 16747, 22645, 25319, 12691, 9592, 27704, 10778, 3522, 20437, 13228, + 30310, 24106, 26357, 23680, 7049, 20772, 8992, 13298, 3994, 11000, + 977, 5792, 27537, 21581, 29788, 4763, 21770, 11492, 9171, 28606, + 13638, 30342, 5163, 4670, 24947, 27707, 9371, 27521, 6189, 22973, + 6981, 27956, 30788, 18220, 29208, 12952, 21696, 3331, 27381, 21532, + 2594, 25675, 12048, 6449, 14573, 18191, 32154, 31834, 2405, 13583, + 11677, 13698, 23703, 12472, 1831, 16019, 3737, 2132, 14250, 1604, + 11795, 16255, 26019, 17715, 19710, 11482, 10106, 10547, 20140, 8195, + 25001, 19175, 22281, 9156, 20513, 8592, 16160, 12426, 16105, 26915, + 13025, 25369, 13833, 11935, 23585, 2943, 7334, 30365, 16098, 10310, + 5998, 12840, 10965, 8686, 9783, 30160, 26141, 23195, 17634, 9978, + 30038, 26122, 4998, 4986, 9430, 32201, 18891, 10084, 14130, 1448, + 31123, 13598, 23812, 13419, 25824, 954, 23020, 13505, 27378, 6019, + 31445, 21114, 20655, 26838, 16762, 2036, 26696, 32052, 26920, 32726, + 23578, 5780, 10437, 24321, 8402, 27902, 22033, 27332, 21205, 9813, + 20012, 24399, 735, 117, 18265, 2667, 13801, 24958, 28673, 32360, + 7249, 17006, 32507, 29014, 17461, 10230, 6410, 7288, 19231, 7558, + 12199, 12277, 21224, 26283, 15617, 11985, 15830, 23194, 323, 18204, + 30216, 7693, 9493, 31337, 17428, 12109, 25957, 10882, 30958, 30864, + 11367, 24154, 28428, 23686, 15322, 21315, 9927, 28975, 19598, 11873, + 517, 23791, 18211, 25183, 25365, 2532, 31251, 7520, 13018, 9079, + 28915, 21305, 4652, 4657, 20725, 14301, 14971, 27089, 24242, 15896, + 20917, 32155, 4390, 28289, 13674, 3826, 22012, 10922, 303, 18985, + 27404, 12441, 21568, 1431, 28963, 29308, 6761, 30441, 7170, 5784, + 23590, 21180, 9129, 13187, 23049, 2668, 23253, 7315, 30556, 7206, + 3783, 21142, 14344, 10565, 5191, 27843, 21941, 18139, 9705, 3569, + 12339, 15501, 30607, 14855, 7448, 9637, 9007, 8116, 23211, 10338, + 21206, 10848, 10753, 14226, 19437, 28770, 21606, 4707, 16316, 20851, + 8191, 30327, 1324, 22882, 30637, 14390, 8145, 15114, 18995, 13672, + 14375, 30594, 25756, 22019, 26908, 1427, 19415, 3905, 1743, 3439, + 22949, 7414, 17048, 3231, 8373, 25889, 20552, 30270, 23564, 14785, + 28850, 9061, 28075, 27882, 1386, 10214, 12396, 27415, 4556, 24381, + 8494, 20418, 19776, 17859, 9009, 10290, 10941, 29221, 25539, 22677, + 31116, 17173, 8477, 22350, 847, 5439, 22664, 12944, 19265, 5503, + 8132, 5164, 21183, 849, 11689, 14247, 1800, 30601, 22031, 1680, + 15425, 836, 31980, 20240, 22972, 15134, 17596, 18627, 7033, 677, + 31782, 23494, 3309, 11278, 3804, 24082, 8451, 16423, 32195, 15112, + 2913, 653, 8788, 14668, 16108, 1567, 16706, 21207, 13710, 26034, + 12380, 21320, 25911, 29959, 5046, 12411, 1569, 32250, 30211, 20829, + 3673, 24059, 16454, 14079, 1659, 6082, 21674, 15337, 7100, 11583, + 20653, 23984, 4910, 21968, 14980, 17611, 576, 25783, 7611, 32418, + 28804, 29643, 23325, 7656, 18102, 9837, 12243, 13611, 12025, 6334, + 14288, 24489, 19043, 20240, 28524, 19558, 24508, 14746, 30798, 32635, + 26499, 8303, 9616, 15793, 6450, 2993, 4894, 22094, 24004, 31745, + 19551, 16175, 19315, 1851, 17944, 14103, 3782, 11616, 12173, 25159, + 25803, 31188, 24297, 5455, 2721, 17610, 23775, 16931, 17978, 10492, + 21394, 15217, 27743, 415, 25778, 16946, 2037, 1174, 10984, 4480, + 1662, 19686, 27253, 23719, 3555, 26986, 13590, 29566, 24432, 17352, + 1322, 248, 27680, 29603, 5142, 6134, 32283, 21010, 28649, 607, + 7037, 24913, 5180, 1651, 1625, 23735, 2653, 5724, 32254, 1256, + 21676, 7686, 9592, 31820, 10564, 25393, 13146, 18368, 16350, 11205, + 20155, 18315, 14067, 7781, 15743, 15507, 21537, 4422, 18392, 11729, + 9168, 15170, 29963, 16898, 15381, 31528, 1400, 24991, 9943, 10583, + 19925, 13584, 14128, 10123, 22458, 21277, 19989, 8950, 27490, 13221, + 21050, 15507, 26288, 14994, 24368, 2999, 1392, 28428, 28624, 12009, + 32480, 13037, 2013, 24307, 7278, 3701, 8421, 14393, 11716, 25664, + 24168, 11850, 32258, 8564, 19773, 23989, 11850, 15935, 12848, 12318, + 6530, 8833, 11248, 1680, 9557, 17093, 11305, 24085, 9339, 2422, + 3053, 8836, 11835, 24656, 16216, 9115, 3886, 28180, 27764, 24653, + 14149, 23680, 26970, 7261, 18701, 14974, 26010, 29740, 11826, 20124, + 17117, 25296, 21827, 28092, 24777, 28261, 31924, 10764, 24613, 28328, + 12073, 28217, 8510, 8642, 21746, 22752, 393, 865, 32465, 27718, + 7305, 6669, 19972, 26184, 5107, 11962, 7551, 21653, 8977, 8857, + 20602, 14618, 356, 22324, 27179, 21182, 22669, 23738, 21205, 24144, + 20846, 23191, 28161, 25533, 31884, 26184, 26123, 25479, 6583, 15470, + 20705, 30834, 750, 4129, 4616, 16769, 15997, 25734, 7648, 10272, + 31848, 9380, 552, 22179, 780, 28019, 22468, 29423, 20228, 15926, + 446, 29047, 27210, 779, 23948, 5227, 16030, 11713, 18577, 27435, + 26412, 20713, 7910, 26114, 17769, 28976, 5977, 21009, 23870, 236, + 8374, 20873, 15464, 14836, 9286, 9246, 28959, 18869, 20886, 21432, + 21493, 898, 22393, 6507, 10498, 3372, 11856, 3753, 23614, 14803, + 22409, 32438, 23854, 1787, 13926, 27741, 24003, 28642, 26112, 8162, + 30847, 2834, 12427, 20791, 473, 9778, 3035, 17010, 2275, 21537, + 16167, 24191, 19242, 8518, 5646, 12306, 8157, 25121, 17623, 17632, + 7730, 20518, 25085, 20751, 774, 26828, 6379, 9963, 6596, 17121, + 31236, 22234, 11399, 20000, 3046, 2962, 16259, 12549, 496, 30345, + 7073, 14560, 15362, 24749, 27753, 4137, 29571, 13142, 29989, 12051, + 14761, 3449, 8515, 14947, 10996, 14622, 29692, 17539, 31740, 5396, + 6963, 9426, 17261, 18147, 10656, 22847, 9552, 18690, 15405, 9750, + 17292, 10570, 3744, 26649, 6060, 2987, 17275, 19667, 6626, 17694, + 25376, 25897, 22860, 8922, 22073, 17970, 26999, 12747, 24407, 11337, + 18722, 6720, 22489, 21600, 9796, 25572, 13328, 32663, 9040, 13807, + 4769, 21773, 439, 26525, 23338, 5621, 21934, 29920, 12328, 22509, + 10035, 11089, 14360, 14732, 9290, 9069, 476, 17722, 18573, 23038, + 2145, 30277, 7651, 32215, 6527, 5169, 31376, 17860, 9248, 22607, + 24686, 25281, 12709, 4497, 24953, 25194, 3717, 15004, 11665, 5555, + 26167, 13855, 11644, 20441, 22099, 9571, 20267, 4505, 15604, 3364, + 26430, 29855, 18016, 15520, 19402, 27457, 32235, 10989, 3161, 10349, + 26136, 16015, 27442, 17376, 23554, 13130, 31707, 28313, 18938, 4661, + 2781, 19342, 28018, 28688, 12999, 16541, 13338, 26071, 17013, 21445, + 29768, 30615, 22674, 26774, 1181, 23937, 22620, 29158, 30438, 25405, + 7079, 27590, 31296, 20865, 1573, 12725, 10368, 5601, 24036, 1299, + 17105, 7401, 3352, 22488, 8607, 4713, 12571, 12165, 23476, 31760, + 6834, 13403, 26785, 25148, 9106, 16587, 16992, 31505, 32035, 1327, + 18999, 3017, 23683, 5047, 29919, 29770, 10190, 26790, 486, 6398, + 2205, 17086, 16978, 7903, 1996, 24568, 12935, 11729, 5761, 29305, + 27499, 11573, 10345, 25026, 11975, 11963, 17221, 11303, 4849, 31607, + 10150, 31874, 4520, 30520, 30452, 18472, 32319, 19989, 31413, 2332, + 9982, 8264, 28940, 26755, 9669, 8256, 640, 27992, 9202, 22257, + 17663, 865, 5509, 623, 28707, 28258, 12162, 17083, 9908, 32144, + 7575, 2268, 31085, 4936, 15103, 31794, 1795, 21761, 21158, 18952, + 23907, 6016, 10894, 4968, 59, 23371, 24177, 27955, 14525, 27999, + 19880, 9841, 4768, 37, 8267, 18661, 10855, 20643, 10138, 2020, + 12517, 11247, 7579, 4456, 18433, 26858, 2053, 30049, 7351, 10794, + 16989, 5963, 25183, 31800, 29535, 24069, 4914, 9212, 29087, 5601, + 6666, 12661, 23408, 14584, 18793, 10926, 26117, 4544, 17341, 25325, + 10198, 27616, 14562, 21037, 25157, 18242, 7401, 206, 3714, 16746, + 15384, 23436, 12716, 10503, 20529, 942, 15417, 28616, 31480, 28543, + 30871, 28273, 18927, 28418, 19731, 12078, 10084, 26850, 21755, 12839, + 23725, 25512, 5375, 24660, 2483, 12080, 20189, 8339, 13741, 16990, + 14398, 17144, 23854, 8461, 6362, 13924, 8546, 24136, 5091, 15449, + 31892, 3773, 8353, 3852, 8180, 16238, 30298, 11708, 24216, 17578, + 28532, 17219, 18880, 30012, 10205, 23275, 25182, 9698, 23211, 22402, + 982, 30977, 17152, 24753, 27650, 22166, 1073, 14985, 1145, 8304, + 23847, 15199, 20753, 30365, 11013, 21466, 10146, 8226, 32522, 23882, + 5389, 27631, 19386, 8763, 4989, 7697, 27960, 21835, 30189, 15658, + 4343, 27192, 11499, 21534, 16529, 1341, 18587, 31760, 5111, 29855, + 23550, 17037, 20171, 26755, 9588, 16390, 27914, 23810, 8320, 29316, + 25326, 27263, 25804, 12439, 5139, 28728, 30317, 18575, 17880, 19740, + 27495, 8175, 4360, 6877, 2676, 26399, 5818, 8005, 18177, 13665, + 31861, 26573, 15994, 8454, 29592, 17260, 6855, 10210, 20473, 3757, + 32279, 31741, 29133, 1540, 23516, 2187, 1736, 4706, 25366, 17183, + 26245, 17415, 24021, 7451, 3623, 28568, 10745, 10603, 32577, 8561, + 12093, 19059, 1523, 23356, 30324, 31813, 10838, 20418, 17938, 3864, + 20762, 4354, 26605, 26102, 20334, 25728, 31813, 3508, 30240, 12768, + 32258, 16423, 22534, 16307, 23614, 10133, 20380, 12945, 19715, 30314, + 2030, 616, 15702, 10028, 3257, 23059, 628, 30897, 9677, 5423, + 12379, 31376, 437, 20522, 14732, 15829, 29594, 16028, 15376, 32249, + 18533, 15323, 8121, 22033, 30500, 18890, 27050, 22085, 12463, 29169, + 26905, 3460, 4747, 15571, 8532, 23414, 270, 13179, 5378, 7858, + 27844, 8025, 22188, 4914, 1810, 31820, 636, 29955, 10777, 20736, + 1348, 15169, 30685, 24954, 21697, 32579, 29863, 28657, 32034, 25619, + 8360, 32736, 10180, 4641, 21726, 20313, 32576, 6968, 26428, 31388, + 5287, 23216, 23045, 32232, 10764, 9702, 6777, 2449, 1603, 24809, + 4787, 23635, 13801, 19318, 4818, 6118, 23972, 6695, 21847, 17362, + 428, 12263, 8910, 29317, 26251, 2985, 22705, 15523, 28449, 15962, + 22409, 1693, 7146, 32019, 7254, 17541, 3972, 9108, 2353, 12321, + 15467, 15086, 8496, 9534, 8200, 22655, 15242, 6214, 12010, 26915, + 29179, 23317, 22099, 15529, 20074, 9225, 14070, 25988, 31690, 2904, + 31999, 16579, 32318, 29351, 31060, 14916, 13952, 2776, 24602, 8484, + 13443, 27683, 20604, 8313, 7511, 29800, 2471, 12356, 10318, 24515, + 28953, 4769, 30557, 20322, 19996, 12609, 20164, 12947, 24205, 28442, + 28657, 17953, 10466, 16989, 7074, 7649, 12140, 10921, 32587, 27784, + 26840, 22398, 4535, 32191, 8700, 4852, 8945, 5754, 11473, 17581, + 9061, 12165, 187, 8456, 26179, 15967, 12305, 22679, 25380, 76, + 22233, 23508, 15088, 31654, 26323, 1807, 1023, 11124, 29882, 19115, + 8574, 2366, 27334, 9098, 10773, 26911, 17525, 30662, 8646, 23579, + 14219, 11170, 2841, 3251, 28247, 704, 4982, 18871, 14137, 8596, + 24274, 13148, 19809, 17176, 13966, 16014, 7897, 30401, 8091, 647, + 13254, 31410, 7139, 1185, 6891, 1585, 6884, 30312, 32622, 6231, + 23966, 6644, 19423, 7947, 20912, 16502, 32374, 2609, 10174, 5904, + 4764, 7966, 10517, 13802, 9133, 8271, 4242, 9602, 11517, 7515, + 27328, 20035, 2841, 32551, 16704, 1626, 8783, 9305, 5486, 12741, + 20080, 8719, 28225, 10412, 32724, 31952, 32115, 22517, 10655, 28099, + 8162, 20499, 25923, 2078, 15671, 13854, 28486, 21218, 19141, 19241, + 24902, 10950, 30132, 32208, 19770, 112, 14430, 14864, 7674, 18155, + 24313, 27484, 24834, 9814, 16247, 25836, 23922, 18137, 10410, 9305, + 7258, 18398, 19628, 8807, 17973, 14205, 10878, 9761, 31155, 7735, + 15087, 20012, 32700, 29416, 25791, 17493, 177, 2807, 31749, 20332, + 7925, 23248, 20686, 14165, 6828, 6804, 4359, 12124, 25282, 8974, + 413, 15049, 14127, 5866, 21218, 16308, 16506, 11297, 5171, 15387, + 16508, 10680, 2776, 20786, 16487, 28074, 28715, 6394, 24583, 3875, + 23292, 31253, 10823, 28910, 22581, 28121, 4854, 22577, 30394, 27811, + 459, 23384, 19091, 29806, 3751, 15695, 11085, 23972, 21275, 2292, + 12701, 29235, 29994, 204, 14407, 31460, 20304, 14068, 25662, 14057, + 22979, 24923, 30733, 29108, 14584, 15919, 30325, 11024, 5481, 7317, + 22616, 1386, 8342, 23965, 22998, 28271, 27465, 373, 8885, 13789, + 28959, 5341, 19974, 1682, 32068, 4826, 5381, 30186, 19582, 3759, + 8514, 7495, 4205, 14857, 18942, 29169, 14353, 7034, 16600, 20257, + 8588, 2344, 29097, 26200, 6441, 15303, 6572, 2549, 18846, 3524, + 23010, 1543, 398, 15043, 18612, 31584, 29266, 13132, 18139, 28907, + 19449, 22721, 7037, 3903, 23181, 13716, 7064, 14963, 2200, 1462, + 24740, 32308, 27367, 9043, 21108, 22, 14734, 11196, 2142, 27688, + 23085, 28837, 17819, 4043, 24433, 2448, 5360, 5828, 8669, 6537, + 18318, 7823, 21962, 7444, 23352, 5110, 17068, 21604, 2979, 3811, + 32664, 7629, 4898, 8307, 22787, 21628, 24195, 18251, 17357, 1894, + 14387, 32303, 382, 7294, 11945, 6353, 22991, 13355, 14850, 25318, + 3436, 24825, 25833, 13695, 26104, 1935, 31930, 30359, 1873, 31457, + 3303, 14056, 26134, 10406, 10650, 25092, 1895, 27149, 19868, 13472, + 28140, 19627, 20341, 27955, 22553, 26552, 12757, 27328, 13232, 4476, + 21298, 11759, 27271, 2308, 16563, 28771, 27787, 26649, 12365, 17992, + 6917, 29043, 26156, 32299, 31004, 3881, 2388, 13071, 17819, 5484, + 11144, 17597, 25077, 7541, 2154, 10843, 10887, 3260, 16357, 21312, + 9244, 7275, 4040, 2567, 8886, 29970, 20659, 3554, 19852, 12956, + 14903, 22065, 9295, 25730, 12857, 13396, 27057, 24126, 11159, 18365, + 5928, 27651, 22543, 30793, 15855, 16299, 12636, 27344, 31055, 10737, + 7585, 9211, 22044, 29664, 13067, 19797, 21378, 27787, 24242, 20645, + 24834, 32511, 29377, 15740, 32183, 25718, 24535, 11530, 23774, 18125, + 7597, 6197, 13205, 26424, 1784, 21485, 5091, 7477, 11409, 10169, + 30969, 16830, 25555, 12950, 26807, 23235, 5188, 4610, 20082, 6871, + 2168, 32596, 29429, 2346, 17971, 22350, 13765, 26039, 12750, 16373, + 27562, 7614, 220, 24736, 1642, 25717, 16397, 24656, 27096, 17403, + 28754, 7743, 4621, 14989, 32577, 22716, 28406, 14332, 7868, 13877, + 6415, 3934, 16333, 25750, 17261, 12548, 13525, 17106, 3842, 16557, + 16102, 18622, 8989, 23545, 2669, 8486, 12080, 9505, 24340, 10779, + 14806, 26084, 1766, 20362, 24852, 24261, 12468, 29715, 28639, 5747, + 20920, 23575, 27871, 15753, 19065, 20243, 1651, 31351, 21230, 25389, + 32757, 12736, 29158, 9997, 1087, 7783, 936, 7662, 366, 20131, + 5078, 16885, 29158, 22746, 3735, 193, 13974, 23757, 3316, 26179, + 26237, 9489, 16515, 27088, 14188, 31934, 16963, 4468, 25614, 3435, + 18632, 24458, 13095, 22873, 2456, 16009, 1049, 9710, 18900, 24353, + 1196, 27237, 25115, 15495, 29710, 5829, 14763, 15117, 5913, 18191, + 31945, 5365, 16853, 14012, 4708, 2439, 6718, 2150, 5365, 609, + 30791, 13766, 21161, 8975, 1822, 15326, 6338, 30427, 2994, 22391, + 20026, 12518, 29811, 26062, 8618, 12920, 5720, 15755, 20075, 19349, + 15143, 5106, 15611, 27089, 25841, 16989, 674, 10597, 27355, 32746, + 1109, 31873, 21285, 25457, 16602, 23780, 3684, 16825, 1581, 15275, + 30738, 15339, 5667, 9062, 16457, 4678, 32333, 16497, 8908, 3861, + 10015, 17282, 3911, 7646, 24145, 21118, 9944, 25309, 24710, 31633, + 13005, 26288, 9772, 24189, 30896, 10836, 30627, 50, 3969, 14160, + 3233, 31904, 6722, 48, 15061, 2761, 3001, 9525, 30666, 1818, + 22385, 29003, 12815, 30715, 13314, 9019, 17813, 16549, 21138, 22306, + 3928, 30259, 12188, 9250, 25665, 2812, 24816, 16983, 28491, 17441, + 1275, 19725, 15078, 11618, 14153, 30000, 27033, 32566, 11848, 14339, + 25066, 11034, 26191, 28181, 29058, 553, 32222, 18034, 11149, 10733, + 10024, 534, 1632, 21365, 21198, 935, 31838, 26791, 30702, 1891, + 13340, 9176, 7516, 5975, 29635, 17818, 2095, 4049, 25526, 23765, + 13184, 18457, 25717, 21786, 30062, 13148, 31682, 16179, 30053, 3561, + 7426, 2047, 22433, 27296, 20709, 4477, 16877, 13478, 73, 25126, + 7601, 31070, 26204, 24192, 15147, 3733, 28161, 16624, 14845, 26877, + 32374, 17359, 23272, 14045, 31539, 12737, 7895, 4870, 31509, 20546, + 22303, 25609, 31419, 22658, 2821, 15271, 3633, 29299, 22145, 19081, + 21963, 26144, 9793, 15410, 14932, 30528, 29937, 3889, 13416, 7947, + 28090, 15771, 8991, 6220, 6152, 18872, 30188, 29112, 28817, 765, + 11716, 6782, 5011, 5946, 31106, 7455, 23644, 17429, 29066, 31872, + 7696, 15543, 13996, 26430, 22951, 6085, 30796, 13633, 5429, 22930, + 9664, 25492, 14446, 28307, 20218, 627, 4713, 10771, 30823, 18854, + 27812, 7463, 26134, 18190, 20297, 23013, 8733, 23422, 23215, 30417, + 16953, 31180, 23076, 26264, 12468, 23591, 29674, 30721, 31884, 9130, + 6783, 4640, 29905, 5682, 8993, 205, 4943, 28269, 9618, 25940, + 31341, 29236, 2329, 8046, 2645, 17179, 25289, 17008, 20821, 7494, + 2309, 27331, 23429, 30846, 68, 1940, 9643, 16259, 9490, 27852, + 14171, 4516, 14168, 16707, 10339, 17038, 10972, 7472, 5478, 3170, + 24843, 27877, 18374, 10250, 12279, 27577, 5319, 16332, 23196, 19269, + 14322, 28703, 24167, 23251, 11448, 19134, 14841, 17171, 19288, 4092, + 4392, 10768, 11523, 4695, 21062, 21347, 6973, 30799, 16550, 4262, + 3718, 20601, 2141, 5676, 58, 11215, 16198, 2031, 5046, 12874, + 30537, 21194, 23228, 26035, 17884, 9705, 14617, 1718, 25801, 8317, + 11140, 20536, 20530, 1972, 30154, 31978, 1491, 6376, 31784, 19230, + 18824, 19479, 19448, 29402, 4879, 6803, 6033, 16939, 2318, 7674, + 10600, 21436, 11608, 7017, 4731, 5031, 7788, 20205, 6127, 1675, + 4671, 2874, 9207, 1759, 28219, 416, 15865, 27782, 8327, 17978, + 19948, 21155, 7817, 25705, 12037, 28590, 11433, 6016, 21906, 13256, + 21336, 31681, 3110, 8523, 9553, 19337, 3062, 5030, 15135, 31943, + 29827, 26728, 2016, 21170, 26233, 16026, 27464, 14268, 17879, 16466, + 9449, 29633, 26449, 10885, 3249, 4741, 11507, 1821, 13492, 3677, + 22022, 19787, 22571, 4908, 1591, 23583, 15293, 28049, 18422, 854, + 25342, 31327, 3613, 3699, 16182, 10830, 30930, 1553, 15203, 28144, + 25006, 7031, 18817, 7654, 29471, 29293, 21768, 13139, 20234, 15694, + 16055, 25745, 2229, 24415, 17808, 31267, 30181, 2399, 292, 28651, + 6979, 1119, 20752, 13981, 14340, 7892, 26056, 10612, 13513, 28651, + 18067, 3108, 31172, 25884, 30935, 9487, 28701, 19970, 11415, 18363, + 12948, 23146, 4666, 25476, 12647, 16042, 15758, 27278, 19830, 29236, + 14860, 9470, 11941, 15466, 13406, 17514, 22914, 948, 23838, 19341, + 18472, 4816, 26849, 7744, 2659, 18538, 20852, 19878, 12564, 25837, + 15461, 29862, 2263, 27288, 10949, 14494, 26596, 17364, 22061, 16981, + 4915, 9157, 24319, 17202, 32484, 21791, 12270, 23212, 28125, 27451, + 15041, 28674, 13493, 2410, 29267, 21548, 9169, 13449, 9695, 16401, + 23756, 25301, 8570, 762, 5916, 15873, 14305, 30023, 6131, 19334, + 6517, 865, 24932, 28475, 7300, 25839, 23751, 31781, 23242, 21581, + 1118, 18320, 18488, 23097, 20537, 29278, 9972, 464, 27222, 23712, + 31904, 2234, 28173, 30563, 27871, 24481, 13995, 26828, 7445, 3815, + 350, 13928, 1391, 21400, 26193, 27069, 21284, 2205, 27927, 25381, + 16378, 12339, 16461, 20188, 779, 12700, 17531, 28759, 16073, 7956, + 5692, 14295, 4160, 6151, 13215, 22079, 9115, 7041, 21708, 17186, + 29162, 24294, 14315, 26913, 3106, 31971, 25665, 27764, 5989, 4260, + 10396, 16326, 24196, 4424, 22770, 31558, 7363, 402, 3909, 10747, + 26076, 18401, 29641, 343, 20294, 16994, 20553, 14096, 16054, 27524, + 29383, 26923, 487, 8876, 9059, 19546, 5015, 27947, 12670, 5255, + 12754, 17261, 23582, 19377, 10610, 15986, 6548, 2807, 31463, 14722, + 605, 18930, 15412, 15389, 25415, 14408, 23610, 16434, 15060, 21886, + 11065, 10385, 9699, 15118, 19604, 31649, 32653, 19642, 30164, 13910, + 14414, 17183, 7920, 19294, 10341, 17549, 1996, 8859, 24957, 22, + 14079, 11478, 17292, 32485, 21977, 24498, 22780, 8146, 3493, 11742, + 15881, 28979, 18987, 20367, 5994, 23796, 1401, 28462, 23603, 23274, + 227, 3661, 30458, 7524, 3913, 14048, 568, 27651, 21656, 19626, + 9203, 8396, 7827, 4853, 19610, 31612, 15060, 14982, 23857, 16155, + 28173, 3024, 30893, 19924, 28820, 360, 1186, 14084, 5151, 26297, + 7451, 27537, 30412, 20217, 32713, 3197, 26468, 30812, 12897, 1979, + 24288, 28551, 26687, 30529, 13100, 1863, 27471, 30302, 26412, 30851, + 3117, 24910, 15357, 15983, 23947, 13068, 26493, 771, 16084, 30055, + 5133, 12204, 31246, 30552, 3477, 30273, 30981, 15763, 16329, 26168, + 21926, 24924, 27051, 25114, 18857, 20892, 10833, 2356, 16755, 10804, + 19177, 27024, 5690, 5521, 27130, 3619, 13162, 27776, 27629, 15688, + 11079, 2116, 21707, 31597, 32468, 31463, 28488, 9740, 7412, 8653, + 25857, 15070, 23760, 18063, 11266, 8011, 30584, 16902, 28043, 31780, + 17613, 21925, 18964, 404, 16286, 12044, 7822, 10068, 5393, 18907, + 12012, 15647, 17221, 9156, 23010, 24422, 2869, 30437, 14828, 9284, + 14816, 28244, 22419, 5300, 3239, 2299, 20716, 17922, 13558, 7846, + 6753, 10111, 16166, 15194, 15539, 22649, 24738, 17641, 13376, 6762, + 29001, 7875, 21890, 13168, 5140, 16399, 23257, 21757, 21241, 28666, + 32650, 23248, 12815, 7247, 19463, 30244, 32242, 32329, 30577, 6439, + 22889, 421, 603, 13997, 20038, 8698, 28369, 10790, 25998, 12877, + 14744, 23501, 2778, 17120, 30904, 29987, 16125, 6476, 17208, 14748, + 24039, 28367, 17961, 22424, 31690, 26855, 10982, 17812, 7547, 32, + 12953, 32674, 20506, 18730, 11009, 14614, 25589, 21550, 8307, 22147, + 20138, 5730, 28959, 17382, 30860, 4572, 9621, 31520, 31048, 12196, + 12936, 20138, 1993, 12, 2001, 19588, 8536, 21828, 4278, 19822, + 11272, 27689, 2413, 1115, 6225, 5982, 19932, 8739, 17915, 8350, + 1443, 24952, 12349, 21584, 20828, 3867, 9511, 27484, 23841, 25167, + 3133, 18238, 31823, 14735, 101, 13401, 12938, 3964, 16643, 9869, + 19970, 15385, 15970, 9227, 30452, 22074, 17750, 21096, 22643, 18746, + 3615, 23025, 18024, 29421, 12945, 4927, 1240, 24021, 20338, 13067, + 5221, 27545, 21796, 26691, 10264, 946, 19331, 8284, 10333, 6760, + 3345, 26091, 26447, 9213, 15697, 3439, 27190, 18472, 32368, 9329, + 27726, 27077, 10794, 22224, 32459, 22218, 4588, 15958, 27161, 13437, + 2614, 20622, 13416, 31716, 17732, 24282, 29032, 6781, 16621, 23744, + 16209, 2864, 19403, 24709, 5415, 31618, 26012, 4605, 17980, 20747, + 32488, 31649, 24310, 9002, 23525, 29154, 16082, 31991, 7831, 1213, + 5033, 31609, 15419, 12176, 6707, 7194, 25062, 15148, 18377, 20311, + 3178, 1579, 16751, 9148, 22393, 10116, 17425, 7440, 18445, 8028, + 25101, 3021, 5638, 2591, 14834, 30931, 14613, 2512, 2695, 31068, + 15578, 22941, 18448, 32563, 8265, 16311, 5836, 31009, 17490, 20832, + 20162, 22440, 11713, 15686, 27838, 18932, 7779, 4111, 5270, 18191, + 18794, 10037, 2225, 14164, 10002, 17940, 15050, 21030, 11141, 321, + 23026, 994, 25852, 29726, 468, 28689, 17704, 29867, 28477, 25571, + 6608, 14751, 24028, 19673, 30907, 9912, 29510, 1985, 8912, 13135, + 23356, 7143, 8828, 7983, 31025, 13962, 10580, 27047, 21255, 31373, + 28924, 16094, 6426, 30831, 9031, 20327, 28543, 25213, 3487, 17941, + 23694, 20806, 9128, 20497, 157, 11344, 5408, 27433, 31503, 2825, + 31498, 28267, 8334, 8704, 24587, 243, 26260, 4181, 10260, 29441, + 10288, 8559, 6977, 11599, 10247, 22252, 6145, 10913, 29593, 20190, + 12408, 12189, 3013, 23092, 14113, 13533, 30351, 22390, 32440, 14147, + 7714, 9654, 20823, 25228, 8465, 17347, 25311, 18984, 30690, 28575, + 17049, 9289, 5069, 23091, 4125, 15163, 23233, 17218, 30579, 2291, + 9384, 4508, 14457, 12522, 7660, 15252, 15748, 19500, 13114, 5043, + 1354, 15275, 27270, 19886, 25600, 29914, 19791, 20374, 24511, 31863, + 18468, 9039, 11308, 936, 9063, 7235, 2392, 23370, 31057, 1367, + 2533, 12678, 19766, 27423, 1131, 19877, 10250, 22093, 13337, 15909, + 24101, 2672, 19616, 11532, 25155, 7387, 1356, 20092, 18895, 30690, + 24436, 7560, 26209, 7757, 11285, 23366, 6277, 17158, 5306, 1061, + 16318, 2325, 11167, 12161, 27594, 32718, 18197, 3445, 13096, 12105, + 11304, 30449, 21208, 18707, 19584, 17420, 31586, 20771, 7045, 23707, + 28441, 21264, 16071, 14082, 21921, 30593, 7580, 14666, 26799, 3556, + 5131, 12404, 24080, 24163, 19688, 21005, 13037, 22547, 1857, 31747, + 15437, 28527, 19193, 9911, 8411, 16646, 20659, 15699, 8170, 19568, + 8433, 20574, 18870, 23430, 11901, 22158, 3308, 27091, 28681, 28637, + 15725, 8261, 15634, 24286, 30799, 14632, 29194, 11346, 2341, 24661, + 8117, 10487, 13441, 25511, 18900, 16800, 2999, 18351, 30977, 22922, + 11747, 5289, 9178, 7918, 6701, 28590, 13153, 3539, 30539, 21547, + 9891, 5414, 29768, 401, 6552, 4620, 9860, 29006, 13131, 5495, + 2301, 26877, 16186, 10232, 204, 31961, 30208, 32724, 2803, 5752, + 14641, 23897, 30549, 21430, 18054, 370, 21766, 20975, 9786, 29306, + 29637, 14705, 22707, 846, 7879, 7472, 1893, 20646, 29879, 700, + 15604, 10405, 250, 9841, 26776, 4270, 31287, 20461, 12486, 23930, + 18967, 19144, 22797, 4455, 16969, 22399, 16190, 29971, 8906, 10416, + 13263, 7722, 21859, 16525, 12673, 22652, 627, 17035, 14401, 9555, + 25326, 3418, 20300, 17667, 29606, 12570, 29335, 32450, 28105, 5580, + 28526, 6842, 10411, 26118, 29787, 16989, 18630, 4587, 22601, 15097, + 24576, 32347, 23785, 11916, 25388, 13415, 28757, 30425, 6354, 9480, + 12788, 7867, 25907, 21776, 16029, 3514, 6024, 1376, 2047, 4572, + 7307, 11100, 15168, 15293, 14904, 19868, 31460, 16610, 14549, 6372, + 28657, 22603, 7472, 16727, 30288, 17735, 22568, 999, 26743, 1165, + 3950, 23568, 20037, 20359, 25485, 16838, 29485, 27121, 22312, 940, + 10135, 28067, 15235, 4486, 12675, 2219, 17990, 19185, 12400, 24991, + 13437, 9810, 30844, 5682, 7906, 17420, 10444, 2052, 25439, 16886, + 302, 27583, 18383, 25356, 15101, 20063, 14853, 24420, 26950, 1233, + 20751, 28772, 9256, 14784, 29627, 27955, 897, 3500, 18523, 2925, + 11463, 32060, 20934, 6171, 30771, 469, 23967, 15586, 28965, 8299, + 10202, 9594, 13764, 23191, 22395, 6498, 31204, 13802, 14497, 24018, + 12590, 19842, 22468, 29682, 9338, 8666, 25147, 1865, 30336, 6886, + 28673, 8312, 23472, 28768, 6840, 28712, 2398, 27741, 1518, 17985, + 8226, 15395, 15520, 2396, 5503, 17214, 2044, 17020, 24770, 9425, + 9067, 4344, 28461, 30507, 29135, 6844, 9045, 21914, 6153, 19634, + 25545, 17540, 8383, 32634, 31988, 7091, 3420, 21601, 23109, 27377, + 30808, 24209, 23181, 3285, 11449, 27781, 17320, 23516, 22234, 1472, + 6495, 15803, 12907, 28863, 23437, 30964, 22969, 21804, 28545, 2216, + 6703, 20052, 14500, 25260, 29213, 475, 9307, 25567, 7097, 3758, + 11567, 22202, 18788, 26682, 6444, 18472, 30510, 29148, 30598, 7832, + 30107, 28312, 8579, 10065, 4520, 6413, 17044, 3590, 32265, 30632, + 11205, 29355, 25013, 16665, 30296, 4487, 12196, 25859, 14765, 12681, + 11223, 3320, 22630, 19679, 16794, 10660, 10440, 10181, 3846, 24648, + 31843, 18843, 18889, 27755, 19189, 3131, 24874, 18892, 1164, 13705, + 327, 15002, 20203, 20300, 23457, 32231, 12847, 12604, 22747, 19035, + 30898, 29019, 6079, 24869, 17968, 11913, 23938, 19767, 10792, 26963, + 23369, 8883, 12677, 24333, 27883, 21152, 15230, 6944, 13561, 26338, + 5563, 12964, 14844, 14953, 9145, 24114, 26877, 424, 5487, 26099, + 8523, 14153, 28978, 5117, 11640, 16671, 29910, 8416, 19877, 24924, + 4630, 20091, 14079, 18436, 2613, 9493, 11402, 22689, 27878, 1705, + 5406, 30777, 32362, 3200, 12975, 30589, 18680, 25349, 24273, 27971, + 23905, 6102, 28794, 5430, 1780, 3607, 12042, 1740, 30367, 3339, + 30346, 22649, 10584, 7397, 28977, 803, 3515, 9691, 2605, 6613, + 11509, 1405, 32160, 10679, 23264, 3653, 10728, 22078, 12655, 9583, + 1571, 1440, 23373, 29450, 25074, 12683, 29121, 22608, 21118, 12310, + 29141, 8243, 28857, 2296, 3255, 7736, 19687, 10999, 22367, 12168, + 15196, 5540, 7538, 21061, 24199, 19270, 23399, 18202, 4655, 7956, + 9192, 28950, 7165, 13223, 29819, 26554, 17723, 15790, 15713, 28496, + 16859, 24292, 8382, 19327, 2235, 11583, 11515, 8221, 295, 14954, + 5737, 21572, 10656, 12150, 13351, 11176, 7131, 20685, 17477, 2599, + 4621, 32359, 15145, 5538, 20838, 1088, 8754, 10764, 7643, 20233, + 18844, 21440, 6953, 22813, 20900, 17432, 16151, 25528, 20204, 29755, + 14587, 20579, 3616, 27190, 29311, 11955, 15066, 21583, 3493, 21043, + 28099, 28309, 1033, 1183, 26108, 26300, 19303, 10323, 29116, 12840, + 9052, 24322, 19002, 2632, 28785, 2219, 26317, 805, 18517, 11977, + 2897, 28731, 4970, 12235, 5582, 2951, 26499, 16067, 20308, 4833, + 32749, 2914, 17449, 20364, 30380, 9764, 26407, 15372, 8966, 29896, + 1119, 15415, 16710, 19203, 18178, 7055, 18362, 31605, 22146, 17871, + 3326, 29916, 20212, 28100, 9084, 16264, 6329, 5726, 3636, 28431, + 25437, 12807, 12985, 23617, 18367, 19507, 11388, 15198, 21874, 28985, + 25933, 25223, 3940, 18529, 5919, 21619, 7101, 5182, 6924, 25443, + 11018, 3390, 10649, 88, 23195, 19071, 28730, 2691, 5673, 11492, + 8217, 13053, 15549, 105, 23059, 18616, 3276, 18355, 7171, 182, + 7079, 26092, 4932, 3704, 23571, 6861, 30833, 8378, 30771, 1276, + 22639, 5725, 687, 7833, 28334, 32677, 17167, 26643, 29867, 16292, + 8552, 13633, 2746, 25078, 30011, 20654, 7682, 30895, 15258, 26932, + 28617, 5778, 30860, 32204, 8690, 14724, 18355, 15268, 27843, 14656, + 24620, 626, 25068, 3659, 4023, 777, 24773, 1323, 28952, 11305, + 8152, 12756, 25021, 22940, 28575, 14179, 31392, 31102, 6799, 14350, + 4207, 7133, 1621, 347, 32491, 8322, 31327, 18577, 17294, 30477, + 12190, 2924, 11120, 9544, 3466, 26901, 6251, 1973, 14439, 1873, + 8573, 105, 21703, 22521, 4719, 2077, 9297, 21162, 3764, 21891, + 24512, 30397, 23444, 3798, 29231, 10618, 5514, 14773, 31123, 17544, + 21738, 23857, 12425, 26792, 1478, 2597, 9554, 26788, 30483, 15814, + 25115, 24705, 25864, 21114, 27266, 19708, 711, 29422, 12887, 14133, + 18239, 17250, 29087, 18597, 337, 13640, 31199, 23381, 30513, 14606, + 28823, 23122, 5392, 16952, 4876, 1628, 28408, 20913, 26442, 29406, + 6647, 31331, 32362, 20259, 8319, 5125, 11281, 20422, 5645, 4148, + 22329, 19562, 3593, 9670, 18484, 26028, 31213, 18011, 6125, 18262, + 12970, 4660, 9330, 23253, 12616, 15945, 11951, 32238, 15052, 8341, + 1404, 4524, 862, 22197, 25051, 30109, 3243, 15025, 22650, 17214, + 17289, 6682, 1152, 23987, 8409, 25429, 20358, 10683, 20914, 15670, + 31458, 484, 10940, 15569, 20012, 15099, 27353, 11660, 3835, 19655, + 4363, 8057, 9712, 29431, 18834, 12259, 5366, 23128, 13908, 16423, + 8903, 11851, 5762, 3781, 24499, 6775, 4473, 26295, 26525, 5307, + 10470, 22786, 1350, 870, 5678, 13450, 6862, 5035, 5901, 12330, + 29559, 21583, 28029, 31492, 25261, 26789, 5849, 10006, 21571, 26556, + 32322, 8719, 12800, 15319, 11842, 12384, 16986, 5661, 17839, 7829, + 25729, 19090, 13266, 7398, 19871, 10310, 32713, 30985, 11102, 8653, + 7978, 10827, 20396, 23022, 8169, 25567, 18875, 26783, 10620, 8566, + 4171, 27698, 6460, 25104, 8491, 15049, 15887, 1297, 14845, 18447, + 30532, 32151, 3643, 4906, 15038, 20491, 7799, 12523, 18005, 3574, + 5351, 17992, 121, 17430, 19232, 1805, 19480, 3762, 23842, 3278, + 21442, 18474, 28053, 6688, 4108, 1490, 32594, 7689, 3378, 22272, + 2085, 2996, 13312, 31332, 8401, 23145, 6642, 32152, 30293, 16829, + 7322, 31480, 24686, 1542, 19522, 27060, 16733, 26898, 27091, 13348, + 4842, 4889, 31733, 2139, 17577, 2031, 16248, 5886, 20348, 31870, + 30451, 13399, 8793, 8308, 23978, 4005, 19534, 12345, 16821, 29878, + 1986, 25380, 2399, 19384, 2813, 2542, 10189, 19277, 16875, 29031, + 17632, 4317, 21788, 23911, 7259, 4912, 8720, 12296, 22601, 9020, + 2226, 4370, 27289, 28475, 12834, 10679, 10764, 15361, 13495, 18561, + 13905, 18573, 14661, 392, 26478, 4062, 1435, 7912, 19553, 23816, + 4176, 30066, 32007, 25990, 448, 29090, 17214, 24018, 18001, 28881, + 24390, 15411, 5627, 15020, 27502, 7638, 5383, 12005, 8940, 545, + 29206, 31529, 4108, 20548, 9574, 27985, 25997, 23313, 13649, 13788, + 1863, 22285, 9238, 11933, 1554, 30407, 27356, 31153, 4836, 8552, + 9716, 3252, 11577, 30604, 22111, 31796, 4280, 6736, 23696, 7014, + 9747, 26796, 4210, 17432, 31535, 9988, 6829, 23271, 1669, 17657, + 29478, 25522, 14792, 13701, 10436, 14927, 10183, 3399, 22138, 17276, + 16790, 23323, 27010, 10776, 28392, 28398, 7398, 31891, 1472, 721, + 20037, 16035, 25929, 31307, 4052, 17208, 14337, 11252, 7075, 25267, + 8477, 26943, 3106, 24914, 22826, 14488, 32227, 6639, 32240, 30127, + 32417, 16619, 6555, 19475, 28034, 21349, 9501, 16386, 12441, 1711, + 2510, 14720, 25519, 19458, 26197, 17830, 25667, 15472, 6860, 573, + 24271, 283, 24607, 26507, 16717, 2981, 28341, 25709, 14074, 13031, + 30803, 166, 4442, 2022, 31037, 31095, 17020, 30466, 9374, 3184, + 19583, 2417, 28713, 25733, 19029, 23276, 769, 23908, 30151, 6056, + 3030, 4358, 20153, 5768, 16977, 25476, 18352, 25489, 6067, 301, + 7057, 20126, 23225, 725, 12460, 21077, 21186, 11375, 11512, 26457, + 5683, 31199, 8096, 6081, 8219, 28169, 25535, 26314, 26899, 22574, + 8296, 24776, 1897, 888, 2538, 26467, 6781, 1235, 18514, 3583, + 1386, 6911, 10033, 6780, 15593, 21731, 29343, 32288, 11379, 29221, + 9599, 23249, 30374, 12791, 19717, 26760, 5354, 1076, 17782, 20226, + 6740, 20651, 13359, 18361, 7627, 18619, 25321, 7186, 3945, 30496, + 24635, 21464, 32035, 524, 2400, 10217, 2733, 21369, 24891, 3375, + 8344, 7102, 22680, 27602, 5822, 25185, 27573, 22188, 24031, 6445, + 14593, 22270, 27788, 25988, 29987, 8289, 29508, 5110, 24638, 16639, + 7134, 27154, 8308, 8706, 22566, 19067, 9641, 27725, 971, 24383, + 31369, 9578, 23929, 15973, 9932, 6340, 6978, 15557, 1720, 24855, + 7746, 25373, 15336, 9764, 2435, 14688, 6989, 25257, 16414, 23633, + 15474, 18064, 3747, 4433, 3588, 2407, 955, 19573, 5009, 16656, + 15372, 23772, 12432, 4282, 9565, 13120, 9378, 17424, 31995, 30410, + 7447, 10927, 3419, 23595, 32692, 18398, 28416, 29416, 21171, 10873, + 20639, 23797, 26124, 303, 2690, 9350, 8691, 16110, 298, 9250, + 7842, 25498, 6968, 13561, 26968, 15621, 20110, 6007, 6818, 13340, + 2225, 18969, 8665, 31326, 30321, 17024, 27116, 11155, 28365, 15556, + 15199, 14266, 1297, 5386, 32660, 10563, 1601, 24409, 18762, 7038, + 26042, 12713, 18019, 30036, 23831, 21565, 32490, 15413, 17234, 19507, + 1364, 4945, 24363, 29786, 31750, 15115, 14542, 6211, 5144, 9386, + 11952, 17819, 26969, 28242, 20922, 18359, 24467, 23348, 19625, 369, + 25872, 7169, 23221, 6451, 15964, 31652, 4464, 11089, 16205, 17456, + 498, 30364, 5441, 28695, 30003, 14132, 1703, 31646, 18360, 9117, + 19393, 27277, 10354, 16405, 25767, 22059, 16099, 31781, 4240, 5993, + 31874, 20087, 19021, 24926, 7521, 17127, 28182, 24563, 7181, 11996, + 27785, 12000, 7349, 32729, 12016, 15575, 5007, 22607, 4291, 28414, + 10872, 28342, 4718, 6307, 10715, 25307, 10579, 6956, 26835, 32124, + 20755, 16972, 2196, 3602, 16905, 12413, 32519, 23278, 28952, 28586, + 13492, 22407, 15527, 31275, 14821, 5395, 27369, 20471, 22280, 8532, + 23906, 27669, 31830, 18357, 7591, 11860, 21682, 26773, 10085, 3388, + 32724, 23267, 5028, 9284, 14246, 3051, 2381, 29254, 25838, 20847, + 5866, 23599, 26414, 8698, 22958, 18427, 5064, 10097, 5557, 25569, + 12390, 24555, 9100, 28106, 27801, 16578, 22260, 3848, 7873, 4075, + 17123, 8261, 18476, 26498, 32460, 27065, 9346, 6057, 20768, 13217, + 18528, 23187, 25365, 15084, 4931, 3804, 24998, 24119, 30888, 27676, + 12251, 5883, 25713, 26043, 3658, 4869, 16011, 12295, 23518, 551, + 20942, 7043, 21463, 27694, 6692, 14978, 27922, 8979, 15854, 1269, + 16011, 9590, 13987, 28290, 3360, 1351, 27834, 8918, 26264, 9927, + 19147, 23611, 28131, 27567, 3057, 2157, 15523, 7854, 18802, 12201, + 32236, 13081, 2978, 21000, 9800, 11487, 25092, 12043, 27903, 17595, + 18099, 11127, 27285, 7341, 20736, 18787, 147, 6314, 31581, 26255, + 28299, 12179, 122, 13559, 14422, 18532, 5037, 8616, 16800, 30638, + 30871, 7635, 17063, 12615, 15877, 32288, 31295, 16959, 8212, 25668, + 16666, 10007, 21913, 26326, 25158, 4654, 19647, 27486, 23799, 13948, + 6898, 26288, 29575, 10882, 9212, 30584, 11195, 30593, 15470, 3861, + 9288, 10475, 19266, 7339, 25493, 28695, 11966, 28275, 963, 12344, + 26780, 15702, 24555, 1680, 21863, 5819, 9894, 8929, 29480, 6301, + 30163, 1061, 31554, 13280, 25492, 28030, 21403, 26853, 6298, 10096, + 6908, 12892, 24483, 24910, 21691, 4697, 20032, 18162, 30615, 30122, + 27356, 7857, 25571, 24937, 29690, 911, 1255, 7885, 11454, 4014, + 8579, 22853, 21167, 2829, 8213, 25288, 8379, 29295, 16797, 4710, + 892, 27288, 6175, 28964, 25273, 29631, 15433, 10502, 9264, 28993, + 1622, 27245, 19346, 22931, 20018, 15267, 23504, 8826, 30431, 26293, + 23957, 15742, 18711, 31105, 12051, 14492, 24883, 3087, 259, 22331, + 13387, 16677, 9447, 14177, 28831, 13589, 4243, 22311, 15669, 20186, + 15083, 3690, 7364, 10083, 13806, 20676, 30673, 31535, 10740, 19386, + 4632, 23066, 5683, 13623, 28461, 2423, 31368, 24122, 27235, 19550, + 20085, 24011, 1517, 1607, 26982, 26675, 13635, 27608, 16440, 9836, + 9928, 13820, 7163, 8663, 20649, 2869, 11767, 6189, 17672, 4915, + 9454, 19384, 4442, 26789, 10145, 9155, 11533, 23887, 22677, 12623, + 15214, 17501, 18199, 27655, 9419, 14071, 29054, 15222, 19156, 1813, + 15536, 8312, 23623, 21653, 1256, 4846, 3021, 30859, 11190, 26550, + 11807, 15064, 18247, 21211, 876, 1534, 22246, 23048, 12287, 3378, + 8387, 19267, 10269, 6323, 7411, 3031, 4908, 27739, 4537, 21394, + 27163, 31859, 17668, 10289, 21757, 25804, 26424, 11545, 3063, 22027, + 6961, 6984, 16221, 27973, 16262, 30090, 19224, 10700, 23818, 19696, + 14667, 30024, 24217, 10622, 22582, 29193, 30225, 27428, 26458, 923, + 21150, 14064, 8157, 17819, 3131, 17537, 7217, 25802, 26741, 8127, + 25329, 3680, 23666, 13001, 6411, 27043, 28723, 11311, 7972, 20407, + 26390, 5474, 7736, 13752, 20895, 11624, 18305, 32006, 27767, 23508, + 19355, 7648, 28853, 20470, 19685, 24629, 14685, 8271, 20778, 5814, + 25126, 32327, 30337, 4144, 17281, 21027, 28529, 13081, 28630, 21297, + 2649, 30569, 5649, 28741, 18447, 12971, 27997, 7273, 17864, 27219, + 3631, 21457, 32312, 8274, 23687, 27917, 9722, 19944, 26483, 2690, + 10033, 185, 25044, 16411, 112, 20004, 6244, 16700, 2775, 4304, + 2296, 11000, 27519, 9351, 32515, 16003, 13337, 5192, 22876, 8170, + 30417, 28311, 23873, 27905, 20857, 13336, 15003, 16541, 13283, 5212, + 9603, 30212, 25998, 27899, 10502, 16546, 3717, 16099, 5925, 27646, + 18309, 3567, 14880, 22508, 30046, 24252, 10997, 1283, 29595, 18104, + 28963, 17745, 21093, 17019, 20403, 25934, 11173, 18011, 25465, 24847, + 21353, 15674, 30360, 31164, 26505, 27043, 11588, 16196, 650, 23830, + 28620, 25167, 5570, 31135, 13783, 25205, 11965, 12717, 5869, 27821, + 11232, 31266, 17466, 32264, 9292, 8871, 20367, 7839, 1798, 2114, + 25514, 18633, 13652, 22058, 20919, 20626, 7274, 18650, 6167, 24425, + 4677, 10084, 18015, 22553, 31192, 26376, 25252, 14075, 29286, 11525, + 13302, 17001, 5679, 8879, 5674, 8053, 3150, 20418, 15069, 7899, + 29154, 28406, 31669, 22069, 24172, 28883, 8682, 22894, 1912, 8391, + 18181, 10361, 18304, 8310, 13032, 9539, 3085, 11601, 23311, 9702, + 28447, 25706, 26389, 30495, 32719, 30854, 24186, 25346, 32307, 3700, + 25507, 4076, 3714, 13214, 8226, 19386, 10104, 8926, 17976, 5944, + 16380, 19347, 19352, 1500, 1, 20211, 16750, 17845, 30890, 26531, + 28064, 25502, 10161, 28549, 2388, 8119, 18953, 30673, 18610, 26134, + 16834, 10087, 4075, 16313, 4910, 21347, 31098, 20415, 15682, 1522, + 4522, 12504, 28990, 26469, 27480, 18171, 28049, 15748, 29178, 22871, + 32758, 29989, 7392, 2187, 29412, 27133, 26554, 29977, 27173, 24757, + 13403, 10776, 29380, 26432, 31164, 8234, 18989, 3949, 25668, 27822, + 8336, 14264, 19270, 28983, 23174, 22118, 8140, 7996, 5623, 31441, + 14483, 9233, 30267, 30976, 8022, 4373, 31615, 29562, 17231, 21550, + 26987, 14172, 13564, 5234, 258, 7850, 20255, 18215, 4553, 27875, + 28936, 4334, 9771, 23000, 11798, 18235, 20252, 16061, 5756, 9736, + 21245, 6529, 32065, 3531, 17343, 23131, 24459, 23973, 16460, 12198, + 20764, 15836, 25890, 17390, 4637, 28299, 12353, 8779, 12826, 28312, + 23624, 13549, 25679, 3815, 30427, 4601, 26914, 17869, 13895, 2708, + 12528, 22216, 21097, 12348, 7180, 8813, 25804, 17503, 31749, 12961, + 5137, 17958, 14888, 22284, 3732, 4754, 18246, 26059, 6220, 19147, + 14520, 29797, 12275, 29877, 13983, 7425, 3943, 2499, 8048, 9174, + 2897, 26640, 1201, 4322, 5636, 22956, 25853, 4575, 27048, 9779, + 18254, 14451, 7575, 25253, 7036, 26791, 11015, 29553, 1024, 2970, + 9450, 29907, 27902, 23159, 10002, 32096, 5251, 21924, 16537, 28384, + 26635, 15420, 23936, 18779, 23366, 6080, 29590, 13431, 7195, 12297, + 30363, 13057, 18414, 31617, 22438, 13513, 1566, 24467, 5060, 31411, + 11218, 21856, 5602, 267, 19702, 18100, 28653, 25625, 24192, 14136, + 12087, 29429, 19960, 3561, 20509, 25757, 12855, 4808, 5351, 25413, + 31087, 29907, 17391, 25070, 14443, 27701, 4358, 28730, 32536, 19642, + 13843, 127, 22085, 7301, 32509, 21530, 19111, 773, 19659, 518, + 10528, 9253, 21514, 19373, 20544, 15299, 6767, 13309, 15117, 3428, + 30654, 3263, 10495, 31782, 7658, 28196, 1871, 24310, 27774, 19617, + 32626, 25858, 4923, 8445, 20192, 7793, 18435, 22185, 19481, 32082, + 5110, 29775, 15594, 25299, 31706, 12973, 25068, 3252, 1984, 30116, + 23375, 30459, 28132, 23436, 19157, 1512, 25814, 22219, 22305, 3115, + 24791, 1446, 29746, 341, 16518, 15447, 5419, 31885, 11452, 30047, + 11093, 18308, 12212, 7197, 5734, 7595, 18287, 26016, 27140, 27555, + 30550, 11194, 11397, 8948, 7458, 32341, 29433, 30211, 3321, 24330, + 32362, 4158, 14553, 23280, 15970, 7834, 26426, 30386, 23726, 15423, + 15599, 8421, 13922, 6017, 7758, 7832, 26141, 25363, 11152, 9772, + 17294, 26487, 23675, 7270, 31799, 18617, 32574, 10178, 10609, 21597, + 7836, 3594, 1969, 27542, 4940, 4136, 13065, 23993, 17568, 14393, + 3314, 18160, 23654, 19938, 12104, 18227, 29196, 25831, 4246, 9588, + 28935, 14841, 15764, 13082, 15988, 20944, 19438, 945, 25088, 3706, + 27207, 19680, 13830, 12019, 12670, 667, 25336, 27893, 6960, 921, + 10752, 11537, 16766, 27145, 17127, 29951, 19472, 21633, 20789, 7219, + 11865, 15159, 13466, 27312, 6941, 9587, 14476, 6689, 20785, 21316, + 26418, 27942, 9603, 12147, 897, 20716, 28250, 7605, 28325, 15616, + 13278, 31012, 31906, 17765, 18906, 22256, 31803, 4836, 14017, 24914, + 30227, 13731, 3055, 24258, 26386, 1529, 4914, 14698, 31242, 1921, + 30089, 6874, 1178, 621, 16171, 28669, 19283, 18205, 25803, 26956, + 20762, 20465, 11803, 19265, 13773, 13659, 10639, 8975, 26666, 10296, + 3427, 13200, 12012, 26208, 10719, 11903, 19766, 23931, 20698, 3559, + 17973, 28695, 22141, 8667, 1026, 4344, 3913, 9832, 26053, 10415, + 28507, 30152, 31008, 31087, 12015, 10968, 25291, 29979, 6180, 18381, + 14971, 14755, 25087, 13602, 24073, 10129, 15537, 11476, 32720, 18143, + 29469, 26066, 29783, 28172, 17813, 5039, 816, 24431, 25060, 3739, + 16159, 20682, 7314, 14225, 10623, 4386, 20323, 27858, 9442, 16590, + 289, 8849, 31480, 20431, 15003, 29680, 31154, 13919, 11502, 16427, + 27558, 7116, 3331, 10259, 28598, 24455, 18521, 32703, 29676, 14456, + 30469, 2174, 22441, 24213, 12023, 1807, 30975, 29363, 7294, 2581, + 12635, 6300, 1039, 29893, 1383, 30561, 4418, 8674, 3118, 5106, + 5917, 13035, 20866, 14827, 9621, 24467, 14234, 26289, 6226, 22897, + 7168, 27326, 691, 6137, 17323, 13625, 7442, 8363, 21095, 32202, + 5961, 8074, 20862, 16556, 29812, 25552, 2449, 16644, 21497, 5344, + 13255, 1393, 19398, 13180, 17962, 740, 14298, 15160, 24397, 27058, + 8967, 30766, 5426, 18663, 11510, 25844, 11116, 31710, 4459, 9419, + 29825, 5646, 13514, 28185, 25234, 16679, 23003, 31002, 2878, 16734, + 25802, 18130, 8899, 5124, 20338, 21574, 12902, 23646, 13018, 3563, + 3606, 4785, 2560, 5983, 19788, 10652, 4566, 15191, 19253, 7594, + 27275, 6105, 29203, 9000, 24075, 23211, 21985, 26164, 28617, 8715, + 25141, 4375, 6588, 21920, 8043, 3285, 23207, 2993, 21248, 12290, + 31134, 20117, 32572, 20438, 8902, 22122, 1556, 26500, 16732, 2826, + 29518, 18888, 15097, 25147, 12774, 4287, 28692, 28198, 12465, 16380, + 9672, 12299, 14912, 19672, 15575, 818, 1031, 265, 23024, 13349, + 14526, 27511, 25389, 6172, 520, 21323, 30432, 14521, 21843, 24904, + 16271, 26613, 2047, 29534, 18749, 20571, 28354, 20092, 15339, 21876, + 3147, 8871, 8031, 22658, 4901, 5449, 3108, 13680, 16095, 17867, + 28114, 3082, 18538, 101, 14992, 10602, 31170, 4917, 17366, 9650, + 9424, 31119, 1144, 2561, 400, 20885, 4064, 4767, 14191, 20487, + 1690, 24955, 5623, 9820, 13335, 13528, 16557, 3857, 14762, 30275, + 21050, 29761, 27351, 13768, 15050, 25718, 16077, 25957, 23986, 18879, + 31537, 26815, 12885, 5890, 31773, 19732, 27720, 24468, 15721, 15729, + 28340, 3335, 2345, 1829, 3836, 10051, 28534, 17013, 27296, 25304, + 27506, 29585, 21602, 27027, 22918, 14279, 7335, 4432, 8942, 21239, + 14623, 29966, 17016, 12615, 4008, 9289, 1672, 12567, 26736, 23684, + 13408, 18744, 19290, 293, 28830, 24408, 19620, 17797, 4358, 25281, + 3270, 15688, 15234, 32580, 28573, 18154, 30311, 31855, 20168, 1179, + 3578, 7431, 11091, 22349, 29491, 4786, 8850, 4107, 30657, 23027, + 10832, 19340, 2893, 4699, 22570, 11272, 24067, 9982, 14389, 25930, + 3151, 23333, 30439, 20231, 14308, 3906, 25140, 29038, 12803, 21033, + 1527, 16384, 6906, 4732, 26516, 30370, 6891, 1127, 9435, 19696, + 6094, 3982, 31280, 29653, 3257, 22852, 29673, 4440, 26207, 16126, + 20216, 11961, 2264, 21767, 19658, 19645, 25248, 15733, 32367, 4678, + 12513, 27483, 19194, 18726, 22773, 9792, 13611, 7477, 31526, 25040, + 9291, 20898, 8889, 15826, 13400, 24710, 14124, 16986, 20834, 14527, + 4795, 11328, 8757, 3579, 25129, 24016, 1592, 28435, 12732, 18371, + 10901, 5787, 19338, 24175, 31386, 10679, 19187, 28474, 28512, 22831, + 2415, 27220, 682, 6342, 18570, 29862, 18703, 24776, 15660, 22021, + 30556, 11747, 24606, 6060, 22324, 13111, 10186, 26345, 28409, 29360, + 3906, 4118, 24816, 12180, 3716, 9675, 17823, 28504, 26621, 10322, + 1402, 31928, 28491, 16731, 30143, 8455, 11591, 26609, 15519, 1987, + 1347, 173, 16542, 108, 16272, 20010, 10044, 4758, 7492, 17848, + 9941, 6206, 22841, 7253, 12230, 21082, 14937, 8153, 12497, 11187, + 6937, 12400, 31427, 7671, 2628, 28237, 1875, 27914, 26874, 28573, + 10062, 11422, 10744, 4794, 23537, 17411, 5658, 16546, 24470, 25664, + 14877, 13223, 8596, 9314, 25002, 5736, 14688, 14356, 17296, 3839, + 5368, 29808, 17234, 2684, 26241, 5909, 27617, 23699, 5422, 890, + 1962, 1937, 13669, 27266, 31187, 16532, 19485, 4176, 15554, 1020, + 24971, 9308, 10720, 19997, 9981, 24518, 28316, 19510, 13318, 24043, + 21170, 6567, 7899, 25399, 13229, 23902, 29693, 1594, 22954, 23443, + 20760, 28413, 2400, 20405, 10027, 8342, 30557, 8868, 26602, 250, + 146, 20768, 8801, 4883, 19474, 5762, 21209, 5906, 14871, 12201, + 27135, 1287, 17478, 19000, 20291, 5550, 13108, 29695, 25215, 19852, + 19752, 7862, 16601, 15931, 25123, 3057, 20714, 29187, 22813, 13648, + 23072, 13738, 8850, 1270, 11104, 13452, 13373, 29245, 5360, 6894, + 29220, 22184, 14741, 12943, 10452, 3866, 31480, 11648, 31982, 15685, + 1358, 4918, 29666, 15716, 15928, 990, 31227, 9261, 6888, 17904, + 2460, 22785, 8582, 20999, 22473, 8295, 6610, 20720, 10728, 10936, + 12554, 16384, 14537, 31516, 5390, 12203, 17637, 15028, 19138, 20563, + 16355, 19481, 17332, 16734, 12129, 13062, 30463, 22155, 11504, 9297, + 367, 18310, 10080, 21024, 4325, 32186, 18751, 18521, 7933, 29798, + 10396, 27994, 16111, 24307, 9232, 31467, 691, 2437, 4352, 880, + 15209, 10192, 7964, 22372, 6391, 1581, 5419, 24322, 27892, 20716, + 19211, 15429, 5317, 26092, 1267, 18707, 20229, 25706, 5116, 17845, + 43, 28439, 24544, 19339, 10854, 21613, 15873, 22526, 7372, 4418, + 14765, 949, 7604, 24990, 12704, 14860, 5216, 21450, 28362, 1218, + 30176, 4521, 6227, 16369, 31026, 21579, 10461, 19012, 22190, 13760, + 18697, 1380, 5794, 21802, 13116, 15027, 13113, 278, 1187, 14397, + 4513, 23565, 8052, 14828, 5740, 12133, 631, 29435, 22839, 24669, + 14790, 19372, 15242, 15210, 19665, 10046, 26368, 2507, 19876, 17945, + 30376, 9571, 28013, 6221, 20394, 32456, 14904, 5178, 27950, 18840, + 27134, 26000, 9081, 17164, 22238, 2171, 7769, 18127, 4266, 9495, + 7876, 9744, 10149, 16170, 15292, 29910, 5259, 3602, 10869, 16787, + 25042, 4348, 20922, 27389, 31280, 13143, 11405, 2073, 16477, 5513, + 16398, 5784, 21530, 19936, 10398, 25447, 10354, 13771, 579, 24962, + 20288, 5962, 12366, 25353, 7665, 20131, 18492, 26773, 3979, 13399, + 9179, 31436, 2030, 25428, 24238, 16947, 23643, 12140, 17041, 20804, + 17714, 24582, 26528, 21697, 7424, 787, 2902, 7751, 4280, 27716, + 25330, 15654, 9537, 13434, 27935, 6512, 13873, 19839, 1747, 17744, + 20953, 32243, 18296, 30877, 5761, 22921, 26698, 27311, 29940, 2419, + 15104, 32370, 31808, 31002, 29614, 29880, 4791, 15123, 18922, 6356, + 16441, 14926, 13614, 7012, 28086, 2961, 18557, 3950, 15854, 29477, + 29001, 15791, 25467, 29392, 7573, 16943, 6162, 29699, 3415, 10265, + 6406, 25171, 21280, 30666, 19725, 18989, 18430, 27432, 30535, 20422, + 30370, 15413, 7036, 4690, 16830, 16868, 30630, 20342, 18865, 20267, + 13999, 23295, 21862, 2137, 16507, 16084, 17017, 8874, 19241, 5929, + 16540, 16920, 4502, 32502, 23610, 26997, 21283, 8360, 7258, 12471, + 185, 16150, 14277, 13715, 15114, 19126, 9579, 31309, 1561, 22289, + 3075, 9934, 22012, 1140, 21746, 24567, 22752, 12777, 26222, 10549, + 8783, 2917, 18659, 1109, 7086, 18499, 15253, 17306, 24074, 11761, + 31880, 16909, 22804, 10986, 18333, 30229, 27497, 7652, 20733, 4679, + 20503, 7498, 16058, 10367, 1373, 21228, 26036, 816, 4275, 16374, + 19898, 10488, 17418, 32030, 16626, 15992, 26709, 19234, 27259, 14946, + 29000, 1906, 18512, 8147, 7776, 22825, 29808, 13028, 11555, 10818, + 2364, 23420, 169, 16649, 9504, 10856, 25179, 3535, 20853, 13485, + 16379, 226, 20646, 14620, 2789, 31933, 6034, 17448, 7316, 14459, + 30258, 17909, 17511, 9874, 13180, 20145, 25253, 13245, 8327, 25895, + 13504, 29242, 26351, 11153, 14038, 1334, 5755, 18094, 15997, 24710, + 13821, 15125, 12992, 12439, 23418, 13513, 1381, 32424, 3979, 27917, + 20850, 25910, 3, 8826, 17135, 26808, 17874, 22350, 11244, 18573, + 16938, 18951, 27334, 23603, 22240, 23601, 1447, 20665, 32276, 23420, + 8012, 23159, 1300, 24849, 10507, 12028, 28286, 15142, 30228, 23368, + 16293, 5328, 5111, 19004, 15670, 20505, 3174, 3918, 23324, 18003, + 29756, 18817, 25504, 25260, 27583, 12949, 6613, 19202, 29730, 11898, + 23347, 2808, 15951, 28427, 1154, 30025, 22243, 32761, 7674, 12124, + 21369, 20229, 4776, 6937, 25899, 1139, 30483, 4860, 29782, 16315, + 8992, 11367, 10654, 3201, 9011, 29757, 2098, 9159, 17559, 22956, + 32081, 19810, 16672, 15178, 2318, 30122, 19982, 19044, 25680, 19318, + 4661, 14482, 18460, 31304, 18267, 25487, 14871, 21700, 2786, 29512, + 582, 871, 8615, 7731, 11801, 2933, 13097, 2018, 17576, 16947, + 16366, 28120, 4156, 87, 22306, 5787, 8465, 10685, 20354, 14091, + 27994, 23899, 1912, 16881, 12934, 24951, 28807, 21979, 31682, 8215, + 27114, 15656, 29119, 18362, 11896, 13407, 22453, 25669, 22987, 27333, + 22952, 1088, 18593, 28974, 4074, 15938, 22658, 2835, 23521, 10922, + 19108, 5605, 522, 9616, 19979, 31393, 9534, 27900, 29940, 23973, + 1844, 28501, 6516, 194, 12872, 24092, 30045, 2962, 28794, 22746, + 29025, 11787, 13242, 17494, 28538, 20821, 21347, 8716, 5257, 19161, + 17157, 31707, 21821, 17819, 9831, 14334, 18998, 21961, 26316, 26941, + 3946, 27284, 11638, 28516, 13000, 4670, 26473, 31733, 30236, 17524, + 13937, 20250, 26675, 9951, 29542, 16605, 6463, 32758, 9647, 27291, + 28284, 19478, 5125, 1206, 22804, 10452, 21113, 19595, 29761, 2706, + 29113, 1700, 20145, 28752, 13199, 28974, 25390, 2501, 26794, 15021, + 1150, 942, 23457, 20718, 16520, 28263, 27957, 7229, 32441, 29368, + 17879, 31449, 23974, 18673, 16490, 4261, 5107, 9785, 29257, 16160, + 22643, 5155, 29836, 22935, 5520, 26242, 21127, 27034, 27630, 17279, + 16788, 6060, 18507, 29160, 1555, 3134, 4266, 25035, 10670, 23775, + 11440, 9028, 26338, 22957, 27982, 16275, 28090, 20239, 6048, 10353, + 26078, 4006, 5909, 12709, 20941, 23168, 6865, 18642, 14183, 29071, + 8783, 24467, 12718, 29786, 15691, 15305, 20429, 21235, 735, 8756, + 25946, 27945, 1004, 3905, 12248, 12798, 15547, 29219, 30080, 21340, + 18916, 7717, 321, 25091, 25711, 26175, 8197, 26054, 26938, 5863, + 822, 17911, 28570, 16937, 11392, 5664, 13125, 4602, 18137, 6899, + 21872, 5322, 4287, 7044, 14644, 14424, 15297, 21174, 32566, 26314, + 25921, 5363, 25735, 31229, 3192, 32289, 2249, 31002, 10683, 15313, + 24468, 21610, 19196, 18361, 20760, 4971, 22931, 9828, 19971, 10531, + 27967, 32296, 23205, 25268, 20583, 6745, 21841, 9265, 6566, 11964, + 31751, 26544, 32391, 28478, 30033, 12960, 17293, 20454, 24683, 30045, + 28023, 265, 12454, 2306, 6081, 31677, 22542, 16255, 5230, 12943, + 1355, 14827, 14597, 14969, 30917, 31003, 31962, 14, 13580, 23667, + 19598, 23800, 3702, 764, 14041, 8612, 24074, 17079, 22151, 26428, + 23452, 10801, 7617, 15830, 25402, 16472, 26254, 2795, 10695, 30349, + 27802, 11274, 9446, 250, 5173, 12749, 10778, 2342, 8862, 18189, + 2347, 28358, 19330, 6226, 9675, 2775, 14272, 20365, 21625, 10473, + 26991, 30730, 32422, 10942, 8482, 24224, 21014, 2579, 10346, 16939, + 32650, 11454, 9629, 26684, 19612, 16121, 23766, 27150, 31978, 2046, + 573, 19900, 22134, 27860, 393, 16306, 17741, 5480, 24259, 97, + 25191, 26591, 5196, 23096, 25039, 20011, 4641, 13263, 11267, 31196, + 16990, 4709, 9886, 6713, 14081, 8332, 28724, 14480, 25841, 3363, + 4990, 11978, 4159, 22149, 30135, 27714, 19883, 22016, 1093, 31960, + 15722, 8073, 26012, 1601, 25382, 2477, 12640, 12193, 16199, 29131, + 29641, 10075, 3441, 4225, 76, 15143, 19533, 29284, 22392, 13926, + 29325, 26868, 9246, 31411, 29994, 1999, 5044, 1723, 752, 548, + 3579, 24398, 6461, 31851, 4064, 3766, 22022, 9811, 11680, 11847, + 11034, 12540, 48, 11831, 22187, 30055, 476, 20400, 17674, 24363, + 7726, 1316, 16306, 28521, 30993, 11489, 3464, 27649, 12598, 23013, + 26721, 3846, 8631, 7923, 27589, 19056, 26363, 4070, 24819, 29713, + 10969, 27974, 3108, 28107, 31744, 11801, 24340, 7679, 13462, 23547, + 13503, 19528, 13343, 30065, 31336, 13930, 28833, 19067, 27104, 29061, + 8965, 1896, 14597, 27535, 4801, 25070, 25225, 8448, 17588, 7290, + 27637, 23247, 4433, 21445, 19693, 2206, 26112, 19112, 23477, 2866, + 13761, 10289, 25332, 805, 23827, 23542, 17592, 30687, 32020, 7337, + 20155, 27330, 4585, 13549, 9189, 10917, 18528, 4712, 24337, 31620, + 22228, 19226, 21268, 26229, 23890, 3309, 3067, 17886, 4345, 11149, + 30101, 25062, 13991, 16505, 19250, 12585, 12498, 4443, 13481, 4528, + 9395, 12437, 13472, 3616, 31402, 3100, 19463, 817, 15838, 24201, + 30438, 14690, 7972, 13933, 26201, 18156, 7970, 2021, 7128, 19956, + 9140, 31855, 18876, 27396, 31886, 29948, 7423, 26556, 6947, 29255, + 21572, 1477, 454, 22372, 18455, 30215, 11791, 15004, 18549, 20508, + 641, 14456, 29583, 7684, 29545, 32028, 27083, 20506, 31497, 30678, + 780, 11590, 6390, 27895, 26931, 4981, 24767, 23010, 15334, 7238, + 23503, 24418, 8592, 31238, 17045, 9793, 27048, 8415, 20706, 29206, + 9271, 4172, 14738, 22110, 21140, 20688, 32110, 16846, 5490, 31208, + 10577, 21480, 31773, 21566, 16266, 12493, 22785, 27679, 20610, 18258, + 16236, 10287, 1695, 15701, 3733, 14428, 10599, 30290, 11740, 28944, + 23529, 26673, 24147, 26283, 6744, 12898, 17790, 23856, 15098, 11052, + 5463, 24638, 30272, 22338, 30101, 5955, 14903, 32664, 4654, 10732, + 19743, 22900, 29467, 18656, 856, 29896, 347, 11949, 3206, 25791, + 23866, 11522, 7149, 22166, 27091, 6688, 28701, 2733, 12756, 12939, + 32212, 25566, 2433, 17157, 16928, 269, 4604, 11964, 32708, 5376, + 451, 9667, 27613, 28785, 22138, 8127, 3525, 29282, 21483, 28387, + 22072, 21169, 29771, 32542, 12725, 31468, 11710, 5522, 6695, 24934, + 12574, 9289, 27589, 6795, 1528, 1571, 311, 5232, 10200, 14099, + 23290, 12463, 2247, 23691, 22021, 12401, 7441, 12853, 13741, 9932, + 21580, 12004, 1222, 25936, 9109, 30154, 4619, 5852, 1309, 26603, + 3008, 31926, 4325, 27089, 32405, 475, 4805, 12674, 15310, 6292, + 16819, 12463, 27297, 5706, 31810, 28462, 18655, 411, 11881, 27051, + 24279, 1710, 17542, 18586, 10872, 1475, 20386, 879, 1461, 28686, + 16119, 17426, 19502, 26309, 21167, 7821, 1036, 20600, 11247, 28936, + 3628, 22689, 7700, 32760, 8577, 3452, 9533, 8851, 5555, 4120, + 9737, 4999, 16652, 2734, 23202, 14827, 28053, 24883, 4346, 32374, + 10620, 20521, 7764, 239, 30393, 28090, 11271, 16868, 7665, 16647, + 16766, 5279, 10395, 25534, 12164, 27990, 18017, 25066, 16574, 24482, + 8649, 19472, 21369, 24882, 26644, 12151, 16551, 9409, 2151, 26823, + 13347, 8517, 28200, 1238, 23101, 6506, 15019, 9150, 1650, 14142, + 16910, 4659, 14203, 4142, 20321, 3347, 7306, 9521, 29706, 25407, + 32720, 13123, 26216, 27050, 14914, 26645, 13239, 30127, 5277, 20227, + 18740, 20904, 14759, 2630, 6273, 24508, 7608, 11371, 23370, 24312, + 32634, 17923, 14889, 1697, 26093, 31645, 23239, 27593, 16302, 14321, + 6469, 25889, 1912, 20062, 28890, 24846, 4587, 8599, 22461, 25087, + 26764, 8857, 4677, 5373, 22030, 15328, 16718, 13956, 2000, 31962, + 12256, 14737, 26790, 6556, 9227, 25852, 6364, 4820, 14101, 23304, + 10104, 6654, 30936, 16185, 28864, 1499, 25189, 28261, 17916, 27423, + 23533, 32014, 17021, 11626, 23010, 28655, 29150, 7290, 21849, 25301, + 11146, 29087, 9139, 4528, 18302, 16751, 29052, 30698, 4177, 13754, + 25747, 31223, 5745, 9153, 10635, 19758, 18607, 21154, 28754, 4567, + 30206, 1559, 7184, 21381, 7287, 8544, 10834, 25510, 10657, 30334, + 5480, 16984, 4080, 5538, 13835, 2430, 21899, 12517, 26692, 3603, + 82, 26681, 30601, 11199, 800, 4512, 5672, 28328, 4173, 20892, + 25577, 3979, 30072, 6118, 18414, 30219, 25731, 21602, 8671, 13641, + 19934, 30803, 5700, 14004, 12297, 6092, 31282, 1827, 84, 16231, + 8746, 31265, 4330, 21048, 6206, 6023, 6157, 1028, 32117, 1873, + 15523, 17327, 6769, 28586, 19442, 10740, 25423, 31049, 31912, 16473, + 29391, 28160, 25374, 13409, 16565, 9618, 31453, 19203, 5999, 13510, + 26978, 10331, 3319, 4793, 24094, 16288, 763, 10360, 13249, 32598, + 29244, 32462, 14008, 3524, 1332, 32347, 13556, 10665, 17915, 30182, + 15086, 811, 25451, 23285, 8680, 8464, 20867, 30874, 21365, 9776, + 18697, 143, 26171, 2858, 27251, 12933, 30712, 8279, 2056, 3514, + 26819, 27644, 23363, 16586, 23445, 14532, 10616, 19399, 8199, 26733, + 7097, 2187, 26973, 24257, 19963, 3405, 30404, 6344, 22901, 7539, + 4312, 17468, 24137, 7335, 105, 27453, 20582, 14381, 25215, 2118, + 2989, 14964, 32155, 5728, 29523, 15595, 31808, 29077, 8326, 11285, + 12466, 7080, 25683, 20960, 17918, 14595, 8391, 23091, 570, 23025, + 15700, 1478, 32270, 25613, 14967, 32566, 30068, 12283, 10392, 1625, + 5563, 12117, 1586, 26063, 27858, 12008, 18739, 26329, 24003, 2337, + 6054, 14190, 15998, 23746, 29583, 30348, 728, 7129, 24086, 23245, + 6355, 1779, 13800, 10042, 23072, 26765, 21723, 25187, 14050, 9419, + 12497, 944, 32181, 29037, 990, 2802, 25839, 18942, 16301, 13885, + 14630, 29774, 20511, 18938, 21746, 10531, 28982, 7992, 17907, 13612, + 7218, 27715, 17174, 9782, 20473, 31608, 22549, 22690, 14524, 17495, + 27197, 30999, 31448, 6874, 31348, 20084, 9466, 26885, 18554, 24585, + 3463, 9919, 15215, 19462, 7995, 992, 13206, 25139, 22800, 30859, + 10584, 22242, 1377, 12222, 15756, 8739, 27727, 39, 7925, 24374, + 1380, 26426, 26892, 16454, 27766, 14976, 24576, 27220, 7630, 818, + 11772, 19825, 15754, 14342, 1448, 19760, 28224, 18761, 19954, 19803, + 31460, 13102, 15118, 30437, 22508, 2662, 30907, 18720, 16305, 14064, + 18889, 19248, 28185, 20787, 16600, 29120, 16243, 21834, 30306, 3315, + 13986, 8351, 24572, 21149, 11902, 28091, 11480, 22708, 14967, 16988, + 19343, 5121, 31356, 968, 32029, 12059, 26139, 9570, 6445, 19862, + 21225, 9595, 26520, 3960, 14884, 19562, 13096, 26860, 407, 25055, + 31137, 8763, 19390, 19572, 23810, 5111, 4907, 20357, 14856, 29562, + 5416, 23362, 2036, 17295, 20382, 8962, 25998, 15726, 5685, 28123, + 30570, 17902, 701, 21871, 31594, 17142, 17613, 26376, 13700, 25908, + 5289, 23272, 6597, 14866, 19817, 21978, 22422, 6630, 29272, 26893, + 10815, 18025, 9115, 17442, 4777, 19819, 17939, 19844, 24168, 965, + 25220, 5368, 8184, 11406, 32758, 21621, 31815, 8338, 32091, 29493, + 29044, 2559, 11891, 16140, 31447, 2146, 16365, 20473, 13889, 20690, + 9935, 18939, 28293, 7787, 7959, 28965, 12087, 1791, 32162, 17397, + 17843, 2055, 22862, 4066, 21345, 24544, 20316, 13364, 21816, 1844, + 26392, 1816, 17537, 24246, 22715, 4340, 13534, 25874, 15248, 4600, + 5002, 2339, 24406, 1526, 27618, 27751, 21734, 6771, 21797, 10673, + 27157, 19198, 28024, 29726, 7388, 6080, 1581, 8778, 18870, 2645, + 25924, 31172, 6474, 31962, 16434, 20159, 29002, 22343, 21474, 19466, + 27803, 14079, 15866, 30530, 7862, 13478, 31583, 17734, 9806, 15411, + 12283, 21227, 21299, 11078, 7102, 5617, 25462, 10683, 24802, 6710, + 7118, 28799, 32628, 7978, 16186, 29346, 10768, 10627, 10728, 7342, + 22508, 23858, 4950, 7736, 14359, 13205, 15287, 29108, 27139, 25070, + 2200, 5774, 8002, 1044, 21699, 12662, 2701, 24424, 30563, 1787, + 16394, 21003, 18026, 1115, 27343, 16974, 6192, 19097, 31650, 8810, + 2140, 28625, 13570, 23666, 1032, 27337, 14444, 14981, 24864, 23752, + 30935, 8008, 1291, 11691, 13953, 8851, 24206, 11829, 26605, 26219, + 14061, 13479, 13356, 14385, 7763, 26512, 25181, 21016, 30579, 10983, + 25200, 20701, 26224, 25431, 21295, 12722, 9978, 986, 10622, 27771, + 3515, 6537, 23777, 32316, 14577, 18867, 15680, 30674, 13404, 30252, + 21991, 25826, 5210, 31719, 465, 7952, 19537, 16327, 5414, 24945, + 17485, 32179, 16386, 12354, 29162, 3561, 3704, 26842, 30837, 5268, + 10711, 3549, 5198, 20384, 16171, 9139, 21904, 30413, 12039, 12600, + 28148, 25090, 27266, 14517, 5619, 27109, 8576, 15632, 19755, 13992, + 9779, 10047, 31236, 28965, 10721, 8194, 11411, 2606, 14055, 12754, + 2239, 3102, 3070, 18196, 7074, 10932, 15549, 22675, 8242, 1314, + 15199, 24621, 30496, 20064, 12231, 25549, 22016, 20422, 16321, 11417, + 29099, 31194, 11403, 32364, 10209, 30567, 27028, 25478, 27699, 30415, + 4227, 24706, 31776, 7585, 22139, 9353, 7807, 23150, 26269, 2502, + 29213, 2265, 17871, 31257, 4006, 7150, 9148, 3088, 713, 17077, + 15644, 27667, 24215, 15110, 13110, 26078, 28972, 18078, 31907, 8757, + 23186, 30323, 21332, 27858, 2786, 16453, 17367, 5795, 27564, 4726, + 16853, 18482, 14489, 23067, 26080, 22629, 19279, 12115, 14443, 31591, + 24213, 7128, 11802, 24038, 3256, 12243, 14653, 127, 3373, 22773, + 5271, 26486, 30071, 12227, 11107, 29355, 332, 12081, 17049, 29129, + 9715, 2675, 18285, 18625, 29936, 5392, 26392, 14455, 4579, 28984, + 21468, 8122, 16302, 30807, 23362, 18554, 18646, 4586, 12711, 5429, + 18220, 27794, 5878, 8883, 3756, 19814, 14519, 12787, 16293, 27022, + 30025, 28261, 17434, 32270, 25707, 25690, 15464, 6623, 18690, 15288, + 7978, 20036, 20502, 2802, 4186, 14683, 16669, 22071, 13241, 7922, + 892, 3818, 32368, 925, 7499, 10067, 19447, 32261, 3928, 3732, + 14902, 12931, 12911, 10462, 4330, 11502, 31193, 6116, 14411, 3249, + 16163, 26512, 748, 14112, 31521, 29429, 20194, 1036, 4145, 5510, + 16936, 6292, 2500, 31877, 9794, 25663, 29979, 11454, 10017, 29768, + 14159, 4212, 31047, 4409, 1172, 8604, 11051, 23227, 23742, 1097, + 9695, 21933, 12549, 9576, 8432, 8307, 5893, 12717, 3470, 16785, + 11529, 30925, 15596, 17528, 5000, 4334, 17898, 6303, 8176, 20059, + 28146, 26326, 11427, 25469, 6431, 20696, 10815, 15623, 1445, 20255, + 22051, 28272, 22125, 8444, 14895, 17062, 12543, 742, 22986, 17665, + 29349, 7955, 12330, 22133, 16589, 27717, 25552, 8973, 30896, 28391, + 21329, 2964, 16945, 4184, 24412, 22421, 18952, 135, 5625, 17287, + 26399, 21952, 8975, 16193, 12092, 24261, 10793, 28852, 16097, 23329, + 28025, 20184, 14449, 3513, 26142, 26855, 16019, 23972, 12706, 11722, + 28123, 31036, 30919, 18384, 6568, 12149, 13187, 24742, 20552, 29550, + 28233, 24964, 27235, 15942, 29834, 932, 10646, 17201, 1148, 17656, + 10846, 10699, 28698, 5404, 11576, 17068, 20332, 20106, 31494, 27196, + 10248, 20493, 26805, 3528, 11737, 19064, 15901, 1750, 287, 5783, + 1064, 16566, 3511, 10086, 32311, 23642, 28573, 27777, 21185, 18024, + 175, 14556, 10045, 29580, 29619, 436, 13123, 14099, 8348, 2501, + 3822, 26550, 26121, 19646, 16516, 18099, 17423, 16114, 19801, 23843, + 5176, 13648, 20976, 17287, 715, 2453, 25956, 1956, 11598, 20160, + 19462, 9361, 2634, 10694, 15688, 16654, 32334, 8840, 16602, 10263, + 16950, 258, 26386, 12217, 32429, 2757, 24721, 549, 16625, 13479, + 14628, 32003, 11479, 7353, 14824, 6920, 4110, 24697, 14002, 7115, + 15015, 9033, 19117, 24020, 16597, 22816, 18501, 7926, 2546, 10624, + 24644, 5856, 20675, 32424, 17467, 8174, 30973, 5386, 18468, 25157, + 24072, 7935, 18245, 13598, 32514, 20666, 20568, 10850, 11271, 26746, + 23784, 30840, 6320, 1796, 6142, 13914, 30681, 27729, 30717, 7663, + 1192, 27780, 12235, 18581, 7371, 12575, 15153, 6652, 20480, 6773, + 2693, 19771, 2267, 21859, 4486, 22557, 12996, 31035, 15751, 6332, + 319, 16897, 5840, 338, 15472, 15066, 4417, 13899, 14046, 27841, + 20536, 21151, 20599, 25980, 25013, 18014, 10964, 10958, 13500, 909, + 29375, 28143, 20219, 27952, 25330, 4921, 1453, 15528, 25634, 25124, + 15806, 20799, 28220, 29044, 30512, 7682, 3823, 9137, 10173, 10703, + 14698, 18392, 10430, 21934, 17255, 12880, 15435, 23908, 13160, 25119, + 28068, 17990, 17472, 1630, 20687, 12364, 16854, 30698, 25987, 15718, + 8998, 31581, 8517, 787, 1416, 24381, 25809, 14895, 27957, 19059, + 24763, 8460, 28423, 20222, 11606, 17349, 10112, 5352, 6613, 7887, + 8974, 12665, 30384, 20433, 7053, 32428, 24394, 18614, 12046, 1759, + 18128, 14592, 29655, 22734, 19556, 8543, 3499, 22466, 6779, 17794, + 10315, 23558, 21891, 28717, 31548, 14235, 24010, 15756, 15051, 29484, + 26779, 24519, 6735, 22340, 13482, 4566, 17326, 5567, 23985, 11415, + 26658, 10111, 4956, 4637, 20029, 12896, 14179, 25419, 299, 2925, + 9973, 13218, 1336, 10580, 13149, 12120, 13713, 6935, 3552, 26538, + 12111, 22277, 10980, 14707, 28081, 30185, 24664, 508, 31657, 11905, + 17231, 5654, 24219, 9521, 2286, 12134, 7777, 25593, 28297, 17409, + 3730, 3749, 27913, 31801, 15620, 10051, 15249, 15030, 10639, 30426, + 25953, 27336, 11028, 28249, 30458, 2299, 9662, 5070, 1666, 6636, + 30344, 20440, 7019, 860, 14336, 12127, 14672, 31619, 17482, 28034, + 4639, 678, 6247, 22993, 9085, 6892, 31928, 28349, 17254, 12910, + 5821, 11340, 24596, 29581, 18467, 2834, 30499, 28552, 32510, 7004, + 2034, 22350, 23379, 9390, 1356, 13968, 19929, 22896, 2632, 25883, + 5120, 29412, 22125, 8805, 13619, 18779, 21786, 3941, 25946, 15529, + 13349, 31400, 28836, 214, 19907, 12962, 5251, 8982, 27048, 21058, + 27128, 23291, 6511, 15305, 17680, 31722, 13371, 13787, 11695, 6860, + 13727, 24278, 21900, 15365, 955, 27236, 12157, 7594, 24744, 23884, + 3061, 29166, 2839, 15246, 24266, 7646, 21044, 23726, 4668, 20559, + 8401, 23120, 2868, 1300, 17344, 19717, 17097, 18548, 28544, 19301, + 6408, 20187, 18717, 24414, 17653, 13519, 30289, 26059, 1485, 31526, + 30959, 3144, 31118, 4982, 22913, 24901, 15545, 4302, 25904, 10842, + 14167, 20643, 741, 29914, 15162, 12301, 25518, 5291, 22405, 8500, + 4707, 1365, 1391, 13572, 9865, 24920, 4675, 27945, 7798, 25539, + 27307, 27191, 9398, 10242, 11055, 18984, 17582, 7160, 6649, 19212, + 21119, 22606, 28123, 1532, 2606, 21258, 1683, 22096, 10753, 8291, + 31309, 16658, 3116, 13779, 7273, 28850, 940, 25455, 24959, 6394, + 116, 30580, 17568, 22341, 16071, 16418, 4575, 25324, 1509, 24525, + 14864, 8706, 14825, 10033, 5072, 30273, 17650, 27142, 11718, 22083, + 7041, 468, 5672, 1782, 24610, 29449, 15232, 2378, 27215, 26697, + 19120, 4140, 13457, 30725, 11827, 4792, 30184, 26703, 4775, 20138, + 27245, 25836, 5256, 20246, 3894, 23467, 31551, 19290, 21882, 29430, + 9563, 29264, 4341, 20135, 2590, 30152, 9614, 5917, 29755, 27189, + 14392, 30350, 23006, 20134, 26390, 158, 30189, 20728, 19682, 14385, + 1677, 8778, 21391, 10110, 21737, 28594, 6701, 15974, 27183, 21294, + 29198, 27395, 5594, 20702, 14119, 8323, 10551, 13424, 1976, 16439, + 3106, 25307, 7984, 6375, 3857, 29776, 22603, 28777, 7765, 10861, + 6781, 19313, 15004, 5813, 13971, 26539, 23225, 1341, 30360, 7175, + 6824, 32679, 25845, 4949, 23500, 31828, 3805, 5393, 14972, 3542, + 17896, 20608, 31934, 9892, 9470, 24060, 24146, 17682, 23671, 27275, + 24051, 7040, 2432, 16823, 28633, 2257, 7361, 10650, 14722, 29983, + 5181, 17721, 15026, 26284, 3193, 28947, 1977, 25372, 27872, 19961, + 30964, 28898, 13158, 28191, 29590, 19705, 9068, 22644, 12200, 10452, + 23523, 22063, 8015, 1175, 6325, 16042, 23374, 31111, 9056, 13684, + 14539, 2239, 14052, 3746, 27734, 16934, 23264, 19813, 24103, 22429, + 31224, 28898, 5031, 28797, 14201, 829, 21043, 20800, 18496, 7927, + 24348, 11134, 21208, 24187, 242, 10780, 6882, 27123, 9730, 3520, + 31674, 5984, 21533, 28039, 30295, 20714, 17819, 28667, 22131, 13684, + 22432, 4159, 13036, 10100, 11678, 17293, 19819, 15874, 11803, 4835, + 5443, 19503, 15541, 31992, 20892, 29335, 21859, 13429, 14032, 28474, + 27901, 3682, 27413, 8071, 15103, 13534, 15965, 26810, 20618, 29204, + 6522, 31336, 8363, 10090, 26758, 8024, 24032, 17659, 1154, 4245, + 20696, 21832, 24637, 32449, 3195, 28970, 31863, 17836, 9002, 12300, + 26439, 32294, 16712, 26710, 19130, 25105, 20729, 21548, 20157, 14010, + 2857, 28249, 21053, 12649, 6609, 7046, 16268, 2614, 12149, 29616, + 7, 12575, 14322, 16345, 21238, 22872, 1654, 32765, 2995, 20194, + 9424, 3399, 31503, 19039, 10218, 11397, 2033, 14117, 11667, 9434, + 15168, 8081, 26498, 11954, 17155, 13213, 22424, 20219, 12487, 7605, + 8550, 29331, 30529, 6599, 5359, 24060, 27445, 666, 22191, 17751, + 19351, 18784, 18652, 8336, 5359, 2958, 6635, 4054, 9218, 15083, + 7254, 25678, 27566, 3047, 19357, 8456, 16918, 7727, 8843, 2098, + 2303, 4990, 9914, 2187, 13690, 2230, 19801, 7963, 31428, 9519, + 7469, 290, 7274, 13391, 12758, 8782, 11184, 32091, 1108, 27440, + 13364, 19852, 7369, 13297, 7119, 26726, 4480, 22337, 14780, 21061, + 30511, 29073, 26876, 10553, 9849, 8322, 6648, 19, 31415, 22173, + 10089, 28608, 28152, 5956, 29301, 21206, 13894, 12008, 2586, 24429, + 18476, 15229, 25040, 4912, 25205, 6443, 23402, 9652, 1845, 1863, + 10302, 29960, 1550, 6529, 17797, 8333, 31660, 18783, 6149, 17927, + 27282, 15338, 27373, 5365, 20761, 4007, 22016, 11294, 24757, 18120, + 20213, 30061, 14971, 8612, 27166, 29694, 21728, 32101, 3657, 14457, + 28356, 2816, 8164, 28575, 13231, 29291, 26468, 16162, 30042, 13834, + 14055, 25786, 7793, 27242, 4887, 19317, 17047, 5681, 25426, 2895, + 28587, 20053, 27913, 1794, 9289, 25229, 7782, 7199, 3988, 11781, + 27698, 6494, 32307, 24662, 18165, 28466, 6298, 21173, 27487, 20129, + 22155, 15722, 4582, 10835, 19420, 24965, 16817, 19945, 12922, 31436, + 19125, 32656, 21934, 3575, 11038, 24533, 17055, 26400, 25584, 31675, + 25113, 23669, 238, 17674, 24460, 10544, 22459, 25835, 344, 29897, + 16959, 7346, 22087, 23145, 22604, 10491, 23643, 1187, 27777, 23288, + 1227, 12639, 20591, 23561, 14664, 12822, 9211, 20365, 18052, 15429, + 13315, 18169, 20469, 7707, 11333, 15399, 12503, 11125, 8316, 1520, + 28272, 30477, 12491, 31484, 8087, 11246, 19807, 14041, 28023, 6956, + 23889, 13643, 4497, 20945, 19657, 14307, 29952, 19226, 18025, 10707, + 21761, 4836, 7145, 30209, 4274, 27745, 8702, 858, 1050, 20751, + 897, 19305, 5226, 23531, 20429, 16168, 20866, 29922, 18537, 28182, + 20663, 20616, 12775, 9097, 9428, 6156, 11275, 28230, 5982, 19820, + 21808, 17999, 1821, 16357, 15592, 17660, 4055, 9390, 27107, 9037, + 24415, 10213, 30482, 29311, 23503, 2506, 3083, 17117, 32529, 7211, + 13488, 11481, 2980, 13343, 23563, 28763, 32412, 25591, 28285, 7769, + 25323, 4466, 26392, 168, 27104, 15408, 19529, 13330, 27885, 2153, + 24506, 10408, 15533, 25055, 1398, 31833, 24869, 19217, 9807, 25540, + 12107, 11442, 4477, 5451, 22468, 14681, 8583, 27241, 21234, 14714, + 10343, 11077, 20014, 21397, 26345, 14471, 32338, 9389, 4948, 1898, + 31611, 15612, 13132, 9234, 2573, 21733, 19703, 11809, 29943, 20010, + 13151, 901, 23864, 21855, 29207, 3475, 8866, 14239, 7202, 25690, + 18909, 16509, 528, 15409, 944, 13373, 4970, 20883, 14716, 14917, + 28496, 18221, 17161, 19406, 26721, 10804, 10629, 31455, 32731, 4348, + 7114, 9596, 7171, 1991, 30688, 26907, 32200, 16347, 3523, 27223, + 14755, 13527, 32614, 17640, 14273, 15430, 25079, 14291, 32612, 13173, + 1125, 25054, 20871, 16258, 32544, 8775, 22209, 21430, 28960, 29109, + 25707, 29204, 3624, 18287, 7429, 23090, 25164, 24268, 28630, 6728, + 28337, 28907, 15315, 13959, 916, 13987, 6594, 23212, 31629, 18109, + 4511, 18900, 15381, 9313, 16191, 146, 8007, 4559, 10803, 15679, + 10979, 31568, 21743, 25233, 14488, 3691, 18220, 26735, 29571, 20827, + 6026, 20228, 20211, 20161, 14458, 18451, 23151, 10647, 3163, 2698, + 5882, 11621, 1251, 1113, 12693, 11959, 20929, 24196, 18182, 20430, + 25216, 1911, 5336, 32150, 16303, 14235, 15267, 14552, 32575, 9254, + 30349, 32689, 22572, 21559, 27665, 27459, 6496, 19490, 31743, 21930, + 2004, 776, 11972, 7217, 26616, 22188, 10732, 20934, 13909, 29057, + 19941, 30124, 21360, 22280, 24838, 624, 9970, 2648, 27599, 26910, + 32263, 766, 10867, 19636, 10500, 25608, 14227, 4567, 7244, 10589, + 21581, 5829, 6260, 16208, 29045, 10123, 7589, 14452, 24098, 632, + 11842, 5553, 16237, 209, 9419, 21655, 13685, 14278, 8450, 14323, + 20903, 9637, 20556, 32416, 16956, 16206, 5465, 31904, 22274, 29159, + 21255, 19051, 17248, 27857, 24196, 32391, 16788, 18394, 23731, 9561, + 31552, 22964, 3794, 27451, 23333, 21158, 21081, 11471, 11310, 28545, + 22565, 24978, 10672, 22742, 22229, 5300, 25840, 614, 15477, 24615, + 9190, 7898, 9050, 7670, 7488, 31668, 3660, 17368, 13738, 22918, + 12267, 7429, 8781, 5217, 16715, 22088, 5269, 12433, 11234, 4181, + 21304, 8071, 2724, 27742, 25716, 20759, 18028, 18145, 11672, 25828, + 20894, 5316, 11205, 4120, 18186, 5795, 10742, 2904, 28692, 22121, + 6206, 7415, 30226, 8989, 26705, 4969, 17142, 32511, 4934, 17403, + 21805, 13268, 11419, 25026, 2759, 382, 9850, 16593, 10199, 6863, + 11066, 22959, 7197, 25759, 12329, 8457, 19426, 11036, 9609, 1955, + 24612, 16738, 26584, 13972, 25075, 16204, 22101, 23475, 9499, 20873, + 12991, 6733, 17923, 20466, 11872, 29721, 16085, 3610, 18506, 15981, + 22495, 6695, 24801, 30641, 30522, 24033, 9881, 4373, 5033, 22025, + 16385, 127, 20163, 21484, 22273, 11975, 31317, 32056, 26975, 5671, + 3330, 32269, 648, 9453, 18988, 8535, 17038, 14451, 8472, 17244, + 22125, 17489, 1868, 21137, 17810, 31185, 21052, 21170, 14494, 932, + 21542, 5649, 7527, 10707, 16443, 8495, 19519, 29176, 6655, 27785, + 4730, 4013, 13298, 30900, 17962, 14661, 368, 26796, 18221, 13498, + 16342, 5738, 4458, 12519, 15078, 21940, 17495, 16119, 12397, 22841, + 17343, 30, 5922, 28125, 21954, 23431, 19666, 82, 25117, 20621, + 28602, 8424, 13995, 14637, 26421, 31142, 29996, 4283, 24471, 27289, + 3654, 4821, 15704, 16492, 28105, 18070, 19317, 14649, 15415, 18955, + 10515, 19359, 22916, 29163, 14949, 25621, 29978, 4482, 8401, 3197, + 26400, 32495, 32334, 12797, 12028, 10127, 2065, 32010, 15126, 18617, + 11368, 28613, 24031, 8364, 20454, 16011, 5139, 11205, 26048, 5831, + 59, 16886, 26487, 18443, 16245, 10355, 28599, 864, 31607, 27566, + 31872, 9434, 24018, 5510, 7299, 15215, 18548, 5051, 3023, 17160, + 30577, 25254, 940, 17625, 28675, 20519, 16839, 20974, 24783, 27358, + 2704, 3079, 22377, 29086, 10977, 12454, 28443, 22302, 15584, 23142, + 23778, 11762, 13350, 27790, 31930, 4282, 29985, 23782, 17435, 23194, + 31670, 20426, 15812, 17386, 21803, 14972, 29367, 14126, 28997, 32703, + 25733, 29256, 3757, 15009, 31947, 18412, 18072, 11160, 5629, 5461, + 31655, 31696, 354, 16438, 12300, 19140, 11872, 20724, 1775, 14860, + 22266, 32018, 29968, 27509, 21911, 13805, 25773, 9559, 2053, 4009, + 20646, 4387, 25533, 5227, 6879, 18492, 8041, 22472, 8678, 8968, + 5539, 24190, 22998, 32326, 17664, 11874, 27771, 27298, 4429, 6810, + 9398, 31366, 10776, 8204, 10531, 8756, 14963, 26134, 18938, 24082, + 17908, 14208, 26163, 4932, 22272, 1967, 11246, 24192, 25161, 22555, + 14353, 17694, 918, 4611, 22835, 31855, 30659, 10335, 31161, 15184, + 30728, 5654, 30014, 18943, 23166, 3185, 23865, 17453, 7134, 26063, + 28889, 16724, 13964, 12443, 19508, 22346, 27349, 26236, 4769, 11031, + 23669, 9163, 6242, 1028, 10589, 29514, 20359, 5364, 23987, 531, + 31758, 28663, 26905, 20430, 29519, 22557, 31026, 12071, 27288, 25992, + 23906, 10880, 6420, 12229, 22486, 8418, 12529, 29277, 32709, 12090, + 29633, 24486, 6389, 14786, 26145, 5599, 1152, 10685, 26265, 27260, + 11992, 13159, 11204, 32703, 7945, 14380, 28081, 22664, 2878, 6424, + 16008, 24174, 5767, 3471, 22129, 3598, 7129, 21054, 22193, 15779, + 10839, 17489, 18454, 12760, 5839, 15590, 22678, 29914, 18056, 16578, + 7219, 9136, 5947, 22595, 21794, 9679, 2143, 9110, 1835, 745, + 2473, 32554, 23123, 19207, 4820, 19557, 4281, 17623, 26782, 12949, + 5054, 22087, 13075, 27689, 23656, 23378, 18701, 29729, 15113, 24346, + 21756, 10179, 16923, 27448, 14051, 12432, 22639, 24912, 18339, 19062, + 13959, 4840, 24956, 14919, 21312, 2938, 20945, 2154, 947, 16583, + 6187, 21457, 21086, 7535, 5761, 20778, 20863, 6591, 5815, 19034, + 27841, 22218, 18788, 12839, 32282, 28603, 8664, 8244, 10740, 30057, + 13150, 18739, 18059, 18476, 29630, 18262, 21142, 17392, 1798, 18182, + 16169, 29319, 30858, 27608, 4314, 14404, 1322, 445, 7203, 11973, + 23251, 20089, 24839, 8720, 29001, 5974, 18936, 25570, 24611, 12057, + 20985, 26947, 22039, 8245, 22016, 26206, 5495, 18239, 5534, 23696, + 6652, 29090, 9953, 20159, 19395, 21652, 24954, 4077, 25493, 28744, + 1307, 24588, 25707, 13157, 7971, 19220, 20017, 29368, 29565, 32340, + 28107, 2341, 27845, 2053, 28246, 13284, 22445, 13605, 4016, 32490, + 28064, 387, 9786, 21924, 29349, 19990, 18396, 3051, 27269, 20882, + 19018, 29493, 10544, 30525, 30342, 20488, 16989, 8191, 5543, 17333, + 7281, 31161, 15064, 32322, 10914, 24792, 28323, 19624, 4700, 42, + 8048, 22335, 16355, 8944, 31560, 9334, 11835, 27956, 5855, 13255, + 26181, 28253, 1490, 17642, 31018, 16896, 18477, 8009, 31228, 13049, + 12592, 28085, 15003, 5307, 11091, 599, 20438, 18148, 32135, 5865, + 8403, 5909, 30143, 23846, 1457, 29952, 7343, 29566, 14699, 11458, + 22227, 4623, 8960, 7028, 5812, 977, 14801, 4604, 1127, 16400, + 2378, 23081, 15958, 14368, 6084, 4608, 5955, 11596, 13482, 20816, + 30089, 8975, 29147, 19853, 14245, 16775, 19536, 28634, 16886, 15398, + 18431, 4456, 6593, 4267, 767, 20991, 12664, 12880, 1004, 15578, + 19004, 30021, 18317, 12216, 3744, 20062, 20916, 14384, 19120, 15113, + 17766, 23365, 25906, 25986, 18492, 31546, 32299, 22858, 13577, 20392, + 22181, 23509, 25021, 9165, 26032, 15746, 31882, 20263, 16039, 14033, + 17477, 7233, 11444, 9681, 8206, 4558, 10134, 16343, 23091, 20980, + 1442, 2933, 594, 1002, 3848, 13994, 15038, 31685, 22104, 7511, + 29988, 5263, 31301, 5106, 25199, 28826, 13480, 19070, 6632, 5139, + 23240, 23483, 15266, 3546, 13016, 23706, 4629, 8849, 17089, 32755, + 30473, 11764, 31778, 8826, 32694, 11880, 27029, 21232, 13353, 11990, + 16693, 10902, 21993, 697, 93, 2535, 31195, 2166, 29874, 3462, + 31075, 26093, 22115, 20342, 32460, 29517, 22447, 18569, 12522, 18476, + 19880, 26139, 16893, 7895, 22071, 10239, 26073, 486, 8033, 23226, + 29508, 30740, 13256, 16879, 13472, 8782, 28789, 18203, 11210, 10, + 2004, 131, 19777, 19930, 13824, 9322, 30347, 12865, 2625, 15494, + 9172, 11461, 27092, 11701, 10457, 31557, 12266, 26605, 28932, 27021, + 3826, 21105, 2776, 16168, 5915, 18507, 656, 16245, 1557, 14582, + 20483, 28670, 17432, 16731, 23499, 6404, 12840, 20735, 24486, 13407, + 11671, 12154, 9756, 16005, 27851, 30251, 28003, 7656, 6466, 21034, + 29055, 5253, 19154, 8830, 25005, 28601, 18486, 18064, 17666, 27223, + 12486, 12512, 4688, 29945, 29574, 4599, 32585, 22675, 14178, 12189, + 19175, 26446, 6328, 15435, 1803, 12179, 24599, 17123, 9572, 25957, + 5703, 31391, 19346, 30197, 6537, 5959, 12939, 31088, 6175, 27048, + 5438, 30492, 5135, 25665, 29303, 12195, 21541, 22591, 9731, 25007, + 26167, 32428, 7444, 15542, 5804, 8675, 10727, 30232, 319, 31559, + 15011, 13415, 7937, 6452, 30597, 17669, 30109, 24886, 3130, 20469, + 19424, 633, 8332, 32235, 1913, 27777, 17473, 17573, 6141, 7707, + 21056, 19838, 4910, 24724, 3830, 10817, 26236, 17192, 1528, 19515, + 16844, 27325, 27370, 29076, 8057, 2514, 415, 31967, 3480, 5298, + 20865, 27375, 7297, 1517, 10292, 23276, 5771, 18184, 12150, 23719, + 25878, 4468, 6730, 31019, 18127, 23485, 11605, 10645, 15151, 9988, + 9864, 12325, 18245, 16559, 18431, 1940, 30646, 19551, 2601, 23720, + 17410, 10786, 14634, 17927, 14088, 3307, 381, 11849, 205, 21823, + 12254, 9296, 21881, 1072, 10343, 1500, 15778, 23183, 22777, 14426, + 15309, 30814, 10973, 25597, 27442, 4228, 14251, 25872, 31680, 1220, + 17771, 26507, 12129, 32240, 19963, 24804, 15487, 21594, 5553, 23591, + 14791, 23692, 5440, 19529, 30509, 27705, 15072, 22714, 26672, 11123, + 18089, 22585, 863, 1565, 17984, 28140, 15132, 20900, 29622, 31920, + 29600, 31466, 31661, 25774, 6719, 20611, 28349, 16175, 16877, 15652, + 25110, 3249, 26882, 1624, 870, 2361, 19514, 25461, 9213, 6892, + 27392, 207, 32559, 3801, 8428, 17195, 16377, 6170, 30858, 24439, + 2536, 17691, 7005, 6840, 2359, 24377, 803, 17987, 20023, 26767, + 20571, 19504, 30708, 11276, 27141, 24588, 29206, 5065, 19113, 10190, + 18949, 14746, 7834, 28940, 24401, 26084, 6012, 18031, 29520, 28846, + 6404, 31077, 23561, 14586, 8959, 29717, 24144, 9813, 7318, 26520, + 29491, 26797, 25808, 10854, 31893, 27235, 19544, 23310, 11545, 10401, + 23681, 6955, 10050, 26094, 3975, 30355, 6104, 27391, 24088, 19153, + 20365, 18896, 17938, 17110, 8160, 22260, 11919, 15083, 32578, 19754, + 19743, 8426, 14455, 20272, 28756, 9390, 10560, 15324, 21380, 19656, + 21559, 2152, 24522, 17267, 18938, 5390, 32334, 28619, 11659, 12244, + 24518, 12089, 22519, 14762, 23869, 17202, 24374, 8626, 22035, 18209, + 1107, 10975, 12102, 17057, 19661, 12225, 22511, 11248, 19260, 22687, + 30480, 28320, 23351, 30663, 9868, 19088, 21622, 34, 24439, 7963, + 31028, 18955, 31632, 30351, 9175, 6753, 28684, 26081, 30675, 8155, + 9622, 5279, 6604, 23196, 4499, 293, 13051, 8712, 29866, 21780, + 17362, 15573, 25220, 24064, 2531, 16229, 30416, 32361, 31998, 9299, + 23435, 10989, 10137, 21324, 1150, 18363, 24181, 2592, 17416, 31307, + 7039, 23576, 7453, 17229, 32757, 15243, 30351, 29919, 16841, 16812, + 5004, 22786, 2122, 10406, 30057, 7247, 31745, 11243, 2717, 20710, + 3545, 17096, 20241, 7592, 14724, 26942, 16791, 28070, 18201, 27405, + 21175, 13272, 20482, 25873, 12466, 5907, 2837, 28881, 5313, 6709, + 9217, 11862, 30110, 5283, 8684, 8249, 17221, 3829, 8538, 3584, + 4092, 22511, 25937, 24747, 19089, 26035, 955, 11657, 28131, 22326, + 12555, 25424, 18851, 10737, 28451, 19821, 18524, 30681, 9077, 24985, + 22193, 20880, 22633, 2989, 20455, 2269, 11775, 27575, 5027, 32602, + 15798, 14925, 10484, 19820, 30124, 18209, 4755, 21880, 12038, 17878, + 14442, 5972, 914, 23826, 2644, 22524, 9899, 11221, 16420, 29063, + 17758, 14601, 23272, 24409, 12747, 346, 19311, 23193, 24013, 5959, + 25096, 6886, 26845, 2425, 27533, 3909, 17715, 852, 8096, 9054, + 14383, 23501, 17461, 25833, 14840, 1659, 10833, 7523, 30819, 6896, + 26640, 13317, 278, 26185, 29677, 27638, 22253, 15108, 1342, 21184, + 30364, 29458, 24062, 29441, 3734, 22573, 25039, 8035, 28206, 26220, + 7743, 27085, 28668, 7445, 10315, 6875, 25475, 20914, 29474, 15756, + 14864, 14695, 1067, 4923, 31461, 7298, 10312, 977, 17047, 26708, + 3677, 24120, 20917, 4928, 27404, 32019, 28330, 1809, 2499, 9132, + 7909, 1424, 28409, 10029, 31543, 15191, 28596, 14356, 9084, 29920, + 25167, 24545, 5501, 10721, 19578, 1809, 27413, 21212, 12930, 5968, + 17299, 15601, 27603, 3563, 23648, 20472, 8379, 4740, 7394, 31436, + 21872, 26121, 6559, 14825, 9813, 15847, 21158, 27706, 6521, 24442, + 12587, 27180, 10592, 4040, 2161, 4871, 27911, 11120, 7661, 17534, + 290, 31453, 31912, 28805, 5800, 5823, 6117, 17762, 19089, 28860, + 260, 7175, 10975, 21602, 13478, 8351, 3291, 29793, 17170, 26983, + 6305, 22519, 22487, 28607, 11742, 15531, 25779, 8065, 1497, 16446, + 29620, 28817, 14000, 26335, 25757, 16053, 31716, 20894, 6539, 21415, + 26648, 14627, 22232, 22377, 25622, 160, 23104, 6873, 7955, 29956, +}; diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/Support.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/Support.c new file mode 100644 index 00000000..bf14f0b1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/MtrrLib/UnitTest/Support.c @@ -0,0 +1,987 @@ +/** @file + Unit tests of the MtrrLib instance of the MtrrLib class + + Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "MtrrLibUnitTest.h" + +MTRR_MEMORY_CACHE_TYPE mMemoryCacheTypes[] = { + CacheUncacheable, CacheWriteCombining, CacheWriteThrough, CacheWriteProtected, CacheWriteBack + }; + +UINT64 mFixedMtrrsValue[MTRR_NUMBER_OF_FIXED_MTRR]; +MSR_IA32_MTRR_PHYSBASE_REGISTER mVariableMtrrsPhysBase[MTRR_NUMBER_OF_VARIABLE_MTRR]; +MSR_IA32_MTRR_PHYSMASK_REGISTER mVariableMtrrsPhysMask[MTRR_NUMBER_OF_VARIABLE_MTRR]; +MSR_IA32_MTRR_DEF_TYPE_REGISTER mDefTypeMsr; +MSR_IA32_MTRRCAP_REGISTER mMtrrCapMsr; +CPUID_VERSION_INFO_EDX mCpuidVersionInfoEdx; +CPUID_VIR_PHY_ADDRESS_SIZE_EAX mCpuidVirPhyAddressSizeEax; + +BOOLEAN mRandomInput; +UINTN mNumberIndex = 0; +extern UINTN mNumbers[]; +extern UINTN mNumberCount; + +/** + Return a random number between 0 and RAND_MAX. + + If mRandomInput is TRUE, the routine directly calls rand(). + Otherwise, the routine returns the pre-generated numbers. + + @return a number between 0 and RAND_MAX. +**/ +UINTN +Rand ( + VOID + ) +{ + if (mRandomInput) { + return rand (); + } else { + DEBUG ((DEBUG_INFO, "random: %d\n", mNumberIndex)); + return mNumbers[mNumberIndex++ % (mNumberCount - 1)]; + } +} + +CHAR8 mContentTemplate[] = { + "/** @file\n" + " Pre-generated random number used by MtrrLib test.\n" + "\n" + " Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>\n" + " SPDX-License-Identifier: BSD-2-Clause-Patent\n" + "**/\n" + "UINTN mNumberCount = %d;\n" + "UINTN mNumbers[] = {" +}; + +/** + Generate Count random numbers in FilePath. + + @param FilePath The file path to put the generated random numbers. + @param Count Count of random numbers. +**/ +VOID +GenerateRandomNumbers ( + CHAR8 *FilePath, + UINTN Count + ) +{ + FILE *File; + UINTN Index; + + File = fopen (FilePath, "w"); + fprintf (File, mContentTemplate, Count); + for (Index = 0; Index < Count; Index++) { + if (Index % 10 == 0) { + fprintf (File, "\n "); + } + fprintf (File, " %d,", rand ()); + } + fprintf (File, "\n};\n"); + fclose (File); +} + +/** + Retrieves CPUID information. + + Executes the CPUID instruction with EAX set to the value specified by Index. + This function always returns Index. + If Eax is not NULL, then the value of EAX after CPUID is returned in Eax. + If Ebx is not NULL, then the value of EBX after CPUID is returned in Ebx. + If Ecx is not NULL, then the value of ECX after CPUID is returned in Ecx. + If Edx is not NULL, then the value of EDX after CPUID is returned in Edx. + This function is only available on IA-32 and x64. + + @param Index The 32-bit value to load into EAX prior to invoking the CPUID + instruction. + @param Eax The pointer to the 32-bit EAX value returned by the CPUID + instruction. This is an optional parameter that may be NULL. + @param Ebx The pointer to the 32-bit EBX value returned by the CPUID + instruction. This is an optional parameter that may be NULL. + @param Ecx The pointer to the 32-bit ECX value returned by the CPUID + instruction. This is an optional parameter that may be NULL. + @param Edx The pointer to the 32-bit EDX value returned by the CPUID + instruction. This is an optional parameter that may be NULL. + + @return Index. + +**/ +UINT32 +EFIAPI +UnitTestMtrrLibAsmCpuid ( + IN UINT32 Index, + OUT UINT32 *Eax, OPTIONAL + OUT UINT32 *Ebx, OPTIONAL + OUT UINT32 *Ecx, OPTIONAL + OUT UINT32 *Edx OPTIONAL + ) +{ + switch (Index) { + case CPUID_VERSION_INFO: + if (Edx != NULL) { + *Edx = mCpuidVersionInfoEdx.Uint32; + } + return Index; + break; + case CPUID_EXTENDED_FUNCTION: + if (Eax != NULL) { + *Eax = CPUID_VIR_PHY_ADDRESS_SIZE; + } + return Index; + break; + case CPUID_VIR_PHY_ADDRESS_SIZE: + if (Eax != NULL) { + *Eax = mCpuidVirPhyAddressSizeEax.Uint32; + } + return Index; + break; + } + + // + // Should never fall through to here + // + ASSERT(FALSE); + return Index; +} + +/** + Returns a 64-bit Machine Specific Register(MSR). + + Reads and returns the 64-bit MSR specified by Index. No parameter checking is + performed on Index, and some Index values may cause CPU exceptions. The + caller must either guarantee that Index is valid, or the caller must set up + exception handlers to catch the exceptions. This function is only available + on IA-32 and x64. + + @param MsrIndex The 32-bit MSR index to read. + + @return The value of the MSR identified by MsrIndex. + +**/ +UINT64 +EFIAPI +UnitTestMtrrLibAsmReadMsr64( + IN UINT32 MsrIndex + ) +{ + UINT32 Index; + + for (Index = 0; Index < ARRAY_SIZE (mFixedMtrrsValue); Index++) { + if (MsrIndex == mFixedMtrrsIndex[Index]) { + return mFixedMtrrsValue[Index]; + } + } + + if ((MsrIndex >= MSR_IA32_MTRR_PHYSBASE0) && + (MsrIndex <= MSR_IA32_MTRR_PHYSMASK0 + (MTRR_NUMBER_OF_VARIABLE_MTRR << 1))) { + if (MsrIndex % 2 == 0) { + Index = (MsrIndex - MSR_IA32_MTRR_PHYSBASE0) >> 1; + return mVariableMtrrsPhysBase[Index].Uint64; + } else { + Index = (MsrIndex - MSR_IA32_MTRR_PHYSMASK0) >> 1; + return mVariableMtrrsPhysMask[Index].Uint64; + } + } + + if (MsrIndex == MSR_IA32_MTRR_DEF_TYPE) { + return mDefTypeMsr.Uint64; + } + + if (MsrIndex == MSR_IA32_MTRRCAP) { + return mMtrrCapMsr.Uint64; + } + + // + // Should never fall through to here + // + ASSERT(FALSE); + return 0; +} + +/** + Writes a 64-bit value to a Machine Specific Register(MSR), and returns the + value. + + Writes the 64-bit value specified by Value to the MSR specified by Index. The + 64-bit value written to the MSR is returned. No parameter checking is + performed on Index or Value, and some of these may cause CPU exceptions. The + caller must either guarantee that Index and Value are valid, or the caller + must establish proper exception handlers. This function is only available on + IA-32 and x64. + + @param MsrIndex The 32-bit MSR index to write. + @param Value The 64-bit value to write to the MSR. + + @return Value + +**/ +UINT64 +EFIAPI +UnitTestMtrrLibAsmWriteMsr64( + IN UINT32 MsrIndex, + IN UINT64 Value + ) +{ + UINT32 Index; + + for (Index = 0; Index < ARRAY_SIZE (mFixedMtrrsValue); Index++) { + if (MsrIndex == mFixedMtrrsIndex[Index]) { + mFixedMtrrsValue[Index] = Value; + return Value; + } + } + + if ((MsrIndex >= MSR_IA32_MTRR_PHYSBASE0) && + (MsrIndex <= MSR_IA32_MTRR_PHYSMASK0 + (MTRR_NUMBER_OF_VARIABLE_MTRR << 1))) { + if (MsrIndex % 2 == 0) { + Index = (MsrIndex - MSR_IA32_MTRR_PHYSBASE0) >> 1; + mVariableMtrrsPhysBase[Index].Uint64 = Value; + return Value; + } else { + Index = (MsrIndex - MSR_IA32_MTRR_PHYSMASK0) >> 1; + mVariableMtrrsPhysMask[Index].Uint64 = Value; + return Value; + } + } + + if (MsrIndex == MSR_IA32_MTRR_DEF_TYPE) { + mDefTypeMsr.Uint64 = Value; + return Value; + } + + if (MsrIndex == MSR_IA32_MTRRCAP) { + mMtrrCapMsr.Uint64 = Value; + return Value; + } + + // + // Should never fall through to here + // + ASSERT(FALSE); + return 0; +} + +/** + Initialize the MTRR registers. + + @param SystemParameter System parameter that controls the MTRR registers initialization. +**/ +UNIT_TEST_STATUS +EFIAPI +InitializeMtrrRegs ( + IN MTRR_LIB_SYSTEM_PARAMETER *SystemParameter + ) +{ + UINT32 Index; + + SetMem (mFixedMtrrsValue, sizeof (mFixedMtrrsValue), SystemParameter->DefaultCacheType); + + for (Index = 0; Index < ARRAY_SIZE (mVariableMtrrsPhysBase); Index++) { + mVariableMtrrsPhysBase[Index].Uint64 = 0; + mVariableMtrrsPhysBase[Index].Bits.Type = SystemParameter->DefaultCacheType; + mVariableMtrrsPhysBase[Index].Bits.Reserved1 = 0; + + mVariableMtrrsPhysMask[Index].Uint64 = 0; + mVariableMtrrsPhysMask[Index].Bits.V = 0; + mVariableMtrrsPhysMask[Index].Bits.Reserved1 = 0; + } + + mDefTypeMsr.Bits.E = 1; + mDefTypeMsr.Bits.FE = 1; + mDefTypeMsr.Bits.Type = SystemParameter->DefaultCacheType; + mDefTypeMsr.Bits.Reserved1 = 0; + mDefTypeMsr.Bits.Reserved2 = 0; + mDefTypeMsr.Bits.Reserved3 = 0; + + mMtrrCapMsr.Bits.SMRR = 0; + mMtrrCapMsr.Bits.WC = 0; + mMtrrCapMsr.Bits.VCNT = SystemParameter->VariableMtrrCount; + mMtrrCapMsr.Bits.FIX = SystemParameter->FixedMtrrSupported; + mMtrrCapMsr.Bits.Reserved1 = 0; + mMtrrCapMsr.Bits.Reserved2 = 0; + mMtrrCapMsr.Bits.Reserved3 = 0; + + mCpuidVersionInfoEdx.Bits.MTRR = SystemParameter->MtrrSupported; + mCpuidVirPhyAddressSizeEax.Bits.PhysicalAddressBits = SystemParameter->PhysicalAddressBits; + + // + // Hook BaseLib functions used by MtrrLib that require some emulation. + // + gUnitTestHostBaseLib.X86->AsmCpuid = UnitTestMtrrLibAsmCpuid; + gUnitTestHostBaseLib.X86->AsmReadMsr64 = UnitTestMtrrLibAsmReadMsr64; + gUnitTestHostBaseLib.X86->AsmWriteMsr64 = UnitTestMtrrLibAsmWriteMsr64; + + return UNIT_TEST_PASSED; +} + +/** + Initialize the MTRR registers. + + @param Context System parameter that controls the MTRR registers initialization. +**/ +UNIT_TEST_STATUS +EFIAPI +InitializeSystem ( + IN UNIT_TEST_CONTEXT Context + ) +{ + return InitializeMtrrRegs ((MTRR_LIB_SYSTEM_PARAMETER *) Context); +} + +/** + Collect the test result. + + @param DefaultType Default memory type. + @param PhysicalAddressBits Physical address bits. + @param VariableMtrrCount Count of variable MTRRs. + @param Mtrrs MTRR settings to collect from. + @param Ranges Return the memory ranges. + @param RangeCount Return the count of memory ranges. + @param MtrrCount Return the count of variable MTRRs being used. +**/ +VOID +CollectTestResult ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT32 PhysicalAddressBits, + IN UINT32 VariableMtrrCount, + IN MTRR_SETTINGS *Mtrrs, + OUT MTRR_MEMORY_RANGE *Ranges, + IN OUT UINTN *RangeCount, + OUT UINT32 *MtrrCount + ) +{ + UINTN Index; + UINT64 MtrrValidBitsMask; + UINT64 MtrrValidAddressMask; + MTRR_MEMORY_RANGE RawMemoryRanges[ARRAY_SIZE (Mtrrs->Variables.Mtrr)]; + + ASSERT (Mtrrs != NULL); + ASSERT (VariableMtrrCount <= ARRAY_SIZE (Mtrrs->Variables.Mtrr)); + + MtrrValidBitsMask = (1ull << PhysicalAddressBits) - 1; + MtrrValidAddressMask = MtrrValidBitsMask & ~0xFFFull; + + *MtrrCount = 0; + for (Index = 0; Index < VariableMtrrCount; Index++) { + if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *) &Mtrrs->Variables.Mtrr[Index].Mask)->Bits.V == 1) { + RawMemoryRanges[*MtrrCount].BaseAddress = Mtrrs->Variables.Mtrr[Index].Base & MtrrValidAddressMask; + RawMemoryRanges[*MtrrCount].Type = + ((MSR_IA32_MTRR_PHYSBASE_REGISTER *) &Mtrrs->Variables.Mtrr[Index].Base)->Bits.Type; + RawMemoryRanges[*MtrrCount].Length = + ((~(Mtrrs->Variables.Mtrr[Index].Mask & MtrrValidAddressMask)) & MtrrValidBitsMask) + 1; + (*MtrrCount)++; + } + } + + GetEffectiveMemoryRanges (DefaultType, PhysicalAddressBits, RawMemoryRanges, *MtrrCount, Ranges, RangeCount); +} + +/** + Return a 32bit random number. + + @param Start Start of the random number range. + @param Limit Limit of the random number range. + @return 32bit random number +**/ +UINT32 +Random32 ( + UINT32 Start, + UINT32 Limit + ) +{ + return (UINT32) (((double) Rand () / RAND_MAX) * (Limit - Start)) + Start; +} + +/** + Return a 64bit random number. + + @param Start Start of the random number range. + @param Limit Limit of the random number range. + @return 64bit random number +**/ +UINT64 +Random64 ( + UINT64 Start, + UINT64 Limit + ) +{ + return (UINT64) (((double) Rand () / RAND_MAX) * (Limit - Start)) + Start; +} + +/** + Generate random MTRR BASE/MASK for a specified type. + + @param PhysicalAddressBits Physical address bits. + @param CacheType Cache type. + @param MtrrPair Return the random MTRR. + @param MtrrMemoryRange Return the random memory range. +**/ +VOID +GenerateRandomMtrrPair ( + IN UINT32 PhysicalAddressBits, + IN MTRR_MEMORY_CACHE_TYPE CacheType, + OUT MTRR_VARIABLE_SETTING *MtrrPair, OPTIONAL + OUT MTRR_MEMORY_RANGE *MtrrMemoryRange OPTIONAL + ) +{ + MSR_IA32_MTRR_PHYSBASE_REGISTER PhysBase; + MSR_IA32_MTRR_PHYSMASK_REGISTER PhysMask; + UINT32 SizeShift; + UINT32 BaseShift; + UINT64 RandomBoundary; + UINT64 MaxPhysicalAddress; + UINT64 RangeSize; + UINT64 RangeBase; + UINT64 PhysBasePhyMaskValidBitsMask; + + MaxPhysicalAddress = 1ull << PhysicalAddressBits; + do { + SizeShift = Random32 (12, PhysicalAddressBits - 1); + RangeSize = 1ull << SizeShift; + + BaseShift = Random32 (SizeShift, PhysicalAddressBits - 1); + RandomBoundary = Random64 (0, 1ull << (PhysicalAddressBits - BaseShift)); + RangeBase = RandomBoundary << BaseShift; + } while (RangeBase < SIZE_1MB || RangeBase > MaxPhysicalAddress - 1); + + PhysBasePhyMaskValidBitsMask = (MaxPhysicalAddress - 1) & 0xfffffffffffff000ULL; + + PhysBase.Uint64 = 0; + PhysBase.Bits.Type = CacheType; + PhysBase.Uint64 |= RangeBase & PhysBasePhyMaskValidBitsMask; + PhysMask.Uint64 = 0; + PhysMask.Bits.V = 1; + PhysMask.Uint64 |= ((~RangeSize) + 1) & PhysBasePhyMaskValidBitsMask; + + if (MtrrPair != NULL) { + MtrrPair->Base = PhysBase.Uint64; + MtrrPair->Mask = PhysMask.Uint64; + } + + if (MtrrMemoryRange != NULL) { + MtrrMemoryRange->BaseAddress = RangeBase; + MtrrMemoryRange->Length = RangeSize; + MtrrMemoryRange->Type = CacheType; + } +} + + +/** + Check whether the Range overlaps with any one in Ranges. + + @param Range The memory range to check. + @param Ranges The memory ranges. + @param Count Count of memory ranges. + + @return TRUE when overlap exists. +**/ +BOOLEAN +RangesOverlap ( + IN MTRR_MEMORY_RANGE *Range, + IN MTRR_MEMORY_RANGE *Ranges, + IN UINTN Count + ) +{ + while (Count-- != 0) { + // + // Two ranges overlap when: + // 1. range#2.base is in the middle of range#1 + // 2. range#1.base is in the middle of range#2 + // + if ((Range->BaseAddress <= Ranges[Count].BaseAddress && Ranges[Count].BaseAddress < Range->BaseAddress + Range->Length) + || (Ranges[Count].BaseAddress <= Range->BaseAddress && Range->BaseAddress < Ranges[Count].BaseAddress + Ranges[Count].Length)) { + return TRUE; + } + } + return FALSE; +} + +/** + Generate random MTRRs. + + @param PhysicalAddressBits Physical address bits. + @param RawMemoryRanges Return the randomly generated MTRRs. + @param UcCount Count of Uncacheable MTRRs. + @param WtCount Count of Write Through MTRRs. + @param WbCount Count of Write Back MTRRs. + @param WpCount Count of Write Protected MTRRs. + @param WcCount Count of Write Combine MTRRs. +**/ +VOID +GenerateValidAndConfigurableMtrrPairs ( + IN UINT32 PhysicalAddressBits, + IN OUT MTRR_MEMORY_RANGE *RawMemoryRanges, + IN UINT32 UcCount, + IN UINT32 WtCount, + IN UINT32 WbCount, + IN UINT32 WpCount, + IN UINT32 WcCount + ) +{ + UINT32 Index; + + // + // 1. Generate UC, WT, WB in order. + // + for (Index = 0; Index < UcCount; Index++) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheUncacheable, NULL, &RawMemoryRanges[Index]); + } + + for (Index = UcCount; Index < UcCount + WtCount; Index++) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheWriteThrough, NULL, &RawMemoryRanges[Index]); + } + + for (Index = UcCount + WtCount; Index < UcCount + WtCount + WbCount; Index++) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheWriteBack, NULL, &RawMemoryRanges[Index]); + } + + // + // 2. Generate WP MTRR and DO NOT overlap with WT, WB. + // + for (Index = UcCount + WtCount + WbCount; Index < UcCount + WtCount + WbCount + WpCount; Index++) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheWriteProtected, NULL, &RawMemoryRanges[Index]); + while (RangesOverlap (&RawMemoryRanges[Index], &RawMemoryRanges[UcCount], WtCount + WbCount)) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheWriteProtected, NULL, &RawMemoryRanges[Index]); + } + } + + // + // 3. Generate WC MTRR and DO NOT overlap with WT, WB, WP. + // + for (Index = UcCount + WtCount + WbCount + WpCount; Index < UcCount + WtCount + WbCount + WpCount + WcCount; Index++) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheWriteCombining, NULL, &RawMemoryRanges[Index]); + while (RangesOverlap (&RawMemoryRanges[Index], &RawMemoryRanges[UcCount], WtCount + WbCount + WpCount)) { + GenerateRandomMtrrPair (PhysicalAddressBits, CacheWriteCombining, NULL, &RawMemoryRanges[Index]); + } + } +} + +/** + Return a random memory cache type. +**/ +MTRR_MEMORY_CACHE_TYPE +GenerateRandomCacheType ( + VOID + ) +{ + return mMemoryCacheTypes[Random32 (0, ARRAY_SIZE (mMemoryCacheTypes) - 1)]; +} + +/** + Compare function used by qsort(). +**/ + +/** + Compare function used by qsort(). + + @param Left Left operand to compare. + @param Right Right operand to compare. + + @retval 0 Left == Right + @retval -1 Left < Right + @retval 1 Left > Right +**/ +INT32 +CompareFuncUint64 ( + CONST VOID * Left, + CONST VOID * Right + ) +{ + INT64 Delta; + Delta = (*(UINT64*)Left - *(UINT64*)Right); + if (Delta > 0) { + return 1; + } else if (Delta == 0) { + return 0; + } else { + return -1; + } +} + +/** + Determin the memory cache type for the Range. + + @param DefaultType Default cache type. + @param Range The memory range to determin the cache type. + @param Ranges The entire memory ranges. + @param RangeCount Count of the entire memory ranges. +**/ +VOID +DetermineMemoryCacheType ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN OUT MTRR_MEMORY_RANGE *Range, + IN MTRR_MEMORY_RANGE *Ranges, + IN UINT32 RangeCount + ) +{ + UINT32 Index; + Range->Type = CacheInvalid; + for (Index = 0; Index < RangeCount; Index++) { + if (RangesOverlap (Range, &Ranges[Index], 1)) { + if (Ranges[Index].Type < Range->Type) { + Range->Type = Ranges[Index].Type; + } + } + } + + if (Range->Type == CacheInvalid) { + Range->Type = DefaultType; + } +} + +/** + Get the index of the element that does NOT equals to Array[Index]. + + @param Index Current element. + @param Array Array to scan. + @param Count Count of the array. + + @return Next element that doesn't equal to current one. +**/ +UINT32 +GetNextDifferentElementInSortedArray ( + IN UINT32 Index, + IN UINT64 *Array, + IN UINT32 Count + ) +{ + UINT64 CurrentElement; + CurrentElement = Array[Index]; + while (CurrentElement == Array[Index] && Index < Count) { + Index++; + } + return Index; +} + +/** + Remove the duplicates from the array. + + @param Array The array to operate on. + @param Count Count of the array. +**/ +VOID +RemoveDuplicatesInSortedArray ( + IN OUT UINT64 *Array, + IN OUT UINT32 *Count + ) +{ + UINT32 Index; + UINT32 NewCount; + + Index = 0; + NewCount = 0; + while (Index < *Count) { + Array[NewCount] = Array[Index]; + NewCount++; + Index = GetNextDifferentElementInSortedArray (Index, Array, *Count); + } + *Count = NewCount; +} + +/** + Return TRUE when Address is in the Range. + + @param Address The address to check. + @param Range The range to check. + @return TRUE when Address is in the Range. +**/ +BOOLEAN +AddressInRange ( + IN UINT64 Address, + IN MTRR_MEMORY_RANGE Range + ) +{ + return (Address >= Range.BaseAddress) && (Address <= Range.BaseAddress + Range.Length - 1); +} + +/** + Get the overlap bit flag. + + @param RawMemoryRanges Raw memory ranges. + @param RawMemoryRangeCount Count of raw memory ranges. + @param Address The address to check. +**/ +UINT64 +GetOverlapBitFlag ( + IN MTRR_MEMORY_RANGE *RawMemoryRanges, + IN UINT32 RawMemoryRangeCount, + IN UINT64 Address + ) +{ + UINT64 OverlapBitFlag; + UINT32 Index; + OverlapBitFlag = 0; + for (Index = 0; Index < RawMemoryRangeCount; Index++) { + if (AddressInRange (Address, RawMemoryRanges[Index])) { + OverlapBitFlag |= (1ull << Index); + } + } + + return OverlapBitFlag; +} + +/** + Return the relationship between flags. + + @param Flag1 Flag 1 + @param Flag2 Flag 2 + + @retval 0 Flag1 == Flag2 + @retval 1 Flag1 is a subset of Flag2 + @retval 2 Flag2 is a subset of Flag1 + @retval 3 No subset relations between Flag1 and Flag2. +**/ +UINT32 +CheckOverlapBitFlagsRelation ( + IN UINT64 Flag1, + IN UINT64 Flag2 + ) +{ + if (Flag1 == Flag2) return 0; + if ((Flag1 | Flag2) == Flag2) return 1; + if ((Flag1 | Flag2) == Flag1) return 2; + return 3; +} + +/** + Return TRUE when the Endpoint is in any of the Ranges. + + @param Endpoint The endpoint to check. + @param Ranges The memory ranges. + @param RangeCount Count of memory ranges. + + @retval TRUE Endpoint is in one of the range. + @retval FALSE Endpoint is not in any of the ranges. +**/ +BOOLEAN +IsEndpointInRanges ( + IN UINT64 Endpoint, + IN MTRR_MEMORY_RANGE *Ranges, + IN UINTN RangeCount + ) +{ + UINT32 Index; + for (Index = 0; Index < RangeCount; Index++) { + if (AddressInRange (Endpoint, Ranges[Index])) { + return TRUE; + } + } + return FALSE; +} + + +/** + Compact adjacent ranges of the same type. + + @param DefaultType Default memory type. + @param PhysicalAddressBits Physical address bits. + @param EffectiveMtrrMemoryRanges Memory ranges to compact. + @param EffectiveMtrrMemoryRangesCount Return the new count of memory ranges. +**/ +VOID +CompactAndExtendEffectiveMtrrMemoryRanges ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT32 PhysicalAddressBits, + IN OUT MTRR_MEMORY_RANGE **EffectiveMtrrMemoryRanges, + IN OUT UINTN *EffectiveMtrrMemoryRangesCount + ) +{ + UINT64 MaxAddress; + UINTN NewRangesCountAtMost; + MTRR_MEMORY_RANGE *NewRanges; + UINTN NewRangesCountActual; + MTRR_MEMORY_RANGE *CurrentRangeInNewRanges; + MTRR_MEMORY_CACHE_TYPE CurrentRangeTypeInOldRanges; + + MTRR_MEMORY_RANGE *OldRanges; + MTRR_MEMORY_RANGE OldLastRange; + UINTN OldRangesIndex; + + NewRangesCountActual = 0; + NewRangesCountAtMost = *EffectiveMtrrMemoryRangesCount + 2; // At most with 2 more range entries. + NewRanges = (MTRR_MEMORY_RANGE *) calloc (NewRangesCountAtMost, sizeof (MTRR_MEMORY_RANGE)); + OldRanges = *EffectiveMtrrMemoryRanges; + if (OldRanges[0].BaseAddress > 0) { + NewRanges[NewRangesCountActual].BaseAddress = 0; + NewRanges[NewRangesCountActual].Length = OldRanges[0].BaseAddress; + NewRanges[NewRangesCountActual].Type = DefaultType; + NewRangesCountActual++; + } + + OldRangesIndex = 0; + while (OldRangesIndex < *EffectiveMtrrMemoryRangesCount) { + CurrentRangeTypeInOldRanges = OldRanges[OldRangesIndex].Type; + CurrentRangeInNewRanges = NULL; + if (NewRangesCountActual > 0) // We need to check CurrentNewRange first before generate a new NewRange. + { + CurrentRangeInNewRanges = &NewRanges[NewRangesCountActual - 1]; + } + if (CurrentRangeInNewRanges != NULL && CurrentRangeInNewRanges->Type == CurrentRangeTypeInOldRanges) { + CurrentRangeInNewRanges->Length += OldRanges[OldRangesIndex].Length; + } else { + NewRanges[NewRangesCountActual].BaseAddress = OldRanges[OldRangesIndex].BaseAddress; + NewRanges[NewRangesCountActual].Length += OldRanges[OldRangesIndex].Length; + NewRanges[NewRangesCountActual].Type = CurrentRangeTypeInOldRanges; + while (OldRangesIndex + 1 < *EffectiveMtrrMemoryRangesCount && OldRanges[OldRangesIndex + 1].Type == CurrentRangeTypeInOldRanges) + { + OldRangesIndex++; + NewRanges[NewRangesCountActual].Length += OldRanges[OldRangesIndex].Length; + } + NewRangesCountActual++; + } + + OldRangesIndex++; + } + + MaxAddress = (1ull << PhysicalAddressBits) - 1; + OldLastRange = OldRanges[(*EffectiveMtrrMemoryRangesCount) - 1]; + CurrentRangeInNewRanges = &NewRanges[NewRangesCountActual - 1]; + if (OldLastRange.BaseAddress + OldLastRange.Length - 1 < MaxAddress) { + if (CurrentRangeInNewRanges->Type == DefaultType) { + CurrentRangeInNewRanges->Length = MaxAddress - CurrentRangeInNewRanges->BaseAddress + 1; + } else { + NewRanges[NewRangesCountActual].BaseAddress = OldLastRange.BaseAddress + OldLastRange.Length; + NewRanges[NewRangesCountActual].Length = MaxAddress - NewRanges[NewRangesCountActual].BaseAddress + 1; + NewRanges[NewRangesCountActual].Type = DefaultType; + NewRangesCountActual++; + } + } + + free (*EffectiveMtrrMemoryRanges); + *EffectiveMtrrMemoryRanges = NewRanges; + *EffectiveMtrrMemoryRangesCount = NewRangesCountActual; +} + +/** + Collect all the endpoints in the raw memory ranges. + + @param Endpoints Return the collected endpoints. + @param EndPointCount Return the count of endpoints. + @param RawMemoryRanges Raw memory ranges. + @param RawMemoryRangeCount Count of raw memory ranges. +**/ +VOID +CollectEndpoints ( + IN OUT UINT64 *Endpoints, + IN OUT UINT32 *EndPointCount, + IN MTRR_MEMORY_RANGE *RawMemoryRanges, + IN UINT32 RawMemoryRangeCount + ) +{ + UINT32 Index; + UINT32 RawRangeIndex; + + ASSERT ((RawMemoryRangeCount << 1) == *EndPointCount); + + for (Index = 0; Index < *EndPointCount; Index += 2) { + RawRangeIndex = Index >> 1; + Endpoints[Index] = RawMemoryRanges[RawRangeIndex].BaseAddress; + Endpoints[Index + 1] = RawMemoryRanges[RawRangeIndex].BaseAddress + RawMemoryRanges[RawRangeIndex].Length - 1; + } + + qsort (Endpoints, *EndPointCount, sizeof (UINT64), CompareFuncUint64); + RemoveDuplicatesInSortedArray (Endpoints, EndPointCount); +} + +/** + Convert the MTRR BASE/MASK array to memory ranges. + + @param DefaultType Default memory type. + @param PhysicalAddressBits Physical address bits. + @param RawMemoryRanges Raw memory ranges. + @param RawMemoryRangeCount Count of raw memory ranges. + @param MemoryRanges Memory ranges. + @param MemoryRangeCount Count of memory ranges. +**/ +VOID +GetEffectiveMemoryRanges ( + IN MTRR_MEMORY_CACHE_TYPE DefaultType, + IN UINT32 PhysicalAddressBits, + IN MTRR_MEMORY_RANGE *RawMemoryRanges, + IN UINT32 RawMemoryRangeCount, + OUT MTRR_MEMORY_RANGE *MemoryRanges, + OUT UINTN *MemoryRangeCount + ) +{ + UINTN Index; + UINT32 AllEndPointsCount; + UINT64 *AllEndPointsInclusive; + UINT32 AllRangePiecesCountMax; + MTRR_MEMORY_RANGE *AllRangePieces; + UINTN AllRangePiecesCountActual; + UINT64 OverlapBitFlag1; + UINT64 OverlapBitFlag2; + INT32 OverlapFlagRelation; + + if (RawMemoryRangeCount == 0) { + MemoryRanges[0].BaseAddress = 0; + MemoryRanges[0].Length = (1ull << PhysicalAddressBits); + MemoryRanges[0].Type = DefaultType; + *MemoryRangeCount = 1; + return; + } + + AllEndPointsCount = RawMemoryRangeCount << 1; + AllEndPointsInclusive = calloc (AllEndPointsCount, sizeof (UINT64)); + AllRangePiecesCountMax = RawMemoryRangeCount * 3 + 1; + AllRangePieces = calloc (AllRangePiecesCountMax, sizeof (MTRR_MEMORY_RANGE)); + CollectEndpoints (AllEndPointsInclusive, &AllEndPointsCount, RawMemoryRanges, RawMemoryRangeCount); + + for (Index = 0, AllRangePiecesCountActual = 0; Index < AllEndPointsCount - 1; Index++) { + OverlapBitFlag1 = GetOverlapBitFlag (RawMemoryRanges, RawMemoryRangeCount, AllEndPointsInclusive[Index]); + OverlapBitFlag2 = GetOverlapBitFlag (RawMemoryRanges, RawMemoryRangeCount, AllEndPointsInclusive[Index + 1]); + OverlapFlagRelation = CheckOverlapBitFlagsRelation (OverlapBitFlag1, OverlapBitFlag2); + switch (OverlapFlagRelation) { + case 0: // [1, 2] + AllRangePieces[AllRangePiecesCountActual].BaseAddress = AllEndPointsInclusive[Index]; + AllRangePieces[AllRangePiecesCountActual].Length = AllEndPointsInclusive[Index + 1] - AllEndPointsInclusive[Index] + 1; + AllRangePiecesCountActual++; + break; + case 1: // [1, 2) + AllRangePieces[AllRangePiecesCountActual].BaseAddress = AllEndPointsInclusive[Index]; + AllRangePieces[AllRangePiecesCountActual].Length = (AllEndPointsInclusive[Index + 1] - 1) - AllEndPointsInclusive[Index] + 1; + AllRangePiecesCountActual++; + break; + case 2: // (1, 2] + AllRangePieces[AllRangePiecesCountActual].BaseAddress = AllEndPointsInclusive[Index] + 1; + AllRangePieces[AllRangePiecesCountActual].Length = AllEndPointsInclusive[Index + 1] - (AllEndPointsInclusive[Index] + 1) + 1; + AllRangePiecesCountActual++; + + if (!IsEndpointInRanges (AllEndPointsInclusive[Index], AllRangePieces, AllRangePiecesCountActual)) { + AllRangePieces[AllRangePiecesCountActual].BaseAddress = AllEndPointsInclusive[Index]; + AllRangePieces[AllRangePiecesCountActual].Length = 1; + AllRangePiecesCountActual++; + } + break; + case 3: // (1, 2) + AllRangePieces[AllRangePiecesCountActual].BaseAddress = AllEndPointsInclusive[Index] + 1; + AllRangePieces[AllRangePiecesCountActual].Length = (AllEndPointsInclusive[Index + 1] - 1) - (AllEndPointsInclusive[Index] + 1) + 1; + if (AllRangePieces[AllRangePiecesCountActual].Length == 0) // Only in case 3 can exists Length=0, we should skip such "segment". + break; + AllRangePiecesCountActual++; + if (!IsEndpointInRanges (AllEndPointsInclusive[Index], AllRangePieces, AllRangePiecesCountActual)) { + AllRangePieces[AllRangePiecesCountActual].BaseAddress = AllEndPointsInclusive[Index]; + AllRangePieces[AllRangePiecesCountActual].Length = 1; + AllRangePiecesCountActual++; + } + break; + default: + ASSERT (FALSE); + } + } + + for (Index = 0; Index < AllRangePiecesCountActual; Index++) { + DetermineMemoryCacheType (DefaultType, &AllRangePieces[Index], RawMemoryRanges, RawMemoryRangeCount); + } + + CompactAndExtendEffectiveMtrrMemoryRanges (DefaultType, PhysicalAddressBits, &AllRangePieces, &AllRangePiecesCountActual); + ASSERT (*MemoryRangeCount >= AllRangePiecesCountActual); + memcpy (MemoryRanges, AllRangePieces, AllRangePiecesCountActual * sizeof (MTRR_MEMORY_RANGE)); + *MemoryRangeCount = AllRangePiecesCountActual; + + free (AllEndPointsInclusive); + free (AllRangePieces); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.c new file mode 100644 index 00000000..c5d4c3c0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.c @@ -0,0 +1,84 @@ +/** @file +Null instance of Platform Sec Lib. + +Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiPei.h> + +#include <Ppi/SecPlatformInformation.h> + +/** + A developer supplied function to perform platform specific operations. + + It's a developer supplied function to perform any operations appropriate to a + given platform. It's invoked just before passing control to PEI core by SEC + core. Platform developer may modify the SecCoreData passed to PEI Core. + It returns a platform specific PPI list that platform wishes to pass to PEI core. + The Generic SEC core module will merge this list to join the final list passed to + PEI core. + + @param SecCoreData The same parameter as passing to PEI core. It + could be overridden by this function. + + @return The platform specific PPI list to be passed to PEI core or + NULL if there is no need of such platform specific PPI list. + +**/ +EFI_PEI_PPI_DESCRIPTOR * +EFIAPI +SecPlatformMain ( + IN OUT EFI_SEC_PEI_HAND_OFF *SecCoreData + ) +{ + return NULL; +} + +/** + This interface conveys state information out of the Security (SEC) phase into PEI. + + @param PeiServices Pointer to the PEI Services Table. + @param StructureSize Pointer to the variable describing size of the input buffer. + @param PlatformInformationRecord Pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformation ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD *PlatformInformationRecord + ) +{ + return EFI_SUCCESS; +} + +/** + This interface disables temporary memory in SEC Phase. +**/ +VOID +EFIAPI +SecPlatformDisableTemporaryMemory ( + VOID + ) +{ +} + +/** + This function provides dummy function so that SecCore can pass build + validation. All real platform library instances need to implement the real + entry point in assembly. +**/ +VOID +EFIAPI +_ModuleEntryPoint ( + VOID + ) +{ + return; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.inf new file mode 100644 index 00000000..876bc854 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.inf @@ -0,0 +1,31 @@ +## @file +# Library functions for PlatformSecLib. +# +# Null instance of Platform Sec Lib. +# +# Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PlatformSecLibNull + MODULE_UNI_FILE = PlatformSecLibNull.uni + FILE_GUID = 6695974D-968C-420b-80B9-7870CD20118F + MODULE_TYPE = SEC + VERSION_STRING = 1.0 + LIBRARY_CLASS = PlatformSecLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + PlatformSecLibNull.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.uni new file mode 100644 index 00000000..42f46137 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.uni @@ -0,0 +1,14 @@ +// /** @file
+// Library functions for PlatformSecLib.
+//
+// Null instance of Platform Sec Library.
+//
+// Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "Library functions for PlatformSecLib"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Null instance of Platform Sec Library."
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/CpuFeaturesInitialize.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/CpuFeaturesInitialize.c new file mode 100644 index 00000000..9c1c95c4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/CpuFeaturesInitialize.c @@ -0,0 +1,1190 @@ +/** @file + CPU Features Initialize functions. + + Copyright (c) 2017 - 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "RegisterCpuFeatures.h" + +CHAR16 *mDependTypeStr[] = {L"None", L"Thread", L"Core", L"Package", L"Invalid" }; + +/** + Worker function to save PcdCpuFeaturesCapability. + + @param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer + @param[in] BitMaskSize CPU feature bits mask buffer size. + +**/ +VOID +SetCapabilityPcd ( + IN UINT8 *SupportedFeatureMask, + IN UINTN BitMaskSize + ) +{ + EFI_STATUS Status; + + Status = PcdSetPtrS (PcdCpuFeaturesCapability, &BitMaskSize, SupportedFeatureMask); + ASSERT_EFI_ERROR (Status); +} + +/** + Worker function to save PcdCpuFeaturesSetting. + + @param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer + @param[in] BitMaskSize CPU feature bits mask buffer size. +**/ +VOID +SetSettingPcd ( + IN UINT8 *SupportedFeatureMask, + IN UINTN BitMaskSize + ) +{ + EFI_STATUS Status; + + Status = PcdSetPtrS (PcdCpuFeaturesSetting, &BitMaskSize, SupportedFeatureMask); + ASSERT_EFI_ERROR (Status); +} + +/** + Collects CPU type and feature information. + + @param[in, out] CpuInfo The pointer to CPU feature information +**/ +VOID +FillProcessorInfo ( + IN OUT REGISTER_CPU_FEATURE_INFORMATION *CpuInfo + ) +{ + CPUID_VERSION_INFO_EAX Eax; + CPUID_VERSION_INFO_ECX Ecx; + CPUID_VERSION_INFO_EDX Edx; + UINT32 DisplayedFamily; + UINT32 DisplayedModel; + + AsmCpuid (CPUID_VERSION_INFO, &Eax.Uint32, NULL, &Ecx.Uint32, &Edx.Uint32); + + DisplayedFamily = Eax.Bits.FamilyId; + if (Eax.Bits.FamilyId == 0x0F) { + DisplayedFamily |= (Eax.Bits.ExtendedFamilyId << 4); + } + + DisplayedModel = Eax.Bits.Model; + if (Eax.Bits.FamilyId == 0x06 || Eax.Bits.FamilyId == 0x0f) { + DisplayedModel |= (Eax.Bits.ExtendedModelId << 4); + } + + CpuInfo->DisplayFamily = DisplayedFamily; + CpuInfo->DisplayModel = DisplayedModel; + CpuInfo->SteppingId = Eax.Bits.SteppingId; + CpuInfo->ProcessorType = Eax.Bits.ProcessorType; + CpuInfo->CpuIdVersionInfoEcx.Uint32 = Ecx.Uint32; + CpuInfo->CpuIdVersionInfoEdx.Uint32 = Edx.Uint32; +} + +/** + Prepares for private data used for CPU features. + +**/ +VOID +CpuInitDataInitialize ( + VOID + ) +{ + EFI_STATUS Status; + UINTN ProcessorNumber; + EFI_PROCESSOR_INFORMATION ProcessorInfoBuffer; + CPU_FEATURES_ENTRY *CpuFeature; + CPU_FEATURES_INIT_ORDER *InitOrder; + CPU_FEATURES_DATA *CpuFeaturesData; + LIST_ENTRY *Entry; + UINT32 Core; + UINT32 Package; + UINT32 Thread; + EFI_CPU_PHYSICAL_LOCATION *Location; + UINT32 PackageIndex; + UINT32 CoreIndex; + UINTN Pages; + UINT32 FirstPackage; + UINT32 *FirstCore; + UINT32 *FirstThread; + ACPI_CPU_DATA *AcpiCpuData; + CPU_STATUS_INFORMATION *CpuStatus; + UINT32 *ThreadCountPerPackage; + UINT8 *ThreadCountPerCore; + UINTN NumberOfCpus; + UINTN NumberOfEnabledProcessors; + + Core = 0; + Package = 0; + Thread = 0; + + CpuFeaturesData = GetCpuFeaturesData (); + + // + // Initialize CpuFeaturesData->MpService as early as possile, so later function can use it. + // + CpuFeaturesData->MpService = GetMpService (); + + GetNumberOfProcessor (&NumberOfCpus, &NumberOfEnabledProcessors); + + CpuFeaturesData->InitOrder = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (CPU_FEATURES_INIT_ORDER) * NumberOfCpus)); + ASSERT (CpuFeaturesData->InitOrder != NULL); + ZeroMem (CpuFeaturesData->InitOrder, sizeof (CPU_FEATURES_INIT_ORDER) * NumberOfCpus); + + // + // Collect CPU Features information + // + Entry = GetFirstNode (&CpuFeaturesData->FeatureList); + while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) { + CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (Entry); + ASSERT (CpuFeature->InitializeFunc != NULL); + if (CpuFeature->GetConfigDataFunc != NULL) { + CpuFeature->ConfigData = CpuFeature->GetConfigDataFunc (NumberOfCpus); + } + Entry = Entry->ForwardLink; + } + + CpuFeaturesData->NumberOfCpus = (UINT32) NumberOfCpus; + + AcpiCpuData = GetAcpiCpuData (); + ASSERT (AcpiCpuData != NULL); + CpuFeaturesData->AcpiCpuData= AcpiCpuData; + + CpuStatus = &AcpiCpuData->CpuStatus; + Location = AllocateZeroPool (sizeof (EFI_CPU_PHYSICAL_LOCATION) * NumberOfCpus); + ASSERT (Location != NULL); + AcpiCpuData->ApLocation = (EFI_PHYSICAL_ADDRESS)(UINTN)Location; + + for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) { + InitOrder = &CpuFeaturesData->InitOrder[ProcessorNumber]; + InitOrder->FeaturesSupportedMask = AllocateZeroPool (CpuFeaturesData->BitMaskSize); + ASSERT (InitOrder->FeaturesSupportedMask != NULL); + InitializeListHead (&InitOrder->OrderList); + Status = GetProcessorInformation (ProcessorNumber, &ProcessorInfoBuffer); + ASSERT_EFI_ERROR (Status); + CopyMem ( + &InitOrder->CpuInfo.ProcessorInfo, + &ProcessorInfoBuffer, + sizeof (EFI_PROCESSOR_INFORMATION) + ); + CopyMem ( + &Location[ProcessorNumber], + &ProcessorInfoBuffer.Location, + sizeof (EFI_CPU_PHYSICAL_LOCATION) + ); + + // + // Collect CPU package count info. + // + if (Package < ProcessorInfoBuffer.Location.Package) { + Package = ProcessorInfoBuffer.Location.Package; + } + // + // Collect CPU max core count info. + // + if (Core < ProcessorInfoBuffer.Location.Core) { + Core = ProcessorInfoBuffer.Location.Core; + } + // + // Collect CPU max thread count info. + // + if (Thread < ProcessorInfoBuffer.Location.Thread) { + Thread = ProcessorInfoBuffer.Location.Thread; + } + } + CpuStatus->PackageCount = Package + 1; + CpuStatus->MaxCoreCount = Core + 1; + CpuStatus->MaxThreadCount = Thread + 1; + DEBUG ((DEBUG_INFO, "Processor Info: Package: %d, MaxCore : %d, MaxThread: %d\n", + CpuStatus->PackageCount, + CpuStatus->MaxCoreCount, + CpuStatus->MaxThreadCount)); + + // + // Collect valid core count in each package because not all cores are valid. + // + ThreadCountPerPackage = AllocateZeroPool (sizeof (UINT32) * CpuStatus->PackageCount); + ASSERT (ThreadCountPerPackage != NULL); + CpuStatus->ThreadCountPerPackage = (EFI_PHYSICAL_ADDRESS)(UINTN)ThreadCountPerPackage; + + ThreadCountPerCore = AllocateZeroPool (sizeof (UINT8) * CpuStatus->PackageCount * CpuStatus->MaxCoreCount); + ASSERT (ThreadCountPerCore != NULL); + CpuStatus->ThreadCountPerCore = (EFI_PHYSICAL_ADDRESS)(UINTN)ThreadCountPerCore; + + for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) { + Location = &CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.ProcessorInfo.Location; + ThreadCountPerPackage[Location->Package]++; + ThreadCountPerCore[Location->Package * CpuStatus->MaxCoreCount + Location->Core]++; + } + + for (PackageIndex = 0; PackageIndex < CpuStatus->PackageCount; PackageIndex++) { + if (ThreadCountPerPackage[PackageIndex] != 0) { + DEBUG ((DEBUG_INFO, "P%02d: Thread Count = %d\n", PackageIndex, ThreadCountPerPackage[PackageIndex])); + for (CoreIndex = 0; CoreIndex < CpuStatus->MaxCoreCount; CoreIndex++) { + if (ThreadCountPerCore[PackageIndex * CpuStatus->MaxCoreCount + CoreIndex] != 0) { + DEBUG (( + DEBUG_INFO, " P%02d C%04d, Thread Count = %d\n", PackageIndex, CoreIndex, + ThreadCountPerCore[PackageIndex * CpuStatus->MaxCoreCount + CoreIndex] + )); + } + } + } + } + + CpuFeaturesData->CpuFlags.CoreSemaphoreCount = AllocateZeroPool (sizeof (UINT32) * CpuStatus->PackageCount * CpuStatus->MaxCoreCount * CpuStatus->MaxThreadCount); + ASSERT (CpuFeaturesData->CpuFlags.CoreSemaphoreCount != NULL); + CpuFeaturesData->CpuFlags.PackageSemaphoreCount = AllocateZeroPool (sizeof (UINT32) * CpuStatus->PackageCount * CpuStatus->MaxCoreCount * CpuStatus->MaxThreadCount); + ASSERT (CpuFeaturesData->CpuFlags.PackageSemaphoreCount != NULL); + + // + // Initialize CpuFeaturesData->InitOrder[].CpuInfo.First + // Use AllocatePages () instead of AllocatePool () because pool cannot be freed in PEI phase but page can. + // + Pages = EFI_SIZE_TO_PAGES (CpuStatus->PackageCount * sizeof (UINT32) + CpuStatus->PackageCount * CpuStatus->MaxCoreCount * sizeof (UINT32)); + FirstCore = AllocatePages (Pages); + ASSERT (FirstCore != NULL); + FirstThread = FirstCore + CpuStatus->PackageCount; + + // + // Set FirstPackage, FirstCore[], FirstThread[] to maximum package ID, core ID, thread ID. + // + FirstPackage = MAX_UINT32; + SetMem32 (FirstCore, CpuStatus->PackageCount * sizeof (UINT32), MAX_UINT32); + SetMem32 (FirstThread, CpuStatus->PackageCount * CpuStatus->MaxCoreCount * sizeof (UINT32), MAX_UINT32); + + for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) { + Location = &CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.ProcessorInfo.Location; + + // + // Save the minimum package ID in the platform. + // + FirstPackage = MIN (Location->Package, FirstPackage); + + // + // Save the minimum core ID per package. + // + FirstCore[Location->Package] = MIN (Location->Core, FirstCore[Location->Package]); + + // + // Save the minimum thread ID per core. + // + FirstThread[Location->Package * CpuStatus->MaxCoreCount + Location->Core] = MIN ( + Location->Thread, + FirstThread[Location->Package * CpuStatus->MaxCoreCount + Location->Core] + ); + } + + // + // Update the First field. + // + for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) { + Location = &CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.ProcessorInfo.Location; + + if (Location->Package == FirstPackage) { + CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.First.Package = 1; + } + + // + // Set First.Die/Tile/Module for each thread assuming: + // single Die under each package, single Tile under each Die, single Module under each Tile + // + CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.First.Die = 1; + CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.First.Tile = 1; + CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.First.Module = 1; + + if (Location->Core == FirstCore[Location->Package]) { + CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.First.Core = 1; + } + if (Location->Thread == FirstThread[Location->Package * CpuStatus->MaxCoreCount + Location->Core]) { + CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo.First.Thread = 1; + } + } + + FreePages (FirstCore, Pages); +} + +/** + Worker function to do OR operation on CPU feature supported bits mask buffer. + + @param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer + @param[in] OrFeatureBitMask The feature bit mask to do OR operation + @param[in] BitMaskSize The CPU feature bits mask buffer size. + +**/ +VOID +SupportedMaskOr ( + IN UINT8 *SupportedFeatureMask, + IN UINT8 *OrFeatureBitMask, + IN UINT32 BitMaskSize + ) +{ + UINTN Index; + UINT8 *Data1; + UINT8 *Data2; + + Data1 = SupportedFeatureMask; + Data2 = OrFeatureBitMask; + for (Index = 0; Index < BitMaskSize; Index++) { + *(Data1++) |= *(Data2++); + } +} + +/** + Worker function to do AND operation on CPU feature supported bits mask buffer. + + @param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer + @param[in] AndFeatureBitMask The feature bit mask to do AND operation + @param[in] BitMaskSize CPU feature bits mask buffer size. + +**/ +VOID +SupportedMaskAnd ( + IN UINT8 *SupportedFeatureMask, + IN CONST UINT8 *AndFeatureBitMask, + IN UINT32 BitMaskSize + ) +{ + UINTN Index; + UINT8 *Data1; + CONST UINT8 *Data2; + + Data1 = SupportedFeatureMask; + Data2 = AndFeatureBitMask; + for (Index = 0; Index < BitMaskSize; Index++) { + *(Data1++) &= *(Data2++); + } +} + +/** + Worker function to clean bit operation on CPU feature supported bits mask buffer. + + @param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer + @param[in] AndFeatureBitMask The feature bit mask to do XOR operation + @param[in] BitMaskSize CPU feature bits mask buffer size. +**/ +VOID +SupportedMaskCleanBit ( + IN UINT8 *SupportedFeatureMask, + IN UINT8 *AndFeatureBitMask, + IN UINT32 BitMaskSize + ) +{ + UINTN Index; + UINT8 *Data1; + UINT8 *Data2; + + Data1 = SupportedFeatureMask; + Data2 = AndFeatureBitMask; + for (Index = 0; Index < BitMaskSize; Index++) { + *(Data1++) &= ~(*(Data2++)); + } +} + +/** + Worker function to check if the compared CPU feature set in the CPU feature + supported bits mask buffer. + + @param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer + @param[in] ComparedFeatureBitMask The feature bit mask to be compared + @param[in] BitMaskSize CPU feature bits mask buffer size. + + @retval TRUE The ComparedFeatureBitMask is set in CPU feature supported bits + mask buffer. + @retval FALSE The ComparedFeatureBitMask is not set in CPU feature supported bits + mask buffer. +**/ +BOOLEAN +IsBitMaskMatch ( + IN UINT8 *SupportedFeatureMask, + IN UINT8 *ComparedFeatureBitMask, + IN UINT32 BitMaskSize + ) +{ + UINTN Index; + UINT8 *Data1; + UINT8 *Data2; + + Data1 = SupportedFeatureMask; + Data2 = ComparedFeatureBitMask; + for (Index = 0; Index < BitMaskSize; Index++) { + if (((*(Data1++)) & (*(Data2++))) != 0) { + return TRUE; + } + } + return FALSE; +} + +/** + Collects processor data for calling processor. + + @param[in,out] Buffer The pointer to private data buffer. +**/ +VOID +EFIAPI +CollectProcessorData ( + IN OUT VOID *Buffer + ) +{ + UINTN ProcessorNumber; + CPU_FEATURES_ENTRY *CpuFeature; + REGISTER_CPU_FEATURE_INFORMATION *CpuInfo; + LIST_ENTRY *Entry; + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = (CPU_FEATURES_DATA *)Buffer; + ProcessorNumber = GetProcessorIndex (CpuFeaturesData); + CpuInfo = &CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo; + // + // collect processor information + // + FillProcessorInfo (CpuInfo); + Entry = GetFirstNode (&CpuFeaturesData->FeatureList); + while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) { + CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (Entry); + if (CpuFeature->SupportFunc == NULL) { + // + // If SupportFunc is NULL, then the feature is supported. + // + SupportedMaskOr ( + CpuFeaturesData->InitOrder[ProcessorNumber].FeaturesSupportedMask, + CpuFeature->FeatureMask, + CpuFeaturesData->BitMaskSize + ); + } else if (CpuFeature->SupportFunc (ProcessorNumber, CpuInfo, CpuFeature->ConfigData)) { + SupportedMaskOr ( + CpuFeaturesData->InitOrder[ProcessorNumber].FeaturesSupportedMask, + CpuFeature->FeatureMask, + CpuFeaturesData->BitMaskSize + ); + } + Entry = Entry->ForwardLink; + } +} + +/** + Dump the contents of a CPU register table. + + @param[in] ProcessorNumber The index of the CPU to show the register table contents + + @note This service could be called by BSP only. +**/ +VOID +DumpRegisterTableOnProcessor ( + IN UINTN ProcessorNumber + ) +{ + CPU_FEATURES_DATA *CpuFeaturesData; + UINTN FeatureIndex; + CPU_REGISTER_TABLE *RegisterTable; + CPU_REGISTER_TABLE_ENTRY *RegisterTableEntry; + CPU_REGISTER_TABLE_ENTRY *RegisterTableEntryHead; + UINT32 DebugPrintErrorLevel; + + DebugPrintErrorLevel = (ProcessorNumber == 0) ? DEBUG_INFO : DEBUG_VERBOSE; + CpuFeaturesData = GetCpuFeaturesData (); + // + // Debug information + // + RegisterTable = &CpuFeaturesData->RegisterTable[ProcessorNumber]; + DEBUG ((DebugPrintErrorLevel, "RegisterTable->TableLength = %d\n", RegisterTable->TableLength)); + + RegisterTableEntryHead = (CPU_REGISTER_TABLE_ENTRY *) (UINTN) RegisterTable->RegisterTableEntry; + + for (FeatureIndex = 0; FeatureIndex < RegisterTable->TableLength; FeatureIndex++) { + RegisterTableEntry = &RegisterTableEntryHead[FeatureIndex]; + switch (RegisterTableEntry->RegisterType) { + case Msr: + DEBUG (( + DebugPrintErrorLevel, + "Processor: %04d: Index %04d, MSR : %08x, Bit Start: %02d, Bit Length: %02d, Value: %016lx\r\n", + (UINT32) ProcessorNumber, + (UINT32) FeatureIndex, + RegisterTableEntry->Index, + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitLength, + RegisterTableEntry->Value + )); + break; + case ControlRegister: + DEBUG (( + DebugPrintErrorLevel, + "Processor: %04d: Index %04d, CR : %08x, Bit Start: %02d, Bit Length: %02d, Value: %016lx\r\n", + (UINT32) ProcessorNumber, + (UINT32) FeatureIndex, + RegisterTableEntry->Index, + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitLength, + RegisterTableEntry->Value + )); + break; + case MemoryMapped: + DEBUG (( + DebugPrintErrorLevel, + "Processor: %04d: Index %04d, MMIO : %016lx, Bit Start: %02d, Bit Length: %02d, Value: %016lx\r\n", + (UINT32) ProcessorNumber, + (UINT32) FeatureIndex, + RegisterTableEntry->Index | LShiftU64 (RegisterTableEntry->HighIndex, 32), + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitLength, + RegisterTableEntry->Value + )); + break; + case CacheControl: + DEBUG (( + DebugPrintErrorLevel, + "Processor: %04d: Index %04d, CACHE: %08x, Bit Start: %02d, Bit Length: %02d, Value: %016lx\r\n", + (UINT32) ProcessorNumber, + (UINT32) FeatureIndex, + RegisterTableEntry->Index, + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitLength, + RegisterTableEntry->Value + )); + break; + case Semaphore: + DEBUG (( + DebugPrintErrorLevel, + "Processor: %04d: Index %04d, SEMAP: %s\r\n", + (UINT32) ProcessorNumber, + (UINT32) FeatureIndex, + mDependTypeStr[MIN ((UINT32)RegisterTableEntry->Value, InvalidDepType)] + )); + break; + + default: + break; + } + } +} + +/** + Get the biggest dependence type. + PackageDepType > CoreDepType > ThreadDepType > NoneDepType. + + @param[in] BeforeDep Before dependence type. + @param[in] AfterDep After dependence type. + @param[in] NoneNeibBeforeDep Before dependence type for not neighborhood features. + @param[in] NoneNeibAfterDep After dependence type for not neighborhood features. + + @retval Return the biggest dependence type. +**/ +CPU_FEATURE_DEPENDENCE_TYPE +BiggestDep ( + IN CPU_FEATURE_DEPENDENCE_TYPE BeforeDep, + IN CPU_FEATURE_DEPENDENCE_TYPE AfterDep, + IN CPU_FEATURE_DEPENDENCE_TYPE NoneNeibBeforeDep, + IN CPU_FEATURE_DEPENDENCE_TYPE NoneNeibAfterDep + ) +{ + CPU_FEATURE_DEPENDENCE_TYPE Bigger; + + Bigger = MAX (BeforeDep, AfterDep); + Bigger = MAX (Bigger, NoneNeibBeforeDep); + return MAX(Bigger, NoneNeibAfterDep); +} + +/** + Analysis register CPU features on each processor and save CPU setting in CPU register table. + + @param[in] NumberOfCpus Number of processor in system + +**/ +VOID +AnalysisProcessorFeatures ( + IN UINTN NumberOfCpus + ) +{ + EFI_STATUS Status; + UINTN ProcessorNumber; + CPU_FEATURES_ENTRY *CpuFeature; + CPU_FEATURES_ENTRY *CpuFeatureInOrder; + CPU_FEATURES_INIT_ORDER *CpuInitOrder; + REGISTER_CPU_FEATURE_INFORMATION *CpuInfo; + LIST_ENTRY *Entry; + CPU_FEATURES_DATA *CpuFeaturesData; + LIST_ENTRY *NextEntry; + CPU_FEATURES_ENTRY *NextCpuFeatureInOrder; + BOOLEAN Success; + CPU_FEATURE_DEPENDENCE_TYPE BeforeDep; + CPU_FEATURE_DEPENDENCE_TYPE AfterDep; + CPU_FEATURE_DEPENDENCE_TYPE NoneNeibBeforeDep; + CPU_FEATURE_DEPENDENCE_TYPE NoneNeibAfterDep; + + CpuFeaturesData = GetCpuFeaturesData (); + CpuFeaturesData->CapabilityPcd = AllocatePool (CpuFeaturesData->BitMaskSize); + ASSERT (CpuFeaturesData->CapabilityPcd != NULL); + SetMem (CpuFeaturesData->CapabilityPcd, CpuFeaturesData->BitMaskSize, 0xFF); + for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) { + CpuInitOrder = &CpuFeaturesData->InitOrder[ProcessorNumber]; + // + // Calculate the last capability on all processors + // + SupportedMaskAnd (CpuFeaturesData->CapabilityPcd, CpuInitOrder->FeaturesSupportedMask, CpuFeaturesData->BitMaskSize); + } + // + // Calculate the last setting + // + CpuFeaturesData->SettingPcd = AllocateCopyPool (CpuFeaturesData->BitMaskSize, CpuFeaturesData->CapabilityPcd); + ASSERT (CpuFeaturesData->SettingPcd != NULL); + SupportedMaskAnd (CpuFeaturesData->SettingPcd, PcdGetPtr (PcdCpuFeaturesSetting), CpuFeaturesData->BitMaskSize); + + // + // Dump the last CPU feature list + // + DEBUG_CODE ( + DEBUG ((DEBUG_INFO, "Last CPU features list...\n")); + Entry = GetFirstNode (&CpuFeaturesData->FeatureList); + while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) { + CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (Entry); + if (IsBitMaskMatch (CpuFeature->FeatureMask, CpuFeaturesData->CapabilityPcd, CpuFeaturesData->BitMaskSize)) { + if (IsBitMaskMatch (CpuFeature->FeatureMask, CpuFeaturesData->SettingPcd, CpuFeaturesData->BitMaskSize)) { + DEBUG ((DEBUG_INFO, "[Enable ] ")); + } else { + DEBUG ((DEBUG_INFO, "[Disable ] ")); + } + } else { + DEBUG ((DEBUG_INFO, "[Unsupport] ")); + } + DumpCpuFeature (CpuFeature, CpuFeaturesData->BitMaskSize); + Entry = Entry->ForwardLink; + } + DEBUG ((DEBUG_INFO, "PcdCpuFeaturesCapability:\n")); + DumpCpuFeatureMask (CpuFeaturesData->CapabilityPcd, CpuFeaturesData->BitMaskSize); + DEBUG ((DEBUG_INFO, "Origin PcdCpuFeaturesSetting:\n")); + DumpCpuFeatureMask (PcdGetPtr (PcdCpuFeaturesSetting), CpuFeaturesData->BitMaskSize); + DEBUG ((DEBUG_INFO, "Final PcdCpuFeaturesSetting:\n")); + DumpCpuFeatureMask (CpuFeaturesData->SettingPcd, CpuFeaturesData->BitMaskSize); + ); + + // + // Save PCDs and display CPU PCDs + // + SetCapabilityPcd (CpuFeaturesData->CapabilityPcd, CpuFeaturesData->BitMaskSize); + SetSettingPcd (CpuFeaturesData->SettingPcd, CpuFeaturesData->BitMaskSize); + + for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) { + CpuInitOrder = &CpuFeaturesData->InitOrder[ProcessorNumber]; + Entry = GetFirstNode (&CpuFeaturesData->FeatureList); + while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) { + // + // Insert each feature into processor's order list + // + CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (Entry); + if (IsBitMaskMatch (CpuFeature->FeatureMask, CpuFeaturesData->CapabilityPcd, CpuFeaturesData->BitMaskSize)) { + CpuFeatureInOrder = AllocateCopyPool (sizeof (CPU_FEATURES_ENTRY), CpuFeature); + ASSERT (CpuFeatureInOrder != NULL); + InsertTailList (&CpuInitOrder->OrderList, &CpuFeatureInOrder->Link); + } + Entry = Entry->ForwardLink; + } + // + // Go through ordered feature list to initialize CPU features + // + CpuInfo = &CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo; + Entry = GetFirstNode (&CpuInitOrder->OrderList); + while (!IsNull (&CpuInitOrder->OrderList, Entry)) { + CpuFeatureInOrder = CPU_FEATURE_ENTRY_FROM_LINK (Entry); + + Success = FALSE; + if (IsBitMaskMatch (CpuFeatureInOrder->FeatureMask, CpuFeaturesData->SettingPcd, CpuFeaturesData->BitMaskSize)) { + Status = CpuFeatureInOrder->InitializeFunc (ProcessorNumber, CpuInfo, CpuFeatureInOrder->ConfigData, TRUE); + if (EFI_ERROR (Status)) { + // + // Clean the CpuFeatureInOrder->FeatureMask in setting PCD. + // + SupportedMaskCleanBit (CpuFeaturesData->SettingPcd, CpuFeatureInOrder->FeatureMask, CpuFeaturesData->BitMaskSize); + if (CpuFeatureInOrder->FeatureName != NULL) { + DEBUG ((DEBUG_WARN, "Warning :: Failed to enable Feature: Name = %a.\n", CpuFeatureInOrder->FeatureName)); + } else { + DEBUG ((DEBUG_WARN, "Warning :: Failed to enable Feature: Mask = ")); + DumpCpuFeatureMask (CpuFeatureInOrder->FeatureMask, CpuFeaturesData->BitMaskSize); + } + } else { + Success = TRUE; + } + } else { + Status = CpuFeatureInOrder->InitializeFunc (ProcessorNumber, CpuInfo, CpuFeatureInOrder->ConfigData, FALSE); + if (EFI_ERROR (Status)) { + if (CpuFeatureInOrder->FeatureName != NULL) { + DEBUG ((DEBUG_WARN, "Warning :: Failed to disable Feature: Name = %a.\n", CpuFeatureInOrder->FeatureName)); + } else { + DEBUG ((DEBUG_WARN, "Warning :: Failed to disable Feature: Mask = ")); + DumpCpuFeatureMask (CpuFeatureInOrder->FeatureMask, CpuFeaturesData->BitMaskSize); + } + } else { + Success = TRUE; + } + } + + if (Success) { + NextEntry = Entry->ForwardLink; + if (!IsNull (&CpuInitOrder->OrderList, NextEntry)) { + NextCpuFeatureInOrder = CPU_FEATURE_ENTRY_FROM_LINK (NextEntry); + + // + // If feature has dependence with the next feature (ONLY care core/package dependency). + // and feature initialize succeed, add sync semaphere here. + // + BeforeDep = DetectFeatureScope (CpuFeatureInOrder, TRUE, NextCpuFeatureInOrder->FeatureMask); + AfterDep = DetectFeatureScope (NextCpuFeatureInOrder, FALSE, CpuFeatureInOrder->FeatureMask); + // + // Check whether next feature has After type dependence with not neighborhood CPU + // Features in former CPU features. + // + NoneNeibAfterDep = DetectNoneNeighborhoodFeatureScope(NextCpuFeatureInOrder, FALSE, &CpuInitOrder->OrderList); + } else { + BeforeDep = NoneDepType; + AfterDep = NoneDepType; + NoneNeibAfterDep = NoneDepType; + } + // + // Check whether current feature has Before type dependence with none neighborhood + // CPU features in after Cpu features. + // + NoneNeibBeforeDep = DetectNoneNeighborhoodFeatureScope(CpuFeatureInOrder, TRUE, &CpuInitOrder->OrderList); + + // + // Get the biggest dependence and add semaphore for it. + // PackageDepType > CoreDepType > ThreadDepType > NoneDepType. + // + BeforeDep = BiggestDep(BeforeDep, AfterDep, NoneNeibBeforeDep, NoneNeibAfterDep); + if (BeforeDep > ThreadDepType) { + CPU_REGISTER_TABLE_WRITE32 (ProcessorNumber, Semaphore, 0, BeforeDep); + } + } + + Entry = Entry->ForwardLink; + } + + // + // Dump PcdCpuFeaturesSetting again because this value maybe updated + // again during initialize the features. + // + DEBUG ((DEBUG_INFO, "Dump final value for PcdCpuFeaturesSetting:\n")); + DumpCpuFeatureMask (CpuFeaturesData->SettingPcd, CpuFeaturesData->BitMaskSize); + + // + // Dump the RegisterTable + // + DumpRegisterTableOnProcessor (ProcessorNumber); + } +} + +/** + Increment semaphore by 1. + + @param Sem IN: 32-bit unsigned integer + +**/ +VOID +LibReleaseSemaphore ( + IN OUT volatile UINT32 *Sem + ) +{ + InterlockedIncrement (Sem); +} + +/** + Decrement the semaphore by 1 if it is not zero. + + Performs an atomic decrement operation for semaphore. + The compare exchange operation must be performed using + MP safe mechanisms. + + @param Sem IN: 32-bit unsigned integer + +**/ +VOID +LibWaitForSemaphore ( + IN OUT volatile UINT32 *Sem + ) +{ + UINT32 Value; + + do { + Value = *Sem; + } while (Value == 0 || + InterlockedCompareExchange32 ( + Sem, + Value, + Value - 1 + ) != Value); +} + +/** + Read / write CR value. + + @param[in] CrIndex The CR index which need to read/write. + @param[in] Read Read or write. TRUE is read. + @param[in,out] CrValue CR value. + + @retval EFI_SUCCESS means read/write success, else return EFI_UNSUPPORTED. +**/ +UINTN +ReadWriteCr ( + IN UINT32 CrIndex, + IN BOOLEAN Read, + IN OUT UINTN *CrValue + ) +{ + switch (CrIndex) { + case 0: + if (Read) { + *CrValue = AsmReadCr0 (); + } else { + AsmWriteCr0 (*CrValue); + } + break; + case 2: + if (Read) { + *CrValue = AsmReadCr2 (); + } else { + AsmWriteCr2 (*CrValue); + } + break; + case 3: + if (Read) { + *CrValue = AsmReadCr3 (); + } else { + AsmWriteCr3 (*CrValue); + } + break; + case 4: + if (Read) { + *CrValue = AsmReadCr4 (); + } else { + AsmWriteCr4 (*CrValue); + } + break; + default: + return EFI_UNSUPPORTED;; + } + + return EFI_SUCCESS; +} + +/** + Initialize the CPU registers from a register table. + + @param[in] RegisterTable The register table for this AP. + @param[in] ApLocation AP location info for this ap. + @param[in] CpuStatus CPU status info for this CPU. + @param[in] CpuFlags Flags data structure used when program the register. + + @note This service could be called by BSP/APs. +**/ +VOID +ProgramProcessorRegister ( + IN CPU_REGISTER_TABLE *RegisterTable, + IN EFI_CPU_PHYSICAL_LOCATION *ApLocation, + IN CPU_STATUS_INFORMATION *CpuStatus, + IN PROGRAM_CPU_REGISTER_FLAGS *CpuFlags + ) +{ + CPU_REGISTER_TABLE_ENTRY *RegisterTableEntry; + UINTN Index; + UINTN Value; + CPU_REGISTER_TABLE_ENTRY *RegisterTableEntryHead; + volatile UINT32 *SemaphorePtr; + UINT32 FirstThread; + UINT32 CurrentThread; + UINT32 CurrentCore; + UINTN ProcessorIndex; + UINT32 *ThreadCountPerPackage; + UINT8 *ThreadCountPerCore; + EFI_STATUS Status; + UINT64 CurrentValue; + + // + // Traverse Register Table of this logical processor + // + RegisterTableEntryHead = (CPU_REGISTER_TABLE_ENTRY *) (UINTN) RegisterTable->RegisterTableEntry; + + for (Index = 0; Index < RegisterTable->TableLength; Index++) { + + RegisterTableEntry = &RegisterTableEntryHead[Index]; + + // + // Check the type of specified register + // + switch (RegisterTableEntry->RegisterType) { + // + // The specified register is Control Register + // + case ControlRegister: + Status = ReadWriteCr (RegisterTableEntry->Index, TRUE, &Value); + if (EFI_ERROR (Status)) { + break; + } + if (RegisterTableEntry->TestThenWrite) { + CurrentValue = BitFieldRead64 ( + Value, + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1 + ); + if (CurrentValue == RegisterTableEntry->Value) { + break; + } + } + Value = (UINTN) BitFieldWrite64 ( + Value, + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, + RegisterTableEntry->Value + ); + ReadWriteCr (RegisterTableEntry->Index, FALSE, &Value); + break; + + // + // The specified register is Model Specific Register + // + case Msr: + if (RegisterTableEntry->TestThenWrite) { + Value = (UINTN)AsmReadMsr64 (RegisterTableEntry->Index); + if (RegisterTableEntry->ValidBitLength >= 64) { + if (Value == RegisterTableEntry->Value) { + break; + } + } else { + CurrentValue = BitFieldRead64 ( + Value, + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1 + ); + if (CurrentValue == RegisterTableEntry->Value) { + break; + } + } + } + + if (RegisterTableEntry->ValidBitLength >= 64) { + // + // If length is not less than 64 bits, then directly write without reading + // + AsmWriteMsr64 ( + RegisterTableEntry->Index, + RegisterTableEntry->Value + ); + } else { + // + // Set the bit section according to bit start and length + // + AsmMsrBitFieldWrite64 ( + RegisterTableEntry->Index, + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, + RegisterTableEntry->Value + ); + } + break; + // + // MemoryMapped operations + // + case MemoryMapped: + AcquireSpinLock (&CpuFlags->MemoryMappedLock); + MmioBitFieldWrite32 ( + (UINTN)(RegisterTableEntry->Index | LShiftU64 (RegisterTableEntry->HighIndex, 32)), + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, + (UINT32)RegisterTableEntry->Value + ); + ReleaseSpinLock (&CpuFlags->MemoryMappedLock); + break; + // + // Enable or disable cache + // + case CacheControl: + // + // If value of the entry is 0, then disable cache. Otherwise, enable cache. + // + if (RegisterTableEntry->Value == 0) { + AsmDisableCache (); + } else { + AsmEnableCache (); + } + break; + + case Semaphore: + // Semaphore works logic like below: + // + // V(x) = LibReleaseSemaphore (Semaphore[FirstThread + x]); + // P(x) = LibWaitForSemaphore (Semaphore[FirstThread + x]); + // + // All threads (T0...Tn) waits in P() line and continues running + // together. + // + // + // T0 T1 ... Tn + // + // V(0...n) V(0...n) ... V(0...n) + // n * P(0) n * P(1) ... n * P(n) + // + switch (RegisterTableEntry->Value) { + case CoreDepType: + SemaphorePtr = CpuFlags->CoreSemaphoreCount; + ThreadCountPerCore = (UINT8 *)(UINTN)CpuStatus->ThreadCountPerCore; + + CurrentCore = ApLocation->Package * CpuStatus->MaxCoreCount + ApLocation->Core; + // + // Get Offset info for the first thread in the core which current thread belongs to. + // + FirstThread = CurrentCore * CpuStatus->MaxThreadCount; + CurrentThread = FirstThread + ApLocation->Thread; + + // + // Different cores may have different valid threads in them. If driver maintail clearly + // thread index in different cores, the logic will be much complicated. + // Here driver just simply records the max thread number in all cores and use it as expect + // thread number for all cores. + // In below two steps logic, first current thread will Release semaphore for each thread + // in current core. Maybe some threads are not valid in this core, but driver don't + // care. Second, driver will let current thread wait semaphore for all valid threads in + // current core. Because only the valid threads will do release semaphore for this + // thread, driver here only need to wait the valid thread count. + // + + // + // First Notify ALL THREADs in current Core that this thread is ready. + // + for (ProcessorIndex = 0; ProcessorIndex < CpuStatus->MaxThreadCount; ProcessorIndex ++) { + LibReleaseSemaphore (&SemaphorePtr[FirstThread + ProcessorIndex]); + } + // + // Second, check whether all VALID THREADs (not all threads) in current core are ready. + // + for (ProcessorIndex = 0; ProcessorIndex < ThreadCountPerCore[CurrentCore]; ProcessorIndex ++) { + LibWaitForSemaphore (&SemaphorePtr[CurrentThread]); + } + break; + + case PackageDepType: + SemaphorePtr = CpuFlags->PackageSemaphoreCount; + ThreadCountPerPackage = (UINT32 *)(UINTN)CpuStatus->ThreadCountPerPackage; + // + // Get Offset info for the first thread in the package which current thread belongs to. + // + FirstThread = ApLocation->Package * CpuStatus->MaxCoreCount * CpuStatus->MaxThreadCount; + // + // Get the possible threads count for current package. + // + CurrentThread = FirstThread + CpuStatus->MaxThreadCount * ApLocation->Core + ApLocation->Thread; + + // + // Different packages may have different valid threads in them. If driver maintail clearly + // thread index in different packages, the logic will be much complicated. + // Here driver just simply records the max thread number in all packages and use it as expect + // thread number for all packages. + // In below two steps logic, first current thread will Release semaphore for each thread + // in current package. Maybe some threads are not valid in this package, but driver don't + // care. Second, driver will let current thread wait semaphore for all valid threads in + // current package. Because only the valid threads will do release semaphore for this + // thread, driver here only need to wait the valid thread count. + // + + // + // First Notify ALL THREADS in current package that this thread is ready. + // + for (ProcessorIndex = 0; ProcessorIndex < CpuStatus->MaxThreadCount * CpuStatus->MaxCoreCount; ProcessorIndex ++) { + LibReleaseSemaphore (&SemaphorePtr[FirstThread + ProcessorIndex]); + } + // + // Second, check whether VALID THREADS (not all threads) in current package are ready. + // + for (ProcessorIndex = 0; ProcessorIndex < ThreadCountPerPackage[ApLocation->Package]; ProcessorIndex ++) { + LibWaitForSemaphore (&SemaphorePtr[CurrentThread]); + } + break; + + default: + break; + } + break; + + default: + break; + } + } +} + +/** + Programs registers for the calling processor. + + @param[in,out] Buffer The pointer to private data buffer. + +**/ +VOID +EFIAPI +SetProcessorRegister ( + IN OUT VOID *Buffer + ) +{ + CPU_FEATURES_DATA *CpuFeaturesData; + CPU_REGISTER_TABLE *RegisterTable; + CPU_REGISTER_TABLE *RegisterTables; + UINT32 InitApicId; + UINTN ProcIndex; + UINTN Index; + ACPI_CPU_DATA *AcpiCpuData; + + CpuFeaturesData = (CPU_FEATURES_DATA *) Buffer; + AcpiCpuData = CpuFeaturesData->AcpiCpuData; + + RegisterTables = (CPU_REGISTER_TABLE *)(UINTN)AcpiCpuData->RegisterTable; + + InitApicId = GetInitialApicId (); + RegisterTable = NULL; + ProcIndex = (UINTN)-1; + for (Index = 0; Index < AcpiCpuData->NumberOfCpus; Index++) { + if (RegisterTables[Index].InitialApicId == InitApicId) { + RegisterTable = &RegisterTables[Index]; + ProcIndex = Index; + break; + } + } + ASSERT (RegisterTable != NULL); + + ProgramProcessorRegister ( + RegisterTable, + (EFI_CPU_PHYSICAL_LOCATION *)(UINTN)AcpiCpuData->ApLocation + ProcIndex, + &AcpiCpuData->CpuStatus, + &CpuFeaturesData->CpuFlags + ); +} + +/** + Performs CPU features detection. + + This service will invoke MP service to check CPU features' + capabilities on BSP/APs. + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +CpuFeaturesDetect ( + VOID + ) +{ + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = GetCpuFeaturesData(); + + CpuInitDataInitialize (); + + if (CpuFeaturesData->NumberOfCpus > 1) { + // + // Wakeup all APs for data collection. + // + StartupAllAPsWorker (CollectProcessorData, NULL); + } + + // + // Collect data on BSP + // + CollectProcessorData (CpuFeaturesData); + + AnalysisProcessorFeatures (CpuFeaturesData->NumberOfCpus); +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/DxeRegisterCpuFeaturesLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/DxeRegisterCpuFeaturesLib.c new file mode 100644 index 00000000..df43c63d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/DxeRegisterCpuFeaturesLib.c @@ -0,0 +1,276 @@ +/** @file + CPU Register Table Library functions. + + Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> + +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> + +#include "RegisterCpuFeatures.h" + +CPU_FEATURES_DATA mCpuFeaturesData = {0}; + +/** + Worker function to get CPU_FEATURES_DATA pointer. + + @return Pointer to CPU_FEATURES_DATA. +**/ +CPU_FEATURES_DATA * +GetCpuFeaturesData ( + VOID + ) +{ + return &mCpuFeaturesData; +} + +/** + Worker function to get EFI_MP_SERVICES_PROTOCOL pointer. + + @return MP_SERVICES variable. +**/ +MP_SERVICES +GetMpService ( + VOID + ) +{ + EFI_STATUS Status; + MP_SERVICES MpService; + + // + // Get MP Services Protocol + // + Status = gBS->LocateProtocol ( + &gEfiMpServiceProtocolGuid, + NULL, + (VOID **)&MpService.Protocol + ); + ASSERT_EFI_ERROR (Status); + + return MpService; +} + +/** + Worker function to return processor index. + + @param CpuFeaturesData Cpu Feature Data structure. + + @return The processor index. +**/ +UINTN +GetProcessorIndex ( + IN CPU_FEATURES_DATA *CpuFeaturesData + ) +{ + EFI_STATUS Status; + UINTN ProcessorIndex; + EFI_MP_SERVICES_PROTOCOL *MpServices; + + MpServices = CpuFeaturesData->MpService.Protocol; + Status = MpServices->WhoAmI(MpServices, &ProcessorIndex); + ASSERT_EFI_ERROR (Status); + return ProcessorIndex; +} + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. + + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + + @return Status of MpServices->GetProcessorInfo(). +**/ +EFI_STATUS +GetProcessorInformation ( + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ) +{ + EFI_STATUS Status; + EFI_MP_SERVICES_PROTOCOL *MpServices; + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = GetCpuFeaturesData (); + MpServices = CpuFeaturesData->MpService.Protocol; + + Status = MpServices->GetProcessorInfo ( + MpServices, + ProcessorNumber, + ProcessorInfoBuffer + ); + return Status; +} + +/** + Worker function to execute a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] MpEvent A pointer to the event to be used later + to check whether procedure has done. +**/ +VOID +StartupAllAPsWorker ( + IN EFI_AP_PROCEDURE Procedure, + IN EFI_EVENT MpEvent + ) +{ + EFI_STATUS Status; + EFI_MP_SERVICES_PROTOCOL *MpServices; + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = GetCpuFeaturesData (); + MpServices = CpuFeaturesData->MpService.Protocol; + + // + // Wakeup all APs + // + Status = MpServices->StartupAllAPs ( + MpServices, + Procedure, + FALSE, + MpEvent, + 0, + CpuFeaturesData, + NULL + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Worker function to switch the requested AP to be the BSP from that point onward. + + @param[in] ProcessorNumber The handle number of AP that is to become the new BSP. +**/ +VOID +SwitchNewBsp ( + IN UINTN ProcessorNumber + ) +{ + EFI_STATUS Status; + EFI_MP_SERVICES_PROTOCOL *MpServices; + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = GetCpuFeaturesData (); + MpServices = CpuFeaturesData->MpService.Protocol; + + // + // Wakeup all APs + // + Status = MpServices->SwitchBSP ( + MpServices, + ProcessorNumber, + TRUE + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Worker function to retrieve the number of logical processor in the platform. + + @param[out] NumberOfCpus Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. +**/ +VOID +GetNumberOfProcessor ( + OUT UINTN *NumberOfCpus, + OUT UINTN *NumberOfEnabledProcessors + ) +{ + EFI_STATUS Status; + EFI_MP_SERVICES_PROTOCOL *MpServices; + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = GetCpuFeaturesData (); + MpServices = CpuFeaturesData->MpService.Protocol; + + // + // Get the number of CPUs + // + Status = MpServices->GetNumberOfProcessors ( + MpServices, + NumberOfCpus, + NumberOfEnabledProcessors + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Performs CPU features Initialization. + + This service will invoke MP service to perform CPU features + initialization on BSP/APs per user configuration. + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +CpuFeaturesInitialize ( + VOID + ) +{ + CPU_FEATURES_DATA *CpuFeaturesData; + UINTN OldBspNumber; + EFI_EVENT MpEvent; + EFI_STATUS Status; + + CpuFeaturesData = GetCpuFeaturesData (); + + OldBspNumber = GetProcessorIndex (CpuFeaturesData); + CpuFeaturesData->BspNumber = OldBspNumber; + + // + // + // Initialize MpEvent to suppress incorrect compiler/analyzer warnings. + // + MpEvent = NULL; + + if (CpuFeaturesData->NumberOfCpus > 1) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_CALLBACK, + EfiEventEmptyFunction, + NULL, + &MpEvent + ); + ASSERT_EFI_ERROR (Status); + + // + // Wakeup all APs for programming. + // + StartupAllAPsWorker (SetProcessorRegister, MpEvent); + } + + // + // Programming BSP + // + SetProcessorRegister (CpuFeaturesData); + + if (CpuFeaturesData->NumberOfCpus > 1) { + // + // Wait all processors to finish the task. + // + do { + Status = gBS->CheckEvent (MpEvent); + } while (Status == EFI_NOT_READY); + ASSERT_EFI_ERROR (Status); + } + + // + // Switch to new BSP if required + // + if (CpuFeaturesData->BspNumber != OldBspNumber) { + SwitchNewBsp (CpuFeaturesData->BspNumber); + } +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/DxeRegisterCpuFeaturesLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/DxeRegisterCpuFeaturesLib.inf new file mode 100644 index 00000000..7eca3c85 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/DxeRegisterCpuFeaturesLib.inf @@ -0,0 +1,57 @@ +## @file +# Register CPU Features Library DXE instance. +# +# Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeRegisterCpuFeaturesLib + MODULE_UNI_FILE = RegisterCpuFeaturesLib.uni + FILE_GUID = ADE8F745-AA2E-49f6-8ED4-746B34867E52 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = RegisterCpuFeaturesLib|DXE_DRIVER + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.common] + DxeRegisterCpuFeaturesLib.c + RegisterCpuFeaturesLib.c + RegisterCpuFeatures.h + CpuFeaturesInitialize.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + PcdLib + LocalApicLib + BaseMemoryLib + MemoryAllocationLib + SynchronizationLib + UefiBootServicesTableLib + IoLib + UefiBootServicesTableLib + UefiLib + +[Protocols] + gEfiMpServiceProtocolGuid ## CONSUMES + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuS3DataAddress ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesSupport ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesCapability ## PRODUCES + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesSetting ## PRODUCES ## CONSUMES + +[Depex] + gEfiMpServiceProtocolGuid AND gEdkiiCpuFeaturesSetDoneGuid diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/PeiRegisterCpuFeaturesLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/PeiRegisterCpuFeaturesLib.c new file mode 100644 index 00000000..860a5295 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/PeiRegisterCpuFeaturesLib.c @@ -0,0 +1,309 @@ +/** @file + CPU Register Table Library functions. + + Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiPei.h> + +#include <Library/HobLib.h> +#include <Library/PeiServicesLib.h> +#include <Library/PeiServicesTablePointerLib.h> +#include <Ppi/MpServices2.h> + +#include "RegisterCpuFeatures.h" + +#define REGISTER_CPU_FEATURES_GUID \ + { \ + 0xa694c467, 0x697a, 0x446b, { 0xb9, 0x29, 0x5b, 0x14, 0xa0, 0xcf, 0x39, 0xf } \ + } + +EFI_GUID mRegisterCpuFeaturesHobGuid = REGISTER_CPU_FEATURES_GUID; + +/** + Worker function to get CPU_FEATURES_DATA pointer. + + @return Pointer to CPU_FEATURES_DATA. +**/ +CPU_FEATURES_DATA * +GetCpuFeaturesData ( + VOID + ) +{ + CPU_FEATURES_DATA *CpuInitData; + EFI_HOB_GUID_TYPE *GuidHob; + VOID *DataInHob; + UINT64 Data64; + + CpuInitData = NULL; + GuidHob = GetFirstGuidHob (&mRegisterCpuFeaturesHobGuid); + if (GuidHob != NULL) { + DataInHob = GET_GUID_HOB_DATA (GuidHob); + CpuInitData = (CPU_FEATURES_DATA *) (*(UINTN *) DataInHob); + ASSERT (CpuInitData != NULL); + } else { + CpuInitData = AllocateZeroPool (sizeof (CPU_FEATURES_DATA)); + ASSERT (CpuInitData != NULL); + // + // Build location of CPU MP DATA buffer in HOB + // + Data64 = (UINT64) (UINTN) CpuInitData; + BuildGuidDataHob ( + &mRegisterCpuFeaturesHobGuid, + (VOID *) &Data64, + sizeof (UINT64) + ); + } + + return CpuInitData; +} + +/** + Worker function to get MP PPI service pointer. + + @return MP_SERVICES variable. +**/ +MP_SERVICES +GetMpService ( + VOID + ) +{ + EFI_STATUS Status; + MP_SERVICES MpService; + + // + // Get MP Services2 Ppi + // + Status = PeiServicesLocatePpi ( + &gEdkiiPeiMpServices2PpiGuid, + 0, + NULL, + (VOID **)&MpService.Ppi + ); + ASSERT_EFI_ERROR (Status); + return MpService; +} + +/** + Worker function to return processor index. + + @param CpuFeaturesData Cpu Feature Data structure. + + @return The processor index. +**/ +UINTN +GetProcessorIndex ( + IN CPU_FEATURES_DATA *CpuFeaturesData + ) +{ + EFI_STATUS Status; + EDKII_PEI_MP_SERVICES2_PPI *CpuMp2Ppi; + UINTN ProcessorIndex; + + CpuMp2Ppi = CpuFeaturesData->MpService.Ppi; + + // + // For two reasons which use NULL for WhoAmI: + // 1. This function will be called by APs and AP should not use PeiServices Table + // 2. Check WhoAmI implementation, this parameter will not be used. + // + Status = CpuMp2Ppi->WhoAmI (CpuMp2Ppi, &ProcessorIndex); + ASSERT_EFI_ERROR (Status); + return ProcessorIndex; +} + +/** + Worker function to MP-related information on the requested processor at the + instant this call is made. + + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + + @return Status of MpServices->GetProcessorInfo(). +**/ +EFI_STATUS +GetProcessorInformation ( + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ) +{ + EDKII_PEI_MP_SERVICES2_PPI *CpuMp2Ppi; + EFI_STATUS Status; + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = GetCpuFeaturesData (); + CpuMp2Ppi = CpuFeaturesData->MpService.Ppi; + + Status = CpuMp2Ppi->GetProcessorInfo ( + CpuMp2Ppi, + ProcessorNumber, + ProcessorInfoBuffer + ); + return Status; +} + +/** + Worker function to execute a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] MpEvent The Event used to sync the result. + +**/ +VOID +StartupAllAPsWorker ( + IN EFI_AP_PROCEDURE Procedure, + IN EFI_EVENT MpEvent + ) +{ + EFI_STATUS Status; + EDKII_PEI_MP_SERVICES2_PPI *CpuMp2Ppi; + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = GetCpuFeaturesData (); + CpuMp2Ppi = CpuFeaturesData->MpService.Ppi; + + // + // Wakeup all APs for data collection. + // + Status = CpuMp2Ppi->StartupAllAPs ( + CpuMp2Ppi, + Procedure, + FALSE, + 0, + CpuFeaturesData + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Worker function to execute a caller provided function on all enabled CPUs. + + @param[in] Procedure A pointer to the function to be run on + enabled CPUs of the system. + +**/ +VOID +StartupAllCPUsWorker ( + IN EFI_AP_PROCEDURE Procedure + ) +{ + EFI_STATUS Status; + EDKII_PEI_MP_SERVICES2_PPI *CpuMp2Ppi; + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = GetCpuFeaturesData (); + + // + // Get MP Services2 Ppi + // + CpuMp2Ppi = CpuFeaturesData->MpService.Ppi; + Status = CpuMp2Ppi->StartupAllCPUs ( + CpuMp2Ppi, + Procedure, + 0, + CpuFeaturesData + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Worker function to switch the requested AP to be the BSP from that point onward. + + @param[in] ProcessorNumber The handle number of AP that is to become the new BSP. +**/ +VOID +SwitchNewBsp ( + IN UINTN ProcessorNumber + ) +{ + EFI_STATUS Status; + EDKII_PEI_MP_SERVICES2_PPI *CpuMp2Ppi; + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = GetCpuFeaturesData (); + CpuMp2Ppi = CpuFeaturesData->MpService.Ppi; + + // + // Wakeup all APs for data collection. + // + Status = CpuMp2Ppi->SwitchBSP ( + CpuMp2Ppi, + ProcessorNumber, + TRUE + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Worker function to retrieve the number of logical processor in the platform. + + @param[out] NumberOfCpus Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. +**/ +VOID +GetNumberOfProcessor ( + OUT UINTN *NumberOfCpus, + OUT UINTN *NumberOfEnabledProcessors + ) +{ + EFI_STATUS Status; + EDKII_PEI_MP_SERVICES2_PPI *CpuMp2Ppi; + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = GetCpuFeaturesData (); + CpuMp2Ppi = CpuFeaturesData->MpService.Ppi; + + // + // Get the number of CPUs + // + Status = CpuMp2Ppi->GetNumberOfProcessors ( + CpuMp2Ppi, + NumberOfCpus, + NumberOfEnabledProcessors + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Performs CPU features Initialization. + + This service will invoke MP service to perform CPU features + initialization on BSP/APs per user configuration. + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +CpuFeaturesInitialize ( + VOID + ) +{ + CPU_FEATURES_DATA *CpuFeaturesData; + UINTN OldBspNumber; + + CpuFeaturesData = GetCpuFeaturesData (); + + OldBspNumber = GetProcessorIndex (CpuFeaturesData); + CpuFeaturesData->BspNumber = OldBspNumber; + + // + // Start to program register for all CPUs. + // + StartupAllCPUsWorker (SetProcessorRegister); + + // + // Switch to new BSP if required + // + if (CpuFeaturesData->BspNumber != OldBspNumber) { + SwitchNewBsp (CpuFeaturesData->BspNumber); + } +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/PeiRegisterCpuFeaturesLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/PeiRegisterCpuFeaturesLib.inf new file mode 100644 index 00000000..159cba73 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/PeiRegisterCpuFeaturesLib.inf @@ -0,0 +1,57 @@ +## @file +# Register CPU Features Library PEI instance. +# +# Copyright (c) 2017 - 2020, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PeiRegisterCpuFeaturesLib + MODULE_UNI_FILE = RegisterCpuFeaturesLib.uni + FILE_GUID = D8855DB3-8348-41B5-BDA4-385351767D41 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + LIBRARY_CLASS = RegisterCpuFeaturesLib|PEIM + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources.common] + PeiRegisterCpuFeaturesLib.c + RegisterCpuFeaturesLib.c + RegisterCpuFeatures.h + CpuFeaturesInitialize.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + PcdLib + LocalApicLib + BaseMemoryLib + MemoryAllocationLib + SynchronizationLib + HobLib + PeiServicesLib + PeiServicesTablePointerLib + IoLib + +[Ppis] + gEdkiiPeiMpServices2PpiGuid ## CONSUMES + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuS3DataAddress ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesSupport ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesCapability ## PRODUCES + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesSetting ## CONSUMES ## PRODUCES + +[Depex] + gEdkiiPeiMpServices2PpiGuid AND gEdkiiCpuFeaturesSetDoneGuid diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/RegisterCpuFeatures.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/RegisterCpuFeatures.h new file mode 100644 index 00000000..d798993a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/RegisterCpuFeatures.h @@ -0,0 +1,268 @@ +/** @file + CPU Register Table Library definitions. + + Copyright (c) 2017 - 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _REGISTER_CPU_FEATURES_H_ +#define _REGISTER_CPU_FEATURES_H_ +#include <PiPei.h> +#include <PiDxe.h> +#include <Ppi/MpServices2.h> +#include <Protocol/MpService.h> + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/PcdLib.h> +#include <Library/RegisterCpuFeaturesLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/SynchronizationLib.h> +#include <Library/IoLib.h> +#include <Library/LocalApicLib.h> + +#include <AcpiCpuData.h> + +#define CPU_FEATURE_ENTRY_SIGNATURE SIGNATURE_32 ('C', 'F', 'E', 'S') + +#define CPU_FEATURE_NAME_SIZE 128 + +typedef struct { + REGISTER_CPU_FEATURE_INFORMATION CpuInfo; + UINT8 *FeaturesSupportedMask; + LIST_ENTRY OrderList; +} CPU_FEATURES_INIT_ORDER; + +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + UINT8 *FeatureMask; + CHAR8 *FeatureName; + CPU_FEATURE_GET_CONFIG_DATA GetConfigDataFunc; + CPU_FEATURE_SUPPORT SupportFunc; + CPU_FEATURE_INITIALIZE InitializeFunc; + UINT8 *ThreadBeforeFeatureBitMask; + UINT8 *ThreadAfterFeatureBitMask; + UINT8 *CoreBeforeFeatureBitMask; + UINT8 *CoreAfterFeatureBitMask; + UINT8 *PackageBeforeFeatureBitMask; + UINT8 *PackageAfterFeatureBitMask; + VOID *ConfigData; + BOOLEAN BeforeAll; + BOOLEAN AfterAll; +} CPU_FEATURES_ENTRY; + +// +// Flags used when program the register. +// +typedef struct { + volatile UINTN MemoryMappedLock; // Spinlock used to program mmio + volatile UINT32 *CoreSemaphoreCount; // Semaphore containers used to program Core semaphore. + volatile UINT32 *PackageSemaphoreCount; // Semaphore containers used to program Package semaphore. +} PROGRAM_CPU_REGISTER_FLAGS; + +typedef union { + EFI_MP_SERVICES_PROTOCOL *Protocol; + EDKII_PEI_MP_SERVICES2_PPI *Ppi; +} MP_SERVICES; + +typedef struct { + UINTN FeaturesCount; + UINT32 BitMaskSize; + LIST_ENTRY FeatureList; + + CPU_FEATURES_INIT_ORDER *InitOrder; + UINT8 *CapabilityPcd; + UINT8 *SettingPcd; + + UINT32 NumberOfCpus; + ACPI_CPU_DATA *AcpiCpuData; + + CPU_REGISTER_TABLE *RegisterTable; + CPU_REGISTER_TABLE *PreSmmRegisterTable; + UINTN BspNumber; + + PROGRAM_CPU_REGISTER_FLAGS CpuFlags; + + MP_SERVICES MpService; +} CPU_FEATURES_DATA; + +#define CPU_FEATURE_ENTRY_FROM_LINK(a) \ + CR ( \ + (a), \ + CPU_FEATURES_ENTRY, \ + Link, \ + CPU_FEATURE_ENTRY_SIGNATURE \ + ) + +/** + Worker function to get CPU_FEATURES_DATA pointer. + + @return Pointer to CPU_FEATURES_DATA. +**/ +CPU_FEATURES_DATA * +GetCpuFeaturesData ( + VOID + ); + +/** + Worker function to return processor index. + + @param CpuFeaturesData Cpu Feature Data structure. + + @return The processor index. +**/ +UINTN +GetProcessorIndex ( + IN CPU_FEATURES_DATA *CpuFeaturesData + ); + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. + + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + + @return Status of MpServices->GetProcessorInfo(). +**/ +EFI_STATUS +GetProcessorInformation ( + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ); + +/** + Worker function to execute a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] MpEvent A pointer to the event to be used later + to check whether procedure has done. +**/ +VOID +StartupAllAPsWorker ( + IN EFI_AP_PROCEDURE Procedure, + IN EFI_EVENT MpEvent + ); + +/** + Worker function to retrieve the number of logical processor in the platform. + + @param[out] NumberOfCpus Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. +**/ +VOID +GetNumberOfProcessor ( + OUT UINTN *NumberOfCpus, + OUT UINTN *NumberOfEnabledProcessors + ); + +/** + Worker function to switch the requested AP to be the BSP from that point onward. + + @param[in] ProcessorNumber The handle number of AP that is to become the new BSP. +**/ +VOID +SwitchNewBsp ( + IN UINTN ProcessorNumber + ); + +/** + Function that uses DEBUG() macros to display the contents of a a CPU feature bit mask. + + @param[in] FeatureMask A pointer to the CPU feature bit mask. + @param[in] BitMaskSize CPU feature bits mask buffer size. + +**/ +VOID +DumpCpuFeatureMask ( + IN UINT8 *FeatureMask, + IN UINT32 BitMaskSize + ); + +/** + Dump CPU feature name or CPU feature bit mask. + + @param[in] CpuFeature Pointer to CPU_FEATURES_ENTRY + @param[in] BitMaskSize CPU feature bits mask buffer size. + +**/ +VOID +DumpCpuFeature ( + IN CPU_FEATURES_ENTRY *CpuFeature, + IN UINT32 BitMaskSize + ); + +/** + Return feature dependence result. + + @param[in] CpuFeature Pointer to CPU feature. + @param[in] Before Check before dependence or after. + @param[in] NextCpuFeatureMask Pointer to next CPU feature Mask. + + @retval return the dependence result. +**/ +CPU_FEATURE_DEPENDENCE_TYPE +DetectFeatureScope ( + IN CPU_FEATURES_ENTRY *CpuFeature, + IN BOOLEAN Before, + IN UINT8 *NextCpuFeatureMask + ); + +/** + Return feature dependence result. + + @param[in] CpuFeature Pointer to CPU feature. + @param[in] Before Check before dependence or after. + @param[in] FeatureList Pointer to CPU feature list. + + @retval return the dependence result. +**/ +CPU_FEATURE_DEPENDENCE_TYPE +DetectNoneNeighborhoodFeatureScope ( + IN CPU_FEATURES_ENTRY *CpuFeature, + IN BOOLEAN Before, + IN LIST_ENTRY *FeatureList + ); + +/** + Programs registers for the calling processor. + + @param[in,out] Buffer The pointer to private data buffer. + +**/ +VOID +EFIAPI +SetProcessorRegister ( + IN OUT VOID *Buffer + ); + +/** + Return ACPI_CPU_DATA data. + + @return Pointer to ACPI_CPU_DATA data. +**/ +ACPI_CPU_DATA * +GetAcpiCpuData ( + VOID + ); + +/** + Worker function to get MP service pointer. + + @return MP_SERVICES variable. +**/ +MP_SERVICES +GetMpService ( + VOID + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/RegisterCpuFeaturesLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/RegisterCpuFeaturesLib.c new file mode 100644 index 00000000..2661690e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/RegisterCpuFeaturesLib.c @@ -0,0 +1,1291 @@ +/** @file + CPU Register Table Library functions. + + Copyright (c) 2017 - 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "RegisterCpuFeatures.h" + +/** + Function that uses DEBUG() macros to display the contents of a a CPU feature bit mask. + + @param[in] FeatureMask A pointer to the CPU feature bit mask. + @param[in] BitMaskSize CPU feature bits mask buffer size. + +**/ +VOID +DumpCpuFeatureMask ( + IN UINT8 *FeatureMask, + IN UINT32 BitMaskSize + ) +{ + UINTN Index; + UINT8 *Data8; + + Data8 = (UINT8 *) FeatureMask; + for (Index = 0; Index < BitMaskSize; Index++) { + DEBUG ((DEBUG_INFO, " %02x ", *Data8++)); + } + DEBUG ((DEBUG_INFO, "\n")); +} + +/** + Dump CPU feature name or CPU feature bit mask. + + @param[in] CpuFeature Pointer to CPU_FEATURES_ENTRY + @param[in] BitMaskSize CPU feature bits mask buffer size. + +**/ +VOID +DumpCpuFeature ( + IN CPU_FEATURES_ENTRY *CpuFeature, + IN UINT32 BitMaskSize + ) +{ + + if (CpuFeature->FeatureName != NULL) { + DEBUG ((DEBUG_INFO, "FeatureName: %a\n", CpuFeature->FeatureName)); + } else { + DEBUG ((DEBUG_INFO, "FeatureMask = ")); + DumpCpuFeatureMask (CpuFeature->FeatureMask, BitMaskSize); + } +} + +/** + Determines if the feature bit mask is in dependent CPU feature bit mask buffer. + + @param[in] FeatureMask Pointer to CPU feature bit mask + @param[in] DependentBitMask Pointer to dependent CPU feature bit mask buffer + + @retval TRUE The feature bit mask is in dependent CPU feature bit mask buffer. + @retval FALSE The feature bit mask is not in dependent CPU feature bit mask buffer. +**/ +BOOLEAN +IsBitMaskMatchCheck ( + IN UINT8 *FeatureMask, + IN UINT8 *DependentBitMask + ) +{ + UINTN Index; + UINT8 *Data1; + UINT8 *Data2; + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = GetCpuFeaturesData (); + + Data1 = FeatureMask; + Data2 = DependentBitMask; + for (Index = 0; Index < CpuFeaturesData->BitMaskSize; Index++) { + if (((*(Data1++)) & (*(Data2++))) != 0) { + return TRUE; + } + } + return FALSE; +} + +/** + Try to find the specify cpu featuren in former/after feature list. + + @param[in] FeatureList Pointer to dependent CPU feature list + @param[in] CurrentEntry Pointer to current CPU feature entry. + @param[in] SearchFormer Find in former feature or after features. + @param[in] FeatureMask Pointer to CPU feature bit mask + + @retval TRUE The feature bit mask is in dependent CPU feature bit mask buffer. + @retval FALSE The feature bit mask is not in dependent CPU feature bit mask buffer. +**/ +BOOLEAN +FindSpecifyFeature ( + IN LIST_ENTRY *FeatureList, + IN LIST_ENTRY *CurrentEntry, + IN BOOLEAN SearchFormer, + IN UINT8 *FeatureMask + ) +{ + CPU_FEATURES_ENTRY *CpuFeature; + LIST_ENTRY *NextEntry; + + // + // Check whether exist the not neighborhood entry first. + // If not exist, return FALSE means not found status. + // + if (SearchFormer) { + NextEntry = CurrentEntry->BackLink; + if (IsNull (FeatureList, NextEntry)) { + return FALSE; + } + + NextEntry = NextEntry->BackLink; + if (IsNull (FeatureList, NextEntry)) { + return FALSE; + } + + NextEntry = CurrentEntry->BackLink->BackLink; + } else { + NextEntry = CurrentEntry->ForwardLink; + if (IsNull (FeatureList, NextEntry)) { + return FALSE; + } + + NextEntry = NextEntry->ForwardLink; + if (IsNull (FeatureList, NextEntry)) { + return FALSE; + } + + NextEntry = CurrentEntry->ForwardLink->ForwardLink; + } + + while (!IsNull (FeatureList, NextEntry)) { + CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (NextEntry); + + if (IsBitMaskMatchCheck (FeatureMask, CpuFeature->FeatureMask)) { + return TRUE; + } + + if (SearchFormer) { + NextEntry = NextEntry->BackLink; + } else { + NextEntry = NextEntry->ForwardLink; + } + } + + return FALSE; +} + +/** + Return feature dependence result. + + @param[in] CpuFeature Pointer to CPU feature. + @param[in] Before Check before dependence or after. + @param[in] NextCpuFeatureMask Pointer to next CPU feature Mask. + + @retval return the dependence result. +**/ +CPU_FEATURE_DEPENDENCE_TYPE +DetectFeatureScope ( + IN CPU_FEATURES_ENTRY *CpuFeature, + IN BOOLEAN Before, + IN UINT8 *NextCpuFeatureMask + ) +{ + // + // if need to check before type dependence but the feature after current feature is not + // exist, means this before type dependence not valid, just return NoneDepType. + // Just like Feature A has a dependence of feature B, but Feature B not installed, so + // Feature A maybe insert to the last entry of the list. In this case, for below code, + // Featrure A has depend of feature B, but it is the last entry of the list, so the + // NextCpuFeatureMask is NULL, so the dependence for feature A here is useless and code + // just return NoneDepType. + // + if (NextCpuFeatureMask == NULL) { + return NoneDepType; + } + + if (Before) { + if ((CpuFeature->PackageBeforeFeatureBitMask != NULL) && + IsBitMaskMatchCheck (NextCpuFeatureMask, CpuFeature->PackageBeforeFeatureBitMask)) { + return PackageDepType; + } + + if ((CpuFeature->CoreBeforeFeatureBitMask != NULL) && + IsBitMaskMatchCheck (NextCpuFeatureMask, CpuFeature->CoreBeforeFeatureBitMask)) { + return CoreDepType; + } + + if ((CpuFeature->ThreadBeforeFeatureBitMask != NULL) && + IsBitMaskMatchCheck (NextCpuFeatureMask, CpuFeature->ThreadBeforeFeatureBitMask)) { + return ThreadDepType; + } + + return NoneDepType; + } + + if ((CpuFeature->PackageAfterFeatureBitMask != NULL) && + IsBitMaskMatchCheck (NextCpuFeatureMask, CpuFeature->PackageAfterFeatureBitMask)) { + return PackageDepType; + } + + if ((CpuFeature->CoreAfterFeatureBitMask != NULL) && + IsBitMaskMatchCheck (NextCpuFeatureMask, CpuFeature->CoreAfterFeatureBitMask)) { + return CoreDepType; + } + + if ((CpuFeature->ThreadAfterFeatureBitMask != NULL) && + IsBitMaskMatchCheck (NextCpuFeatureMask, CpuFeature->ThreadAfterFeatureBitMask)) { + return ThreadDepType; + } + + return NoneDepType; +} + +/** + Return feature dependence result. + + @param[in] CpuFeature Pointer to CPU feature. + @param[in] Before Check before dependence or after. + @param[in] FeatureList Pointer to CPU feature list. + + @retval return the dependence result. +**/ +CPU_FEATURE_DEPENDENCE_TYPE +DetectNoneNeighborhoodFeatureScope ( + IN CPU_FEATURES_ENTRY *CpuFeature, + IN BOOLEAN Before, + IN LIST_ENTRY *FeatureList + ) +{ + if (Before) { + if ((CpuFeature->PackageBeforeFeatureBitMask != NULL) && + FindSpecifyFeature(FeatureList, &CpuFeature->Link, FALSE, CpuFeature->PackageBeforeFeatureBitMask)) { + return PackageDepType; + } + + if ((CpuFeature->CoreBeforeFeatureBitMask != NULL) && + FindSpecifyFeature(FeatureList, &CpuFeature->Link, FALSE, CpuFeature->CoreBeforeFeatureBitMask)) { + return CoreDepType; + } + + if ((CpuFeature->ThreadBeforeFeatureBitMask != NULL) && + FindSpecifyFeature(FeatureList, &CpuFeature->Link, FALSE, CpuFeature->ThreadBeforeFeatureBitMask)) { + return ThreadDepType; + } + + return NoneDepType; + } + + if ((CpuFeature->PackageAfterFeatureBitMask != NULL) && + FindSpecifyFeature(FeatureList, &CpuFeature->Link, TRUE, CpuFeature->PackageAfterFeatureBitMask)) { + return PackageDepType; + } + + if ((CpuFeature->CoreAfterFeatureBitMask != NULL) && + FindSpecifyFeature(FeatureList, &CpuFeature->Link, TRUE, CpuFeature->CoreAfterFeatureBitMask)) { + return CoreDepType; + } + + if ((CpuFeature->ThreadAfterFeatureBitMask != NULL) && + FindSpecifyFeature(FeatureList, &CpuFeature->Link, TRUE, CpuFeature->ThreadAfterFeatureBitMask)) { + return ThreadDepType; + } + + return NoneDepType; +} + +/** + Base on dependence relationship to asjust feature dependence. + + ONLY when the feature before(or after) the find feature also has + dependence with the find feature. In this case, driver need to base + on dependce relationship to decide how to insert current feature and + adjust the feature dependence. + + @param[in, out] PreviousFeature CPU feature current before the find one. + @param[in, out] CurrentFeature Cpu feature need to adjust. + @param[in] FindFeature Cpu feature which current feature depends. + @param[in] Before Before or after dependence relationship. + + @retval TRUE means the current feature dependence has been adjusted. + + @retval FALSE means the previous feature dependence has been adjusted. + or previous feature has no dependence with the find one. + +**/ +BOOLEAN +AdjustFeaturesDependence ( + IN OUT CPU_FEATURES_ENTRY *PreviousFeature, + IN OUT CPU_FEATURES_ENTRY *CurrentFeature, + IN CPU_FEATURES_ENTRY *FindFeature, + IN BOOLEAN Before + ) +{ + CPU_FEATURE_DEPENDENCE_TYPE PreDependType; + CPU_FEATURE_DEPENDENCE_TYPE CurrentDependType; + + PreDependType = DetectFeatureScope(PreviousFeature, Before, FindFeature->FeatureMask); + CurrentDependType = DetectFeatureScope(CurrentFeature, Before, FindFeature->FeatureMask); + + // + // If previous feature has no dependence with the find featue. + // return FALSE. + // + if (PreDependType == NoneDepType) { + return FALSE; + } + + // + // If both feature have dependence, keep the one which needs use more + // processors and clear the dependence for the other one. + // + if (PreDependType >= CurrentDependType) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Base on dependence relationship to asjust feature order. + + @param[in] FeatureList Pointer to CPU feature list + @param[in, out] FindEntry The entry this feature depend on. + @param[in, out] CurrentEntry The entry for this feature. + @param[in] Before Before or after dependence relationship. + +**/ +VOID +AdjustEntry ( + IN LIST_ENTRY *FeatureList, + IN OUT LIST_ENTRY *FindEntry, + IN OUT LIST_ENTRY *CurrentEntry, + IN BOOLEAN Before + ) +{ + LIST_ENTRY *PreviousEntry; + CPU_FEATURES_ENTRY *PreviousFeature; + CPU_FEATURES_ENTRY *CurrentFeature; + CPU_FEATURES_ENTRY *FindFeature; + + // + // For CPU feature which has core or package type dependence, later code need to insert + // AcquireSpinLock/ReleaseSpinLock logic to sequency the execute order. + // So if driver finds both feature A and B need to execute before feature C, driver will + // base on dependence type of feature A and B to update the logic here. + // For example, feature A has package type dependence and feature B has core type dependence, + // because package type dependence need to wait for more processors which has strong dependence + // than core type dependence. So driver will adjust the feature order to B -> A -> C. and driver + // will remove the feature dependence in feature B. + // Driver just needs to make sure before feature C been executed, feature A has finished its task + // in all all thread. Feature A finished in all threads also means feature B have finshed in all + // threads. + // + if (Before) { + PreviousEntry = GetPreviousNode (FeatureList, FindEntry); + } else { + + PreviousEntry = GetNextNode (FeatureList, FindEntry); + } + + CurrentFeature = CPU_FEATURE_ENTRY_FROM_LINK (CurrentEntry); + RemoveEntryList (CurrentEntry); + + if (IsNull (FeatureList, PreviousEntry)) { + // + // If not exist the previous or next entry, just insert the current entry. + // + if (Before) { + InsertTailList (FindEntry, CurrentEntry); + } else { + InsertHeadList (FindEntry, CurrentEntry); + } + } else { + // + // If exist the previous or next entry, need to check it before insert curent entry. + // + PreviousFeature = CPU_FEATURE_ENTRY_FROM_LINK (PreviousEntry); + FindFeature = CPU_FEATURE_ENTRY_FROM_LINK (FindEntry); + + if (AdjustFeaturesDependence (PreviousFeature, CurrentFeature, FindFeature, Before)) { + // + // Return TRUE means current feature dependence has been cleared and the previous + // feature dependence has been kept and used. So insert current feature before (or after) + // the previous feature. + // + if (Before) { + InsertTailList (PreviousEntry, CurrentEntry); + } else { + InsertHeadList (PreviousEntry, CurrentEntry); + } + } else { + if (Before) { + InsertTailList (FindEntry, CurrentEntry); + } else { + InsertHeadList (FindEntry, CurrentEntry); + } + } + } +} + + +/** + Checks and adjusts current CPU features per dependency relationship. + + @param[in] FeatureList Pointer to CPU feature list + @param[in] CurrentEntry Pointer to current checked CPU feature + @param[in] FeatureMask The feature bit mask. + + @retval return Swapped info. +**/ +BOOLEAN +InsertToBeforeEntry ( + IN LIST_ENTRY *FeatureList, + IN LIST_ENTRY *CurrentEntry, + IN UINT8 *FeatureMask + ) +{ + LIST_ENTRY *CheckEntry; + CPU_FEATURES_ENTRY *CheckFeature; + BOOLEAN Swapped; + + Swapped = FALSE; + + // + // Check all features dispatched before this entry + // + CheckEntry = GetFirstNode (FeatureList); + while (CheckEntry != CurrentEntry) { + CheckFeature = CPU_FEATURE_ENTRY_FROM_LINK (CheckEntry); + if (IsBitMaskMatchCheck (CheckFeature->FeatureMask, FeatureMask)) { + AdjustEntry (FeatureList, CheckEntry, CurrentEntry, TRUE); + Swapped = TRUE; + break; + } + CheckEntry = CheckEntry->ForwardLink; + } + + return Swapped; +} + +/** + Checks and adjusts current CPU features per dependency relationship. + + @param[in] FeatureList Pointer to CPU feature list + @param[in] CurrentEntry Pointer to current checked CPU feature + @param[in] FeatureMask The feature bit mask. + + @retval return Swapped info. +**/ +BOOLEAN +InsertToAfterEntry ( + IN LIST_ENTRY *FeatureList, + IN LIST_ENTRY *CurrentEntry, + IN UINT8 *FeatureMask + ) +{ + LIST_ENTRY *CheckEntry; + CPU_FEATURES_ENTRY *CheckFeature; + BOOLEAN Swapped; + + Swapped = FALSE; + + // + // Check all features dispatched after this entry + // + CheckEntry = GetNextNode (FeatureList, CurrentEntry); + while (!IsNull (FeatureList, CheckEntry)) { + CheckFeature = CPU_FEATURE_ENTRY_FROM_LINK (CheckEntry); + if (IsBitMaskMatchCheck (CheckFeature->FeatureMask, FeatureMask)) { + AdjustEntry (FeatureList, CheckEntry, CurrentEntry, FALSE); + Swapped = TRUE; + break; + } + CheckEntry = CheckEntry->ForwardLink; + } + + return Swapped; +} + +/** + Checks and adjusts CPU features order per dependency relationship. + + @param[in] FeatureList Pointer to CPU feature list +**/ +VOID +CheckCpuFeaturesDependency ( + IN LIST_ENTRY *FeatureList + ) +{ + LIST_ENTRY *CurrentEntry; + CPU_FEATURES_ENTRY *CpuFeature; + LIST_ENTRY *CheckEntry; + CPU_FEATURES_ENTRY *CheckFeature; + BOOLEAN Swapped; + LIST_ENTRY *TempEntry; + LIST_ENTRY *NextEntry; + + CurrentEntry = GetFirstNode (FeatureList); + while (!IsNull (FeatureList, CurrentEntry)) { + Swapped = FALSE; + CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (CurrentEntry); + NextEntry = CurrentEntry->ForwardLink; + if (CpuFeature->BeforeAll) { + // + // Check all features dispatched before this entry + // + CheckEntry = GetFirstNode (FeatureList); + while (CheckEntry != CurrentEntry) { + CheckFeature = CPU_FEATURE_ENTRY_FROM_LINK (CheckEntry); + if (!CheckFeature->BeforeAll) { + // + // If this feature has no BeforeAll flag and is dispatched before CpuFeature, + // insert currentEntry before Checked feature + // + RemoveEntryList (CurrentEntry); + InsertTailList (CheckEntry, CurrentEntry); + Swapped = TRUE; + break; + } + CheckEntry = CheckEntry->ForwardLink; + } + if (Swapped) { + CurrentEntry = NextEntry; + continue; + } + } + + if (CpuFeature->AfterAll) { + // + // Check all features dispatched after this entry + // + CheckEntry = GetNextNode (FeatureList, CurrentEntry); + while (!IsNull (FeatureList, CheckEntry)) { + CheckFeature = CPU_FEATURE_ENTRY_FROM_LINK (CheckEntry); + if (!CheckFeature->AfterAll) { + // + // If this feature has no AfterAll flag and is dispatched after CpuFeature, + // insert currentEntry after Checked feature + // + TempEntry = GetNextNode (FeatureList, CurrentEntry); + RemoveEntryList (CurrentEntry); + InsertHeadList (CheckEntry, CurrentEntry); + CurrentEntry = TempEntry; + Swapped = TRUE; + break; + } + CheckEntry = CheckEntry->ForwardLink; + } + if (Swapped) { + CurrentEntry = NextEntry; + continue; + } + } + + if (CpuFeature->ThreadBeforeFeatureBitMask != NULL) { + Swapped = InsertToBeforeEntry (FeatureList, CurrentEntry, CpuFeature->ThreadBeforeFeatureBitMask); + if (Swapped) { + continue; + } + } + + if (CpuFeature->ThreadAfterFeatureBitMask != NULL) { + Swapped = InsertToAfterEntry (FeatureList, CurrentEntry, CpuFeature->ThreadAfterFeatureBitMask); + if (Swapped) { + continue; + } + } + + if (CpuFeature->CoreBeforeFeatureBitMask != NULL) { + Swapped = InsertToBeforeEntry (FeatureList, CurrentEntry, CpuFeature->CoreBeforeFeatureBitMask); + if (Swapped) { + continue; + } + } + + if (CpuFeature->CoreAfterFeatureBitMask != NULL) { + Swapped = InsertToAfterEntry (FeatureList, CurrentEntry, CpuFeature->CoreAfterFeatureBitMask); + if (Swapped) { + continue; + } + } + + if (CpuFeature->PackageBeforeFeatureBitMask != NULL) { + Swapped = InsertToBeforeEntry (FeatureList, CurrentEntry, CpuFeature->PackageBeforeFeatureBitMask); + if (Swapped) { + continue; + } + } + + if (CpuFeature->PackageAfterFeatureBitMask != NULL) { + Swapped = InsertToAfterEntry (FeatureList, CurrentEntry, CpuFeature->PackageAfterFeatureBitMask); + if (Swapped) { + continue; + } + } + + CurrentEntry = CurrentEntry->ForwardLink; + } +} + +/** + Worker function to register CPU Feature. + + @param[in] CpuFeaturesData Pointer to CPU feature data structure. + @param[in] CpuFeature Pointer to CPU feature entry + + @retval RETURN_SUCCESS The CPU feature was successfully registered. + @retval RETURN_OUT_OF_RESOURCES There are not enough resources to register + the CPU feature. + @retval RETURN_UNSUPPORTED Registration of the CPU feature is not + supported due to a circular dependency between + BEFORE and AFTER features. +**/ +RETURN_STATUS +RegisterCpuFeatureWorker ( + IN CPU_FEATURES_DATA *CpuFeaturesData, + IN CPU_FEATURES_ENTRY *CpuFeature + ) +{ + EFI_STATUS Status; + CPU_FEATURES_ENTRY *CpuFeatureEntry; + LIST_ENTRY *Entry; + BOOLEAN FeatureExist; + + FeatureExist = FALSE; + CpuFeatureEntry = NULL; + Entry = GetFirstNode (&CpuFeaturesData->FeatureList); + while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) { + CpuFeatureEntry = CPU_FEATURE_ENTRY_FROM_LINK (Entry); + if (CompareMem (CpuFeature->FeatureMask, CpuFeatureEntry->FeatureMask, CpuFeaturesData->BitMaskSize) == 0) { + // + // If this feature already registered + // + FeatureExist = TRUE; + break; + } + Entry = Entry->ForwardLink; + } + + if (!FeatureExist) { + DEBUG ((DEBUG_INFO, "[NEW] ")); + DumpCpuFeature (CpuFeature, CpuFeaturesData->BitMaskSize); + InsertTailList (&CpuFeaturesData->FeatureList, &CpuFeature->Link); + CpuFeaturesData->FeaturesCount++; + } else { + DEBUG ((DEBUG_INFO, "[OVERRIDE] ")); + DumpCpuFeature (CpuFeature, CpuFeaturesData->BitMaskSize); + ASSERT (CpuFeatureEntry != NULL); + // + // Overwrite original parameters of CPU feature + // + if (CpuFeature->GetConfigDataFunc != NULL) { + CpuFeatureEntry->GetConfigDataFunc = CpuFeature->GetConfigDataFunc; + } + if (CpuFeature->SupportFunc != NULL) { + CpuFeatureEntry->SupportFunc = CpuFeature->SupportFunc; + } + if (CpuFeature->InitializeFunc != NULL) { + CpuFeatureEntry->InitializeFunc = CpuFeature->InitializeFunc; + } + if (CpuFeature->FeatureName != NULL) { + if (CpuFeatureEntry->FeatureName == NULL) { + CpuFeatureEntry->FeatureName = AllocatePool (CPU_FEATURE_NAME_SIZE); + ASSERT (CpuFeatureEntry->FeatureName != NULL); + } + Status = AsciiStrCpyS (CpuFeatureEntry->FeatureName, CPU_FEATURE_NAME_SIZE, CpuFeature->FeatureName); + ASSERT_EFI_ERROR (Status); + FreePool (CpuFeature->FeatureName); + } + if (CpuFeature->ThreadBeforeFeatureBitMask != NULL) { + if (CpuFeatureEntry->ThreadBeforeFeatureBitMask != NULL) { + FreePool (CpuFeatureEntry->ThreadBeforeFeatureBitMask); + } + CpuFeatureEntry->ThreadBeforeFeatureBitMask = CpuFeature->ThreadBeforeFeatureBitMask; + } + if (CpuFeature->ThreadAfterFeatureBitMask != NULL) { + if (CpuFeatureEntry->ThreadAfterFeatureBitMask != NULL) { + FreePool (CpuFeatureEntry->ThreadAfterFeatureBitMask); + } + CpuFeatureEntry->ThreadAfterFeatureBitMask = CpuFeature->ThreadAfterFeatureBitMask; + } + if (CpuFeature->CoreBeforeFeatureBitMask != NULL) { + if (CpuFeatureEntry->CoreBeforeFeatureBitMask != NULL) { + FreePool (CpuFeatureEntry->CoreBeforeFeatureBitMask); + } + CpuFeatureEntry->CoreBeforeFeatureBitMask = CpuFeature->CoreBeforeFeatureBitMask; + } + if (CpuFeature->CoreAfterFeatureBitMask != NULL) { + if (CpuFeatureEntry->CoreAfterFeatureBitMask != NULL) { + FreePool (CpuFeatureEntry->CoreAfterFeatureBitMask); + } + CpuFeatureEntry->CoreAfterFeatureBitMask = CpuFeature->CoreAfterFeatureBitMask; + } + if (CpuFeature->PackageBeforeFeatureBitMask != NULL) { + if (CpuFeatureEntry->PackageBeforeFeatureBitMask != NULL) { + FreePool (CpuFeatureEntry->PackageBeforeFeatureBitMask); + } + CpuFeatureEntry->PackageBeforeFeatureBitMask = CpuFeature->PackageBeforeFeatureBitMask; + } + if (CpuFeature->PackageAfterFeatureBitMask != NULL) { + if (CpuFeatureEntry->PackageAfterFeatureBitMask != NULL) { + FreePool (CpuFeatureEntry->PackageAfterFeatureBitMask); + } + CpuFeatureEntry->PackageAfterFeatureBitMask = CpuFeature->PackageAfterFeatureBitMask; + } + + CpuFeatureEntry->BeforeAll = CpuFeature->BeforeAll; + CpuFeatureEntry->AfterAll = CpuFeature->AfterAll; + + FreePool (CpuFeature->FeatureMask); + FreePool (CpuFeature); + } + // + // Verify CPU features dependency can change CPU feature order + // + CheckCpuFeaturesDependency (&CpuFeaturesData->FeatureList); + return RETURN_SUCCESS; +} + +/** + Sets CPU feature bit mask in CPU feature bit mask buffer. + + @param[in] FeaturesBitMask Pointer to CPU feature bit mask buffer + @param[in] Feature The bit number of the CPU feature + @param[in] BitMaskSize CPU feature bit mask buffer size +**/ +VOID +SetCpuFeaturesBitMask ( + IN UINT8 **FeaturesBitMask, + IN UINT32 Feature, + IN UINTN BitMaskSize + ) +{ + UINT8 *CpuFeaturesBitMask; + + ASSERT (FeaturesBitMask != NULL); + CpuFeaturesBitMask = *FeaturesBitMask; + if (CpuFeaturesBitMask == NULL) { + CpuFeaturesBitMask = AllocateZeroPool (BitMaskSize); + ASSERT (CpuFeaturesBitMask != NULL); + *FeaturesBitMask = CpuFeaturesBitMask; + } + + CpuFeaturesBitMask += (Feature / 8); + *CpuFeaturesBitMask |= (UINT8) (1 << (Feature % 8)); +} + +/** + Registers a CPU Feature. + + @param[in] FeatureName A Null-terminated Ascii string indicates CPU feature + name. + @param[in] GetConfigDataFunc CPU feature get configuration data function. This + is an optional parameter that may be NULL. If NULL, + then the most recently registered function for the + CPU feature is used. If no functions are registered + for a CPU feature, then the CPU configuration data + for the registered feature is NULL. + @param[in] SupportFunc CPU feature support function. This is an optional + parameter that may be NULL. If NULL, then the most + recently registered function for the CPU feature is + used. If no functions are registered for a CPU + feature, then the CPU feature is assumed to be + supported by all CPUs. + @param[in] InitializeFunc CPU feature initialize function. This is an optional + parameter that may be NULL. If NULL, then the most + recently registered function for the CPU feature is + used. If no functions are registered for a CPU + feature, then the CPU feature initialization is + skipped. + @param[in] ... Variable argument list of UINT32 CPU feature value. + Values with no modifiers are the features provided + by the registered functions. + Values with CPU_FEATURE_BEFORE modifier are features + that must be initialized after the features provided + by the registered functions are used. + Values with CPU_FEATURE_AFTER modifier are features + that must be initialized before the features provided + by the registered functions are used. + The last argument in this variable argument list must + always be CPU_FEATURE_END. + + @retval RETURN_SUCCESS The CPU feature was successfully registered. + @retval RETURN_OUT_OF_RESOURCES There are not enough resources to register + the CPU feature. + @retval RETURN_UNSUPPORTED Registration of the CPU feature is not + supported due to a circular dependency between + BEFORE and AFTER features. + @retval RETURN_NOT_READY CPU feature PCD PcdCpuFeaturesUserConfiguration + not updated by Platform driver yet. + + @note This service could be called by BSP only. +**/ +RETURN_STATUS +EFIAPI +RegisterCpuFeature ( + IN CHAR8 *FeatureName, OPTIONAL + IN CPU_FEATURE_GET_CONFIG_DATA GetConfigDataFunc, OPTIONAL + IN CPU_FEATURE_SUPPORT SupportFunc, OPTIONAL + IN CPU_FEATURE_INITIALIZE InitializeFunc, OPTIONAL + ... + ) +{ + EFI_STATUS Status; + VA_LIST Marker; + UINT32 Feature; + CPU_FEATURES_ENTRY *CpuFeature; + UINT8 *FeatureMask; + UINT8 *ThreadBeforeFeatureBitMask; + UINT8 *ThreadAfterFeatureBitMask; + UINT8 *CoreBeforeFeatureBitMask; + UINT8 *CoreAfterFeatureBitMask; + UINT8 *PackageBeforeFeatureBitMask; + UINT8 *PackageAfterFeatureBitMask; + BOOLEAN BeforeAll; + BOOLEAN AfterAll; + CPU_FEATURES_DATA *CpuFeaturesData; + + FeatureMask = NULL; + ThreadBeforeFeatureBitMask = NULL; + ThreadAfterFeatureBitMask = NULL; + CoreBeforeFeatureBitMask = NULL; + CoreAfterFeatureBitMask = NULL; + PackageBeforeFeatureBitMask = NULL; + PackageAfterFeatureBitMask = NULL; + BeforeAll = FALSE; + AfterAll = FALSE; + + CpuFeaturesData = GetCpuFeaturesData (); + if (CpuFeaturesData->FeaturesCount == 0) { + InitializeListHead (&CpuFeaturesData->FeatureList); + InitializeSpinLock (&CpuFeaturesData->CpuFlags.MemoryMappedLock); + // + // Code assumes below three PCDs have PCD same buffer size. + // + ASSERT (PcdGetSize (PcdCpuFeaturesSetting) == PcdGetSize (PcdCpuFeaturesCapability)); + ASSERT (PcdGetSize (PcdCpuFeaturesSetting) == PcdGetSize (PcdCpuFeaturesSupport)); + CpuFeaturesData->BitMaskSize = (UINT32) PcdGetSize (PcdCpuFeaturesSetting); + } + + VA_START (Marker, InitializeFunc); + Feature = VA_ARG (Marker, UINT32); + while (Feature != CPU_FEATURE_END) { + // + // It's invalid to require a feature is before AND after all other features. + // + ASSERT ((Feature & (CPU_FEATURE_BEFORE_ALL | CPU_FEATURE_AFTER_ALL)) + != (CPU_FEATURE_BEFORE_ALL | CPU_FEATURE_AFTER_ALL)); + + // + // It's invalid to require feature A is before AND after before feature B, + // either in thread level, core level or package level. + // + ASSERT ((Feature & (CPU_FEATURE_THREAD_BEFORE | CPU_FEATURE_THREAD_AFTER)) + != (CPU_FEATURE_THREAD_BEFORE | CPU_FEATURE_THREAD_AFTER)); + ASSERT ((Feature & (CPU_FEATURE_CORE_BEFORE | CPU_FEATURE_CORE_AFTER)) + != (CPU_FEATURE_CORE_BEFORE | CPU_FEATURE_CORE_AFTER)); + ASSERT ((Feature & (CPU_FEATURE_PACKAGE_BEFORE | CPU_FEATURE_PACKAGE_AFTER)) + != (CPU_FEATURE_PACKAGE_BEFORE | CPU_FEATURE_PACKAGE_AFTER)); + if (Feature < CPU_FEATURE_THREAD_BEFORE) { + BeforeAll = ((Feature & CPU_FEATURE_BEFORE_ALL) != 0) ? TRUE : FALSE; + AfterAll = ((Feature & CPU_FEATURE_AFTER_ALL) != 0) ? TRUE : FALSE; + Feature &= ~(CPU_FEATURE_BEFORE_ALL | CPU_FEATURE_AFTER_ALL); + ASSERT (FeatureMask == NULL); + SetCpuFeaturesBitMask (&FeatureMask, Feature, CpuFeaturesData->BitMaskSize); + } else if ((Feature & CPU_FEATURE_THREAD_BEFORE) != 0) { + SetCpuFeaturesBitMask (&ThreadBeforeFeatureBitMask, Feature & ~CPU_FEATURE_THREAD_BEFORE, CpuFeaturesData->BitMaskSize); + } else if ((Feature & CPU_FEATURE_THREAD_AFTER) != 0) { + SetCpuFeaturesBitMask (&ThreadAfterFeatureBitMask, Feature & ~CPU_FEATURE_THREAD_AFTER, CpuFeaturesData->BitMaskSize); + } else if ((Feature & CPU_FEATURE_CORE_BEFORE) != 0) { + SetCpuFeaturesBitMask (&CoreBeforeFeatureBitMask, Feature & ~CPU_FEATURE_CORE_BEFORE, CpuFeaturesData->BitMaskSize); + } else if ((Feature & CPU_FEATURE_CORE_AFTER) != 0) { + SetCpuFeaturesBitMask (&CoreAfterFeatureBitMask, Feature & ~CPU_FEATURE_CORE_AFTER, CpuFeaturesData->BitMaskSize); + } else if ((Feature & CPU_FEATURE_PACKAGE_BEFORE) != 0) { + SetCpuFeaturesBitMask (&PackageBeforeFeatureBitMask, Feature & ~CPU_FEATURE_PACKAGE_BEFORE, CpuFeaturesData->BitMaskSize); + } else if ((Feature & CPU_FEATURE_PACKAGE_AFTER) != 0) { + SetCpuFeaturesBitMask (&PackageAfterFeatureBitMask, Feature & ~CPU_FEATURE_PACKAGE_AFTER, CpuFeaturesData->BitMaskSize); + } + Feature = VA_ARG (Marker, UINT32); + } + VA_END (Marker); + + CpuFeature = AllocateZeroPool (sizeof (CPU_FEATURES_ENTRY)); + ASSERT (CpuFeature != NULL); + CpuFeature->Signature = CPU_FEATURE_ENTRY_SIGNATURE; + CpuFeature->FeatureMask = FeatureMask; + CpuFeature->ThreadBeforeFeatureBitMask = ThreadBeforeFeatureBitMask; + CpuFeature->ThreadAfterFeatureBitMask = ThreadAfterFeatureBitMask; + CpuFeature->CoreBeforeFeatureBitMask = CoreBeforeFeatureBitMask; + CpuFeature->CoreAfterFeatureBitMask = CoreAfterFeatureBitMask; + CpuFeature->PackageBeforeFeatureBitMask = PackageBeforeFeatureBitMask; + CpuFeature->PackageAfterFeatureBitMask = PackageAfterFeatureBitMask; + CpuFeature->BeforeAll = BeforeAll; + CpuFeature->AfterAll = AfterAll; + CpuFeature->GetConfigDataFunc = GetConfigDataFunc; + CpuFeature->SupportFunc = SupportFunc; + CpuFeature->InitializeFunc = InitializeFunc; + if (FeatureName != NULL) { + CpuFeature->FeatureName = AllocatePool (CPU_FEATURE_NAME_SIZE); + ASSERT (CpuFeature->FeatureName != NULL); + Status = AsciiStrCpyS (CpuFeature->FeatureName, CPU_FEATURE_NAME_SIZE, FeatureName); + ASSERT_EFI_ERROR (Status); + } + + Status = RegisterCpuFeatureWorker (CpuFeaturesData, CpuFeature); + ASSERT_EFI_ERROR (Status); + + return RETURN_SUCCESS; +} + +/** + Return ACPI_CPU_DATA data. + + @return Pointer to ACPI_CPU_DATA data. +**/ +ACPI_CPU_DATA * +GetAcpiCpuData ( + VOID + ) +{ + EFI_STATUS Status; + UINTN NumberOfCpus; + UINTN NumberOfEnabledProcessors; + ACPI_CPU_DATA *AcpiCpuData; + UINTN TableSize; + CPU_REGISTER_TABLE *RegisterTable; + UINTN Index; + EFI_PROCESSOR_INFORMATION ProcessorInfoBuffer; + + AcpiCpuData = (ACPI_CPU_DATA *) (UINTN) PcdGet64 (PcdCpuS3DataAddress); + if (AcpiCpuData == NULL) { + AcpiCpuData = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (ACPI_CPU_DATA))); + ASSERT (AcpiCpuData != NULL); + ZeroMem (AcpiCpuData, sizeof (ACPI_CPU_DATA)); + + // + // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure + // + Status = PcdSet64S (PcdCpuS3DataAddress, (UINT64)(UINTN)AcpiCpuData); + ASSERT_EFI_ERROR (Status); + + GetNumberOfProcessor (&NumberOfCpus, &NumberOfEnabledProcessors); + AcpiCpuData->NumberOfCpus = (UINT32)NumberOfCpus; + } + + if (AcpiCpuData->RegisterTable == 0 || + AcpiCpuData->PreSmmInitRegisterTable == 0) { + // + // Allocate buffer for empty RegisterTable and PreSmmInitRegisterTable for all CPUs + // + NumberOfCpus = AcpiCpuData->NumberOfCpus; + TableSize = 2 * NumberOfCpus * sizeof (CPU_REGISTER_TABLE); + RegisterTable = AllocatePages (EFI_SIZE_TO_PAGES (TableSize)); + ASSERT (RegisterTable != NULL); + + for (Index = 0; Index < NumberOfCpus; Index++) { + Status = GetProcessorInformation (Index, &ProcessorInfoBuffer); + ASSERT_EFI_ERROR (Status); + + RegisterTable[Index].InitialApicId = (UINT32)ProcessorInfoBuffer.ProcessorId; + RegisterTable[Index].TableLength = 0; + RegisterTable[Index].AllocatedSize = 0; + RegisterTable[Index].RegisterTableEntry = 0; + + RegisterTable[NumberOfCpus + Index].InitialApicId = (UINT32)ProcessorInfoBuffer.ProcessorId; + RegisterTable[NumberOfCpus + Index].TableLength = 0; + RegisterTable[NumberOfCpus + Index].AllocatedSize = 0; + RegisterTable[NumberOfCpus + Index].RegisterTableEntry = 0; + } + if (AcpiCpuData->RegisterTable == 0) { + AcpiCpuData->RegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)RegisterTable; + } + if (AcpiCpuData->PreSmmInitRegisterTable == 0) { + AcpiCpuData->PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)(RegisterTable + NumberOfCpus); + } + } + + return AcpiCpuData; +} + +/** + Enlarges CPU register table for each processor. + + @param[in, out] RegisterTable Pointer processor's CPU register table +**/ +STATIC +VOID +EnlargeRegisterTable ( + IN OUT CPU_REGISTER_TABLE *RegisterTable + ) +{ + EFI_PHYSICAL_ADDRESS Address; + UINTN UsedPages; + + UsedPages = RegisterTable->AllocatedSize / EFI_PAGE_SIZE; + Address = (UINTN)AllocatePages (UsedPages + 1); + ASSERT (Address != 0); + + // + // If there are records existing in the register table, then copy its contents + // to new region and free the old one. + // + if (RegisterTable->AllocatedSize > 0) { + CopyMem ( + (VOID *) (UINTN) Address, + (VOID *) (UINTN) RegisterTable->RegisterTableEntry, + RegisterTable->AllocatedSize + ); + + FreePages ((VOID *)(UINTN)RegisterTable->RegisterTableEntry, UsedPages); + } + + // + // Adjust the allocated size and register table base address. + // + RegisterTable->AllocatedSize += EFI_PAGE_SIZE; + RegisterTable->RegisterTableEntry = Address; +} + +/** + Add an entry in specified register table. + + This function adds an entry in specified register table, with given register type, + register index, bit section and value. + + @param[in] PreSmmFlag If TRUE, entry will be added into PreSmm register table + If FALSE, entry will be added into register table + @param[in] ProcessorNumber The index of the CPU to add a register table entry + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] ValidBitStart Start of the bit section + @param[in] ValidBitLength Length of the bit section + @param[in] Value Value to write + @param[in] TestThenWrite Whether need to test current Value before writing. + +**/ +VOID +CpuRegisterTableWriteWorker ( + IN BOOLEAN PreSmmFlag, + IN UINTN ProcessorNumber, + IN REGISTER_TYPE RegisterType, + IN UINT64 Index, + IN UINT8 ValidBitStart, + IN UINT8 ValidBitLength, + IN UINT64 Value, + IN BOOLEAN TestThenWrite + ) +{ + CPU_FEATURES_DATA *CpuFeaturesData; + ACPI_CPU_DATA *AcpiCpuData; + CPU_REGISTER_TABLE *RegisterTable; + CPU_REGISTER_TABLE_ENTRY *RegisterTableEntry; + + CpuFeaturesData = GetCpuFeaturesData (); + if (CpuFeaturesData->RegisterTable == NULL) { + AcpiCpuData = GetAcpiCpuData (); + ASSERT ((AcpiCpuData != NULL) && (AcpiCpuData->RegisterTable != 0)); + CpuFeaturesData->RegisterTable = (CPU_REGISTER_TABLE *) (UINTN) AcpiCpuData->RegisterTable; + CpuFeaturesData->PreSmmRegisterTable = (CPU_REGISTER_TABLE *) (UINTN) AcpiCpuData->PreSmmInitRegisterTable; + } + + if (PreSmmFlag) { + RegisterTable = &CpuFeaturesData->PreSmmRegisterTable[ProcessorNumber]; + } else { + RegisterTable = &CpuFeaturesData->RegisterTable[ProcessorNumber]; + } + + if (RegisterTable->TableLength == RegisterTable->AllocatedSize / sizeof (CPU_REGISTER_TABLE_ENTRY)) { + EnlargeRegisterTable (RegisterTable); + } + + // + // Append entry in the register table. + // + RegisterTableEntry = (CPU_REGISTER_TABLE_ENTRY *) (UINTN) RegisterTable->RegisterTableEntry; + RegisterTableEntry[RegisterTable->TableLength].RegisterType = RegisterType; + RegisterTableEntry[RegisterTable->TableLength].Index = (UINT32) Index; + RegisterTableEntry[RegisterTable->TableLength].HighIndex = (UINT32) RShiftU64 (Index, 32); + RegisterTableEntry[RegisterTable->TableLength].ValidBitStart = ValidBitStart; + RegisterTableEntry[RegisterTable->TableLength].ValidBitLength = ValidBitLength; + RegisterTableEntry[RegisterTable->TableLength].Value = Value; + RegisterTableEntry[RegisterTable->TableLength].TestThenWrite = TestThenWrite; + + RegisterTable->TableLength++; +} + +/** + Adds an entry in specified register table. + + This function adds an entry in specified register table, with given register type, + register index, bit section and value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] ValueMask Mask of bits in register to write + @param[in] Value Value to write + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +CpuRegisterTableWrite ( + IN UINTN ProcessorNumber, + IN REGISTER_TYPE RegisterType, + IN UINT64 Index, + IN UINT64 ValueMask, + IN UINT64 Value + ) +{ + UINT8 Start; + UINT8 End; + UINT8 Length; + + Start = (UINT8)LowBitSet64 (ValueMask); + End = (UINT8)HighBitSet64 (ValueMask); + Length = End - Start + 1; + CpuRegisterTableWriteWorker (FALSE, ProcessorNumber, RegisterType, Index, Start, Length, Value, FALSE); +} + +/** + Adds an entry in specified register table. + + This function adds an entry in specified register table, with given register type, + register index, bit section and value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] ValueMask Mask of bits in register to write + @param[in] Value Value to write + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +CpuRegisterTableTestThenWrite ( + IN UINTN ProcessorNumber, + IN REGISTER_TYPE RegisterType, + IN UINT64 Index, + IN UINT64 ValueMask, + IN UINT64 Value + ) +{ + UINT8 Start; + UINT8 End; + UINT8 Length; + + Start = (UINT8)LowBitSet64 (ValueMask); + End = (UINT8)HighBitSet64 (ValueMask); + Length = End - Start + 1; + CpuRegisterTableWriteWorker (FALSE, ProcessorNumber, RegisterType, Index, Start, Length, Value, TRUE); +} + +/** + Adds an entry in specified Pre-SMM register table. + + This function adds an entry in specified register table, with given register type, + register index, bit section and value. + + @param[in] ProcessorNumber The index of the CPU to add a register table entry. + @param[in] RegisterType Type of the register to program + @param[in] Index Index of the register to program + @param[in] ValueMask Mask of bits in register to write + @param[in] Value Value to write + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +PreSmmCpuRegisterTableWrite ( + IN UINTN ProcessorNumber, + IN REGISTER_TYPE RegisterType, + IN UINT64 Index, + IN UINT64 ValueMask, + IN UINT64 Value + ) +{ + UINT8 Start; + UINT8 End; + UINT8 Length; + + Start = (UINT8)LowBitSet64 (ValueMask); + End = (UINT8)HighBitSet64 (ValueMask); + Length = End - Start + 1; + CpuRegisterTableWriteWorker (TRUE, ProcessorNumber, RegisterType, Index, Start, Length, Value, FALSE); +} + +/** + Worker function to determine if a CPU feature is set in input CPU feature bit mask buffer. + + @param[in] CpuBitMask CPU feature bit mask buffer + @param[in] CpuBitMaskSize The size of CPU feature bit mask buffer + @param[in] Feature The bit number of the CPU feature + + @retval TRUE The CPU feature is set in CpuBitMask. + @retval FALSE The CPU feature is not set in CpuBitMask. + +**/ +BOOLEAN +IsCpuFeatureSetInCpuPcd ( + IN UINT8 *CpuBitMask, + IN UINTN CpuBitMaskSize, + IN UINT32 Feature + ) +{ + if ((Feature >> 3) >= CpuBitMaskSize) { + return FALSE; + } + return ((*(CpuBitMask + (Feature >> 3)) & (1 << (Feature & 0x07))) != 0); +} + +/** + Determines if a CPU feature is enabled in PcdCpuFeaturesSupport bit mask. + If a CPU feature is disabled in PcdCpuFeaturesSupport then all the code/data + associated with that feature should be optimized away if compiler + optimizations are enabled. + + @param[in] Feature The bit number of the CPU feature to check in the PCD + PcdCpuFeaturesSupport + + @retval TRUE The CPU feature is set in PcdCpuFeaturesSupport. + @retval FALSE The CPU feature is not set in PcdCpuFeaturesSupport. + + @note This service could be called by BSP only. +**/ +BOOLEAN +EFIAPI +IsCpuFeatureSupported ( + IN UINT32 Feature + ) +{ + return IsCpuFeatureSetInCpuPcd ( + (UINT8 *)PcdGetPtr (PcdCpuFeaturesSupport), + PcdGetSize (PcdCpuFeaturesSupport), + Feature + ); +} + +/** + Determines if a CPU feature is set in PcdCpuFeaturesSetting bit mask. + + @param[in] Feature The bit number of the CPU feature to check in the PCD + PcdCpuFeaturesSetting + + @retval TRUE The CPU feature is set in PcdCpuFeaturesSetting. + @retval FALSE The CPU feature is not set in PcdCpuFeaturesSetting. + + @note This service could be called by BSP only. +**/ +BOOLEAN +EFIAPI +IsCpuFeatureInSetting ( + IN UINT32 Feature + ) +{ + return IsCpuFeatureSetInCpuPcd ( + (UINT8 *)PcdGetPtr (PcdCpuFeaturesSetting), + PcdGetSize (PcdCpuFeaturesSetting), + Feature + ); +} + +/** + Switches to assigned BSP after CPU features initialization. + + @param[in] ProcessorNumber The index of the CPU executing this function. + + @note This service could be called by BSP only. +**/ +VOID +EFIAPI +SwitchBspAfterFeaturesInitialize ( + IN UINTN ProcessorNumber + ) +{ + CPU_FEATURES_DATA *CpuFeaturesData; + + CpuFeaturesData = GetCpuFeaturesData (); + CpuFeaturesData->BspNumber = ProcessorNumber; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/RegisterCpuFeaturesLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/RegisterCpuFeaturesLib.uni new file mode 100644 index 00000000..fee90471 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/RegisterCpuFeaturesLib/RegisterCpuFeaturesLib.uni @@ -0,0 +1,16 @@ +// /** @file
+// Register CPU Features Library instance.
+//
+// Register CPU Features Library instance.
+//
+// Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Register CPU Features Library instance"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Register CPU Features Library instance."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SecPeiDxeTimerLibUefiCpu/SecPeiDxeTimerLibUefiCpu.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SecPeiDxeTimerLibUefiCpu/SecPeiDxeTimerLibUefiCpu.inf new file mode 100644 index 00000000..9b418381 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SecPeiDxeTimerLibUefiCpu/SecPeiDxeTimerLibUefiCpu.inf @@ -0,0 +1,54 @@ +## @file +# Instance of Timer Library only using CPU resources. +# +# Timer Library that only uses CPU resources to provide calibrated delays +# on IA-32, x64, and IPF. +# Note: A driver of type DXE_RUNTIME_DRIVER and DXE_SMM_DRIVER can use this TimerLib +# in their initialization without any issues. They only have to be careful in +# the implementation of runtime services and SMI handlers. +# Because CPU Local APIC and ITC could be programmed by OS, it cannot be +# used by SMM drivers and runtime drivers, ACPI timer is recommended for SMM +# drivers and runtime drivers. +# +# This library differs with the SecPeiDxeTimerLibCpu library in the MdePkg in +# that it uses the local APIC library so that it supports x2APIC mode. +# +# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SecPeiDxeTimerLibUefiCpu + MODULE_UNI_FILE = SecPeiDxeTimerLibUefiCpu.uni + FILE_GUID = 4FFF2014-2086-4ee6-9B58-886D1967861C + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TimerLib + + +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + X86TimerLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + +[LibraryClasses] + PcdLib + DebugLib + LocalApicLib + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdFSBClock ## SOMETIMES_CONSUMES + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SecPeiDxeTimerLibUefiCpu/SecPeiDxeTimerLibUefiCpu.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SecPeiDxeTimerLibUefiCpu/SecPeiDxeTimerLibUefiCpu.uni new file mode 100644 index 00000000..e40ee106 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SecPeiDxeTimerLibUefiCpu/SecPeiDxeTimerLibUefiCpu.uni @@ -0,0 +1,26 @@ +// /** @file
+// Instance of Timer Library only using CPU resources.
+//
+// Timer Library that only uses CPU resources to provide calibrated delays
+// on IA-32, x64, and IPF.
+// Note: A driver of type DXE_RUNTIME_DRIVER and DXE_SMM_DRIVER can use this TimerLib
+// in their initialization without any issues. They only have to be careful in
+// the implementation of runtime services and SMI handlers.
+// Because CPU Local APIC and ITC could be programmed by OS, it cannot be
+// used by SMM drivers and runtime drivers, ACPI timer is recommended for SMM
+// drivers and runtime drivers.
+//
+// This library differs with the SecPeiDxeTimerLibCpu library in the MdePkg in
+// that it uses the local APIC library so that it supports x2APIC mode.
+//
+// Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Instance of Timer Library only using CPU resources"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Timer Library that only uses CPU resources to provide calibrated delays on IA-32, x64, and IPF. Note: A driver of type DXE_RUNTIME_DRIVER and DXE_SMM_DRIVER can use this TimerLib in their initialization without any issues. They only have to be careful in the implementation of runtime services and SMI handlers. Because CPU Local APIC and ITC could be programmed by OS, it cannot be used by SMM drivers and runtime drivers, ACPI timer is recommended for SMM drivers and runtime drivers. This library differs with the SecPeiDxeTimerLibCpu library in the MdePkg in that it uses the local APIC library so that it supports x2APIC mode."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SecPeiDxeTimerLibUefiCpu/X86TimerLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SecPeiDxeTimerLibUefiCpu/X86TimerLib.c new file mode 100644 index 00000000..03683be8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SecPeiDxeTimerLibUefiCpu/X86TimerLib.c @@ -0,0 +1,260 @@ +/** @file + Timer Library functions built upon local APIC on IA32/x64. + + This library uses the local APIC library so that it supports x2APIC mode. + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Base.h> +#include <Library/TimerLib.h> +#include <Library/BaseLib.h> +#include <Library/PcdLib.h> +#include <Library/DebugLib.h> +#include <Library/LocalApicLib.h> + +/** + Internal function to return the frequency of the local APIC timer. + + @return The frequency of the timer in Hz. + +**/ +UINT32 +EFIAPI +InternalX86GetTimerFrequency ( + VOID + ) +{ + UINTN Divisor; + + GetApicTimerState (&Divisor, NULL, NULL); + return PcdGet32(PcdFSBClock) / (UINT32)Divisor; +} + +/** + Stalls the CPU for at least the given number of ticks. + + Stalls the CPU for at least the given number of ticks. It's invoked by + MicroSecondDelay() and NanoSecondDelay(). + + This function will ASSERT if the APIC timer intial count returned from + GetApicTimerInitCount() is zero. + + @param Delay A period of time to delay in ticks. + +**/ +VOID +EFIAPI +InternalX86Delay ( + IN UINT32 Delay + ) +{ + INT32 Ticks; + UINT32 Times; + UINT32 InitCount; + UINT32 StartTick; + + // + // In case Delay is too larger, separate it into several small delay slot. + // Devided Delay by half value of Init Count is to avoid Delay close to + // the Init Count, timeout maybe missing if the time consuming between 2 + // GetApicTimerCurrentCount() invoking is larger than the time gap between + // Delay and the Init Count. + // + InitCount = GetApicTimerInitCount (); + ASSERT (InitCount != 0); + Times = Delay / (InitCount / 2); + Delay = Delay % (InitCount / 2); + + // + // Get Start Tick and do delay + // + StartTick = GetApicTimerCurrentCount (); + do { + // + // Wait until time out by Delay value + // + do { + CpuPause (); + // + // Get Ticks from Start to Current. + // + Ticks = StartTick - GetApicTimerCurrentCount (); + // + // Ticks < 0 means Timer wrap-arounds happens. + // + if (Ticks < 0) { + Ticks += InitCount; + } + } while ((UINT32)Ticks < Delay); + + // + // Update StartTick and Delay for next delay slot + // + StartTick -= (StartTick > Delay) ? Delay : (Delay - InitCount); + Delay = InitCount / 2; + } while (Times-- > 0); +} + +/** + Stalls the CPU for at least the given number of microseconds. + + Stalls the CPU for the number of microseconds specified by MicroSeconds. + + @param MicroSeconds The minimum number of microseconds to delay. + + @return The value of MicroSeconds inputted. + +**/ +UINTN +EFIAPI +MicroSecondDelay ( + IN UINTN MicroSeconds + ) +{ + InternalX86Delay ( + (UINT32)DivU64x32 ( + MultU64x64 ( + InternalX86GetTimerFrequency (), + MicroSeconds + ), + 1000000u + ) + ); + return MicroSeconds; +} + +/** + Stalls the CPU for at least the given number of nanoseconds. + + Stalls the CPU for the number of nanoseconds specified by NanoSeconds. + + @param NanoSeconds The minimum number of nanoseconds to delay. + + @return The value of NanoSeconds inputted. + +**/ +UINTN +EFIAPI +NanoSecondDelay ( + IN UINTN NanoSeconds + ) +{ + InternalX86Delay ( + (UINT32)DivU64x32 ( + MultU64x64 ( + InternalX86GetTimerFrequency (), + NanoSeconds + ), + 1000000000u + ) + ); + return NanoSeconds; +} + +/** + Retrieves the current value of a 64-bit free running performance counter. + + The counter can either count up by 1 or count down by 1. If the physical + performance counter counts by a larger increment, then the counter values + must be translated. The properties of the counter can be retrieved from + GetPerformanceCounterProperties(). + + @return The current value of the free running performance counter. + +**/ +UINT64 +EFIAPI +GetPerformanceCounter ( + VOID + ) +{ + return (UINT64)GetApicTimerCurrentCount (); +} + +/** + Retrieves the 64-bit frequency in Hz and the range of performance counter + values. + + If StartValue is not NULL, then the value that the performance counter starts + with immediately after is it rolls over is returned in StartValue. If + EndValue is not NULL, then the value that the performance counter end with + immediately before it rolls over is returned in EndValue. The 64-bit + frequency of the performance counter in Hz is always returned. If StartValue + is less than EndValue, then the performance counter counts up. If StartValue + is greater than EndValue, then the performance counter counts down. For + example, a 64-bit free running counter that counts up would have a StartValue + of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter + that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0. + + @param StartValue The value the performance counter starts with when it + rolls over. + @param EndValue The value that the performance counter ends with before + it rolls over. + + @return The frequency in Hz. + +**/ +UINT64 +EFIAPI +GetPerformanceCounterProperties ( + OUT UINT64 *StartValue, OPTIONAL + OUT UINT64 *EndValue OPTIONAL + ) +{ + if (StartValue != NULL) { + *StartValue = (UINT64)GetApicTimerInitCount (); + } + + if (EndValue != NULL) { + *EndValue = 0; + } + + return (UINT64) InternalX86GetTimerFrequency (); +} + +/** + Converts elapsed ticks of performance counter to time in nanoseconds. + + This function converts the elapsed ticks of running performance counter to + time value in unit of nanoseconds. + + @param Ticks The number of elapsed ticks of running performance counter. + + @return The elapsed time in nanoseconds. + +**/ +UINT64 +EFIAPI +GetTimeInNanoSecond ( + IN UINT64 Ticks + ) +{ + UINT64 Frequency; + UINT64 NanoSeconds; + UINT64 Remainder; + INTN Shift; + + Frequency = GetPerformanceCounterProperties (NULL, NULL); + + // + // Ticks + // Time = --------- x 1,000,000,000 + // Frequency + // + NanoSeconds = MultU64x32 (DivU64x64Remainder (Ticks, Frequency, &Remainder), 1000000000u); + + // + // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit. + // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34, + // i.e. highest bit set in Remainder should <= 33. + // + Shift = MAX (0, HighBitSet64 (Remainder) - 33); + Remainder = RShiftU64 (Remainder, (UINTN) Shift); + Frequency = RShiftU64 (Frequency, (UINTN) Shift); + NanoSeconds += DivU64x64Remainder (MultU64x32 (Remainder, 1000000000u), Frequency, NULL); + + return NanoSeconds; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/CpuFeaturesLib.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/CpuFeaturesLib.h new file mode 100644 index 00000000..5928fbb8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/CpuFeaturesLib.h @@ -0,0 +1,48 @@ +/** @file + Internal library function definitions. + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef CPU_FEATURES_LIB_H_ +#define CPU_FEATURES_LIB_H_ + +/** + Performs library initialization. + + This initialization function contains common functionality shared betwen all + library instance constructors. + +**/ +VOID +CpuFeaturesLibInitialization ( + VOID + ); + +/** + Internal worker function that is called to complete CPU initialization at the + end of SmmCpuFeaturesInitializeProcessor(). + +**/ +VOID +FinishSmmCpuFeaturesInitializeProcessor ( + VOID + ); + +/** + Gets the maximum number of logical processors from the PCD PcdCpuMaxLogicalProcessorNumber. + + This access is abstracted from the PCD services to enforce that the PCD be + FixedAtBuild in the Standalone MM build of this driver. + + @return The value of PcdCpuMaxLogicalProcessorNumber. + +**/ +UINT32 +GetCpuMaxLogicalProcessorNumber ( + VOID + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/Ia32/SmiEntry.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/Ia32/SmiEntry.nasm new file mode 100644 index 00000000..2ff64510 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/Ia32/SmiEntry.nasm @@ -0,0 +1,276 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; SmiEntry.nasm +; +; Abstract: +; +; Code template of the SMI handler for a particular processor +; +;------------------------------------------------------------------------------- + +%include "StuffRsbNasm.inc" + +%define MSR_IA32_MISC_ENABLE 0x1A0 +%define MSR_EFER 0xc0000080 +%define MSR_EFER_XD 0x800 + +; +; Constants relating to TXT_PROCESSOR_SMM_DESCRIPTOR +; +%define DSC_OFFSET 0xfb00 +%define DSC_GDTPTR 0x48 +%define DSC_GDTSIZ 0x50 +%define DSC_CS 0x14 +%define DSC_DS 0x16 +%define DSC_SS 0x18 +%define DSC_OTHERSEG 0x1a + +%define PROTECT_MODE_CS 0x8 +%define PROTECT_MODE_DS 0x20 +%define TSS_SEGMENT 0x40 + +extern ASM_PFX(SmiRendezvous) +extern ASM_PFX(FeaturePcdGet (PcdCpuSmmStackGuard)) +extern ASM_PFX(CpuSmmDebugEntry) +extern ASM_PFX(CpuSmmDebugExit) + +global ASM_PFX(gcStmSmiHandlerTemplate) +global ASM_PFX(gcStmSmiHandlerSize) +global ASM_PFX(gcStmSmiHandlerOffset) +global ASM_PFX(gStmSmiCr3) +global ASM_PFX(gStmSmiStack) +global ASM_PFX(gStmSmbase) +global ASM_PFX(gStmXdSupported) +extern ASM_PFX(gStmSmiHandlerIdtr) + +ASM_PFX(gStmSmiCr3) EQU StmSmiCr3Patch - 4 +ASM_PFX(gStmSmiStack) EQU StmSmiStackPatch - 4 +ASM_PFX(gStmSmbase) EQU StmSmbasePatch - 4 +ASM_PFX(gStmXdSupported) EQU StmXdSupportedPatch - 1 + + SECTION .text + +BITS 16 +ASM_PFX(gcStmSmiHandlerTemplate): +_StmSmiEntryPoint: + mov bx, _StmGdtDesc - _StmSmiEntryPoint + 0x8000 + mov ax,[cs:DSC_OFFSET + DSC_GDTSIZ] + dec ax + mov [cs:bx], ax + mov eax, [cs:DSC_OFFSET + DSC_GDTPTR] + mov [cs:bx + 2], eax + mov ebp, eax ; ebp = GDT base +o32 lgdt [cs:bx] ; lgdt fword ptr cs:[bx] + mov ax, PROTECT_MODE_CS + mov [cs:bx-0x2],ax +o32 mov edi, strict dword 0 +StmSmbasePatch: + lea eax, [edi + (@32bit - _StmSmiEntryPoint) + 0x8000] + mov [cs:bx-0x6],eax + mov ebx, cr0 + and ebx, 0x9ffafff3 + or ebx, 0x23 + mov cr0, ebx + jmp dword 0x0:0x0 +_StmGdtDesc: + DW 0 + DD 0 + +BITS 32 +@32bit: + mov ax, PROTECT_MODE_DS +o16 mov ds, ax +o16 mov es, ax +o16 mov fs, ax +o16 mov gs, ax +o16 mov ss, ax + mov esp, strict dword 0 +StmSmiStackPatch: + mov eax, ASM_PFX(gStmSmiHandlerIdtr) + lidt [eax] + jmp ProtFlatMode + +ProtFlatMode: + mov eax, strict dword 0 +StmSmiCr3Patch: + mov cr3, eax +; +; Need to test for CR4 specific bit support +; + mov eax, 1 + cpuid ; use CPUID to determine if specific CR4 bits are supported + xor eax, eax ; Clear EAX + test edx, BIT2 ; Check for DE capabilities + jz .0 + or eax, BIT3 +.0: + test edx, BIT6 ; Check for PAE capabilities + jz .1 + or eax, BIT5 +.1: + test edx, BIT7 ; Check for MCE capabilities + jz .2 + or eax, BIT6 +.2: + test edx, BIT24 ; Check for FXSR capabilities + jz .3 + or eax, BIT9 +.3: + test edx, BIT25 ; Check for SSE capabilities + jz .4 + or eax, BIT10 +.4: ; as cr4.PGE is not set here, refresh cr3 + mov cr4, eax ; in PreModifyMtrrs() to flush TLB. + + cmp byte [dword ASM_PFX(FeaturePcdGet (PcdCpuSmmStackGuard))], 0 + jz .6 +; Load TSS + mov byte [ebp + TSS_SEGMENT + 5], 0x89 ; clear busy flag + mov eax, TSS_SEGMENT + ltr ax +.6: + +; enable NXE if supported + mov al, strict byte 1 +StmXdSupportedPatch: + cmp al, 0 + jz @SkipXd +; +; Check XD disable bit +; + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + push edx ; save MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 ; MSR_IA32_MISC_ENABLE[34] + jz .5 + and dx, 0xFFFB ; clear XD Disable bit if it is set + wrmsr +.5: + mov ecx, MSR_EFER + rdmsr + or ax, MSR_EFER_XD ; enable NXE + wrmsr + jmp @XdDone +@SkipXd: + sub esp, 4 +@XdDone: + + mov ebx, cr0 + or ebx, 0x80010023 ; enable paging + WP + NE + MP + PE + mov cr0, ebx + lea ebx, [edi + DSC_OFFSET] + mov ax, [ebx + DSC_DS] + mov ds, eax + mov ax, [ebx + DSC_OTHERSEG] + mov es, eax + mov fs, eax + mov gs, eax + mov ax, [ebx + DSC_SS] + mov ss, eax + +CommonHandler: + mov ebx, [esp + 4] ; CPU Index + push ebx + mov eax, ASM_PFX(CpuSmmDebugEntry) + call eax + add esp, 4 + + push ebx + mov eax, ASM_PFX(SmiRendezvous) + call eax + add esp, 4 + + push ebx + mov eax, ASM_PFX(CpuSmmDebugExit) + call eax + add esp, 4 + + mov eax, ASM_PFX(gStmXdSupported) + mov al, [eax] + cmp al, 0 + jz .7 + pop edx ; get saved MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 + jz .7 + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + or dx, BIT2 ; set XD Disable bit if it was set before entering into SMM + wrmsr + +.7: + StuffRsb32 + rsm + + +_StmSmiHandler: +; +; Check XD disable bit +; + xor esi, esi + mov eax, ASM_PFX(gStmXdSupported) + mov al, [eax] + cmp al, 0 + jz @StmXdDone + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + mov esi, edx ; save MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 ; MSR_IA32_MISC_ENABLE[34] + jz .5 + and dx, 0xFFFB ; clear XD Disable bit if it is set + wrmsr +.5: + mov ecx, MSR_EFER + rdmsr + or ax, MSR_EFER_XD ; enable NXE + wrmsr +@StmXdDone: + push esi + + ; below step is needed, because STM does not run above code. + ; we have to run below code to set IDT/CR0/CR4 + mov eax, ASM_PFX(gStmSmiHandlerIdtr) + lidt [eax] + + mov eax, cr0 + or eax, 0x80010023 ; enable paging + WP + NE + MP + PE + mov cr0, eax +; +; Need to test for CR4 specific bit support +; + mov eax, 1 + cpuid ; use CPUID to determine if specific CR4 bits are supported + mov eax, cr4 ; init EAX + test edx, BIT2 ; Check for DE capabilities + jz .0 + or eax, BIT3 +.0: + test edx, BIT6 ; Check for PAE capabilities + jz .1 + or eax, BIT5 +.1: + test edx, BIT7 ; Check for MCE capabilities + jz .2 + or eax, BIT6 +.2: + test edx, BIT24 ; Check for FXSR capabilities + jz .3 + or eax, BIT9 +.3: + test edx, BIT25 ; Check for SSE capabilities + jz .4 + or eax, BIT10 +.4: ; as cr4.PGE is not set here, refresh cr3 + mov cr4, eax ; in PreModifyMtrrs() to flush TLB. + ; STM init finish + jmp CommonHandler + +ASM_PFX(gcStmSmiHandlerSize) : DW $ - _StmSmiEntryPoint +ASM_PFX(gcStmSmiHandlerOffset) : DW _StmSmiHandler - _StmSmiEntryPoint + +global ASM_PFX(SmmCpuFeaturesLibStmSmiEntryFixupAddress) +ASM_PFX(SmmCpuFeaturesLibStmSmiEntryFixupAddress): + ret diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/Ia32/SmiException.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/Ia32/SmiException.nasm new file mode 100644 index 00000000..d9ae5493 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/Ia32/SmiException.nasm @@ -0,0 +1,173 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; SmiException.nasm +; +; Abstract: +; +; Exception handlers used in SM mode +; +;------------------------------------------------------------------------------- + +%include "StuffRsbNasm.inc" + +global ASM_PFX(gcStmPsd) + +extern ASM_PFX(SmmStmExceptionHandler) +extern ASM_PFX(SmmStmSetup) +extern ASM_PFX(SmmStmTeardown) +extern ASM_PFX(gStmXdSupported) +extern ASM_PFX(gStmSmiHandlerIdtr) + +%define MSR_IA32_MISC_ENABLE 0x1A0 +%define MSR_EFER 0xc0000080 +%define MSR_EFER_XD 0x800 + +CODE_SEL equ 0x08 +DATA_SEL equ 0x20 +TSS_SEL equ 0x40 + + SECTION .data + +ASM_PFX(gcStmPsd): + DB 'TXTPSSIG' + DW PSD_SIZE + DW 1 ; Version + DD 0 ; LocalApicId + DB 0x05 ; Cr4Pse;Cr4Pae;Intel64Mode;ExecutionDisableOutsideSmrr + DB 0 ; BIOS to STM + DB 0 ; STM to BIOS + DB 0 + DW CODE_SEL + DW DATA_SEL + DW DATA_SEL + DW DATA_SEL + DW TSS_SEL + DW 0 + DQ 0 ; SmmCr3 + DD ASM_PFX(OnStmSetup) + DD 0 + DD ASM_PFX(OnStmTeardown) + DD 0 + DQ 0 ; SmmSmiHandlerRip - SMM guest entrypoint + DQ 0 ; SmmSmiHandlerRsp + DQ 0 + DD 0 + DD 0x80010100 ; RequiredStmSmmRevId + DD ASM_PFX(OnException) + DD 0 + DQ 0 ; ExceptionStack + DW DATA_SEL + DW 0x01F ; ExceptionFilter + DD 0 + DD 0 + DD 0 + DQ 0 ; BiosHwResourceRequirementsPtr + DQ 0 ; AcpiRsdp + DB 0 ; PhysicalAddressBits +PSD_SIZE equ $ - ASM_PFX(gcStmPsd) + + SECTION .text +;------------------------------------------------------------------------------ +; SMM Exception handlers +;------------------------------------------------------------------------------ +global ASM_PFX(OnException) +ASM_PFX(OnException): + mov ecx, esp + push ecx + call ASM_PFX(SmmStmExceptionHandler) + add esp, 4 + + mov ebx, eax + mov eax, 4 + vmcall + jmp $ + +global ASM_PFX(OnStmSetup) +ASM_PFX(OnStmSetup): +; +; Check XD disable bit +; + xor esi, esi + mov eax, ASM_PFX(gStmXdSupported) + mov al, [eax] + cmp al, 0 + jz @StmXdDone1 + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + mov esi, edx ; save MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 ; MSR_IA32_MISC_ENABLE[34] + jz .51 + and dx, 0xFFFB ; clear XD Disable bit if it is set + wrmsr +.51: + mov ecx, MSR_EFER + rdmsr + or ax, MSR_EFER_XD ; enable NXE + wrmsr +@StmXdDone1: + push esi + + call ASM_PFX(SmmStmSetup) + + mov eax, ASM_PFX(gStmXdSupported) + mov al, [eax] + cmp al, 0 + jz .71 + pop edx ; get saved MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 + jz .71 + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + or dx, BIT2 ; set XD Disable bit if it was set before entering into SMM + wrmsr + +.71: + StuffRsb32 + rsm + +global ASM_PFX(OnStmTeardown) +ASM_PFX(OnStmTeardown): +; +; Check XD disable bit +; + xor esi, esi + mov eax, ASM_PFX(gStmXdSupported) + mov al, [eax] + cmp al, 0 + jz @StmXdDone2 + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + mov esi, edx ; save MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 ; MSR_IA32_MISC_ENABLE[34] + jz .52 + and dx, 0xFFFB ; clear XD Disable bit if it is set + wrmsr +.52: + mov ecx, MSR_EFER + rdmsr + or ax, MSR_EFER_XD ; enable NXE + wrmsr +@StmXdDone2: + push esi + + call ASM_PFX(SmmStmTeardown) + + mov eax, ASM_PFX(gStmXdSupported) + mov al, [eax] + cmp al, 0 + jz .72 + pop edx ; get saved MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 + jz .72 + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + or dx, BIT2 ; set XD Disable bit if it was set before entering into SMM + wrmsr + +.72: + StuffRsb32 + rsm diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/Ia32/SmmStmSupport.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/Ia32/SmmStmSupport.c new file mode 100644 index 00000000..d6166b75 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/Ia32/SmmStmSupport.c @@ -0,0 +1,77 @@ +/** @file + SMM STM support functions + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiMm.h> +#include <Library/DebugLib.h> + +#include "SmmStm.h" + +/// +/// Page Table Entry +/// +#define IA32_PG_P BIT0 +#define IA32_PG_RW BIT1 +#define IA32_PG_PS BIT7 + +/** + + Create 4G page table for STM. + 4M Non-PAE page table in IA32 version. + + @param PageTableBase The page table base in MSEG + +**/ +VOID +StmGen4GPageTable ( + IN UINTN PageTableBase + ) +{ + UINTN Index; + UINT32 *Pte; + UINT32 Address; + + Pte = (UINT32*)(UINTN)PageTableBase; + + Address = 0; + for (Index = 0; Index < SIZE_4KB / sizeof (*Pte); Index++) { + *Pte = Address | IA32_PG_PS | IA32_PG_RW | IA32_PG_P; + Pte++; + Address += SIZE_4MB; + } +} + +/** + This is SMM exception handle. + Consumed by STM when exception happen. + + @param Context STM protection exception stack frame + + @return the EBX value for STM reference. + EBX = 0: resume SMM guest using register state found on exception stack. + EBX = 1 to 0x0F: EBX contains a BIOS error code which the STM must record in the + TXT.ERRORCODE register and subsequently reset the system via + TXT.CMD.SYS_RESET. The value of the TXT.ERRORCODE register is calculated as + follows: TXT.ERRORCODE = (EBX & 0x0F) | STM_CRASH_BIOS_PANIC + EBX = 0x10 to 0xFFFFFFFF - reserved, do not use. + +**/ +UINT32 +EFIAPI +SmmStmExceptionHandler ( + IN OUT STM_PROTECTION_EXCEPTION_STACK_FRAME Context + ) +{ + // TBD - SmmStmExceptionHandler, record information + DEBUG ((DEBUG_ERROR, "SmmStmExceptionHandler ...\n")); + // + // Skip this instruction and continue; + // + Context.Ia32StackFrame->Rip += Context.Ia32StackFrame->VmcsExitInstructionLength; + + return 0; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.c new file mode 100644 index 00000000..7cb6639d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.c @@ -0,0 +1,31 @@ +/** @file +Implementation specific to the SmmCpuFeatureLib library instance. + +Copyright (c) Microsoft Corporation.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiMm.h> +#include "CpuFeaturesLib.h" + +/** + The constructor function for the Traditional MM library instance without STM. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. + +**/ +EFI_STATUS +EFIAPI +SmmCpuFeaturesLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + CpuFeaturesLibInitialization (); + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.inf new file mode 100644 index 00000000..e504218a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.inf @@ -0,0 +1,37 @@ +## @file +# The CPU specific programming for PiSmmCpuDxeSmm module. +# +# Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SmmCpuFeaturesLib + MODULE_UNI_FILE = SmmCpuFeaturesLib.uni + FILE_GUID = FC3DC10D-D271-422a-AFF3-CBCF70344431 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = SmmCpuFeaturesLib + CONSTRUCTOR = SmmCpuFeaturesLibConstructor + +[Sources] + CpuFeaturesLib.h + SmmCpuFeaturesLib.c + SmmCpuFeaturesLibCommon.c + SmmCpuFeaturesLibNoStm.c + TraditionalMmCpuFeaturesLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + PcdLib + MemoryAllocationLib + DebugLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## SOMETIMES_CONSUMES diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.uni new file mode 100644 index 00000000..6ee54e92 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.uni @@ -0,0 +1,12 @@ +// /** @file
+// The CPU specific programming for PiSmmCpuDxeSmm module.
+//
+// Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "The CPU specific programming for PiSmmCpuDxeSmm module."
+
+#string STR_MODULE_DESCRIPTION #language en-US "The CPU specific programming for PiSmmCpuDxeSmm module."
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLibCommon.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLibCommon.c new file mode 100644 index 00000000..b5c55f38 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLibCommon.c @@ -0,0 +1,613 @@ +/** @file +Implementation shared across all library instances. + +Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> +Copyright (c) Microsoft Corporation.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiMm.h> +#include <Library/SmmCpuFeaturesLib.h> +#include <Library/BaseLib.h> +#include <Library/MtrrLib.h> +#include <Library/PcdLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DebugLib.h> +#include <Register/Intel/Cpuid.h> +#include <Register/Intel/SmramSaveStateMap.h> +#include "CpuFeaturesLib.h" + +// +// Machine Specific Registers (MSRs) +// +#define SMM_FEATURES_LIB_IA32_MTRR_CAP 0x0FE +#define SMM_FEATURES_LIB_IA32_FEATURE_CONTROL 0x03A +#define SMM_FEATURES_LIB_IA32_SMRR_PHYSBASE 0x1F2 +#define SMM_FEATURES_LIB_IA32_SMRR_PHYSMASK 0x1F3 +#define SMM_FEATURES_LIB_IA32_CORE_SMRR_PHYSBASE 0x0A0 +#define SMM_FEATURES_LIB_IA32_CORE_SMRR_PHYSMASK 0x0A1 +#define EFI_MSR_SMRR_MASK 0xFFFFF000 +#define EFI_MSR_SMRR_PHYS_MASK_VALID BIT11 +#define SMM_FEATURES_LIB_SMM_FEATURE_CONTROL 0x4E0 + +// +// MSRs required for configuration of SMM Code Access Check +// +#define SMM_FEATURES_LIB_IA32_MCA_CAP 0x17D +#define SMM_CODE_ACCESS_CHK_BIT BIT58 + +// +// Set default value to assume SMRR is not supported +// +BOOLEAN mSmrrSupported = FALSE; + +// +// Set default value to assume MSR_SMM_FEATURE_CONTROL is not supported +// +BOOLEAN mSmmFeatureControlSupported = FALSE; + +// +// Set default value to assume IA-32 Architectural MSRs are used +// +UINT32 mSmrrPhysBaseMsr = SMM_FEATURES_LIB_IA32_SMRR_PHYSBASE; +UINT32 mSmrrPhysMaskMsr = SMM_FEATURES_LIB_IA32_SMRR_PHYSMASK; + +// +// Set default value to assume MTRRs need to be configured on each SMI +// +BOOLEAN mNeedConfigureMtrrs = TRUE; + +// +// Array for state of SMRR enable on all CPUs +// +BOOLEAN *mSmrrEnabled; + +/** + Performs library initialization. + + This initialization function contains common functionality shared betwen all + library instance constructors. + +**/ +VOID +CpuFeaturesLibInitialization ( + VOID + ) +{ + UINT32 RegEax; + UINT32 RegEdx; + UINTN FamilyId; + UINTN ModelId; + + // + // Retrieve CPU Family and Model + // + AsmCpuid (CPUID_VERSION_INFO, &RegEax, NULL, NULL, &RegEdx); + FamilyId = (RegEax >> 8) & 0xf; + ModelId = (RegEax >> 4) & 0xf; + if (FamilyId == 0x06 || FamilyId == 0x0f) { + ModelId = ModelId | ((RegEax >> 12) & 0xf0); + } + + // + // Check CPUID(CPUID_VERSION_INFO).EDX[12] for MTRR capability + // + if ((RegEdx & BIT12) != 0) { + // + // Check MTRR_CAP MSR bit 11 for SMRR support + // + if ((AsmReadMsr64 (SMM_FEATURES_LIB_IA32_MTRR_CAP) & BIT11) != 0) { + mSmrrSupported = TRUE; + } + } + + // + // Intel(R) 64 and IA-32 Architectures Software Developer's Manual + // Volume 3C, Section 35.3 MSRs in the Intel(R) Atom(TM) Processor Family + // + // If CPU Family/Model is 06_1CH, 06_26H, 06_27H, 06_35H or 06_36H, then + // SMRR Physical Base and SMM Physical Mask MSRs are not available. + // + if (FamilyId == 0x06) { + if (ModelId == 0x1C || ModelId == 0x26 || ModelId == 0x27 || ModelId == 0x35 || ModelId == 0x36) { + mSmrrSupported = FALSE; + } + } + + // + // Intel(R) 64 and IA-32 Architectures Software Developer's Manual + // Volume 3C, Section 35.2 MSRs in the Intel(R) Core(TM) 2 Processor Family + // + // If CPU Family/Model is 06_0F or 06_17, then use Intel(R) Core(TM) 2 + // Processor Family MSRs + // + if (FamilyId == 0x06) { + if (ModelId == 0x17 || ModelId == 0x0f) { + mSmrrPhysBaseMsr = SMM_FEATURES_LIB_IA32_CORE_SMRR_PHYSBASE; + mSmrrPhysMaskMsr = SMM_FEATURES_LIB_IA32_CORE_SMRR_PHYSMASK; + } + } + + // + // Intel(R) 64 and IA-32 Architectures Software Developer's Manual + // Volume 3C, Section 34.4.2 SMRAM Caching + // An IA-32 processor does not automatically write back and invalidate its + // caches before entering SMM or before exiting SMM. Because of this behavior, + // care must be taken in the placement of the SMRAM in system memory and in + // the caching of the SMRAM to prevent cache incoherence when switching back + // and forth between SMM and protected mode operation. + // + // An IA-32 processor is a processor that does not support the Intel 64 + // Architecture. Support for the Intel 64 Architecture can be detected from + // CPUID(CPUID_EXTENDED_CPU_SIG).EDX[29] + // + // If an IA-32 processor is detected, then set mNeedConfigureMtrrs to TRUE, + // so caches are flushed on SMI entry and SMI exit, the interrupted code + // MTRRs are saved/restored, and MTRRs for SMM are loaded. + // + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_EXTENDED_CPU_SIG) { + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT29) != 0) { + mNeedConfigureMtrrs = FALSE; + } + } + + // + // Allocate array for state of SMRR enable on all CPUs + // + mSmrrEnabled = (BOOLEAN *)AllocatePool (sizeof (BOOLEAN) * GetCpuMaxLogicalProcessorNumber ()); + ASSERT (mSmrrEnabled != NULL); +} + +/** + Called during the very first SMI into System Management Mode to initialize + CPU features, including SMBASE, for the currently executing CPU. Since this + is the first SMI, the SMRAM Save State Map is at the default address of + SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET. The currently executing + CPU is specified by CpuIndex and CpuIndex can be used to access information + about the currently executing CPU in the ProcessorInfo array and the + HotPlugCpuData data structure. + + @param[in] CpuIndex The index of the CPU to initialize. The value + must be between 0 and the NumberOfCpus field in + the System Management System Table (SMST). + @param[in] IsMonarch TRUE if the CpuIndex is the index of the CPU that + was elected as monarch during System Management + Mode initialization. + FALSE if the CpuIndex is not the index of the CPU + that was elected as monarch during System + Management Mode initialization. + @param[in] ProcessorInfo Pointer to an array of EFI_PROCESSOR_INFORMATION + structures. ProcessorInfo[CpuIndex] contains the + information for the currently executing CPU. + @param[in] CpuHotPlugData Pointer to the CPU_HOT_PLUG_DATA structure that + contains the ApidId and SmBase arrays. +**/ +VOID +EFIAPI +SmmCpuFeaturesInitializeProcessor ( + IN UINTN CpuIndex, + IN BOOLEAN IsMonarch, + IN EFI_PROCESSOR_INFORMATION *ProcessorInfo, + IN CPU_HOT_PLUG_DATA *CpuHotPlugData + ) +{ + SMRAM_SAVE_STATE_MAP *CpuState; + UINT64 FeatureControl; + UINT32 RegEax; + UINT32 RegEdx; + UINTN FamilyId; + UINTN ModelId; + + // + // Configure SMBASE. + // + CpuState = (SMRAM_SAVE_STATE_MAP *)(UINTN)(SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET); + CpuState->x86.SMBASE = (UINT32)CpuHotPlugData->SmBase[CpuIndex]; + + // + // Intel(R) 64 and IA-32 Architectures Software Developer's Manual + // Volume 3C, Section 35.2 MSRs in the Intel(R) Core(TM) 2 Processor Family + // + // If Intel(R) Core(TM) Core(TM) 2 Processor Family MSRs are being used, then + // make sure SMRR Enable(BIT3) of MSR_FEATURE_CONTROL MSR(0x3A) is set before + // accessing SMRR base/mask MSRs. If Lock(BIT0) of MSR_FEATURE_CONTROL MSR(0x3A) + // is set, then the MSR is locked and can not be modified. + // + if (mSmrrSupported && mSmrrPhysBaseMsr == SMM_FEATURES_LIB_IA32_CORE_SMRR_PHYSBASE) { + FeatureControl = AsmReadMsr64 (SMM_FEATURES_LIB_IA32_FEATURE_CONTROL); + if ((FeatureControl & BIT3) == 0) { + if ((FeatureControl & BIT0) == 0) { + AsmWriteMsr64 (SMM_FEATURES_LIB_IA32_FEATURE_CONTROL, FeatureControl | BIT3); + } else { + mSmrrSupported = FALSE; + } + } + } + + // + // If SMRR is supported, then program SMRR base/mask MSRs. + // The EFI_MSR_SMRR_PHYS_MASK_VALID bit is not set until the first normal SMI. + // The code that initializes SMM environment is running in normal mode + // from SMRAM region. If SMRR is enabled here, then the SMRAM region + // is protected and the normal mode code execution will fail. + // + if (mSmrrSupported) { + // + // SMRR size cannot be less than 4-KBytes + // SMRR size must be of length 2^n + // SMRR base alignment cannot be less than SMRR length + // + if ((CpuHotPlugData->SmrrSize < SIZE_4KB) || + (CpuHotPlugData->SmrrSize != GetPowerOfTwo32 (CpuHotPlugData->SmrrSize)) || + ((CpuHotPlugData->SmrrBase & ~(CpuHotPlugData->SmrrSize - 1)) != CpuHotPlugData->SmrrBase)) { + // + // Print message and halt if CPU is Monarch + // + if (IsMonarch) { + DEBUG ((DEBUG_ERROR, "SMM Base/Size does not meet alignment/size requirement!\n")); + CpuDeadLoop (); + } + } else { + AsmWriteMsr64 (mSmrrPhysBaseMsr, CpuHotPlugData->SmrrBase | MTRR_CACHE_WRITE_BACK); + AsmWriteMsr64 (mSmrrPhysMaskMsr, (~(CpuHotPlugData->SmrrSize - 1) & EFI_MSR_SMRR_MASK)); + mSmrrEnabled[CpuIndex] = FALSE; + } + } + + // + // Retrieve CPU Family and Model + // + AsmCpuid (CPUID_VERSION_INFO, &RegEax, NULL, NULL, &RegEdx); + FamilyId = (RegEax >> 8) & 0xf; + ModelId = (RegEax >> 4) & 0xf; + if (FamilyId == 0x06 || FamilyId == 0x0f) { + ModelId = ModelId | ((RegEax >> 12) & 0xf0); + } + + // + // Intel(R) 64 and IA-32 Architectures Software Developer's Manual + // Volume 3C, Section 35.10.1 MSRs in 4th Generation Intel(R) Core(TM) + // Processor Family. + // + // If CPU Family/Model is 06_3C, 06_45, or 06_46 then use 4th Generation + // Intel(R) Core(TM) Processor Family MSRs. + // + if (FamilyId == 0x06) { + if (ModelId == 0x3C || ModelId == 0x45 || ModelId == 0x46 || + ModelId == 0x3D || ModelId == 0x47 || ModelId == 0x4E || ModelId == 0x4F || + ModelId == 0x3F || ModelId == 0x56 || ModelId == 0x57 || ModelId == 0x5C || + ModelId == 0x8C) { + // + // Check to see if the CPU supports the SMM Code Access Check feature + // Do not access this MSR unless the CPU supports the SmmRegFeatureControl + // + if ((AsmReadMsr64 (SMM_FEATURES_LIB_IA32_MCA_CAP) & SMM_CODE_ACCESS_CHK_BIT) != 0) { + mSmmFeatureControlSupported = TRUE; + } + } + } + + // + // Call internal worker function that completes the CPU initialization + // + FinishSmmCpuFeaturesInitializeProcessor (); +} + +/** + This function updates the SMRAM save state on the currently executing CPU + to resume execution at a specific address after an RSM instruction. This + function must evaluate the SMRAM save state to determine the execution mode + the RSM instruction resumes and update the resume execution address with + either NewInstructionPointer32 or NewInstructionPoint. The auto HALT restart + flag in the SMRAM save state must always be cleared. This function returns + the value of the instruction pointer from the SMRAM save state that was + replaced. If this function returns 0, then the SMRAM save state was not + modified. + + This function is called during the very first SMI on each CPU after + SmmCpuFeaturesInitializeProcessor() to set a flag in normal execution mode + to signal that the SMBASE of each CPU has been updated before the default + SMBASE address is used for the first SMI to the next CPU. + + @param[in] CpuIndex The index of the CPU to hook. The value + must be between 0 and the NumberOfCpus + field in the System Management System Table + (SMST). + @param[in] CpuState Pointer to SMRAM Save State Map for the + currently executing CPU. + @param[in] NewInstructionPointer32 Instruction pointer to use if resuming to + 32-bit execution mode from 64-bit SMM. + @param[in] NewInstructionPointer Instruction pointer to use if resuming to + same execution mode as SMM. + + @retval 0 This function did modify the SMRAM save state. + @retval > 0 The original instruction pointer value from the SMRAM save state + before it was replaced. +**/ +UINT64 +EFIAPI +SmmCpuFeaturesHookReturnFromSmm ( + IN UINTN CpuIndex, + IN SMRAM_SAVE_STATE_MAP *CpuState, + IN UINT64 NewInstructionPointer32, + IN UINT64 NewInstructionPointer + ) +{ + return 0; +} + +/** + Hook point in normal execution mode that allows the one CPU that was elected + as monarch during System Management Mode initialization to perform additional + initialization actions immediately after all of the CPUs have processed their + first SMI and called SmmCpuFeaturesInitializeProcessor() relocating SMBASE + into a buffer in SMRAM and called SmmCpuFeaturesHookReturnFromSmm(). +**/ +VOID +EFIAPI +SmmCpuFeaturesSmmRelocationComplete ( + VOID + ) +{ +} + +/** + Determines if MTRR registers must be configured to set SMRAM cache-ability + when executing in System Management Mode. + + @retval TRUE MTRR registers must be configured to set SMRAM cache-ability. + @retval FALSE MTRR registers do not need to be configured to set SMRAM + cache-ability. +**/ +BOOLEAN +EFIAPI +SmmCpuFeaturesNeedConfigureMtrrs ( + VOID + ) +{ + return mNeedConfigureMtrrs; +} + +/** + Disable SMRR register if SMRR is supported and SmmCpuFeaturesNeedConfigureMtrrs() + returns TRUE. +**/ +VOID +EFIAPI +SmmCpuFeaturesDisableSmrr ( + VOID + ) +{ + if (mSmrrSupported && mNeedConfigureMtrrs) { + AsmWriteMsr64 (mSmrrPhysMaskMsr, AsmReadMsr64(mSmrrPhysMaskMsr) & ~EFI_MSR_SMRR_PHYS_MASK_VALID); + } +} + +/** + Enable SMRR register if SMRR is supported and SmmCpuFeaturesNeedConfigureMtrrs() + returns TRUE. +**/ +VOID +EFIAPI +SmmCpuFeaturesReenableSmrr ( + VOID + ) +{ + if (mSmrrSupported && mNeedConfigureMtrrs) { + AsmWriteMsr64 (mSmrrPhysMaskMsr, AsmReadMsr64(mSmrrPhysMaskMsr) | EFI_MSR_SMRR_PHYS_MASK_VALID); + } +} + +/** + Processor specific hook point each time a CPU enters System Management Mode. + + @param[in] CpuIndex The index of the CPU that has entered SMM. The value + must be between 0 and the NumberOfCpus field in the + System Management System Table (SMST). +**/ +VOID +EFIAPI +SmmCpuFeaturesRendezvousEntry ( + IN UINTN CpuIndex + ) +{ + // + // If SMRR is supported and this is the first normal SMI, then enable SMRR + // + if (mSmrrSupported && !mSmrrEnabled[CpuIndex]) { + AsmWriteMsr64 (mSmrrPhysMaskMsr, AsmReadMsr64 (mSmrrPhysMaskMsr) | EFI_MSR_SMRR_PHYS_MASK_VALID); + mSmrrEnabled[CpuIndex] = TRUE; + } +} + +/** + Processor specific hook point each time a CPU exits System Management Mode. + + @param[in] CpuIndex The index of the CPU that is exiting SMM. The value must + be between 0 and the NumberOfCpus field in the System + Management System Table (SMST). +**/ +VOID +EFIAPI +SmmCpuFeaturesRendezvousExit ( + IN UINTN CpuIndex + ) +{ +} + +/** + Check to see if an SMM register is supported by a specified CPU. + + @param[in] CpuIndex The index of the CPU to check for SMM register support. + The value must be between 0 and the NumberOfCpus field + in the System Management System Table (SMST). + @param[in] RegName Identifies the SMM register to check for support. + + @retval TRUE The SMM register specified by RegName is supported by the CPU + specified by CpuIndex. + @retval FALSE The SMM register specified by RegName is not supported by the + CPU specified by CpuIndex. +**/ +BOOLEAN +EFIAPI +SmmCpuFeaturesIsSmmRegisterSupported ( + IN UINTN CpuIndex, + IN SMM_REG_NAME RegName + ) +{ + if (mSmmFeatureControlSupported && RegName == SmmRegFeatureControl) { + return TRUE; + } + return FALSE; +} + +/** + Returns the current value of the SMM register for the specified CPU. + If the SMM register is not supported, then 0 is returned. + + @param[in] CpuIndex The index of the CPU to read the SMM register. The + value must be between 0 and the NumberOfCpus field in + the System Management System Table (SMST). + @param[in] RegName Identifies the SMM register to read. + + @return The value of the SMM register specified by RegName from the CPU + specified by CpuIndex. +**/ +UINT64 +EFIAPI +SmmCpuFeaturesGetSmmRegister ( + IN UINTN CpuIndex, + IN SMM_REG_NAME RegName + ) +{ + if (mSmmFeatureControlSupported && RegName == SmmRegFeatureControl) { + return AsmReadMsr64 (SMM_FEATURES_LIB_SMM_FEATURE_CONTROL); + } + return 0; +} + +/** + Sets the value of an SMM register on a specified CPU. + If the SMM register is not supported, then no action is performed. + + @param[in] CpuIndex The index of the CPU to write the SMM register. The + value must be between 0 and the NumberOfCpus field in + the System Management System Table (SMST). + @param[in] RegName Identifies the SMM register to write. + registers are read-only. + @param[in] Value The value to write to the SMM register. +**/ +VOID +EFIAPI +SmmCpuFeaturesSetSmmRegister ( + IN UINTN CpuIndex, + IN SMM_REG_NAME RegName, + IN UINT64 Value + ) +{ + if (mSmmFeatureControlSupported && RegName == SmmRegFeatureControl) { + AsmWriteMsr64 (SMM_FEATURES_LIB_SMM_FEATURE_CONTROL, Value); + } +} + +/** + Read an SMM Save State register on the target processor. If this function + returns EFI_UNSUPPORTED, then the caller is responsible for reading the + SMM Save Sate register. + + @param[in] CpuIndex The index of the CPU to read the SMM Save State. The + value must be between 0 and the NumberOfCpus field in + the System Management System Table (SMST). + @param[in] Register The SMM Save State register to read. + @param[in] Width The number of bytes to read from the CPU save state. + @param[out] Buffer Upon return, this holds the CPU register value read + from the save state. + + @retval EFI_SUCCESS The register was read from Save State. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED This function does not support reading Register. + +**/ +EFI_STATUS +EFIAPI +SmmCpuFeaturesReadSaveStateRegister ( + IN UINTN CpuIndex, + IN EFI_SMM_SAVE_STATE_REGISTER Register, + IN UINTN Width, + OUT VOID *Buffer + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Writes an SMM Save State register on the target processor. If this function + returns EFI_UNSUPPORTED, then the caller is responsible for writing the + SMM Save Sate register. + + @param[in] CpuIndex The index of the CPU to write the SMM Save State. The + value must be between 0 and the NumberOfCpus field in + the System Management System Table (SMST). + @param[in] Register The SMM Save State register to write. + @param[in] Width The number of bytes to write to the CPU save state. + @param[in] Buffer Upon entry, this holds the new CPU register value. + + @retval EFI_SUCCESS The register was written to Save State. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED This function does not support writing Register. +**/ +EFI_STATUS +EFIAPI +SmmCpuFeaturesWriteSaveStateRegister ( + IN UINTN CpuIndex, + IN EFI_SMM_SAVE_STATE_REGISTER Register, + IN UINTN Width, + IN CONST VOID *Buffer + ) +{ + return EFI_UNSUPPORTED; +} + +/** + This function is hook point called after the gEfiSmmReadyToLockProtocolGuid + notification is completely processed. +**/ +VOID +EFIAPI +SmmCpuFeaturesCompleteSmmReadyToLock ( + VOID + ) +{ +} + +/** + This API provides a method for a CPU to allocate a specific region for storing page tables. + + This API can be called more once to allocate memory for page tables. + + Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the + allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL + is returned. If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + This function can also return NULL if there is no preference on where the page tables are allocated in SMRAM. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer for page tables. + @retval NULL Fail to allocate a specific region for storing page tables, + Or there is no preference on where the page tables are allocated in SMRAM. + +**/ +VOID * +EFIAPI +SmmCpuFeaturesAllocatePageTableMemory ( + IN UINTN Pages + ) +{ + return NULL; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLibNoStm.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLibNoStm.c new file mode 100644 index 00000000..6baa6030 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLibNoStm.c @@ -0,0 +1,84 @@ +/** @file +The CPU specific programming for PiSmmCpuDxeSmm module when STM support +is not included. + +Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiMm.h> +#include <Library/SmmCpuFeaturesLib.h> +#include "CpuFeaturesLib.h" + +/** + Internal worker function that is called to complete CPU initialization at the + end of SmmCpuFeaturesInitializeProcessor(). + +**/ +VOID +FinishSmmCpuFeaturesInitializeProcessor ( + VOID + ) +{ +} + +/** + Return the size, in bytes, of a custom SMI Handler in bytes. If 0 is + returned, then a custom SMI handler is not provided by this library, + and the default SMI handler must be used. + + @retval 0 Use the default SMI handler. + @retval > 0 Use the SMI handler installed by SmmCpuFeaturesInstallSmiHandler() + The caller is required to allocate enough SMRAM for each CPU to + support the size of the custom SMI handler. +**/ +UINTN +EFIAPI +SmmCpuFeaturesGetSmiHandlerSize ( + VOID + ) +{ + return 0; +} + +/** + Install a custom SMI handler for the CPU specified by CpuIndex. This function + is only called if SmmCpuFeaturesGetSmiHandlerSize() returns a size is greater + than zero and is called by the CPU that was elected as monarch during System + Management Mode initialization. + + @param[in] CpuIndex The index of the CPU to install the custom SMI handler. + The value must be between 0 and the NumberOfCpus field + in the System Management System Table (SMST). + @param[in] SmBase The SMBASE address for the CPU specified by CpuIndex. + @param[in] SmiStack The stack to use when an SMI is processed by the + the CPU specified by CpuIndex. + @param[in] StackSize The size, in bytes, if the stack used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] GdtBase The base address of the GDT to use when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] GdtSize The size, in bytes, of the GDT used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] IdtBase The base address of the IDT to use when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] IdtSize The size, in bytes, of the IDT used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] Cr3 The base address of the page tables to use when an SMI + is processed by the CPU specified by CpuIndex. +**/ +VOID +EFIAPI +SmmCpuFeaturesInstallSmiHandler ( + IN UINTN CpuIndex, + IN UINT32 SmBase, + IN VOID *SmiStack, + IN UINTN StackSize, + IN UINTN GdtBase, + IN UINTN GdtSize, + IN UINTN IdtBase, + IN UINTN IdtSize, + IN UINT32 Cr3 + ) +{ +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLibStm.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLibStm.inf new file mode 100644 index 00000000..6583b3bd --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLibStm.inf @@ -0,0 +1,74 @@ +## @file +# The CPU specific programming for PiSmmCpuDxeSmm module when STM support +# is included. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SmmCpuFeaturesLibStm + MODULE_UNI_FILE = SmmCpuFeaturesLib.uni + FILE_GUID = 374DE830-81C5-4CC8-B2AB-28F0AB73710B + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = SmmCpuFeaturesLib + CONSTRUCTOR = SmmCpuFeaturesLibStmConstructor + +[Sources] + CpuFeaturesLib.h + SmmCpuFeaturesLibCommon.c + SmmStm.c + SmmStm.h + TraditionalMmCpuFeaturesLib.c + +[Sources.Ia32] + Ia32/SmmStmSupport.c + + + Ia32/SmiEntry.nasm + Ia32/SmiException.nasm + +[Sources.X64] + X64/SmmStmSupport.c + + + X64/SmiEntry.nasm + X64/SmiException.nasm + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + PcdLib + HobLib + MemoryAllocationLib + DebugLib + UefiBootServicesTableLib + SmmServicesTableLib + TpmMeasurementLib + +[Protocols] + gEfiMpServiceProtocolGuid ## CONSUMES + gEfiSmmEndOfDxeProtocolGuid ## CONSUMES + gEfiSmMonitorInitProtocolGuid ## PRODUCES + +[Guids] + gMsegSmramGuid ## SOMETIMES_CONSUMES ## HOB + gEfiAcpi20TableGuid ## SOMETIMES_CONSUMES ## SystemTable + gEfiAcpi10TableGuid ## SOMETIMES_CONSUMES ## SystemTable + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuMsegSize ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStmExceptionStackSize ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStackGuard ## CONSUMES + +[Depex] + gEfiMpServiceProtocolGuid diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c new file mode 100644 index 00000000..6ae8fb92 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.c @@ -0,0 +1,1281 @@ +/** @file + SMM STM support functions + + Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiMm.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/HobLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/SmmServicesTableLib.h> +#include <Library/TpmMeasurementLib.h> +#include <Register/Intel/Cpuid.h> +#include <Register/Intel/ArchitecturalMsr.h> +#include <Register/Intel/SmramSaveStateMap.h> + +#include <Protocol/MpService.h> + +#include "CpuFeaturesLib.h" +#include "SmmStm.h" + +#define TXT_EVTYPE_BASE 0x400 +#define TXT_EVTYPE_STM_HASH (TXT_EVTYPE_BASE + 14) + +#define RDWR_ACCS 3 +#define FULL_ACCS 7 + +EFI_HANDLE mStmSmmCpuHandle = NULL; + +BOOLEAN mLockLoadMonitor = FALSE; + +// +// Template of STM_RSC_END structure for copying. +// +GLOBAL_REMOVE_IF_UNREFERENCED STM_RSC_END mRscEndNode = { + {END_OF_RESOURCES, sizeof (STM_RSC_END)}, +}; + +GLOBAL_REMOVE_IF_UNREFERENCED UINT8 *mStmResourcesPtr = NULL; +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mStmResourceTotalSize = 0x0; +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mStmResourceSizeUsed = 0x0; +GLOBAL_REMOVE_IF_UNREFERENCED UINTN mStmResourceSizeAvailable = 0x0; + +GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mStmState = 0; + +// +// System Configuration Table pointing to STM Configuration Table +// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_SM_MONITOR_INIT_PROTOCOL mSmMonitorInitProtocol = { + LoadMonitor, + AddPiResource, + DeletePiResource, + GetPiResource, + GetMonitorState, +}; + + + + +#define CPUID1_EDX_XD_SUPPORT 0x100000 + +// +// External global variables associated with SMI Handler Template +// +extern CONST TXT_PROCESSOR_SMM_DESCRIPTOR gcStmPsd; +extern UINT32 gStmSmbase; +extern volatile UINT32 gStmSmiStack; +extern UINT32 gStmSmiCr3; +extern volatile UINT8 gcStmSmiHandlerTemplate[]; +extern CONST UINT16 gcStmSmiHandlerSize; +extern UINT16 gcStmSmiHandlerOffset; +extern BOOLEAN gStmXdSupported; + +// +// Variables used by SMI Handler +// +IA32_DESCRIPTOR gStmSmiHandlerIdtr; + +// +// MP Services Protocol +// +EFI_MP_SERVICES_PROTOCOL *mSmmCpuFeaturesLibMpService = NULL; + +// +// MSEG Base and Length in SMRAM +// +UINTN mMsegBase = 0; +UINTN mMsegSize = 0; + +BOOLEAN mStmConfigurationTableInitialized = FALSE; + +/** + The constructor function for the Traditional MM library instance with STM. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. + +**/ +EFI_STATUS +EFIAPI +SmmCpuFeaturesLibStmConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + CPUID_VERSION_INFO_ECX RegEcx; + EFI_HOB_GUID_TYPE *GuidHob; + EFI_SMRAM_DESCRIPTOR *SmramDescriptor; + + // + // Initialize address fixup + // + SmmCpuFeaturesLibStmSmiEntryFixupAddress (); + + // + // Perform library initialization common across all instances + // + CpuFeaturesLibInitialization (); + + // + // Lookup the MP Services Protocol + // + Status = gBS->LocateProtocol ( + &gEfiMpServiceProtocolGuid, + NULL, + (VOID **)&mSmmCpuFeaturesLibMpService + ); + ASSERT_EFI_ERROR (Status); + + // + // If CPU supports VMX, then determine SMRAM range for MSEG. + // + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &RegEcx.Uint32, NULL); + if (RegEcx.Bits.VMX == 1) { + GuidHob = GetFirstGuidHob (&gMsegSmramGuid); + if (GuidHob != NULL) { + // + // Retrieve MSEG location from MSEG SRAM HOB + // + SmramDescriptor = (EFI_SMRAM_DESCRIPTOR *) GET_GUID_HOB_DATA (GuidHob); + if (SmramDescriptor->PhysicalSize > 0) { + mMsegBase = (UINTN)SmramDescriptor->CpuStart; + mMsegSize = (UINTN)SmramDescriptor->PhysicalSize; + } + } else if (PcdGet32 (PcdCpuMsegSize) > 0) { + // + // Allocate MSEG from SMRAM memory + // + mMsegBase = (UINTN)AllocatePages (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuMsegSize))); + if (mMsegBase > 0) { + mMsegSize = ALIGN_VALUE (PcdGet32 (PcdCpuMsegSize), EFI_PAGE_SIZE); + } else { + DEBUG ((DEBUG_ERROR, "Not enough SMRAM resource to allocate MSEG size %08x\n", PcdGet32 (PcdCpuMsegSize))); + } + } + if (mMsegBase > 0) { + DEBUG ((DEBUG_INFO, "MsegBase: 0x%08x, MsegSize: 0x%08x\n", mMsegBase, mMsegSize)); + } + } + + return EFI_SUCCESS; +} + +/** + Internal worker function that is called to complete CPU initialization at the + end of SmmCpuFeaturesInitializeProcessor(). + +**/ +VOID +FinishSmmCpuFeaturesInitializeProcessor ( + VOID + ) +{ + MSR_IA32_SMM_MONITOR_CTL_REGISTER SmmMonitorCtl; + + // + // Set MSEG Base Address in SMM Monitor Control MSR. + // + if (mMsegBase > 0) { + SmmMonitorCtl.Uint64 = 0; + SmmMonitorCtl.Bits.MsegBase = (UINT32)mMsegBase >> 12; + SmmMonitorCtl.Bits.Valid = 1; + AsmWriteMsr64 (MSR_IA32_SMM_MONITOR_CTL, SmmMonitorCtl.Uint64); + } +} + +/** + Return the size, in bytes, of a custom SMI Handler in bytes. If 0 is + returned, then a custom SMI handler is not provided by this library, + and the default SMI handler must be used. + + @retval 0 Use the default SMI handler. + @retval > 0 Use the SMI handler installed by SmmCpuFeaturesInstallSmiHandler() + The caller is required to allocate enough SMRAM for each CPU to + support the size of the custom SMI handler. +**/ +UINTN +EFIAPI +SmmCpuFeaturesGetSmiHandlerSize ( + VOID + ) +{ + return gcStmSmiHandlerSize; +} + +/** + Install a custom SMI handler for the CPU specified by CpuIndex. This function + is only called if SmmCpuFeaturesGetSmiHandlerSize() returns a size is greater + than zero and is called by the CPU that was elected as monarch during System + Management Mode initialization. + + @param[in] CpuIndex The index of the CPU to install the custom SMI handler. + The value must be between 0 and the NumberOfCpus field + in the System Management System Table (SMST). + @param[in] SmBase The SMBASE address for the CPU specified by CpuIndex. + @param[in] SmiStack The stack to use when an SMI is processed by the + the CPU specified by CpuIndex. + @param[in] StackSize The size, in bytes, if the stack used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] GdtBase The base address of the GDT to use when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] GdtSize The size, in bytes, of the GDT used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] IdtBase The base address of the IDT to use when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] IdtSize The size, in bytes, of the IDT used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] Cr3 The base address of the page tables to use when an SMI + is processed by the CPU specified by CpuIndex. +**/ +VOID +EFIAPI +SmmCpuFeaturesInstallSmiHandler ( + IN UINTN CpuIndex, + IN UINT32 SmBase, + IN VOID *SmiStack, + IN UINTN StackSize, + IN UINTN GdtBase, + IN UINTN GdtSize, + IN UINTN IdtBase, + IN UINTN IdtSize, + IN UINT32 Cr3 + ) +{ + EFI_STATUS Status; + TXT_PROCESSOR_SMM_DESCRIPTOR *Psd; + VOID *Hob; + UINT32 RegEax; + UINT32 RegEdx; + EFI_PROCESSOR_INFORMATION ProcessorInfo; + + CopyMem ((VOID *)((UINTN)SmBase + TXT_SMM_PSD_OFFSET), &gcStmPsd, sizeof (gcStmPsd)); + Psd = (TXT_PROCESSOR_SMM_DESCRIPTOR *)(VOID *)((UINTN)SmBase + TXT_SMM_PSD_OFFSET); + Psd->SmmGdtPtr = GdtBase; + Psd->SmmGdtSize = (UINT32)GdtSize; + + // + // Initialize values in template before copy + // + gStmSmiStack = (UINT32)((UINTN)SmiStack + StackSize - sizeof (UINTN)); + gStmSmiCr3 = Cr3; + gStmSmbase = SmBase; + gStmSmiHandlerIdtr.Base = IdtBase; + gStmSmiHandlerIdtr.Limit = (UINT16)(IdtSize - 1); + + if (gStmXdSupported) { + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax <= CPUID_EXTENDED_FUNCTION) { + // + // Extended CPUID functions are not supported on this processor. + // + gStmXdSupported = FALSE; + } + + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & CPUID1_EDX_XD_SUPPORT) == 0) { + // + // Execute Disable Bit feature is not supported on this processor. + // + gStmXdSupported = FALSE; + } + } + + // + // Set the value at the top of the CPU stack to the CPU Index + // + *(UINTN*)(UINTN)gStmSmiStack = CpuIndex; + + // + // Copy template to CPU specific SMI handler location + // + CopyMem ( + (VOID*)((UINTN)SmBase + SMM_HANDLER_OFFSET), + (VOID*)gcStmSmiHandlerTemplate, + gcStmSmiHandlerSize + ); + + Psd->SmmSmiHandlerRip = SmBase + SMM_HANDLER_OFFSET + gcStmSmiHandlerOffset; + Psd->SmmSmiHandlerRsp = (UINTN)SmiStack + StackSize - sizeof(UINTN); + Psd->SmmCr3 = Cr3; + + DEBUG((DEBUG_INFO, "CpuSmmStmExceptionStackSize - %x\n", PcdGet32(PcdCpuSmmStmExceptionStackSize))); + DEBUG((DEBUG_INFO, "Pages - %x\n", EFI_SIZE_TO_PAGES(PcdGet32(PcdCpuSmmStmExceptionStackSize)))); + Psd->StmProtectionExceptionHandler.SpeRsp = (UINT64)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStmExceptionStackSize))); + Psd->StmProtectionExceptionHandler.SpeRsp += EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStmExceptionStackSize))); + + Psd->BiosHwResourceRequirementsPtr = (UINT64)(UINTN)GetStmResource (); + + // + // Get the APIC ID for the CPU specified by CpuIndex + // + Status = mSmmCpuFeaturesLibMpService->GetProcessorInfo ( + mSmmCpuFeaturesLibMpService, + CpuIndex, + &ProcessorInfo + ); + ASSERT_EFI_ERROR (Status); + + Psd->LocalApicId = (UINT32)ProcessorInfo.ProcessorId; + Psd->AcpiRsdp = 0; + + Hob = GetFirstHob (EFI_HOB_TYPE_CPU); + if (Hob != NULL) { + Psd->PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; + } else { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000008) { + AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); + Psd->PhysicalAddressBits = (UINT8) RegEax; + } else { + Psd->PhysicalAddressBits = 36; + } + } + + if (!mStmConfigurationTableInitialized) { + StmSmmConfigurationTableInit (); + mStmConfigurationTableInitialized = TRUE; + } +} + +/** + SMM End Of Dxe event notification handler. + + STM support need patch AcpiRsdp in TXT_PROCESSOR_SMM_DESCRIPTOR. + + @param[in] Protocol Points to the protocol's unique identifier. + @param[in] Interface Points to the interface instance. + @param[in] Handle The handle on which the interface was installed. + + @retval EFI_SUCCESS Notification handler runs successfully. +**/ +EFI_STATUS +EFIAPI +SmmEndOfDxeEventNotify ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + VOID *Rsdp; + UINTN Index; + TXT_PROCESSOR_SMM_DESCRIPTOR *Psd; + + DEBUG ((DEBUG_INFO, "SmmEndOfDxeEventNotify\n")); + + // + // found ACPI table RSD_PTR from system table + // + Rsdp = NULL; + for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { + if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi20TableGuid)) { + // + // A match was found. + // + Rsdp = gST->ConfigurationTable[Index].VendorTable; + break; + } + } + if (Rsdp == NULL) { + for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { + if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi10TableGuid)) { + // + // A match was found. + // + Rsdp = gST->ConfigurationTable[Index].VendorTable; + break; + } + } + } + + for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { + Psd = (TXT_PROCESSOR_SMM_DESCRIPTOR *)((UINTN)gSmst->CpuSaveState[Index] - SMRAM_SAVE_STATE_MAP_OFFSET + TXT_SMM_PSD_OFFSET); + DEBUG ((DEBUG_INFO, "Index=%d Psd=%p Rsdp=%p\n", Index, Psd, Rsdp)); + Psd->AcpiRsdp = (UINT64)(UINTN)Rsdp; + } + + mLockLoadMonitor = TRUE; + + return EFI_SUCCESS; +} + +/** + This function initializes the STM configuration table. +**/ +VOID +StmSmmConfigurationTableInit ( + VOID + ) +{ + EFI_STATUS Status; + VOID *Registration; + + Status = gSmst->SmmInstallProtocolInterface ( + &mStmSmmCpuHandle, + &gEfiSmMonitorInitProtocolGuid, + EFI_NATIVE_INTERFACE, + &mSmMonitorInitProtocol + ); + ASSERT_EFI_ERROR (Status); + + // + // + // Register SMM End of DXE Event + // + Status = gSmst->SmmRegisterProtocolNotify ( + &gEfiSmmEndOfDxeProtocolGuid, + SmmEndOfDxeEventNotify, + &Registration + ); + ASSERT_EFI_ERROR (Status); +} + +/** + + Get STM state. + + @return STM state + +**/ +EFI_SM_MONITOR_STATE +EFIAPI +GetMonitorState ( + VOID + ) +{ + return mStmState; +} + +/** + + Handle single Resource to see if it can be merged into Record. + + @param Resource A pointer to resource node to be added + @param Record A pointer to record node to be merged + + @retval TRUE resource handled + @retval FALSE resource is not handled + +**/ +BOOLEAN +HandleSingleResource ( + IN STM_RSC *Resource, + IN STM_RSC *Record + ) +{ + UINT64 ResourceLo; + UINT64 ResourceHi; + UINT64 RecordLo; + UINT64 RecordHi; + + ResourceLo = 0; + ResourceHi = 0; + RecordLo = 0; + RecordHi = 0; + + // + // Calling code is responsible for making sure that + // Resource->Header.RscType == (*Record)->Header.RscType + // thus we use just one of them as switch variable. + // + switch (Resource->Header.RscType) { + case MEM_RANGE: + case MMIO_RANGE: + ResourceLo = Resource->Mem.Base; + ResourceHi = Resource->Mem.Base + Resource->Mem.Length; + RecordLo = Record->Mem.Base; + RecordHi = Record->Mem.Base + Record->Mem.Length; + if (Resource->Mem.RWXAttributes != Record->Mem.RWXAttributes) { + if ((ResourceLo == RecordLo) && (ResourceHi == RecordHi)) { + Record->Mem.RWXAttributes = Resource->Mem.RWXAttributes | Record->Mem.RWXAttributes; + return TRUE; + } else { + return FALSE; + } + } + break; + case IO_RANGE: + case TRAPPED_IO_RANGE: + ResourceLo = (UINT64) Resource->Io.Base; + ResourceHi = (UINT64) Resource->Io.Base + (UINT64) Resource->Io.Length; + RecordLo = (UINT64) Record->Io.Base; + RecordHi = (UINT64) Record->Io.Base + (UINT64) Record->Io.Length; + break; + case PCI_CFG_RANGE: + if ((Resource->PciCfg.OriginatingBusNumber != Record->PciCfg.OriginatingBusNumber) || + (Resource->PciCfg.LastNodeIndex != Record->PciCfg.LastNodeIndex)) { + return FALSE; + } + if (CompareMem (Resource->PciCfg.PciDevicePath, Record->PciCfg.PciDevicePath, sizeof(STM_PCI_DEVICE_PATH_NODE) * (Resource->PciCfg.LastNodeIndex + 1)) != 0) { + return FALSE; + } + ResourceLo = (UINT64) Resource->PciCfg.Base; + ResourceHi = (UINT64) Resource->PciCfg.Base + (UINT64) Resource->PciCfg.Length; + RecordLo = (UINT64) Record->PciCfg.Base; + RecordHi = (UINT64) Record->PciCfg.Base + (UINT64) Record->PciCfg.Length; + if (Resource->PciCfg.RWAttributes != Record->PciCfg.RWAttributes) { + if ((ResourceLo == RecordLo) && (ResourceHi == RecordHi)) { + Record->PciCfg.RWAttributes = Resource->PciCfg.RWAttributes | Record->PciCfg.RWAttributes; + return TRUE; + } else { + return FALSE; + } + } + break; + case MACHINE_SPECIFIC_REG: + // + // Special case - merge MSR masks in place. + // + if (Resource->Msr.MsrIndex != Record->Msr.MsrIndex) { + return FALSE; + } + Record->Msr.ReadMask |= Resource->Msr.ReadMask; + Record->Msr.WriteMask |= Resource->Msr.WriteMask; + return TRUE; + default: + return FALSE; + } + // + // If resources are disjoint + // + if ((ResourceHi < RecordLo) || (ResourceLo > RecordHi)) { + return FALSE; + } + + // + // If resource is consumed by record. + // + if ((ResourceLo >= RecordLo) && (ResourceHi <= RecordHi)) { + return TRUE; + } + // + // Resources are overlapping. + // Resource and record are merged. + // + ResourceLo = (ResourceLo < RecordLo) ? ResourceLo : RecordLo; + ResourceHi = (ResourceHi > RecordHi) ? ResourceHi : RecordHi; + + switch (Resource->Header.RscType) { + case MEM_RANGE: + case MMIO_RANGE: + Record->Mem.Base = ResourceLo; + Record->Mem.Length = ResourceHi - ResourceLo; + break; + case IO_RANGE: + case TRAPPED_IO_RANGE: + Record->Io.Base = (UINT16) ResourceLo; + Record->Io.Length = (UINT16) (ResourceHi - ResourceLo); + break; + case PCI_CFG_RANGE: + Record->PciCfg.Base = (UINT16) ResourceLo; + Record->PciCfg.Length = (UINT16) (ResourceHi - ResourceLo); + break; + default: + return FALSE; + } + + return TRUE; +} + +/** + + Add resource node. + + @param Resource A pointer to resource node to be added + +**/ +VOID +AddSingleResource ( + IN STM_RSC *Resource + ) +{ + STM_RSC *Record; + + Record = (STM_RSC *)mStmResourcesPtr; + + while (TRUE) { + if (Record->Header.RscType == END_OF_RESOURCES) { + break; + } + // + // Go to next record if resource and record types don't match. + // + if (Resource->Header.RscType != Record->Header.RscType) { + Record = (STM_RSC *)((UINTN)Record + Record->Header.Length); + continue; + } + // + // Record is handled inside of procedure - don't adjust. + // + if (HandleSingleResource (Resource, Record)) { + return ; + } + Record = (STM_RSC *)((UINTN)Record + Record->Header.Length); + } + + // + // Add resource to the end of area. + // + CopyMem ( + mStmResourcesPtr + mStmResourceSizeUsed - sizeof(mRscEndNode), + Resource, + Resource->Header.Length + ); + CopyMem ( + mStmResourcesPtr + mStmResourceSizeUsed - sizeof(mRscEndNode) + Resource->Header.Length, + &mRscEndNode, + sizeof(mRscEndNode) + ); + mStmResourceSizeUsed += Resource->Header.Length; + mStmResourceSizeAvailable = mStmResourceTotalSize - mStmResourceSizeUsed; + + return ; +} + +/** + + Add resource list. + + @param ResourceList A pointer to resource list to be added + @param NumEntries Optional number of entries. + If 0, list must be terminated by END_OF_RESOURCES. + +**/ +VOID +AddResource ( + IN STM_RSC *ResourceList, + IN UINT32 NumEntries OPTIONAL + ) +{ + UINT32 Count; + UINTN Index; + STM_RSC *Resource; + + if (NumEntries == 0) { + Count = 0xFFFFFFFF; + } else { + Count = NumEntries; + } + + Resource = ResourceList; + + for (Index = 0; Index < Count; Index++) { + if (Resource->Header.RscType == END_OF_RESOURCES) { + return ; + } + AddSingleResource (Resource); + Resource = (STM_RSC *)((UINTN)Resource + Resource->Header.Length); + } + return ; +} + +/** + + Validate resource list. + + @param ResourceList A pointer to resource list to be added + @param NumEntries Optional number of entries. + If 0, list must be terminated by END_OF_RESOURCES. + + @retval TRUE resource valid + @retval FALSE resource invalid + +**/ +BOOLEAN +ValidateResource ( + IN STM_RSC *ResourceList, + IN UINT32 NumEntries OPTIONAL + ) +{ + UINT32 Count; + UINTN Index; + STM_RSC *Resource; + UINTN SubIndex; + + // + // If NumEntries == 0 make it very big. Scan will be terminated by + // END_OF_RESOURCES. + // + if (NumEntries == 0) { + Count = 0xFFFFFFFF; + } else { + Count = NumEntries; + } + + // + // Start from beginning of resource list. + // + Resource = ResourceList; + + for (Index = 0; Index < Count; Index++) { + DEBUG ((DEBUG_INFO, "ValidateResource (%d) - RscType(%x)\n", Index, Resource->Header.RscType)); + // + // Validate resource. + // + switch (Resource->Header.RscType) { + case END_OF_RESOURCES: + if (Resource->Header.Length != sizeof (STM_RSC_END)) { + return FALSE; + } + // + // If we are passed actual number of resources to add, + // END_OF_RESOURCES structure between them is considered an + // error. If NumEntries == 0 END_OF_RESOURCES is a termination. + // + if (NumEntries != 0) { + return FALSE; + } else { + // + // If NumEntries == 0 and list reached end - return success. + // + return TRUE; + } + break; + + case MEM_RANGE: + case MMIO_RANGE: + if (Resource->Header.Length != sizeof (STM_RSC_MEM_DESC)) { + return FALSE; + } + + if (Resource->Mem.RWXAttributes > FULL_ACCS) { + return FALSE; + } + break; + + case IO_RANGE: + case TRAPPED_IO_RANGE: + if (Resource->Header.Length != sizeof (STM_RSC_IO_DESC)) { + return FALSE; + } + + if ((Resource->Io.Base + Resource->Io.Length) > 0xFFFF) { + return FALSE; + } + break; + + case PCI_CFG_RANGE: + DEBUG ((DEBUG_INFO, "ValidateResource - PCI (0x%02x, 0x%08x, 0x%02x, 0x%02x)\n", Resource->PciCfg.OriginatingBusNumber, Resource->PciCfg.LastNodeIndex, Resource->PciCfg.PciDevicePath[0].PciDevice, Resource->PciCfg.PciDevicePath[0].PciFunction)); + if (Resource->Header.Length != sizeof (STM_RSC_PCI_CFG_DESC) + (sizeof(STM_PCI_DEVICE_PATH_NODE) * Resource->PciCfg.LastNodeIndex)) { + return FALSE; + } + for (SubIndex = 0; SubIndex <= Resource->PciCfg.LastNodeIndex; SubIndex++) { + if ((Resource->PciCfg.PciDevicePath[SubIndex].PciDevice > 0x1F) || (Resource->PciCfg.PciDevicePath[SubIndex].PciFunction > 7)) { + return FALSE; + } + } + if ((Resource->PciCfg.Base + Resource->PciCfg.Length) > 0x1000) { + return FALSE; + } + break; + + case MACHINE_SPECIFIC_REG: + if (Resource->Header.Length != sizeof (STM_RSC_MSR_DESC)) { + return FALSE; + } + break; + + default : + DEBUG ((DEBUG_ERROR, "ValidateResource - Unknown RscType(%x)\n", Resource->Header.RscType)); + return FALSE; + } + Resource = (STM_RSC *)((UINTN)Resource + Resource->Header.Length); + } + return TRUE; +} + +/** + + Get resource list. + EndResource is excluded. + + @param ResourceList A pointer to resource list to be added + @param NumEntries Optional number of entries. + If 0, list must be terminated by END_OF_RESOURCES. + + @retval TRUE resource valid + @retval FALSE resource invalid + +**/ +UINTN +GetResourceSize ( + IN STM_RSC *ResourceList, + IN UINT32 NumEntries OPTIONAL + ) +{ + UINT32 Count; + UINTN Index; + STM_RSC *Resource; + + Resource = ResourceList; + + // + // If NumEntries == 0 make it very big. Scan will be terminated by + // END_OF_RESOURCES. + // + if (NumEntries == 0) { + Count = 0xFFFFFFFF; + } else { + Count = NumEntries; + } + + // + // Start from beginning of resource list. + // + Resource = ResourceList; + + for (Index = 0; Index < Count; Index++) { + if (Resource->Header.RscType == END_OF_RESOURCES) { + break; + } + Resource = (STM_RSC *)((UINTN)Resource + Resource->Header.Length); + } + + return (UINTN)Resource - (UINTN)ResourceList; +} + +/** + + Add resources in list to database. Allocate new memory areas as needed. + + @param ResourceList A pointer to resource list to be added + @param NumEntries Optional number of entries. + If 0, list must be terminated by END_OF_RESOURCES. + + @retval EFI_SUCCESS If resources are added + @retval EFI_INVALID_PARAMETER If nested procedure detected resource failer + @retval EFI_OUT_OF_RESOURCES If nested procedure returned it and we cannot allocate more areas. + +**/ +EFI_STATUS +EFIAPI +AddPiResource ( + IN STM_RSC *ResourceList, + IN UINT32 NumEntries OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN ResourceSize; + EFI_PHYSICAL_ADDRESS NewResource; + UINTN NewResourceSize; + + DEBUG ((DEBUG_INFO, "AddPiResource - Enter\n")); + + if (!ValidateResource (ResourceList, NumEntries)) { + return EFI_INVALID_PARAMETER; + } + + ResourceSize = GetResourceSize (ResourceList, NumEntries); + DEBUG ((DEBUG_INFO, "ResourceSize - 0x%08x\n", ResourceSize)); + if (ResourceSize == 0) { + return EFI_INVALID_PARAMETER; + } + + if (mStmResourcesPtr == NULL) { + // + // First time allocation + // + NewResourceSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (ResourceSize + sizeof(mRscEndNode))); + DEBUG ((DEBUG_INFO, "Allocate - 0x%08x\n", NewResourceSize)); + Status = gSmst->SmmAllocatePages ( + AllocateAnyPages, + EfiRuntimeServicesData, + EFI_SIZE_TO_PAGES (NewResourceSize), + &NewResource + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Copy EndResource for initialization + // + mStmResourcesPtr = (UINT8 *)(UINTN)NewResource; + mStmResourceTotalSize = NewResourceSize; + CopyMem (mStmResourcesPtr, &mRscEndNode, sizeof(mRscEndNode)); + mStmResourceSizeUsed = sizeof(mRscEndNode); + mStmResourceSizeAvailable = mStmResourceTotalSize - sizeof(mRscEndNode); + + // + // Let SmmCore change resource ptr + // + NotifyStmResourceChange (mStmResourcesPtr); + } else if (mStmResourceSizeAvailable < ResourceSize) { + // + // Need enlarge + // + NewResourceSize = mStmResourceTotalSize + (ResourceSize - mStmResourceSizeAvailable); + NewResourceSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (NewResourceSize)); + DEBUG ((DEBUG_INFO, "ReAllocate - 0x%08x\n", NewResourceSize)); + Status = gSmst->SmmAllocatePages ( + AllocateAnyPages, + EfiRuntimeServicesData, + EFI_SIZE_TO_PAGES (NewResourceSize), + &NewResource + ); + if (EFI_ERROR (Status)) { + return Status; + } + CopyMem ((VOID *)(UINTN)NewResource, mStmResourcesPtr, mStmResourceSizeUsed); + mStmResourceSizeAvailable = NewResourceSize - mStmResourceSizeUsed; + + gSmst->SmmFreePages ( + (EFI_PHYSICAL_ADDRESS)(UINTN)mStmResourcesPtr, + EFI_SIZE_TO_PAGES (mStmResourceTotalSize) + ); + + mStmResourceTotalSize = NewResourceSize; + mStmResourcesPtr = (UINT8 *)(UINTN)NewResource; + + // + // Let SmmCore change resource ptr + // + NotifyStmResourceChange (mStmResourcesPtr); + } + + // + // Check duplication + // + AddResource (ResourceList, NumEntries); + + return EFI_SUCCESS; +} + +/** + + Delete resources in list to database. + + @param ResourceList A pointer to resource list to be deleted + NULL means delete all resources. + @param NumEntries Optional number of entries. + If 0, list must be terminated by END_OF_RESOURCES. + + @retval EFI_SUCCESS If resources are deleted + @retval EFI_INVALID_PARAMETER If nested procedure detected resource failer + +**/ +EFI_STATUS +EFIAPI +DeletePiResource ( + IN STM_RSC *ResourceList, + IN UINT32 NumEntries OPTIONAL + ) +{ + if (ResourceList != NULL) { + // TBD + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + // + // Delete all + // + CopyMem (mStmResourcesPtr, &mRscEndNode, sizeof(mRscEndNode)); + mStmResourceSizeUsed = sizeof(mRscEndNode); + mStmResourceSizeAvailable = mStmResourceTotalSize - sizeof(mRscEndNode); + return EFI_SUCCESS; +} + +/** + + Get BIOS resources. + + @param ResourceList A pointer to resource list to be filled + @param ResourceSize On input it means size of resource list input. + On output it means size of resource list filled, + or the size of resource list to be filled if size of too small. + + @retval EFI_SUCCESS If resources are returned. + @retval EFI_BUFFER_TOO_SMALL If resource list buffer is too small to hold the whole resources. + +**/ +EFI_STATUS +EFIAPI +GetPiResource ( + OUT STM_RSC *ResourceList, + IN OUT UINT32 *ResourceSize + ) +{ + if (*ResourceSize < mStmResourceSizeUsed) { + *ResourceSize = (UINT32)mStmResourceSizeUsed; + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem (ResourceList, mStmResourcesPtr, mStmResourceSizeUsed); + *ResourceSize = (UINT32)mStmResourceSizeUsed; + return EFI_SUCCESS; +} + +/** + + Set valid bit for MSEG MSR. + + @param Buffer Ap function buffer. (not used) + +**/ +VOID +EFIAPI +EnableMsegMsr ( + IN VOID *Buffer + ) +{ + MSR_IA32_SMM_MONITOR_CTL_REGISTER SmmMonitorCtl; + + SmmMonitorCtl.Uint64 = AsmReadMsr64 (MSR_IA32_SMM_MONITOR_CTL); + SmmMonitorCtl.Bits.Valid = 1; + AsmWriteMsr64 (MSR_IA32_SMM_MONITOR_CTL, SmmMonitorCtl.Uint64); +} + +/** + + Get 4K page aligned VMCS size. + + @return 4K page aligned VMCS size + +**/ +UINT32 +GetVmcsSize ( + VOID + ) +{ + MSR_IA32_VMX_BASIC_REGISTER VmxBasic; + + // + // Read VMCS size and and align to 4KB + // + VmxBasic.Uint64 = AsmReadMsr64 (MSR_IA32_VMX_BASIC); + return ALIGN_VALUE (VmxBasic.Bits.VmcsSize, SIZE_4KB); +} + +/** + + Check STM image size. + + @param StmImage STM image + @param StmImageSize STM image size + + @retval TRUE check pass + @retval FALSE check fail +**/ +BOOLEAN +StmCheckStmImage ( + IN EFI_PHYSICAL_ADDRESS StmImage, + IN UINTN StmImageSize + ) +{ + UINTN MinMsegSize; + STM_HEADER *StmHeader; + IA32_VMX_MISC_REGISTER VmxMiscMsr; + + // + // Check to see if STM image is compatible with CPU + // + StmHeader = (STM_HEADER *)(UINTN)StmImage; + VmxMiscMsr.Uint64 = AsmReadMsr64 (MSR_IA32_VMX_MISC); + if (StmHeader->HwStmHdr.MsegHeaderRevision != VmxMiscMsr.Bits.MsegRevisionIdentifier) { + DEBUG ((DEBUG_ERROR, "STM Image not compatible with CPU\n")); + DEBUG ((DEBUG_ERROR, " StmHeader->HwStmHdr.MsegHeaderRevision = %08x\n", StmHeader->HwStmHdr.MsegHeaderRevision)); + DEBUG ((DEBUG_ERROR, " VmxMiscMsr.Bits.MsegRevisionIdentifier = %08x\n", VmxMiscMsr.Bits.MsegRevisionIdentifier)); + return FALSE; + } + + // + // Get Minimal required Mseg size + // + MinMsegSize = (EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (StmHeader->SwStmHdr.StaticImageSize)) + + StmHeader->SwStmHdr.AdditionalDynamicMemorySize + + (StmHeader->SwStmHdr.PerProcDynamicMemorySize + GetVmcsSize () * 2) * gSmst->NumberOfCpus); + if (MinMsegSize < StmImageSize) { + MinMsegSize = StmImageSize; + } + + if (StmHeader->HwStmHdr.Cr3Offset >= StmHeader->SwStmHdr.StaticImageSize) { + // + // We will create page table, just in case that SINIT does not create it. + // + if (MinMsegSize < StmHeader->HwStmHdr.Cr3Offset + EFI_PAGES_TO_SIZE(6)) { + MinMsegSize = StmHeader->HwStmHdr.Cr3Offset + EFI_PAGES_TO_SIZE(6); + } + } + + // + // Check if it exceeds MSEG size + // + if (MinMsegSize > mMsegSize) { + DEBUG ((DEBUG_ERROR, "MSEG too small. Min MSEG Size = %08x Current MSEG Size = %08x\n", MinMsegSize, mMsegSize)); + DEBUG ((DEBUG_ERROR, " StmHeader->SwStmHdr.StaticImageSize = %08x\n", StmHeader->SwStmHdr.StaticImageSize)); + DEBUG ((DEBUG_ERROR, " StmHeader->SwStmHdr.AdditionalDynamicMemorySize = %08x\n", StmHeader->SwStmHdr.AdditionalDynamicMemorySize)); + DEBUG ((DEBUG_ERROR, " StmHeader->SwStmHdr.PerProcDynamicMemorySize = %08x\n", StmHeader->SwStmHdr.PerProcDynamicMemorySize)); + DEBUG ((DEBUG_ERROR, " VMCS Size = %08x\n", GetVmcsSize ())); + DEBUG ((DEBUG_ERROR, " Max CPUs = %08x\n", gSmst->NumberOfCpus)); + DEBUG ((DEBUG_ERROR, " StmHeader->HwStmHdr.Cr3Offset = %08x\n", StmHeader->HwStmHdr.Cr3Offset)); + return FALSE; + } + + return TRUE; +} + +/** + + Load STM image to MSEG. + + @param StmImage STM image + @param StmImageSize STM image size + +**/ +VOID +StmLoadStmImage ( + IN EFI_PHYSICAL_ADDRESS StmImage, + IN UINTN StmImageSize + ) +{ + MSR_IA32_SMM_MONITOR_CTL_REGISTER SmmMonitorCtl; + UINT32 MsegBase; + STM_HEADER *StmHeader; + + // + // Get MSEG base address from MSR_IA32_SMM_MONITOR_CTL + // + SmmMonitorCtl.Uint64 = AsmReadMsr64 (MSR_IA32_SMM_MONITOR_CTL); + MsegBase = SmmMonitorCtl.Bits.MsegBase << 12; + + // + // Zero all of MSEG base address + // + ZeroMem ((VOID *)(UINTN)MsegBase, mMsegSize); + + // + // Copy STM Image into MSEG + // + CopyMem ((VOID *)(UINTN)MsegBase, (VOID *)(UINTN)StmImage, StmImageSize); + + // + // STM Header is at the beginning of the STM Image + // + StmHeader = (STM_HEADER *)(UINTN)StmImage; + + StmGen4GPageTable ((UINTN)MsegBase + StmHeader->HwStmHdr.Cr3Offset); +} + +/** + + Load STM image to MSEG. + + @param StmImage STM image + @param StmImageSize STM image size + + @retval EFI_SUCCESS Load STM to MSEG successfully + @retval EFI_ALREADY_STARTED STM image is already loaded to MSEG + @retval EFI_BUFFER_TOO_SMALL MSEG is smaller than minimal requirement of STM image + @retval EFI_UNSUPPORTED MSEG is not enabled + +**/ +EFI_STATUS +EFIAPI +LoadMonitor ( + IN EFI_PHYSICAL_ADDRESS StmImage, + IN UINTN StmImageSize + ) +{ + MSR_IA32_SMM_MONITOR_CTL_REGISTER SmmMonitorCtl; + + if (mLockLoadMonitor) { + return EFI_ACCESS_DENIED; + } + + SmmMonitorCtl.Uint64 = AsmReadMsr64 (MSR_IA32_SMM_MONITOR_CTL); + if (SmmMonitorCtl.Bits.MsegBase == 0) { + return EFI_UNSUPPORTED; + } + + if (!StmCheckStmImage (StmImage, StmImageSize)) { + return EFI_BUFFER_TOO_SMALL; + } + + // Record STM_HASH to PCR 0, just in case it is NOT TXT launch, we still need provide the evidence. + TpmMeasureAndLogData( + 0, // PcrIndex + TXT_EVTYPE_STM_HASH, // EventType + NULL, // EventLog + 0, // LogLen + (VOID *)(UINTN)StmImage, // HashData + StmImageSize // HashDataLen + ); + + StmLoadStmImage (StmImage, StmImageSize); + + mStmState |= EFI_SM_MONITOR_STATE_ENABLED; + + return EFI_SUCCESS; +} + +/** + This function return BIOS STM resource. + Produced by SmmStm. + Consumed by SmmMpService when Init. + + @return BIOS STM resource + +**/ +VOID * +GetStmResource( + VOID + ) +{ + return mStmResourcesPtr; +} + +/** + This function notify STM resource change. + + @param StmResource BIOS STM resource + +**/ +VOID +NotifyStmResourceChange ( + VOID *StmResource + ) +{ + UINTN Index; + TXT_PROCESSOR_SMM_DESCRIPTOR *Psd; + + for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { + Psd = (TXT_PROCESSOR_SMM_DESCRIPTOR *)((UINTN)gSmst->CpuSaveState[Index] - SMRAM_SAVE_STATE_MAP_OFFSET + TXT_SMM_PSD_OFFSET); + Psd->BiosHwResourceRequirementsPtr = (UINT64)(UINTN)StmResource; + } + return ; +} + + +/** + This is STM setup BIOS callback. +**/ +VOID +EFIAPI +SmmStmSetup ( + VOID + ) +{ + mStmState |= EFI_SM_MONITOR_STATE_ACTIVATED; +} + +/** + This is STM teardown BIOS callback. +**/ +VOID +EFIAPI +SmmStmTeardown ( + VOID + ) +{ + mStmState &= ~EFI_SM_MONITOR_STATE_ACTIVATED; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.h new file mode 100644 index 00000000..417b3945 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmStm.h @@ -0,0 +1,179 @@ +/** @file + SMM STM support + + Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SMM_STM_H_ +#define _SMM_STM_H_ + +#include <Protocol/SmMonitorInit.h> + +/** + + Create 4G page table for STM. + 2M PAE page table in X64 version. + + @param PageTableBase The page table base in MSEG + +**/ +VOID +StmGen4GPageTable ( + IN UINTN PageTableBase + ); + +/** + This is SMM exception handle. + Consumed by STM when exception happen. + + @param Context STM protection exception stack frame + + @return the EBX value for STM reference. + EBX = 0: resume SMM guest using register state found on exception stack. + EBX = 1 to 0x0F: EBX contains a BIOS error code which the STM must record in the + TXT.ERRORCODE register and subsequently reset the system via + TXT.CMD.SYS_RESET. The value of the TXT.ERRORCODE register is calculated as + follows: TXT.ERRORCODE = (EBX & 0x0F) | STM_CRASH_BIOS_PANIC + EBX = 0x10 to 0xFFFFFFFF - reserved, do not use. + +**/ +UINT32 +EFIAPI +SmmStmExceptionHandler ( + IN OUT STM_PROTECTION_EXCEPTION_STACK_FRAME Context + ); + + +/** + + Get STM state. + + @return STM state + +**/ +EFI_SM_MONITOR_STATE +EFIAPI +GetMonitorState ( + VOID + ); + +/** + + Load STM image to MSEG. + + @param StmImage STM image + @param StmImageSize STM image size + + @retval EFI_SUCCESS Load STM to MSEG successfully + @retval EFI_BUFFER_TOO_SMALL MSEG is smaller than minimal requirement of STM image + +**/ +EFI_STATUS +EFIAPI +LoadMonitor ( + IN EFI_PHYSICAL_ADDRESS StmImage, + IN UINTN StmImageSize + ); + +/** + + Add resources in list to database. Allocate new memory areas as needed. + + @param ResourceList A pointer to resource list to be added + @param NumEntries Optional number of entries. + If 0, list must be terminated by END_OF_RESOURCES. + + @retval EFI_SUCCESS If resources are added + @retval EFI_INVALID_PARAMETER If nested procedure detected resource failer + @retval EFI_OUT_OF_RESOURCES If nested procedure returned it and we cannot allocate more areas. + +**/ +EFI_STATUS +EFIAPI +AddPiResource ( + IN STM_RSC *ResourceList, + IN UINT32 NumEntries OPTIONAL + ); + +/** + + Delete resources in list to database. + + @param ResourceList A pointer to resource list to be deleted + NULL means delete all resources. + @param NumEntries Optional number of entries. + If 0, list must be terminated by END_OF_RESOURCES. + + @retval EFI_SUCCESS If resources are deleted + @retval EFI_INVALID_PARAMETER If nested procedure detected resource failer + +**/ +EFI_STATUS +EFIAPI +DeletePiResource ( + IN STM_RSC *ResourceList, + IN UINT32 NumEntries OPTIONAL + ); + +/** + + Get BIOS resources. + + @param ResourceList A pointer to resource list to be filled + @param ResourceSize On input it means size of resource list input. + On output it means size of resource list filled, + or the size of resource list to be filled if size of too small. + + @retval EFI_SUCCESS If resources are returned. + @retval EFI_BUFFER_TOO_SMALL If resource list buffer is too small to hold the whole resources. + +**/ +EFI_STATUS +EFIAPI +GetPiResource ( + OUT STM_RSC *ResourceList, + IN OUT UINT32 *ResourceSize + ); + +/** + This function initialize STM configuration table. +**/ +VOID +StmSmmConfigurationTableInit ( + VOID + ); + +/** + This function notify STM resource change. + + @param StmResource BIOS STM resource + +**/ +VOID +NotifyStmResourceChange ( + IN VOID *StmResource + ); + +/** + This function return BIOS STM resource. + + @return BIOS STM resource + +**/ +VOID * +GetStmResource ( + VOID + ); + +/** + This function fixes up the address of the global variable or function + referred in SmiEntry assembly files to be the absolute address. +**/ +VOID +EFIAPI +SmmCpuFeaturesLibStmSmiEntryFixupAddress ( + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/StandaloneMmCpuFeaturesLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/StandaloneMmCpuFeaturesLib.c new file mode 100644 index 00000000..ea985200 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/StandaloneMmCpuFeaturesLib.c @@ -0,0 +1,50 @@ +/** @file +Standalone MM CPU specific programming. + +Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> +Copyright (c) Microsoft Corporation.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiMm.h> +#include <Library/PcdLib.h> +#include "CpuFeaturesLib.h" + +/** + Gets the maximum number of logical processors from the PCD PcdCpuMaxLogicalProcessorNumber. + + This access is abstracted from the PCD services to enforce that the PCD be + FixedAtBuild in the Standalone MM build of this driver. + + @return The value of PcdCpuMaxLogicalProcessorNumber. + +**/ +UINT32 +GetCpuMaxLogicalProcessorNumber ( + VOID + ) +{ + return FixedPcdGet32 (PcdCpuMaxLogicalProcessorNumber); +} + +/** + The Standalone MM library constructor. + + @param[in] ImageHandle Image handle of this driver. + @param[in] SystemTable A Pointer to the EFI System Table. + + @retval EFI_SUCCESS This constructor always returns success. + +**/ +EFI_STATUS +EFIAPI +StandaloneMmCpuFeaturesLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_MM_SYSTEM_TABLE *SystemTable + ) +{ + CpuFeaturesLibInitialization (); + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/StandaloneMmCpuFeaturesLib.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/StandaloneMmCpuFeaturesLib.inf new file mode 100644 index 00000000..ecd1cd4d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/StandaloneMmCpuFeaturesLib.inf @@ -0,0 +1,38 @@ +## @file +# Standalone MM CPU specific programming. +# +# Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR> +# Copyright (c) Microsoft Corporation.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = StandaloneMmCpuFeaturesLib + MODULE_UNI_FILE = SmmCpuFeaturesLib.uni + FILE_GUID = BB554A2D-F5DF-41D3-8C62-46476A2B2B18 + MODULE_TYPE = MM_STANDALONE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x00010032 + LIBRARY_CLASS = SmmCpuFeaturesLib + CONSTRUCTOR = StandaloneMmCpuFeaturesLibConstructor + +[Sources] + CpuFeaturesLib.h + StandaloneMmCpuFeaturesLib.c + SmmCpuFeaturesLibCommon.c + SmmCpuFeaturesLibNoStm.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + MemoryAllocationLib + PcdLib + +[FixedPcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## SOMETIMES_CONSUMES diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/TraditionalMmCpuFeaturesLib.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/TraditionalMmCpuFeaturesLib.c new file mode 100644 index 00000000..8a7b7ead --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/TraditionalMmCpuFeaturesLib.c @@ -0,0 +1,28 @@ +/** @file + Traditional MM CPU specific programming. + + Copyright (c) Microsoft Corporation.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Base.h> +#include <Library/PcdLib.h> +#include "CpuFeaturesLib.h" + +/** + Gets the maximum number of logical processors from the PCD PcdCpuMaxLogicalProcessorNumber. + + This access is abstracted from the PCD services to enforce that the PCD be + FixedAtBuild in the Standalone MM build of this driver. + + @return The value of PcdCpuMaxLogicalProcessorNumber. + +**/ +UINT32 +GetCpuMaxLogicalProcessorNumber ( + VOID + ) +{ + return PcdGet32 (PcdCpuMaxLogicalProcessorNumber); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/X64/SmiEntry.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/X64/SmiEntry.nasm new file mode 100644 index 00000000..13850b0b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/X64/SmiEntry.nasm @@ -0,0 +1,276 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; SmiEntry.nasm +; +; Abstract: +; +; Code template of the SMI handler for a particular processor +; +;------------------------------------------------------------------------------- + +%include "StuffRsbNasm.inc" + +; +; Variables referenced by C code +; + +%define MSR_IA32_MISC_ENABLE 0x1A0 +%define MSR_EFER 0xc0000080 +%define MSR_EFER_XD 0x800 + +; +; Constants relating to TXT_PROCESSOR_SMM_DESCRIPTOR +; +%define DSC_OFFSET 0xfb00 +%define DSC_GDTPTR 0x48 +%define DSC_GDTSIZ 0x50 +%define DSC_CS 0x14 +%define DSC_DS 0x16 +%define DSC_SS 0x18 +%define DSC_OTHERSEG 0x1a +; +; Constants relating to CPU State Save Area +; +%define SSM_DR6 0xffd0 +%define SSM_DR7 0xffc8 + +%define PROTECT_MODE_CS 0x8 +%define PROTECT_MODE_DS 0x20 +%define LONG_MODE_CS 0x38 +%define TSS_SEGMENT 0x40 +%define GDT_SIZE 0x50 + +extern ASM_PFX(SmiRendezvous) +extern ASM_PFX(gStmSmiHandlerIdtr) +extern ASM_PFX(CpuSmmDebugEntry) +extern ASM_PFX(CpuSmmDebugExit) + +global ASM_PFX(gStmSmbase) +global ASM_PFX(gStmXdSupported) +global ASM_PFX(gStmSmiStack) +global ASM_PFX(gStmSmiCr3) +global ASM_PFX(gcStmSmiHandlerTemplate) +global ASM_PFX(gcStmSmiHandlerSize) +global ASM_PFX(gcStmSmiHandlerOffset) + +ASM_PFX(gStmSmbase) EQU StmSmbasePatch - 4 +ASM_PFX(gStmSmiStack) EQU StmSmiStackPatch - 4 +ASM_PFX(gStmSmiCr3) EQU StmSmiCr3Patch - 4 +ASM_PFX(gStmXdSupported) EQU StmXdSupportedPatch - 1 + + DEFAULT REL + SECTION .text + +BITS 16 +ASM_PFX(gcStmSmiHandlerTemplate): +_StmSmiEntryPoint: + mov bx, _StmGdtDesc - _StmSmiEntryPoint + 0x8000 + mov ax,[cs:DSC_OFFSET + DSC_GDTSIZ] + dec ax + mov [cs:bx], ax + mov eax, [cs:DSC_OFFSET + DSC_GDTPTR] + mov [cs:bx + 2], eax +o32 lgdt [cs:bx] ; lgdt fword ptr cs:[bx] + mov ax, PROTECT_MODE_CS + mov [cs:bx-0x2],ax +o32 mov edi, strict dword 0 +StmSmbasePatch: + lea eax, [edi + (@ProtectedMode - _StmSmiEntryPoint) + 0x8000] + mov [cs:bx-0x6],eax + mov ebx, cr0 + and ebx, 0x9ffafff3 + or ebx, 0x23 + mov cr0, ebx + jmp dword 0x0:0x0 +_StmGdtDesc: + DW 0 + DD 0 + +BITS 32 +@ProtectedMode: + mov ax, PROTECT_MODE_DS +o16 mov ds, ax +o16 mov es, ax +o16 mov fs, ax +o16 mov gs, ax +o16 mov ss, ax + mov esp, strict dword 0 +StmSmiStackPatch: + jmp ProtFlatMode + +BITS 64 +ProtFlatMode: + mov eax, strict dword 0 +StmSmiCr3Patch: + mov cr3, rax + mov eax, 0x668 ; as cr4.PGE is not set here, refresh cr3 + mov cr4, rax ; in PreModifyMtrrs() to flush TLB. +; Load TSS + sub esp, 8 ; reserve room in stack + sgdt [rsp] + mov eax, [rsp + 2] ; eax = GDT base + add esp, 8 + mov dl, 0x89 + mov [rax + TSS_SEGMENT + 5], dl ; clear busy flag + mov eax, TSS_SEGMENT + ltr ax + +; enable NXE if supported + mov al, strict byte 1 +StmXdSupportedPatch: + cmp al, 0 + jz @SkipXd +; +; Check XD disable bit +; + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + sub esp, 4 + push rdx ; save MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 ; MSR_IA32_MISC_ENABLE[34] + jz .0 + and dx, 0xFFFB ; clear XD Disable bit if it is set + wrmsr +.0: + mov ecx, MSR_EFER + rdmsr + or ax, MSR_EFER_XD ; enable NXE + wrmsr + jmp @XdDone +@SkipXd: + sub esp, 8 +@XdDone: + +; Switch into @LongMode + push LONG_MODE_CS ; push cs hardcore here + call Base ; push return address for retf later +Base: + add dword [rsp], @LongMode - Base; offset for far retf, seg is the 1st arg + + mov ecx, MSR_EFER + rdmsr + or ah, 1 ; enable LME + wrmsr + mov rbx, cr0 + or ebx, 0x80010023 ; enable paging + WP + NE + MP + PE + mov cr0, rbx + retf +@LongMode: ; long mode (64-bit code) starts here + mov rax, strict qword 0 ; mov rax, ASM_PFX(gStmSmiHandlerIdtr) +StmSmiEntrySmiHandlerIdtrAbsAddr: + lidt [rax] + lea ebx, [rdi + DSC_OFFSET] + mov ax, [rbx + DSC_DS] + mov ds, eax + mov ax, [rbx + DSC_OTHERSEG] + mov es, eax + mov fs, eax + mov gs, eax + mov ax, [rbx + DSC_SS] + mov ss, eax + mov rax, strict qword 0 ; mov rax, CommonHandler +StmSmiEntryCommonHandlerAbsAddr: + jmp rax +CommonHandler: + mov rbx, [rsp + 0x08] ; rbx <- CpuIndex + + ; + ; Save FP registers + ; + sub rsp, 0x200 + fxsave64 [rsp] + + add rsp, -0x20 + + mov rcx, rbx + call ASM_PFX(CpuSmmDebugEntry) + + mov rcx, rbx + call ASM_PFX(SmiRendezvous) + + mov rcx, rbx + call ASM_PFX(CpuSmmDebugExit) + + add rsp, 0x20 + + ; + ; Restore FP registers + ; + fxrstor64 [rsp] + + add rsp, 0x200 + + lea rax, [ASM_PFX(gStmXdSupported)] + mov al, [rax] + cmp al, 0 + jz .1 + pop rdx ; get saved MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 + jz .1 + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + or dx, BIT2 ; set XD Disable bit if it was set before entering into SMM + wrmsr + +.1: + StuffRsb64 + rsm + +_StmSmiHandler: +; +; Check XD disable bit +; + xor r8, r8 + lea rax, [ASM_PFX(gStmXdSupported)] + mov al, [rax] + cmp al, 0 + jz @StmXdDone + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + mov r8, rdx ; save MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 ; MSR_IA32_MISC_ENABLE[34] + jz .0 + and dx, 0xFFFB ; clear XD Disable bit if it is set + wrmsr +.0: + mov ecx, MSR_EFER + rdmsr + or ax, MSR_EFER_XD ; enable NXE + wrmsr +@StmXdDone: + push r8 + + ; below step is needed, because STM does not run above code. + ; we have to run below code to set IDT/CR0/CR4 + mov rax, strict qword 0 ; mov rax, ASM_PFX(gStmSmiHandlerIdtr) +StmSmiHandlerIdtrAbsAddr: + lidt [rax] + + mov rax, cr0 + or eax, 0x80010023 ; enable paging + WP + NE + MP + PE + mov cr0, rax + mov rax, cr4 + mov eax, 0x668 ; as cr4.PGE is not set here, refresh cr3 + mov cr4, rax ; in PreModifyMtrrs() to flush TLB. + ; STM init finish + jmp CommonHandler + +ASM_PFX(gcStmSmiHandlerSize) : DW $ - _StmSmiEntryPoint +ASM_PFX(gcStmSmiHandlerOffset) : DW _StmSmiHandler - _StmSmiEntryPoint + +global ASM_PFX(SmmCpuFeaturesLibStmSmiEntryFixupAddress) +ASM_PFX(SmmCpuFeaturesLibStmSmiEntryFixupAddress): + lea rax, [ASM_PFX(gStmSmiHandlerIdtr)] + lea rcx, [StmSmiEntrySmiHandlerIdtrAbsAddr] + mov qword [rcx - 8], rax + lea rcx, [StmSmiHandlerIdtrAbsAddr] + mov qword [rcx - 8], rax + + lea rax, [CommonHandler] + lea rcx, [StmSmiEntryCommonHandlerAbsAddr] + mov qword [rcx - 8], rax + ret diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/X64/SmiException.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/X64/SmiException.nasm new file mode 100644 index 00000000..c3b92ac8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/X64/SmiException.nasm @@ -0,0 +1,176 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; SmiException.nasm +; +; Abstract: +; +; Exception handlers used in SM mode +; +;------------------------------------------------------------------------------- + +%include "StuffRsbNasm.inc" + +global ASM_PFX(gcStmPsd) + +extern ASM_PFX(SmmStmExceptionHandler) +extern ASM_PFX(SmmStmSetup) +extern ASM_PFX(SmmStmTeardown) +extern ASM_PFX(gStmXdSupported) +extern ASM_PFX(gStmSmiHandlerIdtr) + +%define MSR_IA32_MISC_ENABLE 0x1A0 +%define MSR_EFER 0xc0000080 +%define MSR_EFER_XD 0x800 + +CODE_SEL equ 0x38 +DATA_SEL equ 0x20 +TR_SEL equ 0x40 + + SECTION .data + +; +; This structure serves as a template for all processors. +; +ASM_PFX(gcStmPsd): + DB 'TXTPSSIG' + DW PSD_SIZE + DW 1 ; Version + DD 0 ; LocalApicId + DB 0x0F ; Cr4Pse;Cr4Pae;Intel64Mode;ExecutionDisableOutsideSmrr + DB 0 ; BIOS to STM + DB 0 ; STM to BIOS + DB 0 + DW CODE_SEL + DW DATA_SEL + DW DATA_SEL + DW DATA_SEL + DW TR_SEL + DW 0 + DQ 0 ; SmmCr3 + DQ ASM_PFX(OnStmSetup) + DQ ASM_PFX(OnStmTeardown) + DQ 0 ; SmmSmiHandlerRip - SMM guest entrypoint + DQ 0 ; SmmSmiHandlerRsp + DQ 0 + DD 0 + DD 0x80010100 ; RequiredStmSmmRevId + DQ ASM_PFX(OnException) + DQ 0 ; ExceptionStack + DW DATA_SEL + DW 0x01F ; ExceptionFilter + DD 0 + DQ 0 + DQ 0 ; BiosHwResourceRequirementsPtr + DQ 0 ; AcpiRsdp + DB 0 ; PhysicalAddressBits +PSD_SIZE equ $ - ASM_PFX(gcStmPsd) + + DEFAULT REL + SECTION .text +;------------------------------------------------------------------------------ +; SMM Exception handlers +;------------------------------------------------------------------------------ +global ASM_PFX(OnException) +ASM_PFX(OnException): + mov rcx, rsp + add rsp, -0x28 + call ASM_PFX(SmmStmExceptionHandler) + add rsp, 0x28 + mov ebx, eax + mov eax, 4 + vmcall + jmp $ + +global ASM_PFX(OnStmSetup) +ASM_PFX(OnStmSetup): +; +; Check XD disable bit +; + xor r8, r8 + lea rax, [ASM_PFX(gStmXdSupported)] + mov al, [rax] + cmp al, 0 + jz @StmXdDone1 + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + mov r8, rdx ; save MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 ; MSR_IA32_MISC_ENABLE[34] + jz .01 + and dx, 0xFFFB ; clear XD Disable bit if it is set + wrmsr +.01: + mov ecx, MSR_EFER + rdmsr + or ax, MSR_EFER_XD ; enable NXE + wrmsr +@StmXdDone1: + push r8 + + add rsp, -0x20 + call ASM_PFX(SmmStmSetup) + add rsp, 0x20 + + lea rax, [ASM_PFX(gStmXdSupported)] + mov al, [rax] + cmp al, 0 + jz .11 + pop rdx ; get saved MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 + jz .11 + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + or dx, BIT2 ; set XD Disable bit if it was set before entering into SMM + wrmsr + +.11: + StuffRsb64 + rsm + +global ASM_PFX(OnStmTeardown) +ASM_PFX(OnStmTeardown): +; +; Check XD disable bit +; + xor r8, r8 + lea rax, [ASM_PFX(gStmXdSupported)] + mov al, [rax] + cmp al, 0 + jz @StmXdDone2 + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + mov r8, rdx ; save MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 ; MSR_IA32_MISC_ENABLE[34] + jz .02 + and dx, 0xFFFB ; clear XD Disable bit if it is set + wrmsr +.02: + mov ecx, MSR_EFER + rdmsr + or ax, MSR_EFER_XD ; enable NXE + wrmsr +@StmXdDone2: + push r8 + + add rsp, -0x20 + call ASM_PFX(SmmStmTeardown) + add rsp, 0x20 + + lea rax, [ASM_PFX(gStmXdSupported)] + mov al, [rax] + cmp al, 0 + jz .12 + pop rdx ; get saved MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 + jz .12 + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + or dx, BIT2 ; set XD Disable bit if it was set before entering into SMM + wrmsr + +.12: + StuffRsb64 + rsm diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/X64/SmmStmSupport.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/X64/SmmStmSupport.c new file mode 100644 index 00000000..9ba17dd6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuFeaturesLib/X64/SmmStmSupport.c @@ -0,0 +1,89 @@ +/** @file + SMM STM support functions + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiMm.h> +#include <Library/DebugLib.h> + +#include "SmmStm.h" + +/// +/// Page Table Entry +/// +#define IA32_PG_P BIT0 +#define IA32_PG_RW BIT1 +#define IA32_PG_PS BIT7 + +/** + + Create 4G page table for STM. + 2M PAE page table in X64 version. + + @param PageTableBase The page table base in MSEG + +**/ +VOID +StmGen4GPageTable ( + IN UINTN PageTableBase + ) +{ + UINTN Index; + UINTN SubIndex; + UINT64 *Pde; + UINT64 *Pte; + UINT64 *Pml4; + + Pml4 = (UINT64*)(UINTN)PageTableBase; + PageTableBase += SIZE_4KB; + *Pml4 = PageTableBase | IA32_PG_RW | IA32_PG_P; + + Pde = (UINT64*)(UINTN)PageTableBase; + PageTableBase += SIZE_4KB; + Pte = (UINT64 *)(UINTN)PageTableBase; + + for (Index = 0; Index < 4; Index++) { + *Pde = PageTableBase | IA32_PG_RW | IA32_PG_P; + Pde++; + PageTableBase += SIZE_4KB; + + for (SubIndex = 0; SubIndex < SIZE_4KB / sizeof (*Pte); SubIndex++) { + *Pte = (((Index << 9) + SubIndex) << 21) | IA32_PG_PS | IA32_PG_RW | IA32_PG_P; + Pte++; + } + } +} + +/** + This is SMM exception handle. + Consumed by STM when exception happen. + + @param Context STM protection exception stack frame + + @return the EBX value for STM reference. + EBX = 0: resume SMM guest using register state found on exception stack. + EBX = 1 to 0x0F: EBX contains a BIOS error code which the STM must record in the + TXT.ERRORCODE register and subsequently reset the system via + TXT.CMD.SYS_RESET. The value of the TXT.ERRORCODE register is calculated as + follows: TXT.ERRORCODE = (EBX & 0x0F) | STM_CRASH_BIOS_PANIC + EBX = 0x10 to 0xFFFFFFFF - reserved, do not use. + +**/ +UINT32 +EFIAPI +SmmStmExceptionHandler ( + IN OUT STM_PROTECTION_EXCEPTION_STACK_FRAME Context + ) +{ + // TBD - SmmStmExceptionHandler, record information + DEBUG ((DEBUG_ERROR, "SmmStmExceptionHandler ...\n")); + // + // Skip this instruction and continue; + // + Context.X64StackFrame->Rip += Context.X64StackFrame->VmcsExitInstructionLength; + + return 0; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.c new file mode 100644 index 00000000..4b4204e0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.c @@ -0,0 +1,102 @@ +/** @file +SMM CPU Platform Hook NULL library instance. + +Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include <PiSmm.h> +#include <Library/SmmCpuPlatformHookLib.h> + +/** + Checks if platform produces a valid SMI. + + This function checks if platform produces a valid SMI. This function is + called at SMM entry to detect if this is a spurious SMI. This function + must be implemented in an MP safe way because it is called by multiple CPU + threads. + + @retval TRUE There is a valid SMI + @retval FALSE There is no valid SMI + +**/ +BOOLEAN +EFIAPI +PlatformValidSmi ( + VOID + ) +{ + return TRUE; +} + +/** + Clears platform top level SMI status bit. + + This function clears platform top level SMI status bit. + + @retval TRUE The platform top level SMI status is cleared. + @retval FALSE The platform top level SMI status cannot be cleared. + +**/ +BOOLEAN +EFIAPI +ClearTopLevelSmiStatus ( + VOID + ) +{ + return TRUE; +} + +/** + Performs platform specific way of SMM BSP election. + + This function performs platform specific way of SMM BSP election. + + @param IsBsp Output parameter. TRUE: the CPU this function executes + on is elected to be the SMM BSP. FALSE: the CPU this + function executes on is to be SMM AP. + + @retval EFI_SUCCESS The function executes successfully. + @retval EFI_NOT_READY The function does not determine whether this CPU should be + BSP or AP. This may occur if hardware init sequence to + enable the determination is yet to be done, or the function + chooses not to do BSP election and will let SMM CPU driver to + use its default BSP election process. + @retval EFI_DEVICE_ERROR The function cannot determine whether this CPU should be + BSP or AP due to hardware error. + +**/ +EFI_STATUS +EFIAPI +PlatformSmmBspElection ( + OUT BOOLEAN *IsBsp + ) +{ + return EFI_NOT_READY; +} + +/** + Get platform page table attribute. + + This function gets page table attribute of platform. + + @param Address Input parameter. Obtain the page table entries attribute on this address. + @param PageSize Output parameter. The size of the page. + @param NumOfPages Output parameter. Number of page. + @param PageAttribute Output parameter. Paging Attributes (WB, UC, etc). + + @retval EFI_SUCCESS The platform page table attribute from the address is determined. + @retval EFI_UNSUPPORTED The platform does not support getting page table attribute for the address. + +**/ +EFI_STATUS +EFIAPI +GetPlatformPageTableAttribute ( + IN UINT64 Address, + IN OUT SMM_PAGE_SIZE_TYPE *PageSize, + IN OUT UINTN *NumOfPages, + IN OUT UINTN *PageAttribute + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.inf new file mode 100644 index 00000000..7f147d8b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.inf @@ -0,0 +1,34 @@ +## @file +# SMM CPU Platform Hook NULL library instance. +# +# Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +################################################################################ +# +# Defines Section - statements that will be processed to create a Makefile. +# +################################################################################ +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SmmCpuPlatformHookLibNull + MODULE_UNI_FILE = SmmCpuPlatformHookLibNull.uni + FILE_GUID = D6494E1B-E06F-4ab5-B64D-48B25AA9EB33 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = SmmCpuPlatformHookLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + SmmCpuPlatformHookLibNull.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.uni new file mode 100644 index 00000000..b00beee9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.uni @@ -0,0 +1,12 @@ +// /** @file
+// SMM CPU Platform Hook NULL library instance.
+//
+// Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "SMM CPU Platform Hook NULL library instance"
+
+#string STR_MODULE_DESCRIPTION #language en-US "SMM CPU Platform Hook NULL library instance."
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c new file mode 100644 index 00000000..c1b36573 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c @@ -0,0 +1,165 @@ +/** @file + VMGEXIT Base Support Library. + + Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Base.h> +#include <Uefi.h> +#include <Library/VmgExitLib.h> + +/** + Perform VMGEXIT. + + Sets the necessary fields of the GHCB, invokes the VMGEXIT instruction and + then handles the return actions. + + The base library function returns an error in the form of a + GHCB_EVENT_INJECTION representing a GP_EXCEPTION. + + @param[in, out] Ghcb A pointer to the GHCB + @param[in] ExitCode VMGEXIT code to be assigned to the SwExitCode + field of the GHCB. + @param[in] ExitInfo1 VMGEXIT information to be assigned to the + SwExitInfo1 field of the GHCB. + @param[in] ExitInfo2 VMGEXIT information to be assigned to the + SwExitInfo2 field of the GHCB. + + @retval 0 VMGEXIT succeeded. + @return Exception number to be propagated, VMGEXIT + processing did not succeed. + +**/ +UINT64 +EFIAPI +VmgExit ( + IN OUT GHCB *Ghcb, + IN UINT64 ExitCode, + IN UINT64 ExitInfo1, + IN UINT64 ExitInfo2 + ) +{ + GHCB_EVENT_INJECTION Event; + + Event.Uint64 = 0; + Event.Elements.Vector = GP_EXCEPTION; + Event.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION; + Event.Elements.Valid = 1; + + return Event.Uint64; +} + +/** + Perform pre-VMGEXIT initialization/preparation. + + Performs the necessary steps in preparation for invoking VMGEXIT. Must be + called before setting any fields within the GHCB. + + @param[in, out] Ghcb A pointer to the GHCB + @param[in, out] InterruptState A pointer to hold the current interrupt + state, used for restoring in VmgDone () + +**/ +VOID +EFIAPI +VmgInit ( + IN OUT GHCB *Ghcb, + IN OUT BOOLEAN *InterruptState + ) +{ +} + +/** + Perform post-VMGEXIT cleanup. + + Performs the necessary steps to cleanup after invoking VMGEXIT. Must be + called after obtaining needed fields within the GHCB. + + @param[in, out] Ghcb A pointer to the GHCB + @param[in] InterruptState An indicator to conditionally (re)enable + interrupts + +**/ +VOID +EFIAPI +VmgDone ( + IN OUT GHCB *Ghcb, + IN BOOLEAN InterruptState + ) +{ +} + +/** + Marks a field at the specified offset as valid in the GHCB. + + The ValidBitmap area represents the areas of the GHCB that have been marked + valid. Set the bit in ValidBitmap for the input offset. + + @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication Block + @param[in] Offset Qword offset in the GHCB to mark valid + +**/ +VOID +EFIAPI +VmgSetOffsetValid ( + IN OUT GHCB *Ghcb, + IN GHCB_REGISTER Offset + ) +{ +} + +/** + Checks if a specified offset is valid in the GHCB. + + The ValidBitmap area represents the areas of the GHCB that have been marked + valid. Return whether the bit in the ValidBitmap is set for the input offset. + + @param[in] Ghcb A pointer to the GHCB + @param[in] Offset Qword offset in the GHCB to mark valid + + @retval TRUE Offset is marked valid in the GHCB + @retval FALSE Offset is not marked valid in the GHCB + +**/ +BOOLEAN +EFIAPI +VmgIsOffsetValid ( + IN GHCB *Ghcb, + IN GHCB_REGISTER Offset + ) +{ + return FALSE; +} + +/** + Handle a #VC exception. + + Performs the necessary processing to handle a #VC exception. + + The base library function returns an error equal to VC_EXCEPTION, + to be propagated to the standard exception handling stack. + + @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set + as value to use on error. + @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT + + @retval EFI_SUCCESS Exception handled + @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to + propagate provided + @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to + propagate provided + +**/ +EFI_STATUS +EFIAPI +VmgExitHandleVc ( + IN OUT EFI_EXCEPTION_TYPE *ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + *ExceptionType = VC_EXCEPTION; + + return EFI_UNSUPPORTED; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf new file mode 100644 index 00000000..fe3e0148 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf @@ -0,0 +1,27 @@ +## @file +# VMGEXIT Support Library. +# +# Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VmgExitLibNull + MODULE_UNI_FILE = VmgExitLibNull.uni + FILE_GUID = 3cd7368f-ef9b-4a9b-9571-2ed93813677e + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = VmgExitLib + +[Sources.common] + VmgExitLibNull.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.uni new file mode 100644 index 00000000..8639bc0e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.uni @@ -0,0 +1,15 @@ +// /** @file
+// VMGEXIT support library instance.
+//
+// VMGEXIT support library instance.
+//
+// Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "VMGEXIT support NULL library instance"
+
+#string STR_MODULE_DESCRIPTION #language en-US "VMGEXIT support NULL library instance."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPei.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPei.c new file mode 100644 index 00000000..21fee45e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPei.c @@ -0,0 +1,413 @@ +/** @file +PiSmmCommunication PEI Driver. + +Copyright (c) 2010 - 2021, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiPei.h> +#include <PiDxe.h> +#include <PiSmm.h> +#include <Library/PeiServicesTablePointerLib.h> +#include <Library/PeiServicesLib.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/HobLib.h> +#include <Library/DebugLib.h> +#include <Protocol/SmmCommunication.h> +#include <Ppi/SmmCommunication.h> +#include <Ppi/SmmAccess.h> +#include <Ppi/SmmControl.h> +#include <Guid/AcpiS3Context.h> + +#include "PiSmmCommunicationPrivate.h" + +/** + the whole picture is below: + + +----------------------------------+ + | ACPI_VARIABLE_HOB | + | SmramDescriptor | <- DRAM + | CpuStart |--- + +----------------------------------+ | + | + +----------------------------------+<-- + | SMM_S3_RESUME_STATE | + | Signature | <- SMRAM + | Smst |--- + +----------------------------------+ | + | + +----------------------------------+<-- + | EFI_SMM_SYSTEM_TABLE2 | + | NumberOfTableEntries | <- SMRAM + | SmmConfigurationTable |--- + +----------------------------------+ | + | + +----------------------------------+<-- + | EFI_SMM_COMMUNICATION_CONTEXT | + | SwSmiNumber | <- SMRAM + | BufferPtrAddress |--- + +----------------------------------+ | + | + +----------------------------------+<-- + | Communication Buffer Pointer | <- AcpiNvs + +----------------------------------+--- + | + +----------------------------------+<-- + | EFI_SMM_COMMUNICATE_HEADER | + | HeaderGuid | <- DRAM + | MessageLength | + +----------------------------------+ + +**/ + +#if defined (MDE_CPU_IA32) +typedef struct { + EFI_TABLE_HEADER Hdr; + UINT64 SmmFirmwareVendor; + UINT64 SmmFirmwareRevision; + UINT64 SmmInstallConfigurationTable; + UINT64 SmmIoMemRead; + UINT64 SmmIoMemWrite; + UINT64 SmmIoIoRead; + UINT64 SmmIoIoWrite; + UINT64 SmmAllocatePool; + UINT64 SmmFreePool; + UINT64 SmmAllocatePages; + UINT64 SmmFreePages; + UINT64 SmmStartupThisAp; + UINT64 CurrentlyExecutingCpu; + UINT64 NumberOfCpus; + UINT64 CpuSaveStateSize; + UINT64 CpuSaveState; + UINT64 NumberOfTableEntries; + UINT64 SmmConfigurationTable; +} EFI_SMM_SYSTEM_TABLE2_64; + +typedef struct { + EFI_GUID VendorGuid; + UINT64 VendorTable; +} EFI_CONFIGURATION_TABLE64; +#endif + +#if defined (MDE_CPU_X64) +typedef EFI_SMM_SYSTEM_TABLE2 EFI_SMM_SYSTEM_TABLE2_64; +typedef EFI_CONFIGURATION_TABLE EFI_CONFIGURATION_TABLE64; +#endif + +/** + Communicates with a registered handler. + + This function provides a service to send and receive messages from a registered UEFI service. + + @param[in] This The EFI_PEI_SMM_COMMUNICATION_PPI instance. + @param[in, out] CommBuffer A pointer to the buffer to convey into SMRAM. + @param[in, out] CommSize The size of the data buffer being passed in.On exit, the size of data + being returned. Zero if the handler does not wish to reply with any data. + + @retval EFI_SUCCESS The message was successfully posted. + @retval EFI_INVALID_PARAMETER The CommBuffer was NULL. + @retval EFI_NOT_STARTED The service is NOT started. +**/ +EFI_STATUS +EFIAPI +Communicate ( + IN CONST EFI_PEI_SMM_COMMUNICATION_PPI *This, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommSize + ); + +EFI_PEI_SMM_COMMUNICATION_PPI mSmmCommunicationPpi = { Communicate }; + +EFI_PEI_PPI_DESCRIPTOR mPpiList = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiSmmCommunicationPpiGuid, + &mSmmCommunicationPpi +}; + +/** + Get SMM communication context. + + @return SMM communication context. +**/ +EFI_SMM_COMMUNICATION_CONTEXT * +GetCommunicationContext ( + VOID + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + EFI_SMM_COMMUNICATION_CONTEXT *SmmCommunicationContext; + + GuidHob = GetFirstGuidHob (&gEfiPeiSmmCommunicationPpiGuid); + ASSERT (GuidHob != NULL); + + SmmCommunicationContext = (EFI_SMM_COMMUNICATION_CONTEXT *)GET_GUID_HOB_DATA (GuidHob); + + return SmmCommunicationContext; +} + +/** + Set SMM communication context. + + @param SmmCommunicationContext SMM communication context. +**/ +VOID +SetCommunicationContext ( + IN EFI_SMM_COMMUNICATION_CONTEXT *SmmCommunicationContext + ) +{ + EFI_PEI_HOB_POINTERS Hob; + UINTN BufferSize; + + BufferSize = sizeof (*SmmCommunicationContext); + Hob.Raw = BuildGuidHob ( + &gEfiPeiSmmCommunicationPpiGuid, + BufferSize + ); + ASSERT (Hob.Raw); + + CopyMem ((VOID *)Hob.Raw, SmmCommunicationContext, sizeof(*SmmCommunicationContext)); +} + +/** + Get VendorTable by VendorGuid in Smst. + + @param Signature Signature of SMM_S3_RESUME_STATE + @param Smst SMM system table + @param VendorGuid vendor guid + + @return vendor table. +**/ +VOID * +InternalSmstGetVendorTableByGuid ( + IN UINT64 Signature, + IN EFI_SMM_SYSTEM_TABLE2 *Smst, + IN EFI_GUID *VendorGuid + ) +{ + EFI_CONFIGURATION_TABLE *SmmConfigurationTable; + UINTN NumberOfTableEntries; + UINTN Index; + EFI_SMM_SYSTEM_TABLE2_64 *Smst64; + EFI_CONFIGURATION_TABLE64 *SmmConfigurationTable64; + + if ((sizeof(UINTN) == sizeof(UINT32)) && (Signature == SMM_S3_RESUME_SMM_64)) { + // + // 32 PEI + 64 DXE + // + Smst64 = (EFI_SMM_SYSTEM_TABLE2_64 *)Smst; + DEBUG ((EFI_D_INFO, "InitCommunicationContext - SmmConfigurationTable: %x\n", Smst64->SmmConfigurationTable)); + DEBUG ((EFI_D_INFO, "InitCommunicationContext - NumberOfTableEntries: %x\n", Smst64->NumberOfTableEntries)); + SmmConfigurationTable64 = (EFI_CONFIGURATION_TABLE64 *)(UINTN)Smst64->SmmConfigurationTable; + NumberOfTableEntries = (UINTN)Smst64->NumberOfTableEntries; + for (Index = 0; Index < NumberOfTableEntries; Index++) { + if (CompareGuid (&SmmConfigurationTable64[Index].VendorGuid, VendorGuid)) { + return (VOID *)(UINTN)SmmConfigurationTable64[Index].VendorTable; + } + } + return NULL; + } else { + DEBUG ((EFI_D_INFO, "InitCommunicationContext - SmmConfigurationTable: %x\n", Smst->SmmConfigurationTable)); + DEBUG ((EFI_D_INFO, "InitCommunicationContext - NumberOfTableEntries: %x\n", Smst->NumberOfTableEntries)); + SmmConfigurationTable = Smst->SmmConfigurationTable; + NumberOfTableEntries = Smst->NumberOfTableEntries; + for (Index = 0; Index < NumberOfTableEntries; Index++) { + if (CompareGuid (&SmmConfigurationTable[Index].VendorGuid, VendorGuid)) { + return (VOID *)SmmConfigurationTable[Index].VendorTable; + } + } + return NULL; + } +} + +/** + Init SMM communication context. +**/ +VOID +InitCommunicationContext ( + VOID + ) +{ + EFI_SMRAM_DESCRIPTOR *SmramDescriptor; + SMM_S3_RESUME_STATE *SmmS3ResumeState; + VOID *GuidHob; + EFI_SMM_COMMUNICATION_CONTEXT *SmmCommunicationContext; + + GuidHob = GetFirstGuidHob (&gEfiAcpiVariableGuid); + ASSERT (GuidHob != NULL); + SmramDescriptor = (EFI_SMRAM_DESCRIPTOR *) GET_GUID_HOB_DATA (GuidHob); + SmmS3ResumeState = (SMM_S3_RESUME_STATE *)(UINTN)SmramDescriptor->CpuStart; + + DEBUG ((EFI_D_INFO, "InitCommunicationContext - SmmS3ResumeState: %x\n", SmmS3ResumeState)); + DEBUG ((EFI_D_INFO, "InitCommunicationContext - Smst: %x\n", SmmS3ResumeState->Smst)); + + SmmCommunicationContext = (EFI_SMM_COMMUNICATION_CONTEXT *)InternalSmstGetVendorTableByGuid ( + SmmS3ResumeState->Signature, + (EFI_SMM_SYSTEM_TABLE2 *)(UINTN)SmmS3ResumeState->Smst, + &gEfiPeiSmmCommunicationPpiGuid + ); + ASSERT (SmmCommunicationContext != NULL); + + SetCommunicationContext (SmmCommunicationContext); + + return ; +} + +/** + Communicates with a registered handler. + + This function provides a service to send and receive messages from a registered UEFI service. + + @param[in] This The EFI_PEI_SMM_COMMUNICATION_PPI instance. + @param[in, out] CommBuffer A pointer to the buffer to convey into SMRAM. + @param[in, out] CommSize The size of the data buffer being passed in.On exit, the size of data + being returned. Zero if the handler does not wish to reply with any data. + + @retval EFI_SUCCESS The message was successfully posted. + @retval EFI_INVALID_PARAMETER The CommBuffer was NULL. + @retval EFI_NOT_STARTED The service is NOT started. +**/ +EFI_STATUS +EFIAPI +Communicate ( + IN CONST EFI_PEI_SMM_COMMUNICATION_PPI *This, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommSize + ) +{ + EFI_STATUS Status; + PEI_SMM_CONTROL_PPI *SmmControl; + PEI_SMM_ACCESS_PPI *SmmAccess; + UINT8 SmiCommand; + UINTN Size; + EFI_SMM_COMMUNICATION_CONTEXT *SmmCommunicationContext; + + DEBUG ((EFI_D_INFO, "PiSmmCommunicationPei Communicate Enter\n")); + + if (CommBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Get needed resource + // + Status = PeiServicesLocatePpi ( + &gPeiSmmControlPpiGuid, + 0, + NULL, + (VOID **)&SmmControl + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_STARTED; + } + + Status = PeiServicesLocatePpi ( + &gPeiSmmAccessPpiGuid, + 0, + NULL, + (VOID **)&SmmAccess + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_STARTED; + } + + // + // Check SMRAM locked, it should be done after SMRAM lock. + // + if (!SmmAccess->LockState) { + DEBUG ((EFI_D_INFO, "PiSmmCommunicationPei LockState - %x\n", (UINTN)SmmAccess->LockState)); + return EFI_NOT_STARTED; + } + + SmmCommunicationContext = GetCommunicationContext (); + DEBUG ((EFI_D_INFO, "PiSmmCommunicationPei BufferPtrAddress - 0x%016lx, BufferPtr: 0x%016lx\n", SmmCommunicationContext->BufferPtrAddress, *(EFI_PHYSICAL_ADDRESS *)(UINTN)SmmCommunicationContext->BufferPtrAddress)); + + // + // No need to check if BufferPtr is 0, because it is in PEI phase. + // + *(EFI_PHYSICAL_ADDRESS *)(UINTN)SmmCommunicationContext->BufferPtrAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CommBuffer; + DEBUG ((EFI_D_INFO, "PiSmmCommunicationPei CommBuffer - %x\n", (UINTN)CommBuffer)); + + // + // Send command + // + SmiCommand = (UINT8)SmmCommunicationContext->SwSmiNumber; + Size = sizeof(SmiCommand); + Status = SmmControl->Trigger ( + (EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), + SmmControl, + (INT8 *)&SmiCommand, + &Size, + FALSE, + 0 + ); + ASSERT_EFI_ERROR (Status); + + // + // Setting BufferPtr to 0 means this transaction is done. + // + *(EFI_PHYSICAL_ADDRESS *)(UINTN)SmmCommunicationContext->BufferPtrAddress = 0; + + DEBUG ((EFI_D_INFO, "PiSmmCommunicationPei Communicate Exit\n")); + + return EFI_SUCCESS; +} + +/** + Entry Point for PI SMM communication PEIM. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Pointer to PEI Services table. + + @retval EFI_SUCCESS + @return Others Some error occurs. +**/ +EFI_STATUS +EFIAPI +PiSmmCommunicationPeiEntryPoint ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + PEI_SMM_ACCESS_PPI *SmmAccess; + EFI_BOOT_MODE BootMode; + UINTN Index; + + BootMode = GetBootModeHob (); + if (BootMode != BOOT_ON_S3_RESUME) { + return EFI_UNSUPPORTED; + } + + Status = PeiServicesLocatePpi ( + &gPeiSmmAccessPpiGuid, + 0, + NULL, + (VOID **)&SmmAccess + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_STARTED; + } + + // + // Check SMRAM locked, it should be done before SMRAM lock. + // + if (SmmAccess->LockState) { + DEBUG ((EFI_D_INFO, "PiSmmCommunicationPei LockState - %x\n", (UINTN)SmmAccess->LockState)); + return EFI_ACCESS_DENIED; + } + + // + // Open all SMRAM + // + for (Index = 0; !EFI_ERROR (Status); Index++) { + Status = SmmAccess->Open ((EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), SmmAccess, Index); + } + + InitCommunicationContext (); + + PeiServicesInstallPpi (&mPpiList); + + return RETURN_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPei.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPei.inf new file mode 100644 index 00000000..864a2261 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPei.inf @@ -0,0 +1,64 @@ +## @file +# PI SMM Communication PEIM which produces PEI SMM Communication PPI. +# +# This PEIM retrieves the SMM communication context and produces PEI SMM +# Communication PPIin the S3 boot mode. +# +# Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PiSmmCommunicationPei + MODULE_UNI_FILE = PiSmmCommunicationPei.uni + FILE_GUID = 1C8B7F78-1699-40e6-AF33-9B995D16B043 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = PiSmmCommunicationPeiEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + PiSmmCommunicationPei.c + PiSmmCommunicationPrivate.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PeimEntryPoint + PeiServicesTablePointerLib + PeiServicesLib + BaseLib + BaseMemoryLib + HobLib + DebugLib + +[Guids] + gEfiAcpiVariableGuid ## CONSUMES ## HOB + +[Ppis] + ## PRODUCES + ## UNDEFINED # HOB # SMM Configuration Table + gEfiPeiSmmCommunicationPpiGuid + gPeiSmmAccessPpiGuid ## CONSUMES + gPeiSmmControlPpiGuid ## CONSUMES + +# [BootMode] +# S3_RESUME ## CONSUMES + +[Depex] + gPeiSmmAccessPpiGuid AND + gPeiSmmControlPpiGuid AND + gEfiPeiMasterBootModePpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + PiSmmCommunicationPeiExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPei.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPei.uni new file mode 100644 index 00000000..29c22808 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPei.uni @@ -0,0 +1,15 @@ +// /** @file
+// PI SMM Communication PEIM which produces PEI SMM Communication PPI.
+//
+// This PEIM retrieves the SMM communication context and produces PEI SMM
+// Communication PPIin the S3 boot mode.
+//
+// Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "PI SMM Communication PEIM that produces PEI SMM Communication PPI"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This PEIM retrieves the SMM communication context and produces PEI SMM Communication PPI in the S3 boot mode."
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPeiExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPeiExtra.uni new file mode 100644 index 00000000..c7645784 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPeiExtra.uni @@ -0,0 +1,12 @@ +// /** @file
+// PiSmmCommunicationPei Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"SMM Communication PEI Module"
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPrivate.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPrivate.h new file mode 100644 index 00000000..1d2b7438 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPrivate.h @@ -0,0 +1,24 @@ +/** @file +PiSmmCommunication private data structure + +Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SMM_COMMUNICATION_PRIVATE_H_ +#define _SMM_COMMUNICATION_PRIVATE_H_ + +#pragma pack(push, 1) + +#define SMM_COMMUNICATION_SIGNATURE SIGNATURE_32 ('S','M','M','C') + +typedef struct { + UINT32 Signature; + UINT32 SwSmiNumber; + EFI_PHYSICAL_ADDRESS BufferPtrAddress; +} EFI_SMM_COMMUNICATION_CONTEXT; + +#pragma pack(pop) + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmm.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmm.c new file mode 100644 index 00000000..932361fe --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmm.c @@ -0,0 +1,207 @@ +/** @file +PiSmmCommunication SMM Driver. + +Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiSmm.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include <Library/SmmServicesTableLib.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/SmmMemLib.h> +#include <Protocol/SmmSwDispatch2.h> +#include <Protocol/SmmCommunication.h> +#include <Ppi/SmmCommunication.h> + +#include "PiSmmCommunicationPrivate.h" + +EFI_SMM_COMMUNICATION_CONTEXT mSmmCommunicationContext = { + SMM_COMMUNICATION_SIGNATURE +}; + +/** + Set SMM communication context. +**/ +VOID +SetCommunicationContext ( + VOID + ) +{ + EFI_STATUS Status; + + Status = gSmst->SmmInstallConfigurationTable ( + gSmst, + &gEfiPeiSmmCommunicationPpiGuid, + &mSmmCommunicationContext, + sizeof(mSmmCommunicationContext) + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Dispatch function for a Software SMI handler. + + @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param Context Points to an optional handler context which was specified when the + handler was registered. + @param CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param CommBufferSize The size of the CommBuffer. + + @retval EFI_SUCCESS Command is handled successfully. + +**/ +EFI_STATUS +EFIAPI +PiSmmCommunicationHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context OPTIONAL, + IN OUT VOID *CommBuffer OPTIONAL, + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + UINTN CommSize; + EFI_STATUS Status; + EFI_SMM_COMMUNICATE_HEADER *CommunicateHeader; + EFI_PHYSICAL_ADDRESS *BufferPtrAddress; + + DEBUG ((EFI_D_INFO, "PiSmmCommunicationHandler Enter\n")); + + BufferPtrAddress = (EFI_PHYSICAL_ADDRESS *)(UINTN)mSmmCommunicationContext.BufferPtrAddress; + CommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *)(UINTN)*BufferPtrAddress; + DEBUG ((EFI_D_INFO, "PiSmmCommunicationHandler CommunicateHeader - %x\n", CommunicateHeader)); + if (CommunicateHeader == NULL) { + DEBUG ((EFI_D_INFO, "PiSmmCommunicationHandler is NULL, needn't to call dispatch function\n")); + Status = EFI_SUCCESS; + } else { + if (!SmmIsBufferOutsideSmmValid ((UINTN)CommunicateHeader, OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data))) { + DEBUG ((EFI_D_INFO, "PiSmmCommunicationHandler CommunicateHeader invalid - 0x%x\n", CommunicateHeader)); + Status = EFI_SUCCESS; + goto Done; + } + + CommSize = (UINTN)CommunicateHeader->MessageLength; + if (!SmmIsBufferOutsideSmmValid ((UINTN)&CommunicateHeader->Data[0], CommSize)) { + DEBUG ((EFI_D_INFO, "PiSmmCommunicationHandler CommunicateData invalid - 0x%x\n", &CommunicateHeader->Data[0])); + Status = EFI_SUCCESS; + goto Done; + } + + // + // Call dispatch function + // + DEBUG ((EFI_D_INFO, "PiSmmCommunicationHandler Data - %x\n", &CommunicateHeader->Data[0])); + Status = gSmst->SmiManage ( + &CommunicateHeader->HeaderGuid, + NULL, + &CommunicateHeader->Data[0], + &CommSize + ); + } + +Done: + DEBUG ((EFI_D_INFO, "PiSmmCommunicationHandler %r\n", Status)); + DEBUG ((EFI_D_INFO, "PiSmmCommunicationHandler Exit\n")); + + return (Status == EFI_SUCCESS) ? EFI_SUCCESS : EFI_INTERRUPT_PENDING; +} + +/** + Allocate EfiACPIMemoryNVS below 4G memory address. + + This function allocates EfiACPIMemoryNVS below 4G memory address. + + @param Size Size of memory to allocate. + + @return Allocated address for output. + +**/ +VOID* +AllocateAcpiNvsMemoryBelow4G ( + IN UINTN Size + ) +{ + UINTN Pages; + EFI_PHYSICAL_ADDRESS Address; + EFI_STATUS Status; + VOID* Buffer; + + Pages = EFI_SIZE_TO_PAGES (Size); + Address = 0xffffffff; + + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiACPIMemoryNVS, + Pages, + &Address + ); + ASSERT_EFI_ERROR (Status); + + Buffer = (VOID *) (UINTN) Address; + ZeroMem (Buffer, Size); + + return Buffer; +} + +/** + Entry Point for PI SMM communication SMM driver. + + @param[in] ImageHandle Image handle of this driver. + @param[in] SystemTable A Pointer to the EFI System Table. + + @retval EFI_SUCCESS + @return Others Some error occurs. +**/ +EFI_STATUS +EFIAPI +PiSmmCommunicationSmmEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_SMM_SW_DISPATCH2_PROTOCOL *SmmSwDispatch2; + EFI_SMM_SW_REGISTER_CONTEXT SmmSwDispatchContext; + EFI_HANDLE DispatchHandle; + EFI_PHYSICAL_ADDRESS *BufferPtrAddress; + + // + // Register software SMI handler + // + Status = gSmst->SmmLocateProtocol ( + &gEfiSmmSwDispatch2ProtocolGuid, + NULL, + (VOID **)&SmmSwDispatch2 + ); + ASSERT_EFI_ERROR (Status); + + SmmSwDispatchContext.SwSmiInputValue = (UINTN)-1; + Status = SmmSwDispatch2->Register ( + SmmSwDispatch2, + PiSmmCommunicationHandler, + &SmmSwDispatchContext, + &DispatchHandle + ); + ASSERT_EFI_ERROR (Status); + + DEBUG ((EFI_D_INFO, "SmmCommunication SwSmi: %x\n", (UINTN)SmmSwDispatchContext.SwSmiInputValue)); + + BufferPtrAddress = AllocateAcpiNvsMemoryBelow4G (sizeof(EFI_PHYSICAL_ADDRESS)); + ASSERT (BufferPtrAddress != NULL); + DEBUG ((EFI_D_INFO, "SmmCommunication BufferPtrAddress: 0x%016lx, BufferPtr: 0x%016lx\n", (EFI_PHYSICAL_ADDRESS)(UINTN)BufferPtrAddress, *BufferPtrAddress)); + + // + // Save context + // + mSmmCommunicationContext.SwSmiNumber = (UINT32)SmmSwDispatchContext.SwSmiInputValue; + mSmmCommunicationContext.BufferPtrAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)BufferPtrAddress; + SetCommunicationContext (); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmm.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmm.inf new file mode 100644 index 00000000..c70a7d2f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmm.inf @@ -0,0 +1,55 @@ +## @file +# PI SMM Communication SMM driver that saves SMM communication context +# for use by SMM Communication PEIM in the S3 boot mode. +# +# Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PiSmmCommunicationSmm + MODULE_UNI_FILE = PiSmmCommunicationSmm.uni + FILE_GUID = E21F35A8-42FF-4050-82D6-93F7CDFA7073 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = PiSmmCommunicationSmmEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + PiSmmCommunicationSmm.c + PiSmmCommunicationPrivate.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + SmmServicesTableLib + BaseLib + BaseMemoryLib + DebugLib + SmmMemLib + +[Ppis] + gEfiPeiSmmCommunicationPpiGuid ## UNDEFINED # SMM Configuration Table + +[Protocols] + gEfiSmmSwDispatch2ProtocolGuid ## CONSUMES + +[Depex] + gEfiSmmSwDispatch2ProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + PiSmmCommunicationSmmExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmm.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmm.uni new file mode 100644 index 00000000..cfb642e6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmm.uni @@ -0,0 +1,13 @@ +// /** @file
+// PI SMM Communication SMM driver that saves SMM communication context
+// for use by SMM Communication PEIM in the S3 boot mode.
+//
+// Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "PI SMM Communication SMM driver that saves SMM communication context"
+
+#string STR_MODULE_DESCRIPTION #language en-US "PI SMM Communication SMM driver that saves SMM communication context for use by SMM Communication PEIM in the S3 boot mode."
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmmExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmmExtra.uni new file mode 100644 index 00000000..7d9c2b67 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmmExtra.uni @@ -0,0 +1,12 @@ +// /** @file
+// PiSmmCommunicationSmm Localized Strings and Content
+//
+// Copyright (c) 2013 - 2017, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"SMM Communication SMM Driver"
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/CpuS3.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/CpuS3.c new file mode 100644 index 00000000..6df07ac6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/CpuS3.c @@ -0,0 +1,1160 @@ +/** @file +Code for Processor S3 restoration + +Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" + +#pragma pack(1) +typedef struct { + UINTN Lock; + VOID *StackStart; + UINTN StackSize; + VOID *ApFunction; + IA32_DESCRIPTOR GdtrProfile; + IA32_DESCRIPTOR IdtrProfile; + UINT32 BufferStart; + UINT32 Cr3; + UINTN InitializeFloatingPointUnitsAddress; +} MP_CPU_EXCHANGE_INFO; +#pragma pack() + +typedef struct { + UINT8 *RendezvousFunnelAddress; + UINTN PModeEntryOffset; + UINTN FlatJumpOffset; + UINTN Size; + UINTN LModeEntryOffset; + UINTN LongJumpOffset; +} MP_ASSEMBLY_ADDRESS_MAP; + +// +// Flags used when program the register. +// +typedef struct { + volatile UINTN MemoryMappedLock; // Spinlock used to program mmio + volatile UINT32 *CoreSemaphoreCount; // Semaphore container used to program + // core level semaphore. + volatile UINT32 *PackageSemaphoreCount; // Semaphore container used to program + // package level semaphore. +} PROGRAM_CPU_REGISTER_FLAGS; + +// +// Signal that SMM BASE relocation is complete. +// +volatile BOOLEAN mInitApsAfterSmmBaseReloc; + +/** + Get starting address and size of the rendezvous entry for APs. + Information for fixing a jump instruction in the code is also returned. + + @param AddressMap Output buffer for address map information. +**/ +VOID * +EFIAPI +AsmGetAddressMap ( + MP_ASSEMBLY_ADDRESS_MAP *AddressMap + ); + +#define LEGACY_REGION_SIZE (2 * 0x1000) +#define LEGACY_REGION_BASE (0xA0000 - LEGACY_REGION_SIZE) + +PROGRAM_CPU_REGISTER_FLAGS mCpuFlags; +ACPI_CPU_DATA mAcpiCpuData; +volatile UINT32 mNumberToFinish; +MP_CPU_EXCHANGE_INFO *mExchangeInfo; +BOOLEAN mRestoreSmmConfigurationInS3 = FALSE; + +// +// S3 boot flag +// +BOOLEAN mSmmS3Flag = FALSE; + +// +// Pointer to structure used during S3 Resume +// +SMM_S3_RESUME_STATE *mSmmS3ResumeState = NULL; + +BOOLEAN mAcpiS3Enable = TRUE; + +UINT8 *mApHltLoopCode = NULL; +UINT8 mApHltLoopCodeTemplate[] = { + 0x8B, 0x44, 0x24, 0x04, // mov eax, dword ptr [esp+4] + 0xF0, 0xFF, 0x08, // lock dec dword ptr [eax] + 0xFA, // cli + 0xF4, // hlt + 0xEB, 0xFC // jmp $-2 + }; + +/** + Sync up the MTRR values for all processors. + + @param MtrrTable Table holding fixed/variable MTRR values to be loaded. +**/ +VOID +EFIAPI +LoadMtrrData ( + EFI_PHYSICAL_ADDRESS MtrrTable + ) +/*++ + +Routine Description: + + Sync up the MTRR values for all processors. + +Arguments: + +Returns: + None + +--*/ +{ + MTRR_SETTINGS *MtrrSettings; + + MtrrSettings = (MTRR_SETTINGS *) (UINTN) MtrrTable; + MtrrSetAllMtrrs (MtrrSettings); +} + +/** + Increment semaphore by 1. + + @param Sem IN: 32-bit unsigned integer + +**/ +VOID +S3ReleaseSemaphore ( + IN OUT volatile UINT32 *Sem + ) +{ + InterlockedIncrement (Sem); +} + +/** + Decrement the semaphore by 1 if it is not zero. + + Performs an atomic decrement operation for semaphore. + The compare exchange operation must be performed using + MP safe mechanisms. + + @param Sem IN: 32-bit unsigned integer + +**/ +VOID +S3WaitForSemaphore ( + IN OUT volatile UINT32 *Sem + ) +{ + UINT32 Value; + + do { + Value = *Sem; + } while (Value == 0 || + InterlockedCompareExchange32 ( + Sem, + Value, + Value - 1 + ) != Value); +} + +/** + Read / write CR value. + + @param[in] CrIndex The CR index which need to read/write. + @param[in] Read Read or write. TRUE is read. + @param[in,out] CrValue CR value. + + @retval EFI_SUCCESS means read/write success, else return EFI_UNSUPPORTED. +**/ +UINTN +ReadWriteCr ( + IN UINT32 CrIndex, + IN BOOLEAN Read, + IN OUT UINTN *CrValue + ) +{ + switch (CrIndex) { + case 0: + if (Read) { + *CrValue = AsmReadCr0 (); + } else { + AsmWriteCr0 (*CrValue); + } + break; + case 2: + if (Read) { + *CrValue = AsmReadCr2 (); + } else { + AsmWriteCr2 (*CrValue); + } + break; + case 3: + if (Read) { + *CrValue = AsmReadCr3 (); + } else { + AsmWriteCr3 (*CrValue); + } + break; + case 4: + if (Read) { + *CrValue = AsmReadCr4 (); + } else { + AsmWriteCr4 (*CrValue); + } + break; + default: + return EFI_UNSUPPORTED;; + } + + return EFI_SUCCESS; +} + +/** + Initialize the CPU registers from a register table. + + @param[in] RegisterTable The register table for this AP. + @param[in] ApLocation AP location info for this ap. + @param[in] CpuStatus CPU status info for this CPU. + @param[in] CpuFlags Flags data structure used when program the register. + + @note This service could be called by BSP/APs. +**/ +VOID +ProgramProcessorRegister ( + IN CPU_REGISTER_TABLE *RegisterTable, + IN EFI_CPU_PHYSICAL_LOCATION *ApLocation, + IN CPU_STATUS_INFORMATION *CpuStatus, + IN PROGRAM_CPU_REGISTER_FLAGS *CpuFlags + ) +{ + CPU_REGISTER_TABLE_ENTRY *RegisterTableEntry; + UINTN Index; + UINTN Value; + CPU_REGISTER_TABLE_ENTRY *RegisterTableEntryHead; + volatile UINT32 *SemaphorePtr; + UINT32 FirstThread; + UINT32 CurrentThread; + UINT32 CurrentCore; + UINTN ProcessorIndex; + UINT32 *ThreadCountPerPackage; + UINT8 *ThreadCountPerCore; + EFI_STATUS Status; + UINT64 CurrentValue; + + // + // Traverse Register Table of this logical processor + // + RegisterTableEntryHead = (CPU_REGISTER_TABLE_ENTRY *) (UINTN) RegisterTable->RegisterTableEntry; + + for (Index = 0; Index < RegisterTable->TableLength; Index++) { + + RegisterTableEntry = &RegisterTableEntryHead[Index]; + + // + // Check the type of specified register + // + switch (RegisterTableEntry->RegisterType) { + // + // The specified register is Control Register + // + case ControlRegister: + Status = ReadWriteCr (RegisterTableEntry->Index, TRUE, &Value); + if (EFI_ERROR (Status)) { + break; + } + if (RegisterTableEntry->TestThenWrite) { + CurrentValue = BitFieldRead64 ( + Value, + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1 + ); + if (CurrentValue == RegisterTableEntry->Value) { + break; + } + } + Value = (UINTN) BitFieldWrite64 ( + Value, + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, + RegisterTableEntry->Value + ); + ReadWriteCr (RegisterTableEntry->Index, FALSE, &Value); + break; + // + // The specified register is Model Specific Register + // + case Msr: + if (RegisterTableEntry->TestThenWrite) { + Value = (UINTN)AsmReadMsr64 (RegisterTableEntry->Index); + if (RegisterTableEntry->ValidBitLength >= 64) { + if (Value == RegisterTableEntry->Value) { + break; + } + } else { + CurrentValue = BitFieldRead64 ( + Value, + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1 + ); + if (CurrentValue == RegisterTableEntry->Value) { + break; + } + } + } + + // + // If this function is called to restore register setting after INIT signal, + // there is no need to restore MSRs in register table. + // + if (RegisterTableEntry->ValidBitLength >= 64) { + // + // If length is not less than 64 bits, then directly write without reading + // + AsmWriteMsr64 ( + RegisterTableEntry->Index, + RegisterTableEntry->Value + ); + } else { + // + // Set the bit section according to bit start and length + // + AsmMsrBitFieldWrite64 ( + RegisterTableEntry->Index, + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, + RegisterTableEntry->Value + ); + } + break; + // + // MemoryMapped operations + // + case MemoryMapped: + AcquireSpinLock (&CpuFlags->MemoryMappedLock); + MmioBitFieldWrite32 ( + (UINTN)(RegisterTableEntry->Index | LShiftU64 (RegisterTableEntry->HighIndex, 32)), + RegisterTableEntry->ValidBitStart, + RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, + (UINT32)RegisterTableEntry->Value + ); + ReleaseSpinLock (&CpuFlags->MemoryMappedLock); + break; + // + // Enable or disable cache + // + case CacheControl: + // + // If value of the entry is 0, then disable cache. Otherwise, enable cache. + // + if (RegisterTableEntry->Value == 0) { + AsmDisableCache (); + } else { + AsmEnableCache (); + } + break; + + case Semaphore: + // Semaphore works logic like below: + // + // V(x) = LibReleaseSemaphore (Semaphore[FirstThread + x]); + // P(x) = LibWaitForSemaphore (Semaphore[FirstThread + x]); + // + // All threads (T0...Tn) waits in P() line and continues running + // together. + // + // + // T0 T1 ... Tn + // + // V(0...n) V(0...n) ... V(0...n) + // n * P(0) n * P(1) ... n * P(n) + // + ASSERT ( + (ApLocation != NULL) && + (CpuStatus->ThreadCountPerPackage != 0) && + (CpuStatus->ThreadCountPerCore != 0) && + (CpuFlags->CoreSemaphoreCount != NULL) && + (CpuFlags->PackageSemaphoreCount != NULL) + ); + switch (RegisterTableEntry->Value) { + case CoreDepType: + SemaphorePtr = CpuFlags->CoreSemaphoreCount; + ThreadCountPerCore = (UINT8 *)(UINTN)CpuStatus->ThreadCountPerCore; + + CurrentCore = ApLocation->Package * CpuStatus->MaxCoreCount + ApLocation->Core; + // + // Get Offset info for the first thread in the core which current thread belongs to. + // + FirstThread = CurrentCore * CpuStatus->MaxThreadCount; + CurrentThread = FirstThread + ApLocation->Thread; + + // + // Different cores may have different valid threads in them. If driver maintail clearly + // thread index in different cores, the logic will be much complicated. + // Here driver just simply records the max thread number in all cores and use it as expect + // thread number for all cores. + // In below two steps logic, first current thread will Release semaphore for each thread + // in current core. Maybe some threads are not valid in this core, but driver don't + // care. Second, driver will let current thread wait semaphore for all valid threads in + // current core. Because only the valid threads will do release semaphore for this + // thread, driver here only need to wait the valid thread count. + // + + // + // First Notify ALL THREADs in current Core that this thread is ready. + // + for (ProcessorIndex = 0; ProcessorIndex < CpuStatus->MaxThreadCount; ProcessorIndex ++) { + S3ReleaseSemaphore (&SemaphorePtr[FirstThread + ProcessorIndex]); + } + // + // Second, check whether all VALID THREADs (not all threads) in current core are ready. + // + for (ProcessorIndex = 0; ProcessorIndex < ThreadCountPerCore[CurrentCore]; ProcessorIndex ++) { + S3WaitForSemaphore (&SemaphorePtr[CurrentThread]); + } + break; + + case PackageDepType: + SemaphorePtr = CpuFlags->PackageSemaphoreCount; + ThreadCountPerPackage = (UINT32 *)(UINTN)CpuStatus->ThreadCountPerPackage; + // + // Get Offset info for the first thread in the package which current thread belongs to. + // + FirstThread = ApLocation->Package * CpuStatus->MaxCoreCount * CpuStatus->MaxThreadCount; + // + // Get the possible threads count for current package. + // + CurrentThread = FirstThread + CpuStatus->MaxThreadCount * ApLocation->Core + ApLocation->Thread; + + // + // Different packages may have different valid threads in them. If driver maintail clearly + // thread index in different packages, the logic will be much complicated. + // Here driver just simply records the max thread number in all packages and use it as expect + // thread number for all packages. + // In below two steps logic, first current thread will Release semaphore for each thread + // in current package. Maybe some threads are not valid in this package, but driver don't + // care. Second, driver will let current thread wait semaphore for all valid threads in + // current package. Because only the valid threads will do release semaphore for this + // thread, driver here only need to wait the valid thread count. + // + + // + // First Notify ALL THREADS in current package that this thread is ready. + // + for (ProcessorIndex = 0; ProcessorIndex < CpuStatus->MaxThreadCount * CpuStatus->MaxCoreCount; ProcessorIndex ++) { + S3ReleaseSemaphore (&SemaphorePtr[FirstThread + ProcessorIndex]); + } + // + // Second, check whether VALID THREADS (not all threads) in current package are ready. + // + for (ProcessorIndex = 0; ProcessorIndex < ThreadCountPerPackage[ApLocation->Package]; ProcessorIndex ++) { + S3WaitForSemaphore (&SemaphorePtr[CurrentThread]); + } + break; + + default: + break; + } + break; + + default: + break; + } + } +} + +/** + + Set Processor register for one AP. + + @param PreSmmRegisterTable Use pre Smm register table or register table. + +**/ +VOID +SetRegister ( + IN BOOLEAN PreSmmRegisterTable + ) +{ + CPU_REGISTER_TABLE *RegisterTable; + CPU_REGISTER_TABLE *RegisterTables; + UINT32 InitApicId; + UINTN ProcIndex; + UINTN Index; + + if (PreSmmRegisterTable) { + RegisterTables = (CPU_REGISTER_TABLE *)(UINTN)mAcpiCpuData.PreSmmInitRegisterTable; + } else { + RegisterTables = (CPU_REGISTER_TABLE *)(UINTN)mAcpiCpuData.RegisterTable; + } + if (RegisterTables == NULL) { + return; + } + + InitApicId = GetInitialApicId (); + RegisterTable = NULL; + ProcIndex = (UINTN)-1; + for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) { + if (RegisterTables[Index].InitialApicId == InitApicId) { + RegisterTable = &RegisterTables[Index]; + ProcIndex = Index; + break; + } + } + ASSERT (RegisterTable != NULL); + + if (mAcpiCpuData.ApLocation != 0) { + ProgramProcessorRegister ( + RegisterTable, + (EFI_CPU_PHYSICAL_LOCATION *)(UINTN)mAcpiCpuData.ApLocation + ProcIndex, + &mAcpiCpuData.CpuStatus, + &mCpuFlags + ); + } else { + ProgramProcessorRegister ( + RegisterTable, + NULL, + &mAcpiCpuData.CpuStatus, + &mCpuFlags + ); + } +} + +/** + AP initialization before then after SMBASE relocation in the S3 boot path. +**/ +VOID +InitializeAp ( + VOID + ) +{ + UINTN TopOfStack; + UINT8 Stack[128]; + + LoadMtrrData (mAcpiCpuData.MtrrTable); + + SetRegister (TRUE); + + // + // Count down the number with lock mechanism. + // + InterlockedDecrement (&mNumberToFinish); + + // + // Wait for BSP to signal SMM Base relocation done. + // + while (!mInitApsAfterSmmBaseReloc) { + CpuPause (); + } + + ProgramVirtualWireMode (); + DisableLvtInterrupts (); + + SetRegister (FALSE); + + // + // Place AP into the safe code, count down the number with lock mechanism in the safe code. + // + TopOfStack = (UINTN) Stack + sizeof (Stack); + TopOfStack &= ~(UINTN) (CPU_STACK_ALIGNMENT - 1); + CopyMem ((VOID *) (UINTN) mApHltLoopCode, mApHltLoopCodeTemplate, sizeof (mApHltLoopCodeTemplate)); + TransferApToSafeState ((UINTN)mApHltLoopCode, TopOfStack, (UINTN)&mNumberToFinish); +} + +/** + Prepares startup vector for APs. + + This function prepares startup vector for APs. + + @param WorkingBuffer The address of the work buffer. +**/ +VOID +PrepareApStartupVector ( + EFI_PHYSICAL_ADDRESS WorkingBuffer + ) +{ + EFI_PHYSICAL_ADDRESS StartupVector; + MP_ASSEMBLY_ADDRESS_MAP AddressMap; + + // + // Get the address map of startup code for AP, + // including code size, and offset of long jump instructions to redirect. + // + ZeroMem (&AddressMap, sizeof (AddressMap)); + AsmGetAddressMap (&AddressMap); + + StartupVector = WorkingBuffer; + + // + // Copy AP startup code to startup vector, and then redirect the long jump + // instructions for mode switching. + // + CopyMem ((VOID *) (UINTN) StartupVector, AddressMap.RendezvousFunnelAddress, AddressMap.Size); + *(UINT32 *) (UINTN) (StartupVector + AddressMap.FlatJumpOffset + 3) = (UINT32) (StartupVector + AddressMap.PModeEntryOffset); + if (AddressMap.LongJumpOffset != 0) { + *(UINT32 *) (UINTN) (StartupVector + AddressMap.LongJumpOffset + 2) = (UINT32) (StartupVector + AddressMap.LModeEntryOffset); + } + + // + // Get the start address of exchange data between BSP and AP. + // + mExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) (StartupVector + AddressMap.Size); + ZeroMem ((VOID *) mExchangeInfo, sizeof (MP_CPU_EXCHANGE_INFO)); + + CopyMem ((VOID *) (UINTN) &mExchangeInfo->GdtrProfile, (VOID *) (UINTN) mAcpiCpuData.GdtrProfile, sizeof (IA32_DESCRIPTOR)); + CopyMem ((VOID *) (UINTN) &mExchangeInfo->IdtrProfile, (VOID *) (UINTN) mAcpiCpuData.IdtrProfile, sizeof (IA32_DESCRIPTOR)); + + mExchangeInfo->StackStart = (VOID *) (UINTN) mAcpiCpuData.StackAddress; + mExchangeInfo->StackSize = mAcpiCpuData.StackSize; + mExchangeInfo->BufferStart = (UINT32) StartupVector; + mExchangeInfo->Cr3 = (UINT32) (AsmReadCr3 ()); + mExchangeInfo->InitializeFloatingPointUnitsAddress = (UINTN)InitializeFloatingPointUnits; +} + +/** + The function is invoked before SMBASE relocation in S3 path to restores CPU status. + + The function is invoked before SMBASE relocation in S3 path. It does first time microcode load + and restores MTRRs for both BSP and APs. + +**/ +VOID +InitializeCpuBeforeRebase ( + VOID + ) +{ + LoadMtrrData (mAcpiCpuData.MtrrTable); + + SetRegister (TRUE); + + ProgramVirtualWireMode (); + + PrepareApStartupVector (mAcpiCpuData.StartupVector); + + if (FeaturePcdGet (PcdCpuHotPlugSupport)) { + ASSERT (mNumberOfCpus <= mAcpiCpuData.NumberOfCpus); + } else { + ASSERT (mNumberOfCpus == mAcpiCpuData.NumberOfCpus); + } + mNumberToFinish = (UINT32)(mNumberOfCpus - 1); + mExchangeInfo->ApFunction = (VOID *) (UINTN) InitializeAp; + + // + // Execute code for before SmmBaseReloc. Note: This flag is maintained across S3 boots. + // + mInitApsAfterSmmBaseReloc = FALSE; + + // + // Send INIT IPI - SIPI to all APs + // + SendInitSipiSipiAllExcludingSelf ((UINT32)mAcpiCpuData.StartupVector); + + while (mNumberToFinish > 0) { + CpuPause (); + } +} + +/** + The function is invoked after SMBASE relocation in S3 path to restores CPU status. + + The function is invoked after SMBASE relocation in S3 path. It restores configuration according to + data saved by normal boot path for both BSP and APs. + +**/ +VOID +InitializeCpuAfterRebase ( + VOID + ) +{ + if (FeaturePcdGet (PcdCpuHotPlugSupport)) { + ASSERT (mNumberOfCpus <= mAcpiCpuData.NumberOfCpus); + } else { + ASSERT (mNumberOfCpus == mAcpiCpuData.NumberOfCpus); + } + mNumberToFinish = (UINT32)(mNumberOfCpus - 1); + + // + // Signal that SMM base relocation is complete and to continue initialization for all APs. + // + mInitApsAfterSmmBaseReloc = TRUE; + + // + // Must begin set register after all APs have continue their initialization. + // This is a requirement to support semaphore mechanism in register table. + // Because if semaphore's dependence type is package type, semaphore will wait + // for all Aps in one package finishing their tasks before set next register + // for all APs. If the Aps not begin its task during BSP doing its task, the + // BSP thread will hang because it is waiting for other Aps in the same + // package finishing their task. + // + SetRegister (FALSE); + + while (mNumberToFinish > 0) { + CpuPause (); + } +} + +/** + Restore SMM Configuration in S3 boot path. + +**/ +VOID +RestoreSmmConfigurationInS3 ( + VOID + ) +{ + if (!mAcpiS3Enable) { + return; + } + + // + // Restore SMM Configuration in S3 boot path. + // + if (mRestoreSmmConfigurationInS3) { + // + // Need make sure gSmst is correct because below function may use them. + // + gSmst->SmmStartupThisAp = gSmmCpuPrivate->SmmCoreEntryContext.SmmStartupThisAp; + gSmst->CurrentlyExecutingCpu = gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu; + gSmst->NumberOfCpus = gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; + gSmst->CpuSaveStateSize = gSmmCpuPrivate->SmmCoreEntryContext.CpuSaveStateSize; + gSmst->CpuSaveState = gSmmCpuPrivate->SmmCoreEntryContext.CpuSaveState; + + // + // Configure SMM Code Access Check feature if available. + // + ConfigSmmCodeAccessCheck (); + + SmmCpuFeaturesCompleteSmmReadyToLock (); + + mRestoreSmmConfigurationInS3 = FALSE; + } +} + +/** + Perform SMM initialization for all processors in the S3 boot path. + + For a native platform, MP initialization in the S3 boot path is also performed in this function. +**/ +VOID +EFIAPI +SmmRestoreCpu ( + VOID + ) +{ + SMM_S3_RESUME_STATE *SmmS3ResumeState; + IA32_DESCRIPTOR Ia32Idtr; + IA32_DESCRIPTOR X64Idtr; + IA32_IDT_GATE_DESCRIPTOR IdtEntryTable[EXCEPTION_VECTOR_NUMBER]; + EFI_STATUS Status; + + DEBUG ((EFI_D_INFO, "SmmRestoreCpu()\n")); + + mSmmS3Flag = TRUE; + + // + // See if there is enough context to resume PEI Phase + // + if (mSmmS3ResumeState == NULL) { + DEBUG ((EFI_D_ERROR, "No context to return to PEI Phase\n")); + CpuDeadLoop (); + } + + SmmS3ResumeState = mSmmS3ResumeState; + ASSERT (SmmS3ResumeState != NULL); + + if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_64) { + // + // Save the IA32 IDT Descriptor + // + AsmReadIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr); + + // + // Setup X64 IDT table + // + ZeroMem (IdtEntryTable, sizeof (IA32_IDT_GATE_DESCRIPTOR) * 32); + X64Idtr.Base = (UINTN) IdtEntryTable; + X64Idtr.Limit = (UINT16) (sizeof (IA32_IDT_GATE_DESCRIPTOR) * 32 - 1); + AsmWriteIdtr ((IA32_DESCRIPTOR *) &X64Idtr); + + // + // Setup the default exception handler + // + Status = InitializeCpuExceptionHandlers (NULL); + ASSERT_EFI_ERROR (Status); + + // + // Initialize Debug Agent to support source level debug + // + InitializeDebugAgent (DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64, (VOID *)&Ia32Idtr, NULL); + } + + // + // Skip initialization if mAcpiCpuData is not valid + // + if (mAcpiCpuData.NumberOfCpus > 0) { + // + // First time microcode load and restore MTRRs + // + InitializeCpuBeforeRebase (); + } + + // + // Restore SMBASE for BSP and all APs + // + SmmRelocateBases (); + + // + // Skip initialization if mAcpiCpuData is not valid + // + if (mAcpiCpuData.NumberOfCpus > 0) { + // + // Restore MSRs for BSP and all APs + // + InitializeCpuAfterRebase (); + } + + // + // Set a flag to restore SMM configuration in S3 path. + // + mRestoreSmmConfigurationInS3 = TRUE; + + DEBUG (( EFI_D_INFO, "SMM S3 Return CS = %x\n", SmmS3ResumeState->ReturnCs)); + DEBUG (( EFI_D_INFO, "SMM S3 Return Entry Point = %x\n", SmmS3ResumeState->ReturnEntryPoint)); + DEBUG (( EFI_D_INFO, "SMM S3 Return Context1 = %x\n", SmmS3ResumeState->ReturnContext1)); + DEBUG (( EFI_D_INFO, "SMM S3 Return Context2 = %x\n", SmmS3ResumeState->ReturnContext2)); + DEBUG (( EFI_D_INFO, "SMM S3 Return Stack Pointer = %x\n", SmmS3ResumeState->ReturnStackPointer)); + + // + // If SMM is in 32-bit mode, then use SwitchStack() to resume PEI Phase + // + if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_32) { + DEBUG ((EFI_D_INFO, "Call SwitchStack() to return to S3 Resume in PEI Phase\n")); + + SwitchStack ( + (SWITCH_STACK_ENTRY_POINT)(UINTN)SmmS3ResumeState->ReturnEntryPoint, + (VOID *)(UINTN)SmmS3ResumeState->ReturnContext1, + (VOID *)(UINTN)SmmS3ResumeState->ReturnContext2, + (VOID *)(UINTN)SmmS3ResumeState->ReturnStackPointer + ); + } + + // + // If SMM is in 64-bit mode, then use AsmDisablePaging64() to resume PEI Phase + // + if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_64) { + DEBUG ((EFI_D_INFO, "Call AsmDisablePaging64() to return to S3 Resume in PEI Phase\n")); + // + // Disable interrupt of Debug timer, since new IDT table is for IA32 and will not work in long mode. + // + SaveAndSetDebugTimerInterrupt (FALSE); + // + // Restore IA32 IDT table + // + AsmWriteIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr); + AsmDisablePaging64 ( + SmmS3ResumeState->ReturnCs, + (UINT32)SmmS3ResumeState->ReturnEntryPoint, + (UINT32)SmmS3ResumeState->ReturnContext1, + (UINT32)SmmS3ResumeState->ReturnContext2, + (UINT32)SmmS3ResumeState->ReturnStackPointer + ); + } + + // + // Can not resume PEI Phase + // + DEBUG ((EFI_D_ERROR, "No context to return to PEI Phase\n")); + CpuDeadLoop (); +} + +/** + Initialize SMM S3 resume state structure used during S3 Resume. + + @param[in] Cr3 The base address of the page tables to use in SMM. + +**/ +VOID +InitSmmS3ResumeState ( + IN UINT32 Cr3 + ) +{ + VOID *GuidHob; + EFI_SMRAM_DESCRIPTOR *SmramDescriptor; + SMM_S3_RESUME_STATE *SmmS3ResumeState; + EFI_PHYSICAL_ADDRESS Address; + EFI_STATUS Status; + + if (!mAcpiS3Enable) { + return; + } + + GuidHob = GetFirstGuidHob (&gEfiAcpiVariableGuid); + if (GuidHob == NULL) { + DEBUG (( + DEBUG_ERROR, + "ERROR:%a(): HOB(gEfiAcpiVariableGuid=%g) needed by S3 resume doesn't exist!\n", + __FUNCTION__, + &gEfiAcpiVariableGuid + )); + CpuDeadLoop (); + } else { + SmramDescriptor = (EFI_SMRAM_DESCRIPTOR *) GET_GUID_HOB_DATA (GuidHob); + + DEBUG ((EFI_D_INFO, "SMM S3 SMRAM Structure = %x\n", SmramDescriptor)); + DEBUG ((EFI_D_INFO, "SMM S3 Structure = %x\n", SmramDescriptor->CpuStart)); + + SmmS3ResumeState = (SMM_S3_RESUME_STATE *)(UINTN)SmramDescriptor->CpuStart; + ZeroMem (SmmS3ResumeState, sizeof (SMM_S3_RESUME_STATE)); + + mSmmS3ResumeState = SmmS3ResumeState; + SmmS3ResumeState->Smst = (EFI_PHYSICAL_ADDRESS)(UINTN)gSmst; + + SmmS3ResumeState->SmmS3ResumeEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)SmmRestoreCpu; + + SmmS3ResumeState->SmmS3StackSize = SIZE_32KB; + SmmS3ResumeState->SmmS3StackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)SmmS3ResumeState->SmmS3StackSize)); + if (SmmS3ResumeState->SmmS3StackBase == 0) { + SmmS3ResumeState->SmmS3StackSize = 0; + } + + SmmS3ResumeState->SmmS3Cr0 = mSmmCr0; + SmmS3ResumeState->SmmS3Cr3 = Cr3; + SmmS3ResumeState->SmmS3Cr4 = mSmmCr4; + + if (sizeof (UINTN) == sizeof (UINT64)) { + SmmS3ResumeState->Signature = SMM_S3_RESUME_SMM_64; + } + if (sizeof (UINTN) == sizeof (UINT32)) { + SmmS3ResumeState->Signature = SMM_S3_RESUME_SMM_32; + } + + // + // Patch SmmS3ResumeState->SmmS3Cr3 + // + InitSmmS3Cr3 (); + } + + // + // Allocate safe memory in ACPI NVS for AP to execute hlt loop in + // protected mode on S3 path + // + Address = BASE_4GB - 1; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES (sizeof (mApHltLoopCodeTemplate)), + &Address + ); + ASSERT_EFI_ERROR (Status); + mApHltLoopCode = (UINT8 *) (UINTN) Address; +} + +/** + Copy register table from non-SMRAM into SMRAM. + + @param[in] DestinationRegisterTableList Points to destination register table. + @param[in] SourceRegisterTableList Points to source register table. + @param[in] NumberOfCpus Number of CPUs. + +**/ +VOID +CopyRegisterTable ( + IN CPU_REGISTER_TABLE *DestinationRegisterTableList, + IN CPU_REGISTER_TABLE *SourceRegisterTableList, + IN UINT32 NumberOfCpus + ) +{ + UINTN Index; + CPU_REGISTER_TABLE_ENTRY *RegisterTableEntry; + + CopyMem (DestinationRegisterTableList, SourceRegisterTableList, NumberOfCpus * sizeof (CPU_REGISTER_TABLE)); + for (Index = 0; Index < NumberOfCpus; Index++) { + if (DestinationRegisterTableList[Index].TableLength != 0) { + DestinationRegisterTableList[Index].AllocatedSize = DestinationRegisterTableList[Index].TableLength * sizeof (CPU_REGISTER_TABLE_ENTRY); + RegisterTableEntry = AllocateCopyPool ( + DestinationRegisterTableList[Index].AllocatedSize, + (VOID *)(UINTN)SourceRegisterTableList[Index].RegisterTableEntry + ); + ASSERT (RegisterTableEntry != NULL); + DestinationRegisterTableList[Index].RegisterTableEntry = (EFI_PHYSICAL_ADDRESS)(UINTN)RegisterTableEntry; + } + } +} + +/** + Check whether the register table is empty or not. + + @param[in] RegisterTable Point to the register table. + @param[in] NumberOfCpus Number of CPUs. + + @retval TRUE The register table is empty. + @retval FALSE The register table is not empty. +**/ +BOOLEAN +IsRegisterTableEmpty ( + IN CPU_REGISTER_TABLE *RegisterTable, + IN UINT32 NumberOfCpus + ) +{ + UINTN Index; + + if (RegisterTable != NULL) { + for (Index = 0; Index < NumberOfCpus; Index++) { + if (RegisterTable[Index].TableLength != 0) { + return FALSE; + } + } + } + + return TRUE; +} + +/** + Get ACPI CPU data. + +**/ +VOID +GetAcpiCpuData ( + VOID + ) +{ + ACPI_CPU_DATA *AcpiCpuData; + IA32_DESCRIPTOR *Gdtr; + IA32_DESCRIPTOR *Idtr; + VOID *GdtForAp; + VOID *IdtForAp; + VOID *MachineCheckHandlerForAp; + CPU_STATUS_INFORMATION *CpuStatus; + + if (!mAcpiS3Enable) { + return; + } + + // + // Prevent use of mAcpiCpuData by initialize NumberOfCpus to 0 + // + mAcpiCpuData.NumberOfCpus = 0; + + // + // If PcdCpuS3DataAddress was never set, then do not copy CPU S3 Data into SMRAM + // + AcpiCpuData = (ACPI_CPU_DATA *)(UINTN)PcdGet64 (PcdCpuS3DataAddress); + if (AcpiCpuData == 0) { + return; + } + + // + // For a native platform, copy the CPU S3 data into SMRAM for use on CPU S3 Resume. + // + CopyMem (&mAcpiCpuData, AcpiCpuData, sizeof (mAcpiCpuData)); + + mAcpiCpuData.MtrrTable = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (sizeof (MTRR_SETTINGS)); + ASSERT (mAcpiCpuData.MtrrTable != 0); + + CopyMem ((VOID *)(UINTN)mAcpiCpuData.MtrrTable, (VOID *)(UINTN)AcpiCpuData->MtrrTable, sizeof (MTRR_SETTINGS)); + + mAcpiCpuData.GdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (sizeof (IA32_DESCRIPTOR)); + ASSERT (mAcpiCpuData.GdtrProfile != 0); + + CopyMem ((VOID *)(UINTN)mAcpiCpuData.GdtrProfile, (VOID *)(UINTN)AcpiCpuData->GdtrProfile, sizeof (IA32_DESCRIPTOR)); + + mAcpiCpuData.IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (sizeof (IA32_DESCRIPTOR)); + ASSERT (mAcpiCpuData.IdtrProfile != 0); + + CopyMem ((VOID *)(UINTN)mAcpiCpuData.IdtrProfile, (VOID *)(UINTN)AcpiCpuData->IdtrProfile, sizeof (IA32_DESCRIPTOR)); + + if (!IsRegisterTableEmpty ((CPU_REGISTER_TABLE *)(UINTN)AcpiCpuData->PreSmmInitRegisterTable, mAcpiCpuData.NumberOfCpus)) { + mAcpiCpuData.PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (mAcpiCpuData.NumberOfCpus * sizeof (CPU_REGISTER_TABLE)); + ASSERT (mAcpiCpuData.PreSmmInitRegisterTable != 0); + + CopyRegisterTable ( + (CPU_REGISTER_TABLE *)(UINTN)mAcpiCpuData.PreSmmInitRegisterTable, + (CPU_REGISTER_TABLE *)(UINTN)AcpiCpuData->PreSmmInitRegisterTable, + mAcpiCpuData.NumberOfCpus + ); + } else { + mAcpiCpuData.PreSmmInitRegisterTable = 0; + } + + if (!IsRegisterTableEmpty ((CPU_REGISTER_TABLE *)(UINTN)AcpiCpuData->RegisterTable, mAcpiCpuData.NumberOfCpus)) { + mAcpiCpuData.RegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePool (mAcpiCpuData.NumberOfCpus * sizeof (CPU_REGISTER_TABLE)); + ASSERT (mAcpiCpuData.RegisterTable != 0); + + CopyRegisterTable ( + (CPU_REGISTER_TABLE *)(UINTN)mAcpiCpuData.RegisterTable, + (CPU_REGISTER_TABLE *)(UINTN)AcpiCpuData->RegisterTable, + mAcpiCpuData.NumberOfCpus + ); + } else { + mAcpiCpuData.RegisterTable = 0; + } + + // + // Copy AP's GDT, IDT and Machine Check handler into SMRAM. + // + Gdtr = (IA32_DESCRIPTOR *)(UINTN)mAcpiCpuData.GdtrProfile; + Idtr = (IA32_DESCRIPTOR *)(UINTN)mAcpiCpuData.IdtrProfile; + + GdtForAp = AllocatePool ((Gdtr->Limit + 1) + (Idtr->Limit + 1) + mAcpiCpuData.ApMachineCheckHandlerSize); + ASSERT (GdtForAp != NULL); + IdtForAp = (VOID *) ((UINTN)GdtForAp + (Gdtr->Limit + 1)); + MachineCheckHandlerForAp = (VOID *) ((UINTN)IdtForAp + (Idtr->Limit + 1)); + + CopyMem (GdtForAp, (VOID *)Gdtr->Base, Gdtr->Limit + 1); + CopyMem (IdtForAp, (VOID *)Idtr->Base, Idtr->Limit + 1); + CopyMem (MachineCheckHandlerForAp, (VOID *)(UINTN)mAcpiCpuData.ApMachineCheckHandlerBase, mAcpiCpuData.ApMachineCheckHandlerSize); + + Gdtr->Base = (UINTN)GdtForAp; + Idtr->Base = (UINTN)IdtForAp; + mAcpiCpuData.ApMachineCheckHandlerBase = (EFI_PHYSICAL_ADDRESS)(UINTN)MachineCheckHandlerForAp; + + CpuStatus = &mAcpiCpuData.CpuStatus; + CopyMem (CpuStatus, &AcpiCpuData->CpuStatus, sizeof (CPU_STATUS_INFORMATION)); + if (AcpiCpuData->CpuStatus.ThreadCountPerPackage != 0) { + CpuStatus->ThreadCountPerPackage = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateCopyPool ( + sizeof (UINT32) * CpuStatus->PackageCount, + (UINT32 *)(UINTN)AcpiCpuData->CpuStatus.ThreadCountPerPackage + ); + ASSERT (CpuStatus->ThreadCountPerPackage != 0); + } + if (AcpiCpuData->CpuStatus.ThreadCountPerCore != 0) { + CpuStatus->ThreadCountPerCore = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateCopyPool ( + sizeof (UINT8) * (CpuStatus->PackageCount * CpuStatus->MaxCoreCount), + (UINT32 *)(UINTN)AcpiCpuData->CpuStatus.ThreadCountPerCore + ); + ASSERT (CpuStatus->ThreadCountPerCore != 0); + } + if (AcpiCpuData->ApLocation != 0) { + mAcpiCpuData.ApLocation = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateCopyPool ( + mAcpiCpuData.NumberOfCpus * sizeof (EFI_CPU_PHYSICAL_LOCATION), + (EFI_CPU_PHYSICAL_LOCATION *)(UINTN)AcpiCpuData->ApLocation + ); + ASSERT (mAcpiCpuData.ApLocation != 0); + } + if (CpuStatus->PackageCount != 0) { + mCpuFlags.CoreSemaphoreCount = AllocateZeroPool ( + sizeof (UINT32) * CpuStatus->PackageCount * + CpuStatus->MaxCoreCount * CpuStatus->MaxThreadCount + ); + ASSERT (mCpuFlags.CoreSemaphoreCount != NULL); + mCpuFlags.PackageSemaphoreCount = AllocateZeroPool ( + sizeof (UINT32) * CpuStatus->PackageCount * + CpuStatus->MaxCoreCount * CpuStatus->MaxThreadCount + ); + ASSERT (mCpuFlags.PackageSemaphoreCount != NULL); + } + InitializeSpinLock((SPIN_LOCK*) &mCpuFlags.MemoryMappedLock); +} + +/** + Get ACPI S3 enable flag. + +**/ +VOID +GetAcpiS3EnableFlag ( + VOID + ) +{ + mAcpiS3Enable = PcdGetBool (PcdAcpiS3Enable); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/CpuService.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/CpuService.c new file mode 100644 index 00000000..79ff0ea3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/CpuService.c @@ -0,0 +1,366 @@ +/** @file +Implementation of SMM CPU Services Protocol. + +Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" + +// +// SMM CPU Service Protocol instance +// +EFI_SMM_CPU_SERVICE_PROTOCOL mSmmCpuService = { + SmmGetProcessorInfo, + SmmSwitchBsp, + SmmAddProcessor, + SmmRemoveProcessor, + SmmWhoAmI, + SmmRegisterExceptionHandler +}; + +/** + Gets processor information on the requested processor at the instant this call is made. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_INVALID_PARAMETER ProcessorNumber is invalid. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. + +**/ +EFI_STATUS +EFIAPI +SmmGetProcessorInfo ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ) +{ + // + // Check parameter + // + if (ProcessorNumber >= mMaxNumberOfCpus || ProcessorInfoBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId == INVALID_APIC_ID) { + return EFI_NOT_FOUND; + } + + // + // Fill in processor information + // + CopyMem (ProcessorInfoBuffer, &gSmmCpuPrivate->ProcessorInfo[ProcessorNumber], sizeof (EFI_PROCESSOR_INFORMATION)); + return EFI_SUCCESS; +} + +/** + This service switches the requested AP to be the BSP since the next SMI. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of AP that is to become the new BSP. + + @retval EFI_SUCCESS BSP will be switched in next SMI. + @retval EFI_UNSUPPORTED Switching the BSP or a processor to be hot-removed is not supported. + @retval EFI_NOT_FOUND The processor with the handle specified by ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber is invalid. +**/ +EFI_STATUS +EFIAPI +SmmSwitchBsp ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN UINTN ProcessorNumber + ) +{ + // + // Check parameter + // + if (ProcessorNumber >= mMaxNumberOfCpus) { + return EFI_INVALID_PARAMETER; + } + + if (gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId == INVALID_APIC_ID) { + return EFI_NOT_FOUND; + } + + if (gSmmCpuPrivate->Operation[ProcessorNumber] != SmmCpuNone || + gSmst->CurrentlyExecutingCpu == ProcessorNumber) { + return EFI_UNSUPPORTED; + } + + // + // Setting of the BSP for next SMI is pending until all SMI handlers are finished + // + gSmmCpuPrivate->Operation[ProcessorNumber] = SmmCpuSwitchBsp; + return EFI_SUCCESS; +} + +/** + Notify that a processor was hot-added. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param[in] ProcessorId Local APIC ID of the hot-added processor. + @param[out] ProcessorNumber The handle number of the hot-added processor. + + @retval EFI_SUCCESS The hot-addition of the specified processors was successfully notified. + @retval EFI_UNSUPPORTED Hot addition of processor is not supported. + @retval EFI_NOT_FOUND The processor with the handle specified by ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber is invalid. + @retval EFI_ALREADY_STARTED The processor is already online in the system. +**/ +EFI_STATUS +EFIAPI +SmmAddProcessor ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN UINT64 ProcessorId, + OUT UINTN *ProcessorNumber + ) +{ + UINTN Index; + + if (!FeaturePcdGet (PcdCpuHotPlugSupport)) { + return EFI_UNSUPPORTED; + } + + // + // Check parameter + // + if (ProcessorNumber == NULL || ProcessorId == INVALID_APIC_ID) { + return EFI_INVALID_PARAMETER; + } + + // + // Check if the processor already exists + // + + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == ProcessorId) { + return EFI_ALREADY_STARTED; + } + } + + // + // Check CPU hot plug data. The CPU RAS handler should have created the mapping + // of the APIC ID to SMBASE. + // + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (mCpuHotPlugData.ApicId[Index] == ProcessorId && + gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == INVALID_APIC_ID) { + gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId = ProcessorId; + gSmmCpuPrivate->ProcessorInfo[Index].StatusFlag = 0; + GetProcessorLocationByApicId ( + (UINT32)ProcessorId, + &gSmmCpuPrivate->ProcessorInfo[Index].Location.Package, + &gSmmCpuPrivate->ProcessorInfo[Index].Location.Core, + &gSmmCpuPrivate->ProcessorInfo[Index].Location.Thread + ); + + *ProcessorNumber = Index; + gSmmCpuPrivate->Operation[Index] = SmmCpuAdd; + return EFI_SUCCESS; + } + } + + return EFI_INVALID_PARAMETER; +} + +/** + Notify that a processor was hot-removed. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of the hot-added processor. + + @retval EFI_SUCCESS The hot-removal of the specified processors was successfully notified. + @retval EFI_UNSUPPORTED Hot removal of processor is not supported. + @retval EFI_UNSUPPORTED Hot removal of BSP is not supported. + @retval EFI_UNSUPPORTED Hot removal of a processor with pending hot-plug operation is not supported. + @retval EFI_INVALID_PARAMETER ProcessorNumber is invalid. +**/ +EFI_STATUS +EFIAPI +SmmRemoveProcessor ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN UINTN ProcessorNumber + ) +{ + if (!FeaturePcdGet (PcdCpuHotPlugSupport)) { + return EFI_UNSUPPORTED; + } + + // + // Check parameter + // + if (ProcessorNumber >= mMaxNumberOfCpus || + gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId == INVALID_APIC_ID) { + return EFI_INVALID_PARAMETER; + } + + // + // Can't remove BSP + // + if (ProcessorNumber == gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) { + return EFI_UNSUPPORTED; + } + + if (gSmmCpuPrivate->Operation[ProcessorNumber] != SmmCpuNone) { + return EFI_UNSUPPORTED; + } + + gSmmCpuPrivate->ProcessorInfo[ProcessorNumber].ProcessorId = INVALID_APIC_ID; + mCpuHotPlugData.ApicId[ProcessorNumber] = INVALID_APIC_ID; + + // + // Removal of the processor from the CPU list is pending until all SMI handlers are finished + // + gSmmCpuPrivate->Operation[ProcessorNumber] = SmmCpuRemove; + return EFI_SUCCESS; +} + +/** + This return the handle number for the calling processor. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param[out] ProcessorNumber The handle number of currently executing processor. + + @retval EFI_SUCCESS The current processor handle number was returned + in ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. + +**/ +EFI_STATUS +EFIAPI +SmmWhoAmI ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + OUT UINTN *ProcessorNumber + ) +{ + UINTN Index; + UINT64 ApicId; + + // + // Check parameter + // + if (ProcessorNumber == NULL) { + return EFI_INVALID_PARAMETER; + } + + ApicId = GetApicId (); + + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == ApicId) { + *ProcessorNumber = Index; + return EFI_SUCCESS; + } + } + // + // This should not happen + // + ASSERT (FALSE); + return EFI_NOT_FOUND; +} + +/** + Update the SMM CPU list per the pending operation. + + This function is called after return from SMI handlers. +**/ +VOID +SmmCpuUpdate ( + VOID + ) +{ + UINTN Index; + + // + // Handle pending BSP switch operations + // + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (gSmmCpuPrivate->Operation[Index] == SmmCpuSwitchBsp) { + gSmmCpuPrivate->Operation[Index] = SmmCpuNone; + mSmmMpSyncData->SwitchBsp = TRUE; + mSmmMpSyncData->CandidateBsp[Index] = TRUE; + } + } + + // + // Handle pending hot-add operations + // + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (gSmmCpuPrivate->Operation[Index] == SmmCpuAdd) { + gSmmCpuPrivate->Operation[Index] = SmmCpuNone; + mNumberOfCpus++; + } + } + + // + // Handle pending hot-remove operations + // + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (gSmmCpuPrivate->Operation[Index] == SmmCpuRemove) { + gSmmCpuPrivate->Operation[Index] = SmmCpuNone; + mNumberOfCpus--; + } + } +} + +/** + Register exception handler. + + @param This A pointer to the SMM_CPU_SERVICE_PROTOCOL instance. + @param ExceptionType Defines which interrupt or exception to hook. Type EFI_EXCEPTION_TYPE and + the valid values for this parameter are defined in EFI_DEBUG_SUPPORT_PROTOCOL + of the UEFI 2.0 specification. + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER + that is called when a processor interrupt occurs. + If this parameter is NULL, then the handler will be uninstalled. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported. + +**/ +EFI_STATUS +EFIAPI +SmmRegisterExceptionHandler ( + IN EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ) +{ + return RegisterCpuInterruptHandler (ExceptionType, InterruptHandler); +} + +/** + Initialize SMM CPU Services. + + It installs EFI SMM CPU Services Protocol. + + @param ImageHandle The firmware allocated handle for the EFI image. + + @retval EFI_SUCCESS EFI SMM CPU Services Protocol was installed successfully. +**/ +EFI_STATUS +InitializeSmmCpuServices ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + + Status = gSmst->SmmInstallProtocolInterface ( + &Handle, + &gEfiSmmCpuServiceProtocolGuid, + EFI_NATIVE_INTERFACE, + &mSmmCpuService + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/CpuService.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/CpuService.h new file mode 100644 index 00000000..efe90ffe --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/CpuService.h @@ -0,0 +1,175 @@ +/** @file +Include file for SMM CPU Services protocol implementation. + +Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_SERVICE_H_ +#define _CPU_SERVICE_H_ + +typedef enum { + SmmCpuNone, + SmmCpuAdd, + SmmCpuRemove, + SmmCpuSwitchBsp +} SMM_CPU_OPERATION; + +// +// SMM CPU Service Protocol function prototypes. +// + +/** + Gets processor information on the requested processor at the instant this call is made. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_INVALID_PARAMETER ProcessorNumber is invalid. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. + +**/ +EFI_STATUS +EFIAPI +SmmGetProcessorInfo ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ); + +/** + This service switches the requested AP to be the BSP since the next SMI. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of AP that is to become the new BSP. + + @retval EFI_SUCCESS BSP will be switched in next SMI. + @retval EFI_UNSUPPORTED Switching the BSP or a processor to be hot-removed is not supported. + @retval EFI_NOT_FOUND The processor with the handle specified by ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber is invalid. +**/ +EFI_STATUS +EFIAPI +SmmSwitchBsp ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN UINTN ProcessorNumber + ); + +/** + Notify that a processor was hot-added. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param[in] ProcessorId Local APIC ID of the hot-added processor. + @param[out] ProcessorNumber The handle number of the hot-added processor. + + @retval EFI_SUCCESS The hot-addition of the specified processors was successfully notified. + @retval EFI_UNSUPPORTED Hot addition of processor is not supported. + @retval EFI_NOT_FOUND The processor with the handle specified by ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber is invalid. + @retval EFI_ALREADY_STARTED The processor is already online in the system. +**/ +EFI_STATUS +EFIAPI +SmmAddProcessor ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN UINT64 ProcessorId, + OUT UINTN *ProcessorNumber + ); + +/** + Notify that a processor was hot-removed. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of the hot-added processor. + + @retval EFI_SUCCESS The hot-removal of the specified processors was successfully notified. + @retval EFI_UNSUPPORTED Hot removal of processor is not supported. + @retval EFI_UNSUPPORTED Hot removal of BSP is not supported. + @retval EFI_UNSUPPORTED Hot removal of a processor with pending hot-plug operation is not supported. + @retval EFI_INVALID_PARAMETER ProcessorNumber is invalid. +**/ +EFI_STATUS +EFIAPI +SmmRemoveProcessor ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN UINTN ProcessorNumber + ); + +/** + This return the handle number for the calling processor. + + @param[in] This A pointer to the EFI_SMM_CPU_SERVICE_PROTOCOL instance. + @param[out] ProcessorNumber The handle number of currently executing processor. + + @retval EFI_SUCCESS The current processor handle number was returned + in ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. + +**/ +EFI_STATUS +EFIAPI +SmmWhoAmI ( + IN CONST EFI_SMM_CPU_SERVICE_PROTOCOL *This, + OUT UINTN *ProcessorNumber + ); + +/** + Register exception handler. + + @param This A pointer to the SMM_CPU_SERVICE_PROTOCOL instance. + @param ExceptionType Defines which interrupt or exception to hook. Type EFI_EXCEPTION_TYPE and + the valid values for this parameter are defined in EFI_DEBUG_SUPPORT_PROTOCOL + of the UEFI 2.0 specification. + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER + that is called when a processor interrupt occurs. + If this parameter is NULL, then the handler will be uninstalled. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported. + +**/ +EFI_STATUS +EFIAPI +SmmRegisterExceptionHandler ( + IN EFI_SMM_CPU_SERVICE_PROTOCOL *This, + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ); + +// +// Internal function prototypes +// + +/** + Update the SMM CPU list per the pending operation. + + This function is called after return from SMI handlers. +**/ +VOID +SmmCpuUpdate ( + VOID + ); + +/** + Initialize SMM CPU Services. + + It installs EFI SMM CPU Services Protocol. + + @param ImageHandle The firmware allocated handle for the EFI image. + + @retval EFI_SUCCESS EFI SMM CPU Services Protocol was installed successfully. +**/ +EFI_STATUS +InitializeSmmCpuServices ( + IN EFI_HANDLE Handle + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/Cet.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/Cet.nasm new file mode 100644 index 00000000..9862fd78 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/Cet.nasm @@ -0,0 +1,33 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------- + +%include "Nasm.inc" + +SECTION .text + +global ASM_PFX(DisableCet) +ASM_PFX(DisableCet): + + ; Skip the pushed data for call + mov eax, 1 + INCSSP_EAX + + mov eax, cr4 + btr eax, 23 ; clear CET + mov cr4, eax + ret + +global ASM_PFX(EnableCet) +ASM_PFX(EnableCet): + + mov eax, cr4 + bts eax, 23 ; set CET + mov cr4, eax + + ; use jmp to skip the check for ret + pop eax + jmp eax + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/MpFuncs.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/MpFuncs.nasm new file mode 100644 index 00000000..7444505f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/MpFuncs.nasm @@ -0,0 +1,153 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; MpFuncs.nasm +; +; Abstract: +; +; This is the assembly code for Multi-processor S3 support +; +;------------------------------------------------------------------------------- + +SECTION .text + +extern ASM_PFX(InitializeFloatingPointUnits) + +%define VacantFlag 0x0 +%define NotVacantFlag 0xff + +%define LockLocation RendezvousFunnelProcEnd - RendezvousFunnelProcStart +%define StackStart LockLocation + 0x4 +%define StackSize LockLocation + 0x8 +%define RendezvousProc LockLocation + 0xC +%define GdtrProfile LockLocation + 0x10 +%define IdtrProfile LockLocation + 0x16 +%define BufferStart LockLocation + 0x1C + +;------------------------------------------------------------------------------------- +;RendezvousFunnelProc procedure follows. All APs execute their procedure. This +;procedure serializes all the AP processors through an Init sequence. It must be +;noted that APs arrive here very raw...ie: real mode, no stack. +;ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC +;IS IN MACHINE CODE. +;------------------------------------------------------------------------------------- +;RendezvousFunnelProc (&WakeUpBuffer,MemAddress); + +BITS 16 +global ASM_PFX(RendezvousFunnelProc) +ASM_PFX(RendezvousFunnelProc): +RendezvousFunnelProcStart: + +; At this point CS = 0x(vv00) and ip= 0x0. + + mov ax, cs + mov ds, ax + mov es, ax + mov ss, ax + xor ax, ax + mov fs, ax + mov gs, ax + +flat32Start: + + mov si, BufferStart + mov edx,dword [si] ; EDX is keeping the start address of wakeup buffer + + mov si, GdtrProfile +o32 lgdt [cs:si] + + mov si, IdtrProfile +o32 lidt [cs:si] + + xor ax, ax + mov ds, ax + + mov eax, cr0 ; Get control register 0 + or eax, 0x000000001 ; Set PE bit (bit #0) + mov cr0, eax + +FLAT32_JUMP: + +a32 jmp dword 0x20:0x0 + +BITS 32 +PMODE_ENTRY: ; protected mode entry point + + mov ax, 0x8 +o16 mov ds, ax +o16 mov es, ax +o16 mov fs, ax +o16 mov gs, ax +o16 mov ss, ax ; Flat mode setup. + + mov esi, edx + + mov edi, esi + add edi, LockLocation + mov al, NotVacantFlag +TestLock: + xchg byte [edi], al + cmp al, NotVacantFlag + jz TestLock + +ProgramStack: + + mov edi, esi + add edi, StackSize + mov eax, dword [edi] + mov edi, esi + add edi, StackStart + add eax, dword [edi] + mov esp, eax + mov dword [edi], eax + +Releaselock: + + mov al, VacantFlag + mov edi, esi + add edi, LockLocation + xchg byte [edi], al + + ; + ; Call assembly function to initialize FPU. + ; + mov ebx, ASM_PFX(InitializeFloatingPointUnits) + call ebx + ; + ; Call C Function + ; + mov edi, esi + add edi, RendezvousProc + mov eax, dword [edi] + + test eax, eax + jz GoToSleep + call eax ; Call C function + +GoToSleep: + cli + hlt + jmp $-2 + +RendezvousFunnelProcEnd: +;------------------------------------------------------------------------------------- +; AsmGetAddressMap (&AddressMap); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmGetAddressMap) +ASM_PFX(AsmGetAddressMap): + + pushad + mov ebp,esp + + mov ebx, dword [ebp+0x24] + mov dword [ebx], RendezvousFunnelProcStart + mov dword [ebx+0x4], PMODE_ENTRY - RendezvousFunnelProcStart + mov dword [ebx+0x8], FLAT32_JUMP - RendezvousFunnelProcStart + mov dword [ebx+0xc], RendezvousFunnelProcEnd - RendezvousFunnelProcStart + + popad + ret + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c new file mode 100644 index 00000000..628d8c37 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c @@ -0,0 +1,374 @@ +/** @file +Page table manipulation functions for IA-32 processors + +Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" + +/** + Disable CET. +**/ +VOID +EFIAPI +DisableCet ( + VOID + ); + +/** + Enable CET. +**/ +VOID +EFIAPI +EnableCet ( + VOID + ); + +/** + Get page table base address and the depth of the page table. + + @param[out] Base Page table base address. + @param[out] FiveLevels TRUE means 5 level paging. FALSE means 4 level paging. +**/ +VOID +GetPageTable ( + OUT UINTN *Base, + OUT BOOLEAN *FiveLevels OPTIONAL + ) +{ + *Base = ((mInternalCr3 == 0) ? + (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64) : + mInternalCr3); + if (FiveLevels != NULL) { + *FiveLevels = FALSE; + } +} + +/** + Create PageTable for SMM use. + + @return PageTable Address + +**/ +UINT32 +SmmInitPageTable ( + VOID + ) +{ + UINTN PageFaultHandlerHookAddress; + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + EFI_STATUS Status; + + // + // Initialize spin lock + // + InitializeSpinLock (mPFLock); + + mPhysicalAddressBits = 32; + + if (FeaturePcdGet (PcdCpuSmmProfileEnable) || + HEAP_GUARD_NONSTOP_MODE || + NULL_DETECTION_NONSTOP_MODE) { + // + // Set own Page Fault entry instead of the default one, because SMM Profile + // feature depends on IRET instruction to do Single Step + // + PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile; + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base; + IdtEntry += EXCEPT_IA32_PAGE_FAULT; + IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress; + IdtEntry->Bits.Reserved_0 = 0; + IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; + IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16); + } else { + // + // Register SMM Page Fault Handler + // + Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler); + ASSERT_EFI_ERROR (Status); + } + + // + // Additional SMM IDT initialization for SMM stack guard + // + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + InitializeIDTSmmStackGuard (); + } + return Gen4GPageTable (TRUE); +} + +/** + Page Fault handler for SMM use. + +**/ +VOID +SmiDefaultPFHandler ( + VOID + ) +{ + CpuDeadLoop (); +} + +/** + ThePage Fault handler wrapper for SMM use. + + @param InterruptType Defines the type of interrupt or exception that + occurred on the processor.This parameter is processor architecture specific. + @param SystemContext A pointer to the processor context when + the interrupt occurred on the processor. +**/ +VOID +EFIAPI +SmiPFHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN PFAddress; + UINTN GuardPageAddress; + UINTN CpuIndex; + + ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT); + + AcquireSpinLock (mPFLock); + + PFAddress = AsmReadCr2 (); + + // + // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page, + // or SMM page protection violation. + // + if ((PFAddress >= mCpuHotPlugData.SmrrBase) && + (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) { + DumpCpuContext (InterruptType, SystemContext); + CpuIndex = GetCpuIndex (); + GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * mSmmStackSize); + if ((FeaturePcdGet (PcdCpuSmmStackGuard)) && + (PFAddress >= GuardPageAddress) && + (PFAddress < (GuardPageAddress + EFI_PAGE_SIZE))) { + DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n")); + } else { + if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) { + DEBUG ((DEBUG_ERROR, "SMM exception at execution (0x%x)\n", PFAddress)); + DEBUG_CODE ( + DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp); + ); + } else { + DEBUG ((DEBUG_ERROR, "SMM exception at access (0x%x)\n", PFAddress)); + DEBUG_CODE ( + DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip); + ); + } + + if (HEAP_GUARD_NONSTOP_MODE) { + GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData); + goto Exit; + } + } + CpuDeadLoop (); + goto Exit; + } + + // + // If a page fault occurs in non-SMRAM range. + // + if ((PFAddress < mCpuHotPlugData.SmrrBase) || + (PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) { + if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) { + DumpCpuContext (InterruptType, SystemContext); + DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress)); + DEBUG_CODE ( + DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp); + ); + CpuDeadLoop (); + goto Exit; + } + + // + // If NULL pointer was just accessed + // + if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0 && + (PFAddress < EFI_PAGE_SIZE)) { + DumpCpuContext (InterruptType, SystemContext); + DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n")); + DEBUG_CODE ( + DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip); + ); + + if (NULL_DETECTION_NONSTOP_MODE) { + GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData); + goto Exit; + } + + CpuDeadLoop (); + goto Exit; + } + + if (IsSmmCommBufferForbiddenAddress (PFAddress)) { + DumpCpuContext (InterruptType, SystemContext); + DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%x)!\n", PFAddress)); + DEBUG_CODE ( + DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip); + ); + CpuDeadLoop (); + goto Exit; + } + } + + if (FeaturePcdGet (PcdCpuSmmProfileEnable)) { + SmmProfilePFHandler ( + SystemContext.SystemContextIa32->Eip, + SystemContext.SystemContextIa32->ExceptionData + ); + } else { + DumpCpuContext (InterruptType, SystemContext); + SmiDefaultPFHandler (); + } + +Exit: + ReleaseSpinLock (mPFLock); +} + +/** + This function sets memory attribute for page table. +**/ +VOID +SetPageTableAttributes ( + VOID + ) +{ + UINTN Index2; + UINTN Index3; + UINT64 *L1PageTable; + UINT64 *L2PageTable; + UINT64 *L3PageTable; + UINTN PageTableBase; + BOOLEAN IsSplitted; + BOOLEAN PageTableSplitted; + BOOLEAN CetEnabled; + + // + // Don't mark page table to read-only if heap guard is enabled. + // + // BIT2: SMM page guard enabled + // BIT3: SMM pool guard enabled + // + if ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) { + DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as heap guard is enabled\n")); + return ; + } + + // + // Don't mark page table to read-only if SMM profile is enabled. + // + if (FeaturePcdGet (PcdCpuSmmProfileEnable)) { + DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as SMM profile is enabled\n")); + return ; + } + + DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n")); + + // + // Disable write protection, because we need mark page table to be write protected. + // We need *write* page table memory, to mark itself to be *read only*. + // + CetEnabled = ((AsmReadCr4() & CR4_CET_ENABLE) != 0) ? TRUE : FALSE; + if (CetEnabled) { + // + // CET must be disabled if WP is disabled. + // + DisableCet(); + } + AsmWriteCr0 (AsmReadCr0() & ~CR0_WP); + + do { + DEBUG ((DEBUG_INFO, "Start...\n")); + PageTableSplitted = FALSE; + + GetPageTable (&PageTableBase, NULL); + L3PageTable = (UINT64 *)PageTableBase; + + SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)PageTableBase, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); + PageTableSplitted = (PageTableSplitted || IsSplitted); + + for (Index3 = 0; Index3 < 4; Index3++) { + L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); + if (L2PageTable == NULL) { + continue; + } + + SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); + PageTableSplitted = (PageTableSplitted || IsSplitted); + + for (Index2 = 0; Index2 < SIZE_4KB/sizeof(UINT64); Index2++) { + if ((L2PageTable[Index2] & IA32_PG_PS) != 0) { + // 2M + continue; + } + L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); + if (L1PageTable == NULL) { + continue; + } + SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); + PageTableSplitted = (PageTableSplitted || IsSplitted); + } + } + } while (PageTableSplitted); + + // + // Enable write protection, after page table updated. + // + AsmWriteCr0 (AsmReadCr0() | CR0_WP); + if (CetEnabled) { + // + // re-enable CET. + // + EnableCet(); + } + + return ; +} + +/** + This function returns with no action for 32 bit. + + @param[out] *Cr2 Pointer to variable to hold CR2 register value. +**/ +VOID +SaveCr2 ( + OUT UINTN *Cr2 + ) +{ + return ; +} + +/** + This function returns with no action for 32 bit. + + @param[in] Cr2 Value to write into CR2 register. +**/ +VOID +RestoreCr2 ( + IN UINTN Cr2 + ) +{ + return ; +} + +/** + Return whether access to non-SMRAM is restricted. + + @retval TRUE Access to non-SMRAM is restricted. + @retval FALSE Access to non-SMRAM is not restricted. +**/ +BOOLEAN +IsRestrictedMemoryAccess ( + VOID + ) +{ + return TRUE; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/Semaphore.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/Semaphore.c new file mode 100644 index 00000000..020dc782 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/Semaphore.c @@ -0,0 +1,42 @@ +/** @file +Semaphore mechanism to indicate to the BSP that an AP has exited SMM +after SMBASE relocation. + +Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" + +UINTN mSmmRelocationOriginalAddress; +volatile BOOLEAN *mRebasedFlag; + +/** + Hook return address of SMM Save State so that semaphore code + can be executed immediately after AP exits SMM to indicate to + the BSP that an AP has exited SMM after SMBASE relocation. + + @param[in] CpuIndex The processor index. + @param[in] RebasedFlag A pointer to a flag that is set to TRUE + immediately after AP exits SMM. + +**/ +VOID +SemaphoreHook ( + IN UINTN CpuIndex, + IN volatile BOOLEAN *RebasedFlag + ) +{ + SMRAM_SAVE_STATE_MAP *CpuState; + + mRebasedFlag = RebasedFlag; + + CpuState = (SMRAM_SAVE_STATE_MAP *)(UINTN)(SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET); + mSmmRelocationOriginalAddress = (UINTN)HookReturnFromSmm ( + CpuIndex, + CpuState, + (UINT64)(UINTN)&SmmRelocationSemaphoreComplete, + (UINT64)(UINTN)&SmmRelocationSemaphoreComplete + ); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmiEntry.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmiEntry.nasm new file mode 100644 index 00000000..84057170 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmiEntry.nasm @@ -0,0 +1,314 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR> +; Copyright (c) 2020, AMD Incorporated. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; SmiEntry.nasm +; +; Abstract: +; +; Code template of the SMI handler for a particular processor +; +;------------------------------------------------------------------------------- + +%include "StuffRsbNasm.inc" +%include "Nasm.inc" + +%define MSR_IA32_S_CET 0x6A2 +%define MSR_IA32_CET_SH_STK_EN 0x1 +%define MSR_IA32_CET_WR_SHSTK_EN 0x2 +%define MSR_IA32_CET_ENDBR_EN 0x4 +%define MSR_IA32_CET_LEG_IW_EN 0x8 +%define MSR_IA32_CET_NO_TRACK_EN 0x10 +%define MSR_IA32_CET_SUPPRESS_DIS 0x20 +%define MSR_IA32_CET_SUPPRESS 0x400 +%define MSR_IA32_CET_TRACKER 0x800 +%define MSR_IA32_PL0_SSP 0x6A4 + +%define CR4_CET 0x800000 + +%define MSR_IA32_MISC_ENABLE 0x1A0 +%define MSR_EFER 0xc0000080 +%define MSR_EFER_XD 0x800 + +; +; Constants relating to PROCESSOR_SMM_DESCRIPTOR +; +%define DSC_OFFSET 0xfb00 +%define DSC_GDTPTR 0x30 +%define DSC_GDTSIZ 0x38 +%define DSC_CS 14 +%define DSC_DS 16 +%define DSC_SS 18 +%define DSC_OTHERSEG 20 + +%define PROTECT_MODE_CS 0x8 +%define PROTECT_MODE_DS 0x20 +%define TSS_SEGMENT 0x40 + +extern ASM_PFX(SmiRendezvous) +extern ASM_PFX(FeaturePcdGet (PcdCpuSmmStackGuard)) +extern ASM_PFX(CpuSmmDebugEntry) +extern ASM_PFX(CpuSmmDebugExit) + +global ASM_PFX(gcSmiHandlerTemplate) +global ASM_PFX(gcSmiHandlerSize) +global ASM_PFX(gPatchSmiCr3) +global ASM_PFX(gPatchSmiStack) +global ASM_PFX(gPatchSmbase) +extern ASM_PFX(mXdSupported) +global ASM_PFX(gPatchXdSupported) +global ASM_PFX(gPatchMsrIa32MiscEnableSupported) +extern ASM_PFX(gSmiHandlerIdtr) + +extern ASM_PFX(mCetSupported) +global ASM_PFX(mPatchCetSupported) +global ASM_PFX(mPatchCetPl0Ssp) +global ASM_PFX(mPatchCetInterruptSsp) + + SECTION .text + +BITS 16 +ASM_PFX(gcSmiHandlerTemplate): +_SmiEntryPoint: + mov bx, _GdtDesc - _SmiEntryPoint + 0x8000 + mov ax,[cs:DSC_OFFSET + DSC_GDTSIZ] + dec ax + mov [cs:bx], ax + mov eax, [cs:DSC_OFFSET + DSC_GDTPTR] + mov [cs:bx + 2], eax + mov ebp, eax ; ebp = GDT base +o32 lgdt [cs:bx] ; lgdt fword ptr cs:[bx] + mov ax, PROTECT_MODE_CS + mov [cs:bx-0x2],ax + mov edi, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmbase): + lea eax, [edi + (@32bit - _SmiEntryPoint) + 0x8000] + mov [cs:bx-0x6],eax + mov ebx, cr0 + and ebx, 0x9ffafff3 + or ebx, 0x23 + mov cr0, ebx + jmp dword 0x0:0x0 +_GdtDesc: + DW 0 + DD 0 + +BITS 32 +@32bit: + mov ax, PROTECT_MODE_DS +o16 mov ds, ax +o16 mov es, ax +o16 mov fs, ax +o16 mov gs, ax +o16 mov ss, ax + mov esp, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmiStack): + mov eax, ASM_PFX(gSmiHandlerIdtr) + lidt [eax] + jmp ProtFlatMode + +ProtFlatMode: + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmiCr3): + mov cr3, eax +; +; Need to test for CR4 specific bit support +; + mov eax, 1 + cpuid ; use CPUID to determine if specific CR4 bits are supported + xor eax, eax ; Clear EAX + test edx, BIT2 ; Check for DE capabilities + jz .0 + or eax, BIT3 +.0: + test edx, BIT6 ; Check for PAE capabilities + jz .1 + or eax, BIT5 +.1: + test edx, BIT7 ; Check for MCE capabilities + jz .2 + or eax, BIT6 +.2: + test edx, BIT24 ; Check for FXSR capabilities + jz .3 + or eax, BIT9 +.3: + test edx, BIT25 ; Check for SSE capabilities + jz .4 + or eax, BIT10 +.4: ; as cr4.PGE is not set here, refresh cr3 + mov cr4, eax ; in PreModifyMtrrs() to flush TLB. + + cmp byte [dword ASM_PFX(FeaturePcdGet (PcdCpuSmmStackGuard))], 0 + jz .6 +; Load TSS + mov byte [ebp + TSS_SEGMENT + 5], 0x89 ; clear busy flag + mov eax, TSS_SEGMENT + ltr ax +.6: + +; enable NXE if supported + mov al, strict byte 1 ; source operand may be patched +ASM_PFX(gPatchXdSupported): + cmp al, 0 + jz @SkipXd + +; If MSR_IA32_MISC_ENABLE is supported, clear XD Disable bit + mov al, strict byte 1 ; source operand may be patched +ASM_PFX(gPatchMsrIa32MiscEnableSupported): + cmp al, 1 + jz MsrIa32MiscEnableSupported + +; MSR_IA32_MISC_ENABLE not supported + xor edx, edx + push edx ; don't try to restore the XD Disable bit just before RSM + jmp EnableNxe + +; +; Check XD disable bit +; +MsrIa32MiscEnableSupported: + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + push edx ; save MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 ; MSR_IA32_MISC_ENABLE[34] + jz EnableNxe + and dx, 0xFFFB ; clear XD Disable bit if it is set + wrmsr +EnableNxe: + mov ecx, MSR_EFER + rdmsr + or ax, MSR_EFER_XD ; enable NXE + wrmsr + jmp @XdDone +@SkipXd: + sub esp, 4 +@XdDone: + + mov ebx, cr0 + or ebx, 0x80010023 ; enable paging + WP + NE + MP + PE + mov cr0, ebx + lea ebx, [edi + DSC_OFFSET] + mov ax, [ebx + DSC_DS] + mov ds, eax + mov ax, [ebx + DSC_OTHERSEG] + mov es, eax + mov fs, eax + mov gs, eax + mov ax, [ebx + DSC_SS] + mov ss, eax + + mov ebx, [esp + 4] ; ebx <- CpuIndex + +; enable CET if supported + mov al, strict byte 1 ; source operand may be patched +ASM_PFX(mPatchCetSupported): + cmp al, 0 + jz CetDone + + mov ecx, MSR_IA32_S_CET + rdmsr + push edx + push eax + + mov ecx, MSR_IA32_PL0_SSP + rdmsr + push edx + push eax + + mov ecx, MSR_IA32_S_CET + mov eax, MSR_IA32_CET_SH_STK_EN + xor edx, edx + wrmsr + + mov ecx, MSR_IA32_PL0_SSP + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(mPatchCetPl0Ssp): + xor edx, edx + wrmsr + mov ecx, cr0 + btr ecx, 16 ; clear WP + mov cr0, ecx + mov [eax], eax ; reload SSP, and clear busyflag. + xor ecx, ecx + mov [eax + 4], ecx + + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(mPatchCetInterruptSsp): + cmp eax, 0 + jz CetInterruptDone + mov [eax], eax ; reload SSP, and clear busyflag. + xor ecx, ecx + mov [eax + 4], ecx +CetInterruptDone: + + mov ecx, cr0 + bts ecx, 16 ; set WP + mov cr0, ecx + + mov eax, 0x668 | CR4_CET + mov cr4, eax + + SETSSBSY + +CetDone: + + push ebx + mov eax, ASM_PFX(CpuSmmDebugEntry) + call eax + add esp, 4 + + push ebx + mov eax, ASM_PFX(SmiRendezvous) + call eax + add esp, 4 + + push ebx + mov eax, ASM_PFX(CpuSmmDebugExit) + call eax + add esp, 4 + + mov eax, ASM_PFX(mCetSupported) + mov al, [eax] + cmp al, 0 + jz CetDone2 + + mov eax, 0x668 + mov cr4, eax ; disable CET + + mov ecx, MSR_IA32_PL0_SSP + pop eax + pop edx + wrmsr + + mov ecx, MSR_IA32_S_CET + pop eax + pop edx + wrmsr +CetDone2: + + mov eax, ASM_PFX(mXdSupported) + mov al, [eax] + cmp al, 0 + jz .7 + pop edx ; get saved MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 + jz .7 + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + or dx, BIT2 ; set XD Disable bit if it was set before entering into SMM + wrmsr + +.7: + + StuffRsb32 + rsm + +ASM_PFX(gcSmiHandlerSize): DW $ - _SmiEntryPoint + +global ASM_PFX(PiSmmCpuSmiEntryFixupAddress) +ASM_PFX(PiSmmCpuSmiEntryFixupAddress): + ret diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmiException.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmiException.nasm new file mode 100644 index 00000000..bcd5a007 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmiException.nasm @@ -0,0 +1,705 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; SmiException.nasm +; +; Abstract: +; +; Exception handlers used in SM mode +; +;------------------------------------------------------------------------------- + +extern ASM_PFX(FeaturePcdGet (PcdCpuSmmProfileEnable)) +extern ASM_PFX(SmiPFHandler) +extern ASM_PFX(mSetupDebugTrap) + +global ASM_PFX(gcSmiIdtr) +global ASM_PFX(gcSmiGdtr) +global ASM_PFX(gTaskGateDescriptor) +global ASM_PFX(gcPsd) + + SECTION .data + +NullSeg: DQ 0 ; reserved by architecture +CodeSeg32: + DW -1 ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x9b + DB 0xcf ; LimitHigh + DB 0 ; BaseHigh +ProtModeCodeSeg32: + DW -1 ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x9b + DB 0xcf ; LimitHigh + DB 0 ; BaseHigh +ProtModeSsSeg32: + DW -1 ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x93 + DB 0xcf ; LimitHigh + DB 0 ; BaseHigh +DataSeg32: + DW -1 ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x93 + DB 0xcf ; LimitHigh + DB 0 ; BaseHigh +CodeSeg16: + DW -1 + DW 0 + DB 0 + DB 0x9b + DB 0x8f + DB 0 +DataSeg16: + DW -1 + DW 0 + DB 0 + DB 0x93 + DB 0x8f + DB 0 +CodeSeg64: + DW -1 ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x9b + DB 0xaf ; LimitHigh + DB 0 ; BaseHigh +GDT_SIZE equ $ - NullSeg + +TssSeg: + DW TSS_DESC_SIZE ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x89 + DB 0x80 ; LimitHigh + DB 0 ; BaseHigh +ExceptionTssSeg: + DW EXCEPTION_TSS_DESC_SIZE ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x89 + DB 0x80 ; LimitHigh + DB 0 ; BaseHigh + +CODE_SEL equ CodeSeg32 - NullSeg +DATA_SEL equ DataSeg32 - NullSeg +TSS_SEL equ TssSeg - NullSeg +EXCEPTION_TSS_SEL equ ExceptionTssSeg - NullSeg + +struc IA32_TSS + resw 1 + resw 1 + .ESP0: resd 1 + .SS0: resw 1 + resw 1 + .ESP1: resd 1 + .SS1: resw 1 + resw 1 + .ESP2: resd 1 + .SS2: resw 1 + resw 1 + ._CR3: resd 1 + .EIP: resd 1 + .EFLAGS: resd 1 + ._EAX: resd 1 + ._ECX: resd 1 + ._EDX: resd 1 + ._EBX: resd 1 + ._ESP: resd 1 + ._EBP: resd 1 + ._ESI: resd 1 + ._EDI: resd 1 + ._ES: resw 1 + resw 1 + ._CS: resw 1 + resw 1 + ._SS: resw 1 + resw 1 + ._DS: resw 1 + resw 1 + ._FS: resw 1 + resw 1 + ._GS: resw 1 + resw 1 + .LDT: resw 1 + resw 1 + resw 1 + resw 1 +endstruc + +; Create 2 TSS segments just after GDT +TssDescriptor: + DW 0 ; PreviousTaskLink + DW 0 ; Reserved + DD 0 ; ESP0 + DW 0 ; SS0 + DW 0 ; Reserved + DD 0 ; ESP1 + DW 0 ; SS1 + DW 0 ; Reserved + DD 0 ; ESP2 + DW 0 ; SS2 + DW 0 ; Reserved + DD 0 ; CR3 + DD 0 ; EIP + DD 0 ; EFLAGS + DD 0 ; EAX + DD 0 ; ECX + DD 0 ; EDX + DD 0 ; EBX + DD 0 ; ESP + DD 0 ; EBP + DD 0 ; ESI + DD 0 ; EDI + DW 0 ; ES + DW 0 ; Reserved + DW 0 ; CS + DW 0 ; Reserved + DW 0 ; SS + DW 0 ; Reserved + DW 0 ; DS + DW 0 ; Reserved + DW 0 ; FS + DW 0 ; Reserved + DW 0 ; GS + DW 0 ; Reserved + DW 0 ; LDT Selector + DW 0 ; Reserved + DW 0 ; T + DW 0 ; I/O Map Base +TSS_DESC_SIZE equ $ - TssDescriptor + +ExceptionTssDescriptor: + DW 0 ; PreviousTaskLink + DW 0 ; Reserved + DD 0 ; ESP0 + DW 0 ; SS0 + DW 0 ; Reserved + DD 0 ; ESP1 + DW 0 ; SS1 + DW 0 ; Reserved + DD 0 ; ESP2 + DW 0 ; SS2 + DW 0 ; Reserved + DD 0 ; CR3 + DD PFHandlerEntry ; EIP + DD 00000002 ; EFLAGS + DD 0 ; EAX + DD 0 ; ECX + DD 0 ; EDX + DD 0 ; EBX + DD 0 ; ESP + DD 0 ; EBP + DD 0 ; ESI + DD 0 ; EDI + DW DATA_SEL ; ES + DW 0 ; Reserved + DW CODE_SEL ; CS + DW 0 ; Reserved + DW DATA_SEL ; SS + DW 0 ; Reserved + DW DATA_SEL ; DS + DW 0 ; Reserved + DW DATA_SEL ; FS + DW 0 ; Reserved + DW DATA_SEL ; GS + DW 0 ; Reserved + DW 0 ; LDT Selector + DW 0 ; Reserved + DW 0 ; T + DW 0 ; I/O Map Base + DD 0 ; SSP +EXCEPTION_TSS_DESC_SIZE equ $ - ExceptionTssDescriptor + +ASM_PFX(gcPsd): + DB 'PSDSIG ' + DW PSD_SIZE + DW 2 + DW 1 << 2 + DW CODE_SEL + DW DATA_SEL + DW DATA_SEL + DW DATA_SEL + DW 0 + DQ 0 + DQ 0 + DQ 0 + DD 0 + DD NullSeg + DD GDT_SIZE + DD 0 + times 24 DB 0 + DD 0 + DD 0 +PSD_SIZE equ $ - ASM_PFX(gcPsd) + +ASM_PFX(gcSmiGdtr): + DW GDT_SIZE - 1 + DD NullSeg + +ASM_PFX(gcSmiIdtr): + DW 0 + DD 0 + +ASM_PFX(gTaskGateDescriptor): + DW 0 ; Reserved + DW EXCEPTION_TSS_SEL ; TSS Segment selector + DB 0 ; Reserved + DB 0x85 ; Task Gate, present, DPL = 0 + DW 0 ; Reserved + + SECTION .text +;------------------------------------------------------------------------------ +; PageFaultIdtHandlerSmmProfile is the entry point page fault only +; +; +; Stack: +; +---------------------+ +; + EFlags + +; +---------------------+ +; + CS + +; +---------------------+ +; + EIP + +; +---------------------+ +; + Error Code + +; +---------------------+ +; + Vector Number + +; +---------------------+ +; + EBP + +; +---------------------+ <-- EBP +; +; +;------------------------------------------------------------------------------ +global ASM_PFX(PageFaultIdtHandlerSmmProfile) +ASM_PFX(PageFaultIdtHandlerSmmProfile): + push 0xe ; Page Fault + + push ebp + mov ebp, esp + + ; + ; Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32 + ; is 16-byte aligned + ; + and esp, 0xfffffff0 + sub esp, 12 + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + push eax + push ecx + push edx + push ebx + lea ecx, [ebp + 6 * 4] + push ecx ; ESP + push dword [ebp] ; EBP + push esi + push edi + +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; + mov eax, ss + push eax + movzx eax, word [ebp + 4 * 4] + push eax + mov eax, ds + push eax + mov eax, es + push eax + mov eax, fs + push eax + mov eax, gs + push eax + +;; UINT32 Eip; + mov eax, [ebp + 3 * 4] + push eax + +;; UINT32 Gdtr[2], Idtr[2]; + sub esp, 8 + sidt [esp] + mov eax, [esp + 2] + xchg eax, [esp] + and eax, 0xFFFF + mov [esp+4], eax + + sub esp, 8 + sgdt [esp] + mov eax, [esp + 2] + xchg eax, [esp] + and eax, 0xFFFF + mov [esp+4], eax + +;; UINT32 Ldtr, Tr; + xor eax, eax + str ax + push eax + sldt ax + push eax + +;; UINT32 EFlags; + mov eax, [ebp + 5 * 4] + push eax + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + mov eax, cr4 + or eax, 0x208 + mov cr4, eax + push eax + mov eax, cr3 + push eax + mov eax, cr2 + push eax + xor eax, eax + push eax + mov eax, cr0 + push eax + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov eax, dr7 + push eax + mov eax, dr6 + push eax + mov eax, dr3 + push eax + mov eax, dr2 + push eax + mov eax, dr1 + push eax + mov eax, dr0 + push eax + +;; FX_SAVE_STATE_IA32 FxSaveState; + sub esp, 512 + mov edi, esp + fxsave [edi] + +; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear + cld + +;; UINT32 ExceptionData; + push dword [ebp + 2 * 4] + +;; call into exception handler + +;; Prepare parameter and call + mov edx, esp + push edx + mov edx, dword [ebp + 1 * 4] + push edx + + ; + ; Call External Exception Handler + ; + mov eax, ASM_PFX(SmiPFHandler) + call eax + add esp, 8 + +;; UINT32 ExceptionData; + add esp, 4 + +;; FX_SAVE_STATE_IA32 FxSaveState; + mov esi, esp + fxrstor [esi] + add esp, 512 + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; Skip restoration of DRx registers to support debuggers +;; that set breakpoint in interrupt/exception context + add esp, 4 * 6 + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + pop eax + mov cr0, eax + add esp, 4 ; not for Cr1 + pop eax + mov cr2, eax + pop eax + mov cr3, eax + pop eax + mov cr4, eax + +;; UINT32 EFlags; + pop dword [ebp + 5 * 4] + +;; UINT32 Ldtr, Tr; +;; UINT32 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add esp, 24 + +;; UINT32 Eip; + pop dword [ebp + 3 * 4] + +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; +;; NOTE - modified segment registers could hang the debugger... We +;; could attempt to insulate ourselves against this possibility, +;; but that poses risks as well. +;; + pop gs + pop fs + pop es + pop ds + pop dword [ebp + 4 * 4] + pop ss + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + pop edi + pop esi + add esp, 4 ; not for ebp + add esp, 4 ; not for esp + pop ebx + pop edx + pop ecx + pop eax + + mov esp, ebp + pop ebp + +; Enable TF bit after page fault handler runs + bts dword [esp + 16], 8 ; EFLAGS + + add esp, 8 ; skip INT# & ErrCode +Return: + iretd +; +; Page Fault Exception Handler entry when SMM Stack Guard is enabled +; Executiot starts here after a task switch +; +PFHandlerEntry: +; +; Get this processor's TSS +; + sub esp, 8 + sgdt [esp + 2] + mov eax, [esp + 4] ; GDT base + add esp, 8 + mov ecx, [eax + TSS_SEL + 2] + shl ecx, 8 + mov cl, [eax + TSS_SEL + 7] + ror ecx, 8 ; ecx = TSS base + + mov ebp, esp + + ; + ; Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32 + ; is 16-byte aligned + ; + and esp, 0xfffffff0 + sub esp, 12 + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + push dword [ecx + IA32_TSS._EAX] + push dword [ecx + IA32_TSS._ECX] + push dword [ecx + IA32_TSS._EDX] + push dword [ecx + IA32_TSS._EBX] + push dword [ecx + IA32_TSS._ESP] + push dword [ecx + IA32_TSS._EBP] + push dword [ecx + IA32_TSS._ESI] + push dword [ecx + IA32_TSS._EDI] + +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; + movzx eax, word [ecx + IA32_TSS._SS] + push eax + movzx eax, word [ecx + IA32_TSS._CS] + push eax + movzx eax, word [ecx + IA32_TSS._DS] + push eax + movzx eax, word [ecx + IA32_TSS._ES] + push eax + movzx eax, word [ecx + IA32_TSS._FS] + push eax + movzx eax, word [ecx + IA32_TSS._GS] + push eax + +;; UINT32 Eip; + push dword [ecx + IA32_TSS.EIP] + +;; UINT32 Gdtr[2], Idtr[2]; + sub esp, 8 + sidt [esp] + mov eax, [esp + 2] + xchg eax, [esp] + and eax, 0xFFFF + mov [esp+4], eax + + sub esp, 8 + sgdt [esp] + mov eax, [esp + 2] + xchg eax, [esp] + and eax, 0xFFFF + mov [esp+4], eax + +;; UINT32 Ldtr, Tr; + mov eax, TSS_SEL + push eax + movzx eax, word [ecx + IA32_TSS.LDT] + push eax + +;; UINT32 EFlags; + push dword [ecx + IA32_TSS.EFLAGS] + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + mov eax, cr4 + or eax, 0x208 + mov cr4, eax + push eax + mov eax, cr3 + push eax + mov eax, cr2 + push eax + xor eax, eax + push eax + mov eax, cr0 + push eax + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov eax, dr7 + push eax + mov eax, dr6 + push eax + mov eax, dr3 + push eax + mov eax, dr2 + push eax + mov eax, dr1 + push eax + mov eax, dr0 + push eax + +;; FX_SAVE_STATE_IA32 FxSaveState; +;; Clear TS bit in CR0 to avoid Device Not Available Exception (#NM) +;; when executing fxsave/fxrstor instruction + clts + sub esp, 512 + mov edi, esp + fxsave [edi] + +; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear + cld + +;; UINT32 ExceptionData; + push dword [ebp] + +;; call into exception handler + mov ebx, ecx + mov eax, ASM_PFX(SmiPFHandler) + +;; Prepare parameter and call + mov edx, esp + push edx + mov edx, 14 + push edx + + ; + ; Call External Exception Handler + ; + call eax + add esp, 8 + + mov ecx, ebx +;; UINT32 ExceptionData; + add esp, 4 + +;; FX_SAVE_STATE_IA32 FxSaveState; + mov esi, esp + fxrstor [esi] + add esp, 512 + +;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; Skip restoration of DRx registers to support debuggers +;; that set breakpoints in interrupt/exception context + add esp, 4 * 6 + +;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4; + pop eax + mov cr0, eax + add esp, 4 ; not for Cr1 + pop eax + mov cr2, eax + pop eax + mov dword [ecx + IA32_TSS._CR3], eax + pop eax + mov cr4, eax + +;; UINT32 EFlags; + pop dword [ecx + IA32_TSS.EFLAGS] + +;; UINT32 Ldtr, Tr; +;; UINT32 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add esp, 24 + +;; UINT32 Eip; + pop dword [ecx + IA32_TSS.EIP] + +;; UINT32 Gs, Fs, Es, Ds, Cs, Ss; +;; NOTE - modified segment registers could hang the debugger... We +;; could attempt to insulate ourselves against this possibility, +;; but that poses risks as well. +;; + pop eax +o16 mov [ecx + IA32_TSS._GS], ax + pop eax +o16 mov [ecx + IA32_TSS._FS], ax + pop eax +o16 mov [ecx + IA32_TSS._ES], ax + pop eax +o16 mov [ecx + IA32_TSS._DS], ax + pop eax +o16 mov [ecx + IA32_TSS._CS], ax + pop eax +o16 mov [ecx + IA32_TSS._SS], ax + +;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax; + pop dword [ecx + IA32_TSS._EDI] + pop dword [ecx + IA32_TSS._ESI] + add esp, 4 ; not for ebp + add esp, 4 ; not for esp + pop dword [ecx + IA32_TSS._EBX] + pop dword [ecx + IA32_TSS._EDX] + pop dword [ecx + IA32_TSS._ECX] + pop dword [ecx + IA32_TSS._EAX] + + mov esp, ebp + +; Set single step DB# if SMM profile is enabled and page fault exception happens + cmp byte [dword ASM_PFX(mSetupDebugTrap)], 0 + jz @Done2 + +; Create return context for iretd in stub function + mov eax, dword [ecx + IA32_TSS._ESP] ; Get old stack pointer + mov ebx, dword [ecx + IA32_TSS.EIP] + mov [eax - 0xc], ebx ; create EIP in old stack + movzx ebx, word [ecx + IA32_TSS._CS] + mov [eax - 0x8], ebx ; create CS in old stack + mov ebx, dword [ecx + IA32_TSS.EFLAGS] + bts ebx, 8 + mov [eax - 0x4], ebx ; create eflags in old stack + mov eax, dword [ecx + IA32_TSS._ESP] ; Get old stack pointer + sub eax, 0xc ; minus 12 byte + mov dword [ecx + IA32_TSS._ESP], eax ; Set new stack pointer +; Replace the EIP of interrupted task with stub function + mov eax, ASM_PFX(PageFaultStubFunction) + mov dword [ecx + IA32_TSS.EIP], eax +; Jump to the iretd so next page fault handler as a task will start again after iretd. +@Done2: + add esp, 4 ; skip ErrCode + + jmp Return + +global ASM_PFX(PageFaultStubFunction) +ASM_PFX(PageFaultStubFunction): +; +; we need clean TS bit in CR0 to execute +; x87 FPU/MMX/SSE/SSE2/SSE3/SSSE3/SSE4 instructions. +; + clts + iretd + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmmFuncsArch.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmmFuncsArch.c new file mode 100644 index 00000000..b7f4949c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmmFuncsArch.c @@ -0,0 +1,204 @@ +/** @file + SMM CPU misc functions for Ia32 arch specific. + +Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" + +extern UINT64 gTaskGateDescriptor; + +EFI_PHYSICAL_ADDRESS mGdtBuffer; +UINTN mGdtBufferSize; + +extern BOOLEAN mCetSupported; +extern UINTN mSmmShadowStackSize; + +X86_ASSEMBLY_PATCH_LABEL mPatchCetPl0Ssp; +X86_ASSEMBLY_PATCH_LABEL mPatchCetInterruptSsp; +UINT32 mCetPl0Ssp; +UINT32 mCetInterruptSsp; + +/** + Initialize IDT for SMM Stack Guard. + +**/ +VOID +EFIAPI +InitializeIDTSmmStackGuard ( + VOID + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtGate; + + // + // If SMM Stack Guard feature is enabled, the Page Fault Exception entry in IDT + // is a Task Gate Descriptor so that when a Page Fault Exception occurs, + // the processors can use a known good stack in case stack is ran out. + // + IdtGate = (IA32_IDT_GATE_DESCRIPTOR *)gcSmiIdtr.Base; + IdtGate += EXCEPT_IA32_PAGE_FAULT; + IdtGate->Uint64 = gTaskGateDescriptor; +} + +/** + Initialize Gdt for all processors. + + @param[in] Cr3 CR3 value. + @param[out] GdtStepSize The step size for GDT table. + + @return GdtBase for processor 0. + GdtBase for processor X is: GdtBase + (GdtStepSize * X) +**/ +VOID * +InitGdt ( + IN UINTN Cr3, + OUT UINTN *GdtStepSize + ) +{ + UINTN Index; + IA32_SEGMENT_DESCRIPTOR *GdtDescriptor; + UINTN TssBase; + UINTN GdtTssTableSize; + UINT8 *GdtTssTables; + UINTN GdtTableStepSize; + UINTN InterruptShadowStack; + + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + // + // For IA32 SMM, if SMM Stack Guard feature is enabled, we use 2 TSS. + // in this case, we allocate separate GDT/TSS for each CPUs to avoid TSS load contention + // on each SMI entry. + // + + // + // Enlarge GDT to contain 2 TSS descriptors + // + gcSmiGdtr.Limit += (UINT16)(2 * sizeof (IA32_SEGMENT_DESCRIPTOR)); + + GdtTssTableSize = (gcSmiGdtr.Limit + 1 + TSS_SIZE + EXCEPTION_TSS_SIZE + 7) & ~7; // 8 bytes aligned + mGdtBufferSize = GdtTssTableSize * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; + // + // IA32 Stack Guard need use task switch to switch stack that need + // write GDT and TSS, so AllocateCodePages() could not be used here + // as code pages will be set to RO. + // + GdtTssTables = (UINT8*)AllocatePages (EFI_SIZE_TO_PAGES (mGdtBufferSize)); + ASSERT (GdtTssTables != NULL); + mGdtBuffer = (UINTN)GdtTssTables; + GdtTableStepSize = GdtTssTableSize; + + for (Index = 0; Index < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; Index++) { + CopyMem (GdtTssTables + GdtTableStepSize * Index, (VOID*)(UINTN)gcSmiGdtr.Base, gcSmiGdtr.Limit + 1 + TSS_SIZE + EXCEPTION_TSS_SIZE); + // + // Fixup TSS descriptors + // + TssBase = (UINTN)(GdtTssTables + GdtTableStepSize * Index + gcSmiGdtr.Limit + 1); + GdtDescriptor = (IA32_SEGMENT_DESCRIPTOR *)(TssBase) - 2; + GdtDescriptor->Bits.BaseLow = (UINT16)TssBase; + GdtDescriptor->Bits.BaseMid = (UINT8)(TssBase >> 16); + GdtDescriptor->Bits.BaseHigh = (UINT8)(TssBase >> 24); + + TssBase += TSS_SIZE; + GdtDescriptor++; + GdtDescriptor->Bits.BaseLow = (UINT16)TssBase; + GdtDescriptor->Bits.BaseMid = (UINT8)(TssBase >> 16); + GdtDescriptor->Bits.BaseHigh = (UINT8)(TssBase >> 24); + // + // Fixup TSS segments + // + // ESP as known good stack + // + *(UINTN *)(TssBase + TSS_IA32_ESP_OFFSET) = mSmmStackArrayBase + EFI_PAGE_SIZE + Index * mSmmStackSize; + *(UINT32 *)(TssBase + TSS_IA32_CR3_OFFSET) = Cr3; + + // + // Setup ShadowStack for stack switch + // + if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) { + InterruptShadowStack = (UINTN)(mSmmStackArrayBase + mSmmStackSize + EFI_PAGES_TO_SIZE (1) - sizeof(UINT64) + (mSmmStackSize + mSmmShadowStackSize) * Index); + *(UINT32 *)(TssBase + TSS_IA32_SSP_OFFSET) = (UINT32)InterruptShadowStack; + } + } + } else { + // + // Just use original table, AllocatePage and copy them here to make sure GDTs are covered in page memory. + // + GdtTssTableSize = gcSmiGdtr.Limit + 1; + mGdtBufferSize = GdtTssTableSize * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; + GdtTssTables = (UINT8*)AllocateCodePages (EFI_SIZE_TO_PAGES (mGdtBufferSize)); + ASSERT (GdtTssTables != NULL); + mGdtBuffer = (UINTN)GdtTssTables; + GdtTableStepSize = GdtTssTableSize; + + for (Index = 0; Index < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; Index++) { + CopyMem (GdtTssTables + GdtTableStepSize * Index, (VOID*)(UINTN)gcSmiGdtr.Base, gcSmiGdtr.Limit + 1); + } + } + + *GdtStepSize = GdtTableStepSize; + return GdtTssTables; +} + +/** + Transfer AP to safe hlt-loop after it finished restore CPU features on S3 patch. + + @param[in] ApHltLoopCode The address of the safe hlt-loop function. + @param[in] TopOfStack A pointer to the new stack to use for the ApHltLoopCode. + @param[in] NumberToFinishAddress Address of Semaphore of APs finish count. + +**/ +VOID +TransferApToSafeState ( + IN UINTN ApHltLoopCode, + IN UINTN TopOfStack, + IN UINTN NumberToFinishAddress + ) +{ + SwitchStack ( + (SWITCH_STACK_ENTRY_POINT)ApHltLoopCode, + (VOID *)NumberToFinishAddress, + NULL, + (VOID *)TopOfStack + ); + // + // It should never reach here + // + ASSERT (FALSE); +} + +/** + Initialize the shadow stack related data structure. + + @param CpuIndex The index of CPU. + @param ShadowStack The bottom of the shadow stack for this CPU. +**/ +VOID +InitShadowStack ( + IN UINTN CpuIndex, + IN VOID *ShadowStack + ) +{ + UINTN SmmShadowStackSize; + + if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) { + SmmShadowStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize))); + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + SmmShadowStackSize += EFI_PAGES_TO_SIZE (2); + } + mCetPl0Ssp = (UINT32)((UINTN)ShadowStack + SmmShadowStackSize - sizeof(UINT64)); + PatchInstructionX86 (mPatchCetPl0Ssp, mCetPl0Ssp, 4); + DEBUG ((DEBUG_INFO, "mCetPl0Ssp - 0x%x\n", mCetPl0Ssp)); + DEBUG ((DEBUG_INFO, "ShadowStack - 0x%x\n", ShadowStack)); + DEBUG ((DEBUG_INFO, " SmmShadowStackSize - 0x%x\n", SmmShadowStackSize)); + + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + mCetInterruptSsp = (UINT32)((UINTN)ShadowStack + EFI_PAGES_TO_SIZE(1) - sizeof(UINT64)); + PatchInstructionX86 (mPatchCetInterruptSsp, mCetInterruptSsp, 4); + DEBUG ((DEBUG_INFO, "mCetInterruptSsp - 0x%x\n", mCetInterruptSsp)); + } + } +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmmInit.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmmInit.nasm new file mode 100644 index 00000000..69291ec1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmmInit.nasm @@ -0,0 +1,96 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; SmmInit.nasm +; +; Abstract: +; +; Functions for relocating SMBASE's for all processors +; +;------------------------------------------------------------------------------- + +%include "StuffRsbNasm.inc" + +extern ASM_PFX(SmmInitHandler) +extern ASM_PFX(mRebasedFlag) +extern ASM_PFX(mSmmRelocationOriginalAddress) + +global ASM_PFX(gPatchSmmCr3) +global ASM_PFX(gPatchSmmCr4) +global ASM_PFX(gPatchSmmCr0) +global ASM_PFX(gPatchSmmInitStack) +global ASM_PFX(gcSmiInitGdtr) +global ASM_PFX(gcSmmInitSize) +global ASM_PFX(gcSmmInitTemplate) + +%define PROTECT_MODE_CS 0x8 +%define PROTECT_MODE_DS 0x20 + + SECTION .text + +ASM_PFX(gcSmiInitGdtr): + DW 0 + DQ 0 + +global ASM_PFX(SmmStartup) + +BITS 16 +ASM_PFX(SmmStartup): + mov eax, 0x80000001 ; read capability + cpuid + mov ebx, edx ; rdmsr will change edx. keep it in ebx. + and ebx, BIT20 ; extract NX capability bit + shr ebx, 9 ; shift bit to IA32_EFER.NXE[BIT11] position + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmmCr3): + mov cr3, eax +o32 lgdt [cs:ebp + (ASM_PFX(gcSmiInitGdtr) - ASM_PFX(SmmStartup))] + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmmCr4): + mov cr4, eax + mov ecx, 0xc0000080 ; IA32_EFER MSR + rdmsr + or eax, ebx ; set NXE bit if NX is available + wrmsr + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmmCr0): + mov di, PROTECT_MODE_DS + mov cr0, eax + jmp PROTECT_MODE_CS : dword @32bit + +BITS 32 +@32bit: + mov ds, edi + mov es, edi + mov fs, edi + mov gs, edi + mov ss, edi + mov esp, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmmInitStack): + call ASM_PFX(SmmInitHandler) + StuffRsb32 + rsm + +BITS 16 +ASM_PFX(gcSmmInitTemplate): + mov ebp, ASM_PFX(SmmStartup) + sub ebp, 0x30000 + jmp ebp + +ASM_PFX(gcSmmInitSize): DW $ - ASM_PFX(gcSmmInitTemplate) + +BITS 32 +global ASM_PFX(SmmRelocationSemaphoreComplete) +ASM_PFX(SmmRelocationSemaphoreComplete): + push eax + mov eax, [ASM_PFX(mRebasedFlag)] + mov byte [eax], 1 + pop eax + jmp [ASM_PFX(mSmmRelocationOriginalAddress)] + +global ASM_PFX(PiSmmCpuSmmInitFixupAddress) +ASM_PFX(PiSmmCpuSmmInitFixupAddress): + ret diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmmProfileArch.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmmProfileArch.c new file mode 100644 index 00000000..67ad5ace --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmmProfileArch.c @@ -0,0 +1,74 @@ +/** @file +IA-32 processor specific functions to enable SMM profile. + +Copyright (c) 2012 - 2016, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" +#include "SmmProfileInternal.h" + +/** + Create SMM page table for S3 path. + +**/ +VOID +InitSmmS3Cr3 ( + VOID + ) +{ + mSmmS3ResumeState->SmmS3Cr3 = Gen4GPageTable (TRUE); + + return ; +} + +/** + Allocate pages for creating 4KB-page based on 2MB-page when page fault happens. + 32-bit firmware does not need it. + +**/ +VOID +InitPagesForPFHandler ( + VOID + ) +{ +} + +/** + Update page table to map the memory correctly in order to make the instruction + which caused page fault execute successfully. And it also save the original page + table to be restored in single-step exception. 32-bit firmware does not need it. + + @param PageTable PageTable Address. + @param PFAddress The memory address which caused page fault exception. + @param CpuIndex The index of the processor. + @param ErrorCode The Error code of exception. + @param IsValidPFAddress The flag indicates if SMM profile data need be added. + +**/ +VOID +RestorePageTableAbove4G ( + UINT64 *PageTable, + UINT64 PFAddress, + UINTN CpuIndex, + UINTN ErrorCode, + BOOLEAN *IsValidPFAddress + ) +{ +} + +/** + Clear TF in FLAGS. + + @param SystemContext A pointer to the processor context when + the interrupt occurred on the processor. + +**/ +VOID +ClearTrapFlag ( + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + SystemContext.SystemContextIa32->Eflags &= (UINTN) ~BIT8; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmmProfileArch.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmmProfileArch.h new file mode 100644 index 00000000..69f43116 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmmProfileArch.h @@ -0,0 +1,91 @@ +/** @file +IA-32 processor specific header file to enable SMM profile. + +Copyright (c) 2012 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SMM_PROFILE_ARCH_H_ +#define _SMM_PROFILE_ARCH_H_ + +#pragma pack (1) + +typedef struct _MSR_DS_AREA_STRUCT { + UINT32 BTSBufferBase; + UINT32 BTSIndex; + UINT32 BTSAbsoluteMaximum; + UINT32 BTSInterruptThreshold; + UINT32 PEBSBufferBase; + UINT32 PEBSIndex; + UINT32 PEBSAbsoluteMaximum; + UINT32 PEBSInterruptThreshold; + UINT32 PEBSCounterReset[4]; + UINT32 Reserved; +} MSR_DS_AREA_STRUCT; + +typedef struct _BRANCH_TRACE_RECORD { + UINT32 LastBranchFrom; + UINT32 LastBranchTo; + UINT32 Rsvd0 : 4; + UINT32 BranchPredicted : 1; + UINT32 Rsvd1 : 27; +} BRANCH_TRACE_RECORD; + +typedef struct _PEBS_RECORD { + UINT32 Eflags; + UINT32 LinearIP; + UINT32 Eax; + UINT32 Ebx; + UINT32 Ecx; + UINT32 Edx; + UINT32 Esi; + UINT32 Edi; + UINT32 Ebp; + UINT32 Esp; +} PEBS_RECORD; + +#pragma pack () + +#define PHYSICAL_ADDRESS_MASK ((1ull << 32) - SIZE_4KB) + +/** + Update page table to map the memory correctly in order to make the instruction + which caused page fault execute successfully. And it also save the original page + table to be restored in single-step exception. 32-bit firmware does not need it. + + @param PageTable PageTable Address. + @param PFAddress The memory address which caused page fault exception. + @param CpuIndex The index of the processor. + @param ErrorCode The Error code of exception. + @param IsValidPFAddress The flag indicates if SMM profile data need be added. + +**/ +VOID +RestorePageTableAbove4G ( + UINT64 *PageTable, + UINT64 PFAddress, + UINTN CpuIndex, + UINTN ErrorCode, + BOOLEAN *IsValidPFAddress + ); + +/** + Create SMM page table for S3 path. + +**/ +VOID +InitSmmS3Cr3 ( + VOID + ); + +/** + Allocate pages for creating 4KB-page based on 2MB-page when page fault happens. + +**/ +VOID +InitPagesForPFHandler ( + VOID + ); + +#endif // _SMM_PROFILE_ARCH_H_ diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c new file mode 100644 index 00000000..ae8c90be --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c @@ -0,0 +1,2027 @@ +/** @file +SMM MP service implementation + +Copyright (c) 2009 - 2021, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" + +// +// Slots for all MTRR( FIXED MTRR + VARIABLE MTRR + MTRR_LIB_IA32_MTRR_DEF_TYPE) +// +MTRR_SETTINGS gSmiMtrrs; +UINT64 gPhyMask; +SMM_DISPATCHER_MP_SYNC_DATA *mSmmMpSyncData = NULL; +UINTN mSmmMpSyncDataSize; +SMM_CPU_SEMAPHORES mSmmCpuSemaphores; +UINTN mSemaphoreSize; +SPIN_LOCK *mPFLock = NULL; +SMM_CPU_SYNC_MODE mCpuSmmSyncMode; +BOOLEAN mMachineCheckSupported = FALSE; +MM_COMPLETION mSmmStartupThisApToken; + +extern UINTN mSmmShadowStackSize; + +/** + Performs an atomic compare exchange operation to get semaphore. + The compare exchange operation must be performed using + MP safe mechanisms. + + @param Sem IN: 32-bit unsigned integer + OUT: original integer - 1 + @return Original integer - 1 + +**/ +UINT32 +WaitForSemaphore ( + IN OUT volatile UINT32 *Sem + ) +{ + UINT32 Value; + + for (;;) { + Value = *Sem; + if (Value != 0 && + InterlockedCompareExchange32 ( + (UINT32*)Sem, + Value, + Value - 1 + ) == Value) { + break; + } + CpuPause (); + } + return Value - 1; +} + + +/** + Performs an atomic compare exchange operation to release semaphore. + The compare exchange operation must be performed using + MP safe mechanisms. + + @param Sem IN: 32-bit unsigned integer + OUT: original integer + 1 + @return Original integer + 1 + +**/ +UINT32 +ReleaseSemaphore ( + IN OUT volatile UINT32 *Sem + ) +{ + UINT32 Value; + + do { + Value = *Sem; + } while (Value + 1 != 0 && + InterlockedCompareExchange32 ( + (UINT32*)Sem, + Value, + Value + 1 + ) != Value); + return Value + 1; +} + +/** + Performs an atomic compare exchange operation to lock semaphore. + The compare exchange operation must be performed using + MP safe mechanisms. + + @param Sem IN: 32-bit unsigned integer + OUT: -1 + @return Original integer + +**/ +UINT32 +LockdownSemaphore ( + IN OUT volatile UINT32 *Sem + ) +{ + UINT32 Value; + + do { + Value = *Sem; + } while (InterlockedCompareExchange32 ( + (UINT32*)Sem, + Value, (UINT32)-1 + ) != Value); + return Value; +} + +/** + Wait all APs to performs an atomic compare exchange operation to release semaphore. + + @param NumberOfAPs AP number + +**/ +VOID +WaitForAllAPs ( + IN UINTN NumberOfAPs + ) +{ + UINTN BspIndex; + + BspIndex = mSmmMpSyncData->BspIndex; + while (NumberOfAPs-- > 0) { + WaitForSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run); + } +} + +/** + Performs an atomic compare exchange operation to release semaphore + for each AP. + +**/ +VOID +ReleaseAllAPs ( + VOID + ) +{ + UINTN Index; + + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (IsPresentAp (Index)) { + ReleaseSemaphore (mSmmMpSyncData->CpuData[Index].Run); + } + } +} + +/** + Checks if all CPUs (with certain exceptions) have checked in for this SMI run + + @param Exceptions CPU Arrival exception flags. + + @retval TRUE if all CPUs the have checked in. + @retval FALSE if at least one Normal AP hasn't checked in. + +**/ +BOOLEAN +AllCpusInSmmWithExceptions ( + SMM_CPU_ARRIVAL_EXCEPTIONS Exceptions + ) +{ + UINTN Index; + SMM_CPU_DATA_BLOCK *CpuData; + EFI_PROCESSOR_INFORMATION *ProcessorInfo; + + ASSERT (*mSmmMpSyncData->Counter <= mNumberOfCpus); + + if (*mSmmMpSyncData->Counter == mNumberOfCpus) { + return TRUE; + } + + CpuData = mSmmMpSyncData->CpuData; + ProcessorInfo = gSmmCpuPrivate->ProcessorInfo; + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (!(*(CpuData[Index].Present)) && ProcessorInfo[Index].ProcessorId != INVALID_APIC_ID) { + if (((Exceptions & ARRIVAL_EXCEPTION_DELAYED) != 0) && SmmCpuFeaturesGetSmmRegister (Index, SmmRegSmmDelayed) != 0) { + continue; + } + if (((Exceptions & ARRIVAL_EXCEPTION_BLOCKED) != 0) && SmmCpuFeaturesGetSmmRegister (Index, SmmRegSmmBlocked) != 0) { + continue; + } + if (((Exceptions & ARRIVAL_EXCEPTION_SMI_DISABLED) != 0) && SmmCpuFeaturesGetSmmRegister (Index, SmmRegSmmEnable) != 0) { + continue; + } + return FALSE; + } + } + + + return TRUE; +} + +/** + Has OS enabled Lmce in the MSR_IA32_MCG_EXT_CTL + + @retval TRUE Os enable lmce. + @retval FALSE Os not enable lmce. + +**/ +BOOLEAN +IsLmceOsEnabled ( + VOID + ) +{ + MSR_IA32_MCG_CAP_REGISTER McgCap; + MSR_IA32_FEATURE_CONTROL_REGISTER FeatureCtrl; + MSR_IA32_MCG_EXT_CTL_REGISTER McgExtCtrl; + + McgCap.Uint64 = AsmReadMsr64 (MSR_IA32_MCG_CAP); + if (McgCap.Bits.MCG_LMCE_P == 0) { + return FALSE; + } + + FeatureCtrl.Uint64 = AsmReadMsr64 (MSR_IA32_FEATURE_CONTROL); + if (FeatureCtrl.Bits.LmceOn == 0) { + return FALSE; + } + + McgExtCtrl.Uint64 = AsmReadMsr64 (MSR_IA32_MCG_EXT_CTL); + return (BOOLEAN) (McgExtCtrl.Bits.LMCE_EN == 1); +} + +/** + Return if Local machine check exception signaled. + + Indicates (when set) that a local machine check exception was generated. This indicates that the current machine-check event was + delivered to only the logical processor. + + @retval TRUE LMCE was signaled. + @retval FALSE LMCE was not signaled. + +**/ +BOOLEAN +IsLmceSignaled ( + VOID + ) +{ + MSR_IA32_MCG_STATUS_REGISTER McgStatus; + + McgStatus.Uint64 = AsmReadMsr64 (MSR_IA32_MCG_STATUS); + return (BOOLEAN) (McgStatus.Bits.LMCE_S == 1); +} + +/** + Given timeout constraint, wait for all APs to arrive, and insure when this function returns, no AP will execute normal mode code before + entering SMM, except SMI disabled APs. + +**/ +VOID +SmmWaitForApArrival ( + VOID + ) +{ + UINT64 Timer; + UINTN Index; + BOOLEAN LmceEn; + BOOLEAN LmceSignal; + + ASSERT (*mSmmMpSyncData->Counter <= mNumberOfCpus); + + LmceEn = FALSE; + LmceSignal = FALSE; + if (mMachineCheckSupported) { + LmceEn = IsLmceOsEnabled (); + LmceSignal = IsLmceSignaled(); + } + + // + // Platform implementor should choose a timeout value appropriately: + // - The timeout value should balance the SMM time constrains and the likelihood that delayed CPUs are excluded in the SMM run. Note + // the SMI Handlers must ALWAYS take into account the cases that not all APs are available in an SMI run. + // - The timeout value must, in the case of 2nd timeout, be at least long enough to give time for all APs to receive the SMI IPI + // and either enter SMM or buffer the SMI, to insure there is no CPU running normal mode code when SMI handling starts. This will + // be TRUE even if a blocked CPU is brought out of the blocked state by a normal mode CPU (before the normal mode CPU received the + // SMI IPI), because with a buffered SMI, and CPU will enter SMM immediately after it is brought out of the blocked state. + // - The timeout value must be longer than longest possible IO operation in the system + // + + // + // Sync with APs 1st timeout + // + for (Timer = StartSyncTimer (); + !IsSyncTimerTimeout (Timer) && !(LmceEn && LmceSignal) && + !AllCpusInSmmWithExceptions (ARRIVAL_EXCEPTION_BLOCKED | ARRIVAL_EXCEPTION_SMI_DISABLED ); + ) { + CpuPause (); + } + + // + // Not all APs have arrived, so we need 2nd round of timeout. IPIs should be sent to ALL none present APs, + // because: + // a) Delayed AP may have just come out of the delayed state. Blocked AP may have just been brought out of blocked state by some AP running + // normal mode code. These APs need to be guaranteed to have an SMI pending to insure that once they are out of delayed / blocked state, they + // enter SMI immediately without executing instructions in normal mode. Note traditional flow requires there are no APs doing normal mode + // work while SMI handling is on-going. + // b) As a consequence of SMI IPI sending, (spurious) SMI may occur after this SMM run. + // c) ** NOTE **: Use SMI disabling feature VERY CAREFULLY (if at all) for traditional flow, because a processor in SMI-disabled state + // will execute normal mode code, which breaks the traditional SMI handlers' assumption that no APs are doing normal + // mode work while SMI handling is on-going. + // d) We don't add code to check SMI disabling status to skip sending IPI to SMI disabled APs, because: + // - In traditional flow, SMI disabling is discouraged. + // - In relaxed flow, CheckApArrival() will check SMI disabling status before calling this function. + // In both cases, adding SMI-disabling checking code increases overhead. + // + if (*mSmmMpSyncData->Counter < mNumberOfCpus) { + // + // Send SMI IPIs to bring outside processors in + // + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (!(*(mSmmMpSyncData->CpuData[Index].Present)) && gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId != INVALID_APIC_ID) { + SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId); + } + } + + // + // Sync with APs 2nd timeout. + // + for (Timer = StartSyncTimer (); + !IsSyncTimerTimeout (Timer) && + !AllCpusInSmmWithExceptions (ARRIVAL_EXCEPTION_BLOCKED | ARRIVAL_EXCEPTION_SMI_DISABLED ); + ) { + CpuPause (); + } + } + + return; +} + + +/** + Replace OS MTRR's with SMI MTRR's. + + @param CpuIndex Processor Index + +**/ +VOID +ReplaceOSMtrrs ( + IN UINTN CpuIndex + ) +{ + SmmCpuFeaturesDisableSmrr (); + + // + // Replace all MTRRs registers + // + MtrrSetAllMtrrs (&gSmiMtrrs); +} + +/** + Wheck whether task has been finished by all APs. + + @param BlockMode Whether did it in block mode or non-block mode. + + @retval TRUE Task has been finished by all APs. + @retval FALSE Task not has been finished by all APs. + +**/ +BOOLEAN +WaitForAllAPsNotBusy ( + IN BOOLEAN BlockMode + ) +{ + UINTN Index; + + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + // + // Ignore BSP and APs which not call in SMM. + // + if (!IsPresentAp(Index)) { + continue; + } + + if (BlockMode) { + AcquireSpinLock(mSmmMpSyncData->CpuData[Index].Busy); + ReleaseSpinLock(mSmmMpSyncData->CpuData[Index].Busy); + } else { + if (AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[Index].Busy)) { + ReleaseSpinLock(mSmmMpSyncData->CpuData[Index].Busy); + } else { + return FALSE; + } + } + } + + return TRUE; +} + +/** + Check whether it is an present AP. + + @param CpuIndex The AP index which calls this function. + + @retval TRUE It's a present AP. + @retval TRUE This is not an AP or it is not present. + +**/ +BOOLEAN +IsPresentAp ( + IN UINTN CpuIndex + ) +{ + return ((CpuIndex != gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) && + *(mSmmMpSyncData->CpuData[CpuIndex].Present)); +} + +/** + Clean up the status flags used during executing the procedure. + + @param CpuIndex The AP index which calls this function. + +**/ +VOID +ReleaseToken ( + IN UINTN CpuIndex + ) +{ + PROCEDURE_TOKEN *Token; + + Token = mSmmMpSyncData->CpuData[CpuIndex].Token; + + if (InterlockedDecrement (&Token->RunningApCount) == 0) { + ReleaseSpinLock (Token->SpinLock); + } + + mSmmMpSyncData->CpuData[CpuIndex].Token = NULL; +} + +/** + Free the tokens in the maintained list. + +**/ +VOID +ResetTokens ( + VOID + ) +{ + // + // Reset the FirstFreeToken to the beginning of token list upon exiting SMI. + // + gSmmCpuPrivate->FirstFreeToken = GetFirstNode (&gSmmCpuPrivate->TokenList); +} + +/** + SMI handler for BSP. + + @param CpuIndex BSP processor Index + @param SyncMode SMM MP sync mode + +**/ +VOID +BSPHandler ( + IN UINTN CpuIndex, + IN SMM_CPU_SYNC_MODE SyncMode + ) +{ + UINTN Index; + MTRR_SETTINGS Mtrrs; + UINTN ApCount; + BOOLEAN ClearTopLevelSmiResult; + UINTN PresentCount; + + ASSERT (CpuIndex == mSmmMpSyncData->BspIndex); + ApCount = 0; + + // + // Flag BSP's presence + // + *mSmmMpSyncData->InsideSmm = TRUE; + + // + // Initialize Debug Agent to start source level debug in BSP handler + // + InitializeDebugAgent (DEBUG_AGENT_INIT_ENTER_SMI, NULL, NULL); + + // + // Mark this processor's presence + // + *(mSmmMpSyncData->CpuData[CpuIndex].Present) = TRUE; + + // + // Clear platform top level SMI status bit before calling SMI handlers. If + // we cleared it after SMI handlers are run, we would miss the SMI that + // occurs after SMI handlers are done and before SMI status bit is cleared. + // + ClearTopLevelSmiResult = ClearTopLevelSmiStatus(); + ASSERT (ClearTopLevelSmiResult == TRUE); + + // + // Set running processor index + // + gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu = CpuIndex; + + // + // If Traditional Sync Mode or need to configure MTRRs: gather all available APs. + // + if (SyncMode == SmmCpuSyncModeTradition || SmmCpuFeaturesNeedConfigureMtrrs()) { + + // + // Wait for APs to arrive + // + SmmWaitForApArrival(); + + // + // Lock the counter down and retrieve the number of APs + // + *mSmmMpSyncData->AllCpusInSync = TRUE; + ApCount = LockdownSemaphore (mSmmMpSyncData->Counter) - 1; + + // + // Wait for all APs to get ready for programming MTRRs + // + WaitForAllAPs (ApCount); + + if (SmmCpuFeaturesNeedConfigureMtrrs()) { + // + // Signal all APs it's time for backup MTRRs + // + ReleaseAllAPs (); + + // + // WaitForSemaphore() may wait for ever if an AP happens to enter SMM at + // exactly this point. Please make sure PcdCpuSmmMaxSyncLoops has been set + // to a large enough value to avoid this situation. + // Note: For HT capable CPUs, threads within a core share the same set of MTRRs. + // We do the backup first and then set MTRR to avoid race condition for threads + // in the same core. + // + MtrrGetAllMtrrs(&Mtrrs); + + // + // Wait for all APs to complete their MTRR saving + // + WaitForAllAPs (ApCount); + + // + // Let all processors program SMM MTRRs together + // + ReleaseAllAPs (); + + // + // WaitForSemaphore() may wait for ever if an AP happens to enter SMM at + // exactly this point. Please make sure PcdCpuSmmMaxSyncLoops has been set + // to a large enough value to avoid this situation. + // + ReplaceOSMtrrs (CpuIndex); + + // + // Wait for all APs to complete their MTRR programming + // + WaitForAllAPs (ApCount); + } + } + + // + // The BUSY lock is initialized to Acquired state + // + AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy); + + // + // Perform the pre tasks + // + PerformPreTasks (); + + // + // Invoke SMM Foundation EntryPoint with the processor information context. + // + gSmmCpuPrivate->SmmCoreEntry (&gSmmCpuPrivate->SmmCoreEntryContext); + + // + // Make sure all APs have completed their pending none-block tasks + // + WaitForAllAPsNotBusy (TRUE); + + // + // Perform the remaining tasks + // + PerformRemainingTasks (); + + // + // If Relaxed-AP Sync Mode: gather all available APs after BSP SMM handlers are done, and + // make those APs to exit SMI synchronously. APs which arrive later will be excluded and + // will run through freely. + // + if (SyncMode != SmmCpuSyncModeTradition && !SmmCpuFeaturesNeedConfigureMtrrs()) { + + // + // Lock the counter down and retrieve the number of APs + // + *mSmmMpSyncData->AllCpusInSync = TRUE; + ApCount = LockdownSemaphore (mSmmMpSyncData->Counter) - 1; + // + // Make sure all APs have their Present flag set + // + while (TRUE) { + PresentCount = 0; + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (*(mSmmMpSyncData->CpuData[Index].Present)) { + PresentCount ++; + } + } + if (PresentCount > ApCount) { + break; + } + } + } + + // + // Notify all APs to exit + // + *mSmmMpSyncData->InsideSmm = FALSE; + ReleaseAllAPs (); + + // + // Wait for all APs to complete their pending tasks + // + WaitForAllAPs (ApCount); + + if (SmmCpuFeaturesNeedConfigureMtrrs()) { + // + // Signal APs to restore MTRRs + // + ReleaseAllAPs (); + + // + // Restore OS MTRRs + // + SmmCpuFeaturesReenableSmrr (); + MtrrSetAllMtrrs(&Mtrrs); + + // + // Wait for all APs to complete MTRR programming + // + WaitForAllAPs (ApCount); + } + + // + // Stop source level debug in BSP handler, the code below will not be + // debugged. + // + InitializeDebugAgent (DEBUG_AGENT_INIT_EXIT_SMI, NULL, NULL); + + // + // Signal APs to Reset states/semaphore for this processor + // + ReleaseAllAPs (); + + // + // Perform pending operations for hot-plug + // + SmmCpuUpdate (); + + // + // Clear the Present flag of BSP + // + *(mSmmMpSyncData->CpuData[CpuIndex].Present) = FALSE; + + // + // Gather APs to exit SMM synchronously. Note the Present flag is cleared by now but + // WaitForAllAps does not depend on the Present flag. + // + WaitForAllAPs (ApCount); + + // + // Reset the tokens buffer. + // + ResetTokens (); + + // + // Reset BspIndex to -1, meaning BSP has not been elected. + // + if (FeaturePcdGet (PcdCpuSmmEnableBspElection)) { + mSmmMpSyncData->BspIndex = (UINT32)-1; + } + + // + // Allow APs to check in from this point on + // + *mSmmMpSyncData->Counter = 0; + *mSmmMpSyncData->AllCpusInSync = FALSE; +} + +/** + SMI handler for AP. + + @param CpuIndex AP processor Index. + @param ValidSmi Indicates that current SMI is a valid SMI or not. + @param SyncMode SMM MP sync mode. + +**/ +VOID +APHandler ( + IN UINTN CpuIndex, + IN BOOLEAN ValidSmi, + IN SMM_CPU_SYNC_MODE SyncMode + ) +{ + UINT64 Timer; + UINTN BspIndex; + MTRR_SETTINGS Mtrrs; + EFI_STATUS ProcedureStatus; + + // + // Timeout BSP + // + for (Timer = StartSyncTimer (); + !IsSyncTimerTimeout (Timer) && + !(*mSmmMpSyncData->InsideSmm); + ) { + CpuPause (); + } + + if (!(*mSmmMpSyncData->InsideSmm)) { + // + // BSP timeout in the first round + // + if (mSmmMpSyncData->BspIndex != -1) { + // + // BSP Index is known + // + BspIndex = mSmmMpSyncData->BspIndex; + ASSERT (CpuIndex != BspIndex); + + // + // Send SMI IPI to bring BSP in + // + SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[BspIndex].ProcessorId); + + // + // Now clock BSP for the 2nd time + // + for (Timer = StartSyncTimer (); + !IsSyncTimerTimeout (Timer) && + !(*mSmmMpSyncData->InsideSmm); + ) { + CpuPause (); + } + + if (!(*mSmmMpSyncData->InsideSmm)) { + // + // Give up since BSP is unable to enter SMM + // and signal the completion of this AP + WaitForSemaphore (mSmmMpSyncData->Counter); + return; + } + } else { + // + // Don't know BSP index. Give up without sending IPI to BSP. + // + WaitForSemaphore (mSmmMpSyncData->Counter); + return; + } + } + + // + // BSP is available + // + BspIndex = mSmmMpSyncData->BspIndex; + ASSERT (CpuIndex != BspIndex); + + // + // Mark this processor's presence + // + *(mSmmMpSyncData->CpuData[CpuIndex].Present) = TRUE; + + if (SyncMode == SmmCpuSyncModeTradition || SmmCpuFeaturesNeedConfigureMtrrs()) { + // + // Notify BSP of arrival at this point + // + ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run); + } + + if (SmmCpuFeaturesNeedConfigureMtrrs()) { + // + // Wait for the signal from BSP to backup MTRRs + // + WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run); + + // + // Backup OS MTRRs + // + MtrrGetAllMtrrs(&Mtrrs); + + // + // Signal BSP the completion of this AP + // + ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run); + + // + // Wait for BSP's signal to program MTRRs + // + WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run); + + // + // Replace OS MTRRs with SMI MTRRs + // + ReplaceOSMtrrs (CpuIndex); + + // + // Signal BSP the completion of this AP + // + ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run); + } + + while (TRUE) { + // + // Wait for something to happen + // + WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run); + + // + // Check if BSP wants to exit SMM + // + if (!(*mSmmMpSyncData->InsideSmm)) { + break; + } + + // + // BUSY should be acquired by SmmStartupThisAp() + // + ASSERT ( + !AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[CpuIndex].Busy) + ); + + // + // Invoke the scheduled procedure + // + ProcedureStatus = (*mSmmMpSyncData->CpuData[CpuIndex].Procedure) ( + (VOID*)mSmmMpSyncData->CpuData[CpuIndex].Parameter + ); + if (mSmmMpSyncData->CpuData[CpuIndex].Status != NULL) { + *mSmmMpSyncData->CpuData[CpuIndex].Status = ProcedureStatus; + } + + if (mSmmMpSyncData->CpuData[CpuIndex].Token != NULL) { + ReleaseToken (CpuIndex); + } + + // + // Release BUSY + // + ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy); + } + + if (SmmCpuFeaturesNeedConfigureMtrrs()) { + // + // Notify BSP the readiness of this AP to program MTRRs + // + ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run); + + // + // Wait for the signal from BSP to program MTRRs + // + WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run); + + // + // Restore OS MTRRs + // + SmmCpuFeaturesReenableSmrr (); + MtrrSetAllMtrrs(&Mtrrs); + } + + // + // Notify BSP the readiness of this AP to Reset states/semaphore for this processor + // + ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run); + + // + // Wait for the signal from BSP to Reset states/semaphore for this processor + // + WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run); + + // + // Reset states/semaphore for this processor + // + *(mSmmMpSyncData->CpuData[CpuIndex].Present) = FALSE; + + // + // Notify BSP the readiness of this AP to exit SMM + // + ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run); + +} + +/** + Create 4G PageTable in SMRAM. + + @param[in] Is32BitPageTable Whether the page table is 32-bit PAE + @return PageTable Address + +**/ +UINT32 +Gen4GPageTable ( + IN BOOLEAN Is32BitPageTable + ) +{ + VOID *PageTable; + UINTN Index; + UINT64 *Pte; + UINTN PagesNeeded; + UINTN Low2MBoundary; + UINTN High2MBoundary; + UINTN Pages; + UINTN GuardPage; + UINT64 *Pdpte; + UINTN PageIndex; + UINTN PageAddress; + + Low2MBoundary = 0; + High2MBoundary = 0; + PagesNeeded = 0; + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + // + // Add one more page for known good stack, then find the lower 2MB aligned address. + // + Low2MBoundary = (mSmmStackArrayBase + EFI_PAGE_SIZE) & ~(SIZE_2MB-1); + // + // Add two more pages for known good stack and stack guard page, + // then find the lower 2MB aligned address. + // + High2MBoundary = (mSmmStackArrayEnd - mSmmStackSize - mSmmShadowStackSize + EFI_PAGE_SIZE * 2) & ~(SIZE_2MB-1); + PagesNeeded = ((High2MBoundary - Low2MBoundary) / SIZE_2MB) + 1; + } + // + // Allocate the page table + // + PageTable = AllocatePageTableMemory (5 + PagesNeeded); + ASSERT (PageTable != NULL); + + PageTable = (VOID *)((UINTN)PageTable); + Pte = (UINT64*)PageTable; + + // + // Zero out all page table entries first + // + ZeroMem (Pte, EFI_PAGES_TO_SIZE (1)); + + // + // Set Page Directory Pointers + // + for (Index = 0; Index < 4; Index++) { + Pte[Index] = ((UINTN)PageTable + EFI_PAGE_SIZE * (Index + 1)) | mAddressEncMask | + (Is32BitPageTable ? IA32_PAE_PDPTE_ATTRIBUTE_BITS : PAGE_ATTRIBUTE_BITS); + } + Pte += EFI_PAGE_SIZE / sizeof (*Pte); + + // + // Fill in Page Directory Entries + // + for (Index = 0; Index < EFI_PAGE_SIZE * 4 / sizeof (*Pte); Index++) { + Pte[Index] = (Index << 21) | mAddressEncMask | IA32_PG_PS | PAGE_ATTRIBUTE_BITS; + } + + Pdpte = (UINT64*)PageTable; + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + Pages = (UINTN)PageTable + EFI_PAGES_TO_SIZE (5); + GuardPage = mSmmStackArrayBase + EFI_PAGE_SIZE; + for (PageIndex = Low2MBoundary; PageIndex <= High2MBoundary; PageIndex += SIZE_2MB) { + Pte = (UINT64*)(UINTN)(Pdpte[BitFieldRead32 ((UINT32)PageIndex, 30, 31)] & ~mAddressEncMask & ~(EFI_PAGE_SIZE - 1)); + Pte[BitFieldRead32 ((UINT32)PageIndex, 21, 29)] = (UINT64)Pages | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + // + // Fill in Page Table Entries + // + Pte = (UINT64*)Pages; + PageAddress = PageIndex; + for (Index = 0; Index < EFI_PAGE_SIZE / sizeof (*Pte); Index++) { + if (PageAddress == GuardPage) { + // + // Mark the guard page as non-present + // + Pte[Index] = PageAddress | mAddressEncMask; + GuardPage += (mSmmStackSize + mSmmShadowStackSize); + if (GuardPage > mSmmStackArrayEnd) { + GuardPage = 0; + } + } else { + Pte[Index] = PageAddress | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + } + PageAddress+= EFI_PAGE_SIZE; + } + Pages += EFI_PAGE_SIZE; + } + } + + if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0) { + Pte = (UINT64*)(UINTN)(Pdpte[0] & ~mAddressEncMask & ~(EFI_PAGE_SIZE - 1)); + if ((Pte[0] & IA32_PG_PS) == 0) { + // 4K-page entries are already mapped. Just hide the first one anyway. + Pte = (UINT64*)(UINTN)(Pte[0] & ~mAddressEncMask & ~(EFI_PAGE_SIZE - 1)); + Pte[0] &= ~(UINT64)IA32_PG_P; // Hide page 0 + } else { + // Create 4K-page entries + Pages = (UINTN)AllocatePageTableMemory (1); + ASSERT (Pages != 0); + + Pte[0] = (UINT64)(Pages | mAddressEncMask | PAGE_ATTRIBUTE_BITS); + + Pte = (UINT64*)Pages; + PageAddress = 0; + Pte[0] = PageAddress | mAddressEncMask; // Hide page 0 but present left + for (Index = 1; Index < EFI_PAGE_SIZE / sizeof (*Pte); Index++) { + PageAddress += EFI_PAGE_SIZE; + Pte[Index] = PageAddress | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + } + } + } + + return (UINT32)(UINTN)PageTable; +} + +/** + Checks whether the input token is the current used token. + + @param[in] Token This parameter describes the token that was passed into DispatchProcedure or + BroadcastProcedure. + + @retval TRUE The input token is the current used token. + @retval FALSE The input token is not the current used token. +**/ +BOOLEAN +IsTokenInUse ( + IN SPIN_LOCK *Token + ) +{ + LIST_ENTRY *Link; + PROCEDURE_TOKEN *ProcToken; + + if (Token == NULL) { + return FALSE; + } + + Link = GetFirstNode (&gSmmCpuPrivate->TokenList); + // + // Only search used tokens. + // + while (Link != gSmmCpuPrivate->FirstFreeToken) { + ProcToken = PROCEDURE_TOKEN_FROM_LINK (Link); + + if (ProcToken->SpinLock == Token) { + return TRUE; + } + + Link = GetNextNode (&gSmmCpuPrivate->TokenList, Link); + } + + return FALSE; +} + +/** + Allocate buffer for the SPIN_LOCK and PROCEDURE_TOKEN. + + @return First token of the token buffer. +**/ +LIST_ENTRY * +AllocateTokenBuffer ( + VOID + ) +{ + UINTN SpinLockSize; + UINT32 TokenCountPerChunk; + UINTN Index; + SPIN_LOCK *SpinLock; + UINT8 *SpinLockBuffer; + PROCEDURE_TOKEN *ProcTokens; + + SpinLockSize = GetSpinLockProperties (); + + TokenCountPerChunk = FixedPcdGet32 (PcdCpuSmmMpTokenCountPerChunk); + ASSERT (TokenCountPerChunk != 0); + if (TokenCountPerChunk == 0) { + DEBUG ((DEBUG_ERROR, "PcdCpuSmmMpTokenCountPerChunk should not be Zero!\n")); + CpuDeadLoop (); + } + DEBUG ((DEBUG_INFO, "CpuSmm: SpinLock Size = 0x%x, PcdCpuSmmMpTokenCountPerChunk = 0x%x\n", SpinLockSize, TokenCountPerChunk)); + + // + // Separate the Spin_lock and Proc_token because the alignment requires by Spin_Lock. + // + SpinLockBuffer = AllocatePool (SpinLockSize * TokenCountPerChunk); + ASSERT (SpinLockBuffer != NULL); + + ProcTokens = AllocatePool (sizeof (PROCEDURE_TOKEN) * TokenCountPerChunk); + ASSERT (ProcTokens != NULL); + + for (Index = 0; Index < TokenCountPerChunk; Index++) { + SpinLock = (SPIN_LOCK *)(SpinLockBuffer + SpinLockSize * Index); + InitializeSpinLock (SpinLock); + + ProcTokens[Index].Signature = PROCEDURE_TOKEN_SIGNATURE; + ProcTokens[Index].SpinLock = SpinLock; + ProcTokens[Index].RunningApCount = 0; + + InsertTailList (&gSmmCpuPrivate->TokenList, &ProcTokens[Index].Link); + } + + return &ProcTokens[0].Link; +} + +/** + Get the free token. + + If no free token, allocate new tokens then return the free one. + + @param RunningApsCount The Running Aps count for this token. + + @retval return the first free PROCEDURE_TOKEN. + +**/ +PROCEDURE_TOKEN * +GetFreeToken ( + IN UINT32 RunningApsCount + ) +{ + PROCEDURE_TOKEN *NewToken; + + // + // If FirstFreeToken meets the end of token list, enlarge the token list. + // Set FirstFreeToken to the first free token. + // + if (gSmmCpuPrivate->FirstFreeToken == &gSmmCpuPrivate->TokenList) { + gSmmCpuPrivate->FirstFreeToken = AllocateTokenBuffer (); + } + NewToken = PROCEDURE_TOKEN_FROM_LINK (gSmmCpuPrivate->FirstFreeToken); + gSmmCpuPrivate->FirstFreeToken = GetNextNode (&gSmmCpuPrivate->TokenList, gSmmCpuPrivate->FirstFreeToken); + + NewToken->RunningApCount = RunningApsCount; + AcquireSpinLock (NewToken->SpinLock); + + return NewToken; +} + +/** + Checks status of specified AP. + + This function checks whether the specified AP has finished the task assigned + by StartupThisAP(), and whether timeout expires. + + @param[in] Token This parameter describes the token that was passed into DispatchProcedure or + BroadcastProcedure. + + @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs(). + @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired. +**/ +EFI_STATUS +IsApReady ( + IN SPIN_LOCK *Token + ) +{ + if (AcquireSpinLockOrFail (Token)) { + ReleaseSpinLock (Token); + return EFI_SUCCESS; + } + + return EFI_NOT_READY; +} + +/** + Schedule a procedure to run on the specified CPU. + + @param[in] Procedure The address of the procedure to run + @param[in] CpuIndex Target CPU Index + @param[in,out] ProcArguments The parameter to pass to the procedure + @param[in] Token This is an optional parameter that allows the caller to execute the + procedure in a blocking or non-blocking fashion. If it is NULL the + call is blocking, and the call will not return until the AP has + completed the procedure. If the token is not NULL, the call will + return immediately. The caller can check whether the procedure has + completed with CheckOnProcedure or WaitForProcedure. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for the APs to finish + execution of Procedure, either for blocking or non-blocking mode. + Zero means infinity. If the timeout expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. If + the timeout expires in blocking mode, the call returns EFI_TIMEOUT. + If the timeout expires in non-blocking mode, the timeout determined + can be through CheckOnProcedure or WaitForProcedure. + Note that timeout support is optional. Whether an implementation + supports this feature can be determined via the Attributes data + member. + @param[in,out] CpuStatus This optional pointer may be used to get the status code returned + by Procedure when it completes execution on the target AP, or with + EFI_TIMEOUT if the Procedure fails to complete within the optional + timeout. The implementation will update this variable with + EFI_NOT_READY prior to starting Procedure on the target AP. + + @retval EFI_INVALID_PARAMETER CpuNumber not valid + @retval EFI_INVALID_PARAMETER CpuNumber specifying BSP + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber did not enter SMM + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber is busy + @retval EFI_SUCCESS The procedure has been successfully scheduled + +**/ +EFI_STATUS +InternalSmmStartupThisAp ( + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN CpuIndex, + IN OUT VOID *ProcArguments OPTIONAL, + IN MM_COMPLETION *Token, + IN UINTN TimeoutInMicroseconds, + IN OUT EFI_STATUS *CpuStatus + ) +{ + PROCEDURE_TOKEN *ProcToken; + + if (CpuIndex >= gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus) { + DEBUG((DEBUG_ERROR, "CpuIndex(%d) >= gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus(%d)\n", CpuIndex, gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus)); + return EFI_INVALID_PARAMETER; + } + if (CpuIndex == gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) { + DEBUG((DEBUG_ERROR, "CpuIndex(%d) == gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu\n", CpuIndex)); + return EFI_INVALID_PARAMETER; + } + if (gSmmCpuPrivate->ProcessorInfo[CpuIndex].ProcessorId == INVALID_APIC_ID) { + return EFI_INVALID_PARAMETER; + } + if (!(*(mSmmMpSyncData->CpuData[CpuIndex].Present))) { + if (mSmmMpSyncData->EffectiveSyncMode == SmmCpuSyncModeTradition) { + DEBUG((DEBUG_ERROR, "!mSmmMpSyncData->CpuData[%d].Present\n", CpuIndex)); + } + return EFI_INVALID_PARAMETER; + } + if (gSmmCpuPrivate->Operation[CpuIndex] == SmmCpuRemove) { + if (!FeaturePcdGet (PcdCpuHotPlugSupport)) { + DEBUG((DEBUG_ERROR, "gSmmCpuPrivate->Operation[%d] == SmmCpuRemove\n", CpuIndex)); + } + return EFI_INVALID_PARAMETER; + } + if ((TimeoutInMicroseconds != 0) && ((mSmmMp.Attributes & EFI_MM_MP_TIMEOUT_SUPPORTED) == 0)) { + return EFI_INVALID_PARAMETER; + } + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy); + + mSmmMpSyncData->CpuData[CpuIndex].Procedure = Procedure; + mSmmMpSyncData->CpuData[CpuIndex].Parameter = ProcArguments; + if (Token != NULL) { + if (Token != &mSmmStartupThisApToken) { + // + // When Token points to mSmmStartupThisApToken, this routine is called + // from SmmStartupThisAp() in non-blocking mode (PcdCpuSmmBlockStartupThisAp == FALSE). + // + // In this case, caller wants to startup AP procedure in non-blocking + // mode and cannot get the completion status from the Token because there + // is no way to return the Token to caller from SmmStartupThisAp(). + // Caller needs to use its implementation specific way to query the completion status. + // + // There is no need to allocate a token for such case so the 3 overheads + // can be avoided: + // 1. Call AllocateTokenBuffer() when there is no free token. + // 2. Get a free token from the token buffer. + // 3. Call ReleaseToken() in APHandler(). + // + ProcToken = GetFreeToken (1); + mSmmMpSyncData->CpuData[CpuIndex].Token = ProcToken; + *Token = (MM_COMPLETION)ProcToken->SpinLock; + } + } + mSmmMpSyncData->CpuData[CpuIndex].Status = CpuStatus; + if (mSmmMpSyncData->CpuData[CpuIndex].Status != NULL) { + *mSmmMpSyncData->CpuData[CpuIndex].Status = EFI_NOT_READY; + } + + ReleaseSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run); + + if (Token == NULL) { + AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy); + ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy); + } + + return EFI_SUCCESS; +} + +/** + Worker function to execute a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. + @param[in,out] ProcedureArguments The parameter passed into Procedure for + all APs. + @param[in,out] Token This is an optional parameter that allows the caller to execute the + procedure in a blocking or non-blocking fashion. If it is NULL the + call is blocking, and the call will not return until the AP has + completed the procedure. If the token is not NULL, the call will + return immediately. The caller can check whether the procedure has + completed with CheckOnProcedure or WaitForProcedure. + @param[in,out] CPUStatus This optional pointer may be used to get the status code returned + by Procedure when it completes execution on the target AP, or with + EFI_TIMEOUT if the Procedure fails to complete within the optional + timeout. The implementation will update this variable with + EFI_NOT_READY prior to starting Procedure on the target AP. + + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval others Failed to Startup all APs. + +**/ +EFI_STATUS +InternalSmmStartupAllAPs ( + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN TimeoutInMicroseconds, + IN OUT VOID *ProcedureArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN OUT EFI_STATUS *CPUStatus + ) +{ + UINTN Index; + UINTN CpuCount; + PROCEDURE_TOKEN *ProcToken; + + if ((TimeoutInMicroseconds != 0) && ((mSmmMp.Attributes & EFI_MM_MP_TIMEOUT_SUPPORTED) == 0)) { + return EFI_INVALID_PARAMETER; + } + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + CpuCount = 0; + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (IsPresentAp (Index)) { + CpuCount ++; + + if (gSmmCpuPrivate->Operation[Index] == SmmCpuRemove) { + return EFI_INVALID_PARAMETER; + } + + if (!AcquireSpinLockOrFail(mSmmMpSyncData->CpuData[Index].Busy)) { + return EFI_NOT_READY; + } + ReleaseSpinLock (mSmmMpSyncData->CpuData[Index].Busy); + } + } + if (CpuCount == 0) { + return EFI_NOT_STARTED; + } + + if (Token != NULL) { + ProcToken = GetFreeToken ((UINT32)mMaxNumberOfCpus); + *Token = (MM_COMPLETION)ProcToken->SpinLock; + } else { + ProcToken = NULL; + } + + // + // Make sure all BUSY should be acquired. + // + // Because former code already check mSmmMpSyncData->CpuData[***].Busy for each AP. + // Here code always use AcquireSpinLock instead of AcquireSpinLockOrFail for not + // block mode. + // + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (IsPresentAp (Index)) { + AcquireSpinLock (mSmmMpSyncData->CpuData[Index].Busy); + } + } + + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (IsPresentAp (Index)) { + mSmmMpSyncData->CpuData[Index].Procedure = (EFI_AP_PROCEDURE2) Procedure; + mSmmMpSyncData->CpuData[Index].Parameter = ProcedureArguments; + if (ProcToken != NULL) { + mSmmMpSyncData->CpuData[Index].Token = ProcToken; + } + if (CPUStatus != NULL) { + mSmmMpSyncData->CpuData[Index].Status = &CPUStatus[Index]; + if (mSmmMpSyncData->CpuData[Index].Status != NULL) { + *mSmmMpSyncData->CpuData[Index].Status = EFI_NOT_READY; + } + } + } else { + // + // PI spec requirement: + // For every excluded processor, the array entry must contain a value of EFI_NOT_STARTED. + // + if (CPUStatus != NULL) { + CPUStatus[Index] = EFI_NOT_STARTED; + } + + // + // Decrease the count to mark this processor(AP or BSP) as finished. + // + if (ProcToken != NULL) { + WaitForSemaphore (&ProcToken->RunningApCount); + } + } + } + + ReleaseAllAPs (); + + if (Token == NULL) { + // + // Make sure all APs have completed their tasks. + // + WaitForAllAPsNotBusy (TRUE); + } + + return EFI_SUCCESS; +} + +/** + ISO C99 6.5.2.2 "Function calls", paragraph 9: + If the function is defined with a type that is not compatible with + the type (of the expression) pointed to by the expression that + denotes the called function, the behavior is undefined. + + So add below wrapper function to convert between EFI_AP_PROCEDURE + and EFI_AP_PROCEDURE2. + + Wrapper for Procedures. + + @param[in] Buffer Pointer to PROCEDURE_WRAPPER buffer. + +**/ +EFI_STATUS +EFIAPI +ProcedureWrapper ( + IN VOID *Buffer + ) +{ + PROCEDURE_WRAPPER *Wrapper; + + Wrapper = Buffer; + Wrapper->Procedure (Wrapper->ProcedureArgument); + + return EFI_SUCCESS; +} + +/** + Schedule a procedure to run on the specified CPU in blocking mode. + + @param[in] Procedure The address of the procedure to run + @param[in] CpuIndex Target CPU Index + @param[in, out] ProcArguments The parameter to pass to the procedure + + @retval EFI_INVALID_PARAMETER CpuNumber not valid + @retval EFI_INVALID_PARAMETER CpuNumber specifying BSP + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber did not enter SMM + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber is busy + @retval EFI_SUCCESS The procedure has been successfully scheduled + +**/ +EFI_STATUS +EFIAPI +SmmBlockingStartupThisAp ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN CpuIndex, + IN OUT VOID *ProcArguments OPTIONAL + ) +{ + PROCEDURE_WRAPPER Wrapper; + + Wrapper.Procedure = Procedure; + Wrapper.ProcedureArgument = ProcArguments; + + // + // Use wrapper function to convert EFI_AP_PROCEDURE to EFI_AP_PROCEDURE2. + // + return InternalSmmStartupThisAp (ProcedureWrapper, CpuIndex, &Wrapper, NULL, 0, NULL); +} + +/** + Schedule a procedure to run on the specified CPU. + + @param Procedure The address of the procedure to run + @param CpuIndex Target CPU Index + @param ProcArguments The parameter to pass to the procedure + + @retval EFI_INVALID_PARAMETER CpuNumber not valid + @retval EFI_INVALID_PARAMETER CpuNumber specifying BSP + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber did not enter SMM + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber is busy + @retval EFI_SUCCESS The procedure has been successfully scheduled + +**/ +EFI_STATUS +EFIAPI +SmmStartupThisAp ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN CpuIndex, + IN OUT VOID *ProcArguments OPTIONAL + ) +{ + gSmmCpuPrivate->ApWrapperFunc[CpuIndex].Procedure = Procedure; + gSmmCpuPrivate->ApWrapperFunc[CpuIndex].ProcedureArgument = ProcArguments; + + // + // Use wrapper function to convert EFI_AP_PROCEDURE to EFI_AP_PROCEDURE2. + // + return InternalSmmStartupThisAp ( + ProcedureWrapper, + CpuIndex, + &gSmmCpuPrivate->ApWrapperFunc[CpuIndex], + FeaturePcdGet (PcdCpuSmmBlockStartupThisAp) ? NULL : &mSmmStartupThisApToken, + 0, + NULL + ); +} + +/** + This function sets DR6 & DR7 according to SMM save state, before running SMM C code. + They are useful when you want to enable hardware breakpoints in SMM without entry SMM mode. + + NOTE: It might not be appreciated in runtime since it might + conflict with OS debugging facilities. Turn them off in RELEASE. + + @param CpuIndex CPU Index + +**/ +VOID +EFIAPI +CpuSmmDebugEntry ( + IN UINTN CpuIndex + ) +{ + SMRAM_SAVE_STATE_MAP *CpuSaveState; + + if (FeaturePcdGet (PcdCpuSmmDebug)) { + ASSERT(CpuIndex < mMaxNumberOfCpus); + CpuSaveState = (SMRAM_SAVE_STATE_MAP *)gSmmCpuPrivate->CpuSaveState[CpuIndex]; + if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) { + AsmWriteDr6 (CpuSaveState->x86._DR6); + AsmWriteDr7 (CpuSaveState->x86._DR7); + } else { + AsmWriteDr6 ((UINTN)CpuSaveState->x64._DR6); + AsmWriteDr7 ((UINTN)CpuSaveState->x64._DR7); + } + } +} + +/** + This function restores DR6 & DR7 to SMM save state. + + NOTE: It might not be appreciated in runtime since it might + conflict with OS debugging facilities. Turn them off in RELEASE. + + @param CpuIndex CPU Index + +**/ +VOID +EFIAPI +CpuSmmDebugExit ( + IN UINTN CpuIndex + ) +{ + SMRAM_SAVE_STATE_MAP *CpuSaveState; + + if (FeaturePcdGet (PcdCpuSmmDebug)) { + ASSERT(CpuIndex < mMaxNumberOfCpus); + CpuSaveState = (SMRAM_SAVE_STATE_MAP *)gSmmCpuPrivate->CpuSaveState[CpuIndex]; + if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) { + CpuSaveState->x86._DR7 = (UINT32)AsmReadDr7 (); + CpuSaveState->x86._DR6 = (UINT32)AsmReadDr6 (); + } else { + CpuSaveState->x64._DR7 = AsmReadDr7 (); + CpuSaveState->x64._DR6 = AsmReadDr6 (); + } + } +} + +/** + C function for SMI entry, each processor comes here upon SMI trigger. + + @param CpuIndex CPU Index + +**/ +VOID +EFIAPI +SmiRendezvous ( + IN UINTN CpuIndex + ) +{ + EFI_STATUS Status; + BOOLEAN ValidSmi; + BOOLEAN IsBsp; + BOOLEAN BspInProgress; + UINTN Index; + UINTN Cr2; + + ASSERT(CpuIndex < mMaxNumberOfCpus); + + // + // Save Cr2 because Page Fault exception in SMM may override its value, + // when using on-demand paging for above 4G memory. + // + Cr2 = 0; + SaveCr2 (&Cr2); + + // + // Call the user register Startup function first. + // + if (mSmmMpSyncData->StartupProcedure != NULL) { + mSmmMpSyncData->StartupProcedure (mSmmMpSyncData->StartupProcArgs); + } + + // + // Perform CPU specific entry hooks + // + SmmCpuFeaturesRendezvousEntry (CpuIndex); + + // + // Determine if this is a valid SMI + // + ValidSmi = PlatformValidSmi(); + + // + // Determine if BSP has been already in progress. Note this must be checked after + // ValidSmi because BSP may clear a valid SMI source after checking in. + // + BspInProgress = *mSmmMpSyncData->InsideSmm; + + if (!BspInProgress && !ValidSmi) { + // + // If we reach here, it means when we sampled the ValidSmi flag, SMI status had not + // been cleared by BSP in a new SMI run (so we have a truly invalid SMI), or SMI + // status had been cleared by BSP and an existing SMI run has almost ended. (Note + // we sampled ValidSmi flag BEFORE judging BSP-in-progress status.) In both cases, there + // is nothing we need to do. + // + goto Exit; + } else { + // + // Signal presence of this processor + // + if (ReleaseSemaphore (mSmmMpSyncData->Counter) == 0) { + // + // BSP has already ended the synchronization, so QUIT!!! + // + + // + // Wait for BSP's signal to finish SMI + // + while (*mSmmMpSyncData->AllCpusInSync) { + CpuPause (); + } + goto Exit; + } else { + + // + // The BUSY lock is initialized to Released state. + // This needs to be done early enough to be ready for BSP's SmmStartupThisAp() call. + // E.g., with Relaxed AP flow, SmmStartupThisAp() may be called immediately + // after AP's present flag is detected. + // + InitializeSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy); + } + + if (FeaturePcdGet (PcdCpuSmmProfileEnable)) { + ActivateSmmProfile (CpuIndex); + } + + if (BspInProgress) { + // + // BSP has been elected. Follow AP path, regardless of ValidSmi flag + // as BSP may have cleared the SMI status + // + APHandler (CpuIndex, ValidSmi, mSmmMpSyncData->EffectiveSyncMode); + } else { + // + // We have a valid SMI + // + + // + // Elect BSP + // + IsBsp = FALSE; + if (FeaturePcdGet (PcdCpuSmmEnableBspElection)) { + if (!mSmmMpSyncData->SwitchBsp || mSmmMpSyncData->CandidateBsp[CpuIndex]) { + // + // Call platform hook to do BSP election + // + Status = PlatformSmmBspElection (&IsBsp); + if (EFI_SUCCESS == Status) { + // + // Platform hook determines successfully + // + if (IsBsp) { + mSmmMpSyncData->BspIndex = (UINT32)CpuIndex; + } + } else { + // + // Platform hook fails to determine, use default BSP election method + // + InterlockedCompareExchange32 ( + (UINT32*)&mSmmMpSyncData->BspIndex, + (UINT32)-1, + (UINT32)CpuIndex + ); + } + } + } + + // + // "mSmmMpSyncData->BspIndex == CpuIndex" means this is the BSP + // + if (mSmmMpSyncData->BspIndex == CpuIndex) { + + // + // Clear last request for SwitchBsp. + // + if (mSmmMpSyncData->SwitchBsp) { + mSmmMpSyncData->SwitchBsp = FALSE; + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + mSmmMpSyncData->CandidateBsp[Index] = FALSE; + } + } + + if (FeaturePcdGet (PcdCpuSmmProfileEnable)) { + SmmProfileRecordSmiNum (); + } + + // + // BSP Handler is always called with a ValidSmi == TRUE + // + BSPHandler (CpuIndex, mSmmMpSyncData->EffectiveSyncMode); + } else { + APHandler (CpuIndex, ValidSmi, mSmmMpSyncData->EffectiveSyncMode); + } + } + + ASSERT (*mSmmMpSyncData->CpuData[CpuIndex].Run == 0); + + // + // Wait for BSP's signal to exit SMI + // + while (*mSmmMpSyncData->AllCpusInSync) { + CpuPause (); + } + } + +Exit: + SmmCpuFeaturesRendezvousExit (CpuIndex); + + // + // Restore Cr2 + // + RestoreCr2 (Cr2); +} + +/** + Allocate buffer for SpinLock and Wrapper function buffer. + +**/ +VOID +InitializeDataForMmMp ( + VOID + ) +{ + gSmmCpuPrivate->ApWrapperFunc = AllocatePool (sizeof (PROCEDURE_WRAPPER) * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus); + ASSERT (gSmmCpuPrivate->ApWrapperFunc != NULL); + + InitializeListHead (&gSmmCpuPrivate->TokenList); + + gSmmCpuPrivate->FirstFreeToken = AllocateTokenBuffer (); +} + +/** + Allocate buffer for all semaphores and spin locks. + +**/ +VOID +InitializeSmmCpuSemaphores ( + VOID + ) +{ + UINTN ProcessorCount; + UINTN TotalSize; + UINTN GlobalSemaphoresSize; + UINTN CpuSemaphoresSize; + UINTN SemaphoreSize; + UINTN Pages; + UINTN *SemaphoreBlock; + UINTN SemaphoreAddr; + + SemaphoreSize = GetSpinLockProperties (); + ProcessorCount = gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; + GlobalSemaphoresSize = (sizeof (SMM_CPU_SEMAPHORE_GLOBAL) / sizeof (VOID *)) * SemaphoreSize; + CpuSemaphoresSize = (sizeof (SMM_CPU_SEMAPHORE_CPU) / sizeof (VOID *)) * ProcessorCount * SemaphoreSize; + TotalSize = GlobalSemaphoresSize + CpuSemaphoresSize; + DEBUG((EFI_D_INFO, "One Semaphore Size = 0x%x\n", SemaphoreSize)); + DEBUG((EFI_D_INFO, "Total Semaphores Size = 0x%x\n", TotalSize)); + Pages = EFI_SIZE_TO_PAGES (TotalSize); + SemaphoreBlock = AllocatePages (Pages); + ASSERT (SemaphoreBlock != NULL); + ZeroMem (SemaphoreBlock, TotalSize); + + SemaphoreAddr = (UINTN)SemaphoreBlock; + mSmmCpuSemaphores.SemaphoreGlobal.Counter = (UINT32 *)SemaphoreAddr; + SemaphoreAddr += SemaphoreSize; + mSmmCpuSemaphores.SemaphoreGlobal.InsideSmm = (BOOLEAN *)SemaphoreAddr; + SemaphoreAddr += SemaphoreSize; + mSmmCpuSemaphores.SemaphoreGlobal.AllCpusInSync = (BOOLEAN *)SemaphoreAddr; + SemaphoreAddr += SemaphoreSize; + mSmmCpuSemaphores.SemaphoreGlobal.PFLock = (SPIN_LOCK *)SemaphoreAddr; + SemaphoreAddr += SemaphoreSize; + mSmmCpuSemaphores.SemaphoreGlobal.CodeAccessCheckLock + = (SPIN_LOCK *)SemaphoreAddr; + SemaphoreAddr += SemaphoreSize; + + SemaphoreAddr = (UINTN)SemaphoreBlock + GlobalSemaphoresSize; + mSmmCpuSemaphores.SemaphoreCpu.Busy = (SPIN_LOCK *)SemaphoreAddr; + SemaphoreAddr += ProcessorCount * SemaphoreSize; + mSmmCpuSemaphores.SemaphoreCpu.Run = (UINT32 *)SemaphoreAddr; + SemaphoreAddr += ProcessorCount * SemaphoreSize; + mSmmCpuSemaphores.SemaphoreCpu.Present = (BOOLEAN *)SemaphoreAddr; + + mPFLock = mSmmCpuSemaphores.SemaphoreGlobal.PFLock; + mConfigSmmCodeAccessCheckLock = mSmmCpuSemaphores.SemaphoreGlobal.CodeAccessCheckLock; + + mSemaphoreSize = SemaphoreSize; +} + +/** + Initialize un-cacheable data. + +**/ +VOID +EFIAPI +InitializeMpSyncData ( + VOID + ) +{ + UINTN CpuIndex; + + if (mSmmMpSyncData != NULL) { + // + // mSmmMpSyncDataSize includes one structure of SMM_DISPATCHER_MP_SYNC_DATA, one + // CpuData array of SMM_CPU_DATA_BLOCK and one CandidateBsp array of BOOLEAN. + // + ZeroMem (mSmmMpSyncData, mSmmMpSyncDataSize); + mSmmMpSyncData->CpuData = (SMM_CPU_DATA_BLOCK *)((UINT8 *)mSmmMpSyncData + sizeof (SMM_DISPATCHER_MP_SYNC_DATA)); + mSmmMpSyncData->CandidateBsp = (BOOLEAN *)(mSmmMpSyncData->CpuData + gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus); + if (FeaturePcdGet (PcdCpuSmmEnableBspElection)) { + // + // Enable BSP election by setting BspIndex to -1 + // + mSmmMpSyncData->BspIndex = (UINT32)-1; + } + mSmmMpSyncData->EffectiveSyncMode = mCpuSmmSyncMode; + + mSmmMpSyncData->Counter = mSmmCpuSemaphores.SemaphoreGlobal.Counter; + mSmmMpSyncData->InsideSmm = mSmmCpuSemaphores.SemaphoreGlobal.InsideSmm; + mSmmMpSyncData->AllCpusInSync = mSmmCpuSemaphores.SemaphoreGlobal.AllCpusInSync; + ASSERT (mSmmMpSyncData->Counter != NULL && mSmmMpSyncData->InsideSmm != NULL && + mSmmMpSyncData->AllCpusInSync != NULL); + *mSmmMpSyncData->Counter = 0; + *mSmmMpSyncData->InsideSmm = FALSE; + *mSmmMpSyncData->AllCpusInSync = FALSE; + + for (CpuIndex = 0; CpuIndex < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; CpuIndex ++) { + mSmmMpSyncData->CpuData[CpuIndex].Busy = + (SPIN_LOCK *)((UINTN)mSmmCpuSemaphores.SemaphoreCpu.Busy + mSemaphoreSize * CpuIndex); + mSmmMpSyncData->CpuData[CpuIndex].Run = + (UINT32 *)((UINTN)mSmmCpuSemaphores.SemaphoreCpu.Run + mSemaphoreSize * CpuIndex); + mSmmMpSyncData->CpuData[CpuIndex].Present = + (BOOLEAN *)((UINTN)mSmmCpuSemaphores.SemaphoreCpu.Present + mSemaphoreSize * CpuIndex); + *(mSmmMpSyncData->CpuData[CpuIndex].Busy) = 0; + *(mSmmMpSyncData->CpuData[CpuIndex].Run) = 0; + *(mSmmMpSyncData->CpuData[CpuIndex].Present) = FALSE; + } + } +} + +/** + Initialize global data for MP synchronization. + + @param Stacks Base address of SMI stack buffer for all processors. + @param StackSize Stack size for each processor in SMM. + @param ShadowStackSize Shadow Stack size for each processor in SMM. + +**/ +UINT32 +InitializeMpServiceData ( + IN VOID *Stacks, + IN UINTN StackSize, + IN UINTN ShadowStackSize + ) +{ + UINT32 Cr3; + UINTN Index; + UINT8 *GdtTssTables; + UINTN GdtTableStepSize; + CPUID_VERSION_INFO_EDX RegEdx; + UINT32 MaxExtendedFunction; + CPUID_VIR_PHY_ADDRESS_SIZE_EAX VirPhyAddressSize; + + // + // Determine if this CPU supports machine check + // + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx.Uint32); + mMachineCheckSupported = (BOOLEAN)(RegEdx.Bits.MCA == 1); + + // + // Allocate memory for all locks and semaphores + // + InitializeSmmCpuSemaphores (); + + // + // Initialize mSmmMpSyncData + // + mSmmMpSyncDataSize = sizeof (SMM_DISPATCHER_MP_SYNC_DATA) + + (sizeof (SMM_CPU_DATA_BLOCK) + sizeof (BOOLEAN)) * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; + mSmmMpSyncData = (SMM_DISPATCHER_MP_SYNC_DATA*) AllocatePages (EFI_SIZE_TO_PAGES (mSmmMpSyncDataSize)); + ASSERT (mSmmMpSyncData != NULL); + mCpuSmmSyncMode = (SMM_CPU_SYNC_MODE)PcdGet8 (PcdCpuSmmSyncMode); + InitializeMpSyncData (); + + // + // Initialize physical address mask + // NOTE: Physical memory above virtual address limit is not supported !!! + // + AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedFunction, NULL, NULL, NULL); + if (MaxExtendedFunction >= CPUID_VIR_PHY_ADDRESS_SIZE) { + AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &VirPhyAddressSize.Uint32, NULL, NULL, NULL); + } else { + VirPhyAddressSize.Bits.PhysicalAddressBits = 36; + } + gPhyMask = LShiftU64 (1, VirPhyAddressSize.Bits.PhysicalAddressBits) - 1; + // + // Clear the low 12 bits + // + gPhyMask &= 0xfffffffffffff000ULL; + + // + // Create page tables + // + Cr3 = SmmInitPageTable (); + + GdtTssTables = InitGdt (Cr3, &GdtTableStepSize); + + // + // Install SMI handler for each CPU + // + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + InstallSmiHandler ( + Index, + (UINT32)mCpuHotPlugData.SmBase[Index], + (VOID*)((UINTN)Stacks + (StackSize + ShadowStackSize) * Index), + StackSize, + (UINTN)(GdtTssTables + GdtTableStepSize * Index), + gcSmiGdtr.Limit + 1, + gcSmiIdtr.Base, + gcSmiIdtr.Limit + 1, + Cr3 + ); + } + + // + // Record current MTRR settings + // + ZeroMem (&gSmiMtrrs, sizeof (gSmiMtrrs)); + MtrrGetAllMtrrs (&gSmiMtrrs); + + return Cr3; +} + +/** + + Register the SMM Foundation entry point. + + @param This Pointer to EFI_SMM_CONFIGURATION_PROTOCOL instance + @param SmmEntryPoint SMM Foundation EntryPoint + + @retval EFI_SUCCESS Successfully to register SMM foundation entry point + +**/ +EFI_STATUS +EFIAPI +RegisterSmmEntry ( + IN CONST EFI_SMM_CONFIGURATION_PROTOCOL *This, + IN EFI_SMM_ENTRY_POINT SmmEntryPoint + ) +{ + // + // Record SMM Foundation EntryPoint, later invoke it on SMI entry vector. + // + gSmmCpuPrivate->SmmCoreEntry = SmmEntryPoint; + return EFI_SUCCESS; +} + +/** + + Register the SMM Foundation entry point. + + @param[in] Procedure A pointer to the code stream to be run on the designated target AP + of the system. Type EFI_AP_PROCEDURE is defined below in Volume 2 + with the related definitions of + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs. + If caller may pass a value of NULL to deregister any existing + startup procedure. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code that is + run by the AP. It is an optional common mailbox between APs and + the caller to share information + + @retval EFI_SUCCESS The Procedure has been set successfully. + @retval EFI_INVALID_PARAMETER The Procedure is NULL but ProcedureArguments not NULL. + +**/ +EFI_STATUS +RegisterStartupProcedure ( + IN EFI_AP_PROCEDURE Procedure, + IN OUT VOID *ProcedureArguments OPTIONAL + ) +{ + if (Procedure == NULL && ProcedureArguments != NULL) { + return EFI_INVALID_PARAMETER; + } + if (mSmmMpSyncData == NULL) { + return EFI_NOT_READY; + } + + mSmmMpSyncData->StartupProcedure = Procedure; + mSmmMpSyncData->StartupProcArgs = ProcedureArguments; + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c new file mode 100644 index 00000000..028f4362 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c @@ -0,0 +1,1470 @@ +/** @file +Agent Module to load other modules to deploy SMM Entry Vector for X86 CPU. + +Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" + +// +// SMM CPU Private Data structure that contains SMM Configuration Protocol +// along its supporting fields. +// +SMM_CPU_PRIVATE_DATA mSmmCpuPrivateData = { + SMM_CPU_PRIVATE_DATA_SIGNATURE, // Signature + NULL, // SmmCpuHandle + NULL, // Pointer to ProcessorInfo array + NULL, // Pointer to Operation array + NULL, // Pointer to CpuSaveStateSize array + NULL, // Pointer to CpuSaveState array + { {0} }, // SmmReservedSmramRegion + { + SmmStartupThisAp, // SmmCoreEntryContext.SmmStartupThisAp + 0, // SmmCoreEntryContext.CurrentlyExecutingCpu + 0, // SmmCoreEntryContext.NumberOfCpus + NULL, // SmmCoreEntryContext.CpuSaveStateSize + NULL // SmmCoreEntryContext.CpuSaveState + }, + NULL, // SmmCoreEntry + { + mSmmCpuPrivateData.SmmReservedSmramRegion, // SmmConfiguration.SmramReservedRegions + RegisterSmmEntry // SmmConfiguration.RegisterSmmEntry + }, + NULL, // pointer to Ap Wrapper Func array + {NULL, NULL}, // List_Entry for Tokens. +}; + +CPU_HOT_PLUG_DATA mCpuHotPlugData = { + CPU_HOT_PLUG_DATA_REVISION_1, // Revision + 0, // Array Length of SmBase and APIC ID + NULL, // Pointer to APIC ID array + NULL, // Pointer to SMBASE array + 0, // Reserved + 0, // SmrrBase + 0 // SmrrSize +}; + +// +// Global pointer used to access mSmmCpuPrivateData from outside and inside SMM +// +SMM_CPU_PRIVATE_DATA *gSmmCpuPrivate = &mSmmCpuPrivateData; + +// +// SMM Relocation variables +// +volatile BOOLEAN *mRebased; +volatile BOOLEAN mIsBsp; + +/// +/// Handle for the SMM CPU Protocol +/// +EFI_HANDLE mSmmCpuHandle = NULL; + +/// +/// SMM CPU Protocol instance +/// +EFI_SMM_CPU_PROTOCOL mSmmCpu = { + SmmReadSaveState, + SmmWriteSaveState +}; + +/// +/// SMM Memory Attribute Protocol instance +/// +EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL mSmmMemoryAttribute = { + EdkiiSmmGetMemoryAttributes, + EdkiiSmmSetMemoryAttributes, + EdkiiSmmClearMemoryAttributes +}; + +EFI_CPU_INTERRUPT_HANDLER mExternalVectorTable[EXCEPTION_VECTOR_NUMBER]; + +// +// SMM stack information +// +UINTN mSmmStackArrayBase; +UINTN mSmmStackArrayEnd; +UINTN mSmmStackSize; + +UINTN mSmmShadowStackSize; +BOOLEAN mCetSupported = TRUE; + +UINTN mMaxNumberOfCpus = 1; +UINTN mNumberOfCpus = 1; + +// +// SMM ready to lock flag +// +BOOLEAN mSmmReadyToLock = FALSE; + +// +// Global used to cache PCD for SMM Code Access Check enable +// +BOOLEAN mSmmCodeAccessCheckEnable = FALSE; + +// +// Global copy of the PcdPteMemoryEncryptionAddressOrMask +// +UINT64 mAddressEncMask = 0; + +// +// Spin lock used to serialize setting of SMM Code Access Check feature +// +SPIN_LOCK *mConfigSmmCodeAccessCheckLock = NULL; + +// +// Saved SMM ranges information +// +EFI_SMRAM_DESCRIPTOR *mSmmCpuSmramRanges; +UINTN mSmmCpuSmramRangeCount; + +UINT8 mPhysicalAddressBits; + +// +// Control register contents saved for SMM S3 resume state initialization. +// +UINT32 mSmmCr0; +UINT32 mSmmCr4; + +/** + Initialize IDT to setup exception handlers for SMM. + +**/ +VOID +InitializeSmmIdt ( + VOID + ) +{ + EFI_STATUS Status; + BOOLEAN InterruptState; + IA32_DESCRIPTOR DxeIdtr; + + // + // There are 32 (not 255) entries in it since only processor + // generated exceptions will be handled. + // + gcSmiIdtr.Limit = (sizeof(IA32_IDT_GATE_DESCRIPTOR) * 32) - 1; + // + // Allocate page aligned IDT, because it might be set as read only. + // + gcSmiIdtr.Base = (UINTN)AllocateCodePages (EFI_SIZE_TO_PAGES(gcSmiIdtr.Limit + 1)); + ASSERT (gcSmiIdtr.Base != 0); + ZeroMem ((VOID *)gcSmiIdtr.Base, gcSmiIdtr.Limit + 1); + + // + // Disable Interrupt and save DXE IDT table + // + InterruptState = SaveAndDisableInterrupts (); + AsmReadIdtr (&DxeIdtr); + // + // Load SMM temporary IDT table + // + AsmWriteIdtr (&gcSmiIdtr); + // + // Setup SMM default exception handlers, SMM IDT table + // will be updated and saved in gcSmiIdtr + // + Status = InitializeCpuExceptionHandlers (NULL); + ASSERT_EFI_ERROR (Status); + // + // Restore DXE IDT table and CPU interrupt + // + AsmWriteIdtr ((IA32_DESCRIPTOR *) &DxeIdtr); + SetInterruptState (InterruptState); +} + +/** + Search module name by input IP address and output it. + + @param CallerIpAddress Caller instruction pointer. + +**/ +VOID +DumpModuleInfoByIp ( + IN UINTN CallerIpAddress + ) +{ + UINTN Pe32Data; + VOID *PdbPointer; + + // + // Find Image Base + // + Pe32Data = PeCoffSearchImageBase (CallerIpAddress); + if (Pe32Data != 0) { + DEBUG ((DEBUG_ERROR, "It is invoked from the instruction before IP(0x%p)", (VOID *) CallerIpAddress)); + PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *) Pe32Data); + if (PdbPointer != NULL) { + DEBUG ((DEBUG_ERROR, " in module (%a)\n", PdbPointer)); + } + } +} + +/** + Read information from the CPU save state. + + @param This EFI_SMM_CPU_PROTOCOL instance + @param Width The number of bytes to read from the CPU save state. + @param Register Specifies the CPU register to read form the save state. + @param CpuIndex Specifies the zero-based index of the CPU save state. + @param Buffer Upon return, this holds the CPU register value read from the save state. + + @retval EFI_SUCCESS The register was read from Save State + @retval EFI_NOT_FOUND The register is not defined for the Save State of Processor + @retval EFI_INVALID_PARAMETER This or Buffer is NULL. + +**/ +EFI_STATUS +EFIAPI +SmmReadSaveState ( + IN CONST EFI_SMM_CPU_PROTOCOL *This, + IN UINTN Width, + IN EFI_SMM_SAVE_STATE_REGISTER Register, + IN UINTN CpuIndex, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + + // + // Retrieve pointer to the specified CPU's SMM Save State buffer + // + if ((CpuIndex >= gSmst->NumberOfCpus) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + // + // The SpeculationBarrier() call here is to ensure the above check for the + // CpuIndex has been completed before the execution of subsequent codes. + // + SpeculationBarrier (); + + // + // Check for special EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID + // + if (Register == EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID) { + // + // The pseudo-register only supports the 64-bit size specified by Width. + // + if (Width != sizeof (UINT64)) { + return EFI_INVALID_PARAMETER; + } + // + // If the processor is in SMM at the time the SMI occurred, + // the pseudo register value for EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID is returned in Buffer. + // Otherwise, EFI_NOT_FOUND is returned. + // + if (*(mSmmMpSyncData->CpuData[CpuIndex].Present)) { + *(UINT64 *)Buffer = gSmmCpuPrivate->ProcessorInfo[CpuIndex].ProcessorId; + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } + } + + if (!(*(mSmmMpSyncData->CpuData[CpuIndex].Present))) { + return EFI_INVALID_PARAMETER; + } + + Status = SmmCpuFeaturesReadSaveStateRegister (CpuIndex, Register, Width, Buffer); + if (Status == EFI_UNSUPPORTED) { + Status = ReadSaveStateRegister (CpuIndex, Register, Width, Buffer); + } + return Status; +} + +/** + Write data to the CPU save state. + + @param This EFI_SMM_CPU_PROTOCOL instance + @param Width The number of bytes to read from the CPU save state. + @param Register Specifies the CPU register to write to the save state. + @param CpuIndex Specifies the zero-based index of the CPU save state + @param Buffer Upon entry, this holds the new CPU register value. + + @retval EFI_SUCCESS The register was written from Save State + @retval EFI_NOT_FOUND The register is not defined for the Save State of Processor + @retval EFI_INVALID_PARAMETER ProcessorIndex or Width is not correct + +**/ +EFI_STATUS +EFIAPI +SmmWriteSaveState ( + IN CONST EFI_SMM_CPU_PROTOCOL *This, + IN UINTN Width, + IN EFI_SMM_SAVE_STATE_REGISTER Register, + IN UINTN CpuIndex, + IN CONST VOID *Buffer + ) +{ + EFI_STATUS Status; + + // + // Retrieve pointer to the specified CPU's SMM Save State buffer + // + if ((CpuIndex >= gSmst->NumberOfCpus) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Writes to EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID are ignored + // + if (Register == EFI_SMM_SAVE_STATE_REGISTER_PROCESSOR_ID) { + return EFI_SUCCESS; + } + + if (!mSmmMpSyncData->CpuData[CpuIndex].Present) { + return EFI_INVALID_PARAMETER; + } + + Status = SmmCpuFeaturesWriteSaveStateRegister (CpuIndex, Register, Width, Buffer); + if (Status == EFI_UNSUPPORTED) { + Status = WriteSaveStateRegister (CpuIndex, Register, Width, Buffer); + } + return Status; +} + + +/** + C function for SMI handler. To change all processor's SMMBase Register. + +**/ +VOID +EFIAPI +SmmInitHandler ( + VOID + ) +{ + UINT32 ApicId; + UINTN Index; + + // + // Update SMM IDT entries' code segment and load IDT + // + AsmWriteIdtr (&gcSmiIdtr); + ApicId = GetApicId (); + + ASSERT (mNumberOfCpus <= mMaxNumberOfCpus); + + for (Index = 0; Index < mNumberOfCpus; Index++) { + if (ApicId == (UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId) { + // + // Initialize SMM specific features on the currently executing CPU + // + SmmCpuFeaturesInitializeProcessor ( + Index, + mIsBsp, + gSmmCpuPrivate->ProcessorInfo, + &mCpuHotPlugData + ); + + if (!mSmmS3Flag) { + // + // Check XD and BTS features on each processor on normal boot + // + CheckFeatureSupported (); + } + + if (mIsBsp) { + // + // BSP rebase is already done above. + // Initialize private data during S3 resume + // + InitializeMpSyncData (); + } + + // + // Hook return after RSM to set SMM re-based flag + // + SemaphoreHook (Index, &mRebased[Index]); + + return; + } + } + ASSERT (FALSE); +} + +/** + Relocate SmmBases for each processor. + + Execute on first boot and all S3 resumes + +**/ +VOID +EFIAPI +SmmRelocateBases ( + VOID + ) +{ + UINT8 BakBuf[BACK_BUF_SIZE]; + SMRAM_SAVE_STATE_MAP BakBuf2; + SMRAM_SAVE_STATE_MAP *CpuStatePtr; + UINT8 *U8Ptr; + UINT32 ApicId; + UINTN Index; + UINTN BspIndex; + + // + // Make sure the reserved size is large enough for procedure SmmInitTemplate. + // + ASSERT (sizeof (BakBuf) >= gcSmmInitSize); + + // + // Patch ASM code template with current CR0, CR3, and CR4 values + // + mSmmCr0 = (UINT32)AsmReadCr0 (); + PatchInstructionX86 (gPatchSmmCr0, mSmmCr0, 4); + PatchInstructionX86 (gPatchSmmCr3, AsmReadCr3 (), 4); + mSmmCr4 = (UINT32)AsmReadCr4 (); + PatchInstructionX86 (gPatchSmmCr4, mSmmCr4 & (~CR4_CET_ENABLE), 4); + + // + // Patch GDTR for SMM base relocation + // + gcSmiInitGdtr.Base = gcSmiGdtr.Base; + gcSmiInitGdtr.Limit = gcSmiGdtr.Limit; + + U8Ptr = (UINT8*)(UINTN)(SMM_DEFAULT_SMBASE + SMM_HANDLER_OFFSET); + CpuStatePtr = (SMRAM_SAVE_STATE_MAP *)(UINTN)(SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET); + + // + // Backup original contents at address 0x38000 + // + CopyMem (BakBuf, U8Ptr, sizeof (BakBuf)); + CopyMem (&BakBuf2, CpuStatePtr, sizeof (BakBuf2)); + + // + // Load image for relocation + // + CopyMem (U8Ptr, gcSmmInitTemplate, gcSmmInitSize); + + // + // Retrieve the local APIC ID of current processor + // + ApicId = GetApicId (); + + // + // Relocate SM bases for all APs + // This is APs' 1st SMI - rebase will be done here, and APs' default SMI handler will be overridden by gcSmmInitTemplate + // + mIsBsp = FALSE; + BspIndex = (UINTN)-1; + for (Index = 0; Index < mNumberOfCpus; Index++) { + mRebased[Index] = FALSE; + if (ApicId != (UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId) { + SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId); + // + // Wait for this AP to finish its 1st SMI + // + while (!mRebased[Index]); + } else { + // + // BSP will be Relocated later + // + BspIndex = Index; + } + } + + // + // Relocate BSP's SMM base + // + ASSERT (BspIndex != (UINTN)-1); + mIsBsp = TRUE; + SendSmiIpi (ApicId); + // + // Wait for the BSP to finish its 1st SMI + // + while (!mRebased[BspIndex]); + + // + // Restore contents at address 0x38000 + // + CopyMem (CpuStatePtr, &BakBuf2, sizeof (BakBuf2)); + CopyMem (U8Ptr, BakBuf, sizeof (BakBuf)); +} + +/** + SMM Ready To Lock event notification handler. + + The CPU S3 data is copied to SMRAM for security and mSmmReadyToLock is set to + perform additional lock actions that must be performed from SMM on the next SMI. + + @param[in] Protocol Points to the protocol's unique identifier. + @param[in] Interface Points to the interface instance. + @param[in] Handle The handle on which the interface was installed. + + @retval EFI_SUCCESS Notification handler runs successfully. + **/ +EFI_STATUS +EFIAPI +SmmReadyToLockEventNotify ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + GetAcpiCpuData (); + + // + // Cache a copy of UEFI memory map before we start profiling feature. + // + GetUefiMemoryMap (); + + // + // Set SMM ready to lock flag and return + // + mSmmReadyToLock = TRUE; + return EFI_SUCCESS; +} + +/** + The module Entry Point of the CPU SMM driver. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval Other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +PiCpuSmmEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_MP_SERVICES_PROTOCOL *MpServices; + UINTN NumberOfEnabledProcessors; + UINTN Index; + VOID *Buffer; + UINTN BufferPages; + UINTN TileCodeSize; + UINTN TileDataSize; + UINTN TileSize; + UINT8 *Stacks; + VOID *Registration; + UINT32 RegEax; + UINT32 RegEbx; + UINT32 RegEcx; + UINT32 RegEdx; + UINTN FamilyId; + UINTN ModelId; + UINT32 Cr3; + + // + // Initialize address fixup + // + PiSmmCpuSmmInitFixupAddress (); + PiSmmCpuSmiEntryFixupAddress (); + + // + // Initialize Debug Agent to support source level debug in SMM code + // + InitializeDebugAgent (DEBUG_AGENT_INIT_SMM, NULL, NULL); + + // + // Report the start of CPU SMM initialization. + // + REPORT_STATUS_CODE ( + EFI_PROGRESS_CODE, + EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_PC_SMM_INIT + ); + + // + // Find out SMRR Base and SMRR Size + // + FindSmramInfo (&mCpuHotPlugData.SmrrBase, &mCpuHotPlugData.SmrrSize); + + // + // Get MP Services Protocol + // + Status = SystemTable->BootServices->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpServices); + ASSERT_EFI_ERROR (Status); + + // + // Use MP Services Protocol to retrieve the number of processors and number of enabled processors + // + Status = MpServices->GetNumberOfProcessors (MpServices, &mNumberOfCpus, &NumberOfEnabledProcessors); + ASSERT_EFI_ERROR (Status); + ASSERT (mNumberOfCpus <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber)); + + // + // If support CPU hot plug, PcdCpuSmmEnableBspElection should be set to TRUE. + // A constant BSP index makes no sense because it may be hot removed. + // + DEBUG_CODE ( + if (FeaturePcdGet (PcdCpuHotPlugSupport)) { + + ASSERT (FeaturePcdGet (PcdCpuSmmEnableBspElection)); + } + ); + + // + // Save the PcdCpuSmmCodeAccessCheckEnable value into a global variable. + // + mSmmCodeAccessCheckEnable = PcdGetBool (PcdCpuSmmCodeAccessCheckEnable); + DEBUG ((EFI_D_INFO, "PcdCpuSmmCodeAccessCheckEnable = %d\n", mSmmCodeAccessCheckEnable)); + + // + // Save the PcdPteMemoryEncryptionAddressOrMask value into a global variable. + // Make sure AddressEncMask is contained to smallest supported address field. + // + mAddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64; + DEBUG ((EFI_D_INFO, "mAddressEncMask = 0x%lx\n", mAddressEncMask)); + + // + // If support CPU hot plug, we need to allocate resources for possibly hot-added processors + // + if (FeaturePcdGet (PcdCpuHotPlugSupport)) { + mMaxNumberOfCpus = PcdGet32 (PcdCpuMaxLogicalProcessorNumber); + } else { + mMaxNumberOfCpus = mNumberOfCpus; + } + gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus = mMaxNumberOfCpus; + + // + // The CPU save state and code for the SMI entry point are tiled within an SMRAM + // allocated buffer. The minimum size of this buffer for a uniprocessor system + // is 32 KB, because the entry point is SMBASE + 32KB, and CPU save state area + // just below SMBASE + 64KB. If more than one CPU is present in the platform, + // then the SMI entry point and the CPU save state areas can be tiles to minimize + // the total amount SMRAM required for all the CPUs. The tile size can be computed + // by adding the // CPU save state size, any extra CPU specific context, and + // the size of code that must be placed at the SMI entry point to transfer + // control to a C function in the native SMM execution mode. This size is + // rounded up to the nearest power of 2 to give the tile size for a each CPU. + // The total amount of memory required is the maximum number of CPUs that + // platform supports times the tile size. The picture below shows the tiling, + // where m is the number of tiles that fit in 32KB. + // + // +-----------------------------+ <-- 2^n offset from Base of allocated buffer + // | CPU m+1 Save State | + // +-----------------------------+ + // | CPU m+1 Extra Data | + // +-----------------------------+ + // | Padding | + // +-----------------------------+ + // | CPU 2m SMI Entry | + // +#############################+ <-- Base of allocated buffer + 64 KB + // | CPU m-1 Save State | + // +-----------------------------+ + // | CPU m-1 Extra Data | + // +-----------------------------+ + // | Padding | + // +-----------------------------+ + // | CPU 2m-1 SMI Entry | + // +=============================+ <-- 2^n offset from Base of allocated buffer + // | . . . . . . . . . . . . | + // +=============================+ <-- 2^n offset from Base of allocated buffer + // | CPU 2 Save State | + // +-----------------------------+ + // | CPU 2 Extra Data | + // +-----------------------------+ + // | Padding | + // +-----------------------------+ + // | CPU m+1 SMI Entry | + // +=============================+ <-- Base of allocated buffer + 32 KB + // | CPU 1 Save State | + // +-----------------------------+ + // | CPU 1 Extra Data | + // +-----------------------------+ + // | Padding | + // +-----------------------------+ + // | CPU m SMI Entry | + // +#############################+ <-- Base of allocated buffer + 32 KB == CPU 0 SMBASE + 64 KB + // | CPU 0 Save State | + // +-----------------------------+ + // | CPU 0 Extra Data | + // +-----------------------------+ + // | Padding | + // +-----------------------------+ + // | CPU m-1 SMI Entry | + // +=============================+ <-- 2^n offset from Base of allocated buffer + // | . . . . . . . . . . . . | + // +=============================+ <-- 2^n offset from Base of allocated buffer + // | Padding | + // +-----------------------------+ + // | CPU 1 SMI Entry | + // +=============================+ <-- 2^n offset from Base of allocated buffer + // | Padding | + // +-----------------------------+ + // | CPU 0 SMI Entry | + // +#############################+ <-- Base of allocated buffer == CPU 0 SMBASE + 32 KB + // + + // + // Retrieve CPU Family + // + AsmCpuid (CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL); + FamilyId = (RegEax >> 8) & 0xf; + ModelId = (RegEax >> 4) & 0xf; + if (FamilyId == 0x06 || FamilyId == 0x0f) { + ModelId = ModelId | ((RegEax >> 12) & 0xf0); + } + + RegEdx = 0; + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_EXTENDED_CPU_SIG) { + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx); + } + // + // Determine the mode of the CPU at the time an SMI occurs + // Intel(R) 64 and IA-32 Architectures Software Developer's Manual + // Volume 3C, Section 34.4.1.1 + // + mSmmSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT; + if ((RegEdx & BIT29) != 0) { + mSmmSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_64BIT; + } + if (FamilyId == 0x06) { + if (ModelId == 0x17 || ModelId == 0x0f || ModelId == 0x1c) { + mSmmSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_64BIT; + } + } + + DEBUG ((DEBUG_INFO, "PcdControlFlowEnforcementPropertyMask = %d\n", PcdGet32 (PcdControlFlowEnforcementPropertyMask))); + if (PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) { + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax > CPUID_EXTENDED_FUNCTION) { + AsmCpuidEx (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_SUB_LEAF_INFO, NULL, NULL, &RegEcx, &RegEdx); + DEBUG ((DEBUG_INFO, "CPUID[7/0] ECX - 0x%08x\n", RegEcx)); + DEBUG ((DEBUG_INFO, " CET_SS - 0x%08x\n", RegEcx & CPUID_CET_SS)); + DEBUG ((DEBUG_INFO, " CET_IBT - 0x%08x\n", RegEdx & CPUID_CET_IBT)); + if ((RegEcx & CPUID_CET_SS) == 0) { + mCetSupported = FALSE; + PatchInstructionX86 (mPatchCetSupported, mCetSupported, 1); + } + if (mCetSupported) { + AsmCpuidEx (CPUID_EXTENDED_STATE, CPUID_EXTENDED_STATE_SUB_LEAF, NULL, &RegEbx, &RegEcx, NULL); + DEBUG ((DEBUG_INFO, "CPUID[D/1] EBX - 0x%08x, ECX - 0x%08x\n", RegEbx, RegEcx)); + AsmCpuidEx (CPUID_EXTENDED_STATE, 11, &RegEax, NULL, &RegEcx, NULL); + DEBUG ((DEBUG_INFO, "CPUID[D/11] EAX - 0x%08x, ECX - 0x%08x\n", RegEax, RegEcx)); + AsmCpuidEx(CPUID_EXTENDED_STATE, 12, &RegEax, NULL, &RegEcx, NULL); + DEBUG ((DEBUG_INFO, "CPUID[D/12] EAX - 0x%08x, ECX - 0x%08x\n", RegEax, RegEcx)); + } + } + } else { + mCetSupported = FALSE; + PatchInstructionX86 (mPatchCetSupported, mCetSupported, 1); + } + + // + // Compute tile size of buffer required to hold the CPU SMRAM Save State Map, extra CPU + // specific context start starts at SMBASE + SMM_PSD_OFFSET, and the SMI entry point. + // This size is rounded up to nearest power of 2. + // + TileCodeSize = GetSmiHandlerSize (); + TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB); + TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP); + TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB); + TileSize = TileDataSize + TileCodeSize - 1; + TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize); + DEBUG ((EFI_D_INFO, "SMRAM TileSize = 0x%08x (0x%08x, 0x%08x)\n", TileSize, TileCodeSize, TileDataSize)); + + // + // If the TileSize is larger than space available for the SMI Handler of + // CPU[i], the extra CPU specific context of CPU[i+1], and the SMRAM Save + // State Map of CPU[i+1], then ASSERT(). If this ASSERT() is triggered, then + // the SMI Handler size must be reduced or the size of the extra CPU specific + // context must be reduced. + // + ASSERT (TileSize <= (SMRAM_SAVE_STATE_MAP_OFFSET + sizeof (SMRAM_SAVE_STATE_MAP) - SMM_HANDLER_OFFSET)); + + // + // Allocate buffer for all of the tiles. + // + // Intel(R) 64 and IA-32 Architectures Software Developer's Manual + // Volume 3C, Section 34.11 SMBASE Relocation + // For Pentium and Intel486 processors, the SMBASE values must be + // aligned on a 32-KByte boundary or the processor will enter shutdown + // state during the execution of a RSM instruction. + // + // Intel486 processors: FamilyId is 4 + // Pentium processors : FamilyId is 5 + // + BufferPages = EFI_SIZE_TO_PAGES (SIZE_32KB + TileSize * (mMaxNumberOfCpus - 1)); + if ((FamilyId == 4) || (FamilyId == 5)) { + Buffer = AllocateAlignedCodePages (BufferPages, SIZE_32KB); + } else { + Buffer = AllocateAlignedCodePages (BufferPages, SIZE_4KB); + } + ASSERT (Buffer != NULL); + DEBUG ((EFI_D_INFO, "SMRAM SaveState Buffer (0x%08x, 0x%08x)\n", Buffer, EFI_PAGES_TO_SIZE(BufferPages))); + + // + // Allocate buffer for pointers to array in SMM_CPU_PRIVATE_DATA. + // + gSmmCpuPrivate->ProcessorInfo = (EFI_PROCESSOR_INFORMATION *)AllocatePool (sizeof (EFI_PROCESSOR_INFORMATION) * mMaxNumberOfCpus); + ASSERT (gSmmCpuPrivate->ProcessorInfo != NULL); + + gSmmCpuPrivate->Operation = (SMM_CPU_OPERATION *)AllocatePool (sizeof (SMM_CPU_OPERATION) * mMaxNumberOfCpus); + ASSERT (gSmmCpuPrivate->Operation != NULL); + + gSmmCpuPrivate->CpuSaveStateSize = (UINTN *)AllocatePool (sizeof (UINTN) * mMaxNumberOfCpus); + ASSERT (gSmmCpuPrivate->CpuSaveStateSize != NULL); + + gSmmCpuPrivate->CpuSaveState = (VOID **)AllocatePool (sizeof (VOID *) * mMaxNumberOfCpus); + ASSERT (gSmmCpuPrivate->CpuSaveState != NULL); + + mSmmCpuPrivateData.SmmCoreEntryContext.CpuSaveStateSize = gSmmCpuPrivate->CpuSaveStateSize; + mSmmCpuPrivateData.SmmCoreEntryContext.CpuSaveState = gSmmCpuPrivate->CpuSaveState; + + // + // Allocate buffer for pointers to array in CPU_HOT_PLUG_DATA. + // + mCpuHotPlugData.ApicId = (UINT64 *)AllocatePool (sizeof (UINT64) * mMaxNumberOfCpus); + ASSERT (mCpuHotPlugData.ApicId != NULL); + mCpuHotPlugData.SmBase = (UINTN *)AllocatePool (sizeof (UINTN) * mMaxNumberOfCpus); + ASSERT (mCpuHotPlugData.SmBase != NULL); + mCpuHotPlugData.ArrayLength = (UINT32)mMaxNumberOfCpus; + + // + // Retrieve APIC ID of each enabled processor from the MP Services protocol. + // Also compute the SMBASE address, CPU Save State address, and CPU Save state + // size for each CPU in the platform + // + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + mCpuHotPlugData.SmBase[Index] = (UINTN)Buffer + Index * TileSize - SMM_HANDLER_OFFSET; + gSmmCpuPrivate->CpuSaveStateSize[Index] = sizeof(SMRAM_SAVE_STATE_MAP); + gSmmCpuPrivate->CpuSaveState[Index] = (VOID *)(mCpuHotPlugData.SmBase[Index] + SMRAM_SAVE_STATE_MAP_OFFSET); + gSmmCpuPrivate->Operation[Index] = SmmCpuNone; + + if (Index < mNumberOfCpus) { + Status = MpServices->GetProcessorInfo (MpServices, Index, &gSmmCpuPrivate->ProcessorInfo[Index]); + ASSERT_EFI_ERROR (Status); + mCpuHotPlugData.ApicId[Index] = gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId; + + DEBUG ((EFI_D_INFO, "CPU[%03x] APIC ID=%04x SMBASE=%08x SaveState=%08x Size=%08x\n", + Index, + (UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId, + mCpuHotPlugData.SmBase[Index], + gSmmCpuPrivate->CpuSaveState[Index], + gSmmCpuPrivate->CpuSaveStateSize[Index] + )); + } else { + gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId = INVALID_APIC_ID; + mCpuHotPlugData.ApicId[Index] = INVALID_APIC_ID; + } + } + + // + // Allocate SMI stacks for all processors. + // + mSmmStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStackSize))); + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + // + // 2 more pages is allocated for each processor. + // one is guard page and the other is known good stack. + // + // +-------------------------------------------+-----+-------------------------------------------+ + // | Known Good Stack | Guard Page | SMM Stack | ... | Known Good Stack | Guard Page | SMM Stack | + // +-------------------------------------------+-----+-------------------------------------------+ + // | | | | + // |<-------------- Processor 0 -------------->| |<-------------- Processor n -------------->| + // + mSmmStackSize += EFI_PAGES_TO_SIZE (2); + } + + mSmmShadowStackSize = 0; + if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) { + // + // Append Shadow Stack after normal stack + // + // |= Stacks + // +--------------------------------------------------+---------------------------------------------------------------+ + // | Known Good Stack | Guard Page | SMM Stack | Known Good Shadow Stack | Guard Page | SMM Shadow Stack | + // +--------------------------------------------------+---------------------------------------------------------------+ + // | |PcdCpuSmmStackSize| |PcdCpuSmmShadowStackSize| + // |<---------------- mSmmStackSize ----------------->|<--------------------- mSmmShadowStackSize ------------------->| + // | | + // |<-------------------------------------------- Processor N ------------------------------------------------------->| + // + mSmmShadowStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize))); + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + mSmmShadowStackSize += EFI_PAGES_TO_SIZE (2); + } + } + + Stacks = (UINT8 *) AllocatePages (gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus * (EFI_SIZE_TO_PAGES (mSmmStackSize + mSmmShadowStackSize))); + ASSERT (Stacks != NULL); + mSmmStackArrayBase = (UINTN)Stacks; + mSmmStackArrayEnd = mSmmStackArrayBase + gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus * (mSmmStackSize + mSmmShadowStackSize) - 1; + + DEBUG ((DEBUG_INFO, "Stacks - 0x%x\n", Stacks)); + DEBUG ((DEBUG_INFO, "mSmmStackSize - 0x%x\n", mSmmStackSize)); + DEBUG ((DEBUG_INFO, "PcdCpuSmmStackGuard - 0x%x\n", FeaturePcdGet (PcdCpuSmmStackGuard))); + if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) { + DEBUG ((DEBUG_INFO, "mSmmShadowStackSize - 0x%x\n", mSmmShadowStackSize)); + } + + // + // Set SMI stack for SMM base relocation + // + PatchInstructionX86 ( + gPatchSmmInitStack, + (UINTN) (Stacks + mSmmStackSize - sizeof (UINTN)), + sizeof (UINTN) + ); + + // + // Initialize IDT + // + InitializeSmmIdt (); + + // + // Relocate SMM Base addresses to the ones allocated from SMRAM + // + mRebased = (BOOLEAN *)AllocateZeroPool (sizeof (BOOLEAN) * mMaxNumberOfCpus); + ASSERT (mRebased != NULL); + SmmRelocateBases (); + + // + // Call hook for BSP to perform extra actions in normal mode after all + // SMM base addresses have been relocated on all CPUs + // + SmmCpuFeaturesSmmRelocationComplete (); + + DEBUG ((DEBUG_INFO, "mXdSupported - 0x%x\n", mXdSupported)); + + // + // SMM Time initialization + // + InitializeSmmTimer (); + + // + // Initialize MP globals + // + Cr3 = InitializeMpServiceData (Stacks, mSmmStackSize, mSmmShadowStackSize); + + if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) { + for (Index = 0; Index < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; Index++) { + SetShadowStack ( + Cr3, + (EFI_PHYSICAL_ADDRESS)(UINTN)Stacks + mSmmStackSize + (mSmmStackSize + mSmmShadowStackSize) * Index, + mSmmShadowStackSize + ); + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + SetNotPresentPage ( + Cr3, + (EFI_PHYSICAL_ADDRESS)(UINTN)Stacks + mSmmStackSize + EFI_PAGES_TO_SIZE(1) + (mSmmStackSize + mSmmShadowStackSize) * Index, + EFI_PAGES_TO_SIZE(1) + ); + } + } + } + + // + // Fill in SMM Reserved Regions + // + gSmmCpuPrivate->SmmReservedSmramRegion[0].SmramReservedStart = 0; + gSmmCpuPrivate->SmmReservedSmramRegion[0].SmramReservedSize = 0; + + // + // Install the SMM Configuration Protocol onto a new handle on the handle database. + // The entire SMM Configuration Protocol is allocated from SMRAM, so only a pointer + // to an SMRAM address will be present in the handle database + // + Status = SystemTable->BootServices->InstallMultipleProtocolInterfaces ( + &gSmmCpuPrivate->SmmCpuHandle, + &gEfiSmmConfigurationProtocolGuid, &gSmmCpuPrivate->SmmConfiguration, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Install the SMM CPU Protocol into SMM protocol database + // + Status = gSmst->SmmInstallProtocolInterface ( + &mSmmCpuHandle, + &gEfiSmmCpuProtocolGuid, + EFI_NATIVE_INTERFACE, + &mSmmCpu + ); + ASSERT_EFI_ERROR (Status); + + // + // Install the SMM Memory Attribute Protocol into SMM protocol database + // + Status = gSmst->SmmInstallProtocolInterface ( + &mSmmCpuHandle, + &gEdkiiSmmMemoryAttributeProtocolGuid, + EFI_NATIVE_INTERFACE, + &mSmmMemoryAttribute + ); + ASSERT_EFI_ERROR (Status); + + // + // Initialize global buffer for MM MP. + // + InitializeDataForMmMp (); + + // + // Install the SMM Mp Protocol into SMM protocol database + // + Status = gSmst->SmmInstallProtocolInterface ( + &mSmmCpuHandle, + &gEfiMmMpProtocolGuid, + EFI_NATIVE_INTERFACE, + &mSmmMp + ); + ASSERT_EFI_ERROR (Status); + + // + // Expose address of CPU Hot Plug Data structure if CPU hot plug is supported. + // + if (FeaturePcdGet (PcdCpuHotPlugSupport)) { + Status = PcdSet64S (PcdCpuHotPlugDataAddress, (UINT64)(UINTN)&mCpuHotPlugData); + ASSERT_EFI_ERROR (Status); + } + + // + // Initialize SMM CPU Services Support + // + Status = InitializeSmmCpuServices (mSmmCpuHandle); + ASSERT_EFI_ERROR (Status); + + // + // register SMM Ready To Lock Protocol notification + // + Status = gSmst->SmmRegisterProtocolNotify ( + &gEfiSmmReadyToLockProtocolGuid, + SmmReadyToLockEventNotify, + &Registration + ); + ASSERT_EFI_ERROR (Status); + + // + // Initialize SMM Profile feature + // + InitSmmProfile (Cr3); + + GetAcpiS3EnableFlag (); + InitSmmS3ResumeState (Cr3); + + DEBUG ((EFI_D_INFO, "SMM CPU Module exit from SMRAM with EFI_SUCCESS\n")); + + return EFI_SUCCESS; +} + +/** + + Find out SMRAM information including SMRR base and SMRR size. + + @param SmrrBase SMRR base + @param SmrrSize SMRR size + +**/ +VOID +FindSmramInfo ( + OUT UINT32 *SmrrBase, + OUT UINT32 *SmrrSize + ) +{ + EFI_STATUS Status; + UINTN Size; + EFI_SMM_ACCESS2_PROTOCOL *SmmAccess; + EFI_SMRAM_DESCRIPTOR *CurrentSmramRange; + UINTN Index; + UINT64 MaxSize; + BOOLEAN Found; + + // + // Get SMM Access Protocol + // + Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess); + ASSERT_EFI_ERROR (Status); + + // + // Get SMRAM information + // + Size = 0; + Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + + mSmmCpuSmramRanges = (EFI_SMRAM_DESCRIPTOR *)AllocatePool (Size); + ASSERT (mSmmCpuSmramRanges != NULL); + + Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmmCpuSmramRanges); + ASSERT_EFI_ERROR (Status); + + mSmmCpuSmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR); + + // + // Find the largest SMRAM range between 1MB and 4GB that is at least 256K - 4K in size + // + CurrentSmramRange = NULL; + for (Index = 0, MaxSize = SIZE_256KB - EFI_PAGE_SIZE; Index < mSmmCpuSmramRangeCount; Index++) { + // + // Skip any SMRAM region that is already allocated, needs testing, or needs ECC initialization + // + if ((mSmmCpuSmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) { + continue; + } + + if (mSmmCpuSmramRanges[Index].CpuStart >= BASE_1MB) { + if ((mSmmCpuSmramRanges[Index].CpuStart + mSmmCpuSmramRanges[Index].PhysicalSize) <= SMRR_MAX_ADDRESS) { + if (mSmmCpuSmramRanges[Index].PhysicalSize >= MaxSize) { + MaxSize = mSmmCpuSmramRanges[Index].PhysicalSize; + CurrentSmramRange = &mSmmCpuSmramRanges[Index]; + } + } + } + } + + ASSERT (CurrentSmramRange != NULL); + + *SmrrBase = (UINT32)CurrentSmramRange->CpuStart; + *SmrrSize = (UINT32)CurrentSmramRange->PhysicalSize; + + do { + Found = FALSE; + for (Index = 0; Index < mSmmCpuSmramRangeCount; Index++) { + if (mSmmCpuSmramRanges[Index].CpuStart < *SmrrBase && + *SmrrBase == (mSmmCpuSmramRanges[Index].CpuStart + mSmmCpuSmramRanges[Index].PhysicalSize)) { + *SmrrBase = (UINT32)mSmmCpuSmramRanges[Index].CpuStart; + *SmrrSize = (UINT32)(*SmrrSize + mSmmCpuSmramRanges[Index].PhysicalSize); + Found = TRUE; + } else if ((*SmrrBase + *SmrrSize) == mSmmCpuSmramRanges[Index].CpuStart && mSmmCpuSmramRanges[Index].PhysicalSize > 0) { + *SmrrSize = (UINT32)(*SmrrSize + mSmmCpuSmramRanges[Index].PhysicalSize); + Found = TRUE; + } + } + } while (Found); + + DEBUG ((EFI_D_INFO, "SMRR Base: 0x%x, SMRR Size: 0x%x\n", *SmrrBase, *SmrrSize)); +} + +/** +Configure SMM Code Access Check feature on an AP. +SMM Feature Control MSR will be locked after configuration. + +@param[in,out] Buffer Pointer to private data buffer. +**/ +VOID +EFIAPI +ConfigSmmCodeAccessCheckOnCurrentProcessor ( + IN OUT VOID *Buffer + ) +{ + UINTN CpuIndex; + UINT64 SmmFeatureControlMsr; + UINT64 NewSmmFeatureControlMsr; + + // + // Retrieve the CPU Index from the context passed in + // + CpuIndex = *(UINTN *)Buffer; + + // + // Get the current SMM Feature Control MSR value + // + SmmFeatureControlMsr = SmmCpuFeaturesGetSmmRegister (CpuIndex, SmmRegFeatureControl); + + // + // Compute the new SMM Feature Control MSR value + // + NewSmmFeatureControlMsr = SmmFeatureControlMsr; + if (mSmmCodeAccessCheckEnable) { + NewSmmFeatureControlMsr |= SMM_CODE_CHK_EN_BIT; + if (FeaturePcdGet (PcdCpuSmmFeatureControlMsrLock)) { + NewSmmFeatureControlMsr |= SMM_FEATURE_CONTROL_LOCK_BIT; + } + } + + // + // Only set the SMM Feature Control MSR value if the new value is different than the current value + // + if (NewSmmFeatureControlMsr != SmmFeatureControlMsr) { + SmmCpuFeaturesSetSmmRegister (CpuIndex, SmmRegFeatureControl, NewSmmFeatureControlMsr); + } + + // + // Release the spin lock user to serialize the updates to the SMM Feature Control MSR + // + ReleaseSpinLock (mConfigSmmCodeAccessCheckLock); +} + +/** +Configure SMM Code Access Check feature for all processors. +SMM Feature Control MSR will be locked after configuration. +**/ +VOID +ConfigSmmCodeAccessCheck ( + VOID + ) +{ + UINTN Index; + EFI_STATUS Status; + + // + // Check to see if the Feature Control MSR is supported on this CPU + // + Index = gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu; + if (!SmmCpuFeaturesIsSmmRegisterSupported (Index, SmmRegFeatureControl)) { + mSmmCodeAccessCheckEnable = FALSE; + return; + } + + // + // Check to see if the CPU supports the SMM Code Access Check feature + // Do not access this MSR unless the CPU supports the SmmRegFeatureControl + // + if ((AsmReadMsr64 (EFI_MSR_SMM_MCA_CAP) & SMM_CODE_ACCESS_CHK_BIT) == 0) { + mSmmCodeAccessCheckEnable = FALSE; + return; + } + + // + // Initialize the lock used to serialize the MSR programming in BSP and all APs + // + InitializeSpinLock (mConfigSmmCodeAccessCheckLock); + + // + // Acquire Config SMM Code Access Check spin lock. The BSP will release the + // spin lock when it is done executing ConfigSmmCodeAccessCheckOnCurrentProcessor(). + // + AcquireSpinLock (mConfigSmmCodeAccessCheckLock); + + // + // Enable SMM Code Access Check feature on the BSP. + // + ConfigSmmCodeAccessCheckOnCurrentProcessor (&Index); + + // + // Enable SMM Code Access Check feature for the APs. + // + for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { + if (Index != gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) { + if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == INVALID_APIC_ID) { + // + // If this processor does not exist + // + continue; + } + // + // Acquire Config SMM Code Access Check spin lock. The AP will release the + // spin lock when it is done executing ConfigSmmCodeAccessCheckOnCurrentProcessor(). + // + AcquireSpinLock (mConfigSmmCodeAccessCheckLock); + + // + // Call SmmStartupThisAp() to enable SMM Code Access Check on an AP. + // + Status = gSmst->SmmStartupThisAp (ConfigSmmCodeAccessCheckOnCurrentProcessor, Index, &Index); + ASSERT_EFI_ERROR (Status); + + // + // Wait for the AP to release the Config SMM Code Access Check spin lock. + // + while (!AcquireSpinLockOrFail (mConfigSmmCodeAccessCheckLock)) { + CpuPause (); + } + + // + // Release the Config SMM Code Access Check spin lock. + // + ReleaseSpinLock (mConfigSmmCodeAccessCheckLock); + } + } +} + +/** + This API provides a way to allocate memory for page table. + + This API can be called more once to allocate memory for page tables. + + Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the + allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL + is returned. If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +AllocatePageTableMemory ( + IN UINTN Pages + ) +{ + VOID *Buffer; + + Buffer = SmmCpuFeaturesAllocatePageTableMemory (Pages); + if (Buffer != NULL) { + return Buffer; + } + return AllocatePages (Pages); +} + +/** + Allocate pages for code. + + @param[in] Pages Number of pages to be allocated. + + @return Allocated memory. +**/ +VOID * +AllocateCodePages ( + IN UINTN Pages + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Memory; + + if (Pages == 0) { + return NULL; + } + + Status = gSmst->SmmAllocatePages (AllocateAnyPages, EfiRuntimeServicesCode, Pages, &Memory); + if (EFI_ERROR (Status)) { + return NULL; + } + return (VOID *) (UINTN) Memory; +} + +/** + Allocate aligned pages for code. + + @param[in] Pages Number of pages to be allocated. + @param[in] Alignment The requested alignment of the allocation. + Must be a power of two. + If Alignment is zero, then byte alignment is used. + + @return Allocated memory. +**/ +VOID * +AllocateAlignedCodePages ( + IN UINTN Pages, + IN UINTN Alignment + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Memory; + UINTN AlignedMemory; + UINTN AlignmentMask; + UINTN UnalignedPages; + UINTN RealPages; + + // + // Alignment must be a power of two or zero. + // + ASSERT ((Alignment & (Alignment - 1)) == 0); + + if (Pages == 0) { + return NULL; + } + if (Alignment > EFI_PAGE_SIZE) { + // + // Calculate the total number of pages since alignment is larger than page size. + // + AlignmentMask = Alignment - 1; + RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment); + // + // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow. + // + ASSERT (RealPages > Pages); + + Status = gSmst->SmmAllocatePages (AllocateAnyPages, EfiRuntimeServicesCode, RealPages, &Memory); + if (EFI_ERROR (Status)) { + return NULL; + } + AlignedMemory = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask; + UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN) Memory); + if (UnalignedPages > 0) { + // + // Free first unaligned page(s). + // + Status = gSmst->SmmFreePages (Memory, UnalignedPages); + ASSERT_EFI_ERROR (Status); + } + Memory = AlignedMemory + EFI_PAGES_TO_SIZE (Pages); + UnalignedPages = RealPages - Pages - UnalignedPages; + if (UnalignedPages > 0) { + // + // Free last unaligned page(s). + // + Status = gSmst->SmmFreePages (Memory, UnalignedPages); + ASSERT_EFI_ERROR (Status); + } + } else { + // + // Do not over-allocate pages in this case. + // + Status = gSmst->SmmAllocatePages (AllocateAnyPages, EfiRuntimeServicesCode, Pages, &Memory); + if (EFI_ERROR (Status)) { + return NULL; + } + AlignedMemory = (UINTN) Memory; + } + return (VOID *) AlignedMemory; +} + +/** + Perform the remaining tasks. + +**/ +VOID +PerformRemainingTasks ( + VOID + ) +{ + if (mSmmReadyToLock) { + // + // Start SMM Profile feature + // + if (FeaturePcdGet (PcdCpuSmmProfileEnable)) { + SmmProfileStart (); + } + // + // Create a mix of 2MB and 4KB page table. Update some memory ranges absent and execute-disable. + // + InitPaging (); + + // + // Mark critical region to be read-only in page table + // + SetMemMapAttributes (); + + if (IsRestrictedMemoryAccess ()) { + // + // For outside SMRAM, we only map SMM communication buffer or MMIO. + // + SetUefiMemMapAttributes (); + + // + // Set page table itself to be read-only + // + SetPageTableAttributes (); + } + + // + // Configure SMM Code Access Check feature if available. + // + ConfigSmmCodeAccessCheck (); + + SmmCpuFeaturesCompleteSmmReadyToLock (); + + // + // Clean SMM ready to lock flag + // + mSmmReadyToLock = FALSE; + } +} + +/** + Perform the pre tasks. + +**/ +VOID +PerformPreTasks ( + VOID + ) +{ + RestoreSmmConfigurationInS3 (); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h new file mode 100644 index 00000000..6f35d899 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h @@ -0,0 +1,1479 @@ +/** @file +Agent Module to load other modules to deploy SMM Entry Vector for X86 CPU. + +Copyright (c) 2009 - 2020, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _CPU_PISMMCPUDXESMM_H_ +#define _CPU_PISMMCPUDXESMM_H_ + +#include <PiSmm.h> + +#include <Protocol/MpService.h> +#include <Protocol/SmmConfiguration.h> +#include <Protocol/SmmCpu.h> +#include <Protocol/SmmAccess2.h> +#include <Protocol/SmmReadyToLock.h> +#include <Protocol/SmmCpuService.h> +#include <Protocol/SmmMemoryAttribute.h> +#include <Protocol/MmMp.h> + +#include <Guid/AcpiS3Context.h> +#include <Guid/MemoryAttributesTable.h> +#include <Guid/PiSmmMemoryAttributesTable.h> + +#include <Library/BaseLib.h> +#include <Library/IoLib.h> +#include <Library/TimerLib.h> +#include <Library/SynchronizationLib.h> +#include <Library/DebugLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/PcdLib.h> +#include <Library/MtrrLib.h> +#include <Library/SmmCpuPlatformHookLib.h> +#include <Library/SmmServicesTableLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include <Library/DebugAgentLib.h> +#include <Library/UefiLib.h> +#include <Library/HobLib.h> +#include <Library/LocalApicLib.h> +#include <Library/UefiCpuLib.h> +#include <Library/CpuExceptionHandlerLib.h> +#include <Library/ReportStatusCodeLib.h> +#include <Library/SmmCpuFeaturesLib.h> +#include <Library/PeCoffGetEntryPointLib.h> +#include <Library/RegisterCpuFeaturesLib.h> + +#include <AcpiCpuData.h> +#include <CpuHotPlugData.h> + +#include <Register/Intel/Cpuid.h> +#include <Register/Intel/Msr.h> + +#include "CpuService.h" +#include "SmmProfile.h" + +// +// CET definition +// +#define CPUID_CET_SS BIT7 +#define CPUID_CET_IBT BIT20 + +#define CR4_CET_ENABLE BIT23 + +#define MSR_IA32_S_CET 0x6A2 +#define MSR_IA32_PL0_SSP 0x6A4 +#define MSR_IA32_INTERRUPT_SSP_TABLE_ADDR 0x6A8 + +typedef union { + struct { + // enable shadow stacks + UINT32 SH_STK_ENP:1; + // enable the WRSS{D,Q}W instructions. + UINT32 WR_SHSTK_EN:1; + // enable tracking of indirect call/jmp targets to be ENDBRANCH instruction. + UINT32 ENDBR_EN:1; + // enable legacy compatibility treatment for indirect call/jmp tracking. + UINT32 LEG_IW_EN:1; + // enable use of no-track prefix on indirect call/jmp. + UINT32 NO_TRACK_EN:1; + // disable suppression of CET indirect branch tracking on legacy compatibility. + UINT32 SUPPRESS_DIS:1; + UINT32 RSVD:4; + // indirect branch tracking is suppressed. + // This bit can be written to 1 only if TRACKER is written as IDLE. + UINT32 SUPPRESS:1; + // Value of the endbranch state machine + // Values: IDLE (0), WAIT_FOR_ENDBRANCH(1). + UINT32 TRACKER:1; + // linear address of a bitmap in memory indicating valid + // pages as target of CALL/JMP_indirect that do not land on ENDBRANCH when CET is enabled + // and not suppressed. Valid when ENDBR_EN is 1. Must be machine canonical when written on + // parts that support 64 bit mode. On parts that do not support 64 bit mode, the bits 63:32 are + // reserved and must be 0. This value is extended by 12 bits at the low end to form the base address + // (this automatically aligns the address on a 4-Kbyte boundary). + UINT32 EB_LEG_BITMAP_BASE_low:12; + UINT32 EB_LEG_BITMAP_BASE_high:32; + } Bits; + UINT64 Uint64; +} MSR_IA32_CET; + +// +// MSRs required for configuration of SMM Code Access Check +// +#define EFI_MSR_SMM_MCA_CAP 0x17D +#define SMM_CODE_ACCESS_CHK_BIT BIT58 + +#define SMM_FEATURE_CONTROL_LOCK_BIT BIT0 +#define SMM_CODE_CHK_EN_BIT BIT2 + +/// +/// Page Table Entry +/// +#define IA32_PG_P BIT0 +#define IA32_PG_RW BIT1 +#define IA32_PG_U BIT2 +#define IA32_PG_WT BIT3 +#define IA32_PG_CD BIT4 +#define IA32_PG_A BIT5 +#define IA32_PG_D BIT6 +#define IA32_PG_PS BIT7 +#define IA32_PG_PAT_2M BIT12 +#define IA32_PG_PAT_4K IA32_PG_PS +#define IA32_PG_PMNT BIT62 +#define IA32_PG_NX BIT63 + +#define PAGE_ATTRIBUTE_BITS (IA32_PG_D | IA32_PG_A | IA32_PG_U | IA32_PG_RW | IA32_PG_P) +// +// Bits 1, 2, 5, 6 are reserved in the IA32 PAE PDPTE +// X64 PAE PDPTE does not have such restriction +// +#define IA32_PAE_PDPTE_ATTRIBUTE_BITS (IA32_PG_P) + +#define PAGE_PROGATE_BITS (IA32_PG_NX | PAGE_ATTRIBUTE_BITS) + +#define PAGING_4K_MASK 0xFFF +#define PAGING_2M_MASK 0x1FFFFF +#define PAGING_1G_MASK 0x3FFFFFFF + +#define PAGING_PAE_INDEX_MASK 0x1FF + +#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull +#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull +#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull + +#define SMRR_MAX_ADDRESS BASE_4GB + +typedef enum { + PageNone, + Page4K, + Page2M, + Page1G, +} PAGE_ATTRIBUTE; + +typedef struct { + PAGE_ATTRIBUTE Attribute; + UINT64 Length; + UINT64 AddressMask; +} PAGE_ATTRIBUTE_TABLE; + +// +// Size of Task-State Segment defined in IA32 Manual +// +#define TSS_SIZE 104 +#define EXCEPTION_TSS_SIZE (TSS_SIZE + 4) // Add 4 bytes SSP +#define TSS_X64_IST1_OFFSET 36 +#define TSS_IA32_CR3_OFFSET 28 +#define TSS_IA32_ESP_OFFSET 56 +#define TSS_IA32_SSP_OFFSET 104 + +#define CR0_WP BIT16 + +// +// Code select value +// +#define PROTECT_MODE_CODE_SEGMENT 0x08 +#define LONG_MODE_CODE_SEGMENT 0x38 + +// +// The size 0x20 must be bigger than +// the size of template code of SmmInit. Currently, +// the size of SmmInit requires the 0x16 Bytes buffer +// at least. +// +#define BACK_BUF_SIZE 0x20 + +#define EXCEPTION_VECTOR_NUMBER 0x20 + +#define INVALID_APIC_ID 0xFFFFFFFFFFFFFFFFULL + +typedef UINT32 SMM_CPU_ARRIVAL_EXCEPTIONS; +#define ARRIVAL_EXCEPTION_BLOCKED 0x1 +#define ARRIVAL_EXCEPTION_DELAYED 0x2 +#define ARRIVAL_EXCEPTION_SMI_DISABLED 0x4 + +// +// Wrapper used to convert EFI_AP_PROCEDURE2 and EFI_AP_PROCEDURE. +// +typedef struct { + EFI_AP_PROCEDURE Procedure; + VOID *ProcedureArgument; +} PROCEDURE_WRAPPER; + +#define PROCEDURE_TOKEN_SIGNATURE SIGNATURE_32 ('P', 'R', 'T', 'S') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + + SPIN_LOCK *SpinLock; + volatile UINT32 RunningApCount; +} PROCEDURE_TOKEN; + +#define PROCEDURE_TOKEN_FROM_LINK(a) CR (a, PROCEDURE_TOKEN, Link, PROCEDURE_TOKEN_SIGNATURE) + +#define TOKEN_BUFFER_SIGNATURE SIGNATURE_32 ('T', 'K', 'B', 'S') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + + UINT8 *Buffer; +} TOKEN_BUFFER; + +#define TOKEN_BUFFER_FROM_LINK(a) CR (a, TOKEN_BUFFER, Link, TOKEN_BUFFER_SIGNATURE) + +// +// Private structure for the SMM CPU module that is stored in DXE Runtime memory +// Contains the SMM Configuration Protocols that is produced. +// Contains a mix of DXE and SMM contents. All the fields must be used properly. +// +#define SMM_CPU_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('s', 'c', 'p', 'u') + +typedef struct { + UINTN Signature; + + EFI_HANDLE SmmCpuHandle; + + EFI_PROCESSOR_INFORMATION *ProcessorInfo; + SMM_CPU_OPERATION *Operation; + UINTN *CpuSaveStateSize; + VOID **CpuSaveState; + + EFI_SMM_RESERVED_SMRAM_REGION SmmReservedSmramRegion[1]; + EFI_SMM_ENTRY_CONTEXT SmmCoreEntryContext; + EFI_SMM_ENTRY_POINT SmmCoreEntry; + + EFI_SMM_CONFIGURATION_PROTOCOL SmmConfiguration; + + PROCEDURE_WRAPPER *ApWrapperFunc; + LIST_ENTRY TokenList; + LIST_ENTRY *FirstFreeToken; +} SMM_CPU_PRIVATE_DATA; + +extern SMM_CPU_PRIVATE_DATA *gSmmCpuPrivate; +extern CPU_HOT_PLUG_DATA mCpuHotPlugData; +extern UINTN mMaxNumberOfCpus; +extern UINTN mNumberOfCpus; +extern EFI_SMM_CPU_PROTOCOL mSmmCpu; +extern EFI_MM_MP_PROTOCOL mSmmMp; +extern UINTN mInternalCr3; + +/// +/// The mode of the CPU at the time an SMI occurs +/// +extern UINT8 mSmmSaveStateRegisterLma; + +// +// SMM CPU Protocol function prototypes. +// + +/** + Read information from the CPU save state. + + @param This EFI_SMM_CPU_PROTOCOL instance + @param Width The number of bytes to read from the CPU save state. + @param Register Specifies the CPU register to read form the save state. + @param CpuIndex Specifies the zero-based index of the CPU save state + @param Buffer Upon return, this holds the CPU register value read from the save state. + + @retval EFI_SUCCESS The register was read from Save State + @retval EFI_NOT_FOUND The register is not defined for the Save State of Processor + @retval EFI_INVALID_PARAMETER This or Buffer is NULL. + +**/ +EFI_STATUS +EFIAPI +SmmReadSaveState ( + IN CONST EFI_SMM_CPU_PROTOCOL *This, + IN UINTN Width, + IN EFI_SMM_SAVE_STATE_REGISTER Register, + IN UINTN CpuIndex, + OUT VOID *Buffer + ); + +/** + Write data to the CPU save state. + + @param This EFI_SMM_CPU_PROTOCOL instance + @param Width The number of bytes to read from the CPU save state. + @param Register Specifies the CPU register to write to the save state. + @param CpuIndex Specifies the zero-based index of the CPU save state + @param Buffer Upon entry, this holds the new CPU register value. + + @retval EFI_SUCCESS The register was written from Save State + @retval EFI_NOT_FOUND The register is not defined for the Save State of Processor + @retval EFI_INVALID_PARAMETER ProcessorIndex or Width is not correct + +**/ +EFI_STATUS +EFIAPI +SmmWriteSaveState ( + IN CONST EFI_SMM_CPU_PROTOCOL *This, + IN UINTN Width, + IN EFI_SMM_SAVE_STATE_REGISTER Register, + IN UINTN CpuIndex, + IN CONST VOID *Buffer + ); + +/** +Read a CPU Save State register on the target processor. + +This function abstracts the differences that whether the CPU Save State register is in the +IA32 CPU Save State Map or X64 CPU Save State Map. + +This function supports reading a CPU Save State register in SMBase relocation handler. + +@param[in] CpuIndex Specifies the zero-based index of the CPU save state. +@param[in] RegisterIndex Index into mSmmCpuWidthOffset[] look up table. +@param[in] Width The number of bytes to read from the CPU save state. +@param[out] Buffer Upon return, this holds the CPU register value read from the save state. + +@retval EFI_SUCCESS The register was read from Save State. +@retval EFI_NOT_FOUND The register is not defined for the Save State of Processor. +@retval EFI_INVALID_PARAMETER Buffer is NULL, or Width does not meet requirement per Register type. + +**/ +EFI_STATUS +EFIAPI +ReadSaveStateRegister ( + IN UINTN CpuIndex, + IN EFI_SMM_SAVE_STATE_REGISTER Register, + IN UINTN Width, + OUT VOID *Buffer + ); + +/** +Write value to a CPU Save State register on the target processor. + +This function abstracts the differences that whether the CPU Save State register is in the +IA32 CPU Save State Map or X64 CPU Save State Map. + +This function supports writing a CPU Save State register in SMBase relocation handler. + +@param[in] CpuIndex Specifies the zero-based index of the CPU save state. +@param[in] RegisterIndex Index into mSmmCpuWidthOffset[] look up table. +@param[in] Width The number of bytes to read from the CPU save state. +@param[in] Buffer Upon entry, this holds the new CPU register value. + +@retval EFI_SUCCESS The register was written to Save State. +@retval EFI_NOT_FOUND The register is not defined for the Save State of Processor. +@retval EFI_INVALID_PARAMETER ProcessorIndex or Width is not correct. + +**/ +EFI_STATUS +EFIAPI +WriteSaveStateRegister ( + IN UINTN CpuIndex, + IN EFI_SMM_SAVE_STATE_REGISTER Register, + IN UINTN Width, + IN CONST VOID *Buffer + ); + +extern CONST UINT8 gcSmmInitTemplate[]; +extern CONST UINT16 gcSmmInitSize; +X86_ASSEMBLY_PATCH_LABEL gPatchSmmCr0; +extern UINT32 mSmmCr0; +X86_ASSEMBLY_PATCH_LABEL gPatchSmmCr3; +extern UINT32 mSmmCr4; +X86_ASSEMBLY_PATCH_LABEL gPatchSmmCr4; +X86_ASSEMBLY_PATCH_LABEL gPatchSmmInitStack; +X86_ASSEMBLY_PATCH_LABEL mPatchCetSupported; +extern BOOLEAN mCetSupported; + +/** + Semaphore operation for all processor relocate SMMBase. +**/ +VOID +EFIAPI +SmmRelocationSemaphoreComplete ( + VOID + ); + +/// +/// The type of SMM CPU Information +/// +typedef struct { + SPIN_LOCK *Busy; + volatile EFI_AP_PROCEDURE2 Procedure; + volatile VOID *Parameter; + volatile UINT32 *Run; + volatile BOOLEAN *Present; + PROCEDURE_TOKEN *Token; + EFI_STATUS *Status; +} SMM_CPU_DATA_BLOCK; + +typedef enum { + SmmCpuSyncModeTradition, + SmmCpuSyncModeRelaxedAp, + SmmCpuSyncModeMax +} SMM_CPU_SYNC_MODE; + +typedef struct { + // + // Pointer to an array. The array should be located immediately after this structure + // so that UC cache-ability can be set together. + // + SMM_CPU_DATA_BLOCK *CpuData; + volatile UINT32 *Counter; + volatile UINT32 BspIndex; + volatile BOOLEAN *InsideSmm; + volatile BOOLEAN *AllCpusInSync; + volatile SMM_CPU_SYNC_MODE EffectiveSyncMode; + volatile BOOLEAN SwitchBsp; + volatile BOOLEAN *CandidateBsp; + EFI_AP_PROCEDURE StartupProcedure; + VOID *StartupProcArgs; +} SMM_DISPATCHER_MP_SYNC_DATA; + +#define SMM_PSD_OFFSET 0xfb00 + +/// +/// All global semaphores' pointer +/// +typedef struct { + volatile UINT32 *Counter; + volatile BOOLEAN *InsideSmm; + volatile BOOLEAN *AllCpusInSync; + SPIN_LOCK *PFLock; + SPIN_LOCK *CodeAccessCheckLock; +} SMM_CPU_SEMAPHORE_GLOBAL; + +/// +/// All semaphores for each processor +/// +typedef struct { + SPIN_LOCK *Busy; + volatile UINT32 *Run; + volatile BOOLEAN *Present; + SPIN_LOCK *Token; +} SMM_CPU_SEMAPHORE_CPU; + +/// +/// All semaphores' information +/// +typedef struct { + SMM_CPU_SEMAPHORE_GLOBAL SemaphoreGlobal; + SMM_CPU_SEMAPHORE_CPU SemaphoreCpu; +} SMM_CPU_SEMAPHORES; + +extern IA32_DESCRIPTOR gcSmiGdtr; +extern EFI_PHYSICAL_ADDRESS mGdtBuffer; +extern UINTN mGdtBufferSize; +extern IA32_DESCRIPTOR gcSmiIdtr; +extern VOID *gcSmiIdtrPtr; +extern UINT64 gPhyMask; +extern SMM_DISPATCHER_MP_SYNC_DATA *mSmmMpSyncData; +extern UINTN mSmmStackArrayBase; +extern UINTN mSmmStackArrayEnd; +extern UINTN mSmmStackSize; +extern EFI_SMM_CPU_SERVICE_PROTOCOL mSmmCpuService; +extern IA32_DESCRIPTOR gcSmiInitGdtr; +extern SMM_CPU_SEMAPHORES mSmmCpuSemaphores; +extern UINTN mSemaphoreSize; +extern SPIN_LOCK *mPFLock; +extern SPIN_LOCK *mConfigSmmCodeAccessCheckLock; +extern EFI_SMRAM_DESCRIPTOR *mSmmCpuSmramRanges; +extern UINTN mSmmCpuSmramRangeCount; +extern UINT8 mPhysicalAddressBits; + +// +// Copy of the PcdPteMemoryEncryptionAddressOrMask +// +extern UINT64 mAddressEncMask; + +/** + Create 4G PageTable in SMRAM. + + @param[in] Is32BitPageTable Whether the page table is 32-bit PAE + @return PageTable Address + +**/ +UINT32 +Gen4GPageTable ( + IN BOOLEAN Is32BitPageTable + ); + + +/** + Initialize global data for MP synchronization. + + @param Stacks Base address of SMI stack buffer for all processors. + @param StackSize Stack size for each processor in SMM. + @param ShadowStackSize Shadow Stack size for each processor in SMM. + +**/ +UINT32 +InitializeMpServiceData ( + IN VOID *Stacks, + IN UINTN StackSize, + IN UINTN ShadowStackSize + ); + +/** + Initialize Timer for SMM AP Sync. + +**/ +VOID +InitializeSmmTimer ( + VOID + ); + +/** + Start Timer for SMM AP Sync. + +**/ +UINT64 +EFIAPI +StartSyncTimer ( + VOID + ); + +/** + Check if the SMM AP Sync timer is timeout. + + @param Timer The start timer from the begin. + +**/ +BOOLEAN +EFIAPI +IsSyncTimerTimeout ( + IN UINT64 Timer + ); + +/** + Initialize IDT for SMM Stack Guard. + +**/ +VOID +EFIAPI +InitializeIDTSmmStackGuard ( + VOID + ); + +/** + Initialize Gdt for all processors. + + @param[in] Cr3 CR3 value. + @param[out] GdtStepSize The step size for GDT table. + + @return GdtBase for processor 0. + GdtBase for processor X is: GdtBase + (GdtStepSize * X) +**/ +VOID * +InitGdt ( + IN UINTN Cr3, + OUT UINTN *GdtStepSize + ); + +/** + + Register the SMM Foundation entry point. + + @param This Pointer to EFI_SMM_CONFIGURATION_PROTOCOL instance + @param SmmEntryPoint SMM Foundation EntryPoint + + @retval EFI_SUCCESS Successfully to register SMM foundation entry point + +**/ +EFI_STATUS +EFIAPI +RegisterSmmEntry ( + IN CONST EFI_SMM_CONFIGURATION_PROTOCOL *This, + IN EFI_SMM_ENTRY_POINT SmmEntryPoint + ); + +/** + Create PageTable for SMM use. + + @return PageTable Address + +**/ +UINT32 +SmmInitPageTable ( + VOID + ); + +/** + Schedule a procedure to run on the specified CPU. + + @param Procedure The address of the procedure to run + @param CpuIndex Target CPU number + @param ProcArguments The parameter to pass to the procedure + + @retval EFI_INVALID_PARAMETER CpuNumber not valid + @retval EFI_INVALID_PARAMETER CpuNumber specifying BSP + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber did not enter SMM + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber is busy + @retval EFI_SUCCESS - The procedure has been successfully scheduled + +**/ +EFI_STATUS +EFIAPI +SmmStartupThisAp ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN CpuIndex, + IN OUT VOID *ProcArguments OPTIONAL + ); + +/** + Schedule a procedure to run on the specified CPU in a blocking fashion. + + @param Procedure The address of the procedure to run + @param CpuIndex Target CPU Index + @param ProcArguments The parameter to pass to the procedure + + @retval EFI_INVALID_PARAMETER CpuNumber not valid + @retval EFI_INVALID_PARAMETER CpuNumber specifying BSP + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber did not enter SMM + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber is busy + @retval EFI_SUCCESS The procedure has been successfully scheduled + +**/ +EFI_STATUS +EFIAPI +SmmBlockingStartupThisAp ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN CpuIndex, + IN OUT VOID *ProcArguments OPTIONAL + ); + +/** + This function sets the attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attributes The bit mask of attributes to set for the memory region. + + @retval EFI_SUCCESS The attributes were set for the memory region. + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be set together. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +SmmSetMemoryAttributes ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +/** + This function clears the attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attributes The bit mask of attributes to clear for the memory region. + + @retval EFI_SUCCESS The attributes were cleared for the memory region. + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be set together. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +SmmClearMemoryAttributes ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +/** + Initialize MP synchronization data. + +**/ +VOID +EFIAPI +InitializeMpSyncData ( + VOID + ); + +/** + + Find out SMRAM information including SMRR base and SMRR size. + + @param SmrrBase SMRR base + @param SmrrSize SMRR size + +**/ +VOID +FindSmramInfo ( + OUT UINT32 *SmrrBase, + OUT UINT32 *SmrrSize + ); + +/** + Relocate SmmBases for each processor. + + Execute on first boot and all S3 resumes + +**/ +VOID +EFIAPI +SmmRelocateBases ( + VOID + ); + +/** + Page Fault handler for SMM use. + + @param InterruptType Defines the type of interrupt or exception that + occurred on the processor.This parameter is processor architecture specific. + @param SystemContext A pointer to the processor context when + the interrupt occurred on the processor. +**/ +VOID +EFIAPI +SmiPFHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +/** + Perform the remaining tasks. + +**/ +VOID +PerformRemainingTasks ( + VOID + ); + +/** + Perform the pre tasks. + +**/ +VOID +PerformPreTasks ( + VOID + ); + +/** + Initialize MSR spin lock by MSR index. + + @param MsrIndex MSR index value. + +**/ +VOID +InitMsrSpinLockByIndex ( + IN UINT32 MsrIndex + ); + +/** + Hook return address of SMM Save State so that semaphore code + can be executed immediately after AP exits SMM to indicate to + the BSP that an AP has exited SMM after SMBASE relocation. + + @param[in] CpuIndex The processor index. + @param[in] RebasedFlag A pointer to a flag that is set to TRUE + immediately after AP exits SMM. + +**/ +VOID +SemaphoreHook ( + IN UINTN CpuIndex, + IN volatile BOOLEAN *RebasedFlag + ); + +/** +Configure SMM Code Access Check feature for all processors. +SMM Feature Control MSR will be locked after configuration. +**/ +VOID +ConfigSmmCodeAccessCheck ( + VOID + ); + +/** + Hook the code executed immediately after an RSM instruction on the currently + executing CPU. The mode of code executed immediately after RSM must be + detected, and the appropriate hook must be selected. Always clear the auto + HALT restart flag if it is set. + + @param[in] CpuIndex The processor index for the currently + executing CPU. + @param[in] CpuState Pointer to SMRAM Save State Map for the + currently executing CPU. + @param[in] NewInstructionPointer32 Instruction pointer to use if resuming to + 32-bit mode from 64-bit SMM. + @param[in] NewInstructionPointer Instruction pointer to use if resuming to + same mode as SMM. + + @retval The value of the original instruction pointer before it was hooked. + +**/ +UINT64 +EFIAPI +HookReturnFromSmm ( + IN UINTN CpuIndex, + SMRAM_SAVE_STATE_MAP *CpuState, + UINT64 NewInstructionPointer32, + UINT64 NewInstructionPointer + ); + +/** + Get the size of the SMI Handler in bytes. + + @retval The size, in bytes, of the SMI Handler. + +**/ +UINTN +EFIAPI +GetSmiHandlerSize ( + VOID + ); + +/** + Install the SMI handler for the CPU specified by CpuIndex. This function + is called by the CPU that was elected as monarch during System Management + Mode initialization. + + @param[in] CpuIndex The index of the CPU to install the custom SMI handler. + The value must be between 0 and the NumberOfCpus field + in the System Management System Table (SMST). + @param[in] SmBase The SMBASE address for the CPU specified by CpuIndex. + @param[in] SmiStack The stack to use when an SMI is processed by the + the CPU specified by CpuIndex. + @param[in] StackSize The size, in bytes, if the stack used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] GdtBase The base address of the GDT to use when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] GdtSize The size, in bytes, of the GDT used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] IdtBase The base address of the IDT to use when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] IdtSize The size, in bytes, of the IDT used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] Cr3 The base address of the page tables to use when an SMI + is processed by the CPU specified by CpuIndex. +**/ +VOID +EFIAPI +InstallSmiHandler ( + IN UINTN CpuIndex, + IN UINT32 SmBase, + IN VOID *SmiStack, + IN UINTN StackSize, + IN UINTN GdtBase, + IN UINTN GdtSize, + IN UINTN IdtBase, + IN UINTN IdtSize, + IN UINT32 Cr3 + ); + +/** + Search module name by input IP address and output it. + + @param CallerIpAddress Caller instruction pointer. + +**/ +VOID +DumpModuleInfoByIp ( + IN UINTN CallerIpAddress + ); + +/** + This function sets memory attribute according to MemoryAttributesTable. +**/ +VOID +SetMemMapAttributes ( + VOID + ); + +/** + This function sets UEFI memory attribute according to UEFI memory map. +**/ +VOID +SetUefiMemMapAttributes ( + VOID + ); + +/** + Return if the Address is forbidden as SMM communication buffer. + + @param[in] Address the address to be checked + + @return TRUE The address is forbidden as SMM communication buffer. + @return FALSE The address is allowed as SMM communication buffer. +**/ +BOOLEAN +IsSmmCommBufferForbiddenAddress ( + IN UINT64 Address + ); + +/** + This function caches the UEFI memory map information. +**/ +VOID +GetUefiMemoryMap ( + VOID + ); + +/** + This function sets memory attribute for page table. +**/ +VOID +SetPageTableAttributes ( + VOID + ); + +/** + Get page table base address and the depth of the page table. + + @param[out] Base Page table base address. + @param[out] FiveLevels TRUE means 5 level paging. FALSE means 4 level paging. +**/ +VOID +GetPageTable ( + OUT UINTN *Base, + OUT BOOLEAN *FiveLevels OPTIONAL + ); + +/** + This function sets the attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attributes The bit mask of attributes to set for the memory region. + @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted. + + @retval EFI_SUCCESS The attributes were set for the memory region. + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be set together. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +SmmSetMemoryAttributesEx ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes, + OUT BOOLEAN *IsSplitted OPTIONAL + ); + +/** + This function clears the attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attributes The bit mask of attributes to clear for the memory region. + @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted. + + @retval EFI_SUCCESS The attributes were cleared for the memory region. + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be set together. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +SmmClearMemoryAttributesEx ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes, + OUT BOOLEAN *IsSplitted OPTIONAL + ); + +/** + This API provides a way to allocate memory for page table. + + This API can be called more once to allocate memory for page tables. + + Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the + allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL + is returned. If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +AllocatePageTableMemory ( + IN UINTN Pages + ); + +/** + Allocate pages for code. + + @param[in] Pages Number of pages to be allocated. + + @return Allocated memory. +**/ +VOID * +AllocateCodePages ( + IN UINTN Pages + ); + +/** + Allocate aligned pages for code. + + @param[in] Pages Number of pages to be allocated. + @param[in] Alignment The requested alignment of the allocation. + Must be a power of two. + If Alignment is zero, then byte alignment is used. + + @return Allocated memory. +**/ +VOID * +AllocateAlignedCodePages ( + IN UINTN Pages, + IN UINTN Alignment + ); + + +// +// S3 related global variable and function prototype. +// + +extern BOOLEAN mSmmS3Flag; + +/** + Initialize SMM S3 resume state structure used during S3 Resume. + + @param[in] Cr3 The base address of the page tables to use in SMM. + +**/ +VOID +InitSmmS3ResumeState ( + IN UINT32 Cr3 + ); + +/** + Get ACPI CPU data. + +**/ +VOID +GetAcpiCpuData ( + VOID + ); + +/** + Restore SMM Configuration in S3 boot path. + +**/ +VOID +RestoreSmmConfigurationInS3 ( + VOID + ); + +/** + Get ACPI S3 enable flag. + +**/ +VOID +GetAcpiS3EnableFlag ( + VOID + ); + +/** + Transfer AP to safe hlt-loop after it finished restore CPU features on S3 patch. + + @param[in] ApHltLoopCode The address of the safe hlt-loop function. + @param[in] TopOfStack A pointer to the new stack to use for the ApHltLoopCode. + @param[in] NumberToFinishAddress Address of Semaphore of APs finish count. + +**/ +VOID +TransferApToSafeState ( + IN UINTN ApHltLoopCode, + IN UINTN TopOfStack, + IN UINTN NumberToFinishAddress + ); + +/** + Set ShadowStack memory. + + @param[in] Cr3 The page table base address. + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + + @retval EFI_SUCCESS The shadow stack memory is set. +**/ +EFI_STATUS +SetShadowStack ( + IN UINTN Cr3, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +/** + Set not present memory. + + @param[in] Cr3 The page table base address. + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + + @retval EFI_SUCCESS The not present memory is set. +**/ +EFI_STATUS +SetNotPresentPage ( + IN UINTN Cr3, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +/** + Initialize the shadow stack related data structure. + + @param CpuIndex The index of CPU. + @param ShadowStack The bottom of the shadow stack for this CPU. +**/ +VOID +InitShadowStack ( + IN UINTN CpuIndex, + IN VOID *ShadowStack + ); + +/** + This function set given attributes of the memory region specified by + BaseAddress and Length. + + @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance. + @param BaseAddress The physical address that is the start address of + a memory region. + @param Length The size in bytes of the memory region. + @param Attributes The bit mask of attributes to set for the memory + region. + + @retval EFI_SUCCESS The attributes were set for the memory region. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of + attributes that cannot be set together. + @retval EFI_UNSUPPORTED The processor does not support one or more + bytes of the memory resource range specified + by BaseAddress and Length. + The bit mask of attributes is not supported for + the memory resource range specified by + BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +EdkiiSmmSetMemoryAttributes ( + IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +/** + This function clears given attributes of the memory region specified by + BaseAddress and Length. + + @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance. + @param BaseAddress The physical address that is the start address of + a memory region. + @param Length The size in bytes of the memory region. + @param Attributes The bit mask of attributes to clear for the memory + region. + + @retval EFI_SUCCESS The attributes were cleared for the memory region. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of + attributes that cannot be cleared together. + @retval EFI_UNSUPPORTED The processor does not support one or more + bytes of the memory resource range specified + by BaseAddress and Length. + The bit mask of attributes is not supported for + the memory resource range specified by + BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +EdkiiSmmClearMemoryAttributes ( + IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +/** + This function retrieves the attributes of the memory region specified by + BaseAddress and Length. If different attributes are got from different part + of the memory region, EFI_NO_MAPPING will be returned. + + @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance. + @param BaseAddress The physical address that is the start address of + a memory region. + @param Length The size in bytes of the memory region. + @param Attributes Pointer to attributes returned. + + @retval EFI_SUCCESS The attributes got for the memory region. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes is NULL. + @retval EFI_NO_MAPPING Attributes are not consistent cross the memory + region. + @retval EFI_UNSUPPORTED The processor does not support one or more + bytes of the memory resource range specified + by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +EdkiiSmmGetMemoryAttributes ( + IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 *Attributes + ); + +/** + This function fixes up the address of the global variable or function + referred in SmmInit assembly files to be the absolute address. +**/ +VOID +EFIAPI +PiSmmCpuSmmInitFixupAddress ( + ); + +/** + This function fixes up the address of the global variable or function + referred in SmiEntry assembly files to be the absolute address. +**/ +VOID +EFIAPI +PiSmmCpuSmiEntryFixupAddress ( + ); + +/** + This function reads CR2 register when on-demand paging is enabled + for 64 bit and no action for 32 bit. + + @param[out] *Cr2 Pointer to variable to hold CR2 register value. +**/ +VOID +SaveCr2 ( + OUT UINTN *Cr2 + ); + +/** + This function writes into CR2 register when on-demand paging is enabled + for 64 bit and no action for 32 bit. + + @param[in] Cr2 Value to write into CR2 register. +**/ +VOID +RestoreCr2 ( + IN UINTN Cr2 + ); + +/** + Schedule a procedure to run on the specified CPU. + + @param[in] Procedure The address of the procedure to run + @param[in] CpuIndex Target CPU Index + @param[in,out] ProcArguments The parameter to pass to the procedure + @param[in,out] Token This is an optional parameter that allows the caller to execute the + procedure in a blocking or non-blocking fashion. If it is NULL the + call is blocking, and the call will not return until the AP has + completed the procedure. If the token is not NULL, the call will + return immediately. The caller can check whether the procedure has + completed with CheckOnProcedure or WaitForProcedure. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for the APs to finish + execution of Procedure, either for blocking or non-blocking mode. + Zero means infinity. If the timeout expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. If + the timeout expires in blocking mode, the call returns EFI_TIMEOUT. + If the timeout expires in non-blocking mode, the timeout determined + can be through CheckOnProcedure or WaitForProcedure. + Note that timeout support is optional. Whether an implementation + supports this feature can be determined via the Attributes data + member. + @param[in,out] CpuStatus This optional pointer may be used to get the status code returned + by Procedure when it completes execution on the target AP, or with + EFI_TIMEOUT if the Procedure fails to complete within the optional + timeout. The implementation will update this variable with + EFI_NOT_READY prior to starting Procedure on the target AP. + + @retval EFI_INVALID_PARAMETER CpuNumber not valid + @retval EFI_INVALID_PARAMETER CpuNumber specifying BSP + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber did not enter SMM + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber is busy + @retval EFI_SUCCESS The procedure has been successfully scheduled + +**/ +EFI_STATUS +InternalSmmStartupThisAp ( + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN CpuIndex, + IN OUT VOID *ProcArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN UINTN TimeoutInMicroseconds, + IN OUT EFI_STATUS *CpuStatus + ); + +/** + Checks whether the input token is the current used token. + + @param[in] Token This parameter describes the token that was passed into DispatchProcedure or + BroadcastProcedure. + + @retval TRUE The input token is the current used token. + @retval FALSE The input token is not the current used token. +**/ +BOOLEAN +IsTokenInUse ( + IN SPIN_LOCK *Token + ); + +/** + Checks status of specified AP. + + This function checks whether the specified AP has finished the task assigned + by StartupThisAP(), and whether timeout expires. + + @param[in] Token This parameter describes the token that was passed into DispatchProcedure or + BroadcastProcedure. + + @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs(). + @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired. +**/ +EFI_STATUS +IsApReady ( + IN SPIN_LOCK *Token + ); + +/** + Check whether it is an present AP. + + @param CpuIndex The AP index which calls this function. + + @retval TRUE It's a present AP. + @retval TRUE This is not an AP or it is not present. + +**/ +BOOLEAN +IsPresentAp ( + IN UINTN CpuIndex + ); + +/** + Worker function to execute a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. + @param[in,out] ProcedureArguments The parameter passed into Procedure for + all APs. + @param[in,out] Token This is an optional parameter that allows the caller to execute the + procedure in a blocking or non-blocking fashion. If it is NULL the + call is blocking, and the call will not return until the AP has + completed the procedure. If the token is not NULL, the call will + return immediately. The caller can check whether the procedure has + completed with CheckOnProcedure or WaitForProcedure. + @param[in,out] CPUStatus This optional pointer may be used to get the status code returned + by Procedure when it completes execution on the target AP, or with + EFI_TIMEOUT if the Procedure fails to complete within the optional + timeout. The implementation will update this variable with + EFI_NOT_READY prior to starting Procedure on the target AP. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval others Failed to Startup all APs. + +**/ +EFI_STATUS +InternalSmmStartupAllAPs ( + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN TimeoutInMicroseconds, + IN OUT VOID *ProcedureArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN OUT EFI_STATUS *CPUStatus + ); + +/** + + Register the SMM Foundation entry point. + + @param[in] Procedure A pointer to the code stream to be run on the designated target AP + of the system. Type EFI_AP_PROCEDURE is defined below in Volume 2 + with the related definitions of + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs. + If caller may pass a value of NULL to deregister any existing + startup procedure. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code that is + run by the AP. It is an optional common mailbox between APs and + the caller to share information + + @retval EFI_SUCCESS The Procedure has been set successfully. + @retval EFI_INVALID_PARAMETER The Procedure is NULL but ProcedureArguments not NULL. + +**/ +EFI_STATUS +RegisterStartupProcedure ( + IN EFI_AP_PROCEDURE Procedure, + IN OUT VOID *ProcedureArguments OPTIONAL + ); + +/** + Allocate buffer for SpinLock and Wrapper function buffer. + +**/ +VOID +InitializeDataForMmMp ( + VOID + ); + +/** + Return whether access to non-SMRAM is restricted. + + @retval TRUE Access to non-SMRAM is restricted. + @retval FALSE Access to non-SMRAM is not restricted. +**/ +BOOLEAN +IsRestrictedMemoryAccess ( + VOID + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf new file mode 100644 index 00000000..df745216 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf @@ -0,0 +1,153 @@ +## @file +# CPU SMM driver. +# +# This SMM driver performs SMM initialization, deploy SMM Entry Vector, +# provides CPU specific services in SMM. +# +# Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PiSmmCpuDxeSmm + MODULE_UNI_FILE = PiSmmCpuDxeSmm.uni + FILE_GUID = A3FF0EF5-0C28-42f5-B544-8C7DE1E80014 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = PiCpuSmmEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + PiSmmCpuDxeSmm.c + PiSmmCpuDxeSmm.h + MpService.c + SyncTimer.c + CpuS3.c + CpuService.c + CpuService.h + SmmProfile.c + SmmProfile.h + SmmProfileInternal.h + SmramSaveState.c + SmmCpuMemoryManagement.c + SmmMp.h + SmmMp.c + +[Sources.Ia32] + Ia32/Semaphore.c + Ia32/PageTbl.c + Ia32/SmmFuncsArch.c + Ia32/SmmProfileArch.c + Ia32/SmmProfileArch.h + Ia32/SmmInit.nasm + Ia32/SmiEntry.nasm + Ia32/SmiException.nasm + Ia32/MpFuncs.nasm + Ia32/Cet.nasm + +[Sources.X64] + X64/Semaphore.c + X64/PageTbl.c + X64/SmmFuncsArch.c + X64/SmmProfileArch.c + X64/SmmProfileArch.h + X64/SmmInit.nasm + X64/SmiEntry.nasm + X64/SmiException.nasm + X64/MpFuncs.nasm + X64/Cet.nasm + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + PcdLib + DebugLib + BaseLib + SynchronizationLib + BaseMemoryLib + MtrrLib + IoLib + TimerLib + SmmServicesTableLib + MemoryAllocationLib + DebugAgentLib + HobLib + PciLib + LocalApicLib + UefiCpuLib + SmmCpuPlatformHookLib + CpuExceptionHandlerLib + UefiLib + DxeServicesTableLib + CpuLib + ReportStatusCodeLib + SmmCpuFeaturesLib + PeCoffGetEntryPointLib + +[Protocols] + gEfiSmmAccess2ProtocolGuid ## CONSUMES + gEfiMpServiceProtocolGuid ## CONSUMES + gEfiSmmConfigurationProtocolGuid ## PRODUCES + gEfiSmmCpuProtocolGuid ## PRODUCES + gEfiSmmReadyToLockProtocolGuid ## NOTIFY + gEfiSmmCpuServiceProtocolGuid ## PRODUCES + gEdkiiSmmMemoryAttributeProtocolGuid ## PRODUCES + gEfiMmMpProtocolGuid ## PRODUCES + +[Guids] + gEfiAcpiVariableGuid ## SOMETIMES_CONSUMES ## HOB # it is used for S3 boot. + gEdkiiPiSmmMemoryAttributesTableGuid ## CONSUMES ## SystemTable + gEfiMemoryAttributesTableGuid ## CONSUMES ## SystemTable + +[FeaturePcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmDebug ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmBlockStartupThisAp ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmEnableBspElection ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuHotPlugSupport ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStackGuard ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmProfileEnable ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmProfileRingBuffer ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmFeatureControlMsrLock ## CONSUMES + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmProfileSize ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStackSize ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmApSyncTimeout ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuS3DataAddress ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuHotPlugDataAddress ## SOMETIMES_PRODUCES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmCodeAccessCheckEnable ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmSyncMode ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmShadowStackSize ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask ## CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdControlFlowEnforcementPropertyMask ## CONSUMES + +[FixedPcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmMpTokenCountPerChunk ## CONSUMES + +[Pcd.X64] + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmRestrictedMemoryAccess ## CONSUMES + +[Depex] + gEfiMpServiceProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + PiSmmCpuDxeSmmExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.uni new file mode 100644 index 00000000..1ccae17b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.uni @@ -0,0 +1,15 @@ +// /** @file
+// CPU SMM driver.
+//
+// This SMM driver performs SMM initialization, deploy SMM Entry Vector,
+// provides CPU specific services in SMM.
+//
+// Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU SMM driver"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This SMM driver performs SMM initialization, deploys SMM Entry Vector, and provides CPU-specific services in SMM."
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmmExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmmExtra.uni new file mode 100644 index 00000000..97e2815b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmmExtra.uni @@ -0,0 +1,12 @@ +// /** @file
+// PiSmmCpuDxeSmm Localized Strings and Content
+//
+// Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Processor SMM Initialization DXE Driver"
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c new file mode 100644 index 00000000..4f01579b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c @@ -0,0 +1,1571 @@ +/** @file + +Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" + +// +// attributes for reserved memory before it is promoted to system memory +// +#define EFI_MEMORY_PRESENT 0x0100000000000000ULL +#define EFI_MEMORY_INITIALIZED 0x0200000000000000ULL +#define EFI_MEMORY_TESTED 0x0400000000000000ULL + +#define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \ + ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size))) + +EFI_MEMORY_DESCRIPTOR *mUefiMemoryMap; +UINTN mUefiMemoryMapSize; +UINTN mUefiDescriptorSize; + +EFI_GCD_MEMORY_SPACE_DESCRIPTOR *mGcdMemSpace = NULL; +UINTN mGcdMemNumberOfDesc = 0; + +EFI_MEMORY_ATTRIBUTES_TABLE *mUefiMemoryAttributesTable = NULL; + +PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = { + {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64}, + {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64}, + {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64}, +}; + +UINTN mInternalCr3; + +/** + Set the internal page table base address. + If it is non zero, further MemoryAttribute modification will be on this page table. + If it is zero, further MemoryAttribute modification will be on real page table. + + @param Cr3 page table base. +**/ +VOID +SetPageTableBase ( + IN UINTN Cr3 + ) +{ + mInternalCr3 = Cr3; +} + +/** + Return length according to page attributes. + + @param[in] PageAttributes The page attribute of the page entry. + + @return The length of page entry. +**/ +UINTN +PageAttributeToLength ( + IN PAGE_ATTRIBUTE PageAttribute + ) +{ + UINTN Index; + for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) { + if (PageAttribute == mPageAttributeTable[Index].Attribute) { + return (UINTN)mPageAttributeTable[Index].Length; + } + } + return 0; +} + +/** + Return address mask according to page attributes. + + @param[in] PageAttributes The page attribute of the page entry. + + @return The address mask of page entry. +**/ +UINTN +PageAttributeToMask ( + IN PAGE_ATTRIBUTE PageAttribute + ) +{ + UINTN Index; + for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) { + if (PageAttribute == mPageAttributeTable[Index].Attribute) { + return (UINTN)mPageAttributeTable[Index].AddressMask; + } + } + return 0; +} + +/** + Return page table entry to match the address. + + @param[in] Address The address to be checked. + @param[out] PageAttributes The page attribute of the page entry. + + @return The page entry. +**/ +VOID * +GetPageTableEntry ( + IN PHYSICAL_ADDRESS Address, + OUT PAGE_ATTRIBUTE *PageAttribute + ) +{ + UINTN Index1; + UINTN Index2; + UINTN Index3; + UINTN Index4; + UINTN Index5; + UINT64 *L1PageTable; + UINT64 *L2PageTable; + UINT64 *L3PageTable; + UINT64 *L4PageTable; + UINT64 *L5PageTable; + UINTN PageTableBase; + BOOLEAN Enable5LevelPaging; + + GetPageTable (&PageTableBase, &Enable5LevelPaging); + + Index5 = ((UINTN)RShiftU64 (Address, 48)) & PAGING_PAE_INDEX_MASK; + Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK; + Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK; + Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK; + Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK; + + if (sizeof(UINTN) == sizeof(UINT64)) { + if (Enable5LevelPaging) { + L5PageTable = (UINT64 *)PageTableBase; + if (L5PageTable[Index5] == 0) { + *PageAttribute = PageNone; + return NULL; + } + + L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); + } else { + L4PageTable = (UINT64 *)PageTableBase; + } + if (L4PageTable[Index4] == 0) { + *PageAttribute = PageNone; + return NULL; + } + + L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); + } else { + L3PageTable = (UINT64 *)PageTableBase; + } + if (L3PageTable[Index3] == 0) { + *PageAttribute = PageNone; + return NULL; + } + if ((L3PageTable[Index3] & IA32_PG_PS) != 0) { + // 1G + *PageAttribute = Page1G; + return &L3PageTable[Index3]; + } + + L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); + if (L2PageTable[Index2] == 0) { + *PageAttribute = PageNone; + return NULL; + } + if ((L2PageTable[Index2] & IA32_PG_PS) != 0) { + // 2M + *PageAttribute = Page2M; + return &L2PageTable[Index2]; + } + + // 4k + L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); + if ((L1PageTable[Index1] == 0) && (Address != 0)) { + *PageAttribute = PageNone; + return NULL; + } + *PageAttribute = Page4K; + return &L1PageTable[Index1]; +} + +/** + Return memory attributes of page entry. + + @param[in] PageEntry The page entry. + + @return Memory attributes of page entry. +**/ +UINT64 +GetAttributesFromPageEntry ( + IN UINT64 *PageEntry + ) +{ + UINT64 Attributes; + Attributes = 0; + if ((*PageEntry & IA32_PG_P) == 0) { + Attributes |= EFI_MEMORY_RP; + } + if ((*PageEntry & IA32_PG_RW) == 0) { + Attributes |= EFI_MEMORY_RO; + } + if ((*PageEntry & IA32_PG_NX) != 0) { + Attributes |= EFI_MEMORY_XP; + } + return Attributes; +} + +/** + Modify memory attributes of page entry. + + @param[in] PageEntry The page entry. + @param[in] Attributes The bit mask of attributes to modify for the memory region. + @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes. + @param[out] IsModified TRUE means page table modified. FALSE means page table not modified. +**/ +VOID +ConvertPageEntryAttribute ( + IN UINT64 *PageEntry, + IN UINT64 Attributes, + IN BOOLEAN IsSet, + OUT BOOLEAN *IsModified + ) +{ + UINT64 CurrentPageEntry; + UINT64 NewPageEntry; + + CurrentPageEntry = *PageEntry; + NewPageEntry = CurrentPageEntry; + if ((Attributes & EFI_MEMORY_RP) != 0) { + if (IsSet) { + NewPageEntry &= ~(UINT64)IA32_PG_P; + } else { + NewPageEntry |= IA32_PG_P; + } + } + if ((Attributes & EFI_MEMORY_RO) != 0) { + if (IsSet) { + NewPageEntry &= ~(UINT64)IA32_PG_RW; + if (mInternalCr3 != 0) { + // Environment setup + // ReadOnly page need set Dirty bit for shadow stack + NewPageEntry |= IA32_PG_D; + // Clear user bit for supervisor shadow stack + NewPageEntry &= ~(UINT64)IA32_PG_U; + } else { + // Runtime update + // Clear dirty bit for non shadow stack, to protect RO page. + NewPageEntry &= ~(UINT64)IA32_PG_D; + } + } else { + NewPageEntry |= IA32_PG_RW; + } + } + if ((Attributes & EFI_MEMORY_XP) != 0) { + if (mXdSupported) { + if (IsSet) { + NewPageEntry |= IA32_PG_NX; + } else { + NewPageEntry &= ~IA32_PG_NX; + } + } + } + *PageEntry = NewPageEntry; + if (CurrentPageEntry != NewPageEntry) { + *IsModified = TRUE; + DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry)); + DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry)); + } else { + *IsModified = FALSE; + } +} + +/** + This function returns if there is need to split page entry. + + @param[in] BaseAddress The base address to be checked. + @param[in] Length The length to be checked. + @param[in] PageEntry The page entry to be checked. + @param[in] PageAttribute The page attribute of the page entry. + + @retval SplitAttributes on if there is need to split page entry. +**/ +PAGE_ATTRIBUTE +NeedSplitPage ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 *PageEntry, + IN PAGE_ATTRIBUTE PageAttribute + ) +{ + UINT64 PageEntryLength; + + PageEntryLength = PageAttributeToLength (PageAttribute); + + if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) { + return PageNone; + } + + if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) { + return Page4K; + } + + return Page2M; +} + +/** + This function splits one page entry to small page entries. + + @param[in] PageEntry The page entry to be splitted. + @param[in] PageAttribute The page attribute of the page entry. + @param[in] SplitAttribute How to split the page entry. + + @retval RETURN_SUCCESS The page entry is splitted. + @retval RETURN_UNSUPPORTED The page entry does not support to be splitted. + @retval RETURN_OUT_OF_RESOURCES No resource to split page entry. +**/ +RETURN_STATUS +SplitPage ( + IN UINT64 *PageEntry, + IN PAGE_ATTRIBUTE PageAttribute, + IN PAGE_ATTRIBUTE SplitAttribute + ) +{ + UINT64 BaseAddress; + UINT64 *NewPageEntry; + UINTN Index; + + ASSERT (PageAttribute == Page2M || PageAttribute == Page1G); + + if (PageAttribute == Page2M) { + // + // Split 2M to 4K + // + ASSERT (SplitAttribute == Page4K); + if (SplitAttribute == Page4K) { + NewPageEntry = AllocatePageTableMemory (1); + DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry)); + if (NewPageEntry == NULL) { + return RETURN_OUT_OF_RESOURCES; + } + BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64; + for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { + NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | mAddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS); + } + (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + return RETURN_SUCCESS; + } else { + return RETURN_UNSUPPORTED; + } + } else if (PageAttribute == Page1G) { + // + // Split 1G to 2M + // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table. + // + ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K); + if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) { + NewPageEntry = AllocatePageTableMemory (1); + DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry)); + if (NewPageEntry == NULL) { + return RETURN_OUT_OF_RESOURCES; + } + BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64; + for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { + NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | mAddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS); + } + (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + return RETURN_SUCCESS; + } else { + return RETURN_UNSUPPORTED; + } + } else { + return RETURN_UNSUPPORTED; + } +} + +/** + This function modifies the page attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + Caller should make sure BaseAddress and Length is at page boundary. + + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attributes The bit mask of attributes to modify for the memory region. + @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes. + @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted. + @param[out] IsModified TRUE means page table modified. FALSE means page table not modified. + + @retval RETURN_SUCCESS The attributes were modified for the memory region. + @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval RETURN_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be set together. + @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. +**/ +RETURN_STATUS +EFIAPI +ConvertMemoryPageAttributes ( + IN PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes, + IN BOOLEAN IsSet, + OUT BOOLEAN *IsSplitted, OPTIONAL + OUT BOOLEAN *IsModified OPTIONAL + ) +{ + UINT64 *PageEntry; + PAGE_ATTRIBUTE PageAttribute; + UINTN PageEntryLength; + PAGE_ATTRIBUTE SplitAttribute; + RETURN_STATUS Status; + BOOLEAN IsEntryModified; + EFI_PHYSICAL_ADDRESS MaximumSupportMemAddress; + + ASSERT (Attributes != 0); + ASSERT ((Attributes & ~EFI_MEMORY_ATTRIBUTE_MASK) == 0); + + ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0); + ASSERT ((Length & (SIZE_4KB - 1)) == 0); + + if (Length == 0) { + return RETURN_INVALID_PARAMETER; + } + + MaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, mPhysicalAddressBits) - 1); + if (BaseAddress > MaximumSupportMemAddress) { + return RETURN_UNSUPPORTED; + } + if (Length > MaximumSupportMemAddress) { + return RETURN_UNSUPPORTED; + } + if ((Length != 0) && (BaseAddress > MaximumSupportMemAddress - (Length - 1))) { + return RETURN_UNSUPPORTED; + } + +// DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes)); + + if (IsSplitted != NULL) { + *IsSplitted = FALSE; + } + if (IsModified != NULL) { + *IsModified = FALSE; + } + + // + // Below logic is to check 2M/4K page to make sure we do not waste memory. + // + while (Length != 0) { + PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute); + if (PageEntry == NULL) { + return RETURN_UNSUPPORTED; + } + PageEntryLength = PageAttributeToLength (PageAttribute); + SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute); + if (SplitAttribute == PageNone) { + ConvertPageEntryAttribute (PageEntry, Attributes, IsSet, &IsEntryModified); + if (IsEntryModified) { + if (IsModified != NULL) { + *IsModified = TRUE; + } + } + // + // Convert success, move to next + // + BaseAddress += PageEntryLength; + Length -= PageEntryLength; + } else { + Status = SplitPage (PageEntry, PageAttribute, SplitAttribute); + if (RETURN_ERROR (Status)) { + return RETURN_UNSUPPORTED; + } + if (IsSplitted != NULL) { + *IsSplitted = TRUE; + } + if (IsModified != NULL) { + *IsModified = TRUE; + } + // + // Just split current page + // Convert success in next around + // + } + } + + return RETURN_SUCCESS; +} + +/** + FlushTlb on current processor. + + @param[in,out] Buffer Pointer to private data buffer. +**/ +VOID +EFIAPI +FlushTlbOnCurrentProcessor ( + IN OUT VOID *Buffer + ) +{ + CpuFlushTlb (); +} + +/** + FlushTlb for all processors. +**/ +VOID +FlushTlbForAll ( + VOID + ) +{ + UINTN Index; + + FlushTlbOnCurrentProcessor (NULL); + + for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { + if (Index != gSmst->CurrentlyExecutingCpu) { + // Force to start up AP in blocking mode, + SmmBlockingStartupThisAp (FlushTlbOnCurrentProcessor, Index, NULL); + // Do not check return status, because AP might not be present in some corner cases. + } + } +} + +/** + This function sets the attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attributes The bit mask of attributes to set for the memory region. + @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted. + + @retval EFI_SUCCESS The attributes were set for the memory region. + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be set together. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +SmmSetMemoryAttributesEx ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes, + OUT BOOLEAN *IsSplitted OPTIONAL + ) +{ + EFI_STATUS Status; + BOOLEAN IsModified; + + Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified); + if (!EFI_ERROR(Status)) { + if (IsModified) { + // + // Flush TLB as last step + // + FlushTlbForAll(); + } + } + + return Status; +} + +/** + This function clears the attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attributes The bit mask of attributes to clear for the memory region. + @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted. + + @retval EFI_SUCCESS The attributes were cleared for the memory region. + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be cleared together. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not supported for the memory resource + range specified by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +SmmClearMemoryAttributesEx ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes, + OUT BOOLEAN *IsSplitted OPTIONAL + ) +{ + EFI_STATUS Status; + BOOLEAN IsModified; + + Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified); + if (!EFI_ERROR(Status)) { + if (IsModified) { + // + // Flush TLB as last step + // + FlushTlbForAll(); + } + } + + return Status; +} + +/** + This function sets the attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attributes The bit mask of attributes to set for the memory region. + + @retval EFI_SUCCESS The attributes were set for the memory region. + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be set together. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not supported for the memory resource + range specified by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +SmmSetMemoryAttributes ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ) +{ + return SmmSetMemoryAttributesEx (BaseAddress, Length, Attributes, NULL); +} + +/** + This function clears the attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + @param[in] Attributes The bit mask of attributes to clear for the memory region. + + @retval EFI_SUCCESS The attributes were cleared for the memory region. + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of attributes that + cannot be cleared together. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not supported for the memory resource + range specified by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +SmmClearMemoryAttributes ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ) +{ + return SmmClearMemoryAttributesEx (BaseAddress, Length, Attributes, NULL); +} + +/** + Set ShadowStack memory. + + @param[in] Cr3 The page table base address. + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + + @retval EFI_SUCCESS The shadow stack memory is set. +**/ +EFI_STATUS +SetShadowStack ( + IN UINTN Cr3, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + EFI_STATUS Status; + + SetPageTableBase (Cr3); + + Status = SmmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RO); + + SetPageTableBase (0); + + return Status; +} + +/** + Set not present memory. + + @param[in] Cr3 The page table base address. + @param[in] BaseAddress The physical address that is the start address of a memory region. + @param[in] Length The size in bytes of the memory region. + + @retval EFI_SUCCESS The not present memory is set. +**/ +EFI_STATUS +SetNotPresentPage ( + IN UINTN Cr3, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + EFI_STATUS Status; + + SetPageTableBase (Cr3); + + Status = SmmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RP); + + SetPageTableBase (0); + + return Status; +} + +/** + Retrieves a pointer to the system configuration table from the SMM System Table + based on a specified GUID. + + @param[in] TableGuid The pointer to table's GUID type. + @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table. + + @retval EFI_SUCCESS A configuration table matching TableGuid was found. + @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found. + +**/ +EFI_STATUS +EFIAPI +SmmGetSystemConfigurationTable ( + IN EFI_GUID *TableGuid, + OUT VOID **Table + ) +{ + UINTN Index; + + ASSERT (TableGuid != NULL); + ASSERT (Table != NULL); + + *Table = NULL; + for (Index = 0; Index < gSmst->NumberOfTableEntries; Index++) { + if (CompareGuid (TableGuid, &(gSmst->SmmConfigurationTable[Index].VendorGuid))) { + *Table = gSmst->SmmConfigurationTable[Index].VendorTable; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + This function sets SMM save state buffer to be RW and XP. +**/ +VOID +PatchSmmSaveStateMap ( + VOID + ) +{ + UINTN Index; + UINTN TileCodeSize; + UINTN TileDataSize; + UINTN TileSize; + + TileCodeSize = GetSmiHandlerSize (); + TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB); + TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP); + TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB); + TileSize = TileDataSize + TileCodeSize - 1; + TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize); + + DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n")); + for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) { + // + // Code + // + SmmSetMemoryAttributes ( + mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET, + TileCodeSize, + EFI_MEMORY_RO + ); + SmmClearMemoryAttributes ( + mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET, + TileCodeSize, + EFI_MEMORY_XP + ); + + // + // Data + // + SmmClearMemoryAttributes ( + mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize, + TileSize - TileCodeSize, + EFI_MEMORY_RO + ); + SmmSetMemoryAttributes ( + mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize, + TileSize - TileCodeSize, + EFI_MEMORY_XP + ); + } + + // + // Code + // + SmmSetMemoryAttributes ( + mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET, + TileCodeSize, + EFI_MEMORY_RO + ); + SmmClearMemoryAttributes ( + mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET, + TileCodeSize, + EFI_MEMORY_XP + ); + + // + // Data + // + SmmClearMemoryAttributes ( + mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize, + SIZE_32KB - TileCodeSize, + EFI_MEMORY_RO + ); + SmmSetMemoryAttributes ( + mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize, + SIZE_32KB - TileCodeSize, + EFI_MEMORY_XP + ); +} + +/** + This function sets GDT/IDT buffer to be RO and XP. +**/ +VOID +PatchGdtIdtMap ( + VOID + ) +{ + EFI_PHYSICAL_ADDRESS BaseAddress; + UINTN Size; + + // + // GDT + // + DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - GDT:\n")); + + BaseAddress = mGdtBuffer; + Size = ALIGN_VALUE(mGdtBufferSize, SIZE_4KB); + // + // The range should have been set to RO + // if it is allocated with EfiRuntimeServicesCode. + // + SmmSetMemoryAttributes ( + BaseAddress, + Size, + EFI_MEMORY_XP + ); + + // + // IDT + // + DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - IDT:\n")); + + BaseAddress = gcSmiIdtr.Base; + Size = ALIGN_VALUE(gcSmiIdtr.Limit + 1, SIZE_4KB); + // + // The range should have been set to RO + // if it is allocated with EfiRuntimeServicesCode. + // + SmmSetMemoryAttributes ( + BaseAddress, + Size, + EFI_MEMORY_XP + ); +} + +/** + This function sets memory attribute according to MemoryAttributesTable. +**/ +VOID +SetMemMapAttributes ( + VOID + ) +{ + EFI_MEMORY_DESCRIPTOR *MemoryMap; + EFI_MEMORY_DESCRIPTOR *MemoryMapStart; + UINTN MemoryMapEntryCount; + UINTN DescriptorSize; + UINTN Index; + EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable; + + SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable); + if (MemoryAttributesTable == NULL) { + DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n")); + return ; + } + + DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n")); + DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version)); + DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries)); + DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize)); + + MemoryMapEntryCount = MemoryAttributesTable->NumberOfEntries; + DescriptorSize = MemoryAttributesTable->DescriptorSize; + MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1); + MemoryMap = MemoryMapStart; + for (Index = 0; Index < MemoryMapEntryCount; Index++) { + DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryMap)); + DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryMap->Type)); + DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryMap->PhysicalStart)); + DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryMap->VirtualStart)); + DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryMap->NumberOfPages)); + DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryMap->Attribute)); + MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize); + } + + MemoryMap = MemoryMapStart; + for (Index = 0; Index < MemoryMapEntryCount; Index++) { + DEBUG ((DEBUG_VERBOSE, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap->PhysicalStart, MemoryMap->NumberOfPages)); + switch (MemoryMap->Type) { + case EfiRuntimeServicesCode: + SmmSetMemoryAttributes ( + MemoryMap->PhysicalStart, + EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages), + EFI_MEMORY_RO + ); + break; + case EfiRuntimeServicesData: + SmmSetMemoryAttributes ( + MemoryMap->PhysicalStart, + EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages), + EFI_MEMORY_XP + ); + break; + default: + SmmSetMemoryAttributes ( + MemoryMap->PhysicalStart, + EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages), + EFI_MEMORY_XP + ); + break; + } + MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize); + } + + PatchSmmSaveStateMap (); + PatchGdtIdtMap (); + + return ; +} + +/** + Sort memory map entries based upon PhysicalStart, from low to high. + + @param MemoryMap A pointer to the buffer in which firmware places + the current memory map. + @param MemoryMapSize Size, in bytes, of the MemoryMap buffer. + @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR. +**/ +STATIC +VOID +SortMemoryMap ( + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize + ) +{ + EFI_MEMORY_DESCRIPTOR *MemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; + EFI_MEMORY_DESCRIPTOR TempMemoryMap; + + MemoryMapEntry = MemoryMap; + NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); + MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize); + while (MemoryMapEntry < MemoryMapEnd) { + while (NextMemoryMapEntry < MemoryMapEnd) { + if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) { + CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR)); + CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR)); + CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR)); + } + + NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize); + } + + MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); + NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); + } +} + +/** + Return if a UEFI memory page should be marked as not present in SMM page table. + If the memory map entries type is + EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory, + EfiUnusableMemory, EfiACPIReclaimMemory, return TRUE. + Or return FALSE. + + @param[in] MemoryMap A pointer to the memory descriptor. + + @return TRUE The memory described will be marked as not present in SMM page table. + @return FALSE The memory described will not be marked as not present in SMM page table. +**/ +BOOLEAN +IsUefiPageNotPresent ( + IN EFI_MEMORY_DESCRIPTOR *MemoryMap + ) +{ + switch (MemoryMap->Type) { + case EfiLoaderCode: + case EfiLoaderData: + case EfiBootServicesCode: + case EfiBootServicesData: + case EfiConventionalMemory: + case EfiUnusableMemory: + case EfiACPIReclaimMemory: + return TRUE; + default: + return FALSE; + } +} + +/** + Merge continuous memory map entries whose type is + EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory, + EfiUnusableMemory, EfiACPIReclaimMemory, because the memory described by + these entries will be set as NOT present in SMM page table. + + @param[in, out] MemoryMap A pointer to the buffer in which firmware places + the current memory map. + @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the + MemoryMap buffer. On input, this is the size of + the current memory map. On output, + it is the size of new memory map after merge. + @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR. +**/ +STATIC +VOID +MergeMemoryMapForNotPresentEntry ( + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN OUT UINTN *MemoryMapSize, + IN UINTN DescriptorSize + ) +{ + EFI_MEMORY_DESCRIPTOR *MemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; + UINT64 MemoryBlockLength; + EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry; + + MemoryMapEntry = MemoryMap; + NewMemoryMapEntry = MemoryMap; + MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize); + while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) { + CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR)); + NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); + + do { + MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages)); + if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) && + IsUefiPageNotPresent(MemoryMapEntry) && IsUefiPageNotPresent(NextMemoryMapEntry) && + ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) { + MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages; + if (NewMemoryMapEntry != MemoryMapEntry) { + NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages; + } + + NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize); + continue; + } else { + MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize); + break; + } + } while (TRUE); + + MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); + NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize); + } + + *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap; + + return ; +} + +/** + This function caches the GCD memory map information. +**/ +VOID +GetGcdMemoryMap ( + VOID + ) +{ + UINTN NumberOfDescriptors; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemSpaceMap; + EFI_STATUS Status; + UINTN Index; + + Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemSpaceMap); + if (EFI_ERROR (Status)) { + return ; + } + + mGcdMemNumberOfDesc = 0; + for (Index = 0; Index < NumberOfDescriptors; Index++) { + if (MemSpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved && + (MemSpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) == + (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED) + ) { + mGcdMemNumberOfDesc++; + } + } + + mGcdMemSpace = AllocateZeroPool (mGcdMemNumberOfDesc * sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR)); + ASSERT (mGcdMemSpace != NULL); + if (mGcdMemSpace == NULL) { + mGcdMemNumberOfDesc = 0; + gBS->FreePool (MemSpaceMap); + return ; + } + + mGcdMemNumberOfDesc = 0; + for (Index = 0; Index < NumberOfDescriptors; Index++) { + if (MemSpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved && + (MemSpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) == + (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED) + ) { + CopyMem ( + &mGcdMemSpace[mGcdMemNumberOfDesc], + &MemSpaceMap[Index], + sizeof(EFI_GCD_MEMORY_SPACE_DESCRIPTOR) + ); + mGcdMemNumberOfDesc++; + } + } + + gBS->FreePool (MemSpaceMap); +} + +/** + Get UEFI MemoryAttributesTable. +**/ +VOID +GetUefiMemoryAttributesTable ( + VOID + ) +{ + EFI_STATUS Status; + EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable; + UINTN MemoryAttributesTableSize; + + Status = EfiGetSystemConfigurationTable (&gEfiMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable); + if (!EFI_ERROR (Status) && (MemoryAttributesTable != NULL)) { + MemoryAttributesTableSize = sizeof(EFI_MEMORY_ATTRIBUTES_TABLE) + MemoryAttributesTable->DescriptorSize * MemoryAttributesTable->NumberOfEntries; + mUefiMemoryAttributesTable = AllocateCopyPool (MemoryAttributesTableSize, MemoryAttributesTable); + ASSERT (mUefiMemoryAttributesTable != NULL); + } +} + +/** + This function caches the UEFI memory map information. +**/ +VOID +GetUefiMemoryMap ( + VOID + ) +{ + EFI_STATUS Status; + UINTN MapKey; + UINT32 DescriptorVersion; + EFI_MEMORY_DESCRIPTOR *MemoryMap; + UINTN UefiMemoryMapSize; + + DEBUG ((DEBUG_INFO, "GetUefiMemoryMap\n")); + + UefiMemoryMapSize = 0; + MemoryMap = NULL; + Status = gBS->GetMemoryMap ( + &UefiMemoryMapSize, + MemoryMap, + &MapKey, + &mUefiDescriptorSize, + &DescriptorVersion + ); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + + do { + Status = gBS->AllocatePool (EfiBootServicesData, UefiMemoryMapSize, (VOID **)&MemoryMap); + ASSERT (MemoryMap != NULL); + if (MemoryMap == NULL) { + return ; + } + + Status = gBS->GetMemoryMap ( + &UefiMemoryMapSize, + MemoryMap, + &MapKey, + &mUefiDescriptorSize, + &DescriptorVersion + ); + if (EFI_ERROR (Status)) { + gBS->FreePool (MemoryMap); + MemoryMap = NULL; + } + } while (Status == EFI_BUFFER_TOO_SMALL); + + if (MemoryMap == NULL) { + return ; + } + + SortMemoryMap (MemoryMap, UefiMemoryMapSize, mUefiDescriptorSize); + MergeMemoryMapForNotPresentEntry (MemoryMap, &UefiMemoryMapSize, mUefiDescriptorSize); + + mUefiMemoryMapSize = UefiMemoryMapSize; + mUefiMemoryMap = AllocateCopyPool (UefiMemoryMapSize, MemoryMap); + ASSERT (mUefiMemoryMap != NULL); + + gBS->FreePool (MemoryMap); + + // + // Get additional information from GCD memory map. + // + GetGcdMemoryMap (); + + // + // Get UEFI memory attributes table. + // + GetUefiMemoryAttributesTable (); +} + +/** + This function sets UEFI memory attribute according to UEFI memory map. + + The normal memory region is marked as not present, such as + EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory, + EfiUnusableMemory, EfiACPIReclaimMemory. +**/ +VOID +SetUefiMemMapAttributes ( + VOID + ) +{ + EFI_STATUS Status; + EFI_MEMORY_DESCRIPTOR *MemoryMap; + UINTN MemoryMapEntryCount; + UINTN Index; + EFI_MEMORY_DESCRIPTOR *Entry; + + DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n")); + + if (mUefiMemoryMap != NULL) { + MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize; + MemoryMap = mUefiMemoryMap; + for (Index = 0; Index < MemoryMapEntryCount; Index++) { + if (IsUefiPageNotPresent(MemoryMap)) { + Status = SmmSetMemoryAttributes ( + MemoryMap->PhysicalStart, + EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages), + EFI_MEMORY_RP + ); + DEBUG (( + DEBUG_INFO, + "UefiMemory protection: 0x%lx - 0x%lx %r\n", + MemoryMap->PhysicalStart, + MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages), + Status + )); + } + MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize); + } + } + // + // Do not free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress(). + // + + // + // Set untested memory as not present. + // + if (mGcdMemSpace != NULL) { + for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) { + Status = SmmSetMemoryAttributes ( + mGcdMemSpace[Index].BaseAddress, + mGcdMemSpace[Index].Length, + EFI_MEMORY_RP + ); + DEBUG (( + DEBUG_INFO, + "GcdMemory protection: 0x%lx - 0x%lx %r\n", + mGcdMemSpace[Index].BaseAddress, + mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length, + Status + )); + } + } + // + // Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress(). + // + + // + // Set UEFI runtime memory with EFI_MEMORY_RO as not present. + // + if (mUefiMemoryAttributesTable != NULL) { + Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1); + for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) { + if (Entry->Type == EfiRuntimeServicesCode || Entry->Type == EfiRuntimeServicesData) { + if ((Entry->Attribute & EFI_MEMORY_RO) != 0) { + Status = SmmSetMemoryAttributes ( + Entry->PhysicalStart, + EFI_PAGES_TO_SIZE((UINTN)Entry->NumberOfPages), + EFI_MEMORY_RP + ); + DEBUG (( + DEBUG_INFO, + "UefiMemoryAttribute protection: 0x%lx - 0x%lx %r\n", + Entry->PhysicalStart, + Entry->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE((UINTN)Entry->NumberOfPages), + Status + )); + } + } + Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize); + } + } + // + // Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress(). + // +} + +/** + Return if the Address is forbidden as SMM communication buffer. + + @param[in] Address the address to be checked + + @return TRUE The address is forbidden as SMM communication buffer. + @return FALSE The address is allowed as SMM communication buffer. +**/ +BOOLEAN +IsSmmCommBufferForbiddenAddress ( + IN UINT64 Address + ) +{ + EFI_MEMORY_DESCRIPTOR *MemoryMap; + UINTN MemoryMapEntryCount; + UINTN Index; + EFI_MEMORY_DESCRIPTOR *Entry; + + if (mUefiMemoryMap != NULL) { + MemoryMap = mUefiMemoryMap; + MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize; + for (Index = 0; Index < MemoryMapEntryCount; Index++) { + if (IsUefiPageNotPresent (MemoryMap)) { + if ((Address >= MemoryMap->PhysicalStart) && + (Address < MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages)) ) { + return TRUE; + } + } + MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize); + } + } + + if (mGcdMemSpace != NULL) { + for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) { + if ((Address >= mGcdMemSpace[Index].BaseAddress) && + (Address < mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length) ) { + return TRUE; + } + } + } + + if (mUefiMemoryAttributesTable != NULL) { + Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1); + for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) { + if (Entry->Type == EfiRuntimeServicesCode || Entry->Type == EfiRuntimeServicesData) { + if ((Entry->Attribute & EFI_MEMORY_RO) != 0) { + if ((Address >= Entry->PhysicalStart) && + (Address < Entry->PhysicalStart + LShiftU64 (Entry->NumberOfPages, EFI_PAGE_SHIFT))) { + return TRUE; + } + Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize); + } + } + } + } + return FALSE; +} + +/** + This function set given attributes of the memory region specified by + BaseAddress and Length. + + @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance. + @param BaseAddress The physical address that is the start address of + a memory region. + @param Length The size in bytes of the memory region. + @param Attributes The bit mask of attributes to set for the memory + region. + + @retval EFI_SUCCESS The attributes were set for the memory region. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of + attributes that cannot be set together. + @retval EFI_UNSUPPORTED The processor does not support one or more + bytes of the memory resource range specified + by BaseAddress and Length. + The bit mask of attributes is not supported for + the memory resource range specified by + BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +EdkiiSmmSetMemoryAttributes ( + IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ) +{ + return SmmSetMemoryAttributes (BaseAddress, Length, Attributes); +} + +/** + This function clears given attributes of the memory region specified by + BaseAddress and Length. + + @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance. + @param BaseAddress The physical address that is the start address of + a memory region. + @param Length The size in bytes of the memory region. + @param Attributes The bit mask of attributes to clear for the memory + region. + + @retval EFI_SUCCESS The attributes were cleared for the memory region. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes specified an illegal combination of + attributes that cannot be cleared together. + @retval EFI_UNSUPPORTED The processor does not support one or more + bytes of the memory resource range specified + by BaseAddress and Length. + The bit mask of attributes is not supported for + the memory resource range specified by + BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +EdkiiSmmClearMemoryAttributes ( + IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ) +{ + return SmmClearMemoryAttributes (BaseAddress, Length, Attributes); +} + +/** + This function retrieves the attributes of the memory region specified by + BaseAddress and Length. If different attributes are got from different part + of the memory region, EFI_NO_MAPPING will be returned. + + @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance. + @param BaseAddress The physical address that is the start address of + a memory region. + @param Length The size in bytes of the memory region. + @param Attributes Pointer to attributes returned. + + @retval EFI_SUCCESS The attributes got for the memory region. + @retval EFI_INVALID_PARAMETER Length is zero. + Attributes is NULL. + @retval EFI_NO_MAPPING Attributes are not consistent cross the memory + region. + @retval EFI_UNSUPPORTED The processor does not support one or more + bytes of the memory resource range specified + by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +EdkiiSmmGetMemoryAttributes ( + IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + OUT UINT64 *Attributes + ) +{ + EFI_PHYSICAL_ADDRESS Address; + UINT64 *PageEntry; + UINT64 MemAttr; + PAGE_ATTRIBUTE PageAttr; + INT64 Size; + + if (Length < SIZE_4KB || Attributes == NULL) { + return EFI_INVALID_PARAMETER; + } + + Size = (INT64)Length; + MemAttr = (UINT64)-1; + + do { + + PageEntry = GetPageTableEntry (BaseAddress, &PageAttr); + if (PageEntry == NULL || PageAttr == PageNone) { + return EFI_UNSUPPORTED; + } + + // + // If the memory range is cross page table boundary, make sure they + // share the same attribute. Return EFI_NO_MAPPING if not. + // + *Attributes = GetAttributesFromPageEntry (PageEntry); + if (MemAttr != (UINT64)-1 && *Attributes != MemAttr) { + return EFI_NO_MAPPING; + } + + switch (PageAttr) { + case Page4K: + Address = *PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64; + Size -= (SIZE_4KB - (BaseAddress - Address)); + BaseAddress += (SIZE_4KB - (BaseAddress - Address)); + break; + + case Page2M: + Address = *PageEntry & ~mAddressEncMask & PAGING_2M_ADDRESS_MASK_64; + Size -= SIZE_2MB - (BaseAddress - Address); + BaseAddress += SIZE_2MB - (BaseAddress - Address); + break; + + case Page1G: + Address = *PageEntry & ~mAddressEncMask & PAGING_1G_ADDRESS_MASK_64; + Size -= SIZE_1GB - (BaseAddress - Address); + BaseAddress += SIZE_1GB - (BaseAddress - Address); + break; + + default: + return EFI_UNSUPPORTED; + } + + MemAttr = *Attributes; + + } while (Size > 0); + + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.c new file mode 100644 index 00000000..ca058855 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.c @@ -0,0 +1,344 @@ +/** @file +SMM MP protocol implementation + +Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" +#include "SmmMp.h" + +/// +/// SMM MP Protocol instance +/// +EFI_MM_MP_PROTOCOL mSmmMp = { + EFI_MM_MP_PROTOCOL_REVISION, + 0, + SmmMpGetNumberOfProcessors, + SmmMpDispatchProcedure, + SmmMpBroadcastProcedure, + SmmMpSetStartupProcedure, + SmmMpCheckForProcedure, + SmmMpWaitForProcedure +}; + +/** + Service to retrieves the number of logical processor in the platform. + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in the system, + including the BSP and all APs. + + @retval EFI_SUCCESS The number of processors was retrieved successfully + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL +**/ +EFI_STATUS +EFIAPI +SmmMpGetNumberOfProcessors ( + IN CONST EFI_MM_MP_PROTOCOL *This, + OUT UINTN *NumberOfProcessors + ) +{ + if (NumberOfProcessors == NULL) { + return EFI_INVALID_PARAMETER; + } + + *NumberOfProcessors = gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; + + return EFI_SUCCESS; +} + +/** + This service allows the caller to invoke a procedure one of the application processors (AP). This + function uses an optional token parameter to support blocking and non-blocking modes. If the token + is passed into the call, the function will operate in a non-blocking fashion and the caller can + check for completion with CheckOnProcedure or WaitForProcedure. + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Procedure A pointer to the procedure to be run on the designated target + AP of the system. Type EFI_AP_PROCEDURE2 is defined below in + related definitions. + @param[in] CpuNumber The zero-based index of the processor number of the target + AP, on which the code stream is supposed to run. If the number + points to the calling processor then it will not run the + supplied code. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for this AP to + finish execution of Procedure, either for blocking or + non-blocking mode. Zero means infinity. If the timeout + expires before this AP returns from Procedure, then Procedure + on the AP is terminated. If the timeout expires in blocking + mode, the call returns EFI_TIMEOUT. If the timeout expires + in non-blocking mode, the timeout determined can be through + CheckOnProcedure or WaitForProcedure. + Note that timeout support is optional. Whether an + implementation supports this feature, can be determined via + the Attributes data member. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code + that is run by the AP. It is an optional common mailbox + between APs and the caller to share information. + @param[in,out] Token This is parameter is broken into two components: + 1.Token->Completion is an optional parameter that allows the + caller to execute the procedure in a blocking or non-blocking + fashion. If it is NULL the call is blocking, and the call will + not return until the AP has completed the procedure. If the + token is not NULL, the call will return immediately. The caller + can check whether the procedure has completed with + CheckOnProcedure or WaitForProcedure. + 2.Token->Status The implementation updates the address pointed + at by this variable with the status code returned by Procedure + when it completes execution on the target AP, or with EFI_TIMEOUT + if the Procedure fails to complete within the optional timeout. + The implementation will update this variable with EFI_NOT_READY + prior to starting Procedure on the target AP + @param[in,out] CPUStatus This optional pointer may be used to get the status code returned + by Procedure when it completes execution on the target AP, or with + EFI_TIMEOUT if the Procedure fails to complete within the optional + timeout. The implementation will update this variable with + EFI_NOT_READY prior to starting Procedure on the target AP. + + @retval EFI_SUCCESS In the blocking case, this indicates that Procedure has completed + execution on the target AP. + In the non-blocking case this indicates that the procedure has + been successfully scheduled for execution on the target AP. + @retval EFI_INVALID_PARAMETER The input arguments are out of range. Either the target AP is the + caller of the function, or the Procedure or Token is NULL + @retval EFI_NOT_READY If the target AP is busy executing another procedure + @retval EFI_ALREADY_STARTED Token is already in use for another procedure + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the specified AP + has finished + @retval EFI_OUT_OF_RESOURCES Could not allocate a required resource. + +**/ +EFI_STATUS +EFIAPI +SmmMpDispatchProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN CpuNumber, + IN UINTN TimeoutInMicroseconds, + IN OUT VOID *ProcedureArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN OUT EFI_STATUS *CPUStatus + ) +{ + return InternalSmmStartupThisAp ( + Procedure, + CpuNumber, + ProcedureArguments, + Token, + TimeoutInMicroseconds, + CPUStatus + ); +} + +/** + This service allows the caller to invoke a procedure on all running application processors (AP) + except the caller. This function uses an optional token parameter to support blocking and + nonblocking modes. If the token is passed into the call, the function will operate in a non-blocking + fashion and the caller can check for completion with CheckOnProcedure or WaitForProcedure. + + It is not necessary for the implementation to run the procedure on every processor on the platform. + Processors that are powered down in such a way that they cannot respond to interrupts, may be + excluded from the broadcast. + + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Procedure A pointer to the code stream to be run on the APs that have + entered MM. Type EFI_AP_PROCEDURE is defined below in related + definitions. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for the APs to finish + execution of Procedure, either for blocking or non-blocking mode. + Zero means infinity. If the timeout expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. If + the timeout expires in blocking mode, the call returns EFI_TIMEOUT. + If the timeout expires in non-blocking mode, the timeout determined + can be through CheckOnProcedure or WaitForProcedure. + Note that timeout support is optional. Whether an implementation + supports this feature can be determined via the Attributes data + member. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code + that is run by the AP. It is an optional common mailbox + between APs and the caller to share information. + @param[in,out] Token This is parameter is broken into two components: + 1.Token->Completion is an optional parameter that allows the + caller to execute the procedure in a blocking or non-blocking + fashion. If it is NULL the call is blocking, and the call will + not return until the AP has completed the procedure. If the + token is not NULL, the call will return immediately. The caller + can check whether the procedure has completed with + CheckOnProcedure or WaitForProcedure. + 2.Token->Status The implementation updates the address pointed + at by this variable with the status code returned by Procedure + when it completes execution on the target AP, or with EFI_TIMEOUT + if the Procedure fails to complete within the optional timeout. + The implementation will update this variable with EFI_NOT_READY + prior to starting Procedure on the target AP + @param[in,out] CPUStatus This optional pointer may be used to get the individual status + returned by every AP that participated in the broadcast. This + parameter if used provides the base address of an array to hold + the EFI_STATUS value of each AP in the system. The size of the + array can be ascertained by the GetNumberOfProcessors function. + As mentioned above, the broadcast may not include every processor + in the system. Some implementations may exclude processors that + have been powered down in such a way that they are not responsive + to interrupts. Additionally the broadcast excludes the processor + which is making the BroadcastProcedure call. For every excluded + processor, the array entry must contain a value of EFI_NOT_STARTED + + @retval EFI_SUCCESS In the blocking case, this indicates that Procedure has completed + execution on the APs. + In the non-blocking case this indicates that the procedure has + been successfully scheduled for execution on the APs. + @retval EFI_INVALID_PARAMETER The Procedure or Token is NULL + @retval EFI_NOT_READY If the target AP is busy executing another procedure + @retval EFI_ALREADY_STARTED Token is already in use for another procedure + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the specified AP + has finished. + @retval EFI_OUT_OF_RESOURCES Could not allocate a required resource. + +**/ +EFI_STATUS +EFIAPI +SmmMpBroadcastProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN TimeoutInMicroseconds, + IN OUT VOID *ProcedureArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN OUT EFI_STATUS *CPUStatus + ) +{ + return InternalSmmStartupAllAPs( + Procedure, + TimeoutInMicroseconds, + ProcedureArguments, + Token, + CPUStatus + ); +} + +/** + This service allows the caller to set a startup procedure that will be executed when an AP powers + up from a state where core configuration and context is lost. The procedure is execution has the + following properties: + 1. The procedure executes before the processor is handed over to the operating system. + 2. All processors execute the same startup procedure. + 3. The procedure may run in parallel with other procedures invoked through the functions in this + protocol, or with processors that are executing an MM handler or running in the operating system. + + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Procedure A pointer to the code stream to be run on the designated target AP + of the system. Type EFI_AP_PROCEDURE is defined below in Volume 2 + with the related definitions of + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs. + If caller may pass a value of NULL to deregister any existing + startup procedure. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code that is + run by the AP. It is an optional common mailbox between APs and + the caller to share information + + @retval EFI_SUCCESS The Procedure has been set successfully. + @retval EFI_INVALID_PARAMETER The Procedure is NULL but ProcedureArguments not NULL. + +**/ +EFI_STATUS +EFIAPI +SmmMpSetStartupProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN EFI_AP_PROCEDURE Procedure, + IN OUT VOID *ProcedureArguments OPTIONAL + ) +{ + return RegisterStartupProcedure (Procedure, ProcedureArguments); +} + +/** + When non-blocking execution of a procedure on an AP is invoked with DispatchProcedure, + via the use of a token, this function can be used to check for completion of the procedure on the AP. + The function takes the token that was passed into the DispatchProcedure call. If the procedure + is complete, and therefore it is now possible to run another procedure on the same AP, this function + returns EFI_SUCESS. In this case the status returned by the procedure that executed on the AP is + returned in the token's Status field. If the procedure has not yet completed, then this function + returns EFI_NOT_READY. + + When a non-blocking execution of a procedure is invoked with BroadcastProcedure, via the + use of a token, this function can be used to check for completion of the procedure on all the + broadcast APs. The function takes the token that was passed into the BroadcastProcedure + call. If the procedure is complete on all broadcast APs this function returns EFI_SUCESS. In this + case the Status field in the token passed into the function reflects the overall result of the + invocation, which may be EFI_SUCCESS, if all executions succeeded, or the first observed failure. + If the procedure has not yet completed on the broadcast APs, the function returns + EFI_NOT_READY. + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Token This parameter describes the token that was passed into + DispatchProcedure or BroadcastProcedure. + + @retval EFI_SUCCESS Procedure has completed. + @retval EFI_NOT_READY The Procedure has not completed. + @retval EFI_INVALID_PARAMETER Token or Token->Completion is NULL + @retval EFI_NOT_FOUND Token is not currently in use for a non-blocking call + +**/ +EFI_STATUS +EFIAPI +SmmMpCheckForProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN MM_COMPLETION Token + ) +{ + if (Token == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!IsTokenInUse ((SPIN_LOCK *)Token)) { + return EFI_NOT_FOUND; + } + + return IsApReady ((SPIN_LOCK *)Token); +} + +/** + When a non-blocking execution of a procedure on an AP is invoked via DispatchProcedure, + this function will block the caller until the remote procedure has completed on the designated AP. + The non-blocking procedure invocation is identified by the Token parameter, which must match the + token that used when DispatchProcedure was called. Upon completion the status returned by + the procedure that executed on the AP is used to update the token's Status field. + + When a non-blocking execution of a procedure on an AP is invoked via BroadcastProcedure + this function will block the caller until the remote procedure has completed on all of the APs that + entered MM. The non-blocking procedure invocation is identified by the Token parameter, which + must match the token that used when BroadcastProcedure was called. Upon completion the + overall status returned by the procedures that executed on the broadcast AP is used to update the + token's Status field. The overall status may be EFI_SUCCESS, if all executions succeeded, or the + first observed failure. + + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Token This parameter describes the token that was passed into + DispatchProcedure or BroadcastProcedure. + + @retval EFI_SUCCESS Procedure has completed. + @retval EFI_INVALID_PARAMETER Token or Token->Completion is NULL + @retval EFI_NOT_FOUND Token is not currently in use for a non-blocking call + +**/ +EFI_STATUS +EFIAPI +SmmMpWaitForProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN MM_COMPLETION Token + ) +{ + EFI_STATUS Status; + + do { + Status = SmmMpCheckForProcedure (This, Token); + } while (Status == EFI_NOT_READY); + + return Status; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.h new file mode 100644 index 00000000..98146cfc --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.h @@ -0,0 +1,285 @@ +/** @file +Include file for SMM MP protocol implementation. + +Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SMM_MP_PROTOCOL_H_ +#define _SMM_MP_PROTOCOL_H_ + +// +// SMM MP Protocol function prototypes. +// + +/** + Service to retrieves the number of logical processor in the platform. + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in the system, + including the BSP and all APs. + + @retval EFI_SUCCESS The number of processors was retrieved successfully + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL +**/ +EFI_STATUS +EFIAPI +SmmMpGetNumberOfProcessors ( + IN CONST EFI_MM_MP_PROTOCOL *This, + OUT UINTN *NumberOfProcessors + ); + + +/** + This service allows the caller to invoke a procedure one of the application processors (AP). This + function uses an optional token parameter to support blocking and non-blocking modes. If the token + is passed into the call, the function will operate in a non-blocking fashion and the caller can + check for completion with CheckOnProcedure or WaitForProcedure. + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Procedure A pointer to the procedure to be run on the designated target + AP of the system. Type EFI_AP_PROCEDURE2 is defined below in + related definitions. + @param[in] CpuNumber The zero-based index of the processor number of the target + AP, on which the code stream is supposed to run. If the number + points to the calling processor then it will not run the + supplied code. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for this AP to + finish execution of Procedure, either for blocking or + non-blocking mode. Zero means infinity. If the timeout + expires before this AP returns from Procedure, then Procedure + on the AP is terminated. If the timeout expires in blocking + mode, the call returns EFI_TIMEOUT. If the timeout expires + in non-blocking mode, the timeout determined can be through + CheckOnProcedure or WaitForProcedure. + Note that timeout support is optional. Whether an + implementation supports this feature, can be determined via + the Attributes data member. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code + that is run by the AP. It is an optional common mailbox + between APs and the caller to share information. + @param[in,out] Token This is parameter is broken into two components: + 1.Token->Completion is an optional parameter that allows the + caller to execute the procedure in a blocking or non-blocking + fashion. If it is NULL the call is blocking, and the call will + not return until the AP has completed the procedure. If the + token is not NULL, the call will return immediately. The caller + can check whether the procedure has completed with + CheckOnProcedure or WaitForProcedure. + 2.Token->Status The implementation updates the address pointed + at by this variable with the status code returned by Procedure + when it completes execution on the target AP, or with EFI_TIMEOUT + if the Procedure fails to complete within the optional timeout. + The implementation will update this variable with EFI_NOT_READY + prior to starting Procedure on the target AP + @param[in,out] CPUStatus This optional pointer may be used to get the status code returned + by Procedure when it completes execution on the target AP, or with + EFI_TIMEOUT if the Procedure fails to complete within the optional + timeout. The implementation will update this variable with + EFI_NOT_READY prior to starting Procedure on the target AP. + + @retval EFI_SUCCESS In the blocking case, this indicates that Procedure has completed + execution on the target AP. + In the non-blocking case this indicates that the procedure has + been successfully scheduled for execution on the target AP. + @retval EFI_INVALID_PARAMETER The input arguments are out of range. Either the target AP is the + caller of the function, or the Procedure or Token is NULL + @retval EFI_NOT_READY If the target AP is busy executing another procedure + @retval EFI_ALREADY_STARTED Token is already in use for another procedure + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the specified AP + has finished + @retval EFI_OUT_OF_RESOURCES Could not allocate a required resource. + +**/ +EFI_STATUS +EFIAPI +SmmMpDispatchProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN CpuNumber, + IN UINTN TimeoutInMicroseconds, + IN OUT VOID *ProcedureArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN OUT EFI_STATUS *CPUStatus + ); + +/** + This service allows the caller to invoke a procedure on all running application processors (AP) + except the caller. This function uses an optional token parameter to support blocking and + nonblocking modes. If the token is passed into the call, the function will operate in a non-blocking + fashion and the caller can check for completion with CheckOnProcedure or WaitForProcedure. + + It is not necessary for the implementation to run the procedure on every processor on the platform. + Processors that are powered down in such a way that they cannot respond to interrupts, may be + excluded from the broadcast. + + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Procedure A pointer to the code stream to be run on the APs that have + entered MM. Type EFI_AP_PROCEDURE is defined below in related + definitions. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for the APs to finish + execution of Procedure, either for blocking or non-blocking mode. + Zero means infinity. If the timeout expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. If + the timeout expires in blocking mode, the call returns EFI_TIMEOUT. + If the timeout expires in non-blocking mode, the timeout determined + can be through CheckOnProcedure or WaitForProcedure. + Note that timeout support is optional. Whether an implementation + supports this feature can be determined via the Attributes data + member. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code + that is run by the AP. It is an optional common mailbox + between APs and the caller to share information. + @param[in,out] Token This is parameter is broken into two components: + 1.Token->Completion is an optional parameter that allows the + caller to execute the procedure in a blocking or non-blocking + fashion. If it is NULL the call is blocking, and the call will + not return until the AP has completed the procedure. If the + token is not NULL, the call will return immediately. The caller + can check whether the procedure has completed with + CheckOnProcedure or WaitForProcedure. + 2.Token->Status The implementation updates the address pointed + at by this variable with the status code returned by Procedure + when it completes execution on the target AP, or with EFI_TIMEOUT + if the Procedure fails to complete within the optional timeout. + The implementation will update this variable with EFI_NOT_READY + prior to starting Procedure on the target AP + @param[in,out] CPUStatus This optional pointer may be used to get the individual status + returned by every AP that participated in the broadcast. This + parameter if used provides the base address of an array to hold + the EFI_STATUS value of each AP in the system. The size of the + array can be ascertained by the GetNumberOfProcessors function. + As mentioned above, the broadcast may not include every processor + in the system. Some implementations may exclude processors that + have been powered down in such a way that they are not responsive + to interrupts. Additionally the broadcast excludes the processor + which is making the BroadcastProcedure call. For every excluded + processor, the array entry must contain a value of EFI_NOT_STARTED + + @retval EFI_SUCCESS In the blocking case, this indicates that Procedure has completed + execution on the APs. + In the non-blocking case this indicates that the procedure has + been successfully scheduled for execution on the APs. + @retval EFI_INVALID_PARAMETER The Procedure or Token is NULL + @retval EFI_NOT_READY If the target AP is busy executing another procedure + @retval EFI_ALREADY_STARTED Token is already in use for another procedure + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the specified AP + has finished + @retval EFI_OUT_OF_RESOURCES Could not allocate a required resource. + +**/ +EFI_STATUS +EFIAPI +SmmMpBroadcastProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN TimeoutInMicroseconds, + IN OUT VOID *ProcedureArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN OUT EFI_STATUS *CPUStatus + ); + + +/** + This service allows the caller to set a startup procedure that will be executed when an AP powers + up from a state where core configuration and context is lost. The procedure is execution has the + following properties: + 1. The procedure executes before the processor is handed over to the operating system. + 2. All processors execute the same startup procedure. + 3. The procedure may run in parallel with other procedures invoked through the functions in this + protocol, or with processors that are executing an MM handler or running in the operating system. + + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Procedure A pointer to the code stream to be run on the designated target AP + of the system. Type EFI_AP_PROCEDURE is defined below in Volume 2 + with the related definitions of + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs. + If caller may pass a value of NULL to deregister any existing + startup procedure. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code that is + run by the AP. It is an optional common mailbox between APs and + the caller to share information + + @retval EFI_SUCCESS The Procedure has been set successfully. + @retval EFI_INVALID_PARAMETER The Procedure is NULL but ProcedureArguments not NULL. +**/ +EFI_STATUS +EFIAPI +SmmMpSetStartupProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN EFI_AP_PROCEDURE Procedure, + IN OUT VOID *ProcedureArguments OPTIONAL + ); + +/** + When non-blocking execution of a procedure on an AP is invoked with DispatchProcedure, + via the use of a token, this function can be used to check for completion of the procedure on the AP. + The function takes the token that was passed into the DispatchProcedure call. If the procedure + is complete, and therefore it is now possible to run another procedure on the same AP, this function + returns EFI_SUCESS. In this case the status returned by the procedure that executed on the AP is + returned in the token's Status field. If the procedure has not yet completed, then this function + returns EFI_NOT_READY. + + When a non-blocking execution of a procedure is invoked with BroadcastProcedure, via the + use of a token, this function can be used to check for completion of the procedure on all the + broadcast APs. The function takes the token that was passed into the BroadcastProcedure + call. If the procedure is complete on all broadcast APs this function returns EFI_SUCESS. In this + case the Status field in the token passed into the function reflects the overall result of the + invocation, which may be EFI_SUCCESS, if all executions succeeded, or the first observed failure. + If the procedure has not yet completed on the broadcast APs, the function returns + EFI_NOT_READY. + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Token This parameter describes the token that was passed into + DispatchProcedure or BroadcastProcedure. + + @retval EFI_SUCCESS Procedure has completed. + @retval EFI_NOT_READY The Procedure has not completed. + @retval EFI_INVALID_PARAMETER Token or Token->Completion is NULL + @retval EFI_NOT_FOUND Token is not currently in use for a non-blocking call + +**/ +EFI_STATUS +EFIAPI +SmmMpCheckForProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN MM_COMPLETION Token + ); + +/** + When a non-blocking execution of a procedure on an AP is invoked via DispatchProcedure, + this function will block the caller until the remote procedure has completed on the designated AP. + The non-blocking procedure invocation is identified by the Token parameter, which must match the + token that used when DispatchProcedure was called. Upon completion the status returned by + the procedure that executed on the AP is used to update the token's Status field. + + When a non-blocking execution of a procedure on an AP is invoked via BroadcastProcedure + this function will block the caller until the remote procedure has completed on all of the APs that + entered MM. The non-blocking procedure invocation is identified by the Token parameter, which + must match the token that used when BroadcastProcedure was called. Upon completion the + overall status returned by the procedures that executed on the broadcast AP is used to update the + token's Status field. The overall status may be EFI_SUCCESS, if all executions succeeded, or the + first observed failure. + + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Token This parameter describes the token that was passed into + DispatchProcedure or BroadcastProcedure. + + @retval EFI_SUCCESS Procedure has completed. + @retval EFI_INVALID_PARAMETER Token or Token->Completion is NULL + @retval EFI_NOT_FOUND Token is not currently in use for a non-blocking call + +**/ +EFI_STATUS +EFIAPI +SmmMpWaitForProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN MM_COMPLETION Token + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.c new file mode 100644 index 00000000..17c7e552 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.c @@ -0,0 +1,1517 @@ +/** @file +Enable SMM profile. + +Copyright (c) 2012 - 2019, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2017 - 2020, AMD Incorporated. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" +#include "SmmProfileInternal.h" + +UINT32 mSmmProfileCr3; + +SMM_PROFILE_HEADER *mSmmProfileBase; +MSR_DS_AREA_STRUCT *mMsrDsAreaBase; +// +// The buffer to store SMM profile data. +// +UINTN mSmmProfileSize; + +// +// The buffer to enable branch trace store. +// +UINTN mMsrDsAreaSize = SMM_PROFILE_DTS_SIZE; + +// +// The flag indicates if execute-disable is supported by processor. +// +BOOLEAN mXdSupported = TRUE; + +// +// The flag indicates if execute-disable is enabled on processor. +// +BOOLEAN mXdEnabled = FALSE; + +// +// The flag indicates if BTS is supported by processor. +// +BOOLEAN mBtsSupported = TRUE; + +// +// The flag indicates if SMM profile starts to record data. +// +BOOLEAN mSmmProfileStart = FALSE; + +// +// The flag indicates if #DB will be setup in #PF handler. +// +BOOLEAN mSetupDebugTrap = FALSE; + +// +// Record the page fault exception count for one instruction execution. +// +UINTN *mPFEntryCount; + +UINT64 (*mLastPFEntryValue)[MAX_PF_ENTRY_COUNT]; +UINT64 *(*mLastPFEntryPointer)[MAX_PF_ENTRY_COUNT]; + +MSR_DS_AREA_STRUCT **mMsrDsArea; +BRANCH_TRACE_RECORD **mMsrBTSRecord; +UINTN mBTSRecordNumber; +PEBS_RECORD **mMsrPEBSRecord; + +// +// These memory ranges are always present, they does not generate the access type of page fault exception, +// but they possibly generate instruction fetch type of page fault exception. +// +MEMORY_PROTECTION_RANGE *mProtectionMemRange = NULL; +UINTN mProtectionMemRangeCount = 0; + +// +// Some predefined memory ranges. +// +MEMORY_PROTECTION_RANGE mProtectionMemRangeTemplate[] = { + // + // SMRAM range (to be fixed in runtime). + // It is always present and instruction fetches are allowed. + // + {{0x00000000, 0x00000000},TRUE,FALSE}, + + // + // SMM profile data range( to be fixed in runtime). + // It is always present and instruction fetches are not allowed. + // + {{0x00000000, 0x00000000},TRUE,TRUE}, + + // + // SMRAM ranges not covered by mCpuHotPlugData.SmrrBase/mCpuHotPlugData.SmrrSiz (to be fixed in runtime). + // It is always present and instruction fetches are allowed. + // {{0x00000000, 0x00000000},TRUE,FALSE}, + // + + // + // Future extended range could be added here. + // + + // + // PCI MMIO ranges (to be added in runtime). + // They are always present and instruction fetches are not allowed. + // +}; + +// +// These memory ranges are mapped by 4KB-page instead of 2MB-page. +// +MEMORY_RANGE *mSplitMemRange = NULL; +UINTN mSplitMemRangeCount = 0; + +// +// SMI command port. +// +UINT32 mSmiCommandPort; + +/** + Disable branch trace store. + +**/ +VOID +DisableBTS ( + VOID + ) +{ + AsmMsrAnd64 (MSR_DEBUG_CTL, ~((UINT64)(MSR_DEBUG_CTL_BTS | MSR_DEBUG_CTL_TR))); +} + +/** + Enable branch trace store. + +**/ +VOID +EnableBTS ( + VOID + ) +{ + AsmMsrOr64 (MSR_DEBUG_CTL, (MSR_DEBUG_CTL_BTS | MSR_DEBUG_CTL_TR)); +} + +/** + Get CPU Index from APIC ID. + +**/ +UINTN +GetCpuIndex ( + VOID + ) +{ + UINTN Index; + UINT32 ApicId; + + ApicId = GetApicId (); + + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == ApicId) { + return Index; + } + } + ASSERT (FALSE); + return 0; +} + +/** + Get the source of IP after execute-disable exception is triggered. + + @param CpuIndex The index of CPU. + @param DestinationIP The destination address. + +**/ +UINT64 +GetSourceFromDestinationOnBts ( + UINTN CpuIndex, + UINT64 DestinationIP + ) +{ + BRANCH_TRACE_RECORD *CurrentBTSRecord; + UINTN Index; + BOOLEAN FirstMatch; + + FirstMatch = FALSE; + + CurrentBTSRecord = (BRANCH_TRACE_RECORD *)mMsrDsArea[CpuIndex]->BTSIndex; + for (Index = 0; Index < mBTSRecordNumber; Index++) { + if ((UINTN)CurrentBTSRecord < (UINTN)mMsrBTSRecord[CpuIndex]) { + // + // Underflow + // + CurrentBTSRecord = (BRANCH_TRACE_RECORD *)((UINTN)mMsrDsArea[CpuIndex]->BTSAbsoluteMaximum - 1); + CurrentBTSRecord --; + } + if (CurrentBTSRecord->LastBranchTo == DestinationIP) { + // + // Good! find 1st one, then find 2nd one. + // + if (!FirstMatch) { + // + // The first one is DEBUG exception + // + FirstMatch = TRUE; + } else { + // + // Good find proper one. + // + return CurrentBTSRecord->LastBranchFrom; + } + } + CurrentBTSRecord--; + } + + return 0; +} + +/** + SMM profile specific INT 1 (single-step) exception handler. + + @param InterruptType Defines the type of interrupt or exception that + occurred on the processor.This parameter is processor architecture specific. + @param SystemContext A pointer to the processor context when + the interrupt occurred on the processor. +**/ +VOID +EFIAPI +DebugExceptionHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN CpuIndex; + UINTN PFEntry; + + if (!mSmmProfileStart && + !HEAP_GUARD_NONSTOP_MODE && + !NULL_DETECTION_NONSTOP_MODE) { + return; + } + CpuIndex = GetCpuIndex (); + + // + // Clear last PF entries + // + for (PFEntry = 0; PFEntry < mPFEntryCount[CpuIndex]; PFEntry++) { + *mLastPFEntryPointer[CpuIndex][PFEntry] = mLastPFEntryValue[CpuIndex][PFEntry]; + } + + // + // Reset page fault exception count for next page fault. + // + mPFEntryCount[CpuIndex] = 0; + + // + // Flush TLB + // + CpuFlushTlb (); + + // + // Clear TF in EFLAGS + // + ClearTrapFlag (SystemContext); +} + +/** + Check if the input address is in SMM ranges. + + @param[in] Address The input address. + + @retval TRUE The input address is in SMM. + @retval FALSE The input address is not in SMM. +**/ +BOOLEAN +IsInSmmRanges ( + IN EFI_PHYSICAL_ADDRESS Address + ) +{ + UINTN Index; + + if ((Address >= mCpuHotPlugData.SmrrBase) && (Address < mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) { + return TRUE; + } + for (Index = 0; Index < mSmmCpuSmramRangeCount; Index++) { + if (Address >= mSmmCpuSmramRanges[Index].CpuStart && + Address < mSmmCpuSmramRanges[Index].CpuStart + mSmmCpuSmramRanges[Index].PhysicalSize) { + return TRUE; + } + } + return FALSE; +} + +/** + Check if the memory address will be mapped by 4KB-page. + + @param Address The address of Memory. + @param Nx The flag indicates if the memory is execute-disable. + +**/ +BOOLEAN +IsAddressValid ( + IN EFI_PHYSICAL_ADDRESS Address, + IN BOOLEAN *Nx + ) +{ + UINTN Index; + + if (FeaturePcdGet (PcdCpuSmmProfileEnable)) { + // + // Check configuration + // + for (Index = 0; Index < mProtectionMemRangeCount; Index++) { + if ((Address >= mProtectionMemRange[Index].Range.Base) && (Address < mProtectionMemRange[Index].Range.Top)) { + *Nx = mProtectionMemRange[Index].Nx; + return mProtectionMemRange[Index].Present; + } + } + *Nx = TRUE; + return FALSE; + + } else { + *Nx = TRUE; + if (IsInSmmRanges (Address)) { + *Nx = FALSE; + } + return TRUE; + } +} + +/** + Check if the memory address will be mapped by 4KB-page. + + @param Address The address of Memory. + +**/ +BOOLEAN +IsAddressSplit ( + IN EFI_PHYSICAL_ADDRESS Address + ) +{ + UINTN Index; + + if (FeaturePcdGet (PcdCpuSmmProfileEnable)) { + // + // Check configuration + // + for (Index = 0; Index < mSplitMemRangeCount; Index++) { + if ((Address >= mSplitMemRange[Index].Base) && (Address < mSplitMemRange[Index].Top)) { + return TRUE; + } + } + } else { + if (Address < mCpuHotPlugData.SmrrBase) { + if ((mCpuHotPlugData.SmrrBase - Address) < BASE_2MB) { + return TRUE; + } + } else if (Address > (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize - BASE_2MB)) { + if ((Address - (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize - BASE_2MB)) < BASE_2MB) { + return TRUE; + } + } + } + // + // Return default + // + return FALSE; +} + +/** + Initialize the protected memory ranges and the 4KB-page mapped memory ranges. + +**/ +VOID +InitProtectedMemRange ( + VOID + ) +{ + UINTN Index; + UINTN NumberOfDescriptors; + UINTN NumberOfAddedDescriptors; + UINTN NumberOfProtectRange; + UINTN NumberOfSpliteRange; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; + UINTN TotalSize; + EFI_PHYSICAL_ADDRESS ProtectBaseAddress; + EFI_PHYSICAL_ADDRESS ProtectEndAddress; + EFI_PHYSICAL_ADDRESS Top2MBAlignedAddress; + EFI_PHYSICAL_ADDRESS Base2MBAlignedAddress; + UINT64 High4KBPageSize; + UINT64 Low4KBPageSize; + + NumberOfDescriptors = 0; + NumberOfAddedDescriptors = mSmmCpuSmramRangeCount; + NumberOfSpliteRange = 0; + MemorySpaceMap = NULL; + + // + // Get MMIO ranges from GCD and add them into protected memory ranges. + // + gDS->GetMemorySpaceMap ( + &NumberOfDescriptors, + &MemorySpaceMap + ); + for (Index = 0; Index < NumberOfDescriptors; Index++) { + if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) { + NumberOfAddedDescriptors++; + } + } + + if (NumberOfAddedDescriptors != 0) { + TotalSize = NumberOfAddedDescriptors * sizeof (MEMORY_PROTECTION_RANGE) + sizeof (mProtectionMemRangeTemplate); + mProtectionMemRange = (MEMORY_PROTECTION_RANGE *) AllocateZeroPool (TotalSize); + ASSERT (mProtectionMemRange != NULL); + mProtectionMemRangeCount = TotalSize / sizeof (MEMORY_PROTECTION_RANGE); + + // + // Copy existing ranges. + // + CopyMem (mProtectionMemRange, mProtectionMemRangeTemplate, sizeof (mProtectionMemRangeTemplate)); + + // + // Create split ranges which come from protected ranges. + // + TotalSize = (TotalSize / sizeof (MEMORY_PROTECTION_RANGE)) * sizeof (MEMORY_RANGE); + mSplitMemRange = (MEMORY_RANGE *) AllocateZeroPool (TotalSize); + ASSERT (mSplitMemRange != NULL); + + // + // Create SMM ranges which are set to present and execution-enable. + // + NumberOfProtectRange = sizeof (mProtectionMemRangeTemplate) / sizeof (MEMORY_PROTECTION_RANGE); + for (Index = 0; Index < mSmmCpuSmramRangeCount; Index++) { + if (mSmmCpuSmramRanges[Index].CpuStart >= mProtectionMemRange[0].Range.Base && + mSmmCpuSmramRanges[Index].CpuStart + mSmmCpuSmramRanges[Index].PhysicalSize < mProtectionMemRange[0].Range.Top) { + // + // If the address have been already covered by mCpuHotPlugData.SmrrBase/mCpuHotPlugData.SmrrSiz + // + break; + } + mProtectionMemRange[NumberOfProtectRange].Range.Base = mSmmCpuSmramRanges[Index].CpuStart; + mProtectionMemRange[NumberOfProtectRange].Range.Top = mSmmCpuSmramRanges[Index].CpuStart + mSmmCpuSmramRanges[Index].PhysicalSize; + mProtectionMemRange[NumberOfProtectRange].Present = TRUE; + mProtectionMemRange[NumberOfProtectRange].Nx = FALSE; + NumberOfProtectRange++; + } + + // + // Create MMIO ranges which are set to present and execution-disable. + // + for (Index = 0; Index < NumberOfDescriptors; Index++) { + if (MemorySpaceMap[Index].GcdMemoryType != EfiGcdMemoryTypeMemoryMappedIo) { + continue; + } + mProtectionMemRange[NumberOfProtectRange].Range.Base = MemorySpaceMap[Index].BaseAddress; + mProtectionMemRange[NumberOfProtectRange].Range.Top = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length; + mProtectionMemRange[NumberOfProtectRange].Present = TRUE; + mProtectionMemRange[NumberOfProtectRange].Nx = TRUE; + NumberOfProtectRange++; + } + + // + // Check and updated actual protected memory ranges count + // + ASSERT (NumberOfProtectRange <= mProtectionMemRangeCount); + mProtectionMemRangeCount = NumberOfProtectRange; + } + + // + // According to protected ranges, create the ranges which will be mapped by 2KB page. + // + NumberOfSpliteRange = 0; + NumberOfProtectRange = mProtectionMemRangeCount; + for (Index = 0; Index < NumberOfProtectRange; Index++) { + // + // If MMIO base address is not 2MB alignment, make 2MB alignment for create 4KB page in page table. + // + ProtectBaseAddress = mProtectionMemRange[Index].Range.Base; + ProtectEndAddress = mProtectionMemRange[Index].Range.Top; + if (((ProtectBaseAddress & (SIZE_2MB - 1)) != 0) || ((ProtectEndAddress & (SIZE_2MB - 1)) != 0)) { + // + // Check if it is possible to create 4KB-page for not 2MB-aligned range and to create 2MB-page for 2MB-aligned range. + // A mix of 4KB and 2MB page could save SMRAM space. + // + Top2MBAlignedAddress = ProtectEndAddress & ~(SIZE_2MB - 1); + Base2MBAlignedAddress = (ProtectBaseAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1); + if ((Top2MBAlignedAddress > Base2MBAlignedAddress) && + ((Top2MBAlignedAddress - Base2MBAlignedAddress) >= SIZE_2MB)) { + // + // There is an range which could be mapped by 2MB-page. + // + High4KBPageSize = ((ProtectEndAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1)) - (ProtectEndAddress & ~(SIZE_2MB - 1)); + Low4KBPageSize = ((ProtectBaseAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1)) - (ProtectBaseAddress & ~(SIZE_2MB - 1)); + if (High4KBPageSize != 0) { + // + // Add not 2MB-aligned range to be mapped by 4KB-page. + // + mSplitMemRange[NumberOfSpliteRange].Base = ProtectEndAddress & ~(SIZE_2MB - 1); + mSplitMemRange[NumberOfSpliteRange].Top = (ProtectEndAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1); + NumberOfSpliteRange++; + } + if (Low4KBPageSize != 0) { + // + // Add not 2MB-aligned range to be mapped by 4KB-page. + // + mSplitMemRange[NumberOfSpliteRange].Base = ProtectBaseAddress & ~(SIZE_2MB - 1); + mSplitMemRange[NumberOfSpliteRange].Top = (ProtectBaseAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1); + NumberOfSpliteRange++; + } + } else { + // + // The range could only be mapped by 4KB-page. + // + mSplitMemRange[NumberOfSpliteRange].Base = ProtectBaseAddress & ~(SIZE_2MB - 1); + mSplitMemRange[NumberOfSpliteRange].Top = (ProtectEndAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1); + NumberOfSpliteRange++; + } + } + } + + mSplitMemRangeCount = NumberOfSpliteRange; + + DEBUG ((EFI_D_INFO, "SMM Profile Memory Ranges:\n")); + for (Index = 0; Index < mProtectionMemRangeCount; Index++) { + DEBUG ((EFI_D_INFO, "mProtectionMemRange[%d].Base = %lx\n", Index, mProtectionMemRange[Index].Range.Base)); + DEBUG ((EFI_D_INFO, "mProtectionMemRange[%d].Top = %lx\n", Index, mProtectionMemRange[Index].Range.Top)); + } + for (Index = 0; Index < mSplitMemRangeCount; Index++) { + DEBUG ((EFI_D_INFO, "mSplitMemRange[%d].Base = %lx\n", Index, mSplitMemRange[Index].Base)); + DEBUG ((EFI_D_INFO, "mSplitMemRange[%d].Top = %lx\n", Index, mSplitMemRange[Index].Top)); + } +} + +/** + Update page table according to protected memory ranges and the 4KB-page mapped memory ranges. + +**/ +VOID +InitPaging ( + VOID + ) +{ + UINT64 Pml5Entry; + UINT64 Pml4Entry; + UINT64 *Pml5; + UINT64 *Pml4; + UINT64 *Pdpt; + UINT64 *Pd; + UINT64 *Pt; + UINTN Address; + UINTN Pml5Index; + UINTN Pml4Index; + UINTN PdptIndex; + UINTN PdIndex; + UINTN PtIndex; + UINTN NumberOfPdptEntries; + UINTN NumberOfPml4Entries; + UINTN NumberOfPml5Entries; + UINTN SizeOfMemorySpace; + BOOLEAN Nx; + IA32_CR4 Cr4; + BOOLEAN Enable5LevelPaging; + + Cr4.UintN = AsmReadCr4 (); + Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1); + + if (sizeof (UINTN) == sizeof (UINT64)) { + if (!Enable5LevelPaging) { + Pml5Entry = (UINTN) mSmmProfileCr3 | IA32_PG_P; + Pml5 = &Pml5Entry; + } else { + Pml5 = (UINT64*) (UINTN) mSmmProfileCr3; + } + SizeOfMemorySpace = HighBitSet64 (gPhyMask) + 1; + // + // Calculate the table entries of PML4E and PDPTE. + // + NumberOfPml5Entries = 1; + if (SizeOfMemorySpace > 48) { + NumberOfPml5Entries = (UINTN) LShiftU64 (1, SizeOfMemorySpace - 48); + SizeOfMemorySpace = 48; + } + + NumberOfPml4Entries = 1; + if (SizeOfMemorySpace > 39) { + NumberOfPml4Entries = (UINTN) LShiftU64 (1, SizeOfMemorySpace - 39); + SizeOfMemorySpace = 39; + } + + NumberOfPdptEntries = 1; + ASSERT (SizeOfMemorySpace > 30); + NumberOfPdptEntries = (UINTN) LShiftU64 (1, SizeOfMemorySpace - 30); + } else { + Pml4Entry = (UINTN) mSmmProfileCr3 | IA32_PG_P; + Pml4 = &Pml4Entry; + Pml5Entry = (UINTN) Pml4 | IA32_PG_P; + Pml5 = &Pml5Entry; + NumberOfPml5Entries = 1; + NumberOfPml4Entries = 1; + NumberOfPdptEntries = 4; + } + + // + // Go through page table and change 2MB-page into 4KB-page. + // + for (Pml5Index = 0; Pml5Index < NumberOfPml5Entries; Pml5Index++) { + if ((Pml5[Pml5Index] & IA32_PG_P) == 0) { + // + // If PML5 entry does not exist, skip it + // + continue; + } + Pml4 = (UINT64 *) (UINTN) (Pml5[Pml5Index] & PHYSICAL_ADDRESS_MASK); + for (Pml4Index = 0; Pml4Index < NumberOfPml4Entries; Pml4Index++) { + if ((Pml4[Pml4Index] & IA32_PG_P) == 0) { + // + // If PML4 entry does not exist, skip it + // + continue; + } + Pdpt = (UINT64 *)(UINTN)(Pml4[Pml4Index] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + for (PdptIndex = 0; PdptIndex < NumberOfPdptEntries; PdptIndex++, Pdpt++) { + if ((*Pdpt & IA32_PG_P) == 0) { + // + // If PDPT entry does not exist, skip it + // + continue; + } + if ((*Pdpt & IA32_PG_PS) != 0) { + // + // This is 1G entry, skip it + // + continue; + } + Pd = (UINT64 *)(UINTN)(*Pdpt & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + if (Pd == 0) { + continue; + } + for (PdIndex = 0; PdIndex < SIZE_4KB / sizeof (*Pd); PdIndex++, Pd++) { + if ((*Pd & IA32_PG_P) == 0) { + // + // If PD entry does not exist, skip it + // + continue; + } + Address = (UINTN) LShiftU64 ( + LShiftU64 ( + LShiftU64 ((Pml5Index << 9) + Pml4Index, 9) + PdptIndex, + 9 + ) + PdIndex, + 21 + ); + + // + // If it is 2M page, check IsAddressSplit() + // + if (((*Pd & IA32_PG_PS) != 0) && IsAddressSplit (Address)) { + // + // Based on current page table, create 4KB page table for split area. + // + ASSERT (Address == (*Pd & PHYSICAL_ADDRESS_MASK)); + + Pt = AllocatePageTableMemory (1); + ASSERT (Pt != NULL); + + // Split it + for (PtIndex = 0; PtIndex < SIZE_4KB / sizeof(*Pt); PtIndex++) { + Pt[PtIndex] = Address + ((PtIndex << 12) | mAddressEncMask | PAGE_ATTRIBUTE_BITS); + } // end for PT + *Pd = (UINT64)(UINTN)Pt | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + } // end if IsAddressSplit + } // end for PD + } // end for PDPT + } // end for PML4 + } // end for PML5 + + // + // Go through page table and set several page table entries to absent or execute-disable. + // + DEBUG ((EFI_D_INFO, "Patch page table start ...\n")); + for (Pml5Index = 0; Pml5Index < NumberOfPml5Entries; Pml5Index++) { + if ((Pml5[Pml5Index] & IA32_PG_P) == 0) { + // + // If PML5 entry does not exist, skip it + // + continue; + } + Pml4 = (UINT64 *) (UINTN) (Pml5[Pml5Index] & PHYSICAL_ADDRESS_MASK); + for (Pml4Index = 0; Pml4Index < NumberOfPml4Entries; Pml4Index++) { + if ((Pml4[Pml4Index] & IA32_PG_P) == 0) { + // + // If PML4 entry does not exist, skip it + // + continue; + } + Pdpt = (UINT64 *)(UINTN)(Pml4[Pml4Index] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + for (PdptIndex = 0; PdptIndex < NumberOfPdptEntries; PdptIndex++, Pdpt++) { + if ((*Pdpt & IA32_PG_P) == 0) { + // + // If PDPT entry does not exist, skip it + // + continue; + } + if ((*Pdpt & IA32_PG_PS) != 0) { + // + // This is 1G entry, set NX bit and skip it + // + if (mXdSupported) { + *Pdpt = *Pdpt | IA32_PG_NX; + } + continue; + } + Pd = (UINT64 *)(UINTN)(*Pdpt & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + if (Pd == 0) { + continue; + } + for (PdIndex = 0; PdIndex < SIZE_4KB / sizeof (*Pd); PdIndex++, Pd++) { + if ((*Pd & IA32_PG_P) == 0) { + // + // If PD entry does not exist, skip it + // + continue; + } + Address = (UINTN) LShiftU64 ( + LShiftU64 ( + LShiftU64 ((Pml5Index << 9) + Pml4Index, 9) + PdptIndex, + 9 + ) + PdIndex, + 21 + ); + + if ((*Pd & IA32_PG_PS) != 0) { + // 2MB page + + if (!IsAddressValid (Address, &Nx)) { + // + // Patch to remove Present flag and RW flag + // + *Pd = *Pd & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS); + } + if (Nx && mXdSupported) { + *Pd = *Pd | IA32_PG_NX; + } + } else { + // 4KB page + Pt = (UINT64 *)(UINTN)(*Pd & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + if (Pt == 0) { + continue; + } + for (PtIndex = 0; PtIndex < SIZE_4KB / sizeof(*Pt); PtIndex++, Pt++) { + if (!IsAddressValid (Address, &Nx)) { + *Pt = *Pt & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS); + } + if (Nx && mXdSupported) { + *Pt = *Pt | IA32_PG_NX; + } + Address += SIZE_4KB; + } // end for PT + } // end if PS + } // end for PD + } // end for PDPT + } // end for PML4 + } // end for PML5 + + // + // Flush TLB + // + CpuFlushTlb (); + DEBUG ((EFI_D_INFO, "Patch page table done!\n")); + // + // Set execute-disable flag + // + mXdEnabled = TRUE; + + return ; +} + +/** + To get system port address of the SMI Command Port in FADT table. + +**/ +VOID +GetSmiCommandPort ( + VOID + ) +{ + EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt; + + Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *) EfiLocateFirstAcpiTable ( + EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE + ); + ASSERT (Fadt != NULL); + + mSmiCommandPort = Fadt->SmiCmd; + DEBUG ((EFI_D_INFO, "mSmiCommandPort = %x\n", mSmiCommandPort)); +} + +/** + Updates page table to make some memory ranges (like system memory) absent + and make some memory ranges (like MMIO) present and execute disable. It also + update 2MB-page to 4KB-page for some memory ranges. + +**/ +VOID +SmmProfileStart ( + VOID + ) +{ + // + // The flag indicates SMM profile starts to work. + // + mSmmProfileStart = TRUE; +} + +/** + Initialize SMM profile in SmmReadyToLock protocol callback function. + + @param Protocol Points to the protocol's unique identifier. + @param Interface Points to the interface instance. + @param Handle The handle on which the interface was installed. + + @retval EFI_SUCCESS SmmReadyToLock protocol callback runs successfully. +**/ +EFI_STATUS +EFIAPI +InitSmmProfileCallBack ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + // + // Save to variable so that SMM profile data can be found. + // + gRT->SetVariable ( + SMM_PROFILE_NAME, + &gEfiCallerIdGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(mSmmProfileBase), + &mSmmProfileBase + ); + + // + // Get Software SMI from FADT + // + GetSmiCommandPort (); + + // + // Initialize protected memory range for patching page table later. + // + InitProtectedMemRange (); + + return EFI_SUCCESS; +} + +/** + Initialize SMM profile data structures. + +**/ +VOID +InitSmmProfileInternal ( + VOID + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Base; + VOID *Registration; + UINTN Index; + UINTN MsrDsAreaSizePerCpu; + UINTN TotalSize; + + mPFEntryCount = (UINTN *)AllocateZeroPool (sizeof (UINTN) * mMaxNumberOfCpus); + ASSERT (mPFEntryCount != NULL); + mLastPFEntryValue = (UINT64 (*)[MAX_PF_ENTRY_COUNT])AllocateZeroPool ( + sizeof (mLastPFEntryValue[0]) * mMaxNumberOfCpus); + ASSERT (mLastPFEntryValue != NULL); + mLastPFEntryPointer = (UINT64 *(*)[MAX_PF_ENTRY_COUNT])AllocateZeroPool ( + sizeof (mLastPFEntryPointer[0]) * mMaxNumberOfCpus); + ASSERT (mLastPFEntryPointer != NULL); + + // + // Allocate memory for SmmProfile below 4GB. + // The base address + // + mSmmProfileSize = PcdGet32 (PcdCpuSmmProfileSize); + ASSERT ((mSmmProfileSize & 0xFFF) == 0); + + if (mBtsSupported) { + TotalSize = mSmmProfileSize + mMsrDsAreaSize; + } else { + TotalSize = mSmmProfileSize; + } + + Base = 0xFFFFFFFF; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiReservedMemoryType, + EFI_SIZE_TO_PAGES (TotalSize), + &Base + ); + ASSERT_EFI_ERROR (Status); + ZeroMem ((VOID *)(UINTN)Base, TotalSize); + mSmmProfileBase = (SMM_PROFILE_HEADER *)(UINTN)Base; + + // + // Initialize SMM profile data header. + // + mSmmProfileBase->HeaderSize = sizeof (SMM_PROFILE_HEADER); + mSmmProfileBase->MaxDataEntries = (UINT64)((mSmmProfileSize - sizeof(SMM_PROFILE_HEADER)) / sizeof (SMM_PROFILE_ENTRY)); + mSmmProfileBase->MaxDataSize = MultU64x64 (mSmmProfileBase->MaxDataEntries, sizeof(SMM_PROFILE_ENTRY)); + mSmmProfileBase->CurDataEntries = 0; + mSmmProfileBase->CurDataSize = 0; + mSmmProfileBase->TsegStart = mCpuHotPlugData.SmrrBase; + mSmmProfileBase->TsegSize = mCpuHotPlugData.SmrrSize; + mSmmProfileBase->NumSmis = 0; + mSmmProfileBase->NumCpus = gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; + + if (mBtsSupported) { + mMsrDsArea = (MSR_DS_AREA_STRUCT **)AllocateZeroPool (sizeof (MSR_DS_AREA_STRUCT *) * mMaxNumberOfCpus); + ASSERT (mMsrDsArea != NULL); + mMsrBTSRecord = (BRANCH_TRACE_RECORD **)AllocateZeroPool (sizeof (BRANCH_TRACE_RECORD *) * mMaxNumberOfCpus); + ASSERT (mMsrBTSRecord != NULL); + mMsrPEBSRecord = (PEBS_RECORD **)AllocateZeroPool (sizeof (PEBS_RECORD *) * mMaxNumberOfCpus); + ASSERT (mMsrPEBSRecord != NULL); + + mMsrDsAreaBase = (MSR_DS_AREA_STRUCT *)((UINTN)Base + mSmmProfileSize); + MsrDsAreaSizePerCpu = mMsrDsAreaSize / mMaxNumberOfCpus; + mBTSRecordNumber = (MsrDsAreaSizePerCpu - sizeof(PEBS_RECORD) * PEBS_RECORD_NUMBER - sizeof(MSR_DS_AREA_STRUCT)) / sizeof(BRANCH_TRACE_RECORD); + for (Index = 0; Index < mMaxNumberOfCpus; Index++) { + mMsrDsArea[Index] = (MSR_DS_AREA_STRUCT *)((UINTN)mMsrDsAreaBase + MsrDsAreaSizePerCpu * Index); + mMsrBTSRecord[Index] = (BRANCH_TRACE_RECORD *)((UINTN)mMsrDsArea[Index] + sizeof(MSR_DS_AREA_STRUCT)); + mMsrPEBSRecord[Index] = (PEBS_RECORD *)((UINTN)mMsrDsArea[Index] + MsrDsAreaSizePerCpu - sizeof(PEBS_RECORD) * PEBS_RECORD_NUMBER); + + mMsrDsArea[Index]->BTSBufferBase = (UINTN)mMsrBTSRecord[Index]; + mMsrDsArea[Index]->BTSIndex = mMsrDsArea[Index]->BTSBufferBase; + mMsrDsArea[Index]->BTSAbsoluteMaximum = mMsrDsArea[Index]->BTSBufferBase + mBTSRecordNumber * sizeof(BRANCH_TRACE_RECORD) + 1; + mMsrDsArea[Index]->BTSInterruptThreshold = mMsrDsArea[Index]->BTSAbsoluteMaximum + 1; + + mMsrDsArea[Index]->PEBSBufferBase = (UINTN)mMsrPEBSRecord[Index]; + mMsrDsArea[Index]->PEBSIndex = mMsrDsArea[Index]->PEBSBufferBase; + mMsrDsArea[Index]->PEBSAbsoluteMaximum = mMsrDsArea[Index]->PEBSBufferBase + PEBS_RECORD_NUMBER * sizeof(PEBS_RECORD) + 1; + mMsrDsArea[Index]->PEBSInterruptThreshold = mMsrDsArea[Index]->PEBSAbsoluteMaximum + 1; + } + } + + mProtectionMemRange = mProtectionMemRangeTemplate; + mProtectionMemRangeCount = sizeof (mProtectionMemRangeTemplate) / sizeof (MEMORY_PROTECTION_RANGE); + + // + // Update TSeg entry. + // + mProtectionMemRange[0].Range.Base = mCpuHotPlugData.SmrrBase; + mProtectionMemRange[0].Range.Top = mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize; + + // + // Update SMM profile entry. + // + mProtectionMemRange[1].Range.Base = (EFI_PHYSICAL_ADDRESS)(UINTN)mSmmProfileBase; + mProtectionMemRange[1].Range.Top = (EFI_PHYSICAL_ADDRESS)(UINTN)mSmmProfileBase + TotalSize; + + // + // Allocate memory reserved for creating 4KB pages. + // + InitPagesForPFHandler (); + + // + // Start SMM profile when SmmReadyToLock protocol is installed. + // + Status = gSmst->SmmRegisterProtocolNotify ( + &gEfiSmmReadyToLockProtocolGuid, + InitSmmProfileCallBack, + &Registration + ); + ASSERT_EFI_ERROR (Status); + + return ; +} + +/** + Check if feature is supported by a processor. + +**/ +VOID +CheckFeatureSupported ( + VOID + ) +{ + UINT32 RegEax; + UINT32 RegEcx; + UINT32 RegEdx; + MSR_IA32_MISC_ENABLE_REGISTER MiscEnableMsr; + + if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) { + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax <= CPUID_EXTENDED_FUNCTION) { + mCetSupported = FALSE; + PatchInstructionX86 (mPatchCetSupported, mCetSupported, 1); + } + AsmCpuidEx (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_SUB_LEAF_INFO, NULL, NULL, &RegEcx, NULL); + if ((RegEcx & CPUID_CET_SS) == 0) { + mCetSupported = FALSE; + PatchInstructionX86 (mPatchCetSupported, mCetSupported, 1); + } + } + + if (mXdSupported) { + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax <= CPUID_EXTENDED_FUNCTION) { + // + // Extended CPUID functions are not supported on this processor. + // + mXdSupported = FALSE; + PatchInstructionX86 (gPatchXdSupported, mXdSupported, 1); + } + + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & CPUID1_EDX_XD_SUPPORT) == 0) { + // + // Execute Disable Bit feature is not supported on this processor. + // + mXdSupported = FALSE; + PatchInstructionX86 (gPatchXdSupported, mXdSupported, 1); + } + + if (StandardSignatureIsAuthenticAMD ()) { + // + // AMD processors do not support MSR_IA32_MISC_ENABLE + // + PatchInstructionX86 (gPatchMsrIa32MiscEnableSupported, FALSE, 1); + } + } + + if (mBtsSupported) { + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & CPUID1_EDX_BTS_AVAILABLE) != 0) { + // + // Per IA32 manuals: + // When CPUID.1:EDX[21] is set, the following BTS facilities are available: + // 1. The BTS_UNAVAILABLE flag in the IA32_MISC_ENABLE MSR indicates the + // availability of the BTS facilities, including the ability to set the BTS and + // BTINT bits in the MSR_DEBUGCTLA MSR. + // 2. The IA32_DS_AREA MSR can be programmed to point to the DS save area. + // + MiscEnableMsr.Uint64 = AsmReadMsr64 (MSR_IA32_MISC_ENABLE); + if (MiscEnableMsr.Bits.BTS == 1) { + // + // BTS facilities is not supported if MSR_IA32_MISC_ENABLE.BTS bit is set. + // + mBtsSupported = FALSE; + } + } + } +} + +/** + Enable single step. + +**/ +VOID +ActivateSingleStepDB ( + VOID + ) +{ + UINTN Dr6; + + Dr6 = AsmReadDr6 (); + if ((Dr6 & DR6_SINGLE_STEP) != 0) { + return; + } + Dr6 |= DR6_SINGLE_STEP; + AsmWriteDr6 (Dr6); +} + +/** + Enable last branch. + +**/ +VOID +ActivateLBR ( + VOID + ) +{ + UINT64 DebugCtl; + + DebugCtl = AsmReadMsr64 (MSR_DEBUG_CTL); + if ((DebugCtl & MSR_DEBUG_CTL_LBR) != 0) { + return ; + } + DebugCtl |= MSR_DEBUG_CTL_LBR; + AsmWriteMsr64 (MSR_DEBUG_CTL, DebugCtl); +} + +/** + Enable branch trace store. + + @param CpuIndex The index of the processor. + +**/ +VOID +ActivateBTS ( + IN UINTN CpuIndex + ) +{ + UINT64 DebugCtl; + + DebugCtl = AsmReadMsr64 (MSR_DEBUG_CTL); + if ((DebugCtl & MSR_DEBUG_CTL_BTS) != 0) { + return ; + } + + AsmWriteMsr64 (MSR_DS_AREA, (UINT64)(UINTN)mMsrDsArea[CpuIndex]); + DebugCtl |= (UINT64)(MSR_DEBUG_CTL_BTS | MSR_DEBUG_CTL_TR); + DebugCtl &= ~((UINT64)MSR_DEBUG_CTL_BTINT); + AsmWriteMsr64 (MSR_DEBUG_CTL, DebugCtl); +} + +/** + Increase SMI number in each SMI entry. + +**/ +VOID +SmmProfileRecordSmiNum ( + VOID + ) +{ + if (mSmmProfileStart) { + mSmmProfileBase->NumSmis++; + } +} + +/** + Initialize processor environment for SMM profile. + + @param CpuIndex The index of the processor. + +**/ +VOID +ActivateSmmProfile ( + IN UINTN CpuIndex + ) +{ + // + // Enable Single Step DB# + // + ActivateSingleStepDB (); + + if (mBtsSupported) { + // + // We can not get useful information from LER, so we have to use BTS. + // + ActivateLBR (); + + // + // Enable BTS + // + ActivateBTS (CpuIndex); + } +} + +/** + Initialize SMM profile in SMM CPU entry point. + + @param[in] Cr3 The base address of the page tables to use in SMM. + +**/ +VOID +InitSmmProfile ( + UINT32 Cr3 + ) +{ + // + // Save Cr3 + // + mSmmProfileCr3 = Cr3; + + // + // Skip SMM profile initialization if feature is disabled + // + if (!FeaturePcdGet (PcdCpuSmmProfileEnable) && + !HEAP_GUARD_NONSTOP_MODE && + !NULL_DETECTION_NONSTOP_MODE) { + return; + } + + // + // Initialize SmmProfile here + // + InitSmmProfileInternal (); + + // + // Initialize profile IDT. + // + InitIdtr (); + + // + // Tell #PF handler to prepare a #DB subsequently. + // + mSetupDebugTrap = TRUE; +} + +/** + Update page table to map the memory correctly in order to make the instruction + which caused page fault execute successfully. And it also save the original page + table to be restored in single-step exception. + + @param PageTable PageTable Address. + @param PFAddress The memory address which caused page fault exception. + @param CpuIndex The index of the processor. + @param ErrorCode The Error code of exception. + +**/ +VOID +RestorePageTableBelow4G ( + UINT64 *PageTable, + UINT64 PFAddress, + UINTN CpuIndex, + UINTN ErrorCode + ) +{ + UINTN PTIndex; + UINTN PFIndex; + IA32_CR4 Cr4; + BOOLEAN Enable5LevelPaging; + + Cr4.UintN = AsmReadCr4 (); + Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1); + + // + // PML5 + // + if (Enable5LevelPaging) { + PTIndex = (UINTN)BitFieldRead64 (PFAddress, 48, 56); + ASSERT (PageTable[PTIndex] != 0); + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK); + } + + // + // PML4 + // + if (sizeof(UINT64) == sizeof(UINTN)) { + PTIndex = (UINTN)BitFieldRead64 (PFAddress, 39, 47); + ASSERT (PageTable[PTIndex] != 0); + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK); + } + + // + // PDPTE + // + PTIndex = (UINTN)BitFieldRead64 (PFAddress, 30, 38); + ASSERT (PageTable[PTIndex] != 0); + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK); + + // + // PD + // + PTIndex = (UINTN)BitFieldRead64 (PFAddress, 21, 29); + if ((PageTable[PTIndex] & IA32_PG_PS) != 0) { + // + // Large page + // + + // + // Record old entries with non-present status + // Old entries include the memory which instruction is at and the memory which instruction access. + // + // + ASSERT (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT); + if (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT) { + PFIndex = mPFEntryCount[CpuIndex]; + mLastPFEntryValue[CpuIndex][PFIndex] = PageTable[PTIndex]; + mLastPFEntryPointer[CpuIndex][PFIndex] = &PageTable[PTIndex]; + mPFEntryCount[CpuIndex]++; + } + + // + // Set new entry + // + PageTable[PTIndex] = (PFAddress & ~((1ull << 21) - 1)); + PageTable[PTIndex] |= (UINT64)IA32_PG_PS; + PageTable[PTIndex] |= (UINT64)PAGE_ATTRIBUTE_BITS; + if ((ErrorCode & IA32_PF_EC_ID) != 0) { + PageTable[PTIndex] &= ~IA32_PG_NX; + } + } else { + // + // Small page + // + ASSERT (PageTable[PTIndex] != 0); + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK); + + // + // 4K PTE + // + PTIndex = (UINTN)BitFieldRead64 (PFAddress, 12, 20); + + // + // Record old entries with non-present status + // Old entries include the memory which instruction is at and the memory which instruction access. + // + // + ASSERT (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT); + if (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT) { + PFIndex = mPFEntryCount[CpuIndex]; + mLastPFEntryValue[CpuIndex][PFIndex] = PageTable[PTIndex]; + mLastPFEntryPointer[CpuIndex][PFIndex] = &PageTable[PTIndex]; + mPFEntryCount[CpuIndex]++; + } + + // + // Set new entry + // + PageTable[PTIndex] = (PFAddress & ~((1ull << 12) - 1)); + PageTable[PTIndex] |= (UINT64)PAGE_ATTRIBUTE_BITS; + if ((ErrorCode & IA32_PF_EC_ID) != 0) { + PageTable[PTIndex] &= ~IA32_PG_NX; + } + } +} + +/** + Handler for Page Fault triggered by Guard page. + + @param ErrorCode The Error code of exception. + +**/ +VOID +GuardPagePFHandler ( + UINTN ErrorCode + ) +{ + UINT64 *PageTable; + UINT64 PFAddress; + UINT64 RestoreAddress; + UINTN RestorePageNumber; + UINTN CpuIndex; + + PageTable = (UINT64 *)AsmReadCr3 (); + PFAddress = AsmReadCr2 (); + CpuIndex = GetCpuIndex (); + + // + // Memory operation cross pages, like "rep mov" instruction, will cause + // infinite loop between this and Debug Trap handler. We have to make sure + // that current page and the page followed are both in PRESENT state. + // + RestorePageNumber = 2; + RestoreAddress = PFAddress; + while (RestorePageNumber > 0) { + RestorePageTableBelow4G (PageTable, RestoreAddress, CpuIndex, ErrorCode); + RestoreAddress += EFI_PAGE_SIZE; + RestorePageNumber--; + } + + // + // Flush TLB + // + CpuFlushTlb (); +} + +/** + The Page fault handler to save SMM profile data. + + @param Rip The RIP when exception happens. + @param ErrorCode The Error code of exception. + +**/ +VOID +SmmProfilePFHandler ( + UINTN Rip, + UINTN ErrorCode + ) +{ + UINT64 *PageTable; + UINT64 PFAddress; + UINT64 RestoreAddress; + UINTN RestorePageNumber; + UINTN CpuIndex; + UINTN Index; + UINT64 InstructionAddress; + UINTN MaxEntryNumber; + UINTN CurrentEntryNumber; + BOOLEAN IsValidPFAddress; + SMM_PROFILE_ENTRY *SmmProfileEntry; + UINT64 SmiCommand; + EFI_STATUS Status; + UINT8 SoftSmiValue; + EFI_SMM_SAVE_STATE_IO_INFO IoInfo; + + if (!mSmmProfileStart) { + // + // If SMM profile does not start, call original page fault handler. + // + SmiDefaultPFHandler (); + return; + } + + if (mBtsSupported) { + DisableBTS (); + } + + IsValidPFAddress = FALSE; + PageTable = (UINT64 *)AsmReadCr3 (); + PFAddress = AsmReadCr2 (); + CpuIndex = GetCpuIndex (); + + // + // Memory operation cross pages, like "rep mov" instruction, will cause + // infinite loop between this and Debug Trap handler. We have to make sure + // that current page and the page followed are both in PRESENT state. + // + RestorePageNumber = 2; + RestoreAddress = PFAddress; + while (RestorePageNumber > 0) { + if (RestoreAddress <= 0xFFFFFFFF) { + RestorePageTableBelow4G (PageTable, RestoreAddress, CpuIndex, ErrorCode); + } else { + RestorePageTableAbove4G (PageTable, RestoreAddress, CpuIndex, ErrorCode, &IsValidPFAddress); + } + RestoreAddress += EFI_PAGE_SIZE; + RestorePageNumber--; + } + + if (!IsValidPFAddress) { + InstructionAddress = Rip; + if ((ErrorCode & IA32_PF_EC_ID) != 0 && (mBtsSupported)) { + // + // If it is instruction fetch failure, get the correct IP from BTS. + // + InstructionAddress = GetSourceFromDestinationOnBts (CpuIndex, Rip); + if (InstructionAddress == 0) { + // + // It indicates the instruction which caused page fault is not a jump instruction, + // set instruction address same as the page fault address. + // + InstructionAddress = PFAddress; + } + } + + // + // Indicate it is not software SMI + // + SmiCommand = 0xFFFFFFFFFFFFFFFFULL; + for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { + Status = SmmReadSaveState(&mSmmCpu, sizeof(IoInfo), EFI_SMM_SAVE_STATE_REGISTER_IO, Index, &IoInfo); + if (EFI_ERROR (Status)) { + continue; + } + if (IoInfo.IoPort == mSmiCommandPort) { + // + // A software SMI triggered by SMI command port has been found, get SmiCommand from SMI command port. + // + SoftSmiValue = IoRead8 (mSmiCommandPort); + SmiCommand = (UINT64)SoftSmiValue; + break; + } + } + + SmmProfileEntry = (SMM_PROFILE_ENTRY *)(UINTN)(mSmmProfileBase + 1); + // + // Check if there is already a same entry in profile data. + // + for (Index = 0; Index < (UINTN) mSmmProfileBase->CurDataEntries; Index++) { + if ((SmmProfileEntry[Index].ErrorCode == (UINT64)ErrorCode) && + (SmmProfileEntry[Index].Address == PFAddress) && + (SmmProfileEntry[Index].CpuNum == (UINT64)CpuIndex) && + (SmmProfileEntry[Index].Instruction == InstructionAddress) && + (SmmProfileEntry[Index].SmiCmd == SmiCommand)) { + // + // Same record exist, need not save again. + // + break; + } + } + if (Index == mSmmProfileBase->CurDataEntries) { + CurrentEntryNumber = (UINTN) mSmmProfileBase->CurDataEntries; + MaxEntryNumber = (UINTN) mSmmProfileBase->MaxDataEntries; + if (FeaturePcdGet (PcdCpuSmmProfileRingBuffer)) { + CurrentEntryNumber = CurrentEntryNumber % MaxEntryNumber; + } + if (CurrentEntryNumber < MaxEntryNumber) { + // + // Log the new entry + // + SmmProfileEntry[CurrentEntryNumber].SmiNum = mSmmProfileBase->NumSmis; + SmmProfileEntry[CurrentEntryNumber].ErrorCode = (UINT64)ErrorCode; + SmmProfileEntry[CurrentEntryNumber].ApicId = (UINT64)GetApicId (); + SmmProfileEntry[CurrentEntryNumber].CpuNum = (UINT64)CpuIndex; + SmmProfileEntry[CurrentEntryNumber].Address = PFAddress; + SmmProfileEntry[CurrentEntryNumber].Instruction = InstructionAddress; + SmmProfileEntry[CurrentEntryNumber].SmiCmd = SmiCommand; + // + // Update current entry index and data size in the header. + // + mSmmProfileBase->CurDataEntries++; + mSmmProfileBase->CurDataSize = MultU64x64 (mSmmProfileBase->CurDataEntries, sizeof (SMM_PROFILE_ENTRY)); + } + } + } + // + // Flush TLB + // + CpuFlushTlb (); + + if (mBtsSupported) { + EnableBTS (); + } +} + +/** + Replace INT1 exception handler to restore page table to absent/execute-disable state + in order to trigger page fault again to save SMM profile data.. + +**/ +VOID +InitIdtr ( + VOID + ) +{ + EFI_STATUS Status; + + Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_DEBUG, DebugExceptionHandler); + ASSERT_EFI_ERROR (Status); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.h new file mode 100644 index 00000000..24916d30 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.h @@ -0,0 +1,135 @@ +/** @file +SMM profile header file. + +Copyright (c) 2012 - 2019, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SMM_PROFILE_H_ +#define _SMM_PROFILE_H_ + +#include "SmmProfileInternal.h" + +// +// External functions +// + +/** + Initialize processor environment for SMM profile. + + @param CpuIndex The index of the processor. + +**/ +VOID +ActivateSmmProfile ( + IN UINTN CpuIndex + ); + +/** + Initialize SMM profile in SMM CPU entry point. + + @param[in] Cr3 The base address of the page tables to use in SMM. + +**/ +VOID +InitSmmProfile ( + UINT32 Cr3 + ); + +/** + Increase SMI number in each SMI entry. + +**/ +VOID +SmmProfileRecordSmiNum ( + VOID + ); + +/** + The Page fault handler to save SMM profile data. + + @param Rip The RIP when exception happens. + @param ErrorCode The Error code of exception. + +**/ +VOID +SmmProfilePFHandler ( + UINTN Rip, + UINTN ErrorCode + ); + +/** + Updates page table to make some memory ranges (like system memory) absent + and make some memory ranges (like MMIO) present and execute disable. It also + update 2MB-page to 4KB-page for some memory ranges. + +**/ +VOID +SmmProfileStart ( + VOID + ); + +/** + Page fault IDT handler for SMM Profile. + +**/ +VOID +EFIAPI +PageFaultIdtHandlerSmmProfile ( + VOID + ); + + +/** + Check if feature is supported by a processor. + +**/ +VOID +CheckFeatureSupported ( + VOID + ); + +/** + Update page table according to protected memory ranges and the 4KB-page mapped memory ranges. + +**/ +VOID +InitPaging ( + VOID + ); + +/** + Get CPU Index from APIC ID. + +**/ +UINTN +GetCpuIndex ( + VOID + ); + +/** + Handler for Page Fault triggered by Guard page. + + @param ErrorCode The Error code of exception. + +**/ +VOID +GuardPagePFHandler ( + UINTN ErrorCode + ); + +// +// The flag indicates if execute-disable is supported by processor. +// +extern BOOLEAN mXdSupported; +// +// The flag indicates if execute-disable is enabled on processor. +// +extern BOOLEAN mXdEnabled; +// +// The flag indicates if #DB will be setup in #PF handler. +// +extern BOOLEAN mSetupDebugTrap; + +#endif // _SMM_PROFILE_H_ diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfileInternal.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfileInternal.h new file mode 100644 index 00000000..0054da24 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfileInternal.h @@ -0,0 +1,167 @@ +/** @file +SMM profile internal header file. + +Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2020, AMD Incorporated. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SMM_PROFILE_INTERNAL_H_ +#define _SMM_PROFILE_INTERNAL_H_ + +#include <Protocol/SmmReadyToLock.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include <Library/DxeServicesTableLib.h> +#include <Library/CpuLib.h> +#include <Library/UefiCpuLib.h> +#include <IndustryStandard/Acpi.h> + +#include "SmmProfileArch.h" + +// +// Configure the SMM_PROFILE DTS region size +// +#define SMM_PROFILE_DTS_SIZE (4 * 1024 * 1024) // 4M + +#define MAX_PF_PAGE_COUNT 0x2 + +#define PEBS_RECORD_NUMBER 0x2 + +#define MAX_PF_ENTRY_COUNT 10 + +// +// This MACRO just enable unit test for the profile +// Please disable it. +// + +#define IA32_PF_EC_ID (1u << 4) + +#define SMM_PROFILE_NAME L"SmmProfileData" + +// +// CPU generic definition +// +#define CPUID1_EDX_XD_SUPPORT 0x100000 +#define MSR_EFER 0xc0000080 +#define MSR_EFER_XD 0x800 + +#define CPUID1_EDX_BTS_AVAILABLE 0x200000 + +#define DR6_SINGLE_STEP 0x4000 +#define RFLAG_TF 0x100 + +#define MSR_DEBUG_CTL 0x1D9 +#define MSR_DEBUG_CTL_LBR 0x1 +#define MSR_DEBUG_CTL_TR 0x40 +#define MSR_DEBUG_CTL_BTS 0x80 +#define MSR_DEBUG_CTL_BTINT 0x100 +#define MSR_DS_AREA 0x600 + +#define HEAP_GUARD_NONSTOP_MODE \ + ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT6|BIT3|BIT2)) > BIT6) + +#define NULL_DETECTION_NONSTOP_MODE \ + ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & (BIT6|BIT1)) > BIT6) + +typedef struct { + EFI_PHYSICAL_ADDRESS Base; + EFI_PHYSICAL_ADDRESS Top; +} MEMORY_RANGE; + +typedef struct { + MEMORY_RANGE Range; + BOOLEAN Present; + BOOLEAN Nx; +} MEMORY_PROTECTION_RANGE; + +typedef struct { + UINT64 HeaderSize; + UINT64 MaxDataEntries; + UINT64 MaxDataSize; + UINT64 CurDataEntries; + UINT64 CurDataSize; + UINT64 TsegStart; + UINT64 TsegSize; + UINT64 NumSmis; + UINT64 NumCpus; +} SMM_PROFILE_HEADER; + +typedef struct { + UINT64 SmiNum; + UINT64 CpuNum; + UINT64 ApicId; + UINT64 ErrorCode; + UINT64 Instruction; + UINT64 Address; + UINT64 SmiCmd; +} SMM_PROFILE_ENTRY; + +extern SMM_S3_RESUME_STATE *mSmmS3ResumeState; +extern UINTN gSmiExceptionHandlers[]; +extern BOOLEAN mXdSupported; +X86_ASSEMBLY_PATCH_LABEL gPatchXdSupported; +X86_ASSEMBLY_PATCH_LABEL gPatchMsrIa32MiscEnableSupported; +extern UINTN *mPFEntryCount; +extern UINT64 (*mLastPFEntryValue)[MAX_PF_ENTRY_COUNT]; +extern UINT64 *(*mLastPFEntryPointer)[MAX_PF_ENTRY_COUNT]; + +// +// Internal functions +// + +/** + Update IDT table to replace page fault handler and INT 1 handler. + +**/ +VOID +InitIdtr ( + VOID + ); + +/** + Check if the memory address will be mapped by 4KB-page. + + @param Address The address of Memory. + +**/ +BOOLEAN +IsAddressSplit ( + IN EFI_PHYSICAL_ADDRESS Address + ); + +/** + Check if the memory address will be mapped by 4KB-page. + + @param Address The address of Memory. + @param Nx The flag indicates if the memory is execute-disable. + +**/ +BOOLEAN +IsAddressValid ( + IN EFI_PHYSICAL_ADDRESS Address, + IN BOOLEAN *Nx + ); + +/** + Page Fault handler for SMM use. + +**/ +VOID +SmiDefaultPFHandler ( + VOID + ); + +/** + Clear TF in FLAGS. + + @param SystemContext A pointer to the processor context when + the interrupt occurred on the processor. + +**/ +VOID +ClearTrapFlag ( + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ); + +#endif // _SMM_PROFILE_H_ diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmramSaveState.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmramSaveState.c new file mode 100644 index 00000000..9fc5a498 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmramSaveState.c @@ -0,0 +1,742 @@ +/** @file +Provides services to access SMRAM Save State Map + +Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiSmm.h> + +#include <Library/SmmCpuFeaturesLib.h> + +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/SmmServicesTableLib.h> +#include <Library/DebugLib.h> + +#include "PiSmmCpuDxeSmm.h" + +typedef struct { + UINT64 Signature; // Offset 0x00 + UINT16 Reserved1; // Offset 0x08 + UINT16 Reserved2; // Offset 0x0A + UINT16 Reserved3; // Offset 0x0C + UINT16 SmmCs; // Offset 0x0E + UINT16 SmmDs; // Offset 0x10 + UINT16 SmmSs; // Offset 0x12 + UINT16 SmmOtherSegment; // Offset 0x14 + UINT16 Reserved4; // Offset 0x16 + UINT64 Reserved5; // Offset 0x18 + UINT64 Reserved6; // Offset 0x20 + UINT64 Reserved7; // Offset 0x28 + UINT64 SmmGdtPtr; // Offset 0x30 + UINT32 SmmGdtSize; // Offset 0x38 + UINT32 Reserved8; // Offset 0x3C + UINT64 Reserved9; // Offset 0x40 + UINT64 Reserved10; // Offset 0x48 + UINT16 Reserved11; // Offset 0x50 + UINT16 Reserved12; // Offset 0x52 + UINT32 Reserved13; // Offset 0x54 + UINT64 Reserved14; // Offset 0x58 +} PROCESSOR_SMM_DESCRIPTOR; + +extern CONST PROCESSOR_SMM_DESCRIPTOR gcPsd; + +// +// EFER register LMA bit +// +#define LMA BIT10 + +/// +/// Macro used to simplify the lookup table entries of type CPU_SMM_SAVE_STATE_LOOKUP_ENTRY +/// +#define SMM_CPU_OFFSET(Field) OFFSET_OF (SMRAM_SAVE_STATE_MAP, Field) + +/// +/// Macro used to simplify the lookup table entries of type CPU_SMM_SAVE_STATE_REGISTER_RANGE +/// +#define SMM_REGISTER_RANGE(Start, End) { Start, End, End - Start + 1 } + +/// +/// Structure used to describe a range of registers +/// +typedef struct { + EFI_SMM_SAVE_STATE_REGISTER Start; + EFI_SMM_SAVE_STATE_REGISTER End; + UINTN Length; +} CPU_SMM_SAVE_STATE_REGISTER_RANGE; + +/// +/// Structure used to build a lookup table to retrieve the widths and offsets +/// associated with each supported EFI_SMM_SAVE_STATE_REGISTER value +/// + +#define SMM_SAVE_STATE_REGISTER_SMMREVID_INDEX 1 +#define SMM_SAVE_STATE_REGISTER_IOMISC_INDEX 2 +#define SMM_SAVE_STATE_REGISTER_IOMEMADDR_INDEX 3 +#define SMM_SAVE_STATE_REGISTER_MAX_INDEX 4 + +typedef struct { + UINT8 Width32; + UINT8 Width64; + UINT16 Offset32; + UINT16 Offset64Lo; + UINT16 Offset64Hi; + BOOLEAN Writeable; +} CPU_SMM_SAVE_STATE_LOOKUP_ENTRY; + +/// +/// Structure used to build a lookup table for the IOMisc width information +/// +typedef struct { + UINT8 Width; + EFI_SMM_SAVE_STATE_IO_WIDTH IoWidth; +} CPU_SMM_SAVE_STATE_IO_WIDTH; + +/// +/// Variables from SMI Handler +/// +X86_ASSEMBLY_PATCH_LABEL gPatchSmbase; +X86_ASSEMBLY_PATCH_LABEL gPatchSmiStack; +X86_ASSEMBLY_PATCH_LABEL gPatchSmiCr3; +extern volatile UINT8 gcSmiHandlerTemplate[]; +extern CONST UINT16 gcSmiHandlerSize; + +// +// Variables used by SMI Handler +// +IA32_DESCRIPTOR gSmiHandlerIdtr; + +/// +/// Table used by GetRegisterIndex() to convert an EFI_SMM_SAVE_STATE_REGISTER +/// value to an index into a table of type CPU_SMM_SAVE_STATE_LOOKUP_ENTRY +/// +CONST CPU_SMM_SAVE_STATE_REGISTER_RANGE mSmmCpuRegisterRanges[] = { + SMM_REGISTER_RANGE (EFI_SMM_SAVE_STATE_REGISTER_GDTBASE, EFI_SMM_SAVE_STATE_REGISTER_LDTINFO), + SMM_REGISTER_RANGE (EFI_SMM_SAVE_STATE_REGISTER_ES, EFI_SMM_SAVE_STATE_REGISTER_RIP), + SMM_REGISTER_RANGE (EFI_SMM_SAVE_STATE_REGISTER_RFLAGS, EFI_SMM_SAVE_STATE_REGISTER_CR4), + { (EFI_SMM_SAVE_STATE_REGISTER)0, (EFI_SMM_SAVE_STATE_REGISTER)0, 0 } +}; + +/// +/// Lookup table used to retrieve the widths and offsets associated with each +/// supported EFI_SMM_SAVE_STATE_REGISTER value +/// +CONST CPU_SMM_SAVE_STATE_LOOKUP_ENTRY mSmmCpuWidthOffset[] = { + {0, 0, 0, 0, 0, FALSE}, // Reserved + + // + // Internally defined CPU Save State Registers. Not defined in PI SMM CPU Protocol. + // + {4, 4, SMM_CPU_OFFSET (x86.SMMRevId) , SMM_CPU_OFFSET (x64.SMMRevId) , 0 , FALSE}, // SMM_SAVE_STATE_REGISTER_SMMREVID_INDEX = 1 + {4, 4, SMM_CPU_OFFSET (x86.IOMisc) , SMM_CPU_OFFSET (x64.IOMisc) , 0 , FALSE}, // SMM_SAVE_STATE_REGISTER_IOMISC_INDEX = 2 + {4, 8, SMM_CPU_OFFSET (x86.IOMemAddr) , SMM_CPU_OFFSET (x64.IOMemAddr) , SMM_CPU_OFFSET (x64.IOMemAddr) + 4, FALSE}, // SMM_SAVE_STATE_REGISTER_IOMEMADDR_INDEX = 3 + + // + // CPU Save State registers defined in PI SMM CPU Protocol. + // + {0, 8, 0 , SMM_CPU_OFFSET (x64.GdtBaseLoDword) , SMM_CPU_OFFSET (x64.GdtBaseHiDword), FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_GDTBASE = 4 + {0, 8, 0 , SMM_CPU_OFFSET (x64.IdtBaseLoDword) , SMM_CPU_OFFSET (x64.IdtBaseHiDword), FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_IDTBASE = 5 + {0, 8, 0 , SMM_CPU_OFFSET (x64.LdtBaseLoDword) , SMM_CPU_OFFSET (x64.LdtBaseHiDword), FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_LDTBASE = 6 + {0, 0, 0 , 0 , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_GDTLIMIT = 7 + {0, 0, 0 , 0 , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_IDTLIMIT = 8 + {0, 0, 0 , 0 , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_LDTLIMIT = 9 + {0, 0, 0 , 0 , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_LDTINFO = 10 + + {4, 4, SMM_CPU_OFFSET (x86._ES) , SMM_CPU_OFFSET (x64._ES) , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_ES = 20 + {4, 4, SMM_CPU_OFFSET (x86._CS) , SMM_CPU_OFFSET (x64._CS) , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_CS = 21 + {4, 4, SMM_CPU_OFFSET (x86._SS) , SMM_CPU_OFFSET (x64._SS) , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_SS = 22 + {4, 4, SMM_CPU_OFFSET (x86._DS) , SMM_CPU_OFFSET (x64._DS) , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_DS = 23 + {4, 4, SMM_CPU_OFFSET (x86._FS) , SMM_CPU_OFFSET (x64._FS) , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_FS = 24 + {4, 4, SMM_CPU_OFFSET (x86._GS) , SMM_CPU_OFFSET (x64._GS) , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_GS = 25 + {0, 4, 0 , SMM_CPU_OFFSET (x64._LDTR) , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_LDTR_SEL = 26 + {4, 4, SMM_CPU_OFFSET (x86._TR) , SMM_CPU_OFFSET (x64._TR) , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_TR_SEL = 27 + {4, 8, SMM_CPU_OFFSET (x86._DR7) , SMM_CPU_OFFSET (x64._DR7) , SMM_CPU_OFFSET (x64._DR7) + 4, FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_DR7 = 28 + {4, 8, SMM_CPU_OFFSET (x86._DR6) , SMM_CPU_OFFSET (x64._DR6) , SMM_CPU_OFFSET (x64._DR6) + 4, FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_DR6 = 29 + {0, 8, 0 , SMM_CPU_OFFSET (x64._R8) , SMM_CPU_OFFSET (x64._R8) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_R8 = 30 + {0, 8, 0 , SMM_CPU_OFFSET (x64._R9) , SMM_CPU_OFFSET (x64._R9) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_R9 = 31 + {0, 8, 0 , SMM_CPU_OFFSET (x64._R10) , SMM_CPU_OFFSET (x64._R10) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_R10 = 32 + {0, 8, 0 , SMM_CPU_OFFSET (x64._R11) , SMM_CPU_OFFSET (x64._R11) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_R11 = 33 + {0, 8, 0 , SMM_CPU_OFFSET (x64._R12) , SMM_CPU_OFFSET (x64._R12) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_R12 = 34 + {0, 8, 0 , SMM_CPU_OFFSET (x64._R13) , SMM_CPU_OFFSET (x64._R13) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_R13 = 35 + {0, 8, 0 , SMM_CPU_OFFSET (x64._R14) , SMM_CPU_OFFSET (x64._R14) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_R14 = 36 + {0, 8, 0 , SMM_CPU_OFFSET (x64._R15) , SMM_CPU_OFFSET (x64._R15) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_R15 = 37 + {4, 8, SMM_CPU_OFFSET (x86._EAX) , SMM_CPU_OFFSET (x64._RAX) , SMM_CPU_OFFSET (x64._RAX) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_RAX = 38 + {4, 8, SMM_CPU_OFFSET (x86._EBX) , SMM_CPU_OFFSET (x64._RBX) , SMM_CPU_OFFSET (x64._RBX) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_RBX = 39 + {4, 8, SMM_CPU_OFFSET (x86._ECX) , SMM_CPU_OFFSET (x64._RCX) , SMM_CPU_OFFSET (x64._RCX) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_RCX = 40 + {4, 8, SMM_CPU_OFFSET (x86._EDX) , SMM_CPU_OFFSET (x64._RDX) , SMM_CPU_OFFSET (x64._RDX) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_RDX = 41 + {4, 8, SMM_CPU_OFFSET (x86._ESP) , SMM_CPU_OFFSET (x64._RSP) , SMM_CPU_OFFSET (x64._RSP) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_RSP = 42 + {4, 8, SMM_CPU_OFFSET (x86._EBP) , SMM_CPU_OFFSET (x64._RBP) , SMM_CPU_OFFSET (x64._RBP) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_RBP = 43 + {4, 8, SMM_CPU_OFFSET (x86._ESI) , SMM_CPU_OFFSET (x64._RSI) , SMM_CPU_OFFSET (x64._RSI) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_RSI = 44 + {4, 8, SMM_CPU_OFFSET (x86._EDI) , SMM_CPU_OFFSET (x64._RDI) , SMM_CPU_OFFSET (x64._RDI) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_RDI = 45 + {4, 8, SMM_CPU_OFFSET (x86._EIP) , SMM_CPU_OFFSET (x64._RIP) , SMM_CPU_OFFSET (x64._RIP) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_RIP = 46 + + {4, 8, SMM_CPU_OFFSET (x86._EFLAGS) , SMM_CPU_OFFSET (x64._RFLAGS) , SMM_CPU_OFFSET (x64._RFLAGS) + 4, TRUE }, // EFI_SMM_SAVE_STATE_REGISTER_RFLAGS = 51 + {4, 8, SMM_CPU_OFFSET (x86._CR0) , SMM_CPU_OFFSET (x64._CR0) , SMM_CPU_OFFSET (x64._CR0) + 4, FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_CR0 = 52 + {4, 8, SMM_CPU_OFFSET (x86._CR3) , SMM_CPU_OFFSET (x64._CR3) , SMM_CPU_OFFSET (x64._CR3) + 4, FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_CR3 = 53 + {0, 4, 0 , SMM_CPU_OFFSET (x64._CR4) , 0 , FALSE}, // EFI_SMM_SAVE_STATE_REGISTER_CR4 = 54 +}; + +/// +/// Lookup table for the IOMisc width information +/// +CONST CPU_SMM_SAVE_STATE_IO_WIDTH mSmmCpuIoWidth[] = { + { 0, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8 }, // Undefined = 0 + { 1, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8 }, // SMM_IO_LENGTH_BYTE = 1 + { 2, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT16 }, // SMM_IO_LENGTH_WORD = 2 + { 0, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8 }, // Undefined = 3 + { 4, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT32 }, // SMM_IO_LENGTH_DWORD = 4 + { 0, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8 }, // Undefined = 5 + { 0, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8 }, // Undefined = 6 + { 0, EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8 } // Undefined = 7 +}; + +/// +/// Lookup table for the IOMisc type information +/// +CONST EFI_SMM_SAVE_STATE_IO_TYPE mSmmCpuIoType[] = { + EFI_SMM_SAVE_STATE_IO_TYPE_OUTPUT, // SMM_IO_TYPE_OUT_DX = 0 + EFI_SMM_SAVE_STATE_IO_TYPE_INPUT, // SMM_IO_TYPE_IN_DX = 1 + EFI_SMM_SAVE_STATE_IO_TYPE_STRING, // SMM_IO_TYPE_OUTS = 2 + EFI_SMM_SAVE_STATE_IO_TYPE_STRING, // SMM_IO_TYPE_INS = 3 + (EFI_SMM_SAVE_STATE_IO_TYPE)0, // Undefined = 4 + (EFI_SMM_SAVE_STATE_IO_TYPE)0, // Undefined = 5 + EFI_SMM_SAVE_STATE_IO_TYPE_REP_PREFIX, // SMM_IO_TYPE_REP_OUTS = 6 + EFI_SMM_SAVE_STATE_IO_TYPE_REP_PREFIX, // SMM_IO_TYPE_REP_INS = 7 + EFI_SMM_SAVE_STATE_IO_TYPE_OUTPUT, // SMM_IO_TYPE_OUT_IMMEDIATE = 8 + EFI_SMM_SAVE_STATE_IO_TYPE_INPUT, // SMM_IO_TYPE_OUT_IMMEDIATE = 9 + (EFI_SMM_SAVE_STATE_IO_TYPE)0, // Undefined = 10 + (EFI_SMM_SAVE_STATE_IO_TYPE)0, // Undefined = 11 + (EFI_SMM_SAVE_STATE_IO_TYPE)0, // Undefined = 12 + (EFI_SMM_SAVE_STATE_IO_TYPE)0, // Undefined = 13 + (EFI_SMM_SAVE_STATE_IO_TYPE)0, // Undefined = 14 + (EFI_SMM_SAVE_STATE_IO_TYPE)0 // Undefined = 15 +}; + +/// +/// The mode of the CPU at the time an SMI occurs +/// +UINT8 mSmmSaveStateRegisterLma; + +/** + Read information from the CPU save state. + + @param Register Specifies the CPU register to read form the save state. + + @retval 0 Register is not valid + @retval >0 Index into mSmmCpuWidthOffset[] associated with Register + +**/ +UINTN +GetRegisterIndex ( + IN EFI_SMM_SAVE_STATE_REGISTER Register + ) +{ + UINTN Index; + UINTN Offset; + + for (Index = 0, Offset = SMM_SAVE_STATE_REGISTER_MAX_INDEX; mSmmCpuRegisterRanges[Index].Length != 0; Index++) { + if (Register >= mSmmCpuRegisterRanges[Index].Start && Register <= mSmmCpuRegisterRanges[Index].End) { + return Register - mSmmCpuRegisterRanges[Index].Start + Offset; + } + Offset += mSmmCpuRegisterRanges[Index].Length; + } + return 0; +} + +/** + Read a CPU Save State register on the target processor. + + This function abstracts the differences that whether the CPU Save State register is in the + IA32 CPU Save State Map or X64 CPU Save State Map. + + This function supports reading a CPU Save State register in SMBase relocation handler. + + @param[in] CpuIndex Specifies the zero-based index of the CPU save state. + @param[in] RegisterIndex Index into mSmmCpuWidthOffset[] look up table. + @param[in] Width The number of bytes to read from the CPU save state. + @param[out] Buffer Upon return, this holds the CPU register value read from the save state. + + @retval EFI_SUCCESS The register was read from Save State. + @retval EFI_NOT_FOUND The register is not defined for the Save State of Processor. + @retval EFI_INVALID_PARAMETER This or Buffer is NULL. + +**/ +EFI_STATUS +ReadSaveStateRegisterByIndex ( + IN UINTN CpuIndex, + IN UINTN RegisterIndex, + IN UINTN Width, + OUT VOID *Buffer + ) +{ + SMRAM_SAVE_STATE_MAP *CpuSaveState; + + if (RegisterIndex == 0) { + return EFI_NOT_FOUND; + } + + CpuSaveState = gSmst->CpuSaveState[CpuIndex]; + + if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) { + // + // If 32-bit mode width is zero, then the specified register can not be accessed + // + if (mSmmCpuWidthOffset[RegisterIndex].Width32 == 0) { + return EFI_NOT_FOUND; + } + + // + // If Width is bigger than the 32-bit mode width, then the specified register can not be accessed + // + if (Width > mSmmCpuWidthOffset[RegisterIndex].Width32) { + return EFI_INVALID_PARAMETER; + } + + // + // Write return buffer + // + ASSERT(CpuSaveState != NULL); + CopyMem(Buffer, (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset32, Width); + } else { + // + // If 64-bit mode width is zero, then the specified register can not be accessed + // + if (mSmmCpuWidthOffset[RegisterIndex].Width64 == 0) { + return EFI_NOT_FOUND; + } + + // + // If Width is bigger than the 64-bit mode width, then the specified register can not be accessed + // + if (Width > mSmmCpuWidthOffset[RegisterIndex].Width64) { + return EFI_INVALID_PARAMETER; + } + + // + // Write lower 32-bits of return buffer + // + CopyMem(Buffer, (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Lo, MIN(4, Width)); + if (Width >= 4) { + // + // Write upper 32-bits of return buffer + // + CopyMem((UINT8 *)Buffer + 4, (UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Hi, Width - 4); + } + } + return EFI_SUCCESS; +} + +/** + Read a CPU Save State register on the target processor. + + This function abstracts the differences that whether the CPU Save State register is in the + IA32 CPU Save State Map or X64 CPU Save State Map. + + This function supports reading a CPU Save State register in SMBase relocation handler. + + @param[in] CpuIndex Specifies the zero-based index of the CPU save state. + @param[in] RegisterIndex Index into mSmmCpuWidthOffset[] look up table. + @param[in] Width The number of bytes to read from the CPU save state. + @param[out] Buffer Upon return, this holds the CPU register value read from the save state. + + @retval EFI_SUCCESS The register was read from Save State. + @retval EFI_NOT_FOUND The register is not defined for the Save State of Processor. + @retval EFI_INVALID_PARAMETER Buffer is NULL, or Width does not meet requirement per Register type. + +**/ +EFI_STATUS +EFIAPI +ReadSaveStateRegister ( + IN UINTN CpuIndex, + IN EFI_SMM_SAVE_STATE_REGISTER Register, + IN UINTN Width, + OUT VOID *Buffer + ) +{ + UINT32 SmmRevId; + SMRAM_SAVE_STATE_IOMISC IoMisc; + EFI_SMM_SAVE_STATE_IO_INFO *IoInfo; + + // + // Check for special EFI_SMM_SAVE_STATE_REGISTER_LMA + // + if (Register == EFI_SMM_SAVE_STATE_REGISTER_LMA) { + // + // Only byte access is supported for this register + // + if (Width != 1) { + return EFI_INVALID_PARAMETER; + } + + *(UINT8 *)Buffer = mSmmSaveStateRegisterLma; + + return EFI_SUCCESS; + } + + // + // Check for special EFI_SMM_SAVE_STATE_REGISTER_IO + // + if (Register == EFI_SMM_SAVE_STATE_REGISTER_IO) { + // + // Get SMM Revision ID + // + ReadSaveStateRegisterByIndex (CpuIndex, SMM_SAVE_STATE_REGISTER_SMMREVID_INDEX, sizeof(SmmRevId), &SmmRevId); + + // + // See if the CPU supports the IOMisc register in the save state + // + if (SmmRevId < SMRAM_SAVE_STATE_MIN_REV_ID_IOMISC) { + return EFI_NOT_FOUND; + } + + // + // Get the IOMisc register value + // + ReadSaveStateRegisterByIndex (CpuIndex, SMM_SAVE_STATE_REGISTER_IOMISC_INDEX, sizeof(IoMisc.Uint32), &IoMisc.Uint32); + + // + // Check for the SMI_FLAG in IOMisc + // + if (IoMisc.Bits.SmiFlag == 0) { + return EFI_NOT_FOUND; + } + + // + // Only support IN/OUT, but not INS/OUTS/REP INS/REP OUTS. + // + if ((mSmmCpuIoType[IoMisc.Bits.Type] != EFI_SMM_SAVE_STATE_IO_TYPE_INPUT) && + (mSmmCpuIoType[IoMisc.Bits.Type] != EFI_SMM_SAVE_STATE_IO_TYPE_OUTPUT)) { + return EFI_NOT_FOUND; + } + + // + // Compute index for the I/O Length and I/O Type lookup tables + // + if (mSmmCpuIoWidth[IoMisc.Bits.Length].Width == 0 || mSmmCpuIoType[IoMisc.Bits.Type] == 0) { + return EFI_NOT_FOUND; + } + + // + // Make sure the incoming buffer is large enough to hold IoInfo before accessing + // + if (Width < sizeof (EFI_SMM_SAVE_STATE_IO_INFO)) { + return EFI_INVALID_PARAMETER; + } + + // + // Zero the IoInfo structure that will be returned in Buffer + // + IoInfo = (EFI_SMM_SAVE_STATE_IO_INFO *)Buffer; + ZeroMem (IoInfo, sizeof(EFI_SMM_SAVE_STATE_IO_INFO)); + + // + // Use lookup tables to help fill in all the fields of the IoInfo structure + // + IoInfo->IoPort = (UINT16)IoMisc.Bits.Port; + IoInfo->IoWidth = mSmmCpuIoWidth[IoMisc.Bits.Length].IoWidth; + IoInfo->IoType = mSmmCpuIoType[IoMisc.Bits.Type]; + ReadSaveStateRegister (CpuIndex, EFI_SMM_SAVE_STATE_REGISTER_RAX, mSmmCpuIoWidth[IoMisc.Bits.Length].Width, &IoInfo->IoData); + return EFI_SUCCESS; + } + + // + // Convert Register to a register lookup table index + // + return ReadSaveStateRegisterByIndex (CpuIndex, GetRegisterIndex (Register), Width, Buffer); +} + +/** + Write value to a CPU Save State register on the target processor. + + This function abstracts the differences that whether the CPU Save State register is in the + IA32 CPU Save State Map or X64 CPU Save State Map. + + This function supports writing a CPU Save State register in SMBase relocation handler. + + @param[in] CpuIndex Specifies the zero-based index of the CPU save state. + @param[in] RegisterIndex Index into mSmmCpuWidthOffset[] look up table. + @param[in] Width The number of bytes to read from the CPU save state. + @param[in] Buffer Upon entry, this holds the new CPU register value. + + @retval EFI_SUCCESS The register was written to Save State. + @retval EFI_NOT_FOUND The register is not defined for the Save State of Processor. + @retval EFI_INVALID_PARAMETER ProcessorIndex or Width is not correct. + +**/ +EFI_STATUS +EFIAPI +WriteSaveStateRegister ( + IN UINTN CpuIndex, + IN EFI_SMM_SAVE_STATE_REGISTER Register, + IN UINTN Width, + IN CONST VOID *Buffer + ) +{ + UINTN RegisterIndex; + SMRAM_SAVE_STATE_MAP *CpuSaveState; + + // + // Writes to EFI_SMM_SAVE_STATE_REGISTER_LMA are ignored + // + if (Register == EFI_SMM_SAVE_STATE_REGISTER_LMA) { + return EFI_SUCCESS; + } + + // + // Writes to EFI_SMM_SAVE_STATE_REGISTER_IO are not supported + // + if (Register == EFI_SMM_SAVE_STATE_REGISTER_IO) { + return EFI_NOT_FOUND; + } + + // + // Convert Register to a register lookup table index + // + RegisterIndex = GetRegisterIndex (Register); + if (RegisterIndex == 0) { + return EFI_NOT_FOUND; + } + + CpuSaveState = gSmst->CpuSaveState[CpuIndex]; + + // + // Do not write non-writable SaveState, because it will cause exception. + // + if (!mSmmCpuWidthOffset[RegisterIndex].Writeable) { + return EFI_UNSUPPORTED; + } + + // + // Check CPU mode + // + if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) { + // + // If 32-bit mode width is zero, then the specified register can not be accessed + // + if (mSmmCpuWidthOffset[RegisterIndex].Width32 == 0) { + return EFI_NOT_FOUND; + } + + // + // If Width is bigger than the 32-bit mode width, then the specified register can not be accessed + // + if (Width > mSmmCpuWidthOffset[RegisterIndex].Width32) { + return EFI_INVALID_PARAMETER; + } + // + // Write SMM State register + // + ASSERT (CpuSaveState != NULL); + CopyMem((UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset32, Buffer, Width); + } else { + // + // If 64-bit mode width is zero, then the specified register can not be accessed + // + if (mSmmCpuWidthOffset[RegisterIndex].Width64 == 0) { + return EFI_NOT_FOUND; + } + + // + // If Width is bigger than the 64-bit mode width, then the specified register can not be accessed + // + if (Width > mSmmCpuWidthOffset[RegisterIndex].Width64) { + return EFI_INVALID_PARAMETER; + } + + // + // Write lower 32-bits of SMM State register + // + CopyMem((UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Lo, Buffer, MIN (4, Width)); + if (Width >= 4) { + // + // Write upper 32-bits of SMM State register + // + CopyMem((UINT8 *)CpuSaveState + mSmmCpuWidthOffset[RegisterIndex].Offset64Hi, (UINT8 *)Buffer + 4, Width - 4); + } + } + return EFI_SUCCESS; +} + +/** + Hook the code executed immediately after an RSM instruction on the currently + executing CPU. The mode of code executed immediately after RSM must be + detected, and the appropriate hook must be selected. Always clear the auto + HALT restart flag if it is set. + + @param[in] CpuIndex The processor index for the currently + executing CPU. + @param[in] CpuState Pointer to SMRAM Save State Map for the + currently executing CPU. + @param[in] NewInstructionPointer32 Instruction pointer to use if resuming to + 32-bit mode from 64-bit SMM. + @param[in] NewInstructionPointer Instruction pointer to use if resuming to + same mode as SMM. + + @retval The value of the original instruction pointer before it was hooked. + +**/ +UINT64 +EFIAPI +HookReturnFromSmm ( + IN UINTN CpuIndex, + SMRAM_SAVE_STATE_MAP *CpuState, + UINT64 NewInstructionPointer32, + UINT64 NewInstructionPointer + ) +{ + UINT64 OriginalInstructionPointer; + + OriginalInstructionPointer = SmmCpuFeaturesHookReturnFromSmm ( + CpuIndex, + CpuState, + NewInstructionPointer32, + NewInstructionPointer + ); + if (OriginalInstructionPointer != 0) { + return OriginalInstructionPointer; + } + + if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) { + OriginalInstructionPointer = (UINT64)CpuState->x86._EIP; + CpuState->x86._EIP = (UINT32)NewInstructionPointer; + // + // Clear the auto HALT restart flag so the RSM instruction returns + // program control to the instruction following the HLT instruction. + // + if ((CpuState->x86.AutoHALTRestart & BIT0) != 0) { + CpuState->x86.AutoHALTRestart &= ~BIT0; + } + } else { + OriginalInstructionPointer = CpuState->x64._RIP; + if ((CpuState->x64.IA32_EFER & LMA) == 0) { + CpuState->x64._RIP = (UINT32)NewInstructionPointer32; + } else { + CpuState->x64._RIP = (UINT32)NewInstructionPointer; + } + // + // Clear the auto HALT restart flag so the RSM instruction returns + // program control to the instruction following the HLT instruction. + // + if ((CpuState->x64.AutoHALTRestart & BIT0) != 0) { + CpuState->x64.AutoHALTRestart &= ~BIT0; + } + } + return OriginalInstructionPointer; +} + +/** + Get the size of the SMI Handler in bytes. + + @retval The size, in bytes, of the SMI Handler. + +**/ +UINTN +EFIAPI +GetSmiHandlerSize ( + VOID + ) +{ + UINTN Size; + + Size = SmmCpuFeaturesGetSmiHandlerSize (); + if (Size != 0) { + return Size; + } + return gcSmiHandlerSize; +} + +/** + Install the SMI handler for the CPU specified by CpuIndex. This function + is called by the CPU that was elected as monarch during System Management + Mode initialization. + + @param[in] CpuIndex The index of the CPU to install the custom SMI handler. + The value must be between 0 and the NumberOfCpus field + in the System Management System Table (SMST). + @param[in] SmBase The SMBASE address for the CPU specified by CpuIndex. + @param[in] SmiStack The stack to use when an SMI is processed by the + the CPU specified by CpuIndex. + @param[in] StackSize The size, in bytes, if the stack used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] GdtBase The base address of the GDT to use when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] GdtSize The size, in bytes, of the GDT used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] IdtBase The base address of the IDT to use when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] IdtSize The size, in bytes, of the IDT used when an SMI is + processed by the CPU specified by CpuIndex. + @param[in] Cr3 The base address of the page tables to use when an SMI + is processed by the CPU specified by CpuIndex. +**/ +VOID +EFIAPI +InstallSmiHandler ( + IN UINTN CpuIndex, + IN UINT32 SmBase, + IN VOID *SmiStack, + IN UINTN StackSize, + IN UINTN GdtBase, + IN UINTN GdtSize, + IN UINTN IdtBase, + IN UINTN IdtSize, + IN UINT32 Cr3 + ) +{ + PROCESSOR_SMM_DESCRIPTOR *Psd; + UINT32 CpuSmiStack; + + // + // Initialize PROCESSOR_SMM_DESCRIPTOR + // + Psd = (PROCESSOR_SMM_DESCRIPTOR *)(VOID *)((UINTN)SmBase + SMM_PSD_OFFSET); + CopyMem (Psd, &gcPsd, sizeof (gcPsd)); + Psd->SmmGdtPtr = (UINT64)GdtBase; + Psd->SmmGdtSize = (UINT32)GdtSize; + + if (SmmCpuFeaturesGetSmiHandlerSize () != 0) { + // + // Install SMI handler provided by library + // + SmmCpuFeaturesInstallSmiHandler ( + CpuIndex, + SmBase, + SmiStack, + StackSize, + GdtBase, + GdtSize, + IdtBase, + IdtSize, + Cr3 + ); + return; + } + + InitShadowStack (CpuIndex, (VOID *)((UINTN)SmiStack + StackSize)); + + // + // Initialize values in template before copy + // + CpuSmiStack = (UINT32)((UINTN)SmiStack + StackSize - sizeof (UINTN)); + PatchInstructionX86 (gPatchSmiStack, CpuSmiStack, 4); + PatchInstructionX86 (gPatchSmiCr3, Cr3, 4); + PatchInstructionX86 (gPatchSmbase, SmBase, 4); + gSmiHandlerIdtr.Base = IdtBase; + gSmiHandlerIdtr.Limit = (UINT16)(IdtSize - 1); + + // + // Set the value at the top of the CPU stack to the CPU Index + // + *(UINTN*)(UINTN)CpuSmiStack = CpuIndex; + + // + // Copy template to CPU specific SMI handler location + // + CopyMem ( + (VOID*)((UINTN)SmBase + SMM_HANDLER_OFFSET), + (VOID*)gcSmiHandlerTemplate, + gcSmiHandlerSize + ); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SyncTimer.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SyncTimer.c new file mode 100644 index 00000000..1d79a50c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SyncTimer.c @@ -0,0 +1,110 @@ +/** @file +SMM Timer feature support + +Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" + +UINT64 mTimeoutTicker = 0; +// +// Number of counts in a roll-over cycle of the performance counter. +// +UINT64 mCycle = 0; +// +// Flag to indicate the performance counter is count-up or count-down. +// +BOOLEAN mCountDown; + +/** + Initialize Timer for SMM AP Sync. + +**/ +VOID +InitializeSmmTimer ( + VOID + ) +{ + UINT64 TimerFrequency; + UINT64 Start; + UINT64 End; + + TimerFrequency = GetPerformanceCounterProperties (&Start, &End); + mTimeoutTicker = DivU64x32 ( + MultU64x64(TimerFrequency, PcdGet64 (PcdCpuSmmApSyncTimeout)), + 1000 * 1000 + ); + if (End < Start) { + mCountDown = TRUE; + mCycle = Start - End; + } else { + mCountDown = FALSE; + mCycle = End - Start; + } +} + +/** + Start Timer for SMM AP Sync. + +**/ +UINT64 +EFIAPI +StartSyncTimer ( + VOID + ) +{ + return GetPerformanceCounter (); +} + + +/** + Check if the SMM AP Sync timer is timeout. + + @param Timer The start timer from the begin. + +**/ +BOOLEAN +EFIAPI +IsSyncTimerTimeout ( + IN UINT64 Timer + ) +{ + UINT64 CurrentTimer; + UINT64 Delta; + + CurrentTimer = GetPerformanceCounter (); + // + // We need to consider the case that CurrentTimer is equal to Timer + // when some timer runs too slow and CPU runs fast. We think roll over + // condition does not happen on this case. + // + if (mCountDown) { + // + // The performance counter counts down. Check for roll over condition. + // + if (CurrentTimer <= Timer) { + Delta = Timer - CurrentTimer; + } else { + // + // Handle one roll-over. + // + Delta = mCycle - (CurrentTimer - Timer) + 1; + } + } else { + // + // The performance counter counts up. Check for roll over condition. + // + if (CurrentTimer >= Timer) { + Delta = CurrentTimer - Timer; + } else { + // + // Handle one roll-over. + // + Delta = mCycle - (Timer - CurrentTimer) + 1; + } + } + + return (BOOLEAN) (Delta >= mTimeoutTicker); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/Cet.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/Cet.nasm new file mode 100644 index 00000000..99172364 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/Cet.nasm @@ -0,0 +1,34 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------- + +%include "Nasm.inc" + +DEFAULT REL +SECTION .text + +global ASM_PFX(DisableCet) +ASM_PFX(DisableCet): + + ; Skip the pushed data for call + mov rax, 1 + INCSSP_RAX + + mov rax, cr4 + btr eax, 23 ; clear CET + mov cr4, rax + ret + +global ASM_PFX(EnableCet) +ASM_PFX(EnableCet): + + mov rax, cr4 + bts eax, 23 ; set CET + mov cr4, rax + + ; use jmp to skip the check for ret + pop rax + jmp rax + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/MpFuncs.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/MpFuncs.nasm new file mode 100644 index 00000000..3eff0772 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/MpFuncs.nasm @@ -0,0 +1,189 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; MpFuncs.nasm +; +; Abstract: +; +; This is the assembly code for Multi-processor S3 support +; +;------------------------------------------------------------------------------- + +%define VacantFlag 0x0 +%define NotVacantFlag 0xff + +%define LockLocation RendezvousFunnelProcEnd - RendezvousFunnelProcStart +%define StackStartAddressLocation LockLocation + 0x8 +%define StackSizeLocation LockLocation + 0x10 +%define CProcedureLocation LockLocation + 0x18 +%define GdtrLocation LockLocation + 0x20 +%define IdtrLocation LockLocation + 0x2A +%define BufferStartLocation LockLocation + 0x34 +%define Cr3OffsetLocation LockLocation + 0x38 +%define InitializeFloatingPointUnitsAddress LockLocation + 0x3C + +;------------------------------------------------------------------------------------- +;RendezvousFunnelProc procedure follows. All APs execute their procedure. This +;procedure serializes all the AP processors through an Init sequence. It must be +;noted that APs arrive here very raw...ie: real mode, no stack. +;ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC +;IS IN MACHINE CODE. +;------------------------------------------------------------------------------------- +;RendezvousFunnelProc (&WakeUpBuffer,MemAddress); + +;text SEGMENT +DEFAULT REL +SECTION .text + +BITS 16 +global ASM_PFX(RendezvousFunnelProc) +ASM_PFX(RendezvousFunnelProc): +RendezvousFunnelProcStart: + +; At this point CS = 0x(vv00) and ip= 0x0. + + mov ax, cs + mov ds, ax + mov es, ax + mov ss, ax + xor ax, ax + mov fs, ax + mov gs, ax + +flat32Start: + + mov si, BufferStartLocation + mov edx,dword [si] ; EDX is keeping the start address of wakeup buffer + + mov si, Cr3OffsetLocation + mov ecx,dword [si] ; ECX is keeping the value of CR3 + + mov si, GdtrLocation +o32 lgdt [cs:si] + + mov si, IdtrLocation +o32 lidt [cs:si] + + xor ax, ax + mov ds, ax + + mov eax, cr0 ; Get control register 0 + or eax, 0x000000001 ; Set PE bit (bit #0) + mov cr0, eax + +FLAT32_JUMP: + +a32 jmp dword 0x20:0x0 + +BITS 32 +PMODE_ENTRY: ; protected mode entry point + + mov ax, 0x18 +o16 mov ds, ax +o16 mov es, ax +o16 mov fs, ax +o16 mov gs, ax +o16 mov ss, ax ; Flat mode setup. + + mov eax, cr4 + bts eax, 5 + mov cr4, eax + + mov cr3, ecx + + mov esi, edx ; Save wakeup buffer address + + mov ecx, 0xc0000080 ; EFER MSR number. + rdmsr ; Read EFER. + bts eax, 8 ; Set LME=1. + wrmsr ; Write EFER. + + mov eax, cr0 ; Read CR0. + bts eax, 31 ; Set PG=1. + mov cr0, eax ; Write CR0. + +LONG_JUMP: + +a16 jmp dword 0x38:0x0 + +BITS 64 +LongModeStart: + + mov ax, 0x30 +o16 mov ds, ax +o16 mov es, ax +o16 mov ss, ax + + mov edi, esi + add edi, LockLocation + mov al, NotVacantFlag +TestLock: + xchg byte [edi], al + cmp al, NotVacantFlag + jz TestLock + +ProgramStack: + + mov edi, esi + add edi, StackSizeLocation + mov rax, qword [edi] + mov edi, esi + add edi, StackStartAddressLocation + add rax, qword [edi] + mov rsp, rax + mov qword [edi], rax + +Releaselock: + + mov al, VacantFlag + mov edi, esi + add edi, LockLocation + xchg byte [edi], al + + ; + ; Call assembly function to initialize FPU. + ; + mov rax, qword [esi + InitializeFloatingPointUnitsAddress] + sub rsp, 0x20 + call rax + add rsp, 0x20 + + ; + ; Call C Function + ; + mov edi, esi + add edi, CProcedureLocation + mov rax, qword [edi] + + test rax, rax + jz GoToSleep + + sub rsp, 0x20 + call rax + add rsp, 0x20 + +GoToSleep: + cli + hlt + jmp $-2 + +RendezvousFunnelProcEnd: + +;------------------------------------------------------------------------------------- +; AsmGetAddressMap (&AddressMap); +;------------------------------------------------------------------------------------- +; comments here for definition of address map +global ASM_PFX(AsmGetAddressMap) +ASM_PFX(AsmGetAddressMap): + lea rax, [RendezvousFunnelProcStart] + mov qword [rcx], rax + mov qword [rcx+0x8], PMODE_ENTRY - RendezvousFunnelProcStart + mov qword [rcx+0x10], FLAT32_JUMP - RendezvousFunnelProcStart + mov qword [rcx+0x18], RendezvousFunnelProcEnd - RendezvousFunnelProcStart + mov qword [rcx+0x20], LongModeStart - RendezvousFunnelProcStart + mov qword [rcx+0x28], LONG_JUMP - RendezvousFunnelProcStart + ret + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c new file mode 100644 index 00000000..a9a995db --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c @@ -0,0 +1,1325 @@ +/** @file +Page Fault (#PF) handler for X64 processors + +Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" + +#define PAGE_TABLE_PAGES 8 +#define ACC_MAX_BIT BIT3 + +extern UINTN mSmmShadowStackSize; + +LIST_ENTRY mPagePool = INITIALIZE_LIST_HEAD_VARIABLE (mPagePool); +BOOLEAN m1GPageTableSupport = FALSE; +BOOLEAN mCpuSmmRestrictedMemoryAccess; +BOOLEAN m5LevelPagingNeeded; +X86_ASSEMBLY_PATCH_LABEL gPatch5LevelPagingNeeded; + +/** + Disable CET. +**/ +VOID +EFIAPI +DisableCet ( + VOID + ); + +/** + Enable CET. +**/ +VOID +EFIAPI +EnableCet ( + VOID + ); + +/** + Check if 1-GByte pages is supported by processor or not. + + @retval TRUE 1-GByte pages is supported. + @retval FALSE 1-GByte pages is not supported. + +**/ +BOOLEAN +Is1GPageSupport ( + VOID + ) +{ + UINT32 RegEax; + UINT32 RegEdx; + + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000001) { + AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT26) != 0) { + return TRUE; + } + } + return FALSE; +} + +/** + The routine returns TRUE when CPU supports it (CPUID[7,0].ECX.BIT[16] is set) and + the max physical address bits is bigger than 48. Because 4-level paging can support + to address physical address up to 2^48 - 1, there is no need to enable 5-level paging + with max physical address bits <= 48. + + @retval TRUE 5-level paging enabling is needed. + @retval FALSE 5-level paging enabling is not needed. +**/ +BOOLEAN +Is5LevelPagingNeeded ( + VOID + ) +{ + CPUID_VIR_PHY_ADDRESS_SIZE_EAX VirPhyAddressSize; + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_ECX ExtFeatureEcx; + UINT32 MaxExtendedFunctionId; + + AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedFunctionId, NULL, NULL, NULL); + if (MaxExtendedFunctionId >= CPUID_VIR_PHY_ADDRESS_SIZE) { + AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &VirPhyAddressSize.Uint32, NULL, NULL, NULL); + } else { + VirPhyAddressSize.Bits.PhysicalAddressBits = 36; + } + AsmCpuidEx ( + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, + CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_SUB_LEAF_INFO, + NULL, NULL, &ExtFeatureEcx.Uint32, NULL + ); + DEBUG (( + DEBUG_INFO, "PhysicalAddressBits = %d, 5LPageTable = %d.\n", + VirPhyAddressSize.Bits.PhysicalAddressBits, ExtFeatureEcx.Bits.FiveLevelPage + )); + + if (VirPhyAddressSize.Bits.PhysicalAddressBits > 4 * 9 + 12) { + ASSERT (ExtFeatureEcx.Bits.FiveLevelPage == 1); + return TRUE; + } else { + return FALSE; + } +} + +/** + Get page table base address and the depth of the page table. + + @param[out] Base Page table base address. + @param[out] FiveLevels TRUE means 5 level paging. FALSE means 4 level paging. +**/ +VOID +GetPageTable ( + OUT UINTN *Base, + OUT BOOLEAN *FiveLevels OPTIONAL + ) +{ + IA32_CR4 Cr4; + + if (mInternalCr3 == 0) { + *Base = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64; + if (FiveLevels != NULL) { + Cr4.UintN = AsmReadCr4 (); + *FiveLevels = (BOOLEAN)(Cr4.Bits.LA57 == 1); + } + return; + } + + *Base = mInternalCr3; + if (FiveLevels != NULL) { + *FiveLevels = m5LevelPagingNeeded; + } +} + +/** + Set sub-entries number in entry. + + @param[in, out] Entry Pointer to entry + @param[in] SubEntryNum Sub-entries number based on 0: + 0 means there is 1 sub-entry under this entry + 0x1ff means there is 512 sub-entries under this entry + +**/ +VOID +SetSubEntriesNum ( + IN OUT UINT64 *Entry, + IN UINT64 SubEntryNum + ) +{ + // + // Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry + // + *Entry = BitFieldWrite64 (*Entry, 52, 60, SubEntryNum); +} + +/** + Return sub-entries number in entry. + + @param[in] Entry Pointer to entry + + @return Sub-entries number based on 0: + 0 means there is 1 sub-entry under this entry + 0x1ff means there is 512 sub-entries under this entry +**/ +UINT64 +GetSubEntriesNum ( + IN UINT64 *Entry + ) +{ + // + // Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry + // + return BitFieldRead64 (*Entry, 52, 60); +} + +/** + Calculate the maximum support address. + + @return the maximum support address. +**/ +UINT8 +CalculateMaximumSupportAddress ( + VOID + ) +{ + UINT32 RegEax; + UINT8 PhysicalAddressBits; + VOID *Hob; + + // + // Get physical address bits supported. + // + Hob = GetFirstHob (EFI_HOB_TYPE_CPU); + if (Hob != NULL) { + PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; + } else { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000008) { + AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); + PhysicalAddressBits = (UINT8) RegEax; + } else { + PhysicalAddressBits = 36; + } + } + return PhysicalAddressBits; +} + +/** + Set static page table. + + @param[in] PageTable Address of page table. + @param[in] PhysicalAddressBits The maximum physical address bits supported. +**/ +VOID +SetStaticPageTable ( + IN UINTN PageTable, + IN UINT8 PhysicalAddressBits + ) +{ + UINT64 PageAddress; + UINTN NumberOfPml5EntriesNeeded; + UINTN NumberOfPml4EntriesNeeded; + UINTN NumberOfPdpEntriesNeeded; + UINTN IndexOfPml5Entries; + UINTN IndexOfPml4Entries; + UINTN IndexOfPdpEntries; + UINTN IndexOfPageDirectoryEntries; + UINT64 *PageMapLevel5Entry; + UINT64 *PageMapLevel4Entry; + UINT64 *PageMap; + UINT64 *PageDirectoryPointerEntry; + UINT64 *PageDirectory1GEntry; + UINT64 *PageDirectoryEntry; + + // + // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses + // when 5-Level Paging is disabled. + // + ASSERT (PhysicalAddressBits <= 52); + if (!m5LevelPagingNeeded && PhysicalAddressBits > 48) { + PhysicalAddressBits = 48; + } + + NumberOfPml5EntriesNeeded = 1; + if (PhysicalAddressBits > 48) { + NumberOfPml5EntriesNeeded = (UINTN) LShiftU64 (1, PhysicalAddressBits - 48); + PhysicalAddressBits = 48; + } + + NumberOfPml4EntriesNeeded = 1; + if (PhysicalAddressBits > 39) { + NumberOfPml4EntriesNeeded = (UINTN) LShiftU64 (1, PhysicalAddressBits - 39); + PhysicalAddressBits = 39; + } + + NumberOfPdpEntriesNeeded = 1; + ASSERT (PhysicalAddressBits > 30); + NumberOfPdpEntriesNeeded = (UINTN) LShiftU64 (1, PhysicalAddressBits - 30); + + // + // By architecture only one PageMapLevel4 exists - so lets allocate storage for it. + // + PageMap = (VOID *) PageTable; + + PageMapLevel4Entry = PageMap; + PageMapLevel5Entry = NULL; + if (m5LevelPagingNeeded) { + // + // By architecture only one PageMapLevel5 exists - so lets allocate storage for it. + // + PageMapLevel5Entry = PageMap; + } + PageAddress = 0; + + for ( IndexOfPml5Entries = 0 + ; IndexOfPml5Entries < NumberOfPml5EntriesNeeded + ; IndexOfPml5Entries++, PageMapLevel5Entry++) { + // + // Each PML5 entry points to a page of PML4 entires. + // So lets allocate space for them and fill them in in the IndexOfPml4Entries loop. + // When 5-Level Paging is disabled, below allocation happens only once. + // + if (m5LevelPagingNeeded) { + PageMapLevel4Entry = (UINT64 *) ((*PageMapLevel5Entry) & ~mAddressEncMask & gPhyMask); + if (PageMapLevel4Entry == NULL) { + PageMapLevel4Entry = AllocatePageTableMemory (1); + ASSERT(PageMapLevel4Entry != NULL); + ZeroMem (PageMapLevel4Entry, EFI_PAGES_TO_SIZE(1)); + + *PageMapLevel5Entry = (UINT64)(UINTN)PageMapLevel4Entry | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + } + } + + for (IndexOfPml4Entries = 0; IndexOfPml4Entries < (NumberOfPml5EntriesNeeded == 1 ? NumberOfPml4EntriesNeeded : 512); IndexOfPml4Entries++, PageMapLevel4Entry++) { + // + // Each PML4 entry points to a page of Page Directory Pointer entries. + // + PageDirectoryPointerEntry = (UINT64 *) ((*PageMapLevel4Entry) & ~mAddressEncMask & gPhyMask); + if (PageDirectoryPointerEntry == NULL) { + PageDirectoryPointerEntry = AllocatePageTableMemory (1); + ASSERT(PageDirectoryPointerEntry != NULL); + ZeroMem (PageDirectoryPointerEntry, EFI_PAGES_TO_SIZE(1)); + + *PageMapLevel4Entry = (UINT64)(UINTN)PageDirectoryPointerEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + } + + if (m1GPageTableSupport) { + PageDirectory1GEntry = PageDirectoryPointerEntry; + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) { + if (IndexOfPml4Entries == 0 && IndexOfPageDirectoryEntries < 4) { + // + // Skip the < 4G entries + // + continue; + } + // + // Fill in the Page Directory entries + // + *PageDirectory1GEntry = PageAddress | mAddressEncMask | IA32_PG_PS | PAGE_ATTRIBUTE_BITS; + } + } else { + PageAddress = BASE_4GB; + for (IndexOfPdpEntries = 0; IndexOfPdpEntries < (NumberOfPml4EntriesNeeded == 1 ? NumberOfPdpEntriesNeeded : 512); IndexOfPdpEntries++, PageDirectoryPointerEntry++) { + if (IndexOfPml4Entries == 0 && IndexOfPdpEntries < 4) { + // + // Skip the < 4G entries + // + continue; + } + // + // Each Directory Pointer entries points to a page of Page Directory entires. + // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop. + // + PageDirectoryEntry = (UINT64 *) ((*PageDirectoryPointerEntry) & ~mAddressEncMask & gPhyMask); + if (PageDirectoryEntry == NULL) { + PageDirectoryEntry = AllocatePageTableMemory (1); + ASSERT(PageDirectoryEntry != NULL); + ZeroMem (PageDirectoryEntry, EFI_PAGES_TO_SIZE(1)); + + // + // Fill in a Page Directory Pointer Entries + // + *PageDirectoryPointerEntry = (UINT64)(UINTN)PageDirectoryEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + } + + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) { + // + // Fill in the Page Directory entries + // + *PageDirectoryEntry = PageAddress | mAddressEncMask | IA32_PG_PS | PAGE_ATTRIBUTE_BITS; + } + } + } + } + } +} + +/** + Create PageTable for SMM use. + + @return The address of PML4 (to set CR3). + +**/ +UINT32 +SmmInitPageTable ( + VOID + ) +{ + EFI_PHYSICAL_ADDRESS Pages; + UINT64 *PTEntry; + LIST_ENTRY *FreePage; + UINTN Index; + UINTN PageFaultHandlerHookAddress; + IA32_IDT_GATE_DESCRIPTOR *IdtEntry; + EFI_STATUS Status; + UINT64 *Pml4Entry; + UINT64 *Pml5Entry; + + // + // Initialize spin lock + // + InitializeSpinLock (mPFLock); + + mCpuSmmRestrictedMemoryAccess = PcdGetBool (PcdCpuSmmRestrictedMemoryAccess); + m1GPageTableSupport = Is1GPageSupport (); + m5LevelPagingNeeded = Is5LevelPagingNeeded (); + mPhysicalAddressBits = CalculateMaximumSupportAddress (); + PatchInstructionX86 (gPatch5LevelPagingNeeded, m5LevelPagingNeeded, 1); + DEBUG ((DEBUG_INFO, "5LevelPaging Needed - %d\n", m5LevelPagingNeeded)); + DEBUG ((DEBUG_INFO, "1GPageTable Support - %d\n", m1GPageTableSupport)); + DEBUG ((DEBUG_INFO, "PcdCpuSmmRestrictedMemoryAccess - %d\n", mCpuSmmRestrictedMemoryAccess)); + DEBUG ((DEBUG_INFO, "PhysicalAddressBits - %d\n", mPhysicalAddressBits)); + // + // Generate PAE page table for the first 4GB memory space + // + Pages = Gen4GPageTable (FALSE); + + // + // Set IA32_PG_PMNT bit to mask this entry + // + PTEntry = (UINT64*)(UINTN)Pages; + for (Index = 0; Index < 4; Index++) { + PTEntry[Index] |= IA32_PG_PMNT; + } + + // + // Fill Page-Table-Level4 (PML4) entry + // + Pml4Entry = (UINT64*)AllocatePageTableMemory (1); + ASSERT (Pml4Entry != NULL); + *Pml4Entry = Pages | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + ZeroMem (Pml4Entry + 1, EFI_PAGE_SIZE - sizeof (*Pml4Entry)); + + // + // Set sub-entries number + // + SetSubEntriesNum (Pml4Entry, 3); + PTEntry = Pml4Entry; + + if (m5LevelPagingNeeded) { + // + // Fill PML5 entry + // + Pml5Entry = (UINT64*)AllocatePageTableMemory (1); + ASSERT (Pml5Entry != NULL); + *Pml5Entry = (UINTN) Pml4Entry | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + ZeroMem (Pml5Entry + 1, EFI_PAGE_SIZE - sizeof (*Pml5Entry)); + // + // Set sub-entries number + // + SetSubEntriesNum (Pml5Entry, 1); + PTEntry = Pml5Entry; + } + + if (mCpuSmmRestrictedMemoryAccess) { + // + // When access to non-SMRAM memory is restricted, create page table + // that covers all memory space. + // + SetStaticPageTable ((UINTN)PTEntry, mPhysicalAddressBits); + } else { + // + // Add pages to page pool + // + FreePage = (LIST_ENTRY*)AllocatePageTableMemory (PAGE_TABLE_PAGES); + ASSERT (FreePage != NULL); + for (Index = 0; Index < PAGE_TABLE_PAGES; Index++) { + InsertTailList (&mPagePool, FreePage); + FreePage += EFI_PAGE_SIZE / sizeof (*FreePage); + } + } + + if (FeaturePcdGet (PcdCpuSmmProfileEnable) || + HEAP_GUARD_NONSTOP_MODE || + NULL_DETECTION_NONSTOP_MODE) { + // + // Set own Page Fault entry instead of the default one, because SMM Profile + // feature depends on IRET instruction to do Single Step + // + PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile; + IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base; + IdtEntry += EXCEPT_IA32_PAGE_FAULT; + IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress; + IdtEntry->Bits.Reserved_0 = 0; + IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32; + IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16); + IdtEntry->Bits.OffsetUpper = (UINT32)(PageFaultHandlerHookAddress >> 32); + IdtEntry->Bits.Reserved_1 = 0; + } else { + // + // Register Smm Page Fault Handler + // + Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler); + ASSERT_EFI_ERROR (Status); + } + + // + // Additional SMM IDT initialization for SMM stack guard + // + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + InitializeIDTSmmStackGuard (); + } + + // + // Return the address of PML4/PML5 (to set CR3) + // + return (UINT32)(UINTN)PTEntry; +} + +/** + Set access record in entry. + + @param[in, out] Entry Pointer to entry + @param[in] Acc Access record value + +**/ +VOID +SetAccNum ( + IN OUT UINT64 *Entry, + IN UINT64 Acc + ) +{ + // + // Access record is saved in BIT9 to BIT11 (reserved field) in Entry + // + *Entry = BitFieldWrite64 (*Entry, 9, 11, Acc); +} + +/** + Return access record in entry. + + @param[in] Entry Pointer to entry + + @return Access record value. + +**/ +UINT64 +GetAccNum ( + IN UINT64 *Entry + ) +{ + // + // Access record is saved in BIT9 to BIT11 (reserved field) in Entry + // + return BitFieldRead64 (*Entry, 9, 11); +} + +/** + Return and update the access record in entry. + + @param[in, out] Entry Pointer to entry + + @return Access record value. + +**/ +UINT64 +GetAndUpdateAccNum ( + IN OUT UINT64 *Entry + ) +{ + UINT64 Acc; + + Acc = GetAccNum (Entry); + if ((*Entry & IA32_PG_A) != 0) { + // + // If this entry has been accessed, clear access flag in Entry and update access record + // to the initial value 7, adding ACC_MAX_BIT is to make it larger than others + // + *Entry &= ~(UINT64)(UINTN)IA32_PG_A; + SetAccNum (Entry, 0x7); + return (0x7 + ACC_MAX_BIT); + } else { + if (Acc != 0) { + // + // If the access record is not the smallest value 0, minus 1 and update the access record field + // + SetAccNum (Entry, Acc - 1); + } + } + return Acc; +} + +/** + Reclaim free pages for PageFault handler. + + Search the whole entries tree to find the leaf entry that has the smallest + access record value. Insert the page pointed by this leaf entry into the + page pool. And check its upper entries if need to be inserted into the page + pool or not. + +**/ +VOID +ReclaimPages ( + VOID + ) +{ + UINT64 Pml5Entry; + UINT64 *Pml5; + UINT64 *Pml4; + UINT64 *Pdpt; + UINT64 *Pdt; + UINTN Pml5Index; + UINTN Pml4Index; + UINTN PdptIndex; + UINTN PdtIndex; + UINTN MinPml5; + UINTN MinPml4; + UINTN MinPdpt; + UINTN MinPdt; + UINT64 MinAcc; + UINT64 Acc; + UINT64 SubEntriesNum; + BOOLEAN PML4EIgnore; + BOOLEAN PDPTEIgnore; + UINT64 *ReleasePageAddress; + IA32_CR4 Cr4; + BOOLEAN Enable5LevelPaging; + UINT64 PFAddress; + UINT64 PFAddressPml5Index; + UINT64 PFAddressPml4Index; + UINT64 PFAddressPdptIndex; + UINT64 PFAddressPdtIndex; + + Pml4 = NULL; + Pdpt = NULL; + Pdt = NULL; + MinAcc = (UINT64)-1; + MinPml4 = (UINTN)-1; + MinPml5 = (UINTN)-1; + MinPdpt = (UINTN)-1; + MinPdt = (UINTN)-1; + Acc = 0; + ReleasePageAddress = 0; + PFAddress = AsmReadCr2 (); + PFAddressPml5Index = BitFieldRead64 (PFAddress, 48, 48 + 8); + PFAddressPml4Index = BitFieldRead64 (PFAddress, 39, 39 + 8); + PFAddressPdptIndex = BitFieldRead64 (PFAddress, 30, 30 + 8); + PFAddressPdtIndex = BitFieldRead64 (PFAddress, 21, 21 + 8); + + Cr4.UintN = AsmReadCr4 (); + Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1); + Pml5 = (UINT64*)(UINTN)(AsmReadCr3 () & gPhyMask); + + if (!Enable5LevelPaging) { + // + // Create one fake PML5 entry for 4-Level Paging + // so that the page table parsing logic only handles 5-Level page structure. + // + Pml5Entry = (UINTN) Pml5 | IA32_PG_P; + Pml5 = &Pml5Entry; + } + + // + // First, find the leaf entry has the smallest access record value + // + for (Pml5Index = 0; Pml5Index < (Enable5LevelPaging ? (EFI_PAGE_SIZE / sizeof (*Pml4)) : 1); Pml5Index++) { + if ((Pml5[Pml5Index] & IA32_PG_P) == 0 || (Pml5[Pml5Index] & IA32_PG_PMNT) != 0) { + // + // If the PML5 entry is not present or is masked, skip it + // + continue; + } + Pml4 = (UINT64*)(UINTN)(Pml5[Pml5Index] & gPhyMask); + for (Pml4Index = 0; Pml4Index < EFI_PAGE_SIZE / sizeof (*Pml4); Pml4Index++) { + if ((Pml4[Pml4Index] & IA32_PG_P) == 0 || (Pml4[Pml4Index] & IA32_PG_PMNT) != 0) { + // + // If the PML4 entry is not present or is masked, skip it + // + continue; + } + Pdpt = (UINT64*)(UINTN)(Pml4[Pml4Index] & ~mAddressEncMask & gPhyMask); + PML4EIgnore = FALSE; + for (PdptIndex = 0; PdptIndex < EFI_PAGE_SIZE / sizeof (*Pdpt); PdptIndex++) { + if ((Pdpt[PdptIndex] & IA32_PG_P) == 0 || (Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) { + // + // If the PDPT entry is not present or is masked, skip it + // + if ((Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) { + // + // If the PDPT entry is masked, we will ignore checking the PML4 entry + // + PML4EIgnore = TRUE; + } + continue; + } + if ((Pdpt[PdptIndex] & IA32_PG_PS) == 0) { + // + // It's not 1-GByte pages entry, it should be a PDPT entry, + // we will not check PML4 entry more + // + PML4EIgnore = TRUE; + Pdt = (UINT64*)(UINTN)(Pdpt[PdptIndex] & ~mAddressEncMask & gPhyMask); + PDPTEIgnore = FALSE; + for (PdtIndex = 0; PdtIndex < EFI_PAGE_SIZE / sizeof(*Pdt); PdtIndex++) { + if ((Pdt[PdtIndex] & IA32_PG_P) == 0 || (Pdt[PdtIndex] & IA32_PG_PMNT) != 0) { + // + // If the PD entry is not present or is masked, skip it + // + if ((Pdt[PdtIndex] & IA32_PG_PMNT) != 0) { + // + // If the PD entry is masked, we will not PDPT entry more + // + PDPTEIgnore = TRUE; + } + continue; + } + if ((Pdt[PdtIndex] & IA32_PG_PS) == 0) { + // + // It's not 2 MByte page table entry, it should be PD entry + // we will find the entry has the smallest access record value + // + PDPTEIgnore = TRUE; + if (PdtIndex != PFAddressPdtIndex || PdptIndex != PFAddressPdptIndex || + Pml4Index != PFAddressPml4Index || Pml5Index != PFAddressPml5Index) { + Acc = GetAndUpdateAccNum (Pdt + PdtIndex); + if (Acc < MinAcc) { + // + // If the PD entry has the smallest access record value, + // save the Page address to be released + // + MinAcc = Acc; + MinPml5 = Pml5Index; + MinPml4 = Pml4Index; + MinPdpt = PdptIndex; + MinPdt = PdtIndex; + ReleasePageAddress = Pdt + PdtIndex; + } + } + } + } + if (!PDPTEIgnore) { + // + // If this PDPT entry has no PDT entries pointer to 4 KByte pages, + // it should only has the entries point to 2 MByte Pages + // + if (PdptIndex != PFAddressPdptIndex || Pml4Index != PFAddressPml4Index || + Pml5Index != PFAddressPml5Index) { + Acc = GetAndUpdateAccNum (Pdpt + PdptIndex); + if (Acc < MinAcc) { + // + // If the PDPT entry has the smallest access record value, + // save the Page address to be released + // + MinAcc = Acc; + MinPml5 = Pml5Index; + MinPml4 = Pml4Index; + MinPdpt = PdptIndex; + MinPdt = (UINTN)-1; + ReleasePageAddress = Pdpt + PdptIndex; + } + } + } + } + } + if (!PML4EIgnore) { + // + // If PML4 entry has no the PDPT entry pointer to 2 MByte pages, + // it should only has the entries point to 1 GByte Pages + // + if (Pml4Index != PFAddressPml4Index || Pml5Index != PFAddressPml5Index) { + Acc = GetAndUpdateAccNum (Pml4 + Pml4Index); + if (Acc < MinAcc) { + // + // If the PML4 entry has the smallest access record value, + // save the Page address to be released + // + MinAcc = Acc; + MinPml5 = Pml5Index; + MinPml4 = Pml4Index; + MinPdpt = (UINTN)-1; + MinPdt = (UINTN)-1; + ReleasePageAddress = Pml4 + Pml4Index; + } + } + } + } + } + // + // Make sure one PML4/PDPT/PD entry is selected + // + ASSERT (MinAcc != (UINT64)-1); + + // + // Secondly, insert the page pointed by this entry into page pool and clear this entry + // + InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(*ReleasePageAddress & ~mAddressEncMask & gPhyMask)); + *ReleasePageAddress = 0; + + // + // Lastly, check this entry's upper entries if need to be inserted into page pool + // or not + // + while (TRUE) { + if (MinPdt != (UINTN)-1) { + // + // If 4 KByte Page Table is released, check the PDPT entry + // + Pml4 = (UINT64 *) (UINTN) (Pml5[MinPml5] & gPhyMask); + Pdpt = (UINT64*)(UINTN)(Pml4[MinPml4] & ~mAddressEncMask & gPhyMask); + SubEntriesNum = GetSubEntriesNum(Pdpt + MinPdpt); + if (SubEntriesNum == 0 && + (MinPdpt != PFAddressPdptIndex || MinPml4 != PFAddressPml4Index || MinPml5 != PFAddressPml5Index)) { + // + // Release the empty Page Directory table if there was no more 4 KByte Page Table entry + // clear the Page directory entry + // + InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pdpt[MinPdpt] & ~mAddressEncMask & gPhyMask)); + Pdpt[MinPdpt] = 0; + // + // Go on checking the PML4 table + // + MinPdt = (UINTN)-1; + continue; + } + // + // Update the sub-entries filed in PDPT entry and exit + // + SetSubEntriesNum (Pdpt + MinPdpt, (SubEntriesNum - 1) & 0x1FF); + break; + } + if (MinPdpt != (UINTN)-1) { + // + // One 2MB Page Table is released or Page Directory table is released, check the PML4 entry + // + SubEntriesNum = GetSubEntriesNum (Pml4 + MinPml4); + if (SubEntriesNum == 0 && (MinPml4 != PFAddressPml4Index || MinPml5 != PFAddressPml5Index)) { + // + // Release the empty PML4 table if there was no more 1G KByte Page Table entry + // clear the Page directory entry + // + InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pml4[MinPml4] & ~mAddressEncMask & gPhyMask)); + Pml4[MinPml4] = 0; + MinPdpt = (UINTN)-1; + continue; + } + // + // Update the sub-entries filed in PML4 entry and exit + // + SetSubEntriesNum (Pml4 + MinPml4, (SubEntriesNum - 1) & 0x1FF); + break; + } + // + // PLM4 table has been released before, exit it + // + break; + } +} + +/** + Allocate free Page for PageFault handler use. + + @return Page address. + +**/ +UINT64 +AllocPage ( + VOID + ) +{ + UINT64 RetVal; + + if (IsListEmpty (&mPagePool)) { + // + // If page pool is empty, reclaim the used pages and insert one into page pool + // + ReclaimPages (); + } + + // + // Get one free page and remove it from page pool + // + RetVal = (UINT64)(UINTN)mPagePool.ForwardLink; + RemoveEntryList (mPagePool.ForwardLink); + // + // Clean this page and return + // + ZeroMem ((VOID*)(UINTN)RetVal, EFI_PAGE_SIZE); + return RetVal; +} + +/** + Page Fault handler for SMM use. + +**/ +VOID +SmiDefaultPFHandler ( + VOID + ) +{ + UINT64 *PageTable; + UINT64 *PageTableTop; + UINT64 PFAddress; + UINTN StartBit; + UINTN EndBit; + UINT64 PTIndex; + UINTN Index; + SMM_PAGE_SIZE_TYPE PageSize; + UINTN NumOfPages; + UINTN PageAttribute; + EFI_STATUS Status; + UINT64 *UpperEntry; + BOOLEAN Enable5LevelPaging; + IA32_CR4 Cr4; + + // + // Set default SMM page attribute + // + PageSize = SmmPageSize2M; + NumOfPages = 1; + PageAttribute = 0; + + EndBit = 0; + PageTableTop = (UINT64*)(AsmReadCr3 () & gPhyMask); + PFAddress = AsmReadCr2 (); + + Cr4.UintN = AsmReadCr4 (); + Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 != 0); + + Status = GetPlatformPageTableAttribute (PFAddress, &PageSize, &NumOfPages, &PageAttribute); + // + // If platform not support page table attribute, set default SMM page attribute + // + if (Status != EFI_SUCCESS) { + PageSize = SmmPageSize2M; + NumOfPages = 1; + PageAttribute = 0; + } + if (PageSize >= MaxSmmPageSizeType) { + PageSize = SmmPageSize2M; + } + if (NumOfPages > 512) { + NumOfPages = 512; + } + + switch (PageSize) { + case SmmPageSize4K: + // + // BIT12 to BIT20 is Page Table index + // + EndBit = 12; + break; + case SmmPageSize2M: + // + // BIT21 to BIT29 is Page Directory index + // + EndBit = 21; + PageAttribute |= (UINTN)IA32_PG_PS; + break; + case SmmPageSize1G: + if (!m1GPageTableSupport) { + DEBUG ((DEBUG_ERROR, "1-GByte pages is not supported!")); + ASSERT (FALSE); + } + // + // BIT30 to BIT38 is Page Directory Pointer Table index + // + EndBit = 30; + PageAttribute |= (UINTN)IA32_PG_PS; + break; + default: + ASSERT (FALSE); + } + + // + // If execute-disable is enabled, set NX bit + // + if (mXdEnabled) { + PageAttribute |= IA32_PG_NX; + } + + for (Index = 0; Index < NumOfPages; Index++) { + PageTable = PageTableTop; + UpperEntry = NULL; + for (StartBit = Enable5LevelPaging ? 48 : 39; StartBit > EndBit; StartBit -= 9) { + PTIndex = BitFieldRead64 (PFAddress, StartBit, StartBit + 8); + if ((PageTable[PTIndex] & IA32_PG_P) == 0) { + // + // If the entry is not present, allocate one page from page pool for it + // + PageTable[PTIndex] = AllocPage () | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + } else { + // + // Save the upper entry address + // + UpperEntry = PageTable + PTIndex; + } + // + // BIT9 to BIT11 of entry is used to save access record, + // initialize value is 7 + // + PageTable[PTIndex] |= (UINT64)IA32_PG_A; + SetAccNum (PageTable + PTIndex, 7); + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & gPhyMask); + } + + PTIndex = BitFieldRead64 (PFAddress, StartBit, StartBit + 8); + if ((PageTable[PTIndex] & IA32_PG_P) != 0) { + // + // Check if the entry has already existed, this issue may occur when the different + // size page entries created under the same entry + // + DEBUG ((DEBUG_ERROR, "PageTable = %lx, PTIndex = %x, PageTable[PTIndex] = %lx\n", PageTable, PTIndex, PageTable[PTIndex])); + DEBUG ((DEBUG_ERROR, "New page table overlapped with old page table!\n")); + ASSERT (FALSE); + } + // + // Fill the new entry + // + PageTable[PTIndex] = ((PFAddress | mAddressEncMask) & gPhyMask & ~((1ull << EndBit) - 1)) | + PageAttribute | IA32_PG_A | PAGE_ATTRIBUTE_BITS; + if (UpperEntry != NULL) { + SetSubEntriesNum (UpperEntry, (GetSubEntriesNum (UpperEntry) + 1) & 0x1FF); + } + // + // Get the next page address if we need to create more page tables + // + PFAddress += (1ull << EndBit); + } +} + +/** + ThePage Fault handler wrapper for SMM use. + + @param InterruptType Defines the type of interrupt or exception that + occurred on the processor.This parameter is processor architecture specific. + @param SystemContext A pointer to the processor context when + the interrupt occurred on the processor. +**/ +VOID +EFIAPI +SmiPFHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN PFAddress; + UINTN GuardPageAddress; + UINTN ShadowStackGuardPageAddress; + UINTN CpuIndex; + + ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT); + + AcquireSpinLock (mPFLock); + + PFAddress = AsmReadCr2 (); + + if (mCpuSmmRestrictedMemoryAccess && (PFAddress >= LShiftU64 (1, (mPhysicalAddressBits - 1)))) { + DumpCpuContext (InterruptType, SystemContext); + DEBUG ((DEBUG_ERROR, "Do not support address 0x%lx by processor!\n", PFAddress)); + CpuDeadLoop (); + goto Exit; + } + + // + // If a page fault occurs in SMRAM range, it might be in a SMM stack/shadow stack guard page, + // or SMM page protection violation. + // + if ((PFAddress >= mCpuHotPlugData.SmrrBase) && + (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) { + DumpCpuContext (InterruptType, SystemContext); + CpuIndex = GetCpuIndex (); + GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * (mSmmStackSize + mSmmShadowStackSize)); + ShadowStackGuardPageAddress = (mSmmStackArrayBase + mSmmStackSize + EFI_PAGE_SIZE + CpuIndex * (mSmmStackSize + mSmmShadowStackSize)); + if ((FeaturePcdGet (PcdCpuSmmStackGuard)) && + (PFAddress >= GuardPageAddress) && + (PFAddress < (GuardPageAddress + EFI_PAGE_SIZE))) { + DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n")); + } else if ((FeaturePcdGet (PcdCpuSmmStackGuard)) && + (mSmmShadowStackSize > 0) && + (PFAddress >= ShadowStackGuardPageAddress) && + (PFAddress < (ShadowStackGuardPageAddress + EFI_PAGE_SIZE))) { + DEBUG ((DEBUG_ERROR, "SMM shadow stack overflow!\n")); + } else { + if ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0) { + DEBUG ((DEBUG_ERROR, "SMM exception at execution (0x%lx)\n", PFAddress)); + DEBUG_CODE ( + DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp); + ); + } else { + DEBUG ((DEBUG_ERROR, "SMM exception at access (0x%lx)\n", PFAddress)); + DEBUG_CODE ( + DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip); + ); + } + + if (HEAP_GUARD_NONSTOP_MODE) { + GuardPagePFHandler (SystemContext.SystemContextX64->ExceptionData); + goto Exit; + } + } + CpuDeadLoop (); + goto Exit; + } + + // + // If a page fault occurs in non-SMRAM range. + // + if ((PFAddress < mCpuHotPlugData.SmrrBase) || + (PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) { + if ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0) { + DumpCpuContext (InterruptType, SystemContext); + DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%lx) out of SMM range after SMM is locked!\n", PFAddress)); + DEBUG_CODE ( + DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp); + ); + CpuDeadLoop (); + goto Exit; + } + + // + // If NULL pointer was just accessed + // + if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0 && + (PFAddress < EFI_PAGE_SIZE)) { + DumpCpuContext (InterruptType, SystemContext); + DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n")); + DEBUG_CODE ( + DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip); + ); + + if (NULL_DETECTION_NONSTOP_MODE) { + GuardPagePFHandler (SystemContext.SystemContextX64->ExceptionData); + goto Exit; + } + + CpuDeadLoop (); + goto Exit; + } + + if (mCpuSmmRestrictedMemoryAccess && IsSmmCommBufferForbiddenAddress (PFAddress)) { + DumpCpuContext (InterruptType, SystemContext); + DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%lx)!\n", PFAddress)); + DEBUG_CODE ( + DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip); + ); + CpuDeadLoop (); + goto Exit; + } + } + + if (FeaturePcdGet (PcdCpuSmmProfileEnable)) { + SmmProfilePFHandler ( + SystemContext.SystemContextX64->Rip, + SystemContext.SystemContextX64->ExceptionData + ); + } else { + SmiDefaultPFHandler (); + } + +Exit: + ReleaseSpinLock (mPFLock); +} + +/** + This function sets memory attribute for page table. +**/ +VOID +SetPageTableAttributes ( + VOID + ) +{ + UINTN Index2; + UINTN Index3; + UINTN Index4; + UINTN Index5; + UINT64 *L1PageTable; + UINT64 *L2PageTable; + UINT64 *L3PageTable; + UINT64 *L4PageTable; + UINT64 *L5PageTable; + UINTN PageTableBase; + BOOLEAN IsSplitted; + BOOLEAN PageTableSplitted; + BOOLEAN CetEnabled; + BOOLEAN Enable5LevelPaging; + + // + // Don't mark page table memory as read-only if + // - no restriction on access to non-SMRAM memory; or + // - SMM heap guard feature enabled; or + // BIT2: SMM page guard enabled + // BIT3: SMM pool guard enabled + // - SMM profile feature enabled + // + if (!mCpuSmmRestrictedMemoryAccess || + ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) || + FeaturePcdGet (PcdCpuSmmProfileEnable)) { + // + // Restriction on access to non-SMRAM memory and heap guard could not be enabled at the same time. + // + ASSERT (!(mCpuSmmRestrictedMemoryAccess && + (PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0)); + + // + // Restriction on access to non-SMRAM memory and SMM profile could not be enabled at the same time. + // + ASSERT (!(mCpuSmmRestrictedMemoryAccess && FeaturePcdGet (PcdCpuSmmProfileEnable))); + return ; + } + + DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n")); + + // + // Disable write protection, because we need mark page table to be write protected. + // We need *write* page table memory, to mark itself to be *read only*. + // + CetEnabled = ((AsmReadCr4() & CR4_CET_ENABLE) != 0) ? TRUE : FALSE; + if (CetEnabled) { + // + // CET must be disabled if WP is disabled. + // + DisableCet(); + } + AsmWriteCr0 (AsmReadCr0() & ~CR0_WP); + + do { + DEBUG ((DEBUG_INFO, "Start...\n")); + PageTableSplitted = FALSE; + L5PageTable = NULL; + + GetPageTable (&PageTableBase, &Enable5LevelPaging); + + if (Enable5LevelPaging) { + L5PageTable = (UINT64 *)PageTableBase; + SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)PageTableBase, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); + PageTableSplitted = (PageTableSplitted || IsSplitted); + } + + for (Index5 = 0; Index5 < (Enable5LevelPaging ? SIZE_4KB/sizeof(UINT64) : 1); Index5++) { + if (Enable5LevelPaging) { + L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); + if (L4PageTable == NULL) { + continue; + } + } else { + L4PageTable = (UINT64 *)PageTableBase; + } + SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L4PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); + PageTableSplitted = (PageTableSplitted || IsSplitted); + + for (Index4 = 0; Index4 < SIZE_4KB/sizeof(UINT64); Index4++) { + L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); + if (L3PageTable == NULL) { + continue; + } + + SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L3PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); + PageTableSplitted = (PageTableSplitted || IsSplitted); + + for (Index3 = 0; Index3 < SIZE_4KB/sizeof(UINT64); Index3++) { + if ((L3PageTable[Index3] & IA32_PG_PS) != 0) { + // 1G + continue; + } + L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); + if (L2PageTable == NULL) { + continue; + } + + SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); + PageTableSplitted = (PageTableSplitted || IsSplitted); + + for (Index2 = 0; Index2 < SIZE_4KB/sizeof(UINT64); Index2++) { + if ((L2PageTable[Index2] & IA32_PG_PS) != 0) { + // 2M + continue; + } + L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); + if (L1PageTable == NULL) { + continue; + } + SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted); + PageTableSplitted = (PageTableSplitted || IsSplitted); + } + } + } + } + } while (PageTableSplitted); + + // + // Enable write protection, after page table updated. + // + AsmWriteCr0 (AsmReadCr0() | CR0_WP); + if (CetEnabled) { + // + // re-enable CET. + // + EnableCet(); + } + + return ; +} + +/** + This function reads CR2 register when on-demand paging is enabled. + + @param[out] *Cr2 Pointer to variable to hold CR2 register value. +**/ +VOID +SaveCr2 ( + OUT UINTN *Cr2 + ) +{ + if (!mCpuSmmRestrictedMemoryAccess) { + // + // On-demand paging is enabled when access to non-SMRAM is not restricted. + // + *Cr2 = AsmReadCr2 (); + } +} + +/** + This function restores CR2 register when on-demand paging is enabled. + + @param[in] Cr2 Value to write into CR2 register. +**/ +VOID +RestoreCr2 ( + IN UINTN Cr2 + ) +{ + if (!mCpuSmmRestrictedMemoryAccess) { + // + // On-demand paging is enabled when access to non-SMRAM is not restricted. + // + AsmWriteCr2 (Cr2); + } +} + +/** + Return whether access to non-SMRAM is restricted. + + @retval TRUE Access to non-SMRAM is restricted. + @retval FALSE Access to non-SMRAM is not restricted. +**/ +BOOLEAN +IsRestrictedMemoryAccess ( + VOID + ) +{ + return mCpuSmmRestrictedMemoryAccess; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/Semaphore.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/Semaphore.c new file mode 100644 index 00000000..867e5b31 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/Semaphore.c @@ -0,0 +1,69 @@ +/** @file +Semaphore mechanism to indicate to the BSP that an AP has exited SMM +after SMBASE relocation. + +Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" + +X86_ASSEMBLY_PATCH_LABEL gPatchSmmRelocationOriginalAddressPtr32; +X86_ASSEMBLY_PATCH_LABEL gPatchRebasedFlagAddr32; + +UINTN mSmmRelocationOriginalAddress; +volatile BOOLEAN *mRebasedFlag; + +/** +AP Semaphore operation in 32-bit mode while BSP runs in 64-bit mode. +**/ +VOID +SmmRelocationSemaphoreComplete32 ( + VOID + ); + +/** + Hook return address of SMM Save State so that semaphore code + can be executed immediately after AP exits SMM to indicate to + the BSP that an AP has exited SMM after SMBASE relocation. + + @param[in] CpuIndex The processor index. + @param[in] RebasedFlag A pointer to a flag that is set to TRUE + immediately after AP exits SMM. + +**/ +VOID +SemaphoreHook ( + IN UINTN CpuIndex, + IN volatile BOOLEAN *RebasedFlag + ) +{ + SMRAM_SAVE_STATE_MAP *CpuState; + UINTN TempValue; + + mRebasedFlag = RebasedFlag; + PatchInstructionX86 ( + gPatchRebasedFlagAddr32, + (UINT32)(UINTN)mRebasedFlag, + 4 + ); + + CpuState = (SMRAM_SAVE_STATE_MAP *)(UINTN)(SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET); + mSmmRelocationOriginalAddress = HookReturnFromSmm ( + CpuIndex, + CpuState, + (UINT64)(UINTN)&SmmRelocationSemaphoreComplete32, + (UINT64)(UINTN)&SmmRelocationSemaphoreComplete + ); + + // + // Use temp value to fix ICC compiler warning + // + TempValue = (UINTN)&mSmmRelocationOriginalAddress; + PatchInstructionX86 ( + gPatchSmmRelocationOriginalAddressPtr32, + (UINT32)TempValue, + 4 + ); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmiEntry.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmiEntry.nasm new file mode 100644 index 00000000..334cc6c8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmiEntry.nasm @@ -0,0 +1,396 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR> +; Copyright (c) 2020, AMD Incorporated. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; SmiEntry.nasm +; +; Abstract: +; +; Code template of the SMI handler for a particular processor +; +;------------------------------------------------------------------------------- + +%include "StuffRsbNasm.inc" +%include "Nasm.inc" + +; +; Variables referenced by C code +; + +%define MSR_IA32_S_CET 0x6A2 +%define MSR_IA32_CET_SH_STK_EN 0x1 +%define MSR_IA32_CET_WR_SHSTK_EN 0x2 +%define MSR_IA32_CET_ENDBR_EN 0x4 +%define MSR_IA32_CET_LEG_IW_EN 0x8 +%define MSR_IA32_CET_NO_TRACK_EN 0x10 +%define MSR_IA32_CET_SUPPRESS_DIS 0x20 +%define MSR_IA32_CET_SUPPRESS 0x400 +%define MSR_IA32_CET_TRACKER 0x800 +%define MSR_IA32_PL0_SSP 0x6A4 +%define MSR_IA32_INTERRUPT_SSP_TABLE_ADDR 0x6A8 + +%define CR4_CET 0x800000 + +%define MSR_IA32_MISC_ENABLE 0x1A0 +%define MSR_EFER 0xc0000080 +%define MSR_EFER_XD 0x800 + +; +; Constants relating to PROCESSOR_SMM_DESCRIPTOR +; +%define DSC_OFFSET 0xfb00 +%define DSC_GDTPTR 0x30 +%define DSC_GDTSIZ 0x38 +%define DSC_CS 14 +%define DSC_DS 16 +%define DSC_SS 18 +%define DSC_OTHERSEG 20 +; +; Constants relating to CPU State Save Area +; +%define SSM_DR6 0xffd0 +%define SSM_DR7 0xffc8 + +%define PROTECT_MODE_CS 0x8 +%define PROTECT_MODE_DS 0x20 +%define LONG_MODE_CS 0x38 +%define TSS_SEGMENT 0x40 +%define GDT_SIZE 0x50 + +extern ASM_PFX(SmiRendezvous) +extern ASM_PFX(gSmiHandlerIdtr) +extern ASM_PFX(CpuSmmDebugEntry) +extern ASM_PFX(CpuSmmDebugExit) + +global ASM_PFX(gPatchSmbase) +extern ASM_PFX(mXdSupported) +global ASM_PFX(gPatchXdSupported) +global ASM_PFX(gPatchMsrIa32MiscEnableSupported) +global ASM_PFX(gPatchSmiStack) +global ASM_PFX(gPatchSmiCr3) +global ASM_PFX(gPatch5LevelPagingNeeded) +global ASM_PFX(gcSmiHandlerTemplate) +global ASM_PFX(gcSmiHandlerSize) + +extern ASM_PFX(mCetSupported) +global ASM_PFX(mPatchCetSupported) +global ASM_PFX(mPatchCetPl0Ssp) +global ASM_PFX(mPatchCetInterruptSsp) +global ASM_PFX(mPatchCetInterruptSspTable) + + DEFAULT REL + SECTION .text + +BITS 16 +ASM_PFX(gcSmiHandlerTemplate): +_SmiEntryPoint: + mov bx, _GdtDesc - _SmiEntryPoint + 0x8000 + mov ax,[cs:DSC_OFFSET + DSC_GDTSIZ] + dec ax + mov [cs:bx], ax + mov eax, [cs:DSC_OFFSET + DSC_GDTPTR] + mov [cs:bx + 2], eax +o32 lgdt [cs:bx] ; lgdt fword ptr cs:[bx] + mov ax, PROTECT_MODE_CS + mov [cs:bx-0x2],ax + mov edi, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmbase): + lea eax, [edi + (@ProtectedMode - _SmiEntryPoint) + 0x8000] + mov [cs:bx-0x6],eax + mov ebx, cr0 + and ebx, 0x9ffafff3 + or ebx, 0x23 + mov cr0, ebx + jmp dword 0x0:0x0 +_GdtDesc: + DW 0 + DD 0 + +BITS 32 +@ProtectedMode: + mov ax, PROTECT_MODE_DS +o16 mov ds, ax +o16 mov es, ax +o16 mov fs, ax +o16 mov gs, ax +o16 mov ss, ax + mov esp, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmiStack): + jmp ProtFlatMode + +BITS 64 +ProtFlatMode: + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmiCr3): + mov cr3, rax + mov eax, 0x668 ; as cr4.PGE is not set here, refresh cr3 + + mov cl, strict byte 0 ; source operand will be patched +ASM_PFX(gPatch5LevelPagingNeeded): + cmp cl, 0 + je SkipEnable5LevelPaging + ; + ; Enable 5-Level Paging bit + ; + bts eax, 12 ; Set LA57 bit (bit #12) +SkipEnable5LevelPaging: + + mov cr4, rax ; in PreModifyMtrrs() to flush TLB. +; Load TSS + sub esp, 8 ; reserve room in stack + sgdt [rsp] + mov eax, [rsp + 2] ; eax = GDT base + add esp, 8 + mov dl, 0x89 + mov [rax + TSS_SEGMENT + 5], dl ; clear busy flag + mov eax, TSS_SEGMENT + ltr ax + +; enable NXE if supported + mov al, strict byte 1 ; source operand may be patched +ASM_PFX(gPatchXdSupported): + cmp al, 0 + jz @SkipXd + +; If MSR_IA32_MISC_ENABLE is supported, clear XD Disable bit + mov al, strict byte 1 ; source operand may be patched +ASM_PFX(gPatchMsrIa32MiscEnableSupported): + cmp al, 1 + jz MsrIa32MiscEnableSupported + +; MSR_IA32_MISC_ENABLE not supported + sub esp, 4 + xor rdx, rdx + push rdx ; don't try to restore the XD Disable bit just before RSM + jmp EnableNxe + +; +; Check XD disable bit +; +MsrIa32MiscEnableSupported: + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + sub esp, 4 + push rdx ; save MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 ; MSR_IA32_MISC_ENABLE[34] + jz EnableNxe + and dx, 0xFFFB ; clear XD Disable bit if it is set + wrmsr +EnableNxe: + mov ecx, MSR_EFER + rdmsr + or ax, MSR_EFER_XD ; enable NXE + wrmsr + jmp @XdDone +@SkipXd: + sub esp, 8 +@XdDone: + +; Switch into @LongMode + push LONG_MODE_CS ; push cs hardcore here + call Base ; push return address for retf later +Base: + add dword [rsp], @LongMode - Base; offset for far retf, seg is the 1st arg + + mov ecx, MSR_EFER + rdmsr + or ah, 1 ; enable LME + wrmsr + mov rbx, cr0 + or ebx, 0x80010023 ; enable paging + WP + NE + MP + PE + mov cr0, rbx + retf +@LongMode: ; long mode (64-bit code) starts here + mov rax, strict qword 0 ; mov rax, ASM_PFX(gSmiHandlerIdtr) +SmiHandlerIdtrAbsAddr: + lidt [rax] + lea ebx, [rdi + DSC_OFFSET] + mov ax, [rbx + DSC_DS] + mov ds, eax + mov ax, [rbx + DSC_OTHERSEG] + mov es, eax + mov fs, eax + mov gs, eax + mov ax, [rbx + DSC_SS] + mov ss, eax + + mov rbx, [rsp + 0x8] ; rbx <- CpuIndex + +; enable CET if supported + mov al, strict byte 1 ; source operand may be patched +ASM_PFX(mPatchCetSupported): + cmp al, 0 + jz CetDone + + mov ecx, MSR_IA32_S_CET + rdmsr + push rdx + push rax + + mov ecx, MSR_IA32_PL0_SSP + rdmsr + push rdx + push rax + + mov ecx, MSR_IA32_INTERRUPT_SSP_TABLE_ADDR + rdmsr + push rdx + push rax + + mov ecx, MSR_IA32_S_CET + mov eax, MSR_IA32_CET_SH_STK_EN + xor edx, edx + wrmsr + + mov ecx, MSR_IA32_PL0_SSP + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(mPatchCetPl0Ssp): + xor edx, edx + wrmsr + mov rcx, cr0 + btr ecx, 16 ; clear WP + mov cr0, rcx + mov [eax], eax ; reload SSP, and clear busyflag. + xor ecx, ecx + mov [eax + 4], ecx + + mov ecx, MSR_IA32_INTERRUPT_SSP_TABLE_ADDR + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(mPatchCetInterruptSspTable): + xor edx, edx + wrmsr + + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(mPatchCetInterruptSsp): + cmp eax, 0 + jz CetInterruptDone + mov [eax], eax ; reload SSP, and clear busyflag. + xor ecx, ecx + mov [eax + 4], ecx +CetInterruptDone: + + mov rcx, cr0 + bts ecx, 16 ; set WP + mov cr0, rcx + + mov eax, 0x668 | CR4_CET + mov cr4, rax + + SETSSBSY + +CetDone: + + ; + ; Save FP registers + ; + sub rsp, 0x200 + fxsave64 [rsp] + + add rsp, -0x20 + + mov rcx, rbx + mov rax, strict qword 0 ; call ASM_PFX(CpuSmmDebugEntry) +CpuSmmDebugEntryAbsAddr: + call rax + + mov rcx, rbx + mov rax, strict qword 0 ; call ASM_PFX(SmiRendezvous) +SmiRendezvousAbsAddr: + call rax + + mov rcx, rbx + mov rax, strict qword 0 ; call ASM_PFX(CpuSmmDebugExit) +CpuSmmDebugExitAbsAddr: + call rax + + add rsp, 0x20 + + ; + ; Restore FP registers + ; + fxrstor64 [rsp] + + add rsp, 0x200 + + mov rax, strict qword 0 ; mov rax, ASM_PFX(mCetSupported) +mCetSupportedAbsAddr: + mov al, [rax] + cmp al, 0 + jz CetDone2 + + mov eax, 0x668 + mov cr4, rax ; disable CET + + mov ecx, MSR_IA32_INTERRUPT_SSP_TABLE_ADDR + pop rax + pop rdx + wrmsr + + mov ecx, MSR_IA32_PL0_SSP + pop rax + pop rdx + wrmsr + + mov ecx, MSR_IA32_S_CET + pop rax + pop rdx + wrmsr +CetDone2: + + mov rax, strict qword 0 ; lea rax, [ASM_PFX(mXdSupported)] +mXdSupportedAbsAddr: + mov al, [rax] + cmp al, 0 + jz .1 + pop rdx ; get saved MSR_IA32_MISC_ENABLE[63-32] + test edx, BIT2 + jz .1 + mov ecx, MSR_IA32_MISC_ENABLE + rdmsr + or dx, BIT2 ; set XD Disable bit if it was set before entering into SMM + wrmsr + +.1: + + StuffRsb64 + rsm + +ASM_PFX(gcSmiHandlerSize) DW $ - _SmiEntryPoint + +; +; Retrieve the address and fill it into mov opcode. +; +; It is called in the driver entry point first. +; It is used to fix up the real address in mov opcode. +; Then, after the code logic is copied to the different location, +; the code can also run. +; +global ASM_PFX(PiSmmCpuSmiEntryFixupAddress) +ASM_PFX(PiSmmCpuSmiEntryFixupAddress): + lea rax, [ASM_PFX(gSmiHandlerIdtr)] + lea rcx, [SmiHandlerIdtrAbsAddr] + mov qword [rcx - 8], rax + + lea rax, [ASM_PFX(CpuSmmDebugEntry)] + lea rcx, [CpuSmmDebugEntryAbsAddr] + mov qword [rcx - 8], rax + + lea rax, [ASM_PFX(SmiRendezvous)] + lea rcx, [SmiRendezvousAbsAddr] + mov qword [rcx - 8], rax + + lea rax, [ASM_PFX(CpuSmmDebugExit)] + lea rcx, [CpuSmmDebugExitAbsAddr] + mov qword [rcx - 8], rax + + lea rax, [ASM_PFX(mXdSupported)] + lea rcx, [mXdSupportedAbsAddr] + mov qword [rcx - 8], rax + + lea rax, [ASM_PFX(mCetSupported)] + lea rcx, [mCetSupportedAbsAddr] + mov qword [rcx - 8], rax + ret diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmiException.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmiException.nasm new file mode 100644 index 00000000..ec497190 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmiException.nasm @@ -0,0 +1,378 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; SmiException.nasm +; +; Abstract: +; +; Exception handlers used in SM mode +; +;------------------------------------------------------------------------------- + +extern ASM_PFX(SmiPFHandler) + +global ASM_PFX(gcSmiIdtr) +global ASM_PFX(gcSmiGdtr) +global ASM_PFX(gcPsd) + + SECTION .data + +NullSeg: DQ 0 ; reserved by architecture +CodeSeg32: + DW -1 ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x9b + DB 0xcf ; LimitHigh + DB 0 ; BaseHigh +ProtModeCodeSeg32: + DW -1 ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x9b + DB 0xcf ; LimitHigh + DB 0 ; BaseHigh +ProtModeSsSeg32: + DW -1 ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x93 + DB 0xcf ; LimitHigh + DB 0 ; BaseHigh +DataSeg32: + DW -1 ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x93 + DB 0xcf ; LimitHigh + DB 0 ; BaseHigh +CodeSeg16: + DW -1 + DW 0 + DB 0 + DB 0x9b + DB 0x8f + DB 0 +DataSeg16: + DW -1 + DW 0 + DB 0 + DB 0x93 + DB 0x8f + DB 0 +CodeSeg64: + DW -1 ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x9b + DB 0xaf ; LimitHigh + DB 0 ; BaseHigh +; TSS Segment for X64 specially +TssSeg: + DW TSS_DESC_SIZE ; LimitLow + DW 0 ; BaseLow + DB 0 ; BaseMid + DB 0x89 + DB 0x80 ; LimitHigh + DB 0 ; BaseHigh + DD 0 ; BaseUpper + DD 0 ; Reserved +GDT_SIZE equ $ - NullSeg + +; Create TSS Descriptor just after GDT +TssDescriptor: + DD 0 ; Reserved + DQ 0 ; RSP0 + DQ 0 ; RSP1 + DQ 0 ; RSP2 + DD 0 ; Reserved + DD 0 ; Reserved + DQ 0 ; IST1 + DQ 0 ; IST2 + DQ 0 ; IST3 + DQ 0 ; IST4 + DQ 0 ; IST5 + DQ 0 ; IST6 + DQ 0 ; IST7 + DD 0 ; Reserved + DD 0 ; Reserved + DW 0 ; Reserved + DW 0 ; I/O Map Base Address +TSS_DESC_SIZE equ $ - TssDescriptor + +; +; This structure serves as a template for all processors. +; +ASM_PFX(gcPsd): + DB 'PSDSIG ' + DW PSD_SIZE + DW 2 + DW 1 << 2 + DW CODE_SEL + DW DATA_SEL + DW DATA_SEL + DW DATA_SEL + DW 0 + DQ 0 + DQ 0 + DQ 0 ; fixed in InitializeMpServiceData() + DQ NullSeg + DD GDT_SIZE + DD 0 + times 24 DB 0 + DQ 0 +PSD_SIZE equ $ - ASM_PFX(gcPsd) + +; +; CODE & DATA segments for SMM runtime +; +CODE_SEL equ CodeSeg64 - NullSeg +DATA_SEL equ DataSeg32 - NullSeg +CODE32_SEL equ CodeSeg32 - NullSeg + +ASM_PFX(gcSmiGdtr): + DW GDT_SIZE - 1 + DQ NullSeg + +ASM_PFX(gcSmiIdtr): + DW 0 + DQ 0 + + DEFAULT REL + SECTION .text + +;------------------------------------------------------------------------------ +; _SmiExceptionEntryPoints is the collection of exception entrypoints followed +; by a common exception handler. +; +; Stack frame would be as follows as specified in IA32 manuals: +; +; +---------------------+ <-- 16-byte aligned ensured by processor +; + Old SS + +; +---------------------+ +; + Old RSP + +; +---------------------+ +; + RFlags + +; +---------------------+ +; + CS + +; +---------------------+ +; + RIP + +; +---------------------+ +; + Error Code + +; +---------------------+ +; + Vector Number + +; +---------------------+ +; + RBP + +; +---------------------+ <-- RBP, 16-byte aligned +; +; RSP set to odd multiple of 8 at @CommonEntryPoint means ErrCode PRESENT +;------------------------------------------------------------------------------ +global ASM_PFX(PageFaultIdtHandlerSmmProfile) +ASM_PFX(PageFaultIdtHandlerSmmProfile): + push 0xe ; Page Fault + test spl, 8 ; odd multiple of 8 => ErrCode present + jnz .0 + push qword [rsp] ; duplicate INT# if no ErrCode + mov qword [rsp + 8], 0 +.0: + push rbp + mov rbp, rsp + + ; + ; Since here the stack pointer is 16-byte aligned, so + ; EFI_FX_SAVE_STATE_X64 of EFI_SYSTEM_CONTEXT_x64 + ; is 16-byte aligned + ; + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rax + push rcx + push rdx + push rbx + push qword [rbp + 48] ; RSP + push qword [rbp] ; RBP + push rsi + push rdi + +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero + movzx rax, word [rbp + 56] + push rax ; for ss + movzx rax, word [rbp + 32] + push rax ; for cs + mov rax, ds + push rax + mov rax, es + push rax + mov rax, fs + push rax + mov rax, gs + push rax + +;; UINT64 Rip; + push qword [rbp + 24] + +;; UINT64 Gdtr[2], Idtr[2]; + sub rsp, 16 + sidt [rsp] + sub rsp, 16 + sgdt [rsp] + +;; UINT64 Ldtr, Tr; + xor rax, rax + str ax + push rax + sldt ax + push rax + +;; UINT64 RFlags; + push qword [rbp + 40] + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; + mov rax, cr8 + push rax + mov rax, cr4 + or rax, 0x208 + mov cr4, rax + push rax + mov rax, cr3 + push rax + mov rax, cr2 + push rax + xor rax, rax + push rax + mov rax, cr0 + push rax + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + mov rax, dr7 + push rax + mov rax, dr6 + push rax + mov rax, dr3 + push rax + mov rax, dr2 + push rax + mov rax, dr1 + push rax + mov rax, dr0 + push rax + +;; FX_SAVE_STATE_X64 FxSaveState; + + sub rsp, 512 + mov rdi, rsp + fxsave [rdi] + +; UEFI calling convention for x64 requires that Direction flag in EFLAGs is clear + cld + +;; UINT32 ExceptionData; + push qword [rbp + 16] + +;; call into exception handler + mov rcx, [rbp + 8] + lea rax, [ASM_PFX(SmiPFHandler)] + +;; Prepare parameter and call + mov rdx, rsp + ; + ; Per X64 calling convention, allocate maximum parameter stack space + ; and make sure RSP is 16-byte aligned + ; + sub rsp, 4 * 8 + 8 + call rax + add rsp, 4 * 8 + 8 + jmp .1 + +.1: +;; UINT64 ExceptionData; + add rsp, 8 + +;; FX_SAVE_STATE_X64 FxSaveState; + + mov rsi, rsp + fxrstor [rsi] + add rsp, 512 + +;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; +;; Skip restoration of DRx registers to support debuggers +;; that set breakpoints in interrupt/exception context + add rsp, 8 * 6 + +;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8; + pop rax + mov cr0, rax + add rsp, 8 ; not for Cr1 + pop rax + mov cr2, rax + pop rax + mov cr3, rax + pop rax + mov cr4, rax + pop rax + mov cr8, rax + +;; UINT64 RFlags; + pop qword [rbp + 40] + +;; UINT64 Ldtr, Tr; +;; UINT64 Gdtr[2], Idtr[2]; +;; Best not let anyone mess with these particular registers... + add rsp, 48 + +;; UINT64 Rip; + pop qword [rbp + 24] + +;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; + pop rax + ; mov gs, rax ; not for gs + pop rax + ; mov fs, rax ; not for fs + ; (X64 will not use fs and gs, so we do not restore it) + pop rax + mov es, rax + pop rax + mov ds, rax + pop qword [rbp + 32] ; for cs + pop qword [rbp + 56] ; for ss + +;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax; +;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15; + pop rdi + pop rsi + add rsp, 8 ; not for rbp + pop qword [rbp + 48] ; for rsp + pop rbx + pop rdx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + + mov rsp, rbp + +; Enable TF bit after page fault handler runs + bts dword [rsp + 40], 8 ;RFLAGS + + pop rbp + add rsp, 16 ; skip INT# & ErrCode + iretq + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c new file mode 100644 index 00000000..1f30c6d4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmFuncsArch.c @@ -0,0 +1,218 @@ +/** @file + SMM CPU misc functions for x64 arch specific. + +Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" + +EFI_PHYSICAL_ADDRESS mGdtBuffer; +UINTN mGdtBufferSize; + +extern BOOLEAN mCetSupported; +extern UINTN mSmmShadowStackSize; + +X86_ASSEMBLY_PATCH_LABEL mPatchCetPl0Ssp; +X86_ASSEMBLY_PATCH_LABEL mPatchCetInterruptSsp; +X86_ASSEMBLY_PATCH_LABEL mPatchCetInterruptSspTable; +UINT32 mCetPl0Ssp; +UINT32 mCetInterruptSsp; +UINT32 mCetInterruptSspTable; + +UINTN mSmmInterruptSspTables; + +/** + Initialize IDT for SMM Stack Guard. + +**/ +VOID +EFIAPI +InitializeIDTSmmStackGuard ( + VOID + ) +{ + IA32_IDT_GATE_DESCRIPTOR *IdtGate; + + // + // If SMM Stack Guard feature is enabled, set the IST field of + // the interrupt gate for Page Fault Exception to be 1 + // + IdtGate = (IA32_IDT_GATE_DESCRIPTOR *)gcSmiIdtr.Base; + IdtGate += EXCEPT_IA32_PAGE_FAULT; + IdtGate->Bits.Reserved_0 = 1; +} + +/** + Initialize Gdt for all processors. + + @param[in] Cr3 CR3 value. + @param[out] GdtStepSize The step size for GDT table. + + @return GdtBase for processor 0. + GdtBase for processor X is: GdtBase + (GdtStepSize * X) +**/ +VOID * +InitGdt ( + IN UINTN Cr3, + OUT UINTN *GdtStepSize + ) +{ + UINTN Index; + IA32_SEGMENT_DESCRIPTOR *GdtDescriptor; + UINTN TssBase; + UINTN GdtTssTableSize; + UINT8 *GdtTssTables; + UINTN GdtTableStepSize; + + // + // For X64 SMM, we allocate separate GDT/TSS for each CPUs to avoid TSS load contention + // on each SMI entry. + // + GdtTssTableSize = (gcSmiGdtr.Limit + 1 + TSS_SIZE + 7) & ~7; // 8 bytes aligned + mGdtBufferSize = GdtTssTableSize * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; + GdtTssTables = (UINT8*)AllocateCodePages (EFI_SIZE_TO_PAGES (mGdtBufferSize)); + ASSERT (GdtTssTables != NULL); + mGdtBuffer = (UINTN)GdtTssTables; + GdtTableStepSize = GdtTssTableSize; + + for (Index = 0; Index < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; Index++) { + CopyMem (GdtTssTables + GdtTableStepSize * Index, (VOID*)(UINTN)gcSmiGdtr.Base, gcSmiGdtr.Limit + 1 + TSS_SIZE); + + // + // Fixup TSS descriptors + // + TssBase = (UINTN)(GdtTssTables + GdtTableStepSize * Index + gcSmiGdtr.Limit + 1); + GdtDescriptor = (IA32_SEGMENT_DESCRIPTOR *)(TssBase) - 2; + GdtDescriptor->Bits.BaseLow = (UINT16)(UINTN)TssBase; + GdtDescriptor->Bits.BaseMid = (UINT8)((UINTN)TssBase >> 16); + GdtDescriptor->Bits.BaseHigh = (UINT8)((UINTN)TssBase >> 24); + + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + // + // Setup top of known good stack as IST1 for each processor. + // + *(UINTN *)(TssBase + TSS_X64_IST1_OFFSET) = (mSmmStackArrayBase + EFI_PAGE_SIZE + Index * (mSmmStackSize + mSmmShadowStackSize)); + } + } + + *GdtStepSize = GdtTableStepSize; + return GdtTssTables; +} + +/** + Get Protected mode code segment from current GDT table. + + @return Protected mode code segment value. +**/ +UINT16 +GetProtectedModeCS ( + VOID + ) +{ + IA32_DESCRIPTOR GdtrDesc; + IA32_SEGMENT_DESCRIPTOR *GdtEntry; + UINTN GdtEntryCount; + UINT16 Index; + + AsmReadGdtr (&GdtrDesc); + GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR); + GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; + for (Index = 0; Index < GdtEntryCount; Index++) { + if (GdtEntry->Bits.L == 0) { + if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) { + break; + } + } + GdtEntry++; + } + ASSERT (Index != GdtEntryCount); + return Index * 8; +} + +/** + Transfer AP to safe hlt-loop after it finished restore CPU features on S3 patch. + + @param[in] ApHltLoopCode The address of the safe hlt-loop function. + @param[in] TopOfStack A pointer to the new stack to use for the ApHltLoopCode. + @param[in] NumberToFinishAddress Address of Semaphore of APs finish count. + +**/ +VOID +TransferApToSafeState ( + IN UINTN ApHltLoopCode, + IN UINTN TopOfStack, + IN UINTN NumberToFinishAddress + ) +{ + AsmDisablePaging64 ( + GetProtectedModeCS (), + (UINT32)ApHltLoopCode, + (UINT32)NumberToFinishAddress, + 0, + (UINT32)TopOfStack + ); + // + // It should never reach here + // + ASSERT (FALSE); +} + +/** + Initialize the shadow stack related data structure. + + @param CpuIndex The index of CPU. + @param ShadowStack The bottom of the shadow stack for this CPU. +**/ +VOID +InitShadowStack ( + IN UINTN CpuIndex, + IN VOID *ShadowStack + ) +{ + UINTN SmmShadowStackSize; + UINT64 *InterruptSspTable; + UINT32 InterruptSsp; + + if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) { + SmmShadowStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize))); + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + SmmShadowStackSize += EFI_PAGES_TO_SIZE (2); + } + mCetPl0Ssp = (UINT32)((UINTN)ShadowStack + SmmShadowStackSize - sizeof(UINT64)); + PatchInstructionX86 (mPatchCetPl0Ssp, mCetPl0Ssp, 4); + DEBUG ((DEBUG_INFO, "mCetPl0Ssp - 0x%x\n", mCetPl0Ssp)); + DEBUG ((DEBUG_INFO, "ShadowStack - 0x%x\n", ShadowStack)); + DEBUG ((DEBUG_INFO, " SmmShadowStackSize - 0x%x\n", SmmShadowStackSize)); + + if (FeaturePcdGet (PcdCpuSmmStackGuard)) { + if (mSmmInterruptSspTables == 0) { + mSmmInterruptSspTables = (UINTN)AllocateZeroPool(sizeof(UINT64) * 8 * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus); + ASSERT (mSmmInterruptSspTables != 0); + DEBUG ((DEBUG_INFO, "mSmmInterruptSspTables - 0x%x\n", mSmmInterruptSspTables)); + } + + // + // The highest address on the stack (0xFF8) is a save-previous-ssp token pointing to a location that is 40 bytes away - 0xFD0. + // The supervisor shadow stack token is just above it at address 0xFF0. This is where the interrupt SSP table points. + // So when an interrupt of exception occurs, we can use SAVESSP/RESTORESSP/CLEARSSBUSY for the supervisor shadow stack, + // due to the reason the RETF in SMM exception handler cannot clear the BUSY flag with same CPL. + // (only IRET or RETF with different CPL can clear BUSY flag) + // Please refer to UefiCpuPkg/Library/CpuExceptionHandlerLib/X64 for the full stack frame at runtime. + // + InterruptSsp = (UINT32)((UINTN)ShadowStack + EFI_PAGES_TO_SIZE(1) - sizeof(UINT64)); + *(UINT32 *)(UINTN)InterruptSsp = (InterruptSsp - sizeof(UINT64) * 4) | 0x2; + mCetInterruptSsp = InterruptSsp - sizeof(UINT64); + + mCetInterruptSspTable = (UINT32)(UINTN)(mSmmInterruptSspTables + sizeof(UINT64) * 8 * CpuIndex); + InterruptSspTable = (UINT64 *)(UINTN)mCetInterruptSspTable; + InterruptSspTable[1] = mCetInterruptSsp; + PatchInstructionX86 (mPatchCetInterruptSsp, mCetInterruptSsp, 4); + PatchInstructionX86 (mPatchCetInterruptSspTable, mCetInterruptSspTable, 4); + DEBUG ((DEBUG_INFO, "mCetInterruptSsp - 0x%x\n", mCetInterruptSsp)); + DEBUG ((DEBUG_INFO, "mCetInterruptSspTable - 0x%x\n", mCetInterruptSspTable)); + } + } +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmInit.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmInit.nasm new file mode 100644 index 00000000..1c5648ee --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmInit.nasm @@ -0,0 +1,146 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; SmmInit.nasm +; +; Abstract: +; +; Functions for relocating SMBASE's for all processors +; +;------------------------------------------------------------------------------- + +%include "StuffRsbNasm.inc" + +extern ASM_PFX(SmmInitHandler) +extern ASM_PFX(mRebasedFlag) +extern ASM_PFX(mSmmRelocationOriginalAddress) + +global ASM_PFX(gPatchSmmCr3) +global ASM_PFX(gPatchSmmCr4) +global ASM_PFX(gPatchSmmCr0) +global ASM_PFX(gPatchSmmInitStack) +global ASM_PFX(gcSmiInitGdtr) +global ASM_PFX(gcSmmInitSize) +global ASM_PFX(gcSmmInitTemplate) +global ASM_PFX(gPatchRebasedFlagAddr32) +global ASM_PFX(gPatchSmmRelocationOriginalAddressPtr32) + +%define LONG_MODE_CS 0x38 + + DEFAULT REL + SECTION .text + +ASM_PFX(gcSmiInitGdtr): + DW 0 + DQ 0 + +global ASM_PFX(SmmStartup) + +BITS 16 +ASM_PFX(SmmStartup): + mov eax, 0x80000001 ; read capability + cpuid + mov ebx, edx ; rdmsr will change edx. keep it in ebx. + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmmCr3): + mov cr3, eax +o32 lgdt [cs:ebp + (ASM_PFX(gcSmiInitGdtr) - ASM_PFX(SmmStartup))] + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmmCr4): + or ah, 2 ; enable XMM registers access + mov cr4, eax + mov ecx, 0xc0000080 ; IA32_EFER MSR + rdmsr + or ah, BIT0 ; set LME bit + test ebx, BIT20 ; check NXE capability + jz .1 + or ah, BIT3 ; set NXE bit +.1: + wrmsr + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchSmmCr0): + mov cr0, eax ; enable protected mode & paging + jmp LONG_MODE_CS : dword 0 ; offset will be patched to @LongMode +@PatchLongModeOffset: + +BITS 64 +@LongMode: ; long-mode starts here + mov rsp, strict qword 0 ; source operand will be patched +ASM_PFX(gPatchSmmInitStack): + and sp, 0xfff0 ; make sure RSP is 16-byte aligned + ; + ; According to X64 calling convention, XMM0~5 are volatile, we need to save + ; them before calling C-function. + ; + sub rsp, 0x60 + movdqa [rsp], xmm0 + movdqa [rsp + 0x10], xmm1 + movdqa [rsp + 0x20], xmm2 + movdqa [rsp + 0x30], xmm3 + movdqa [rsp + 0x40], xmm4 + movdqa [rsp + 0x50], xmm5 + + add rsp, -0x20 + call ASM_PFX(SmmInitHandler) + add rsp, 0x20 + + ; + ; Restore XMM0~5 after calling C-function. + ; + movdqa xmm0, [rsp] + movdqa xmm1, [rsp + 0x10] + movdqa xmm2, [rsp + 0x20] + movdqa xmm3, [rsp + 0x30] + movdqa xmm4, [rsp + 0x40] + movdqa xmm5, [rsp + 0x50] + + StuffRsb64 + rsm + +BITS 16 +ASM_PFX(gcSmmInitTemplate): + mov ebp, [cs:@L1 - ASM_PFX(gcSmmInitTemplate) + 0x8000] + sub ebp, 0x30000 + jmp ebp +@L1: + DQ 0; ASM_PFX(SmmStartup) + +ASM_PFX(gcSmmInitSize): DW $ - ASM_PFX(gcSmmInitTemplate) + +BITS 64 +global ASM_PFX(SmmRelocationSemaphoreComplete) +ASM_PFX(SmmRelocationSemaphoreComplete): + push rax + mov rax, [ASM_PFX(mRebasedFlag)] + mov byte [rax], 1 + pop rax + jmp [ASM_PFX(mSmmRelocationOriginalAddress)] + +; +; Semaphore code running in 32-bit mode +; +BITS 32 +global ASM_PFX(SmmRelocationSemaphoreComplete32) +ASM_PFX(SmmRelocationSemaphoreComplete32): + push eax + mov eax, strict dword 0 ; source operand will be patched +ASM_PFX(gPatchRebasedFlagAddr32): + mov byte [eax], 1 + pop eax + jmp dword [dword 0] ; destination will be patched +ASM_PFX(gPatchSmmRelocationOriginalAddressPtr32): + +BITS 64 +global ASM_PFX(PiSmmCpuSmmInitFixupAddress) +ASM_PFX(PiSmmCpuSmmInitFixupAddress): + lea rax, [@LongMode] + lea rcx, [@PatchLongModeOffset - 6] + mov dword [rcx], eax + + lea rax, [ASM_PFX(SmmStartup)] + lea rcx, [@L1] + mov qword [rcx], rax + ret diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmProfileArch.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmProfileArch.c new file mode 100644 index 00000000..4e5f9d30 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmProfileArch.c @@ -0,0 +1,333 @@ +/** @file +X64 processor specific functions to enable SMM profile. + +Copyright (c) 2012 - 2019, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" +#include "SmmProfileInternal.h" + +// +// Current page index. +// +UINTN mPFPageIndex; + +// +// Pool for dynamically creating page table in page fault handler. +// +UINT64 mPFPageBuffer; + +// +// Store the uplink information for each page being used. +// +UINT64 *mPFPageUplink[MAX_PF_PAGE_COUNT]; + +/** + Create SMM page table for S3 path. + +**/ +VOID +InitSmmS3Cr3 ( + VOID + ) +{ + EFI_PHYSICAL_ADDRESS Pages; + UINT64 *PTEntry; + + // + // Generate PAE page table for the first 4GB memory space + // + Pages = Gen4GPageTable (FALSE); + + // + // Fill Page-Table-Level4 (PML4) entry + // + PTEntry = (UINT64*)AllocatePageTableMemory (1); + ASSERT (PTEntry != NULL); + *PTEntry = Pages | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + ZeroMem (PTEntry + 1, EFI_PAGE_SIZE - sizeof (*PTEntry)); + + // + // Return the address of PML4 (to set CR3) + // + mSmmS3ResumeState->SmmS3Cr3 = (UINT32)(UINTN)PTEntry; + + return ; +} + +/** + Allocate pages for creating 4KB-page based on 2MB-page when page fault happens. + +**/ +VOID +InitPagesForPFHandler ( + VOID + ) +{ + VOID *Address; + + // + // Pre-Allocate memory for page fault handler + // + Address = NULL; + Address = AllocatePages (MAX_PF_PAGE_COUNT); + ASSERT (Address != NULL); + + mPFPageBuffer = (UINT64)(UINTN) Address; + mPFPageIndex = 0; + ZeroMem ((VOID *) (UINTN) mPFPageBuffer, EFI_PAGE_SIZE * MAX_PF_PAGE_COUNT); + ZeroMem (mPFPageUplink, sizeof (mPFPageUplink)); + + return; +} + +/** + Allocate one page for creating 4KB-page based on 2MB-page. + + @param Uplink The address of Page-Directory entry. + +**/ +VOID +AcquirePage ( + UINT64 *Uplink + ) +{ + UINT64 Address; + + // + // Get the buffer + // + Address = mPFPageBuffer + EFI_PAGES_TO_SIZE (mPFPageIndex); + ZeroMem ((VOID *) (UINTN) Address, EFI_PAGE_SIZE); + + // + // Cut the previous uplink if it exists and wasn't overwritten + // + if ((mPFPageUplink[mPFPageIndex] != NULL) && ((*mPFPageUplink[mPFPageIndex] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK) == Address)) { + *mPFPageUplink[mPFPageIndex] = 0; + } + + // + // Link & Record the current uplink + // + *Uplink = Address | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + mPFPageUplink[mPFPageIndex] = Uplink; + + mPFPageIndex = (mPFPageIndex + 1) % MAX_PF_PAGE_COUNT; +} + +/** + Update page table to map the memory correctly in order to make the instruction + which caused page fault execute successfully. And it also save the original page + table to be restored in single-step exception. + + @param PageTable PageTable Address. + @param PFAddress The memory address which caused page fault exception. + @param CpuIndex The index of the processor. + @param ErrorCode The Error code of exception. + @param IsValidPFAddress The flag indicates if SMM profile data need be added. + +**/ +VOID +RestorePageTableAbove4G ( + UINT64 *PageTable, + UINT64 PFAddress, + UINTN CpuIndex, + UINTN ErrorCode, + BOOLEAN *IsValidPFAddress + ) +{ + UINTN PTIndex; + UINT64 Address; + BOOLEAN Nx; + BOOLEAN Existed; + UINTN Index; + UINTN PFIndex; + IA32_CR4 Cr4; + BOOLEAN Enable5LevelPaging; + + ASSERT ((PageTable != NULL) && (IsValidPFAddress != NULL)); + + Cr4.UintN = AsmReadCr4 (); + Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1); + + // + // If page fault address is 4GB above. + // + + // + // Check if page fault address has existed in page table. + // If it exists in page table but page fault is generated, + // there are 2 possible reasons: 1. present flag is set to 0; 2. instruction fetch in protected memory range. + // + Existed = FALSE; + PageTable = (UINT64*)(AsmReadCr3 () & PHYSICAL_ADDRESS_MASK); + PTIndex = 0; + if (Enable5LevelPaging) { + PTIndex = BitFieldRead64 (PFAddress, 48, 56); + } + if ((!Enable5LevelPaging) || ((PageTable[PTIndex] & IA32_PG_P) != 0)) { + // PML5E + if (Enable5LevelPaging) { + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + } + PTIndex = BitFieldRead64 (PFAddress, 39, 47); + if ((PageTable[PTIndex] & IA32_PG_P) != 0) { + // PML4E + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + PTIndex = BitFieldRead64 (PFAddress, 30, 38); + if ((PageTable[PTIndex] & IA32_PG_P) != 0) { + // PDPTE + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + PTIndex = BitFieldRead64 (PFAddress, 21, 29); + // PD + if ((PageTable[PTIndex] & IA32_PG_PS) != 0) { + // + // 2MB page + // + Address = (UINT64)(PageTable[PTIndex] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + if ((Address & ~((1ull << 21) - 1)) == ((PFAddress & PHYSICAL_ADDRESS_MASK & ~((1ull << 21) - 1)))) { + Existed = TRUE; + } + } else { + // + // 4KB page + // + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask& PHYSICAL_ADDRESS_MASK); + if (PageTable != 0) { + // + // When there is a valid entry to map to 4KB page, need not create a new entry to map 2MB. + // + PTIndex = BitFieldRead64 (PFAddress, 12, 20); + Address = (UINT64)(PageTable[PTIndex] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + if ((Address & ~((1ull << 12) - 1)) == (PFAddress & PHYSICAL_ADDRESS_MASK & ~((1ull << 12) - 1))) { + Existed = TRUE; + } + } + } + } + } + } + + // + // If page entry does not existed in page table at all, create a new entry. + // + if (!Existed) { + + if (IsAddressValid (PFAddress, &Nx)) { + // + // If page fault address above 4GB is in protected range but it causes a page fault exception, + // Will create a page entry for this page fault address, make page table entry as present/rw and execution-disable. + // this access is not saved into SMM profile data. + // + *IsValidPFAddress = TRUE; + } + + // + // Create one entry in page table for page fault address. + // + SmiDefaultPFHandler (); + // + // Find the page table entry created just now. + // + PageTable = (UINT64*)(AsmReadCr3 () & PHYSICAL_ADDRESS_MASK); + PFAddress = AsmReadCr2 (); + // PML5E + if (Enable5LevelPaging) { + PTIndex = BitFieldRead64 (PFAddress, 48, 56); + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + } + // PML4E + PTIndex = BitFieldRead64 (PFAddress, 39, 47); + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + // PDPTE + PTIndex = BitFieldRead64 (PFAddress, 30, 38); + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + // PD + PTIndex = BitFieldRead64 (PFAddress, 21, 29); + Address = PageTable[PTIndex] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK; + // + // Check if 2MB-page entry need be changed to 4KB-page entry. + // + if (IsAddressSplit (Address)) { + AcquirePage (&PageTable[PTIndex]); + + // PTE + PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); + for (Index = 0; Index < 512; Index++) { + PageTable[Index] = Address | mAddressEncMask | PAGE_ATTRIBUTE_BITS; + if (!IsAddressValid (Address, &Nx)) { + PageTable[Index] = PageTable[Index] & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS); + } + if (Nx && mXdSupported) { + PageTable[Index] = PageTable[Index] | IA32_PG_NX; + } + if (Address == (PFAddress & PHYSICAL_ADDRESS_MASK & ~((1ull << 12) - 1))) { + PTIndex = Index; + } + Address += SIZE_4KB; + } // end for PT + } else { + // + // Update 2MB page entry. + // + if (!IsAddressValid (Address, &Nx)) { + // + // Patch to remove present flag and rw flag. + // + PageTable[PTIndex] = PageTable[PTIndex] & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS); + } + // + // Set XD bit to 1 + // + if (Nx && mXdSupported) { + PageTable[PTIndex] = PageTable[PTIndex] | IA32_PG_NX; + } + } + } + + // + // Record old entries with non-present status + // Old entries include the memory which instruction is at and the memory which instruction access. + // + // + ASSERT (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT); + if (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT) { + PFIndex = mPFEntryCount[CpuIndex]; + mLastPFEntryValue[CpuIndex][PFIndex] = PageTable[PTIndex]; + mLastPFEntryPointer[CpuIndex][PFIndex] = &PageTable[PTIndex]; + mPFEntryCount[CpuIndex]++; + } + + // + // Add present flag or clear XD flag to make page fault handler succeed. + // + PageTable[PTIndex] |= (UINT64)(PAGE_ATTRIBUTE_BITS); + if ((ErrorCode & IA32_PF_EC_ID) != 0) { + // + // If page fault is caused by instruction fetch, clear XD bit in the entry. + // + PageTable[PTIndex] &= ~IA32_PG_NX; + } + + return; +} + +/** + Clear TF in FLAGS. + + @param SystemContext A pointer to the processor context when + the interrupt occurred on the processor. + +**/ +VOID +ClearTrapFlag ( + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + SystemContext.SystemContextX64->Rflags &= (UINTN) ~BIT8; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmProfileArch.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmProfileArch.h new file mode 100644 index 00000000..36a1c287 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/X64/SmmProfileArch.h @@ -0,0 +1,99 @@ +/** @file +X64 processor specific header file to enable SMM profile. + +Copyright (c) 2012 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SMM_PROFILE_ARCH_H_ +#define _SMM_PROFILE_ARCH_H_ + +#pragma pack (1) + +typedef struct _MSR_DS_AREA_STRUCT { + UINT64 BTSBufferBase; + UINT64 BTSIndex; + UINT64 BTSAbsoluteMaximum; + UINT64 BTSInterruptThreshold; + UINT64 PEBSBufferBase; + UINT64 PEBSIndex; + UINT64 PEBSAbsoluteMaximum; + UINT64 PEBSInterruptThreshold; + UINT64 PEBSCounterReset[2]; + UINT64 Reserved; +} MSR_DS_AREA_STRUCT; + +typedef struct _BRANCH_TRACE_RECORD { + UINT64 LastBranchFrom; + UINT64 LastBranchTo; + UINT64 Rsvd0 : 4; + UINT64 BranchPredicted : 1; + UINT64 Rsvd1 : 59; +} BRANCH_TRACE_RECORD; + +typedef struct _PEBS_RECORD { + UINT64 Rflags; + UINT64 LinearIP; + UINT64 Rax; + UINT64 Rbx; + UINT64 Rcx; + UINT64 Rdx; + UINT64 Rsi; + UINT64 Rdi; + UINT64 Rbp; + UINT64 Rsp; + UINT64 R8; + UINT64 R9; + UINT64 R10; + UINT64 R11; + UINT64 R12; + UINT64 R13; + UINT64 R14; + UINT64 R15; +} PEBS_RECORD; + +#pragma pack () + +#define PHYSICAL_ADDRESS_MASK ((1ull << 52) - SIZE_4KB) + +/** + Update page table to map the memory correctly in order to make the instruction + which caused page fault execute successfully. And it also save the original page + table to be restored in single-step exception. + + @param PageTable PageTable Address. + @param PFAddress The memory address which caused page fault exception. + @param CpuIndex The index of the processor. + @param ErrorCode The Error code of exception. + @param IsValidPFAddress The flag indicates if SMM profile data need be added. + +**/ +VOID +RestorePageTableAbove4G ( + UINT64 *PageTable, + UINT64 PFAddress, + UINTN CpuIndex, + UINTN ErrorCode, + BOOLEAN *IsValidPFAddress + ); + +/** + Create SMM page table for S3 path. + +**/ +VOID +InitSmmS3Cr3 ( + VOID + ); + +/** + Allocate pages for creating 4KB-page based on 2MB-page when page fault happens. + +**/ +VOID +InitPagesForPFHandler ( + VOID + ); + +#endif // _SMM_PROFILE_ARCH_H_ diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/FixupVtf/ResetVector.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/FixupVtf/ResetVector.uni new file mode 100644 index 00000000..d4f74fd7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/FixupVtf/ResetVector.uni @@ -0,0 +1,16 @@ +// /** @file
+// Reset Vector
+//
+// This VTF requires build time fixups in order to find the SEC entry point.
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Reset Vector"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This VTF requires build time fixups in order to find the SEC entry point"
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/FixupVtf/ResetVectorExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/FixupVtf/ResetVectorExtra.uni new file mode 100644 index 00000000..7d2d5793 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/FixupVtf/ResetVectorExtra.uni @@ -0,0 +1,12 @@ +// /** @file
+// ResetVector Localized Strings and Content
+//
+// Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME #language en-US "ResetVector module"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/FixupVtf/Vtf.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/FixupVtf/Vtf.inf new file mode 100644 index 00000000..6e2a6bc5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/FixupVtf/Vtf.inf @@ -0,0 +1,33 @@ +## @file +# Reset Vector +# +# This VTF requires build time fixups in order to find the SEC entry point. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ResetVector + FILE_GUID = 1BA0062E-C779-4582-8566-336AE8F78F09 + MODULE_TYPE = SEC + VERSION_STRING = 1.1 + MODULE_UNI_FILE = ResetVector.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + Vtf.nasmb + +[Packages] + MdePkg/MdePkg.dec + +[UserExtensions.TianoCore."ExtraFiles"] + ResetVectorExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/FixupVtf/Vtf.nasmb b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/FixupVtf/Vtf.nasmb new file mode 100644 index 00000000..92384427 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/FixupVtf/Vtf.nasmb @@ -0,0 +1,54 @@ +;------------------------------------------------------------------------------ +; @file +; First code exectuted by processor after resetting. +; +; Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +BITS 16 + +ALIGN 16 ; 0xffffffd0 + +applicationProcessorEntryPoint: +; +; Application Processors entry point +; +; GenFv generates code aligned on a 4k boundary which will jump to this +; location. (0xffffffd0) This allows the Local APIC Startup IPI to be +; used to wake up the application processors. +; + jmp short resetVector + +ALIGN 16 ; 0xffffffe0 + +peiCoreEntryPoint: +; +; PEI Core entry point +; +; GenFv fills the address of the PEI Core into this location +; + DD 0x12345678 + +ALIGN 16 ; 0xfffffff0 + +resetVector: +; +; Reset Vector +; +; This is where the processor will begin execution +; + nop + nop + jmp near $ + +ALIGN 8 + +ApStartupSegment: + DD 0x12345678 + +BootFvBaseAddress: + DD 0x12345678 + +ALIGN 16 ; 0x100000000 diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.ia32.port80.raw b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.ia32.port80.raw Binary files differnew file mode 100644 index 00000000..2c6ff655 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.ia32.port80.raw diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.ia32.raw b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.ia32.raw Binary files differnew file mode 100644 index 00000000..e34780a3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.ia32.raw diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.ia32.serial.raw b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.ia32.serial.raw Binary files differnew file mode 100644 index 00000000..6dfa68ea --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.ia32.serial.raw diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.inf new file mode 100644 index 00000000..8d0c3960 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.inf @@ -0,0 +1,31 @@ +## @file +# Reset Vector binary +# +# Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ResetVector + MODULE_UNI_FILE = ResetVector.uni + FILE_GUID = 1BA0062E-C779-4582-8566-336AE8F78F09 + MODULE_TYPE = SEC + VERSION_STRING = 1.1 + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Binaries.Ia32] + RAW|ResetVector.ia32.raw|* + +[Binaries.X64] + RAW|ResetVector.x64.raw|* + +[UserExtensions.TianoCore."ExtraFiles"] + ResetVectorExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.uni new file mode 100644 index 00000000..3e7bcdaf --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.uni @@ -0,0 +1,16 @@ +// /** @file
+// Reset Vector binary
+//
+// Reset Vector binary
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Reset Vector binary"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Reset Vector binary"
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.x64.port80.raw b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.x64.port80.raw Binary files differnew file mode 100644 index 00000000..6c0bcc47 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.x64.port80.raw diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.x64.raw b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.x64.raw Binary files differnew file mode 100644 index 00000000..a78d5b40 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.x64.raw diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.x64.serial.raw b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.x64.serial.raw Binary files differnew file mode 100644 index 00000000..61c71349 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.x64.serial.raw diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVectorExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVectorExtra.uni new file mode 100644 index 00000000..7d2d5793 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVectorExtra.uni @@ -0,0 +1,12 @@ +// /** @file
+// ResetVector Localized Strings and Content
+//
+// Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME #language en-US "ResetVector module"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Build.py b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Build.py new file mode 100755 index 00000000..f2d41c55 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Build.py @@ -0,0 +1,52 @@ +## @file +# Automate the process of building the various reset vector types +# +# Copyright (c) 2009, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +import glob +import os +import subprocess +import sys + +# VBox begin +print 'VBox: We do not use this build script, the reset vector binaries are built by the top-level EFI makefile!'; +sys.exit(1); +# VBox end + +def RunCommand(commandLine): + #print ' '.join(commandLine) + return subprocess.call(commandLine) + +for filename in glob.glob(os.path.join('Bin', '*.raw')): + os.remove(filename) + +for arch in ('ia32', 'x64'): + for debugType in (None, 'port80', 'serial'): + output = os.path.join('Bin', 'ResetVector') + output += '.' + arch + if debugType is not None: + output += '.' + debugType + output += '.raw' + commandLine = ( + 'nasm', + '-D', 'ARCH_%s' % arch.upper(), + '-D', 'DEBUG_%s' % str(debugType).upper(), + '-o', output, + 'Vtf0.nasmb', + ) + ret = RunCommand(commandLine) + print '\tASM\t' + output + if ret != 0: sys.exit(ret) + + commandLine = ( + 'python', + 'Tools/FixupForRawSection.py', + output, + ) + print '\tFIXUP\t' + output + ret = RunCommand(commandLine) + if ret != 0: sys.exit(ret) + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/CommonMacros.inc b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/CommonMacros.inc new file mode 100644 index 00000000..5b3dfba0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/CommonMacros.inc @@ -0,0 +1,25 @@ +;------------------------------------------------------------------------------ +; @file +; Common macros used in the ResetVector VTF module. +; +; Copyright (c) 2008, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +%define ADDR16_OF(x) (0x10000 - fourGigabytes + x) +%define ADDR_OF(x) (0x100000000 - fourGigabytes + x) + +%macro OneTimeCall 1 + jmp %1 +%1 %+ OneTimerCallReturn: +%endmacro + +%macro OneTimeCallRet 1 + jmp %1 %+ OneTimerCallReturn +%endmacro + +StartOfResetVectorCode: + +%define ADDR_OF_START_OF_RESET_CODE ADDR_OF(StartOfResetVectorCode) + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/DebugDisabled.asm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/DebugDisabled.asm new file mode 100644 index 00000000..542f527f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/DebugDisabled.asm @@ -0,0 +1,20 @@ +;------------------------------------------------------------------------------ +; @file +; Debug disabled +; +; Copyright (c) 2009, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +BITS 16 + +%macro debugInitialize 0 + ; + ; No initialization is required + ; +%endmacro + +%macro debugShowPostCode 1 +%endmacro + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia16/Init16.asm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia16/Init16.asm new file mode 100644 index 00000000..9626ca0a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia16/Init16.asm @@ -0,0 +1,42 @@ +;------------------------------------------------------------------------------ +; @file +; 16-bit initialization code +; +; Copyright (c) 2008 - 2009, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + + +BITS 16 + +; +; @param[out] DI 'BP' to indicate boot-strap processor +; +EarlyBspInitReal16: + mov di, 'BP' + jmp short Main16 + +; +; @param[out] DI 'AP' to indicate application processor +; +EarlyApInitReal16: + mov di, 'AP' + jmp short Main16 + +; +; Modified: EAX +; +; @param[in] EAX Initial value of the EAX register (BIST: Built-in Self Test) +; @param[out] ESP Initial value of the EAX register (BIST: Built-in Self Test) +; +EarlyInit16: + ; + ; ESP - Initial value of the EAX register (BIST: Built-in Self Test) + ; + mov esp, eax + + debugInitialize + + OneTimeCallRet EarlyInit16 + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm new file mode 100644 index 00000000..fefe2a41 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm @@ -0,0 +1,142 @@ +;------------------------------------------------------------------------------ +; @file +; Transition from 16 bit real mode into 32 bit flat protected mode +; +; Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +%define SEC_DEFAULT_CR0 0x40000023 +%define SEC_DEFAULT_CR4 0x640 + +BITS 16 + +; +; Modified: EAX, EBX +; +; @param[out] DS Selector allowing flat access to all addresses +; @param[out] ES Selector allowing flat access to all addresses +; @param[out] FS Selector allowing flat access to all addresses +; @param[out] GS Selector allowing flat access to all addresses +; @param[out] SS Selector allowing flat access to all addresses +; +TransitionFromReal16To32BitFlat: + + debugShowPostCode POSTCODE_16BIT_MODE + + cli + + mov bx, 0xf000 + mov ds, bx + + mov bx, ADDR16_OF(gdtr) + +o32 lgdt [cs:bx] + + mov eax, SEC_DEFAULT_CR0 + mov cr0, eax + + jmp LINEAR_CODE_SEL:dword ADDR_OF(jumpTo32BitAndLandHere) +BITS 32 +jumpTo32BitAndLandHere: + + mov eax, SEC_DEFAULT_CR4 + mov cr4, eax + + debugShowPostCode POSTCODE_32BIT_MODE + + mov ax, LINEAR_SEL + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + OneTimeCallRet TransitionFromReal16To32BitFlat + +ALIGN 2 + +gdtr: + dw GDT_END - GDT_BASE - 1 ; GDT limit + dd ADDR_OF(GDT_BASE) + +ALIGN 16 + +; +; Macros for GDT entries +; + +%define PRESENT_FLAG(p) (p << 7) +%define DPL(dpl) (dpl << 5) +%define SYSTEM_FLAG(s) (s << 4) +%define DESC_TYPE(t) (t) + +; Type: data, expand-up, writable, accessed +%define DATA32_TYPE 3 + +; Type: execute, readable, expand-up, accessed +%define CODE32_TYPE 0xb + +; Type: execute, readable, expand-up, accessed +%define CODE64_TYPE 0xb + +%define GRANULARITY_FLAG(g) (g << 7) +%define DEFAULT_SIZE32(d) (d << 6) +%define CODE64_FLAG(l) (l << 5) +%define UPPER_LIMIT(l) (l) + +; +; The Global Descriptor Table (GDT) +; + +GDT_BASE: +; null descriptor +NULL_SEL equ $-GDT_BASE + DW 0 ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB 0 ; sys flag, dpl, type + DB 0 ; limit 19:16, flags + DB 0 ; base 31:24 + +; linear data segment descriptor +LINEAR_SEL equ $-GDT_BASE + DW 0xffff ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB PRESENT_FLAG(1)|DPL(0)|SYSTEM_FLAG(1)|DESC_TYPE(DATA32_TYPE) + DB GRANULARITY_FLAG(1)|DEFAULT_SIZE32(1)|CODE64_FLAG(0)|UPPER_LIMIT(0xf) + DB 0 ; base 31:24 + +; linear code segment descriptor +LINEAR_CODE_SEL equ $-GDT_BASE + DW 0xffff ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB PRESENT_FLAG(1)|DPL(0)|SYSTEM_FLAG(1)|DESC_TYPE(CODE32_TYPE) + DB GRANULARITY_FLAG(1)|DEFAULT_SIZE32(1)|CODE64_FLAG(0)|UPPER_LIMIT(0xf) + DB 0 ; base 31:24 + +%ifdef ARCH_X64 +; linear code (64-bit) segment descriptor +LINEAR_CODE64_SEL equ $-GDT_BASE + DW 0xffff ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB PRESENT_FLAG(1)|DPL(0)|SYSTEM_FLAG(1)|DESC_TYPE(CODE64_TYPE) + DB GRANULARITY_FLAG(1)|DEFAULT_SIZE32(0)|CODE64_FLAG(1)|UPPER_LIMIT(0xf) + DB 0 ; base 31:24 +%endif + +; linear code segment descriptor +LINEAR_CODE16_SEL equ $-GDT_BASE + DW 0xffff ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB PRESENT_FLAG(1)|DPL(0)|SYSTEM_FLAG(1)|DESC_TYPE(CODE32_TYPE) + DB GRANULARITY_FLAG(1)|DEFAULT_SIZE32(0)|CODE64_FLAG(0)|UPPER_LIMIT(0xf) + DB 0 ; base 31:24 + +GDT_END: + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia16/ResetVectorVtf0.asm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia16/ResetVectorVtf0.asm new file mode 100644 index 00000000..cee5c855 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia16/ResetVectorVtf0.asm @@ -0,0 +1,65 @@ +;------------------------------------------------------------------------------ +; @file +; First code executed by processor after resetting. +; +; Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +BITS 16 + +ALIGN 16 + +; +; Pad the image size to 4k when page tables are in VTF0 +; +; If the VTF0 image has page tables built in, then we need to make +; sure the end of VTF0 is 4k above where the page tables end. +; +; This is required so the page tables will be 4k aligned when VTF0 is +; located just below 0x100000000 (4GB) in the firmware device. +; +%ifdef ALIGN_TOP_TO_4K_FOR_PAGING + TIMES (0x1000 - ($ - EndOfPageTables) - 0x20) DB 0 +%endif + +applicationProcessorEntryPoint: +; +; Application Processors entry point +; +; GenFv generates code aligned on a 4k boundary which will jump to this +; location. (0xffffffe0) This allows the Local APIC Startup IPI to be +; used to wake up the application processors. +; + jmp EarlyApInitReal16 + +ALIGN 8 + + DD 0 + +; +; The VTF signature +; +; VTF-0 means that the VTF (Volume Top File) code does not require +; any fixups. +; +vtfSignature: + DB 'V', 'T', 'F', 0 + +ALIGN 16 + +resetVector: +; +; Reset Vector +; +; This is where the processor will begin execution +; + nop + nop + jmp EarlyBspInitReal16 + +ALIGN 16 + +fourGigabytes: + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia32/Flat32ToFlat64.asm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia32/Flat32ToFlat64.asm new file mode 100644 index 00000000..f813c564 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia32/Flat32ToFlat64.asm @@ -0,0 +1,39 @@ +;------------------------------------------------------------------------------ +; @file +; Transition from 32 bit flat protected mode into 64 bit flat protected mode +; +; Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +BITS 32 + +; +; Modified: EAX +; +Transition32FlatTo64Flat: + + OneTimeCall SetCr3ForPageTables64 + + mov eax, cr4 + bts eax, 5 ; enable PAE + mov cr4, eax + + mov ecx, 0xc0000080 + rdmsr + bts eax, 8 ; set LME + wrmsr + + mov eax, cr0 + bts eax, 31 ; set PG + mov cr0, eax ; enable paging + + jmp LINEAR_CODE64_SEL:ADDR_OF(jumpTo64BitAndLandHere) +BITS 64 +jumpTo64BitAndLandHere: + + debugShowPostCode POSTCODE_64BIT_MODE + + OneTimeCallRet Transition32FlatTo64Flat + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia32/PageTables64.asm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia32/PageTables64.asm new file mode 100644 index 00000000..d4e9f762 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia32/PageTables64.asm @@ -0,0 +1,24 @@ +;------------------------------------------------------------------------------ +; @file +; Sets the CR3 register for 64-bit paging +; +; Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +BITS 32 + +; +; Modified: EAX +; +SetCr3ForPageTables64: + + ; + ; These pages are built into the ROM image in X64/PageTables.asm + ; + mov eax, ADDR_OF(TopLevelPageDirectory) + mov cr3, eax + + OneTimeCallRet SetCr3ForPageTables64 + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia32/SearchForBfvBase.asm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia32/SearchForBfvBase.asm new file mode 100644 index 00000000..e1c8344d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia32/SearchForBfvBase.asm @@ -0,0 +1,80 @@ +;------------------------------------------------------------------------------ +; @file +; Search for the Boot Firmware Volume (BFV) base address +; +; Copyright (c) 2008 - 2009, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +;#define EFI_FIRMWARE_FILE_SYSTEM2_GUID \ +; { 0x8c8ce578, 0x8a3d, 0x4f1c, { 0x99, 0x35, 0x89, 0x61, 0x85, 0xc3, 0x2d, 0xd3 } } +%define FFS_GUID_DWORD0 0x8c8ce578 +%define FFS_GUID_DWORD1 0x4f1c8a3d +%define FFS_GUID_DWORD2 0x61893599 +%define FFS_GUID_DWORD3 0xd32dc385 + +BITS 32 + +; +; Modified: EAX, EBX +; Preserved: EDI, ESP +; +; @param[out] EBP Address of Boot Firmware Volume (BFV) +; +Flat32SearchForBfvBase: + + xor eax, eax +searchingForBfvHeaderLoop: + ; + ; We check for a firmware volume at every 4KB address in the top 16MB + ; just below 4GB. (Addresses at 0xffHHH000 where H is any hex digit.) + ; + sub eax, 0x1000 + cmp eax, 0xff000000 + jb searchedForBfvHeaderButNotFound + + ; + ; Check FFS GUID + ; + cmp dword [eax + 0x10], FFS_GUID_DWORD0 + jne searchingForBfvHeaderLoop + cmp dword [eax + 0x14], FFS_GUID_DWORD1 + jne searchingForBfvHeaderLoop + cmp dword [eax + 0x18], FFS_GUID_DWORD2 + jne searchingForBfvHeaderLoop + cmp dword [eax + 0x1c], FFS_GUID_DWORD3 + jne searchingForBfvHeaderLoop + + ; + ; Check FV Length + ; + cmp dword [eax + 0x24], 0 + jne searchingForBfvHeaderLoop + mov ebx, eax + add ebx, dword [eax + 0x20] + jnz searchingForBfvHeaderLoop + + jmp searchedForBfvHeaderAndItWasFound + +searchedForBfvHeaderButNotFound: + ; + ; Hang if the SEC entry point was not found + ; + debugShowPostCode POSTCODE_BFV_NOT_FOUND + + ; + ; 0xbfbfbfbf in the EAX & EBP registers helps signal what failed + ; for debugging purposes. + ; + mov eax, 0xBFBFBFBF + mov ebp, eax + jmp $ + +searchedForBfvHeaderAndItWasFound: + mov ebp, eax + + debugShowPostCode POSTCODE_BFV_FOUND + + OneTimeCallRet Flat32SearchForBfvBase + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia32/SearchForSecEntry.asm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia32/SearchForSecEntry.asm new file mode 100644 index 00000000..53826507 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Ia32/SearchForSecEntry.asm @@ -0,0 +1,194 @@ +;------------------------------------------------------------------------------ +; @file +; Search for the SEC Core entry point +; +; Copyright (c) 2008 - 2011, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +BITS 32 + +%define EFI_FV_FILETYPE_SECURITY_CORE 0x03 + +; +; Modified: EAX, EBX, ECX, EDX +; Preserved: EDI, EBP, ESP +; +; @param[in] EBP Address of Boot Firmware Volume (BFV) +; @param[out] ESI SEC Core Entry Point Address +; +Flat32SearchForSecEntryPoint: + + ; + ; Initialize EBP and ESI to 0 + ; + xor ebx, ebx + mov esi, ebx + + ; + ; Pass over the BFV header + ; + mov eax, ebp + mov bx, [ebp + 0x30] + add eax, ebx + jc secEntryPointWasNotFound + + jmp searchingForFfsFileHeaderLoop + +moveForwardWhileSearchingForFfsFileHeaderLoop: + ; + ; Make forward progress in the search + ; + inc eax + jc secEntryPointWasNotFound + +searchingForFfsFileHeaderLoop: + test eax, eax + jz secEntryPointWasNotFound + + ; + ; Ensure 8 byte alignment + ; + add eax, 7 + jc secEntryPointWasNotFound + and al, 0xf8 + + ; + ; Look to see if there is an FFS file at eax + ; + mov bl, [eax + 0x17] + test bl, 0x20 + jz moveForwardWhileSearchingForFfsFileHeaderLoop + mov ecx, [eax + 0x14] + and ecx, 0x00ffffff + or ecx, ecx + jz moveForwardWhileSearchingForFfsFileHeaderLoop + add ecx, eax + jz jumpSinceWeFoundTheLastFfsFile + jc moveForwardWhileSearchingForFfsFileHeaderLoop +jumpSinceWeFoundTheLastFfsFile: + + ; + ; There seems to be a valid file at eax + ; + cmp byte [eax + 0x12], EFI_FV_FILETYPE_SECURITY_CORE ; Check File Type + jne readyToTryFfsFileAtEcx + +fileTypeIsSecCore: + OneTimeCall GetEntryPointOfFfsFile + test eax, eax + jnz doneSeachingForSecEntryPoint + +readyToTryFfsFileAtEcx: + ; + ; Try the next FFS file at ECX + ; + mov eax, ecx + jmp searchingForFfsFileHeaderLoop + +secEntryPointWasNotFound: + xor eax, eax + +doneSeachingForSecEntryPoint: + mov esi, eax + + test esi, esi + jnz secCoreEntryPointWasFound + +secCoreEntryPointWasNotFound: + ; + ; Hang if the SEC entry point was not found + ; + debugShowPostCode POSTCODE_SEC_NOT_FOUND + jz $ + +secCoreEntryPointWasFound: + debugShowPostCode POSTCODE_SEC_FOUND + + OneTimeCallRet Flat32SearchForSecEntryPoint + +%define EFI_SECTION_PE32 0x10 +%define EFI_SECTION_TE 0x12 + +; +; Input: +; EAX - Start of FFS file +; ECX - End of FFS file +; +; Output: +; EAX - Entry point of PE32 (or 0 if not found) +; +; Modified: +; EBX +; +GetEntryPointOfFfsFile: + test eax, eax + jz getEntryPointOfFfsFileErrorReturn + add eax, 0x18 ; EAX = Start of section + +getEntryPointOfFfsFileLoopForSections: + cmp eax, ecx + jae getEntryPointOfFfsFileErrorReturn + + cmp byte [eax + 3], EFI_SECTION_PE32 + je getEntryPointOfFfsFileFoundPe32Section + + cmp byte [eax + 3], EFI_SECTION_TE + je getEntryPointOfFfsFileFoundTeSection + + ; + ; The section type was not PE32 or TE, so move to next section + ; + mov ebx, dword [eax] + and ebx, 0x00ffffff + add eax, ebx + jc getEntryPointOfFfsFileErrorReturn + + ; + ; Ensure that FFS section is 32-bit aligned + ; + add eax, 3 + jc getEntryPointOfFfsFileErrorReturn + and al, 0xfc + jmp getEntryPointOfFfsFileLoopForSections + +getEntryPointOfFfsFileFoundPe32Section: + add eax, 4 ; EAX = Start of PE32 image + + cmp word [eax], 'MZ' + jne getEntryPointOfFfsFileErrorReturn + movzx ebx, word [eax + 0x3c] + add ebx, eax + + ; if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) + cmp dword [ebx], `PE\x00\x00` + jne getEntryPointOfFfsFileErrorReturn + + ; *EntryPoint = (VOID *)((UINTN)Pe32Data + + ; (UINTN)(Hdr.Pe32->OptionalHeader.AddressOfEntryPoint & 0x0ffffffff)); + add eax, [ebx + 0x4 + 0x14 + 0x10] + jmp getEntryPointOfFfsFileReturn + +getEntryPointOfFfsFileFoundTeSection: + add eax, 4 ; EAX = Start of TE image + mov ebx, eax + + ; if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) + cmp word [ebx], 'VZ' + jne getEntryPointOfFfsFileErrorReturn + ; *EntryPoint = (VOID *)((UINTN)Pe32Data + + ; (UINTN)(Hdr.Te->AddressOfEntryPoint & 0x0ffffffff) + + ; sizeof(EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize); + add eax, [ebx + 0x8] + add eax, 0x28 + movzx ebx, word [ebx + 0x6] + sub eax, ebx + jmp getEntryPointOfFfsFileReturn + +getEntryPointOfFfsFileErrorReturn: + mov eax, 0 + +getEntryPointOfFfsFileReturn: + OneTimeCallRet GetEntryPointOfFfsFile + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Main.asm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Main.asm new file mode 100644 index 00000000..dbebfb9e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Main.asm @@ -0,0 +1,105 @@ +;------------------------------------------------------------------------------ +; @file +; Main routine of the pre-SEC code up through the jump into SEC +; +; Copyright (c) 2008 - 2009, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + + +BITS 16 + +; +; Modified: EBX, ECX, EDX, EBP +; +; @param[in,out] RAX/EAX Initial value of the EAX register +; (BIST: Built-in Self Test) +; @param[in,out] DI 'BP': boot-strap processor, or +; 'AP': application processor +; @param[out] RBP/EBP Address of Boot Firmware Volume (BFV) +; @param[out] DS Selector allowing flat access to all addresses +; @param[out] ES Selector allowing flat access to all addresses +; @param[out] FS Selector allowing flat access to all addresses +; @param[out] GS Selector allowing flat access to all addresses +; @param[out] SS Selector allowing flat access to all addresses +; +; @return None This routine jumps to SEC and does not return +; +Main16: + OneTimeCall EarlyInit16 + + ; + ; Transition the processor from 16-bit real mode to 32-bit flat mode + ; + OneTimeCall TransitionFromReal16To32BitFlat + +BITS 32 + + ; + ; Search for the Boot Firmware Volume (BFV) + ; + OneTimeCall Flat32SearchForBfvBase + + ; + ; EBP - Start of BFV + ; + + ; + ; Search for the SEC entry point + ; + OneTimeCall Flat32SearchForSecEntryPoint + + ; + ; ESI - SEC Core entry point + ; EBP - Start of BFV + ; + +%ifdef ARCH_IA32 + + ; + ; Restore initial EAX value into the EAX register + ; + mov eax, esp + + ; + ; Jump to the 32-bit SEC entry point + ; + jmp esi + +%else + + ; + ; Transition the processor from 32-bit flat mode to 64-bit flat mode + ; + OneTimeCall Transition32FlatTo64Flat + +BITS 64 + + ; + ; Some values were calculated in 32-bit mode. Make sure the upper + ; 32-bits of 64-bit registers are zero for these values. + ; + mov rax, 0x00000000ffffffff + and rsi, rax + and rbp, rax + and rsp, rax + + ; + ; RSI - SEC Core entry point + ; RBP - Start of BFV + ; + + ; + ; Restore initial EAX value into the RAX register + ; + mov rax, rsp + + ; + ; Jump to the 64-bit SEC entry point + ; + jmp rsi + +%endif + + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Port80Debug.asm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Port80Debug.asm new file mode 100644 index 00000000..06b1fafa --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Port80Debug.asm @@ -0,0 +1,22 @@ +;------------------------------------------------------------------------------ +; @file +; Port 0x80 debug support macros +; +; Copyright (c) 2009, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +BITS 16 + +%macro debugInitialize 0 + ; + ; No initialization is required + ; +%endmacro + +%macro debugShowPostCode 1 + mov al, %1 + out 0x80, al +%endmacro + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/PostCodes.inc b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/PostCodes.inc new file mode 100644 index 00000000..4eb7cf8f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/PostCodes.inc @@ -0,0 +1,19 @@ +;------------------------------------------------------------------------------ +; @file +; Definitions of POST CODES for the reset vector module +; +; Copyright (c) 2009, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +%define POSTCODE_16BIT_MODE 0x16 +%define POSTCODE_32BIT_MODE 0x32 +%define POSTCODE_64BIT_MODE 0x64 + +%define POSTCODE_BFV_NOT_FOUND 0xb0 +%define POSTCODE_BFV_FOUND 0xb1 + +%define POSTCODE_SEC_NOT_FOUND 0xf0 +%define POSTCODE_SEC_FOUND 0xf1 + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/ReadMe.txt b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/ReadMe.txt new file mode 100644 index 00000000..cea22457 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/ReadMe.txt @@ -0,0 +1,41 @@ + +=== HOW TO USE VTF0 === + +Add this line to your FDF FV section: +INF RuleOverride=RESET_VECTOR USE = IA32 UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.inf +(For X64 SEC/PEI change IA32 to X64 => 'USE = X64') + +In your FDF FFS file rules sections add: +[Rule.Common.SEC.RESET_VECTOR] + FILE RAW = $(NAMED_GUID) { + RAW RAW |.raw + } + +=== VTF0 Boot Flow === + +1. Transition to IA32 flat mode +2. Locate BFV (Boot Firmware Volume) by checking every 4kb boundary +3. Locate SEC image +4. X64 VTF0 transitions to X64 mode +5. Call SEC image entry point + +== VTF0 SEC input parameters == + +All inputs to SEC image are register based: +EAX/RAX - Initial value of the EAX register (BIST: Built-in Self Test) +DI - 'BP': boot-strap processor, or 'AP': application processor +EBP/RBP - Pointer to the start of the Boot Firmware Volume + +=== HOW TO BUILD VTF0 === + +Dependencies: +* Python 2.5~2.7 +* Nasm 2.03 or newer + +To rebuild the VTF0 binaries: +1. Change to VTF0 source dir: UefiCpuPkg/ResetVector/Vtf0 +2. nasm and python should be in executable path +3. Run this command: + python Build.py +4. Binaries output will be in UefiCpuPkg/ResetVector/Vtf0/Bin + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/ResetVector.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/ResetVector.uni new file mode 100644 index 00000000..afdb5de6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/ResetVector.uni @@ -0,0 +1,16 @@ +// /** @file
+// Reset Vector
+//
+// Reset Vector
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Reset Vector"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Reset Vector"
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/ResetVectorExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/ResetVectorExtra.uni new file mode 100644 index 00000000..7d2d5793 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/ResetVectorExtra.uni @@ -0,0 +1,12 @@ +// /** @file
+// ResetVector Localized Strings and Content
+//
+// Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME #language en-US "ResetVector module"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/SerialDebug.asm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/SerialDebug.asm new file mode 100644 index 00000000..b908aaf7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/SerialDebug.asm @@ -0,0 +1,126 @@ +;------------------------------------------------------------------------------ +; @file +; Serial port debug support macros +; +; Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +;//--------------------------------------------- +;// UART Register Offsets +;//--------------------------------------------- +%define BAUD_LOW_OFFSET 0x00 +%define BAUD_HIGH_OFFSET 0x01 +%define IER_OFFSET 0x01 +%define LCR_SHADOW_OFFSET 0x01 +%define FCR_SHADOW_OFFSET 0x02 +%define IR_CONTROL_OFFSET 0x02 +%define FCR_OFFSET 0x02 +%define EIR_OFFSET 0x02 +%define BSR_OFFSET 0x03 +%define LCR_OFFSET 0x03 +%define MCR_OFFSET 0x04 +%define LSR_OFFSET 0x05 +%define MSR_OFFSET 0x06 + +;//--------------------------------------------- +;// UART Register Bit Defines +;//--------------------------------------------- +%define LSR_TXRDY 0x20 +%define LSR_RXDA 0x01 +%define DLAB 0x01 + +; UINT16 gComBase = 0x3f8; +; UINTN gBps = 115200; +; UINT8 gData = 8; +; UINT8 gStop = 1; +; UINT8 gParity = 0; +; UINT8 gBreakSet = 0; + +%define DEFAULT_COM_BASE 0x3f8 +%define DEFAULT_BPS 115200 +%define DEFAULT_DATA 8 +%define DEFAULT_STOP 1 +%define DEFAULT_PARITY 0 +%define DEFAULT_BREAK_SET 0 + +%define SERIAL_DEFAULT_LCR ( \ + (DEFAULT_BREAK_SET << 6) | \ + (DEFAULT_PARITY << 3) | \ + (DEFAULT_STOP << 2) | \ + (DEFAULT_DATA - 5) \ + ) + +%define SERIAL_PORT_IO_BASE_ADDRESS DEFAULT_COM_BASE + +%macro inFromSerialPort 1 + mov dx, (SERIAL_PORT_IO_BASE_ADDRESS + %1) + in al, dx +%endmacro + +%macro waitForSerialTxReady 0 + +%%waitingForTx: + inFromSerialPort LSR_OFFSET + test al, LSR_TXRDY + jz %%waitingForTx + +%endmacro + +%macro outToSerialPort 2 + mov dx, (SERIAL_PORT_IO_BASE_ADDRESS + %1) + mov al, %2 + out dx, al +%endmacro + +%macro debugShowCharacter 1 + waitForSerialTxReady + outToSerialPort 0, %1 +%endmacro + +%macro debugShowHexDigit 1 + %if (%1 < 0xa) + debugShowCharacter BYTE ('0' + (%1)) + %else + debugShowCharacter BYTE ('a' + ((%1) - 0xa)) + %endif +%endmacro + +%macro debugNewline 0 + debugShowCharacter `\r` + debugShowCharacter `\n` +%endmacro + +%macro debugShowPostCode 1 + debugShowHexDigit (((%1) >> 4) & 0xf) + debugShowHexDigit ((%1) & 0xf) + debugNewline +%endmacro + +BITS 16 + +%macro debugInitialize 0 + jmp real16InitDebug +real16InitDebugReturn: +%endmacro + +real16InitDebug: + ; + ; Set communications format + ; + outToSerialPort LCR_OFFSET, ((DLAB << 7) | SERIAL_DEFAULT_LCR) + + ; + ; Configure baud rate + ; + outToSerialPort BAUD_HIGH_OFFSET, ((115200 / DEFAULT_BPS) >> 8) + outToSerialPort BAUD_LOW_OFFSET, ((115200 / DEFAULT_BPS) & 0xff) + + ; + ; Switch back to bank 0 + ; + outToSerialPort LCR_OFFSET, SERIAL_DEFAULT_LCR + + jmp real16InitDebugReturn + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Tools/FixupForRawSection.py b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Tools/FixupForRawSection.py new file mode 100644 index 00000000..0d1878c4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Tools/FixupForRawSection.py @@ -0,0 +1,22 @@ +## @file +# Apply fixup to VTF binary image for FFS Raw section +# +# Copyright (c) 2008, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +import sys + + +d = open(sys.argv[1], 'rb').read() +c = ((len(d) + 4 + 7) & ~7) - 4 +if c > len(d): + c -= len(d) + # VBox begin + # Original: f = open(sys.argv[1], 'wb'), changed to: + f = open(sys.argv[2], 'wb') + # VBox end + f.write('\x90' * c) + f.write(d) + f.close() diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Vtf0.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Vtf0.inf new file mode 100644 index 00000000..242d31ef --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Vtf0.inf @@ -0,0 +1,31 @@ +## @file +# Reset Vector +# +# Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ResetVector + FILE_GUID = 1BA0062E-C779-4582-8566-336AE8F78F09 + MODULE_TYPE = SEC + VERSION_STRING = 1.1 + MODULE_UNI_FILE = ResetVector.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + Vtf0.nasmb + +[Packages] + MdePkg/MdePkg.dec + +[UserExtensions.TianoCore."ExtraFiles"] + ResetVectorExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Vtf0.nasmb b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Vtf0.nasmb new file mode 100644 index 00000000..d768d08e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/Vtf0.nasmb @@ -0,0 +1,64 @@ +;------------------------------------------------------------------------------ +; @file +; This file includes all other code files to assemble the reset vector code +; +; Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +; +; If neither ARCH_IA32 nor ARCH_X64 are defined, then try to include +; Base.h to use the C pre-processor to determine the architecture. +; +%ifndef ARCH_IA32 + %ifndef ARCH_X64 + #include <Base.h> + #if defined (MDE_CPU_IA32) + %define ARCH_IA32 + #elif defined (MDE_CPU_X64) + %define ARCH_X64 + #endif + %endif +%endif + +%ifdef ARCH_IA32 + %ifdef ARCH_X64 + %error "Only one of ARCH_IA32 or ARCH_X64 can be defined." + %endif +%elifdef ARCH_X64 +%else + %error "Either ARCH_IA32 or ARCH_X64 must be defined." +%endif + +%include "CommonMacros.inc" + +%include "PostCodes.inc" + +%ifdef ARCH_X64 +%include "X64/PageTables.asm" +%endif + +%ifdef DEBUG_PORT80 + %include "Port80Debug.asm" +%elifdef DEBUG_SERIAL + %include "SerialDebug.asm" +%else + %include "DebugDisabled.asm" +%endif + +%include "Ia32/SearchForBfvBase.asm" +%include "Ia32/SearchForSecEntry.asm" + +%ifdef ARCH_X64 +%include "Ia32/Flat32ToFlat64.asm" +%include "Ia32/PageTables64.asm" +%endif + +%include "Ia16/Real16ToFlat32.asm" +%include "Ia16/Init16.asm" + +%include "Main.asm" + +%include "Ia16/ResetVectorVtf0.asm" + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/X64/PageTables.asm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/X64/PageTables.asm new file mode 100644 index 00000000..c58e9e3d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/ResetVector/Vtf0/X64/PageTables.asm @@ -0,0 +1,72 @@ +;------------------------------------------------------------------------------ +; @file +; Emits Page Tables for 1:1 mapping of the addresses 0 - 0x100000000 (4GB) +; +; Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +BITS 64 + +%define ALIGN_TOP_TO_4K_FOR_PAGING + +%define PAGE_PRESENT 0x01 +%define PAGE_READ_WRITE 0x02 +%define PAGE_USER_SUPERVISOR 0x04 +%define PAGE_WRITE_THROUGH 0x08 +%define PAGE_CACHE_DISABLE 0x010 +%define PAGE_ACCESSED 0x020 +%define PAGE_DIRTY 0x040 +%define PAGE_PAT 0x080 +%define PAGE_GLOBAL 0x0100 +%define PAGE_2M_MBO 0x080 +%define PAGE_2M_PAT 0x01000 + +%define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \ + PAGE_ACCESSED + \ + PAGE_DIRTY + \ + PAGE_READ_WRITE + \ + PAGE_PRESENT) + +%define PAGE_PDP_ATTR (PAGE_ACCESSED + \ + PAGE_READ_WRITE + \ + PAGE_PRESENT) + +%define PGTBLS_OFFSET(x) ((x) - TopLevelPageDirectory) +%define PGTBLS_ADDR(x) (ADDR_OF(TopLevelPageDirectory) + (x)) + +%define PDP(offset) (ADDR_OF(TopLevelPageDirectory) + (offset) + \ + PAGE_PDP_ATTR) +%define PTE_2MB(x) ((x << 21) + PAGE_2M_PDE_ATTR) + +TopLevelPageDirectory: + + ; + ; Top level Page Directory Pointers (1 * 512GB entry) + ; + DQ PDP(0x1000) + + + ; + ; Next level Page Directory Pointers (4 * 1GB entries => 4GB) + ; + TIMES 0x1000-PGTBLS_OFFSET($) DB 0 + + DQ PDP(0x2000) + DQ PDP(0x3000) + DQ PDP(0x4000) + DQ PDP(0x5000) + + ; + ; Page Table Entries (2048 * 2MB entries => 4GB) + ; + TIMES 0x2000-PGTBLS_OFFSET($) DB 0 + +%assign i 0 +%rep 0x800 + DQ PTE_2MB(i) + %assign i i+1 +%endrep + +EndOfPageTables: diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/FindPeiCore.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/FindPeiCore.c new file mode 100644 index 00000000..ef9420bf --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/FindPeiCore.c @@ -0,0 +1,190 @@ +/** @file + Locate the entry point for the PEI Core + + Copyright (c) 2008 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiPei.h> + +#include "SecMain.h" + +/** + Find core image base. + + @param FirmwareVolumePtr Point to the firmware volume for finding core image. + @param FileType The FileType for searching, either SecCore or PeiCore. + @param CoreImageBase The base address of the core image. + +**/ +EFI_STATUS +EFIAPI +FindImageBase ( + IN EFI_FIRMWARE_VOLUME_HEADER *FirmwareVolumePtr, + IN EFI_FV_FILETYPE FileType, + OUT EFI_PHYSICAL_ADDRESS *CoreImageBase + ) +{ + EFI_PHYSICAL_ADDRESS CurrentAddress; + EFI_PHYSICAL_ADDRESS EndOfFirmwareVolume; + EFI_FFS_FILE_HEADER *File; + UINT32 Size; + EFI_PHYSICAL_ADDRESS EndOfFile; + EFI_COMMON_SECTION_HEADER *Section; + EFI_PHYSICAL_ADDRESS EndOfSection; + + *CoreImageBase = 0; + + CurrentAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) FirmwareVolumePtr; + EndOfFirmwareVolume = CurrentAddress + FirmwareVolumePtr->FvLength; + + // + // Loop through the FFS files in the Boot Firmware Volume + // + for (EndOfFile = CurrentAddress + FirmwareVolumePtr->HeaderLength; ; ) { + + CurrentAddress = (EndOfFile + 7) & 0xfffffffffffffff8ULL; + if (CurrentAddress > EndOfFirmwareVolume) { + return EFI_NOT_FOUND; + } + + File = (EFI_FFS_FILE_HEADER*)(UINTN) CurrentAddress; + if (IS_FFS_FILE2 (File)) { + Size = FFS_FILE2_SIZE (File); + if (Size <= 0x00FFFFFF) { + return EFI_NOT_FOUND; + } + } else { + Size = FFS_FILE_SIZE (File); + if (Size < sizeof (EFI_FFS_FILE_HEADER)) { + return EFI_NOT_FOUND; + } + } + + EndOfFile = CurrentAddress + Size; + if (EndOfFile > EndOfFirmwareVolume) { + return EFI_NOT_FOUND; + } + + // + // Look for particular Core file (either SEC Core or PEI Core) + // + if (File->Type != FileType) { + continue; + } + + // + // Loop through the FFS file sections within the FFS file + // + if (IS_FFS_FILE2 (File)) { + EndOfSection = (EFI_PHYSICAL_ADDRESS) (UINTN) ((UINT8 *) File + sizeof (EFI_FFS_FILE_HEADER2)); + } else { + EndOfSection = (EFI_PHYSICAL_ADDRESS) (UINTN) ((UINT8 *) File + sizeof (EFI_FFS_FILE_HEADER)); + } + for (;;) { + CurrentAddress = (EndOfSection + 3) & 0xfffffffffffffffcULL; + Section = (EFI_COMMON_SECTION_HEADER*)(UINTN) CurrentAddress; + + if (IS_SECTION2 (Section)) { + Size = SECTION2_SIZE (Section); + if (Size <= 0x00FFFFFF) { + return EFI_NOT_FOUND; + } + } else { + Size = SECTION_SIZE (Section); + if (Size < sizeof (EFI_COMMON_SECTION_HEADER)) { + return EFI_NOT_FOUND; + } + } + + EndOfSection = CurrentAddress + Size; + if (EndOfSection > EndOfFile) { + return EFI_NOT_FOUND; + } + + // + // Look for executable sections + // + if (Section->Type == EFI_SECTION_PE32 || Section->Type == EFI_SECTION_TE) { + if (File->Type == FileType) { + if (IS_SECTION2 (Section)) { + *CoreImageBase = (PHYSICAL_ADDRESS) (UINTN) ((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER2)); + } else { + *CoreImageBase = (PHYSICAL_ADDRESS) (UINTN) ((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER)); + } + } + break; + } + } + + // + // Either SEC Core or PEI Core images found + // + if (*CoreImageBase != 0) { + return EFI_SUCCESS; + } + } +} + +/** + Find and return Pei Core entry point. + + It also find SEC and PEI Core file debug information. It will report them if + remote debug is enabled. + + @param SecCoreFirmwareVolumePtr Point to the firmware volume for finding SecCore. + @param PeiCoreFirmwareVolumePtr Point to the firmware volume for finding PeiCore. + @param PeiCoreEntryPoint The entry point of the PEI core. + +**/ +VOID +EFIAPI +FindAndReportEntryPoints ( + IN EFI_FIRMWARE_VOLUME_HEADER *SecCoreFirmwareVolumePtr, + IN EFI_FIRMWARE_VOLUME_HEADER *PeiCoreFirmwareVolumePtr, + OUT EFI_PEI_CORE_ENTRY_POINT *PeiCoreEntryPoint + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS SecCoreImageBase; + EFI_PHYSICAL_ADDRESS PeiCoreImageBase; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + + // + // Find SEC Core image base + // + Status = FindImageBase (SecCoreFirmwareVolumePtr, EFI_FV_FILETYPE_SECURITY_CORE, &SecCoreImageBase); + ASSERT_EFI_ERROR (Status); + + ZeroMem ((VOID *) &ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT)); + // + // Report SEC Core debug information when remote debug is enabled + // + ImageContext.ImageAddress = SecCoreImageBase; + ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageContext.ImageAddress); + PeCoffLoaderRelocateImageExtraAction (&ImageContext); + + // + // Find PEI Core image base + // + Status = FindImageBase (PeiCoreFirmwareVolumePtr, EFI_FV_FILETYPE_PEI_CORE, &PeiCoreImageBase); + ASSERT_EFI_ERROR (Status); + + // + // Report PEI Core debug information when remote debug is enabled + // + ImageContext.ImageAddress = PeiCoreImageBase; + ImageContext.PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageContext.ImageAddress); + PeCoffLoaderRelocateImageExtraAction (&ImageContext); + + // + // Find PEI Core entry point + // + Status = PeCoffLoaderGetEntryPoint ((VOID *) (UINTN) PeiCoreImageBase, (VOID**) PeiCoreEntryPoint); + if (EFI_ERROR (Status)) { + *PeiCoreEntryPoint = 0; + } + + return; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/Ia32/ResetVec.nasmb b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/Ia32/ResetVec.nasmb new file mode 100644 index 00000000..1d2203de --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/Ia32/ResetVec.nasmb @@ -0,0 +1,114 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) 2014, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; ResetVec.nasmb +; +; Abstract: +; +; Reset Vector Data structure +; This structure is located at 0xFFFFF000 +; +;------------------------------------------------------------------------------ + +; .stack 0x0 +; SECTION .text +USE16 + +; +; The layout of this file is fixed. The build tool makes assumption of the layout. +; + + ORG 0h + +; +; 0xFFFFF000 +; +; We enter here with CS:IP = 0xFF00:0x0000. Do a far-jump to change CS to 0xF000 +; and IP to ApStartup. +; +ApVector: + mov di, "AP" + jmp 0xF000:0xF000+ApStartup + + TIMES 0xFC0-($-$$) nop + +; +; This should be at 0xFFFFFFC0 +; + +; +; Reserved +; +ReservedData: DD 0eeeeeeeeh, 0eeeeeeeeh + + TIMES 0xFD0-($-$$) nop +; +; This is located at 0xFFFFFFD0 +; + mov di, "PA" + jmp ApStartup + + TIMES 0xFE0-($-$$) nop +; +; Pointer to the entry point of the PEI core +; It is located at 0xFFFFFFE0, and is fixed up by some build tool +; So if the value 8..1 appears in the final FD image, tool failure occurs. +; +PeiCoreEntryPoint: DD 87654321h + +; +; This is the handler for all kinds of exceptions. Since it's for debugging +; purpose only, nothing except a dead loop would be done here. Developers could +; analyze the cause of the exception if a debugger had been attached. +; +global ASM_PFX(InterruptHandler) +ASM_PFX(InterruptHandler): + jmp $ + iret + + TIMES 0xFF0-($-$$) nop +; +; For IA32, the reset vector must be at 0xFFFFFFF0, i.e., 4G-16 byte +; Execution starts here upon power-on/platform-reset. +; +ResetHandler: + nop + nop +ApStartup: + ; + ; Jmp Rel16 instruction + ; Use machine code directly in case of the assembler optimization + ; SEC entry point relative address will be fixed up by some build tool. + ; + ; Typically, SEC entry point is the function _ModuleEntryPoint() defined in + ; SecEntry.asm + ; + DB 0e9h + DW -3 + + + TIMES 0xFF8-($-$$) nop +; +; Ap reset vector segment address is at 0xFFFFFFF8 +; This will be fixed up by some build tool, +; so if the value 1..8 appears in the final FD image, +; tool failure occurs +; +ApSegAddress: dd 12345678h + + TIMES 0xFFC-($-$$) nop +; +; BFV Base is at 0xFFFFFFFC +; This will be fixed up by some build tool, +; so if the value 1..8 appears in the final FD image, +; tool failure occurs. +; +BfvBase: DD 12345678h + +; +; Nothing can go here, otherwise the layout of this file would change. +; diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecBist.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecBist.c new file mode 100644 index 00000000..ce85b565 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecBist.c @@ -0,0 +1,264 @@ +/** @file + Get SEC platform information(2) PPI and reinstall it. + + Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "SecMain.h" + +EFI_SEC_PLATFORM_INFORMATION_PPI mSecPlatformInformation = { + SecPlatformInformationBist +}; + +EFI_PEI_PPI_DESCRIPTOR mPeiSecPlatformInformation = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiSecPlatformInformationPpiGuid, + &mSecPlatformInformation +}; + +EFI_SEC_PLATFORM_INFORMATION2_PPI mSecPlatformInformation2 = { + SecPlatformInformation2Bist +}; + +EFI_PEI_PPI_DESCRIPTOR mPeiSecPlatformInformation2 = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiSecPlatformInformation2PpiGuid, + &mSecPlatformInformation2 +}; + +/** + Worker function to parse CPU BIST information from Guided HOB. + + @param[in, out] StructureSize Pointer to the variable describing size of the input buffer. + @param[in, out] StructureBuffer Pointer to the buffer save CPU BIST information. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. + +**/ +EFI_STATUS +GetBistFromHob ( + IN OUT UINT64 *StructureSize, + IN OUT VOID *StructureBuffer + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + VOID *DataInHob; + UINTN DataSize; + + GuidHob = GetFirstGuidHob (&gEfiCallerIdGuid); + if (GuidHob == NULL) { + *StructureSize = 0; + return EFI_SUCCESS; + } + + DataInHob = GET_GUID_HOB_DATA (GuidHob); + DataSize = GET_GUID_HOB_DATA_SIZE (GuidHob); + + // + // return the information from BistHob + // + if ((*StructureSize) < (UINT64) DataSize) { + *StructureSize = (UINT64) DataSize; + return EFI_BUFFER_TOO_SMALL; + } + + *StructureSize = (UINT64) DataSize; + CopyMem (StructureBuffer, DataInHob, DataSize); + return EFI_SUCCESS; +} + +/** + Implementation of the PlatformInformation service in EFI_SEC_PLATFORM_INFORMATION_PPI. + + @param[in] PeiServices Pointer to the PEI Services Table. + @param[in, out] StructureSize Pointer to the variable describing size of the input buffer. + @param[out] PlatformInformationRecord Pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformationBist ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD *PlatformInformationRecord + ) +{ + return GetBistFromHob (StructureSize, PlatformInformationRecord); +} + +/** + Implementation of the PlatformInformation2 service in EFI_SEC_PLATFORM_INFORMATION2_PPI. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in, out] StructureSize The pointer to the variable describing size of the input buffer. + @param[out] PlatformInformationRecord2 The pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD2. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. The current buffer size needed to + hold the record is returned in StructureSize. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformation2Bist ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD2 *PlatformInformationRecord2 + ) +{ + return GetBistFromHob (StructureSize, PlatformInformationRecord2); +} + +/** + Worker function to get CPUs' BIST by calling SecPlatformInformationPpi + or SecPlatformInformation2Ppi. + + @param[in] PeiServices Pointer to PEI Services Table + @param[in] Guid PPI Guid + @param[out] PpiDescriptor Return a pointer to instance of the + EFI_PEI_PPI_DESCRIPTOR + @param[out] BistInformationData Pointer to BIST information data + @param[out] BistInformationSize Return the size in bytes of BIST information + + @retval EFI_SUCCESS Retrieve of the BIST data successfully + @retval EFI_NOT_FOUND No sec platform information(2) ppi export + @retval EFI_DEVICE_ERROR Failed to get CPU Information + +**/ +EFI_STATUS +GetBistInfoFromPpi ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_GUID *Guid, + OUT EFI_PEI_PPI_DESCRIPTOR **PpiDescriptor, + OUT VOID **BistInformationData, + OUT UINT64 *BistInformationSize OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SEC_PLATFORM_INFORMATION2_PPI *SecPlatformInformation2Ppi; + EFI_SEC_PLATFORM_INFORMATION_RECORD2 *SecPlatformInformation2; + UINT64 InformationSize; + + Status = PeiServicesLocatePpi ( + Guid, // GUID + 0, // INSTANCE + PpiDescriptor, // EFI_PEI_PPI_DESCRIPTOR + (VOID **)&SecPlatformInformation2Ppi // PPI + ); + if (Status == EFI_NOT_FOUND) { + return EFI_NOT_FOUND; + } + + if (Status == EFI_SUCCESS) { + // + // Get the size of the sec platform information2(BSP/APs' BIST data) + // + InformationSize = 0; + SecPlatformInformation2 = NULL; + Status = SecPlatformInformation2Ppi->PlatformInformation2 ( + PeiServices, + &InformationSize, + SecPlatformInformation2 + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + Status = PeiServicesAllocatePool ( + (UINTN) InformationSize, + (VOID **) &SecPlatformInformation2 + ); + if (Status == EFI_SUCCESS) { + // + // Retrieve BIST data + // + Status = SecPlatformInformation2Ppi->PlatformInformation2 ( + PeiServices, + &InformationSize, + SecPlatformInformation2 + ); + if (Status == EFI_SUCCESS) { + *BistInformationData = SecPlatformInformation2; + if (BistInformationSize != NULL) { + *BistInformationSize = InformationSize; + } + return EFI_SUCCESS; + } + } + } + } + + return EFI_DEVICE_ERROR; +} + +/** + Get CPUs' BIST by calling SecPlatformInformationPpi/SecPlatformInformation2Ppi. + +**/ +VOID +RepublishSecPlatformInformationPpi ( + VOID + ) +{ + EFI_STATUS Status; + CONST EFI_PEI_SERVICES **PeiServices; + UINT64 BistInformationSize; + VOID *BistInformationData; + EFI_PEI_PPI_DESCRIPTOR *SecInformationDescriptor; + + PeiServices = GetPeiServicesTablePointer (); + Status = GetBistInfoFromPpi ( + PeiServices, + &gEfiSecPlatformInformation2PpiGuid, + &SecInformationDescriptor, + &BistInformationData, + &BistInformationSize + ); + if (Status == EFI_SUCCESS) { + BuildGuidDataHob ( + &gEfiCallerIdGuid, + BistInformationData, + (UINTN) BistInformationSize + ); + // + // The old SecPlatformInformation2 data is on temporary memory. + // After memory discovered, we should never get it from temporary memory, + // or the data will be crashed. So, we reinstall SecPlatformInformation2 PPI here. + // + Status = PeiServicesReInstallPpi ( + SecInformationDescriptor, + &mPeiSecPlatformInformation2 + ); + } if (Status == EFI_NOT_FOUND) { + Status = GetBistInfoFromPpi ( + PeiServices, + &gEfiSecPlatformInformationPpiGuid, + &SecInformationDescriptor, + &BistInformationData, + &BistInformationSize + ); + if (Status == EFI_SUCCESS) { + BuildGuidDataHob ( + &gEfiCallerIdGuid, + BistInformationData, + (UINTN) BistInformationSize + ); + // + // The old SecPlatformInformation data is on temporary memory. + // After memory discovered, we should never get it from temporary memory, + // or the data will be crashed. So, we reinstall SecPlatformInformation PPI here. + // + Status = PeiServicesReInstallPpi ( + SecInformationDescriptor, + &mPeiSecPlatformInformation + ); + } else if (Status == EFI_NOT_FOUND) { + return; + } + } + + ASSERT_EFI_ERROR(Status); +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecCore.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecCore.inf new file mode 100644 index 00000000..29322ae7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecCore.inf @@ -0,0 +1,83 @@ +## @file +# SecCore module that implements the SEC phase. +# +# This is the first module taking control of the platform upon power-on/reset. +# It implements the first phase of the security phase. The entry point function is +# _ModuleEntryPoint in PlatformSecLib. The entry point function will switch to +# protected mode, setup flat memory model, enable temporary memory and +# call into SecStartup(). +# +# Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SecCore + MODULE_UNI_FILE = SecCore.uni + FILE_GUID = 1BA0062E-C779-4582-8566-336AE8F78F09 + MODULE_TYPE = SEC + VERSION_STRING = 1.0 + + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + SecMain.c + SecMain.h + FindPeiCore.c + SecBist.c + +[Sources.IA32] + Ia32/ResetVec.nasmb + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + PlatformSecLib + PcdLib + DebugAgentLib + UefiCpuLib + PeCoffGetEntryPointLib + PeCoffExtraActionLib + CpuExceptionHandlerLib + ReportStatusCodeLib + PeiServicesLib + PeiServicesTablePointerLib + HobLib + +[Ppis] + ## SOMETIMES_CONSUMES + ## PRODUCES + gEfiSecPlatformInformationPpiGuid + ## SOMETIMES_CONSUMES + ## SOMETIMES_PRODUCES + gEfiSecPlatformInformation2PpiGuid + gEfiTemporaryRamDonePpiGuid ## PRODUCES + ## NOTIFY + ## SOMETIMES_CONSUMES + gPeiSecPerformancePpiGuid + gEfiPeiCoreFvLocationPpiGuid + ## CONSUMES + gRepublishSecPpiPpiGuid + +[Guids] + ## SOMETIMES_PRODUCES ## HOB + gEfiFirmwarePerformanceGuid + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdPeiTemporaryRamStackSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMigrateTemporaryRamFirmwareVolumes ## CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + SecCoreExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecCore.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecCore.uni new file mode 100644 index 00000000..1ecf928b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecCore.uni @@ -0,0 +1,18 @@ +// /** @file
+// SecCore module that implements the SEC phase.
+//
+// This is the first module taking control of the platform upon power-on/reset.
+// It implements the first phase of the security phase. The entry point function is
+// _ModuleEntryPoint in PlatformSecLib. The entry point function will switch to
+// protected mode, setup flat memory model, enable temporary memory and
+// call into SecStartup().
+//
+// Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "SecCore module that implements the SEC phase"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This is the first module taking control of the platform upon power-on/reset. It implements the first phase of the security phase. The entry point function is _ModuleEntryPoint in PlatformSecLib. The entry point function will switch to protected mode, will setup flat memory model, will enable temporary memory and will call into SecStartup()."
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecCoreExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecCoreExtra.uni new file mode 100644 index 00000000..4b582f1d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecCoreExtra.uni @@ -0,0 +1,12 @@ +// /** @file
+// SecCore Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"SEC Core Module"
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecMain.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecMain.c new file mode 100644 index 00000000..f8038a9f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecMain.c @@ -0,0 +1,468 @@ +/** @file + C functions in SEC + + Copyright (c) 2008 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "SecMain.h" + +EFI_PEI_TEMPORARY_RAM_DONE_PPI gSecTemporaryRamDonePpi = { + SecTemporaryRamDone +}; + +EFI_SEC_PLATFORM_INFORMATION_PPI mSecPlatformInformationPpi = { SecPlatformInformation }; + +EFI_PEI_PPI_DESCRIPTOR mPeiSecPlatformInformationPpi[] = { + { + // + // SecPerformance PPI notify descriptor. + // + EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK, + &gPeiSecPerformancePpiGuid, + (VOID *) (UINTN) SecPerformancePpiCallBack + }, + { + EFI_PEI_PPI_DESCRIPTOR_PPI, + &gEfiTemporaryRamDonePpiGuid, + &gSecTemporaryRamDonePpi + }, + { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiSecPlatformInformationPpiGuid, + &mSecPlatformInformationPpi + } +}; + +/** + Migrates the Global Descriptor Table (GDT) to permanent memory. + + @retval EFI_SUCCESS The GDT was migrated successfully. + @retval EFI_OUT_OF_RESOURCES The GDT could not be migrated due to lack of available memory. + +**/ +EFI_STATUS +MigrateGdt ( + VOID + ) +{ + EFI_STATUS Status; + UINTN GdtBufferSize; + IA32_DESCRIPTOR Gdtr; + VOID *GdtBuffer; + + AsmReadGdtr ((IA32_DESCRIPTOR *) &Gdtr); + GdtBufferSize = sizeof (IA32_SEGMENT_DESCRIPTOR) -1 + Gdtr.Limit + 1; + + Status = PeiServicesAllocatePool ( + GdtBufferSize, + &GdtBuffer + ); + ASSERT (GdtBuffer != NULL); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + GdtBuffer = ALIGN_POINTER (GdtBuffer, sizeof (IA32_SEGMENT_DESCRIPTOR)); + CopyMem (GdtBuffer, (VOID *) Gdtr.Base, Gdtr.Limit + 1); + Gdtr.Base = (UINTN) GdtBuffer; + AsmWriteGdtr (&Gdtr); + + return EFI_SUCCESS; +} + +// +// These are IDT entries pointing to 10:FFFFFFE4h. +// +UINT64 mIdtEntryTemplate = 0xffff8e000010ffe4ULL; + +/** + Caller provided function to be invoked at the end of InitializeDebugAgent(). + + Entry point to the C language phase of SEC. After the SEC assembly + code has initialized some temporary memory and set up the stack, + the control is transferred to this function. + + @param[in] Context The first input parameter of InitializeDebugAgent(). + +**/ +VOID +NORETURN +EFIAPI +SecStartupPhase2( + IN VOID *Context + ); + +/** + Entry point of the notification callback function itself within the PEIM. + It is to get SEC performance data and build HOB to convey the SEC performance + data to DXE phase. + + @param PeiServices Indirect reference to the PEI Services Table. + @param NotifyDescriptor Address of the notification descriptor data structure. + @param Ppi Address of the PPI that was installed. + + @return Status of the notification. + The status code returned from this function is ignored. +**/ +EFI_STATUS +EFIAPI +SecPerformancePpiCallBack ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ) +{ + EFI_STATUS Status; + PEI_SEC_PERFORMANCE_PPI *SecPerf; + FIRMWARE_SEC_PERFORMANCE Performance; + + SecPerf = (PEI_SEC_PERFORMANCE_PPI *) Ppi; + Status = SecPerf->GetPerformance ((CONST EFI_PEI_SERVICES **) PeiServices, SecPerf, &Performance); + if (!EFI_ERROR (Status)) { + BuildGuidDataHob ( + &gEfiFirmwarePerformanceGuid, + &Performance, + sizeof (FIRMWARE_SEC_PERFORMANCE) + ); + DEBUG ((DEBUG_INFO, "FPDT: SEC Performance Hob ResetEnd = %ld\n", Performance.ResetEnd)); + } + + return Status; +} + +/** + + Entry point to the C language phase of SEC. After the SEC assembly + code has initialized some temporary memory and set up the stack, + the control is transferred to this function. + + + @param SizeOfRam Size of the temporary memory available for use. + @param TempRamBase Base address of temporary ram + @param BootFirmwareVolume Base address of the Boot Firmware Volume. +**/ +VOID +NORETURN +EFIAPI +SecStartup ( + IN UINT32 SizeOfRam, + IN UINT32 TempRamBase, + IN VOID *BootFirmwareVolume + ) +{ + EFI_SEC_PEI_HAND_OFF SecCoreData; + IA32_DESCRIPTOR IdtDescriptor; + SEC_IDT_TABLE IdtTableInStack; + UINT32 Index; + UINT32 PeiStackSize; + EFI_STATUS Status; + + // + // Report Status Code to indicate entering SEC core + // + REPORT_STATUS_CODE ( + EFI_PROGRESS_CODE, + EFI_SOFTWARE_SEC | EFI_SW_SEC_PC_ENTRY_POINT + ); + + PeiStackSize = PcdGet32 (PcdPeiTemporaryRamStackSize); + if (PeiStackSize == 0) { + PeiStackSize = (SizeOfRam >> 1); + } + + ASSERT (PeiStackSize < SizeOfRam); + + // + // Process all libraries constructor function linked to SecCore. + // + ProcessLibraryConstructorList (); + + // + // Initialize floating point operating environment + // to be compliant with UEFI spec. + // + InitializeFloatingPointUnits (); + + // |-------------------|----> + // |IDT Table | + // |-------------------| + // |PeiService Pointer | PeiStackSize + // |-------------------| + // | | + // | Stack | + // |-------------------|----> + // | | + // | | + // | Heap | PeiTemporayRamSize + // | | + // | | + // |-------------------|----> TempRamBase + + IdtTableInStack.PeiService = 0; + for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index ++) { + CopyMem ((VOID*)&IdtTableInStack.IdtTable[Index], (VOID*)&mIdtEntryTemplate, sizeof (UINT64)); + } + + IdtDescriptor.Base = (UINTN) &IdtTableInStack.IdtTable; + IdtDescriptor.Limit = (UINT16)(sizeof (IdtTableInStack.IdtTable) - 1); + + AsmWriteIdtr (&IdtDescriptor); + + // + // Setup the default exception handlers + // + Status = InitializeCpuExceptionHandlers (NULL); + ASSERT_EFI_ERROR (Status); + + // + // Update the base address and length of Pei temporary memory + // + SecCoreData.DataSize = (UINT16) sizeof (EFI_SEC_PEI_HAND_OFF); + SecCoreData.BootFirmwareVolumeBase = BootFirmwareVolume; + SecCoreData.BootFirmwareVolumeSize = (UINTN)((EFI_FIRMWARE_VOLUME_HEADER *) BootFirmwareVolume)->FvLength; + SecCoreData.TemporaryRamBase = (VOID*)(UINTN) TempRamBase; + SecCoreData.TemporaryRamSize = SizeOfRam; + SecCoreData.PeiTemporaryRamBase = SecCoreData.TemporaryRamBase; + SecCoreData.PeiTemporaryRamSize = SizeOfRam - PeiStackSize; + SecCoreData.StackBase = (VOID*)(UINTN)(TempRamBase + SecCoreData.PeiTemporaryRamSize); + SecCoreData.StackSize = PeiStackSize; + + // + // Initialize Debug Agent to support source level debug in SEC/PEI phases before memory ready. + // + InitializeDebugAgent (DEBUG_AGENT_INIT_PREMEM_SEC, &SecCoreData, SecStartupPhase2); + + // + // Should not come here. + // + UNREACHABLE (); +} + +/** + Caller provided function to be invoked at the end of InitializeDebugAgent(). + + Entry point to the C language phase of SEC. After the SEC assembly + code has initialized some temporary memory and set up the stack, + the control is transferred to this function. + + @param[in] Context The first input parameter of InitializeDebugAgent(). + +**/ +VOID +NORETURN +EFIAPI +SecStartupPhase2( + IN VOID *Context + ) +{ + EFI_SEC_PEI_HAND_OFF *SecCoreData; + EFI_PEI_PPI_DESCRIPTOR *PpiList; + UINT32 Index; + EFI_PEI_PPI_DESCRIPTOR *AllSecPpiList; + EFI_PEI_CORE_ENTRY_POINT PeiCoreEntryPoint; + + PeiCoreEntryPoint = NULL; + SecCoreData = (EFI_SEC_PEI_HAND_OFF *) Context; + + // + // Perform platform specific initialization before entering PeiCore. + // + PpiList = SecPlatformMain (SecCoreData); + // + // Find Pei Core entry point. It will report SEC and Pei Core debug information if remote debug + // is enabled. + // + if (PpiList != NULL) { + Index = 0; + do { + if (CompareGuid (PpiList[Index].Guid, &gEfiPeiCoreFvLocationPpiGuid) && + (((EFI_PEI_CORE_FV_LOCATION_PPI *) PpiList[Index].Ppi)->PeiCoreFvLocation != 0) + ) { + // + // In this case, SecCore is in BFV but PeiCore is in another FV reported by PPI. + // + FindAndReportEntryPoints ( + (EFI_FIRMWARE_VOLUME_HEADER *) SecCoreData->BootFirmwareVolumeBase, + (EFI_FIRMWARE_VOLUME_HEADER *) ((EFI_PEI_CORE_FV_LOCATION_PPI *) PpiList[Index].Ppi)->PeiCoreFvLocation, + &PeiCoreEntryPoint + ); + if (PeiCoreEntryPoint != NULL) { + break; + } else { + // + // Invalid PeiCore FV provided by platform + // + CpuDeadLoop (); + } + } + } while ((PpiList[Index++].Flags & EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST) != EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST); + } + // + // If EFI_PEI_CORE_FV_LOCATION_PPI not found, try to locate PeiCore from BFV. + // + if (PeiCoreEntryPoint == NULL) { + // + // Both SecCore and PeiCore are in BFV. + // + FindAndReportEntryPoints ( + (EFI_FIRMWARE_VOLUME_HEADER *) SecCoreData->BootFirmwareVolumeBase, + (EFI_FIRMWARE_VOLUME_HEADER *) SecCoreData->BootFirmwareVolumeBase, + &PeiCoreEntryPoint + ); + if (PeiCoreEntryPoint == NULL) { + CpuDeadLoop (); + } + } + + if (PpiList != NULL) { + AllSecPpiList = (EFI_PEI_PPI_DESCRIPTOR *) SecCoreData->PeiTemporaryRamBase; + + // + // Remove the terminal flag from the terminal PPI + // + CopyMem (AllSecPpiList, mPeiSecPlatformInformationPpi, sizeof (mPeiSecPlatformInformationPpi)); + Index = sizeof (mPeiSecPlatformInformationPpi) / sizeof (EFI_PEI_PPI_DESCRIPTOR) - 1; + AllSecPpiList[Index].Flags = AllSecPpiList[Index].Flags & (~EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST); + + // + // Append the platform additional PPI list + // + Index += 1; + while (((PpiList->Flags & EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST) != EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST)) { + CopyMem (&AllSecPpiList[Index], PpiList, sizeof (EFI_PEI_PPI_DESCRIPTOR)); + Index++; + PpiList++; + } + + // + // Add the terminal PPI + // + CopyMem (&AllSecPpiList[Index ++], PpiList, sizeof (EFI_PEI_PPI_DESCRIPTOR)); + + // + // Set PpiList to the total PPI + // + PpiList = AllSecPpiList; + + // + // Adjust PEI TEMP RAM Range. + // + ASSERT (SecCoreData->PeiTemporaryRamSize > Index * sizeof (EFI_PEI_PPI_DESCRIPTOR)); + SecCoreData->PeiTemporaryRamBase = (VOID *)((UINTN) SecCoreData->PeiTemporaryRamBase + Index * sizeof (EFI_PEI_PPI_DESCRIPTOR)); + SecCoreData->PeiTemporaryRamSize = SecCoreData->PeiTemporaryRamSize - Index * sizeof (EFI_PEI_PPI_DESCRIPTOR); + // + // Adjust the Base and Size to be 8-byte aligned as HOB which has 8byte aligned requirement + // will be built based on them in PEI phase. + // + SecCoreData->PeiTemporaryRamBase = (VOID *)(((UINTN)SecCoreData->PeiTemporaryRamBase + 7) & ~0x07); + SecCoreData->PeiTemporaryRamSize &= ~(UINTN)0x07; + } else { + // + // No addition PPI, PpiList directly point to the common PPI list. + // + PpiList = &mPeiSecPlatformInformationPpi[0]; + } + + DEBUG (( + DEBUG_INFO, + "%a() Stack Base: 0x%p, Stack Size: 0x%x\n", + __FUNCTION__, + SecCoreData->StackBase, + (UINT32) SecCoreData->StackSize + )); + + // + // Report Status Code to indicate transferring to PEI core + // + REPORT_STATUS_CODE ( + EFI_PROGRESS_CODE, + EFI_SOFTWARE_SEC | EFI_SW_SEC_PC_HANDOFF_TO_NEXT + ); + + // + // Transfer the control to the PEI core + // + ASSERT (PeiCoreEntryPoint != NULL); + (*PeiCoreEntryPoint) (SecCoreData, PpiList); + + // + // Should not come here. + // + UNREACHABLE (); +} + +/** + TemporaryRamDone() disables the use of Temporary RAM. If present, this service is invoked + by the PEI Foundation after the EFI_PEI_PERMANANT_MEMORY_INSTALLED_PPI is installed. + + @retval EFI_SUCCESS Use of Temporary RAM was disabled. + @retval EFI_INVALID_PARAMETER Temporary RAM could not be disabled. + +**/ +EFI_STATUS +EFIAPI +SecTemporaryRamDone ( + VOID + ) +{ + EFI_STATUS Status; + EFI_STATUS Status2; + UINTN Index; + BOOLEAN State; + EFI_PEI_PPI_DESCRIPTOR *PeiPpiDescriptor; + REPUBLISH_SEC_PPI_PPI *RepublishSecPpiPpi; + + // + // Republish Sec Platform Information(2) PPI + // + RepublishSecPlatformInformationPpi (); + + // + // Re-install SEC PPIs using a PEIM produced service if published + // + for (Index = 0, Status = EFI_SUCCESS; Status == EFI_SUCCESS; Index++) { + Status = PeiServicesLocatePpi ( + &gRepublishSecPpiPpiGuid, + Index, + &PeiPpiDescriptor, + (VOID **) &RepublishSecPpiPpi + ); + if (!EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "Calling RepublishSecPpi instance %d.\n", Index)); + Status2 = RepublishSecPpiPpi->RepublishSecPpis (); + ASSERT_EFI_ERROR (Status2); + } + } + + // + // Migrate DebugAgentContext. + // + InitializeDebugAgent (DEBUG_AGENT_INIT_POSTMEM_SEC, NULL, NULL); + + // + // Disable interrupts and save current interrupt state + // + State = SaveAndDisableInterrupts (); + + // + // Migrate GDT before NEM near down + // + if (PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes)) { + Status = MigrateGdt (); + ASSERT_EFI_ERROR (Status); + } + + // + // Disable Temporary RAM after Stack and Heap have been migrated at this point. + // + SecPlatformDisableTemporaryMemory (); + + // + // Restore original interrupt state + // + SetInterruptState (State); + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecMain.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecMain.h new file mode 100644 index 00000000..9b474dee --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecCore/SecMain.h @@ -0,0 +1,180 @@ +/** @file + Master header file for SecCore. + + Copyright (c) 2008 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SEC_CORE_H_ +#define _SEC_CORE_H_ + +#include <PiPei.h> + +#include <Ppi/SecPlatformInformation2.h> +#include <Ppi/TemporaryRamDone.h> +#include <Ppi/SecPerformance.h> +#include <Ppi/PeiCoreFvLocation.h> +#include <Ppi/RepublishSecPpi.h> + +#include <Guid/FirmwarePerformance.h> + +#include <Library/DebugLib.h> +#include <Library/PcdLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/PlatformSecLib.h> +#include <Library/UefiCpuLib.h> +#include <Library/PeCoffGetEntryPointLib.h> +#include <Library/PeCoffExtraActionLib.h> +#include <Library/DebugAgentLib.h> +#include <Library/CpuExceptionHandlerLib.h> +#include <Library/ReportStatusCodeLib.h> +#include <Library/PeiServicesTablePointerLib.h> +#include <Library/HobLib.h> +#include <Library/PeiServicesLib.h> + +#define SEC_IDT_ENTRY_COUNT 34 + +typedef struct _SEC_IDT_TABLE { + // + // Reserved 8 bytes preceding IDT to store EFI_PEI_SERVICES**, since IDT base + // address should be 8-byte alignment. + // Note: For IA32, only the 4 bytes immediately preceding IDT is used to store + // EFI_PEI_SERVICES** + // + UINT64 PeiService; + UINT64 IdtTable[SEC_IDT_ENTRY_COUNT]; +} SEC_IDT_TABLE; + +/** + TemporaryRamDone() disables the use of Temporary RAM. If present, this service is invoked + by the PEI Foundation after the EFI_PEI_PERMANANT_MEMORY_INSTALLED_PPI is installed. + + @retval EFI_SUCCESS Use of Temporary RAM was disabled. + @retval EFI_INVALID_PARAMETER Temporary RAM could not be disabled. + +**/ +EFI_STATUS +EFIAPI +SecTemporaryRamDone ( + VOID + ); + +/** + Entry point to the C language phase of SEC. After the SEC assembly + code has initialized some temporary memory and set up the stack, + the control is transferred to this function. + + @param SizeOfRam Size of the temporary memory available for use. + @param TempRamBase Base address of temporary ram + @param BootFirmwareVolume Base address of the Boot Firmware Volume. +**/ +VOID +NORETURN +EFIAPI +SecStartup ( + IN UINT32 SizeOfRam, + IN UINT32 TempRamBase, + IN VOID *BootFirmwareVolume + ); + +/** + Find and return Pei Core entry point. + + It also find SEC and PEI Core file debug information. It will report them if + remote debug is enabled. + + @param SecCoreFirmwareVolumePtr Point to the firmware volume for finding SecCore. + @param PeiCoreFirmwareVolumePtr Point to the firmware volume for finding PeiCore. + @param PeiCoreEntryPoint The entry point of the PEI core. + +**/ +VOID +EFIAPI +FindAndReportEntryPoints ( + IN EFI_FIRMWARE_VOLUME_HEADER *SecCoreFirmwareVolumePtr, + IN EFI_FIRMWARE_VOLUME_HEADER *PeiCoreFirmwareVolumePtr, + OUT EFI_PEI_CORE_ENTRY_POINT *PeiCoreEntryPoint + ); + +/** + Auto-generated function that calls the library constructors for all of the module's + dependent libraries. This function must be called by the SEC Core once a stack has + been established. + +**/ +VOID +EFIAPI +ProcessLibraryConstructorList ( + VOID + ); + +/** + Implementation of the PlatformInformation service in EFI_SEC_PLATFORM_INFORMATION_PPI. + + @param PeiServices Pointer to the PEI Services Table. + @param StructureSize Pointer to the variable describing size of the input buffer. + @param PlatformInformationRecord Pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformationBist ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD *PlatformInformationRecord + ); + +/** + Implementation of the PlatformInformation2 service in EFI_SEC_PLATFORM_INFORMATION2_PPI. + + @param PeiServices The pointer to the PEI Services Table. + @param StructureSize The pointer to the variable describing size of the input buffer. + @param PlatformInformationRecord2 The pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD2. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. The current buffer size needed to + hold the record is returned in StructureSize. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformation2Bist ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD2 *PlatformInformationRecord2 + ); + +/** + Republish SecPlatformInformationPpi/SecPlatformInformation2Ppi. + +**/ +VOID +RepublishSecPlatformInformationPpi ( + VOID + ); + +/** + Entry point of the notification callback function itself within the PEIM. + It is to get SEC performance data and build HOB to convey the SEC performance + data to DXE phase. + + @param PeiServices Indirect reference to the PEI Services Table. + @param NotifyDescriptor Address of the notification descriptor data structure. + @param Ppi Address of the PPI that was installed. + + @return Status of the notification. + The status code returned from this function is ignored. +**/ +EFI_STATUS +EFIAPI +SecPerformancePpiCallBack ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecMigrationPei/SecMigrationPei.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecMigrationPei/SecMigrationPei.c new file mode 100644 index 00000000..9bf39ce1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecMigrationPei/SecMigrationPei.c @@ -0,0 +1,385 @@ +/** @file + Migrates SEC structures after permanent memory is installed. + + Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Base.h> + +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/HobLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/PeiServicesLib.h> +#include <Library/PeiServicesTablePointerLib.h> + +#include "SecMigrationPei.h" + +STATIC REPUBLISH_SEC_PPI_PPI mEdkiiRepublishSecPpiPpi = { + RepublishSecPpis + }; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_SEC_PLATFORM_INFORMATION_PPI mSecPlatformInformationPostMemoryPpi = { + SecPlatformInformationPostMemory + }; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_TEMPORARY_RAM_DONE_PPI mSecTemporaryRamDonePostMemoryPpi = { + SecTemporaryRamDonePostMemory + }; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI mSecTemporaryRamSupportPostMemoryPpi = { + SecTemporaryRamSupportPostMemory + }; + +GLOBAL_REMOVE_IF_UNREFERENCED PEI_SEC_PERFORMANCE_PPI mSecPerformancePpi = { + GetPerformancePostMemory + }; + +STATIC EFI_PEI_PPI_DESCRIPTOR mEdkiiRepublishSecPpiDescriptor = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gRepublishSecPpiPpiGuid, + &mEdkiiRepublishSecPpiPpi + }; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_PPI_DESCRIPTOR mSecPlatformInformationPostMemoryDescriptor = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiSecPlatformInformationPpiGuid, + &mSecPlatformInformationPostMemoryPpi + }; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_PPI_DESCRIPTOR mSecTemporaryRamDonePostMemoryDescriptor = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiTemporaryRamDonePpiGuid, + &mSecTemporaryRamDonePostMemoryPpi + }; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_PPI_DESCRIPTOR mSecTemporaryRamSupportPostMemoryDescriptor = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiTemporaryRamSupportPpiGuid, + &mSecTemporaryRamSupportPostMemoryPpi + }; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_PPI_DESCRIPTOR mSecPerformancePpiDescriptor = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gPeiSecPerformancePpiGuid, + &mSecPerformancePpi + }; + +/** + Disables the use of Temporary RAM. + + If present, this service is invoked by the PEI Foundation after + the EFI_PEI_PERMANANT_MEMORY_INSTALLED_PPI is installed. + + @retval EFI_SUCCESS Dummy function, alway return this value. + +**/ +EFI_STATUS +EFIAPI +SecTemporaryRamDonePostMemory ( + VOID + ) +{ + // + // Temporary RAM Done is already done in post-memory + // install a stub function that is located in permanent memory + // + return EFI_SUCCESS; +} + +/** + This service of the EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI that migrates temporary RAM into + permanent memory. + + @param PeiServices Pointer to the PEI Services Table. + @param TemporaryMemoryBase Source Address in temporary memory from which the SEC or PEIM will copy the + Temporary RAM contents. + @param PermanentMemoryBase Destination Address in permanent memory into which the SEC or PEIM will copy the + Temporary RAM contents. + @param CopySize Amount of memory to migrate from temporary to permanent memory. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_INVALID_PARAMETER PermanentMemoryBase + CopySize > TemporaryMemoryBase when + TemporaryMemoryBase > PermanentMemoryBase. + +**/ +EFI_STATUS +EFIAPI +SecTemporaryRamSupportPostMemory ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase, + IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase, + IN UINTN CopySize + ) +{ + // + // Temporary RAM Support is already done in post-memory + // install a stub function that is located in permanent memory + // + return EFI_SUCCESS; +} + +/** + This interface conveys performance information out of the Security (SEC) phase into PEI. + + This service is published by the SEC phase. The SEC phase handoff has an optional + EFI_PEI_PPI_DESCRIPTOR list as its final argument when control is passed from SEC into the + PEI Foundation. As such, if the platform supports collecting performance data in SEC, + this information is encapsulated into the data structure abstracted by this service. + This information is collected for the boot-strap processor (BSP) on IA-32. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in] This The pointer to this instance of the PEI_SEC_PERFORMANCE_PPI. + @param[out] Performance The pointer to performance data collected in SEC phase. + + @retval EFI_SUCCESS The performance data was successfully returned. + @retval EFI_INVALID_PARAMETER The This or Performance is NULL. + @retval EFI_NOT_FOUND Can't found the HOB created by the SecMigrationPei component. + +**/ +EFI_STATUS +EFIAPI +GetPerformancePostMemory ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN PEI_SEC_PERFORMANCE_PPI *This, + OUT FIRMWARE_SEC_PERFORMANCE *Performance + ) +{ + SEC_PLATFORM_INFORMATION_CONTEXT_HOB *SecPlatformInformationContexHob; + + if (This == NULL || Performance == NULL) { + return EFI_INVALID_PARAMETER; + } + + SecPlatformInformationContexHob = GetFirstGuidHob (&gEfiCallerIdGuid); + if (SecPlatformInformationContexHob == NULL) { + return EFI_NOT_FOUND; + } + + Performance->ResetEnd = SecPlatformInformationContexHob->FirmwareSecPerformance.ResetEnd; + + return EFI_SUCCESS; +} + +/** + This interface conveys state information out of the Security (SEC) phase into PEI. + + @param[in] PeiServices Pointer to the PEI Services Table. + @param[in,out] StructureSize Pointer to the variable describing size of the input buffer. + @param[out] PlatformInformationRecord Pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_NOT_FOUND Can't found the HOB created by SecMigrationPei component. + @retval EFI_BUFFER_TOO_SMALL The size of buffer pointed by StructureSize is too small and will return + the minimal required size in the buffer pointed by StructureSize. + @retval EFI_INVALID_PARAMETER The StructureSize is NULL or PlatformInformationRecord is NULL. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformationPostMemory ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD *PlatformInformationRecord + ) +{ + SEC_PLATFORM_INFORMATION_CONTEXT_HOB *SecPlatformInformationContexHob; + + if (StructureSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + SecPlatformInformationContexHob = GetFirstGuidHob (&gEfiCallerIdGuid); + if (SecPlatformInformationContexHob == NULL) { + return EFI_NOT_FOUND; + } + + if (*StructureSize < SecPlatformInformationContexHob->Context.StructureSize) { + *StructureSize = SecPlatformInformationContexHob->Context.StructureSize; + return EFI_BUFFER_TOO_SMALL; + } + + if (PlatformInformationRecord == NULL) { + return EFI_INVALID_PARAMETER; + } + + *StructureSize = SecPlatformInformationContexHob->Context.StructureSize; + CopyMem ( + (VOID *) PlatformInformationRecord, + (VOID *) SecPlatformInformationContexHob->Context.PlatformInformationRecord, + (UINTN) SecPlatformInformationContexHob->Context.StructureSize + ); + + return EFI_SUCCESS; +} + +/** + This interface re-installs PPIs installed in SecCore from a post-memory PEIM. + + This is to allow a platform that may not support relocation of SecCore to update the PPI instance to a post-memory + copy from a PEIM that has been shadowed to permanent memory. + + @retval EFI_SUCCESS The SecCore PPIs were re-installed successfully. + @retval Others An error occurred re-installing the SecCore PPIs. + +**/ +EFI_STATUS +EFIAPI +RepublishSecPpis ( + VOID + ) +{ + EFI_STATUS Status; + EFI_PEI_PPI_DESCRIPTOR *PeiPpiDescriptor; + VOID *PeiPpi; + SEC_PLATFORM_INFORMATION_CONTEXT_HOB *SecPlatformInformationContextHob; + EFI_SEC_PLATFORM_INFORMATION_RECORD *SecPlatformInformationPtr; + UINT64 SecStructureSize; + + SecPlatformInformationPtr = NULL; + SecStructureSize = 0; + + Status = PeiServicesLocatePpi ( + &gEfiTemporaryRamDonePpiGuid, + 0, + &PeiPpiDescriptor, + (VOID **) &PeiPpi + ); + if (!EFI_ERROR (Status)) { + Status = PeiServicesReInstallPpi ( + PeiPpiDescriptor, + &mSecTemporaryRamDonePostMemoryDescriptor + ); + ASSERT_EFI_ERROR (Status); + } + + Status = PeiServicesLocatePpi ( + &gEfiTemporaryRamSupportPpiGuid, + 0, + &PeiPpiDescriptor, + (VOID **) &PeiPpi + ); + if (!EFI_ERROR (Status)) { + Status = PeiServicesReInstallPpi ( + PeiPpiDescriptor, + &mSecTemporaryRamSupportPostMemoryDescriptor + ); + ASSERT_EFI_ERROR (Status); + } + + Status = PeiServicesCreateHob ( + EFI_HOB_TYPE_GUID_EXTENSION, + sizeof (SEC_PLATFORM_INFORMATION_CONTEXT_HOB), + (VOID **) &SecPlatformInformationContextHob + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "SecPlatformInformation Context HOB could not be created.\n")); + return Status; + } + + SecPlatformInformationContextHob->Header.Name = gEfiCallerIdGuid; + SecPlatformInformationContextHob->Revision = 1; + + Status = PeiServicesLocatePpi ( + &gPeiSecPerformancePpiGuid, + 0, + &PeiPpiDescriptor, + (VOID **) &PeiPpi + ); + if (!EFI_ERROR (Status)) { + Status = ((PEI_SEC_PERFORMANCE_PPI *) PeiPpi)->GetPerformance ( + GetPeiServicesTablePointer (), + (PEI_SEC_PERFORMANCE_PPI *) PeiPpi, + &SecPlatformInformationContextHob->FirmwareSecPerformance + ); + ASSERT_EFI_ERROR (Status); + if (!EFI_ERROR (Status)) { + Status = PeiServicesReInstallPpi ( + PeiPpiDescriptor, + &mSecPerformancePpiDescriptor + ); + ASSERT_EFI_ERROR (Status); + } + } + + Status = PeiServicesLocatePpi ( + &gEfiSecPlatformInformationPpiGuid, + 0, + &PeiPpiDescriptor, + (VOID **) &PeiPpi + ); + if (!EFI_ERROR (Status)) { + Status = ((EFI_SEC_PLATFORM_INFORMATION_PPI *) PeiPpi)->PlatformInformation ( + GetPeiServicesTablePointer (), + &SecStructureSize, + SecPlatformInformationPtr + ); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + if (Status != EFI_BUFFER_TOO_SMALL) { + return EFI_NOT_FOUND; + } + + ZeroMem ((VOID *) &(SecPlatformInformationContextHob->Context), sizeof (SEC_PLATFORM_INFORMATION_CONTEXT)); + SecPlatformInformationContextHob->Context.PlatformInformationRecord = AllocatePool ((UINTN) SecStructureSize); + ASSERT (SecPlatformInformationContextHob->Context.PlatformInformationRecord != NULL); + if (SecPlatformInformationContextHob->Context.PlatformInformationRecord == NULL) { + return EFI_OUT_OF_RESOURCES; + } + SecPlatformInformationContextHob->Context.StructureSize = SecStructureSize; + + Status = ((EFI_SEC_PLATFORM_INFORMATION_PPI *) PeiPpi)->PlatformInformation ( + GetPeiServicesTablePointer (), + &(SecPlatformInformationContextHob->Context.StructureSize), + SecPlatformInformationContextHob->Context.PlatformInformationRecord + ); + ASSERT_EFI_ERROR (Status); + if (!EFI_ERROR (Status)) { + Status = PeiServicesReInstallPpi ( + PeiPpiDescriptor, + &mSecPlatformInformationPostMemoryDescriptor + ); + ASSERT_EFI_ERROR (Status); + } + } + + return EFI_SUCCESS; +} + +/** + This function is the entry point which installs an instance of REPUBLISH_SEC_PPI_PPI. + + It install the RepublishSecPpi depent on PcdMigrateTemporaryRamFirmwareVolumes, install + the PPI when the PcdMigrateTemporaryRamFirmwareVolumes enabled. + + @param[in] FileHandle Pointer to image file handle. + @param[in] PeiServices Pointer to PEI Services Table + + @retval EFI_ABORTED Disable evacuate temporary memory feature by disable + PcdMigrateTemporaryRamFirmwareVolumes. + @retval EFI_SUCCESS An instance of REPUBLISH_SEC_PPI_PPI was installed successfully. + @retval Others An error occurred installing and instance of REPUBLISH_SEC_PPI_PPI. + +**/ +EFI_STATUS +EFIAPI +SecMigrationPeiInitialize ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + + Status = EFI_ABORTED; + + if (PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes)) { + Status = PeiServicesInstallPpi (&mEdkiiRepublishSecPpiDescriptor); + ASSERT_EFI_ERROR (Status); + } + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecMigrationPei/SecMigrationPei.h b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecMigrationPei/SecMigrationPei.h new file mode 100644 index 00000000..4c491e06 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecMigrationPei/SecMigrationPei.h @@ -0,0 +1,158 @@ +/** @file + Migrates SEC structures after permanent memory is installed. + + Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __SEC_MIGRATION_H__ +#define __SEC_MIGRATION_H__ + +#include <Base.h> + +#include <Pi/PiPeiCis.h> +#include <Ppi/RepublishSecPpi.h> +#include <Ppi/SecPerformance.h> +#include <Ppi/SecPlatformInformation.h> +#include <Ppi/SecPlatformInformation2.h> +#include <Ppi/TemporaryRamDone.h> +#include <Ppi/TemporaryRamSupport.h> + +/** + This interface conveys state information out of the Security (SEC) phase into PEI. + + @param[in] PeiServices Pointer to the PEI Services Table. + @param[in,out] StructureSize Pointer to the variable describing size of the input buffer. + @param[out] PlatformInformationRecord Pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_NOT_FOUND Can't found the HOB created by SecMigrationPei component. + @retval EFI_BUFFER_TOO_SMALL The size of buffer pointed by StructureSize is too small and will return + the minimal required size in the buffer pointed by StructureSize. + @retval EFI_INVALID_PARAMETER The StructureSize is NULL or PlatformInformationRecord is NULL. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformationPostMemory ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD *PlatformInformationRecord + ); + +/** + Re-installs the SEC Platform Information PPIs to implementation in this module to support post-memory. + + @param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. + @param[in] NotifyDescriptor Address of the notification descriptor data structure. + @param[in] Ppi Address of the PPI that was installed. + + @retval EFI_SUCCESS The SEC Platform Information PPI could not be re-installed. + @return Others An error occurred during PPI re-install. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformationPpiNotifyCallback ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ); + +/** + This interface re-installs PPIs installed in SecCore from a post-memory PEIM. + + This is to allow a platform that may not support relocation of SecCore to update the PPI instance to a post-memory + copy from a PEIM that has been shadowed to permanent memory. + + @retval EFI_SUCCESS The SecCore PPIs were re-installed successfully. + @retval Others An error occurred re-installing the SecCore PPIs. + +**/ +EFI_STATUS +EFIAPI +RepublishSecPpis ( + VOID + ); + +/** + Disables the use of Temporary RAM. + + If present, this service is invoked by the PEI Foundation after + the EFI_PEI_PERMANANT_MEMORY_INSTALLED_PPI is installed. + + @retval EFI_SUCCESS Dummy function, alway return this value. + +**/ +EFI_STATUS +EFIAPI +SecTemporaryRamDonePostMemory ( + VOID + ); + +/** + This service of the EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI that migrates temporary RAM into + permanent memory. + + @param PeiServices Pointer to the PEI Services Table. + @param TemporaryMemoryBase Source Address in temporary memory from which the SEC or PEIM will copy the + Temporary RAM contents. + @param PermanentMemoryBase Destination Address in permanent memory into which the SEC or PEIM will copy the + Temporary RAM contents. + @param CopySize Amount of memory to migrate from temporary to permanent memory. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_INVALID_PARAMETER PermanentMemoryBase + CopySize > TemporaryMemoryBase when + TemporaryMemoryBase > PermanentMemoryBase. + +**/ +EFI_STATUS +EFIAPI +SecTemporaryRamSupportPostMemory ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PHYSICAL_ADDRESS TemporaryMemoryBase, + IN EFI_PHYSICAL_ADDRESS PermanentMemoryBase, + IN UINTN CopySize + ); + +/** + This interface conveys performance information out of the Security (SEC) phase into PEI. + + This service is published by the SEC phase. The SEC phase handoff has an optional + EFI_PEI_PPI_DESCRIPTOR list as its final argument when control is passed from SEC into the + PEI Foundation. As such, if the platform supports collecting performance data in SEC, + this information is encapsulated into the data structure abstracted by this service. + This information is collected for the boot-strap processor (BSP) on IA-32. + + @param[in] PeiServices The pointer to the PEI Services Table. + @param[in] This The pointer to this instance of the PEI_SEC_PERFORMANCE_PPI. + @param[out] Performance The pointer to performance data collected in SEC phase. + + @retval EFI_SUCCESS The performance data was successfully returned. + @retval EFI_INVALID_PARAMETER The This or Performance is NULL. + @retval EFI_NOT_FOUND Can't found the HOB created by the SecMigrationPei component. + +**/ +EFI_STATUS +EFIAPI +GetPerformancePostMemory ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN PEI_SEC_PERFORMANCE_PPI *This, + OUT FIRMWARE_SEC_PERFORMANCE *Performance + ); + +typedef struct { + UINT64 StructureSize; + EFI_SEC_PLATFORM_INFORMATION_RECORD *PlatformInformationRecord; +} SEC_PLATFORM_INFORMATION_CONTEXT; + +typedef struct { + EFI_HOB_GUID_TYPE Header; + UINT8 Revision; + UINT8 Reserved[3]; + FIRMWARE_SEC_PERFORMANCE FirmwareSecPerformance; + SEC_PLATFORM_INFORMATION_CONTEXT Context; +} SEC_PLATFORM_INFORMATION_CONTEXT_HOB; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecMigrationPei/SecMigrationPei.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecMigrationPei/SecMigrationPei.inf new file mode 100644 index 00000000..7c2b7e2d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecMigrationPei/SecMigrationPei.inf @@ -0,0 +1,68 @@ +## @file +# Migrates SEC structures after permanent memory is installed. +# +# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SecMigrationPei + MODULE_UNI_FILE = SecMigrationPei.uni + FILE_GUID = 58B35361-8922-41BC-B313-EF7ED9ADFDF7 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = SecMigrationPeiInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + SecMigrationPei.c + SecMigrationPei.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + HobLib + MemoryAllocationLib + PeimEntryPoint + PeiServicesLib + PeiServicesTablePointerLib + +[Ppis] + ## PRODUCES + gRepublishSecPpiPpiGuid + + ## SOMETIMES_PRODUCES + gEfiTemporaryRamDonePpiGuid + + ## SOMETIME_PRODUCES + gEfiTemporaryRamSupportPpiGuid + + ## SOMETIMES_PRODUCES + gPeiSecPerformancePpiGuid + + ## SOMETIMES_CONSUMES + ## PRODUCES + gEfiSecPlatformInformationPpiGuid + + ## SOMETIMES_CONSUMES + ## SOMETIMES_PRODUCES + gEfiSecPlatformInformation2PpiGuid + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdMigrateTemporaryRamFirmwareVolumes ## CONSUMES + +[Depex] + TRUE diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecMigrationPei/SecMigrationPei.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecMigrationPei/SecMigrationPei.uni new file mode 100644 index 00000000..62c2064b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/SecMigrationPei/SecMigrationPei.uni @@ -0,0 +1,13 @@ +// /** @file
+// Migrates SEC structures after permanent memory is installed.
+//
+// Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Migrates SEC structures after permanent memory is installed"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Migrates SEC structures after permanent memory is installed."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Test/UefiCpuPkgHostTest.dsc b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Test/UefiCpuPkgHostTest.dsc new file mode 100644 index 00000000..bcb04661 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Test/UefiCpuPkgHostTest.dsc @@ -0,0 +1,31 @@ +## @file +# UefiCpuPkg DSC file used to build host-based unit tests. +# +# Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + PLATFORM_NAME = UefiCpuPkgHostTest + PLATFORM_GUID = E00B9599-5B74-4FF7-AB9F-8183FB13B2F9 + PLATFORM_VERSION = 0.1 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build/UefiCpuPkg/HostTest + SUPPORTED_ARCHITECTURES = IA32|X64 + BUILD_TARGETS = NOOPT + SKUID_IDENTIFIER = DEFAULT + +!include UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc + +[LibraryClasses] + MtrrLib|UefiCpuPkg/Library/MtrrLib/MtrrLib.inf + +[PcdsPatchableInModule] + gUefiCpuPkgTokenSpaceGuid.PcdCpuNumberOfReservedVariableMtrrs|0 + +[Components] + # + # Build HOST_APPLICATION that tests the MtrrLib + # + UefiCpuPkg/Library/MtrrLib/UnitTest/MtrrLibUnitTestHost.inf diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkg.ci.yaml b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkg.ci.yaml new file mode 100644 index 00000000..5d3e3494 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkg.ci.yaml @@ -0,0 +1,76 @@ +## @file +# CI configuration for UefiCpuPkg +# +# Copyright (c) Microsoft Corporation +# Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +## +{ + "LicenseCheck": { + "IgnoreFiles": [] + }, + "EccCheck": { + ## Exception sample looks like below: + ## "ExceptionList": [ + ## "<ErrorID>", "<KeyWord>" + ## ] + "ExceptionList": [ + ], + ## Both file path and directory path are accepted. + "IgnoreFiles": [ + ] + }, + "CompilerPlugin": { + "DscPath": "UefiCpuPkg.dsc" + }, + ## options defined ci/Plugin/HostUnitTestCompilerPlugin + "HostUnitTestCompilerPlugin": { + "DscPath": "Test/UefiCpuPkgHostTest.dsc" + }, + "CharEncodingCheck": { + "IgnoreFiles": [] + }, + "DependencyCheck": { + "AcceptableDependencies": [ + "MdePkg/MdePkg.dec", + "MdeModulePkg/MdeModulePkg.dec", + "UefiCpuPkg/UefiCpuPkg.dec" + ], + # For host based unit tests + "AcceptableDependencies-HOST_APPLICATION":[ + "UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec" + ], + # For UEFI shell based apps + "AcceptableDependencies-UEFI_APPLICATION":[], + "IgnoreInf": [] + }, + "DscCompleteCheck": { + "DscPath": "UefiCpuPkg.dsc", + "IgnoreInf": [ + "UefiCpuPkg/ResetVector/FixupVtf/Vtf.inf", + "UefiCpuPkg/ResetVector/Vtf0/Vtf0.inf" + ] + }, + "HostUnitTestDscCompleteCheck": { + "IgnoreInf": [""], + "DscPath": "Test/UefiCpuPkgHostTest.dsc" + }, + "GuidCheck": { + "IgnoreGuidName": ["SecCore", "ResetVector"], # Expected duplication for gEfiFirmwareVolumeTopFileGuid + "IgnoreGuidValue": [], + "IgnoreFoldersAndFiles": [], + "IgnoreDuplicates": [] + }, + "LibraryClassCheck": { + "IgnoreHeaderFile": [] + }, + + ## options defined ci/Plugin/SpellCheck + "SpellCheck": { + "AuditOnly": True, # Fails test but run in AuditOnly mode to collect log + "IgnoreFiles": [], # use gitignore syntax to ignore errors in matching files + "ExtendWords": [], # words to extend to the dictionary for this package + "IgnoreStandardPaths": [], # Standard Plugin defined paths that should be ignore + "AdditionalIncludePaths": [] # Additional paths to spell check (wildcards supported) + } +} diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkg.dec b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkg.dec new file mode 100644 index 00000000..3bf636fa --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkg.dec @@ -0,0 +1,400 @@ +## @file UefiCpuPkg.dec +# This Package provides UEFI compatible CPU modules and libraries. +# +# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + DEC_SPECIFICATION = 0x00010005 + PACKAGE_NAME = UefiCpuPkg + PACKAGE_UNI_FILE = UefiCpuPkg.uni + PACKAGE_GUID = 2171df9b-0d39-45aa-ac37-2de190010d23 + PACKAGE_VERSION = 0.90 + +[Includes] + Include + +[LibraryClasses] + ## @libraryclass Defines some routines that are generic for IA32 family CPU + ## to be UEFI specification compliant. + ## + UefiCpuLib|Include/Library/UefiCpuLib.h + + ## @libraryclass Defines some routines that are used to register/manage/program + ## CPU features. + ## + RegisterCpuFeaturesLib|Include/Library/RegisterCpuFeaturesLib.h + +[LibraryClasses.IA32, LibraryClasses.X64] + ## @libraryclass Provides functions to manage MTRR settings on IA32 and X64 CPUs. + ## + MtrrLib|Include/Library/MtrrLib.h + + ## @libraryclass Provides functions to manage the Local APIC on IA32 and X64 CPUs. + ## + LocalApicLib|Include/Library/LocalApicLib.h + + ## @libraryclass Provides platform specific initialization functions in the SEC phase. + ## + PlatformSecLib|Include/Library/PlatformSecLib.h + + ## @libraryclass Public include file for the SMM CPU Platform Hook Library. + ## + SmmCpuPlatformHookLib|Include/Library/SmmCpuPlatformHookLib.h + + ## @libraryclass Provides the CPU specific programming for PiSmmCpuDxeSmm module. + ## + SmmCpuFeaturesLib|Include/Library/SmmCpuFeaturesLib.h + + ## @libraryclass Provides functions to support MP services on CpuMpPei and CpuDxe module. + ## + MpInitLib|Include/Library/MpInitLib.h + + ## @libraryclass Provides function to support VMGEXIT processing. + VmgExitLib|Include/Library/VmgExitLib.h + + ## @libraryclass Provides function to get CPU cache information. + CpuCacheInfoLib|Include/Library/CpuCacheInfoLib.h + + ## @libraryclass Provides function for loading microcode. + MicrocodeLib|Include/Library/MicrocodeLib.h + +[Guids] + gUefiCpuPkgTokenSpaceGuid = { 0xac05bf33, 0x995a, 0x4ed4, { 0xaa, 0xb8, 0xef, 0x7a, 0xe8, 0xf, 0x5c, 0xb0 }} + gMsegSmramGuid = { 0x5802bce4, 0xeeee, 0x4e33, { 0xa1, 0x30, 0xeb, 0xad, 0x27, 0xf0, 0xe4, 0x39 }} + + ## Include/Guid/CpuFeaturesSetDone.h + gEdkiiCpuFeaturesSetDoneGuid = { 0xa82485ce, 0xad6b, 0x4101, { 0x99, 0xd3, 0xe1, 0x35, 0x8c, 0x9e, 0x7e, 0x37 }} + + ## Include/Guid/CpuFeaturesInitDone.h + gEdkiiCpuFeaturesInitDoneGuid = { 0xc77c3a41, 0x61ab, 0x4143, { 0x98, 0x3e, 0x33, 0x39, 0x28, 0x6, 0x28, 0xe5 }} + + ## Include/Guid/MicrocodePatchHob.h + gEdkiiMicrocodePatchHobGuid = { 0xd178f11d, 0x8716, 0x418e, { 0xa1, 0x31, 0x96, 0x7d, 0x2a, 0xc4, 0x28, 0x43 }} + +[Protocols] + ## Include/Protocol/SmmCpuService.h + gEfiSmmCpuServiceProtocolGuid = { 0x1d202cab, 0xc8ab, 0x4d5c, { 0x94, 0xf7, 0x3c, 0xfc, 0xc0, 0xd3, 0xd3, 0x35 }} + + ## Include/Protocol/SmMonitorInit.h + gEfiSmMonitorInitProtocolGuid = { 0x228f344d, 0xb3de, 0x43bb, { 0xa4, 0xd7, 0xea, 0x20, 0xb, 0x1b, 0x14, 0x82 }} + +# +# [Error.gUefiCpuPkgTokenSpaceGuid] +# 0x80000001 | Invalid value provided. +# + +[Ppis] + gEdkiiPeiMpServices2PpiGuid = { 0x5cb9cb3d, 0x31a4, 0x480c, { 0x94, 0x98, 0x29, 0xd2, 0x69, 0xba, 0xcf, 0xba}} + + ## Include/Ppi/ShadowMicrocode.h + gEdkiiPeiShadowMicrocodePpiGuid = { 0x430f6965, 0x9a69, 0x41c5, { 0x93, 0xed, 0x8b, 0xf0, 0x64, 0x35, 0xc1, 0xc6 }} + + ## Include/Ppi/RepublishSecPpi.h + gRepublishSecPpiPpiGuid = { 0x27a71b1e, 0x73ee, 0x43d6, { 0xac, 0xe3, 0x52, 0x1a, 0x2d, 0xc5, 0xd0, 0x92 }} + +[PcdsFeatureFlag] + ## Indicates if SMM Profile will be enabled. + # If enabled, instruction executions in and data accesses to memory outside of SMRAM will be logged. + # In X64 build, it could not be enabled when PcdCpuSmmRestrictedMemoryAccess is TRUE. + # In IA32 build, the page table memory is not marked as read-only when it is enabled. + # This PCD is only for validation purpose. It should be set to false in production.<BR><BR> + # TRUE - SMM Profile will be enabled.<BR> + # FALSE - SMM Profile will be disabled.<BR> + # @Prompt Enable SMM Profile. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmProfileEnable|FALSE|BOOLEAN|0x32132109 + + ## Indicates if the SMM profile log buffer is a ring buffer. + # If disabled, no additional log can be done when the buffer is full.<BR><BR> + # TRUE - the SMM profile log buffer is a ring buffer.<BR> + # FALSE - the SMM profile log buffer is a normal buffer.<BR> + # @Prompt The SMM profile log buffer is a ring buffer. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmProfileRingBuffer|FALSE|BOOLEAN|0x3213210a + + ## Indicates if SMM Startup AP in a blocking fashion. + # TRUE - SMM Startup AP in a blocking fashion.<BR> + # FALSE - SMM Startup AP in a non-blocking fashion.<BR> + # @Prompt SMM Startup AP in a blocking fashion. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmBlockStartupThisAp|FALSE|BOOLEAN|0x32132108 + + ## Indicates if SMM Stack Guard will be enabled. + # If enabled, stack overflow in SMM can be caught, preventing chaotic consequences.<BR><BR> + # TRUE - SMM Stack Guard will be enabled.<BR> + # FALSE - SMM Stack Guard will be disabled.<BR> + # @Prompt Enable SMM Stack Guard. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStackGuard|TRUE|BOOLEAN|0x1000001C + + ## Indicates if BSP election in SMM will be enabled. + # If enabled, a BSP will be dynamically elected among all processors in each SMI. + # Otherwise, processor 0 is always as BSP in each SMI.<BR><BR> + # TRUE - BSP election in SMM will be enabled.<BR> + # FALSE - BSP election in SMM will be disabled.<BR> + # @Prompt Enable BSP election in SMM. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmEnableBspElection|TRUE|BOOLEAN|0x32132106 + + ## Indicates if CPU SMM hot-plug will be enabled.<BR><BR> + # TRUE - SMM CPU hot-plug will be enabled.<BR> + # FALSE - SMM CPU hot-plug will be disabled.<BR> + # @Prompt SMM CPU hot-plug. + gUefiCpuPkgTokenSpaceGuid.PcdCpuHotPlugSupport|FALSE|BOOLEAN|0x3213210C + + ## Indicates if SMM Debug will be enabled. + # If enabled, hardware breakpoints in SMRAM can be set outside of SMM mode and take effect in SMM.<BR><BR> + # TRUE - SMM Debug will be enabled.<BR> + # FALSE - SMM Debug will be disabled.<BR> + # @Prompt Enable SMM Debug. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmDebug|FALSE|BOOLEAN|0x1000001B + + ## Indicates if lock SMM Feature Control MSR.<BR><BR> + # TRUE - SMM Feature Control MSR will be locked.<BR> + # FALSE - SMM Feature Control MSR will not be locked.<BR> + # @Prompt Lock SMM Feature Control MSR. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmFeatureControlMsrLock|TRUE|BOOLEAN|0x3213210B + +[PcdsFixedAtBuild] + ## List of exception vectors which need switching stack. + # This PCD will only take into effect if PcdCpuStackGuard is enabled. + # By default exception #DD(8), #PF(14) are supported. + # @Prompt Specify exception vectors which need switching stack. + gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList|{0x08, 0x0E}|VOID*|0x30002000 + + ## Size of good stack for an exception. + # This PCD will only take into effect if PcdCpuStackGuard is enabled. + # @Prompt Specify size of good stack of exception which need switching stack. + gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize|2048|UINT32|0x30002001 + + ## Count of pre allocated SMM MP tokens per chunk. + # @Prompt Specify the count of pre allocated SMM MP tokens per chunk. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmMpTokenCountPerChunk|64|UINT32|0x30002002 + + ## Area of memory where the SEV-ES work area block lives. + # @Prompt Configure the SEV-ES work area base + gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|0x0|UINT32|0x30002005 + + ## Size of teh area of memory where the SEV-ES work area block lives. + # @Prompt Configure the SEV-ES work area base + gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize|0x0|UINT32|0x30002006 + +[PcdsFixedAtBuild, PcdsPatchableInModule] + ## This value is the CPU Local APIC base address, which aligns the address on a 4-KByte boundary. + # @Prompt Configure base address of CPU Local APIC + # @Expression 0x80000001 | (gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress & 0xfff) == 0 + gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress|0xfee00000|UINT32|0x00000001 + + ## Specifies delay value in microseconds after sending out an INIT IPI. + # @Prompt Configure delay value after send an INIT IPI + gUefiCpuPkgTokenSpaceGuid.PcdCpuInitIpiDelayInMicroSeconds|10000|UINT32|0x30000002 + + ## This value specifies the Application Processor (AP) stack size, used for Mp Service, which must + ## aligns the address on a 4-KByte boundary. + # @Prompt Configure stack size for Application Processor (AP) + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize|0x8000|UINT32|0x00000003 + + ## Specifies stack size in the temporary RAM. 0 means half of TemporaryRamSize. + # @Prompt Stack size in the temporary RAM. + gUefiCpuPkgTokenSpaceGuid.PcdPeiTemporaryRamStackSize|0|UINT32|0x10001003 + + ## Specifies buffer size in bytes to save SMM profile data. The value should be a multiple of 4KB. + # @Prompt SMM profile data buffer size. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmProfileSize|0x200000|UINT32|0x32132107 + + ## Specifies stack size in bytes for each processor in SMM. + # @Prompt Processor stack size in SMM. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStackSize|0x2000|UINT32|0x32132105 + + ## Specifies shadow stack size in bytes for each processor in SMM. + # @Prompt Processor shadow stack size in SMM. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmShadowStackSize|0x2000|UINT32|0x3213210E + + ## Indicates if SMM Code Access Check is enabled. + # If enabled, the SMM handler cannot execute the code outside SMM regions. + # This PCD is suggested to TRUE in production image.<BR><BR> + # TRUE - SMM Code Access Check will be enabled.<BR> + # FALSE - SMM Code Access Check will be disabled.<BR> + # @Prompt SMM Code Access Check. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmCodeAccessCheckEnable|TRUE|BOOLEAN|0x60000013 + + ## Specifies the number of variable MTRRs reserved for OS use. The default number of + # MTRRs reserved for OS use is 2. + # @Prompt Number of reserved variable MTRRs. + gUefiCpuPkgTokenSpaceGuid.PcdCpuNumberOfReservedVariableMtrrs|0x2|UINT32|0x00000015 + + ## Specifies buffer size in bytes for STM exception stack. The value should be a multiple of 4KB. + # @Prompt STM exception stack size. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmStmExceptionStackSize|0x1000|UINT32|0x32132111 + + ## Specifies buffer size in bytes of MSEG. The value should be a multiple of 4KB. + # @Prompt MSEG size. + gUefiCpuPkgTokenSpaceGuid.PcdCpuMsegSize|0x200000|UINT32|0x32132112 + + ## Specifies the supported CPU features bit in array. + # @Prompt Supported CPU features. + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesSupport|{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}|VOID*|0x00000016 + + ## Specifies if CPU features will be initialized after SMM relocation. + # @Prompt If CPU features will be initialized after SMM relocation. + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesInitAfterSmmRelocation|FALSE|BOOLEAN|0x0000001C + + ## Specifies if CPU features will be initialized during S3 resume. + # @Prompt If CPU features will be initialized during S3 resume. + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesInitOnS3Resume|FALSE|BOOLEAN|0x0000001D + + ## Specifies CPUID Leaf 0x15 Time Stamp Counter and Nominal Core Crystal Clock Frequency. + # TSC Frequency = ECX (core crystal clock frequency) * EBX/EAX. + # Intel Xeon Processor Scalable Family with CPUID signature 06_55H = 25000000 (25MHz) + # 6th and 7th generation Intel Core processors and Intel Xeon W Processor Family = 24000000 (24MHz) + # Intel Atom processors based on Goldmont Microarchitecture with CPUID signature 06_5CH = 19200000 (19.2MHz) + # @Prompt This PCD is the nominal frequency of the core crystal clock in Hz as is CPUID Leaf 0x15:ECX + gUefiCpuPkgTokenSpaceGuid.PcdCpuCoreCrystalClockFrequency|24000000|UINT64|0x32132113 + + ## Specifies the periodic interval value in microseconds for the status check + # of APs for StartupAllAPs() and StartupThisAP() executed in non-blocking + # mode in DXE phase. + # @Prompt Periodic interval value in microseconds for AP status check in DXE. + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds|100000|UINT32|0x0000001E + +[PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx] + ## Specifies max supported number of Logical Processors. + # @Prompt Configure max supported number of Logical Processors + gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber|64|UINT32|0x00000002 + ## Specifies timeout value in microseconds for the BSP to detect all APs for the first time. + # @Prompt Timeout for the BSP to detect all APs for the first time. + gUefiCpuPkgTokenSpaceGuid.PcdCpuApInitTimeOutInMicroSeconds|50000|UINT32|0x00000004 + ## Specifies the number of Logical Processors that are available in the + # preboot environment after platform reset, including BSP and APs. Possible + # values:<BR><BR> + # zero (default) - PcdCpuBootLogicalProcessorNumber is ignored, and + # PcdCpuApInitTimeOutInMicroSeconds limits the initial AP + # detection by the BSP.<BR> + # nonzero - PcdCpuApInitTimeOutInMicroSeconds is ignored. The initial + # AP detection finishes only when the detected CPU count + # (BSP plus APs) reaches the value of + # PcdCpuBootLogicalProcessorNumber, regardless of how long + # that takes.<BR> + # @Prompt Number of Logical Processors available after platform reset. + gUefiCpuPkgTokenSpaceGuid.PcdCpuBootLogicalProcessorNumber|0|UINT32|0x00000008 + ## Specifies the base address of the first microcode Patch in the microcode Region. + # @Prompt Microcode Region base address. + gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchAddress|0x0|UINT64|0x00000005 + ## Specifies the size of the microcode Region. + # @Prompt Microcode Region size. + gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchRegionSize|0x0|UINT64|0x00000006 + ## Specifies the AP wait loop state during POST phase. + # The value is defined as below.<BR><BR> + # 1: Place AP in the Hlt-Loop state.<BR> + # 2: Place AP in the Mwait-Loop state.<BR> + # 3: Place AP in the Run-Loop state.<BR> + # @Prompt The AP wait loop state. + # @ValidRange 0x80000001 | 1 - 3 + gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode|1|UINT8|0x60008006 + ## Specifies the AP target C-state for Mwait during POST phase. + # The default value 0 means C1 state. + # The value is defined as below.<BR><BR> + # @Prompt The specified AP target C-state for Mwait. + gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate|0|UINT8|0x00000007 + + ## Specifies timeout value in microseconds for the BSP in SMM to wait for all APs to come into SMM. + # @Prompt AP synchronization timeout value in SMM. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmApSyncTimeout|1000000|UINT64|0x32132104 + + ## Indicates the CPU synchronization method used when processing an SMI. + # 0x00 - Traditional CPU synchronization method.<BR> + # 0x01 - Relaxed CPU synchronization method.<BR> + # @Prompt SMM CPU Synchronization Method. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmSyncMode|0x00|UINT8|0x60000014 + + ## Specifies the On-demand clock modulation duty cycle when ACPI feature is enabled. + # @Prompt The encoded values for target duty cycle modulation. + # @ValidRange 0x80000001 | 0 - 15 + gUefiCpuPkgTokenSpaceGuid.PcdCpuClockModulationDutyCycle|0x0|UINT8|0x0000001A + + ## Indicates if the current boot is a power-on reset.<BR><BR> + # TRUE - Current boot is a power-on reset.<BR> + # FALSE - Current boot is not a power-on reset.<BR> + # @Prompt Current boot is a power-on reset. + gUefiCpuPkgTokenSpaceGuid.PcdIsPowerOnReset|FALSE|BOOLEAN|0x0000001B + +[PcdsFixedAtBuild.X64, PcdsPatchableInModule.X64, PcdsDynamic.X64, PcdsDynamicEx.X64] + ## Indicate access to non-SMRAM memory is restricted to reserved, runtime and ACPI NVS type after SmmReadyToLock. + # MMIO access is always allowed regardless of the value of this PCD. + # Loose of such restriction is only required by RAS components in X64 platforms. + # The PCD value is considered as constantly TRUE in IA32 platforms. + # When the PCD value is TRUE, page table is initialized to cover all memory spaces + # and the memory occupied by page table is protected by page table itself as read-only. + # In X64 build, it cannot be enabled at the same time with SMM profile feature (PcdCpuSmmProfileEnable). + # In X64 build, it could not be enabled also at the same time with heap guard feature for SMM + # (PcdHeapGuardPropertyMask in MdeModulePkg). + # In IA32 build, page table memory is not marked as read-only when either SMM profile feature (PcdCpuSmmProfileEnable) + # or heap guard feature for SMM (PcdHeapGuardPropertyMask in MdeModulePkg) is enabled. + # TRUE - Access to non-SMRAM memory is restricted to reserved, runtime and ACPI NVS type after SmmReadyToLock.<BR> + # FALSE - Access to any type of non-SMRAM memory after SmmReadyToLock is allowed.<BR> + # @Prompt Access to non-SMRAM memory is restricted to reserved, runtime and ACPI NVS type after SmmReadyToLock. + gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmRestrictedMemoryAccess|TRUE|BOOLEAN|0x3213210F + +[PcdsDynamic, PcdsDynamicEx] + ## Contains the pointer to a CPU S3 data buffer of structure ACPI_CPU_DATA. + # @Prompt The pointer to a CPU S3 data buffer. + # @ValidList 0x80000001 | 0 + gUefiCpuPkgTokenSpaceGuid.PcdCpuS3DataAddress|0x0|UINT64|0x60000010 + + ## Contains the pointer to a CPU Hot Plug Data structure if CPU hot-plug is supported. + # @Prompt The pointer to CPU Hot Plug Data. + # @ValidList 0x80000001 | 0 + gUefiCpuPkgTokenSpaceGuid.PcdCpuHotPlugDataAddress|0x0|UINT64|0x60000011 + + ## Indicates processor feature capabilities, each bit corresponding to a specific feature. + # @Prompt Processor feature capabilities. + # @ValidList 0x80000001 | 0 + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesCapability|{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}|VOID*|0x00000018 + + ## As input, specifies user's desired settings for enabling/disabling processor features. + ## As output, specifies actual settings for processor features, each bit corresponding to a specific feature. + # @Prompt As input, specifies user's desired processor feature settings. As output, specifies actual processor feature settings. + # @ValidList 0x80000001 | 0 + gUefiCpuPkgTokenSpaceGuid.PcdCpuFeaturesSetting|{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}|VOID*|0x00000019 + + ## Contains the size of memory required when CPU processor trace is enabled.<BR><BR> + # Processor trace is enabled through set BIT44(CPU_FEATURE_PROC_TRACE) in PcdCpuFeaturesSetting.<BR><BR> + # This PCD is ignored if CPU processor trace is disabled.<BR><BR> + # Default value is 0x00 which means 4KB of memory is allocated if CPU processor trace is enabled.<BR> + # 0x0 - 4K.<BR> + # 0x1 - 8K.<BR> + # 0x2 - 16K.<BR> + # 0x3 - 32K.<BR> + # 0x4 - 64K.<BR> + # 0x5 - 128K.<BR> + # 0x6 - 256K.<BR> + # 0x7 - 512K.<BR> + # 0x8 - 1M.<BR> + # 0x9 - 2M.<BR> + # 0xA - 4M.<BR> + # 0xB - 8M.<BR> + # 0xC - 16M.<BR> + # 0xD - 32M.<BR> + # 0xE - 64M.<BR> + # 0xF - 128M.<BR> + # @Prompt The memory size used for processor trace if processor trace is enabled. + # @ValidRange 0x80000001 | 0 - 0xF + gUefiCpuPkgTokenSpaceGuid.PcdCpuProcTraceMemSize|0x0|UINT32|0x60000012 + + ## Contains the processor trace output scheme when CPU processor trace is enabled.<BR><BR> + # Processor trace is enabled through set BIT44(CPU_FEATURE_PROC_TRACE) in PcdCpuFeaturesSetting.<BR><BR> + # This PCD is ignored if CPU processor trace is disabled.<BR><BR> + # Default value is 0 which means single range output scheme will be used if CPU processor trace is enabled.<BR> + # 0 - Single Range output scheme.<BR> + # 1 - ToPA(Table of physical address) scheme.<BR> + # @Prompt The processor trace output scheme used when processor trace is enabled. + # @ValidRange 0x80000001 | 0 - 1 + gUefiCpuPkgTokenSpaceGuid.PcdCpuProcTraceOutputScheme|0x0|UINT8|0x60000015 + + ## This dynamic PCD indicates whether SEV-ES is enabled + # TRUE - SEV-ES is enabled + # FALSE - SEV-ES is not enabled + # @Prompt SEV-ES Status + gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|FALSE|BOOLEAN|0x60000016 + +[UserExtensions.TianoCore."ExtraFiles"] + UefiCpuPkgExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkg.dsc b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkg.dsc new file mode 100644 index 00000000..5be5d4dc --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkg.dsc @@ -0,0 +1,176 @@ +## @file +# UefiCpuPkg Package +# +# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + PLATFORM_NAME = UefiCpu + PLATFORM_GUID = a1b7be22-78b3-4260-9569-8649e8c17d49 + PLATFORM_VERSION = 0.90 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build/UefiCpu + SUPPORTED_ARCHITECTURES = IA32|X64 + BUILD_TARGETS = DEBUG|RELEASE|NOOPT + SKUID_IDENTIFIER = DEFAULT + +# +# External libraries to build package +# + +!include MdePkg/MdeLibs.dsc.inc + +[LibraryClasses] + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + CpuLib|MdePkg/Library/BaseCpuLib/BaseCpuLib.inf + DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + SerialPortLib|MdePkg/Library/BaseSerialPortLibNull/BaseSerialPortLibNull.inf + DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + UefiCpuLib|UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.inf + IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf + MtrrLib|UefiCpuPkg/Library/MtrrLib/MtrrLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf + StandaloneMmDriverEntryPoint|MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf + DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf + PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf + PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf + PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf + TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf + DebugAgentLib|MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf + LocalApicLib|UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf + ReportStatusCodeLib|MdePkg/Library/BaseReportStatusCodeLibNull/BaseReportStatusCodeLibNull.inf + SynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf + SmmMemLib|MdePkg/Library/SmmMemLib/SmmMemLib.inf + CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf + PciLib|MdePkg/Library/BasePciLibPciExpress/BasePciLibPciExpress.inf + PciExpressLib|MdePkg/Library/BasePciExpressLib/BasePciExpressLib.inf + SmmCpuPlatformHookLib|UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.inf + SmmCpuFeaturesLib|UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.inf + PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf + PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf + TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf + VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf + MicrocodeLib|UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf + +[LibraryClasses.common.SEC] + PlatformSecLib|UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.inf +!if $(TOOL_CHAIN_TAG) == "XCODE5" + CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf +!else + CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf +!endif + HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf + PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLibIdt/PeiServicesTablePointerLibIdt.inf + MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf + +[LibraryClasses.common.PEIM] + MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf + HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf + LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxPeiLib.inf + MpInitLib|UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf + RegisterCpuFeaturesLib|UefiCpuPkg/Library/RegisterCpuFeaturesLib/PeiRegisterCpuFeaturesLib.inf + CpuCacheInfoLib|UefiCpuPkg/Library/CpuCacheInfoLib/PeiCpuCacheInfoLib.inf + +[LibraryClasses.IA32.PEIM, LibraryClasses.X64.PEIM] + PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLibIdt/PeiServicesTablePointerLibIdt.inf + CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf + +[LibraryClasses.common.DXE_DRIVER] + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf + CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf + MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf + RegisterCpuFeaturesLib|UefiCpuPkg/Library/RegisterCpuFeaturesLib/DxeRegisterCpuFeaturesLib.inf + CpuCacheInfoLib|UefiCpuPkg/Library/CpuCacheInfoLib/DxeCpuCacheInfoLib.inf + +[LibraryClasses.common.DXE_SMM_DRIVER] + SmmServicesTableLib|MdePkg/Library/SmmServicesTableLib/SmmServicesTableLib.inf + MmServicesTableLib|MdePkg/Library/MmServicesTableLib/MmServicesTableLib.inf + MemoryAllocationLib|MdePkg/Library/SmmMemoryAllocationLib/SmmMemoryAllocationLib.inf + HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf + CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf + +[LibraryClasses.common.MM_STANDALONE] + MmServicesTableLib|MdePkg/Library/StandaloneMmServicesTableLib/StandaloneMmServicesTableLib.inf + +[LibraryClasses.common.UEFI_APPLICATION] + UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + +# +# Drivers/Libraries within this package +# + +[Components] + UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf + UefiCpuPkg/CpuIoPei/CpuIoPei.inf + UefiCpuPkg/Library/SecPeiDxeTimerLibUefiCpu/SecPeiDxeTimerLibUefiCpu.inf + UefiCpuPkg/Application/Cpuid/Cpuid.inf + UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf + UefiCpuPkg/Library/CpuCacheInfoLib/PeiCpuCacheInfoLib.inf + UefiCpuPkg/Library/CpuCacheInfoLib/DxeCpuCacheInfoLib.inf + +[Components.IA32, Components.X64] + UefiCpuPkg/CpuDxe/CpuDxe.inf + UefiCpuPkg/CpuFeatures/CpuFeaturesPei.inf { + <LibraryClasses> + NULL|UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeaturesLib.inf + } + UefiCpuPkg/CpuFeatures/CpuFeaturesDxe.inf { + <LibraryClasses> + NULL|UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeaturesLib.inf + } + UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf + UefiCpuPkg/CpuIo2Smm/CpuIo2StandaloneMm.inf + UefiCpuPkg/CpuMpPei/CpuMpPei.inf + UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxe.inf + UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.inf + UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.inf + UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf + UefiCpuPkg/Library/CpuCommonFeaturesLib/CpuCommonFeaturesLib.inf + UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf +!if $(TOOL_CHAIN_TAG) != "XCODE5" + UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf +!endif + UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf + UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf + UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf + UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf + UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf + UefiCpuPkg/Library/MpInitLibUp/MpInitLibUp.inf + UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf + UefiCpuPkg/Library/MtrrLib/MtrrLib.inf + UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.inf + UefiCpuPkg/Library/RegisterCpuFeaturesLib/PeiRegisterCpuFeaturesLib.inf + UefiCpuPkg/Library/RegisterCpuFeaturesLib/DxeRegisterCpuFeaturesLib.inf + UefiCpuPkg/Library/SmmCpuPlatformHookLibNull/SmmCpuPlatformHookLibNull.inf + UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.inf + UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLibStm.inf + UefiCpuPkg/Library/SmmCpuFeaturesLib/StandaloneMmCpuFeaturesLib.inf + UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf + UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationPei.inf + UefiCpuPkg/PiSmmCommunication/PiSmmCommunicationSmm.inf + UefiCpuPkg/SecCore/SecCore.inf + UefiCpuPkg/SecMigrationPei/SecMigrationPei.inf + UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf + UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf { + <Defines> + FILE_GUID = D1D74FE9-7A4E-41D3-A0B3-67F13AD34D94 + <LibraryClasses> + SmmCpuFeaturesLib|UefiCpuPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLibStm.inf + } + UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.inf + UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.inf + +[BuildOptions] + *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkg.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkg.uni new file mode 100644 index 00000000..219c1963 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkg.uni @@ -0,0 +1,291 @@ +// /** @file
+// This Package provides UEFI compatible CPU modules and libraries.
+//
+// This Package provides UEFI compatible CPU modules and libraries.
+//
+// Copyright (c) 2007 - 2020, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PACKAGE_ABSTRACT #language en-US "Provides UEFI compatible CPU modules and libraries"
+
+#string STR_PACKAGE_DESCRIPTION #language en-US "This Package provides UEFI compatible CPU modules and libraries."
+
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuLocalApicBaseAddress_PROMPT #language en-US "Configure base address of CPU Local APIC"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuLocalApicBaseAddress_HELP #language en-US "This value is the CPU Local APIC base address, which aligns the address on a 4-KByte boundary."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_ERR_80000001 #language en-US "Invalid value provided."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuInitIpiDelayInMicroSeconds_PROMPT #language en-US "Configure delay value after send an INIT IPI"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuInitIpiDelayInMicroSeconds_HELP #language en-US "Specifies delay value in microseconds after sending out an INIT IPI."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuMaxLogicalProcessorNumber_PROMPT #language en-US "Configure max supported number of Logical Processors"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuMaxLogicalProcessorNumber_HELP #language en-US "Specifies max supported number of Logical Processors."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuApStackSize_PROMPT #language en-US "Configure stack size for Application Processor (AP)"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuApStackSize_HELP #language en-US "This value specifies the Application Processor (AP) stack size, used for Mp Service, which must\n"
+ "aligns the address on a 4-KByte boundary."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuApInitTimeOutInMicroSeconds_PROMPT #language en-US "Timeout for the BSP to detect all APs for the first time."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuApInitTimeOutInMicroSeconds_HELP #language en-US "Specifies timeout value in microseconds for the BSP to detect all APs for the first time."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuBootLogicalProcessorNumber_PROMPT #language en-US "Number of Logical Processors available after platform reset."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuBootLogicalProcessorNumber_HELP #language en-US "Specifies the number of Logical Processors that are available in the preboot environment after platform reset, including BSP and APs."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuMicrocodePatchAddress_PROMPT #language en-US "Microcode Region base address."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuMicrocodePatchAddress_HELP #language en-US "Specifies the base address of the first microcode Patch in the microcode Region."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuMicrocodePatchRegionSize_PROMPT #language en-US "Microcode Region size."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuMicrocodePatchRegionSize_HELP #language en-US "Specifies the size of the microcode Region."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmProfileEnable_PROMPT #language en-US "Enable SMM Profile"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmProfileEnable_HELP #language en-US "Indicates if SMM Profile will be enabled.\n"
+ "If enabled, instruction executions in and data accesses to memory outside of SMRAM will be logged.\n"
+ "It could not be enabled at the same time with SMM static page table feature (PcdCpuSmmStaticPageTable).\n"
+ "This PCD is only for validation purpose. It should be set to false in production.<BR><BR>\n"
+ "TRUE - SMM Profile will be enabled.<BR>\n"
+ "FALSE - SMM Profile will be disabled.<BR>"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmProfileRingBuffer_PROMPT #language en-US "The SMM profile log buffer is a ring buffer"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmProfileRingBuffer_HELP #language en-US "Indicates if the SMM profile log buffer is a ring buffer. If disabled, no additional log can be done when the buffer is full.<BR><BR>\n"
+ "TRUE - the SMM profile log buffer is a ring buffer.<BR>\n"
+ "FALSE - the SMM profile log buffer is a normal buffer.<BR>"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmBlockStartupThisAp_PROMPT #language en-US "SMM Startup AP in a blocking fashion"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmBlockStartupThisAp_HELP #language en-US "Indicates if SMM Startup AP in a blocking fashion.\n"
+ "TRUE - SMM Startup AP in a blocking fashion.<BR>\n"
+ "FALSE - SMM Startup AP in a non-blocking fashion.<BR>"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmStackGuard_PROMPT #language en-US "Enable SMM Stack Guard"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmStackGuard_HELP #language en-US "Indicates if SMM Stack Guard will be enabled. If enabled, stack overflow in SMM can be caught, which eases debugging.<BR><BR>\n"
+ "TRUE - SMM Stack Guard will be enabled.<BR>\n"
+ "FALSE - SMM Stack Guard will be disabled.<BR>"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmEnableBspElection_PROMPT #language en-US "Enable BSP election in SMM"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmEnableBspElection_HELP #language en-US "Indicates if BSP election in SMM will be enabled. If enabled, a BSP will be dynamically elected among all processors in each SMI. Otherwise, processor 0 is always as BSP in each SMI.<BR><BR>\n"
+ "TRUE - BSP election in SMM will be enabled.<BR>\n"
+ "FALSE - BSP election in SMM will be disabled.<BR>"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuHotPlugSupport_PROMPT #language en-US "SMM CPU hot-plug"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuHotPlugSupport_HELP #language en-US "Enable CPU SMM hot-plug?<BR><BR>\n"
+ "TRUE - enabled.<BR>\n"
+ "FALSE - disabled.<BR>"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmDebug_PROMPT #language en-US "Enable SMM Debug"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmDebug_HELP #language en-US "Indicates if SMM Debug will be enabled. If enabled, hardware breakpoints in SMRAM can be set outside of SMM mode and take effect in SMM.<BR><BR>\n"
+ "TRUE - SMM Debug will be enabled.<BR>\n"
+ "FALSE - SMM Debug will be disabled.<BR>"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmFeatureControlMsrLock_PROMPT #language en-US "Lock SMM Feature Control MSR"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmFeatureControlMsrLock_HELP #language en-US "Lock SMM Feature Control MSR?<BR><BR>\n"
+ "TRUE - locked.<BR>\n"
+ "FALSE - unlocked.<BR>"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdPeiTemporaryRamStackSize_PROMPT #language en-US "Stack size in the temporary RAM"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdPeiTemporaryRamStackSize_HELP #language en-US "Specifies stack size in the temporary RAM. 0 means half of TemporaryRamSize."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmProfileSize_PROMPT #language en-US "SMM profile data buffer size"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmProfileSize_HELP #language en-US "Specifies buffer size in bytes to save SMM profile data. The value should be a multiple of 4KB."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmStackSize_PROMPT #language en-US "Processor stack size in SMM"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmStackSize_HELP #language en-US "Specifies stack size in bytes for each processor in SMM."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmShadowStackSize_PROMPT #language en-US "Processor shadow stack size in SMM."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmShadowStackSize_HELP #language en-US "Specifies shadow stack size in bytes for each processor in SMM."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmApSyncTimeout_PROMPT #language en-US "AP synchronization timeout value in SMM"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmApSyncTimeout_HELP #language en-US "Specifies timeout value in microseconds for the BSP in SMM to wait for all APs to come into SMM."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmCodeAccessCheckEnable_PROMPT #language en-US "SMM Code Access Check"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmCodeAccessCheckEnable_HELP #language en-US "Enable SMM Code Access Check? If enabled, the SMM handler cannot execute the code outside SMM regions. This PCD is suggested to TRUE in production image.<BR><BR>\n"
+ "TRUE - SMM Code Access Check will be enabled.<BR>\n"
+ "FALSE - SMM Code Access Check will be disabled.<BR>"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmSyncMode_PROMPT #language en-US "SMM CPU Synchronization Method"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmSyncMode_HELP #language en-US "Indicates the CPU synchronization method used when processing an SMI.<BR><BR>\n"
+ "0x00 - Traditional CPU synchronization method.<BR>\n"
+ "0x01 - Relaxed CPU synchronization method.<BR>"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuS3DataAddress_PROMPT #language en-US "The pointer to a CPU S3 data buffer"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuS3DataAddress_HELP #language en-US "Contains the pointer to a CPU S3 data buffer of structure ACPI_CPU_DATA."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuHotPlugDataAddress_PROMPT #language en-US "The pointer to CPU Hot Plug Data"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuHotPlugDataAddress_HELP #language en-US "Contains the pointer to a CPU Hot Plug Data structure if CPU hot-plug is supported."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuNumberOfReservedVariableMtrrs_PROMPT #language en-US "Number of reserved variable MTRRs"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuNumberOfReservedVariableMtrrs_HELP #language en-US "Specifies the number of variable MTRRs reserved for OS use."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuApLoopMode_PROMPT #language en-US "The AP wait loop state"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuApLoopMode_HELP #language en-US "Specifies the AP wait loop state during POST phase."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuApTargetCstate_PROMPT #language en-US "The specified AP target C-state for Mwait"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuApTargetCstate_HELP #language en-US "Specifies the AP target C-state for Mwait during POST phase."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmStaticPageTable_PROMPT #language en-US "Use static page table for all memory in SMM."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmStaticPageTable_HELP #language en-US "Indicates if SMM uses static page table.\n"
+ "If enabled, SMM will not use on-demand paging. SMM will build static page table for all memory.\n"
+ "This flag only impacts X64 build, because SMM always builds static page table for IA32.\n"
+ "It could not be enabled at the same time with SMM profile feature (PcdCpuSmmProfileEnable).\n"
+ "It could not be enabled also at the same time with heap guard feature for SMM\n"
+ "(PcdHeapGuardPropertyMask in MdeModulePkg).<BR><BR>\n"
+ "TRUE - SMM uses static page table for all memory.<BR>\n"
+ "FALSE - SMM uses static page table for below 4G memory and use on-demand paging for above 4G memory.<BR>"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmStmExceptionStackSize_PROMPT #language en-US "STM exception stack size."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmStmExceptionStackSize_HELP #language en-US "Specifies buffer size in bytes for STM exception stack. The value should be a multiple of 4KB."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuMsegSize_PROMPT #language en-US "MSEG size."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuMsegSize_HELP #language en-US "Specifies buffer size in bytes of MSEG. The value should be a multiple of 4KB."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuFeaturesSupport_PROMPT #language en-US "Supported CPU features."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuFeaturesSupport_HELP #language en-US "Specifies the supported CPU features bit in array."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuFeaturesInitAfterSmmRelocation_PROMPT #language en-US "If CPU features will be initialized after SMM relocation."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuFeaturesInitAfterSmmRelocation_HELP #language en-US "Specifies if CPU features will be initialized after SMM relocation."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuFeaturesInitOnS3Resume_PROMPT #language en-US "If CPU features will be initialized during S3 resume."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuFeaturesInitOnS3Resume_HELP #language en-US "Specifies if CPU features will be initialized during S3 resume."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuFeaturesUserConfiguration_PROMPT #language en-US "User settings for enabling/disabling processor features."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuFeaturesUserConfiguration_HELP #language en-US "Specifies user's desired settings for enabling/disabling processor features."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuClockModulationDutyCycle_PROMPT #language en-US "The encoded values for target duty cycle modulation."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuClockModulationDutyCycle_HELP #language en-US "Specifies the On-demand clock modulation duty cycle when ACPI feature is enabled."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdIsPowerOnReset_PROMPT #language en-US "Current boot is a power-on reset."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdIsPowerOnReset_HELP #language en-US "Indicates if the current boot is a power-on reset."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmRestrictedMemoryAccess_PROMPT #language en-US "Access to non-SMRAM memory is restricted to reserved, runtime and ACPI NVS type after SmmReadyToLock."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmRestrictedMemoryAccess_HELP #language en-US "Indicate access to non-SMRAM memory is restricted to reserved, runtime and ACPI NVS type after SmmReadyToLock.<BR><BR>\n"
+ "MMIO access is always allowed regardless of the value of this PCD.<BR>\n"
+ "Loose of such restriction is only required by RAS components in X64 platforms.<BR>\n"
+ "The PCD value is considered as constantly TRUE in IA32 platforms.<BR>\n"
+ "When the PCD value is TRUE, page table is initialized to cover all memory spaces<BR>\n"
+ "and the memory occupied by page table is protected by page table itself as read-only.<BR>\n"
+ "In X64 build, it cannot be enabled at the same time with SMM profile feature (PcdCpuSmmProfileEnable).<BR>\n"
+ "In X64 build, it could not be enabled also at the same time with heap guard feature for SMM<BR>\n"
+ "(PcdHeapGuardPropertyMask in MdeModulePkg).<BR>\n"
+ "In IA32 build, page table memory is not marked as read-only when either SMM profile feature (PcdCpuSmmProfileEnable)<BR>\n"
+ "or heap guard feature for SMM (PcdHeapGuardPropertyMask in MdeModulePkg) is enabled.<BR>\n"
+ "TRUE - Access to non-SMRAM memory is restricted to reserved, runtime and ACPI NVS type after SmmReadyToLock.<BR>\n"
+ "FALSE - Access to any type of non-SMRAM memory after SmmReadyToLock is allowed.<BR>"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuFeaturesCapability_PROMPT #language en-US "Processor feature capabilities."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuFeaturesCapability_HELP #language en-US "Indicates processor feature capabilities, each bit corresponding to a specific feature."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuFeaturesSetting_PROMPT #language en-US "Actual processor feature settings."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuFeaturesSetting_HELP #language en-US "Specifies actual settings for processor features, each bit corresponding to a specific feature."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuProcTraceMemSize_PROMPT #language en-US "Memory size used by Processor Trace feature."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuProcTraceMemSize_HELP #language en-US "User input the size of memory required when CPU processor trace is enabled.<BR><BR>\n"
+ "Processor trace is enabled through set BIT44(CPU_FEATURE_PROC_TRACE) in PcdCpuFeaturesSetting.<BR><BR>\n"
+ "This PCD is ignored if CPU processor trace is disabled.<BR><BR>\n"
+ "Default value is 0x00 which means 4KB of memory is allocated if CPU processor trace is enabled.<BR>\n"
+ "0x0 - 4K.<BR>\n"
+ "0x1 - 8K.<BR>\n"
+ "0x2 - 16K.<BR>\n"
+ "0x3 - 32K.<BR>\n"
+ "0x4 - 64K.<BR>\n"
+ "0x5 - 128K.<BR>\n"
+ "0x6 - 256K.<BR>\n"
+ "0x7 - 512K.<BR>\n"
+ "0x8 - 1M.<BR>\n"
+ "0x9 - 2M.<BR>\n"
+ "0xA - 4M.<BR>\n"
+ "0xB - 8M.<BR>\n"
+ "0xC - 16M.<BR>\n"
+ "0xD - 32M.<BR>\n"
+ "0xE - 64M.<BR>\n"
+ "0xF - 128M.<BR>\n"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuProcTraceOutputScheme_PROMPT #language en-US "Processor Trace output scheme type."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuProcTraceOutputScheme_HELP #language en-US "User input the processor trace output scheme when CPU processor trace is enabled.<BR><BR>\n"
+ "Processor trace is enabled through set BIT44(CPU_FEATURE_PROC_TRACE) in PcdCpuFeaturesSetting.<BR><BR>\n"
+ "This PCD is ignored if CPU processor trace is disabled.<BR><BR>\n"
+ "Default value is 0 which means single range output scheme will be used if CPU processor trace is enabled.<BR>\n"
+ "0 - Single Range output scheme.<BR>\n"
+ "1 - ToPA(Table of physical address) scheme.<BR>\n"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuStackSwitchExceptionList_PROMPT #language en-US "Specify exception vectors which need switching stack."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuStackSwitchExceptionList_HELP #language en-US "List of exception vectors which need switching stack.\n"
+ "This PCD will only take into effect if PcdCpuStackGuard is enabled.n"
+ "By default exception #DD(8), #PF(14) are supported.n"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuKnownGoodStackSize_PROMPT #language en-US "Specify size of good stack of exception which need switching stack."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuKnownGoodStackSize_HELP #language en-US "Size of good stack for an exception.\n"
+ "This PCD will only take into effect if PcdCpuStackGuard is enabled.\n"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuCoreCrystalClockFrequency_PROMPT #language en-US "Specifies CPUID Leaf 0x15 Time Stamp Counter and Nominal Core Crystal Clock Frequency."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuCoreCrystalClockFrequency_HELP #language en-US "Specifies CPUID Leaf 0x15 Time Stamp Counter and Nominal Core Crystal Clock Frequency.<BR><BR>\n"
+ "TSC Frequency = ECX (core crystal clock frequency) * EBX/EAX.<BR><BR>\n"
+ "This PCD is the nominal frequency of the core crystal clock in Hz as is CPUID Leaf 0x15:ECX.<BR><BR>\n"
+ "Default value is 24000000 for 6th and 7th generation Intel Core processors and Intel Xeon W Processor Family.<BR>\n"
+ "25000000 - Intel Xeon Processor Scalable Family with CPUID signature 06_55H(25MHz).<BR>\n"
+ "24000000 - 6th and 7th generation Intel Core processors and Intel Xeon W Processor Family(24MHz).<BR>\n"
+ "19200000 - Intel Atom processors based on Goldmont Microarchitecture with CPUID signature 06_5CH(19.2MHz).<BR>\n"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmMpTokenCountPerChunk_PROMPT #language en-US "Specify the count of pre allocated SMM MP tokens per chunk.\n"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuSmmMpTokenCountPerChunk_HELP #language en-US "This value used to specify the count of pre allocated SMM MP tokens per chunk.\n"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuApStatusCheckIntervalInMicroSeconds_PROMPT #language en-US "Periodic interval value in microseconds for AP status check in DXE.\n"
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdCpuApStatusCheckIntervalInMicroSeconds_HELP #language en-US "Periodic interval value in microseconds for the status check of APs for StartupAllAPs() and StartupThisAP() executed in non-blocking mode in DXE phase.\n"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsIsEnabled_PROMPT #language en-US "Specifies whether SEV-ES is enabled"
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsIsEnabled_HELP #language en-US "Set to TRUE when running as an SEV-ES guest, FALSE otherwise."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsWorkAreaBase_PROMPT #language en-US "Specify the address of the SEV-ES work area"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsWorkAreaBase_HELP #language en-US "Specifies the address of the work area used by an SEV-ES guest."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsWorkAreaSize_PROMPT #language en-US "Specify the size of the SEV-ES work area"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsWorkAreaSize_HELP #language en-US "Specifies the size of the work area used by an SEV-ES guest."
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkgExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkgExtra.uni new file mode 100644 index 00000000..953f2933 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/UefiCpuPkgExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// UefiCpu Package Localized Strings and Content.
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_PACKAGE_NAME
+#language en-US
+"UefiCpu package"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/Ia32/AsmFuncs.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/Ia32/AsmFuncs.nasm new file mode 100644 index 00000000..c81f8212 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/Ia32/AsmFuncs.nasm @@ -0,0 +1,35 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; AsmFuncs.Asm +; +; Abstract: +; +; Assembly function to set segment selectors. +; +; Notes: +; +;------------------------------------------------------------------------------ + +SECTION .text + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; AsmSetDataSelectors ( +; IN UINT16 SelectorValue +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(AsmSetDataSelectors) +ASM_PFX(AsmSetDataSelectors): + mov eax, [esp + 4] +o16 mov ds, ax +o16 mov es, ax +o16 mov fs, ax +o16 mov gs, ax +o16 mov ss, ax + ret + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume.c new file mode 100644 index 00000000..39b41224 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume.c @@ -0,0 +1,1139 @@ +/** @file + This module produces the EFI_PEI_S3_RESUME2_PPI. + This module works with StandAloneBootScriptExecutor to S3 resume to OS. + This module will execute the boot script saved during last boot and after that, + control is passed to OS waking up handler. + + Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> + Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiPei.h> + +#include <Guid/AcpiS3Context.h> +#include <Guid/BootScriptExecutorVariable.h> +#include <Guid/ExtendedFirmwarePerformance.h> +#include <Guid/EndOfS3Resume.h> +#include <Guid/S3SmmInitDone.h> +#include <Ppi/S3Resume2.h> +#include <Ppi/SmmAccess.h> +#include <Ppi/PostBootScriptTable.h> +#include <Ppi/EndOfPeiPhase.h> +#include <Ppi/SmmCommunication.h> + +#include <Library/DebugLib.h> +#include <Library/BaseLib.h> +#include <Library/PeimEntryPoint.h> +#include <Library/PeiServicesLib.h> +#include <Library/HobLib.h> +#include <Library/PerformanceLib.h> +#include <Library/PeiServicesTablePointerLib.h> +#include <Library/IoLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/PcdLib.h> +#include <Library/DebugAgentLib.h> +#include <Library/LocalApicLib.h> +#include <Library/ReportStatusCodeLib.h> + +#include <Library/HobLib.h> +#include <Library/LockBoxLib.h> +#include <IndustryStandard/Acpi.h> + +/** + This macro aligns the address of a variable with auto storage + duration down to CPU_STACK_ALIGNMENT. + + Since the stack grows downward, the result preserves more of the + stack than the original address (or the same amount), not less. +**/ +#define STACK_ALIGN_DOWN(Ptr) \ + ((UINTN)(Ptr) & ~(UINTN)(CPU_STACK_ALIGNMENT - 1)) + +#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull + +#pragma pack(1) +typedef union { + struct { + UINT32 LimitLow : 16; + UINT32 BaseLow : 16; + UINT32 BaseMid : 8; + UINT32 Type : 4; + UINT32 System : 1; + UINT32 Dpl : 2; + UINT32 Present : 1; + UINT32 LimitHigh : 4; + UINT32 Software : 1; + UINT32 Reserved : 1; + UINT32 DefaultSize : 1; + UINT32 Granularity : 1; + UINT32 BaseHigh : 8; + } Bits; + UINT64 Uint64; +} IA32_GDT; + +// +// Page-Map Level-4 Offset (PML4) and +// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB +// +typedef union { + struct { + UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory + UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write + UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User + UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching + UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached + UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU) + UINT64 Reserved:1; // Reserved + UINT64 MustBeZero:2; // Must Be Zero + UINT64 Available:3; // Available for use by system software + UINT64 PageTableBaseAddress:40; // Page Table Base Address + UINT64 AvabilableHigh:11; // Available for use by system software + UINT64 Nx:1; // No Execute bit + } Bits; + UINT64 Uint64; +} PAGE_MAP_AND_DIRECTORY_POINTER; + +// +// Page Table Entry 2MB +// +typedef union { + struct { + UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory + UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write + UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User + UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching + UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached + UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU) + UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by processor on access to page + UINT64 MustBe1:1; // Must be 1 + UINT64 Global:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write + UINT64 Available:3; // Available for use by system software + UINT64 PAT:1; // + UINT64 MustBeZero:8; // Must be zero; + UINT64 PageTableBaseAddress:31; // Page Table Base Address + UINT64 AvabilableHigh:11; // Available for use by system software + UINT64 Nx:1; // 0 = Execute Code, 1 = No Code Execution + } Bits; + UINT64 Uint64; +} PAGE_TABLE_ENTRY; + +// +// Page Table Entry 1GB +// +typedef union { + struct { + UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory + UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write + UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User + UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching + UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached + UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU) + UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by processor on access to page + UINT64 MustBe1:1; // Must be 1 + UINT64 Global:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write + UINT64 Available:3; // Available for use by system software + UINT64 PAT:1; // + UINT64 MustBeZero:17; // Must be zero; + UINT64 PageTableBaseAddress:22; // Page Table Base Address + UINT64 AvabilableHigh:11; // Available for use by system software + UINT64 Nx:1; // 0 = Execute Code, 1 = No Code Execution + } Bits; + UINT64 Uint64; +} PAGE_TABLE_1G_ENTRY; + +// +// Define two type of smm communicate headers. +// One for 32 bits PEI + 64 bits DXE, the other for 32 bits PEI + 32 bits DXE case. +// +typedef struct { + EFI_GUID HeaderGuid; + UINT32 MessageLength; + UINT8 Data[1]; +} SMM_COMMUNICATE_HEADER_32; + +typedef struct { + EFI_GUID HeaderGuid; + UINT64 MessageLength; + UINT8 Data[1]; +} SMM_COMMUNICATE_HEADER_64; + +#pragma pack() + +// +// Function prototypes +// +/** + a ASM function to transfer control to OS. + + @param S3WakingVector The S3 waking up vector saved in ACPI Facs table + @param AcpiLowMemoryBase a buffer under 1M which could be used during the transfer +**/ +typedef +VOID +(EFIAPI *ASM_TRANSFER_CONTROL) ( + IN UINT32 S3WakingVector, + IN UINT32 AcpiLowMemoryBase + ); + +/** + Restores the platform to its preboot configuration for an S3 resume and + jumps to the OS waking vector. + + This function will restore the platform to its pre-boot configuration that was + pre-stored in the boot script table and transfer control to OS waking vector. + Upon invocation, this function is responsible for locating the following + information before jumping to OS waking vector: + - ACPI tables + - boot script table + - any other information that it needs + + The S3RestoreConfig() function then executes the pre-stored boot script table + and transitions the platform to the pre-boot state. The boot script is recorded + during regular boot using the EFI_S3_SAVE_STATE_PROTOCOL.Write() and + EFI_S3_SMM_SAVE_STATE_PROTOCOL.Write() functions. Finally, this function + transfers control to the OS waking vector. If the OS supports only a real-mode + waking vector, this function will switch from flat mode to real mode before + jumping to the waking vector. If all platform pre-boot configurations are + successfully restored and all other necessary information is ready, this + function will never return and instead will directly jump to the OS waking + vector. If this function returns, it indicates that the attempt to resume + from the ACPI S3 sleep state failed. + + @param[in] This Pointer to this instance of the PEI_S3_RESUME_PPI + + @retval EFI_ABORTED Execution of the S3 resume boot script table failed. + @retval EFI_NOT_FOUND Some necessary information that is used for the S3 + resume boot path could not be located. + +**/ +EFI_STATUS +EFIAPI +S3RestoreConfig2 ( + IN EFI_PEI_S3_RESUME2_PPI *This + ); + +/** + Set data segment selectors value including DS/ES/FS/GS/SS. + + @param[in] SelectorValue Segment selector value to be set. + +**/ +VOID +EFIAPI +AsmSetDataSelectors ( + IN UINT16 SelectorValue + ); + +// +// Globals +// +EFI_PEI_S3_RESUME2_PPI mS3ResumePpi = { S3RestoreConfig2 }; + +EFI_PEI_PPI_DESCRIPTOR mPpiList = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiS3Resume2PpiGuid, + &mS3ResumePpi +}; + +EFI_PEI_PPI_DESCRIPTOR mPpiListPostScriptTable = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gPeiPostScriptTablePpiGuid, + 0 +}; + +EFI_PEI_PPI_DESCRIPTOR mPpiListEndOfPeiTable = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiEndOfPeiSignalPpiGuid, + 0 +}; + +EFI_PEI_PPI_DESCRIPTOR mPpiListS3SmmInitDoneTable = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEdkiiS3SmmInitDoneGuid, + 0 +}; + +// +// Global Descriptor Table (GDT) +// +GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT mGdtEntries[] = { +/* selector { Global Segment Descriptor } */ +/* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, +/* 0x08 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, +/* 0x10 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}}, +/* 0x18 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}}, +/* 0x20 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, +/* 0x28 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}}, +/* 0x30 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}}, +/* 0x38 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 1, 0, 1, 0}}, +/* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, +}; + +#define DATA_SEGEMENT_SELECTOR 0x18 + +// +// IA32 Gdt register +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = { + sizeof (mGdtEntries) - 1, + (UINTN) mGdtEntries + }; + + +/** + The function will check if current waking vector is long mode. + + @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT + + @retval TRUE Current context need long mode waking vector. + @retval FALSE Current context need not long mode waking vector. +**/ +BOOLEAN +IsLongModeWakingVector ( + IN ACPI_S3_CONTEXT *AcpiS3Context + ) +{ + EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; + + Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable)); + if ((Facs == NULL) || + (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) || + ((Facs->FirmwareWakingVector == 0) && (Facs->XFirmwareWakingVector == 0)) ) { + // Something wrong with FACS + return FALSE; + } + if (Facs->XFirmwareWakingVector != 0) { + if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) && + ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) && + ((Facs->OspmFlags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0)) { + // Both BIOS and OS wants 64bit vector + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + return TRUE; + } + } + } + return FALSE; +} + +/** + Signal to SMM through communication buffer way. + + @param[in] HandlerType SMI handler type to be signaled. + +**/ +VOID +SignalToSmmByCommunication ( + IN EFI_GUID *HandlerType + ) +{ + EFI_STATUS Status; + EFI_PEI_SMM_COMMUNICATION_PPI *SmmCommunicationPpi; + UINTN CommSize; + SMM_COMMUNICATE_HEADER_32 Header32; + SMM_COMMUNICATE_HEADER_64 Header64; + VOID *CommBuffer; + + DEBUG ((DEBUG_INFO, "Signal %g to SMM - Enter\n", HandlerType)); + + // + // This buffer consumed in DXE phase, so base on DXE mode to prepare communicate buffer. + // Detect whether DXE is 64 bits mode. + // if (sizeof(UINTN) == sizeof(UINT64), PEI already 64 bits, assume DXE also 64 bits. + // or (FeaturePcdGet (PcdDxeIplSwitchToLongMode)), DXE will switch to 64 bits. + // + if ((sizeof(UINTN) == sizeof(UINT64)) || (FeaturePcdGet (PcdDxeIplSwitchToLongMode))) { + CommBuffer = &Header64; + Header64.MessageLength = 0; + CommSize = OFFSET_OF (SMM_COMMUNICATE_HEADER_64, Data); + } else { + CommBuffer = &Header32; + Header32.MessageLength = 0; + CommSize = OFFSET_OF (SMM_COMMUNICATE_HEADER_32, Data); + } + CopyGuid (CommBuffer, HandlerType); + + Status = PeiServicesLocatePpi ( + &gEfiPeiSmmCommunicationPpiGuid, + 0, + NULL, + (VOID **)&SmmCommunicationPpi + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Locate Smm Communicate Ppi failed (%r)!\n", Status)); + return; + } + + Status = SmmCommunicationPpi->Communicate ( + SmmCommunicationPpi, + (VOID *)CommBuffer, + &CommSize + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "SmmCommunicationPpi->Communicate return failure (%r)!\n", Status)); + } + + DEBUG ((DEBUG_INFO, "Signal %g to SMM - Exit (%r)\n", HandlerType, Status)); + return; +} + +/** + Jump to OS waking vector. + The function will install boot script done PPI, report S3 resume status code, and then jump to OS waking vector. + + @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT + @param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE +**/ +VOID +EFIAPI +S3ResumeBootOs ( + IN ACPI_S3_CONTEXT *AcpiS3Context, + IN PEI_S3_RESUME_STATE *PeiS3ResumeState + ) +{ + EFI_STATUS Status; + EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; + ASM_TRANSFER_CONTROL AsmTransferControl; + UINTN TempStackTop; + UINTN TempStack[0x10]; + + // + // Restore IDT + // + AsmWriteIdtr (&PeiS3ResumeState->Idtr); + + if (PeiS3ResumeState->ReturnStatus != EFI_SUCCESS) { + // + // Report Status code that boot script execution is failed + // + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_S3_BOOT_SCRIPT_ERROR) + ); + } + + // + // NOTE: Because Debug Timer interrupt and system interrupts will be disabled + // in BootScriptExecuteDxe, the rest code in S3ResumeBootOs() cannot be halted + // by soft debugger. + // + + PERF_INMODULE_END ("ScriptExec"); + + // + // Install BootScriptDonePpi + // + PERF_INMODULE_BEGIN ("BootScriptDonePpi"); + + Status = PeiServicesInstallPpi (&mPpiListPostScriptTable); + ASSERT_EFI_ERROR (Status); + + PERF_INMODULE_END ("BootScriptDonePpi"); + + // + // Get ACPI Table Address + // + Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable)); + + if ((Facs == NULL) || + (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) || + ((Facs->FirmwareWakingVector == 0) && (Facs->XFirmwareWakingVector == 0)) ) { + // + // Report Status code that no valid vector is found + // + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_S3_OS_WAKE_ERROR) + ); + CpuDeadLoop (); + return ; + } + + // + // Install EndOfPeiPpi + // + PERF_INMODULE_BEGIN("EndOfPeiPpi"); + + Status = PeiServicesInstallPpi (&mPpiListEndOfPeiTable); + ASSERT_EFI_ERROR (Status); + + PERF_INMODULE_END("EndOfPeiPpi"); + + PERF_INMODULE_BEGIN("EndOfS3Resume"); + + DEBUG ((DEBUG_INFO, "Signal EndOfS3Resume\n")); + // + // Signal EndOfS3Resume to SMM. + // + SignalToSmmByCommunication (&gEdkiiEndOfS3ResumeGuid); + + PERF_INMODULE_END ("EndOfS3Resume"); + + // + // report status code on S3 resume + // + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_PC_OS_WAKE); + + AsmTransferControl = (ASM_TRANSFER_CONTROL)(UINTN)PeiS3ResumeState->AsmTransferControl; + if (Facs->XFirmwareWakingVector != 0) { + // + // Switch to native waking vector + // + TempStackTop = (UINTN)&TempStack + sizeof(TempStack); + DEBUG (( + DEBUG_INFO, + "%a() Stack Base: 0x%x, Stack Size: 0x%x\n", + __FUNCTION__, + TempStackTop, + sizeof (TempStack) + )); + if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) && + ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) && + ((Facs->OspmFlags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0)) { + // + // X64 long mode waking vector + // + DEBUG ((DEBUG_INFO, "Transfer to 64bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector)); + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + AsmEnablePaging64 ( + 0x38, + Facs->XFirmwareWakingVector, + 0, + 0, + (UINT64)(UINTN)TempStackTop + ); + } else { + // + // Report Status code that no valid waking vector is found + // + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_S3_OS_WAKE_ERROR) + ); + DEBUG (( EFI_D_ERROR, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n")); + ASSERT (FALSE); + CpuDeadLoop (); + return ; + } + } else { + // + // IA32 protected mode waking vector (Page disabled) + // + DEBUG ((DEBUG_INFO, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector)); + SwitchStack ( + (SWITCH_STACK_ENTRY_POINT) (UINTN) Facs->XFirmwareWakingVector, + NULL, + NULL, + (VOID *)(UINTN)TempStackTop + ); + } + } else { + // + // 16bit Realmode waking vector + // + DEBUG ((DEBUG_INFO, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN)Facs->FirmwareWakingVector)); + AsmTransferControl (Facs->FirmwareWakingVector, 0x0); + } + + // + // Report Status code the failure of S3Resume + // + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_S3_OS_WAKE_ERROR) + ); + + // + // Never run to here + // + CpuDeadLoop(); +} + +/** + Restore S3 page table because we do not trust ACPINvs content. + If BootScriptExector driver will not run in 64-bit mode, this function will do nothing. + + @param S3NvsPageTableAddress PageTableAddress in ACPINvs + @param Build4GPageTableOnly If BIOS just build 4G page table only +**/ +VOID +RestoreS3PageTables ( + IN UINTN S3NvsPageTableAddress, + IN BOOLEAN Build4GPageTableOnly + ) +{ + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + UINT32 RegEax; + UINT32 RegEdx; + UINT8 PhysicalAddressBits; + EFI_PHYSICAL_ADDRESS PageAddress; + UINTN IndexOfPml4Entries; + UINTN IndexOfPdpEntries; + UINTN IndexOfPageDirectoryEntries; + UINT32 NumberOfPml4EntriesNeeded; + UINT32 NumberOfPdpEntriesNeeded; + PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry; + PAGE_MAP_AND_DIRECTORY_POINTER *PageMap; + PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry; + PAGE_TABLE_ENTRY *PageDirectoryEntry; + VOID *Hob; + BOOLEAN Page1GSupport; + PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry; + UINT64 AddressEncMask; + + // + // Make sure AddressEncMask is contained to smallest supported address field + // + AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64; + + // + // NOTE: We have to ASSUME the page table generation format, because we do not know whole page table information. + // The whole page table is too large to be saved in SMRAM. + // + // The assumption is : whole page table is allocated in CONTINUOUS memory and CR3 points to TOP page. + // + DEBUG ((DEBUG_INFO, "S3NvsPageTableAddress - %x (%x)\n", (UINTN)S3NvsPageTableAddress, (UINTN)Build4GPageTableOnly)); + + // + // By architecture only one PageMapLevel4 exists - so lets allocate storage for it. + // + PageMap = (PAGE_MAP_AND_DIRECTORY_POINTER *)S3NvsPageTableAddress; + S3NvsPageTableAddress += SIZE_4KB; + + Page1GSupport = FALSE; + if (PcdGetBool(PcdUse1GPageTable)) { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000001) { + AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT26) != 0) { + Page1GSupport = TRUE; + } + } + } + + // + // Get physical address bits supported. + // + Hob = GetFirstHob (EFI_HOB_TYPE_CPU); + if (Hob != NULL) { + PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; + } else { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000008) { + AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); + PhysicalAddressBits = (UINT8) RegEax; + } else { + PhysicalAddressBits = 36; + } + } + + // + // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses. + // + ASSERT (PhysicalAddressBits <= 52); + if (PhysicalAddressBits > 48) { + PhysicalAddressBits = 48; + } + + // + // NOTE: In order to save time to create full page table, we just create 4G page table by default. + // And let PF handler in BootScript driver to create more on request. + // + if (Build4GPageTableOnly) { + PhysicalAddressBits = 32; + ZeroMem (PageMap, EFI_PAGES_TO_SIZE(2)); + } + // + // Calculate the table entries needed. + // + if (PhysicalAddressBits <= 39) { + NumberOfPml4EntriesNeeded = 1; + NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30)); + } else { + NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39)); + NumberOfPdpEntriesNeeded = 512; + } + + PageMapLevel4Entry = PageMap; + PageAddress = 0; + for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) { + // + // Each PML4 entry points to a page of Page Directory Pointer entires. + // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop. + // + PageDirectoryPointerEntry = (PAGE_MAP_AND_DIRECTORY_POINTER *)S3NvsPageTableAddress; + S3NvsPageTableAddress += SIZE_4KB; + + // + // Make a PML4 Entry + // + PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry | AddressEncMask; + PageMapLevel4Entry->Bits.ReadWrite = 1; + PageMapLevel4Entry->Bits.Present = 1; + + if (Page1GSupport) { + PageDirectory1GEntry = (VOID *) PageDirectoryPointerEntry; + + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) { + // + // Fill in the Page Directory entries + // + PageDirectory1GEntry->Uint64 = (UINT64)PageAddress | AddressEncMask; + PageDirectory1GEntry->Bits.ReadWrite = 1; + PageDirectory1GEntry->Bits.Present = 1; + PageDirectory1GEntry->Bits.MustBe1 = 1; + } + } else { + for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) { + // + // Each Directory Pointer entries points to a page of Page Directory entires. + // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop. + // + PageDirectoryEntry = (PAGE_TABLE_ENTRY *)S3NvsPageTableAddress; + S3NvsPageTableAddress += SIZE_4KB; + + // + // Fill in a Page Directory Pointer Entries + // + PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry | AddressEncMask; + PageDirectoryPointerEntry->Bits.ReadWrite = 1; + PageDirectoryPointerEntry->Bits.Present = 1; + + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) { + // + // Fill in the Page Directory entries + // + PageDirectoryEntry->Uint64 = (UINT64)PageAddress | AddressEncMask; + PageDirectoryEntry->Bits.ReadWrite = 1; + PageDirectoryEntry->Bits.Present = 1; + PageDirectoryEntry->Bits.MustBe1 = 1; + } + } + } + } + return ; + } else { + // + // If DXE is running 32-bit mode, no need to establish page table. + // + return ; + } +} + +/** + Jump to boot script executor driver. + + The function will close and lock SMRAM and then jump to boot script execute driver to executing S3 boot script table. + + @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT + @param EfiBootScriptExecutorVariable The function entry to executing S3 boot Script table. This function is build in + boot script execute driver +**/ +VOID +EFIAPI +S3ResumeExecuteBootScript ( + IN ACPI_S3_CONTEXT *AcpiS3Context, + IN BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable + ) +{ + EFI_STATUS Status; + PEI_SMM_ACCESS_PPI *SmmAccess; + UINTN Index; + VOID *GuidHob; + PEI_S3_RESUME_STATE *PeiS3ResumeState; + BOOLEAN InterruptStatus; + + DEBUG ((DEBUG_INFO, "S3ResumeExecuteBootScript()\n")); + + // + // Attempt to use content from SMRAM first + // + GuidHob = GetFirstGuidHob (&gEfiAcpiVariableGuid); + if (GuidHob != NULL) { + // + // Last step for SMM - send SMI for initialization + // + + // + // Send SMI to APs + // + SendSmiIpiAllExcludingSelf (); + // + // Send SMI to BSP + // + SendSmiIpi (GetApicId ()); + + Status = PeiServicesLocatePpi ( + &gPeiSmmAccessPpiGuid, + 0, + NULL, + (VOID **) &SmmAccess + ); + if (!EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "Close all SMRAM regions before executing boot script\n")); + + for (Index = 0, Status = EFI_SUCCESS; !EFI_ERROR (Status); Index++) { + Status = SmmAccess->Close ((EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), SmmAccess, Index); + } + + DEBUG ((DEBUG_INFO, "Lock all SMRAM regions before executing boot script\n")); + + for (Index = 0, Status = EFI_SUCCESS; !EFI_ERROR (Status); Index++) { + Status = SmmAccess->Lock ((EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), SmmAccess, Index); + } + } + + DEBUG ((DEBUG_INFO, "Signal S3SmmInitDone\n")); + // + // Install S3SmmInitDone PPI. + // + Status = PeiServicesInstallPpi (&mPpiListS3SmmInitDoneTable); + ASSERT_EFI_ERROR (Status); + // + // Signal S3SmmInitDone to SMM. + // + SignalToSmmByCommunication (&gEdkiiS3SmmInitDoneGuid); + } + + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + AsmWriteCr3 ((UINTN)AcpiS3Context->S3NvsPageTableAddress); + } + + InterruptStatus = SaveAndDisableInterrupts (); + // + // Need to make sure the GDT is loaded with values that support long mode and real mode. + // + AsmWriteGdtr (&mGdt); + // + // update segment selectors per the new GDT. + // + AsmSetDataSelectors (DATA_SEGEMENT_SELECTOR); + // + // Restore interrupt state. + // + SetInterruptState (InterruptStatus); + + // + // Prepare data for return back + // + PeiS3ResumeState = AllocatePool (sizeof(*PeiS3ResumeState)); + if (PeiS3ResumeState == NULL) { + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_S3_RESUME_FAILED) + ); + ASSERT (FALSE); + } + DEBUG ((DEBUG_INFO, "PeiS3ResumeState - %x\r\n", PeiS3ResumeState)); + PeiS3ResumeState->ReturnCs = 0x10; + PeiS3ResumeState->ReturnEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)S3ResumeBootOs; + PeiS3ResumeState->ReturnStackPointer = (EFI_PHYSICAL_ADDRESS)STACK_ALIGN_DOWN (&Status); + // + // Save IDT + // + AsmReadIdtr (&PeiS3ResumeState->Idtr); + + // + // Report Status Code to indicate S3 boot script execution + // + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_PC_S3_BOOT_SCRIPT); + + PERF_INMODULE_BEGIN ("ScriptExec"); + + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + // + // X64 S3 Resume + // + DEBUG ((DEBUG_INFO, "Enable X64 and transfer control to Standalone Boot Script Executor\r\n")); + + // + // Switch to long mode to complete resume. + // + AsmEnablePaging64 ( + 0x38, + EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint, + (UINT64)(UINTN)AcpiS3Context, + (UINT64)(UINTN)PeiS3ResumeState, + (UINT64)(UINTN)(AcpiS3Context->BootScriptStackBase + AcpiS3Context->BootScriptStackSize) + ); + } else { + // + // IA32 S3 Resume + // + DEBUG ((DEBUG_INFO, "transfer control to Standalone Boot Script Executor\r\n")); + SwitchStack ( + (SWITCH_STACK_ENTRY_POINT) (UINTN) EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint, + (VOID *)AcpiS3Context, + (VOID *)PeiS3ResumeState, + (VOID *)(UINTN)(AcpiS3Context->BootScriptStackBase + AcpiS3Context->BootScriptStackSize) + ); + } + + // + // Never run to here + // + CpuDeadLoop(); +} +/** + Restores the platform to its preboot configuration for an S3 resume and + jumps to the OS waking vector. + + This function will restore the platform to its pre-boot configuration that was + pre-stored in the boot script table and transfer control to OS waking vector. + Upon invocation, this function is responsible for locating the following + information before jumping to OS waking vector: + - ACPI tables + - boot script table + - any other information that it needs + + The S3RestoreConfig() function then executes the pre-stored boot script table + and transitions the platform to the pre-boot state. The boot script is recorded + during regular boot using the EFI_S3_SAVE_STATE_PROTOCOL.Write() and + EFI_S3_SMM_SAVE_STATE_PROTOCOL.Write() functions. Finally, this function + transfers control to the OS waking vector. If the OS supports only a real-mode + waking vector, this function will switch from flat mode to real mode before + jumping to the waking vector. If all platform pre-boot configurations are + successfully restored and all other necessary information is ready, this + function will never return and instead will directly jump to the OS waking + vector. If this function returns, it indicates that the attempt to resume + from the ACPI S3 sleep state failed. + + @param[in] This Pointer to this instance of the PEI_S3_RESUME_PPI + + @retval EFI_ABORTED Execution of the S3 resume boot script table failed. + @retval EFI_NOT_FOUND Some necessary information that is used for the S3 + resume boot path could not be located. + +**/ +EFI_STATUS +EFIAPI +S3RestoreConfig2 ( + IN EFI_PEI_S3_RESUME2_PPI *This + ) +{ + EFI_STATUS Status; + PEI_SMM_ACCESS_PPI *SmmAccess; + UINTN Index; + ACPI_S3_CONTEXT *AcpiS3Context; + EFI_PHYSICAL_ADDRESS TempEfiBootScriptExecutorVariable; + EFI_PHYSICAL_ADDRESS TempAcpiS3Context; + BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable; + UINTN VarSize; + EFI_SMRAM_DESCRIPTOR *SmramDescriptor; + SMM_S3_RESUME_STATE *SmmS3ResumeState; + VOID *GuidHob; + BOOLEAN Build4GPageTableOnly; + BOOLEAN InterruptStatus; + IA32_CR0 Cr0; + + TempAcpiS3Context = 0; + TempEfiBootScriptExecutorVariable = 0; + + DEBUG ((DEBUG_INFO, "Enter S3 PEIM\r\n")); + + VarSize = sizeof (EFI_PHYSICAL_ADDRESS); + Status = RestoreLockBox ( + &gEfiAcpiVariableGuid, + &TempAcpiS3Context, + &VarSize + ); + ASSERT_EFI_ERROR (Status); + + Status = RestoreLockBox ( + &gEfiAcpiS3ContextGuid, + NULL, + NULL + ); + ASSERT_EFI_ERROR (Status); + + AcpiS3Context = (ACPI_S3_CONTEXT *)(UINTN)TempAcpiS3Context; + ASSERT (AcpiS3Context != NULL); + + VarSize = sizeof (EFI_PHYSICAL_ADDRESS); + Status = RestoreLockBox ( + &gEfiBootScriptExecutorVariableGuid, + &TempEfiBootScriptExecutorVariable, + &VarSize + ); + ASSERT_EFI_ERROR (Status); + + Status = RestoreLockBox ( + &gEfiBootScriptExecutorContextGuid, + NULL, + NULL + ); + ASSERT_EFI_ERROR (Status); + + EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *) (UINTN) TempEfiBootScriptExecutorVariable; + ASSERT (EfiBootScriptExecutorVariable != NULL); + + DEBUG (( DEBUG_INFO, "AcpiS3Context = %x\n", AcpiS3Context)); + DEBUG (( DEBUG_INFO, "Waking Vector = %x\n", ((EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable)))->FirmwareWakingVector)); + DEBUG (( DEBUG_INFO, "AcpiS3Context->AcpiFacsTable = %x\n", AcpiS3Context->AcpiFacsTable)); + DEBUG (( DEBUG_INFO, "AcpiS3Context->IdtrProfile = %x\n", AcpiS3Context->IdtrProfile)); + DEBUG (( DEBUG_INFO, "AcpiS3Context->S3NvsPageTableAddress = %x\n", AcpiS3Context->S3NvsPageTableAddress)); + DEBUG (( DEBUG_INFO, "AcpiS3Context->S3DebugBufferAddress = %x\n", AcpiS3Context->S3DebugBufferAddress)); + DEBUG (( DEBUG_INFO, "AcpiS3Context->BootScriptStackBase = %x\n", AcpiS3Context->BootScriptStackBase)); + DEBUG (( DEBUG_INFO, "AcpiS3Context->BootScriptStackSize = %x\n", AcpiS3Context->BootScriptStackSize)); + DEBUG (( DEBUG_INFO, "EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = %x\n", EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint)); + + // + // Additional step for BootScript integrity - we only handle BootScript and BootScriptExecutor. + // Script dispatch image and context (parameter) are handled by platform. + // We just use restore all lock box in place, no need restore one by one. + // + Status = RestoreAllLockBoxInPlace (); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + // Something wrong + CpuDeadLoop (); + } + + if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { + // + // Need reconstruct page table here, since we do not trust ACPINvs. + // + if (IsLongModeWakingVector (AcpiS3Context)) { + Build4GPageTableOnly = FALSE; + } else { + Build4GPageTableOnly = TRUE; + } + RestoreS3PageTables ((UINTN)AcpiS3Context->S3NvsPageTableAddress, Build4GPageTableOnly); + } + + // + // Attempt to use content from SMRAM first + // + GuidHob = GetFirstGuidHob (&gEfiAcpiVariableGuid); + if (GuidHob != NULL) { + // + // Below SwitchStack/AsmEnablePaging64 function has + // assumption that it's in 32 bits mode now. + // Add ASSERT code to indicate this assumption. + // + ASSERT(sizeof (UINTN) == sizeof (UINT32)); + + Status = PeiServicesLocatePpi ( + &gPeiSmmAccessPpiGuid, + 0, + NULL, + (VOID **) &SmmAccess + ); + for (Index = 0; !EFI_ERROR (Status); Index++) { + Status = SmmAccess->Open ((EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), SmmAccess, Index); + } + + SmramDescriptor = (EFI_SMRAM_DESCRIPTOR *) GET_GUID_HOB_DATA (GuidHob); + SmmS3ResumeState = (SMM_S3_RESUME_STATE *)(UINTN)SmramDescriptor->CpuStart; + + SmmS3ResumeState->ReturnCs = AsmReadCs (); + SmmS3ResumeState->ReturnEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)S3ResumeExecuteBootScript; + SmmS3ResumeState->ReturnContext1 = (EFI_PHYSICAL_ADDRESS)(UINTN)AcpiS3Context; + SmmS3ResumeState->ReturnContext2 = (EFI_PHYSICAL_ADDRESS)(UINTN)EfiBootScriptExecutorVariable; + SmmS3ResumeState->ReturnStackPointer = (EFI_PHYSICAL_ADDRESS)STACK_ALIGN_DOWN (&Status); + + DEBUG (( DEBUG_INFO, "SMM S3 Signature = %x\n", SmmS3ResumeState->Signature)); + DEBUG (( DEBUG_INFO, "SMM S3 Stack Base = %x\n", SmmS3ResumeState->SmmS3StackBase)); + DEBUG (( DEBUG_INFO, "SMM S3 Stack Size = %x\n", SmmS3ResumeState->SmmS3StackSize)); + DEBUG (( DEBUG_INFO, "SMM S3 Resume Entry Point = %x\n", SmmS3ResumeState->SmmS3ResumeEntryPoint)); + DEBUG (( DEBUG_INFO, "SMM S3 CR0 = %x\n", SmmS3ResumeState->SmmS3Cr0)); + DEBUG (( DEBUG_INFO, "SMM S3 CR3 = %x\n", SmmS3ResumeState->SmmS3Cr3)); + DEBUG (( DEBUG_INFO, "SMM S3 CR4 = %x\n", SmmS3ResumeState->SmmS3Cr4)); + DEBUG (( DEBUG_INFO, "SMM S3 Return CS = %x\n", SmmS3ResumeState->ReturnCs)); + DEBUG (( DEBUG_INFO, "SMM S3 Return Entry Point = %x\n", SmmS3ResumeState->ReturnEntryPoint)); + DEBUG (( DEBUG_INFO, "SMM S3 Return Context1 = %x\n", SmmS3ResumeState->ReturnContext1)); + DEBUG (( DEBUG_INFO, "SMM S3 Return Context2 = %x\n", SmmS3ResumeState->ReturnContext2)); + DEBUG (( DEBUG_INFO, "SMM S3 Return Stack Pointer = %x\n", SmmS3ResumeState->ReturnStackPointer)); + DEBUG (( DEBUG_INFO, "SMM S3 Smst = %x\n", SmmS3ResumeState->Smst)); + + if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_32) { + SwitchStack ( + (SWITCH_STACK_ENTRY_POINT)(UINTN)SmmS3ResumeState->SmmS3ResumeEntryPoint, + (VOID *)AcpiS3Context, + 0, + (VOID *)(UINTN)(SmmS3ResumeState->SmmS3StackBase + SmmS3ResumeState->SmmS3StackSize) + ); + } + if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_64) { + // + // Switch to long mode to complete resume. + // + + InterruptStatus = SaveAndDisableInterrupts (); + // + // Need to make sure the GDT is loaded with values that support long mode and real mode. + // + AsmWriteGdtr (&mGdt); + // + // update segment selectors per the new GDT. + // + AsmSetDataSelectors (DATA_SEGEMENT_SELECTOR); + // + // Restore interrupt state. + // + SetInterruptState (InterruptStatus); + + Cr0.UintN = AsmReadCr0 (); + if (Cr0.Bits.PG != 0) { + // + // We're in 32-bit mode, with paging enabled. We can't set CR3 to + // the 64-bit page tables without first disabling paging. + // + Cr0.Bits.PG = 0; + AsmWriteCr0 (Cr0.UintN); + } + AsmWriteCr3 ((UINTN)SmmS3ResumeState->SmmS3Cr3); + + // + // Disable interrupt of Debug timer, since IDT table cannot work in long mode. + // NOTE: On x64 platforms, because DisablePaging64() will disable interrupts, + // the code in S3ResumeExecuteBootScript() cannot be halted by soft debugger. + // + SaveAndSetDebugTimerInterrupt (FALSE); + + AsmEnablePaging64 ( + 0x38, + SmmS3ResumeState->SmmS3ResumeEntryPoint, + (UINT64)(UINTN)AcpiS3Context, + 0, + SmmS3ResumeState->SmmS3StackBase + SmmS3ResumeState->SmmS3StackSize + ); + } + + } + + S3ResumeExecuteBootScript (AcpiS3Context, EfiBootScriptExecutorVariable ); + return EFI_SUCCESS; +} +/** + Main entry for S3 Resume PEIM. + + This routine is to install EFI_PEI_S3_RESUME2_PPI. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Pointer to PEI Services table. + + @retval EFI_SUCCESS S3Resume Ppi is installed successfully. + +**/ +EFI_STATUS +EFIAPI +PeimS3ResumeEntryPoint ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + + // + // Install S3 Resume Ppi + // + Status = (**PeiServices).InstallPpi (PeiServices, &mPpiList); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.inf b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.inf new file mode 100644 index 00000000..00fcbdb4 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.inf @@ -0,0 +1,101 @@ +## @file +# S3 Resume Module installs EFI_PEI_S3_RESUME2_PPI. +# +# This module works with StandAloneBootScriptExecutor to S3 resume to OS. +# This module will excute the boot script saved during last boot and after that, +# control is passed to OS waking up handler. +# +# Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = S3Resume2Pei + MODULE_UNI_FILE = S3Resume2Pei.uni + FILE_GUID = 89E549B0-7CFE-449d-9BA3-10D8B2312D71 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = PeimS3ResumeEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +# +# This module is not always workable in IA32 and X64 mode. It has below result: +# when it works with SMM mode: +# =============================== +# SMM:used SMM:unused +# PEI:IA32 works works +# PEI:X64 fails works +# =============================== +# + +[Sources] + S3Resume.c + +[Sources.IA32] + Ia32/AsmFuncs.nasm + +[Sources.X64] + X64/AsmFuncs.nasm + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + PeiServicesTablePointerLib + PerformanceLib + HobLib + PeiServicesLib + PeimEntryPoint + BaseLib + DebugLib + PcdLib + IoLib + BaseMemoryLib + MemoryAllocationLib + DebugAgentLib + LocalApicLib + ReportStatusCodeLib + LockBoxLib + +[Guids] + gEfiBootScriptExecutorVariableGuid ## SOMETIMES_CONSUMES ## UNDEFINED # LockBox + gEfiBootScriptExecutorContextGuid ## SOMETIMES_CONSUMES ## UNDEFINED # LockBox + ## SOMETIMES_CONSUMES ## HOB + ## SOMETIMES_CONSUMES ## UNDEFINED # LockBox + gEfiAcpiVariableGuid + gEfiAcpiS3ContextGuid ## SOMETIMES_CONSUMES ## UNDEFINED # LockBox + gEdkiiEndOfS3ResumeGuid ## SOMETIMES_CONSUMES ## UNDEFINED # Used to do smm communication + ## SOMETIMES_PRODUCES ## UNDEFINED # Install PPI + ## SOMETIMES_CONSUMES ## UNDEFINED # Used to do smm communication + gEdkiiS3SmmInitDoneGuid + +[Ppis] + gEfiPeiS3Resume2PpiGuid ## PRODUCES + gPeiSmmAccessPpiGuid ## SOMETIMES_CONSUMES + gPeiPostScriptTablePpiGuid ## SOMETIMES_PRODUCES + gEfiEndOfPeiSignalPpiGuid ## SOMETIMES_PRODUCES + gEfiPeiSmmCommunicationPpiGuid ## SOMETIMES_CONSUMES + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode ## CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + S3Resume2PeiExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.uni new file mode 100644 index 00000000..6b76cab8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.uni @@ -0,0 +1,20 @@ +// /** @file
+// S3 Resume Module installs EFI_PEI_S3_RESUME2_PPI.
+//
+// This module works with StandAloneBootScriptExecutor to S3 resume to OS.
+// This module will excute the boot script saved during last boot and after that,
+// control is passed to OS waking up handler.
+//
+// Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "S3 Resume Module installs EFI_PEI_S3_RESUME2_PPI"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module works with StandAloneBootScriptExecutor to S3 resume to OS.\n"
+ "This module will execute the boot script saved during last boot and after that,\n"
+ "control is passed to the OS waking up handler."
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2PeiExtra.uni b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2PeiExtra.uni new file mode 100644 index 00000000..d84d13f5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2PeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// S3Resume2Pei Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"S3 Resume v2 PEI Module"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/X64/AsmFuncs.nasm b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/X64/AsmFuncs.nasm new file mode 100644 index 00000000..ad1e2ff5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Universal/Acpi/S3Resume2Pei/X64/AsmFuncs.nasm @@ -0,0 +1,35 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; AsmFuncs.Asm +; +; Abstract: +; +; Assembly function to set segment selectors. +; +; Notes: +; +;------------------------------------------------------------------------------ + +DEFAULT REL +SECTION .text + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; AsmSetDataSelectors ( +; IN UINT16 SelectorValue +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(AsmSetDataSelectors) +ASM_PFX(AsmSetDataSelectors): +o16 mov ds, cx +o16 mov es, cx +o16 mov fs, cx +o16 mov gs, cx +o16 mov ss, cx + ret + |