From 9027bd6be1d732b46c0433664c9ae1ba90f5eba1 Mon Sep 17 00:00:00 2001 From: vit9696 Date: Mon, 5 Aug 2019 17:11:24 +0300 Subject: [PATCH] OcAppleBootCompatLib: Initial working prototype --- .../OcAppleBootCompatLib/BootCompatInternal.h | 4 - Library/OcAppleBootCompatLib/KernelSupport.c | 2 + .../OcAppleBootCompatLib/ServiceOverrides.c | 89 +++++++++++++++---- .../OcAppleBootCompatLib/X64/ContextSwitch.h | 3 - .../X64/ContextSwitch.nasm | 81 ++++++++++++++--- 5 files changed, 140 insertions(+), 39 deletions(-) diff --git a/Library/OcAppleBootCompatLib/BootCompatInternal.h b/Library/OcAppleBootCompatLib/BootCompatInternal.h index d5332bfe..cb1e7598 100644 --- a/Library/OcAppleBootCompatLib/BootCompatInternal.h +++ b/Library/OcAppleBootCompatLib/BootCompatInternal.h @@ -155,10 +155,6 @@ typedef struct SERVICES_OVERRIDE_STATE_ { /// EFI_PHYSICAL_ADDRESS MinAllocatedAddr; /// - /// Maximum address allocated by AlocatePages. - /// - EFI_PHYSICAL_ADDRESS MaxAllocatedAddr; - /// /// Apple hibernate image address allocated by AlocatePages. /// EFI_PHYSICAL_ADDRESS HibernateImageAddress; diff --git a/Library/OcAppleBootCompatLib/KernelSupport.c b/Library/OcAppleBootCompatLib/KernelSupport.c index 85e87a89..02a54e47 100644 --- a/Library/OcAppleBootCompatLib/KernelSupport.c +++ b/Library/OcAppleBootCompatLib/KernelSupport.c @@ -15,6 +15,7 @@ #include "BootCompatInternal.h" +#include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/Library/OcAppleBootCompatLib/ServiceOverrides.c b/Library/OcAppleBootCompatLib/ServiceOverrides.c index 117d2d27..4125c4e8 100644 --- a/Library/OcAppleBootCompatLib/ServiceOverrides.c +++ b/Library/OcAppleBootCompatLib/ServiceOverrides.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -104,6 +105,63 @@ ForceExitBootServices ( return Status; } +/** + Protect CSM region in memory map from relocation. + + @param[in,out] MemoryMapSize Memory map size in bytes, updated on shrink. + @param[in,out] MemoryMap Memory map to shrink. + @param[in] DescriptorSize Memory map descriptor size in bytes. +**/ +STATIC +VOID +ProtectCsmRegion ( + IN UINTN MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN UINTN DescriptorSize + ) +{ + UINTN NumEntries; + UINTN Index; + EFI_MEMORY_DESCRIPTOR *Desc; + UINTN PhysicalEnd; + + // + // AMI CSM module allocates up to two regions for legacy video output. + // 1. For PMM and EBDA areas. + // On Ivy Bridge and below it ends at 0xA0000-0x1000-0x1 and has EfiBootServicesCode type. + // On Haswell and above it is allocated below 0xA0000 address with the same type. + // 2. For Intel RC S3 reserved area, fixed from 0x9F000 to 0x9FFFF. + // On Sandy Bridge and below it is not present in memory map. + // On Ivy Bridge and newer it is present as EfiRuntimeServicesData. + // Starting from at least SkyLake it is present as EfiReservedMemoryType. + // + // Prior to AptioMemoryFix EfiRuntimeServicesData could have been relocated by boot.efi, + // and the 2nd region could have been overwritten by the kernel. Now it is no longer the + // case, and only the 1st region may need special handling. + // For the 1st region there appear to be (unconfirmed) reports that it may still be accessed + // after waking from sleep. This does not seem to be valid according to AMI code, but we still + // protect it in case such systems really exist. + // + // Initially researched and fixed on GIGABYTE boards by Slice. + // + + Desc = MemoryMap; + NumEntries = MemoryMapSize / DescriptorSize; + + for (Index = 0; Index < NumEntries; ++Index) { + if (Desc->NumberOfPages > 0 && Desc->Type == EfiBootServicesData) { + PhysicalEnd = LAST_DESCRIPTOR_ADDR (Desc) + 1; + + if (PhysicalEnd >= 0x9E000 && PhysicalEnd < 0xA0000) { + Desc->Type = EfiACPIMemoryNVS; + break; + } + } + + Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize); + } +} + /** UEFI Boot Services StartImage override. Called to start an efi image. If this is boot.efi, then our overrides are enabled. @@ -128,7 +186,6 @@ OcStartImage ( // Clear monitoring vars // BootCompat->ServiceState.MinAllocatedAddr = 0; - BootCompat->ServiceState.MaxAllocatedAddr = 0; if (AppleLoadedImage != NULL) { // @@ -191,7 +248,6 @@ OcAllocatePages ( ) { EFI_STATUS Status; - EFI_PHYSICAL_ADDRESS UpperAddr; BOOT_COMPAT_CONTEXT *BootCompat; BootCompat = GetBootCompatContext (); @@ -206,22 +262,13 @@ OcAllocatePages ( if (!EFI_ERROR (Status) && BootCompat->ServiceState.AppleBootNestedCount > 0) { if (Type == AllocateAddress && MemoryType == EfiLoaderData) { // - // Called from boot.efi - // - UpperAddr = *Memory + EFI_PAGES_TO_SIZE (NumberOfPages); - - // - // Store min and max mem: they can be used later to determine - // start and end of kernel boot or hibernation images. + // Called from boot.efi. + // Store minimally allocated address to find kernel image start. // if (BootCompat->ServiceState.MinAllocatedAddr == 0 || *Memory < BootCompat->ServiceState.MinAllocatedAddr) { BootCompat->ServiceState.MinAllocatedAddr = *Memory; } - - if (UpperAddr > BootCompat->ServiceState.MaxAllocatedAddr) { - BootCompat->ServiceState.MaxAllocatedAddr = UpperAddr; - } } else if (BootCompat->ServiceState.AppleHibernateWake && Type == AllocateAnyPages && MemoryType == EfiLoaderData && BootCompat->ServiceState.HibernateImageAddress == 0) { @@ -271,12 +318,11 @@ OcGetMemoryMap ( if (BootCompat->ServiceState.AppleBootNestedCount > 0) { if (BootCompat->Settings.ProtectCsmRegion) { - // FIXME: Implement. - //ProtectCsmRegion ( - // *MemoryMapSize, - // MemoryMap, - // *DescriptorSize - // ); + ProtectCsmRegion ( + *MemoryMapSize, + MemoryMap, + *DescriptorSize + ); } if (BootCompat->Settings.ShrinkMemoryMap) { @@ -504,9 +550,12 @@ InstallServiceOverrides ( EFI_STATUS Status; VOID *Registration; UEFI_SERVICES_POINTERS *ServicePtrs; + EFI_TPL OriginalTpl; ServicePtrs = &BootCompat->ServicePtrs; + OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + ServicePtrs->AllocatePages = gBS->AllocatePages; ServicePtrs->GetMemoryMap = gBS->GetMemoryMap; ServicePtrs->ExitBootServices = gBS->ExitBootServices; @@ -525,6 +574,8 @@ InstallServiceOverrides ( gRT->Hdr.CRC32 = 0; gRT->Hdr.CRC32 = CalculateCrc32 (gRT, gRT->Hdr.HeaderSize); + gBS->RestoreTPL (OriginalTpl); + // // Allocate memory pool if needed. // diff --git a/Library/OcAppleBootCompatLib/X64/ContextSwitch.h b/Library/OcAppleBootCompatLib/X64/ContextSwitch.h index c1588359..a6772e48 100644 --- a/Library/OcAppleBootCompatLib/X64/ContextSwitch.h +++ b/Library/OcAppleBootCompatLib/X64/ContextSwitch.h @@ -39,7 +39,6 @@ typedef PACKED struct ASM_SUPPORT_STATE_ { UINT16 SavedES; UINT16 SavedFS; UINT16 SavedGS; - UINT16 SavedSS; UINT64 SavedGDTR32; UINT16 SavedGDTR32Limit; @@ -50,8 +49,6 @@ typedef PACKED struct ASM_SUPPORT_STATE_ { UINT16 SavedES32; UINT16 SavedFS32; UINT16 SavedGS32; - UINT16 SavedSS32; - UINT32 SavedESP32; VOID *KernelEntry; } ASM_SUPPORT_STATE; diff --git a/Library/OcAppleBootCompatLib/X64/ContextSwitch.nasm b/Library/OcAppleBootCompatLib/X64/ContextSwitch.nasm index b75b4584..b8fcdaf6 100755 --- a/Library/OcAppleBootCompatLib/X64/ContextSwitch.nasm +++ b/Library/OcAppleBootCompatLib/X64/ContextSwitch.nasm @@ -37,7 +37,6 @@ struc ASM_SUPPORT_STATE .SavedES resw 1 .SavedFS resw 1 .SavedGS resw 1 - .SavedSS resw 1 ;------------------------------------------------------------------------------ ; 32-bit state @@ -52,8 +51,6 @@ struc ASM_SUPPORT_STATE .SavedES32 resw 1 .SavedFS32 resw 1 .SavedGS32 resw 1 - .SavedSS32 resw 1 - .SavedESP32 resd 1 ;------------------------------------------------------------------------------ ; Kernel entry address. @@ -100,9 +97,71 @@ BITS 64 mov word [rcx + ASM_SUPPORT_STATE.SavedES], es mov word [rcx + ASM_SUPPORT_STATE.SavedFS], fs mov word [rcx + ASM_SUPPORT_STATE.SavedGS], gs - mov word [rcx + ASM_SUPPORT_STATE.SavedSS], ss ret +;------------------------------------------------------------------------------ +; Apple kernel starts through call gate, an assembly structure allocated in +; 32-bit high memory, that transitions to 32-bit mode and then calls the kernel +; with 32-bit GDT and UEFI stack. +; +; KernelCallGate: +; lea rax, StartKernelIn32Bit +; mov cs:gKernelBooter32, eax +; lea rax, gKernelGdtTable +; mov cs:gKernelGdtBase, rax +; lgdt fword ptr cs:gKernelGdtLimit +; mov ax, 10h +; mov ds, ax +; mov es, ax +; mov gs, ax +; mov fs, ax +; lea rax, gKernelBooter32 +; jmp fword ptr [rax] +; +; StartKernelIn32Bit: +; mov rax, cr0 +; btr eax, 1Fh +; mov cr0, rax +; mov ebx, ecx ; ebx = boot-args +; mov edi, edx +; ; Disable long mode +; mov ecx, 0C0000080h ; EFER MSR number. +; rdmsr +; btr eax, 8 ; Set LME=0. +; wrmsr +; jmp short SwitchTo32Bit +; +; SwitchTo32Bit: +; mov eax, ebx +; jmp rdi ; Jump to kernel +; hlt +; ret +; +; gKernelBooter32: +; dd 0 +; dw 8 ; New CS value +; gKernelGdtLimit: ; IA32_DESCRIPTOR +; dw 18h +; gKernelGdtBase: +; dq 0 +; gKernelGdtTable: ; Array of IA32_GDT. +; dw 0 ; [0] = LimitLow +; dw 0 ; [0] = BaseLow +; db 0 ; [0] = BaseMid +; dw 0 ; [0] = Flags +; db 0 ; [0] = BaseHigh +; dw 0FFFFh ; [1] = LimitLow +; dw 0 ; [1] = BaseLow +; db 0 ; [1] = BaseMid +; dw 0CF9Eh ; [1] - Flags +; db 0 ; [1] = BaseHigh +; dw 0FFFFh ; [2] = LimitLow +; dw 0 ; [2] = BaseLow +; db 0 ; [2] = BaseMid +; dw 0CF92h ; [2] = Flags +; db 0 ; [2] = BaseHigh +;------------------------------------------------------------------------------ + ;------------------------------------------------------------------------------ ; Long (far) return. ; retfq (lretq) - 64-bit encoding 48 CB @@ -178,12 +237,10 @@ ASM_PFX(gOcAbcAsmStateAddr32): mov word [ebx + ASM_SUPPORT_STATE.SavedES32], es mov word [ebx + ASM_SUPPORT_STATE.SavedFS32], fs mov word [ebx + ASM_SUPPORT_STATE.SavedGS32], gs - mov word [ebx + ASM_SUPPORT_STATE.SavedSS32], ss - mov dword [ebx + ASM_SUPPORT_STATE.SavedESP32], esp ; ; Transition to 64-bit mode... - ; FIXME: we should ensure interrupts are disabled here. + ; boot.efi disables interrupts for us, so we are safe. ; ; Load saved UEFI GDT and IDT. @@ -232,10 +289,9 @@ BITS 64 mov fs, ax mov ax, word [rbx + ASM_SUPPORT_STATE.SavedGS] mov gs, ax - mov ax, word [rbx + ASM_SUPPORT_STATE.SavedSS] - mov ss, ax - ; Assume the stack is useasble but potentially misaligned. + ; boot.efi preserves ss selector from UEFI GDT to just use UEFI stack (and memory) as is. + ; For this reason just assume the stack is useable but align it for definite 64-bit compat. and rsp, 0FFFFFFFFFFFFFFF0h ; Call AppleMapPrepareKernelState (rcx = rax = bootArgs, rdx = 0 = 32 bit kernel jump). @@ -287,6 +343,8 @@ AsmJumpFromKernel32Compatibility: ; ; Reload saved 32 bit state data. + ; Since boot.efi relies on segment registers shadow part and preserves ss value, + ; which contains previously read GDT data, we are not allowed to later update it. lidt [ebx + ASM_SUPPORT_STATE.SavedIDTR32] mov ax, word [ebx + ASM_SUPPORT_STATE.SavedDS32] mov ds, ax @@ -296,9 +354,6 @@ AsmJumpFromKernel32Compatibility: mov fs, ax mov ax, word [ebx + ASM_SUPPORT_STATE.SavedGS32] mov gs, ax - mov ax, word [ebx + ASM_SUPPORT_STATE.SavedSS32] - mov ss, ax ; disables interrupts for 1 instruction to load esp - mov esp, dword [ebx + ASM_SUPPORT_STATE.SavedESP32] ; Jump back to the kernel passing boot arguments in eax. mov eax, edi