| ;------------------------------------------------------------------------------ | |
| ; | |
| ; Copyright (c) 2022 - 2025, Intel Corporation. All rights reserved.<BR> | |
| ; SPDX-License-Identifier: BSD-2-Clause-Patent | |
| ; | |
| ; Abstract: | |
| ; | |
| ; Switch the stack from temporary memory to permanent memory. | |
| ; | |
| ;------------------------------------------------------------------------------ | |
| DEFAULT REL | |
| SECTION .text | |
| %include "PushPopRegsNasm.inc" | |
| ; 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 | |
| extern ASM_PFX(SwapStack) | |
| extern ASM_PFX(FeaturePcdGet (PcdFspSaveRestorePageTableEnable)) | |
| ;------------------------------------------------------------------------------ | |
| ; UINT32 | |
| ; EFIAPI | |
| ; Pei2LoaderSwitchStack ( | |
| ; VOID | |
| ; ) | |
| ;------------------------------------------------------------------------------ | |
| global ASM_PFX(Pei2LoaderSwitchStack) | |
| ASM_PFX(Pei2LoaderSwitchStack): | |
| xor rax, rax | |
| 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. The format must align with CONTEXT_STACK_64. | |
| push rdx ; Reserved QWORD for stack alignment | |
| push rdx ; ApiParam2 | |
| push rcx ; ApiParam1 | |
| push rax ; FspInfoHeader | |
| pushfq | |
| cli | |
| PUSHA_64 | |
| ; | |
| ; Allocate 4x8 bytes on the stack. | |
| ; | |
| sub rsp, 32 | |
| lea rdx, [ASM_PFX(FeaturePcdGet (PcdFspSaveRestorePageTableEnable))] | |
| mov dl, byte [rdx] | |
| cmp dl, 0 | |
| jz SkipPagetableSave | |
| add rsp, 32 | |
| ; Save EFER MSR | |
| push rcx | |
| push rax | |
| mov rcx, 0xC0000080 | |
| rdmsr | |
| shl rdx, 0x20 | |
| or rdx, rax | |
| pop rax | |
| pop rcx | |
| push rdx | |
| ; Save CR registers | |
| mov rdx, cr4 | |
| push rdx | |
| mov rdx, cr3 | |
| push rdx | |
| mov rdx, cr0 | |
| push rdx | |
| SkipPagetableSave: | |
| ; Save Segment registers | |
| mov rdx, ss | |
| push rdx | |
| mov rdx, gs | |
| push rdx | |
| mov rdx, fs | |
| push rdx | |
| mov rdx, es | |
| push rdx | |
| mov rdx, ds | |
| push rdx | |
| mov rdx, cs | |
| push rdx | |
| ; Reserve 16 bytes for GDT save/restore | |
| sub rsp, 16 | |
| sgdt [rsp] | |
| ; Reserve 16 bytes for IDT save/restore | |
| sub rsp, 16 | |
| sidt [rsp] | |
| ; Load new stack | |
| mov rcx, rsp | |
| sub rsp, 0x20 | |
| call ASM_PFX(SwapStack) | |
| add rsp, 0x20 | |
| mov rsp, rax | |
| ; Restore previous contexts | |
| lidt [rsp] | |
| add rsp, 16 | |
| ; Restore GDTR | |
| lgdt [rsp] | |
| add rsp, 16 | |
| ; Restore Segment registers | |
| lea rdx, [.0] | |
| push rdx ; Push return address | |
| retfq ; Far return to restore CS (uses CS from stack + return address) | |
| .0: | |
| pop rdx | |
| mov ds, dx | |
| pop rdx | |
| mov es, dx | |
| pop rdx | |
| mov fs, dx | |
| pop rdx | |
| mov gs, dx | |
| pop rdx | |
| mov ss, dx | |
| lea rax, [ASM_PFX(FeaturePcdGet (PcdFspSaveRestorePageTableEnable))] | |
| mov al, byte [rax] | |
| cmp al, 0 | |
| jz SkipPagetableRestore | |
| ; [rsp] stores new cr0 | |
| ; [rsp+8] stores new cr3 | |
| ; [rsp+16] stores new cr4 | |
| ; [rsp+24] stores new Efer | |
| ; | |
| ; When new EFER.NXE == 1, the restore flow is: EFER --> CRx | |
| ; Otherwise: CRx --> EFER | |
| ; | |
| ; 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 rax, [rsp + 24] | |
| bt rax, 11 | |
| jnc SkipEferLabel1 | |
| ; Restore EFER MSR | |
| mov ecx, 0xC0000080 | |
| rdmsr | |
| and eax, ~EFER_PG_MASK | |
| mov ebx, [rsp + 24] | |
| and ebx, EFER_PG_MASK | |
| or eax, ebx | |
| wrmsr | |
| SkipEferLabel1: | |
| mov rbx, [rsp] | |
| mov rdx, cr0 | |
| and rdx, ~CR0_PG_MASK | |
| and rbx, CR0_PG_MASK | |
| or rdx, rbx | |
| mov cr0, rdx | |
| mov rbx, [rsp + 8] | |
| mov cr3, rbx | |
| mov rbx, [rsp + 16] | |
| mov rdx, cr4 | |
| and rdx, ~CR4_PG_MASK | |
| and rbx, CR4_PG_MASK | |
| or rdx, rbx | |
| mov cr4, rdx | |
| ; | |
| ; If NXE bit is changed to 0, change NXE after than CR regiser | |
| ; | |
| mov rax, [rsp + 24] | |
| bt rax, 11 | |
| jc SkipEferLabel2 | |
| ; Restore EFER MSR | |
| mov ecx, 0xC0000080 | |
| rdmsr | |
| and eax, ~EFER_PG_MASK | |
| mov ebx, [rsp + 24] | |
| and ebx, EFER_PG_MASK | |
| or eax, ebx | |
| wrmsr | |
| SkipEferLabel2: | |
| SkipPagetableRestore: | |
| ; pop page table related registers. | |
| add rsp, 32 | |
| POPA_64 | |
| popfq | |
| add rsp, 32 ; FspInfoHeader + ApiParam[2] + Reserved QWORD | |
| ret | |