Zhiguang Liu 14c9ba1a2c IntelFsp2Pkg: Support FSP API to save and restore page table
A potential issue may happen when FSP creates/changes page table while
bootloader doesn't expect page table being changed in FSP.
Current, FSP API support to save/restore stack, IDT and general purpose
registers. Following the same pattern, add save/restore page table
support to solve this issue.
Note that this feature only impacts FSP API mode, and is controlled
by PCD PcdFspSaveRestorePageTableEnable. For compatibility, the PCD
default value is set as FALSE.

Signed-off-by: Zhiguang Liu <zhiguang.liu@intel.com>
2024-09-11 05:45:54 +00:00

201 lines
4.8 KiB
NASM

;------------------------------------------------------------------------------
;
; Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>
; SPDX-License-Identifier: BSD-2-Clause-Patent
;
; Abstract:
;
; Switch the stack from temporary memory to permanent memory.
;
;------------------------------------------------------------------------------
SECTION .text
extern ASM_PFX(SwapStack)
extern ASM_PFX(FeaturePcdGet (PcdFspSaveRestorePageTableEnable))
; Page table related bits in CR0/CR4/EFER
%define CR0_PG_MASK 0x80010000 ; CR0.PG and CR0.WP
%define CR4_PG_MASK 0x10B0 ; CR4.PSE, CR4.PAE, CR4.PGE and CR4.LA57
%define EFER_PG_MASK 0x800 ; EFER.NXE
;------------------------------------------------------------------------------
; UINT32
; EFIAPI
; Pei2LoaderSwitchStack (
; VOID
; )
;------------------------------------------------------------------------------
global ASM_PFX(Pei2LoaderSwitchStack)
ASM_PFX(Pei2LoaderSwitchStack):
xor eax, eax
jmp ASM_PFX(FspSwitchStack)
;------------------------------------------------------------------------------
; UINT32
; EFIAPI
; Loader2PeiSwitchStack (
; VOID
; )
;------------------------------------------------------------------------------
global ASM_PFX(Loader2PeiSwitchStack)
ASM_PFX(Loader2PeiSwitchStack):
jmp ASM_PFX(FspSwitchStack)
;------------------------------------------------------------------------------
; UINT32
; EFIAPI
; FspSwitchStack (
; VOID
; )
;------------------------------------------------------------------------------
global ASM_PFX(FspSwitchStack)
ASM_PFX(FspSwitchStack):
; Save current contexts
push eax
pushfd
cli
pushad
;
; Allocate 4x4 bytes on the stack.
;
sub esp, 16
cmp byte [dword ASM_PFX(FeaturePcdGet (PcdFspSaveRestorePageTableEnable))], 0
jz SkipPagetableSave
add esp, 16
; Save EFER MSR lower 32 bits
push ecx
push eax
mov ecx, 0xC0000080
rdmsr
mov edx, eax
pop eax
pop ecx
push edx
; Save CR registers
mov eax, cr4
push eax
mov eax, cr3
push eax
mov eax, cr0
push eax
SkipPagetableSave:
sub esp, 8
sidt [esp]
; Load new stack
push esp
call ASM_PFX(SwapStack)
mov esp, eax
; Restore previous contexts
lidt [esp]
add esp, 8
cmp byte [dword ASM_PFX(FeaturePcdGet (PcdFspSaveRestorePageTableEnable))], 0
jz SkipPagetableRestore
; [esp] stores new cr0
; [esp+4] stores new cr3
; [esp+8] stores new cr4
; [esp+12] stores new Efer
;
; When new EFER.NXE == 1, the restore flow is: EFER --> CRx
; Otherwise: CRx --> EFER
; When new CR0.PG == 1, the restore flow for CRx is: CR3 --> CR4 --> CR0
; Otherwise, the restore flow is: CR0 --> CR3 --> CR4
;
; If NXE bit is changed to 1, change NXE before CR register
; This is because Nx bit in page table entry in new CR3 will be invalid
; if updating CR3 before EFER MSR.
;
mov eax, [esp+12]
bt eax, 11
jnc SkipEferLabel1
; Restore EFER MSR
mov ecx, 0xC0000080
rdmsr
and eax, ~EFER_PG_MASK
mov ebx, [esp+12]
and ebx, EFER_PG_MASK
or eax, ebx
wrmsr
SkipEferLabel1:
;
; if new cr0 is to disable page table, change CR0 before CR3/CR4
;
mov eax, [esp]
bt eax, 31
jc SkipCr0Label1
; Restore CR0
mov edx, cr0
and edx, ~CR0_PG_MASK
mov eax, [esp]
and eax, CR0_PG_MASK
or edx, eax
mov cr0, edx
SkipCr0Label1:
; Restore CR3/CR4
mov eax, [esp+4]
mov cr3, eax
mov edx, cr4
and edx, ~CR4_PG_MASK
mov eax, [esp+8]
and eax, CR4_PG_MASK
or edx, eax
mov cr4, edx
;
; if new cr0 is to enable page table, change CR0 after CR3/CR4
;
mov eax, [esp]
bt eax, 31
jnc SkipCr0Label2
; Restore CR0
mov edx, cr0
and edx, ~CR0_PG_MASK
mov eax, [esp]
and eax, CR0_PG_MASK
or edx, eax
mov cr0, edx
SkipCr0Label2:
;
; If NXE bit is changed to 0, change NXE after than CR regiser
;
mov eax, [esp+12]
bt eax, 11
jc SkipEferLabel2
; Restore EFER MSR
mov ecx, 0xC0000080
rdmsr
and eax, ~EFER_PG_MASK
mov ebx, [esp+12]
and ebx, EFER_PG_MASK
or eax, ebx
wrmsr
SkipEferLabel2:
SkipPagetableRestore:
; pop page table related registers.
add esp, 16
popad
popfd
add esp, 4
ret