BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198 A GHCB page is needed during the Sec phase, so this new page must be created. Since the #VC exception handler routines assume that a per-CPU variable area is immediately after the GHCB, this per-CPU variable area must also be created. Since the GHCB must be marked as an un-encrypted, or shared, page, an additional pagetable page is required to break down the 2MB region where the GHCB page lives into 4K pagetable entries. Create a new entry in the OVMF memory layout for the new page table page and for the SEC GHCB and per-CPU variable pages. After breaking down the 2MB page, update the GHCB page table entry to remove the encryption mask. The GHCB page will be used by the SEC #VC exception handler. The #VC exception handler will fill in the necessary fields of the GHCB and exit to the hypervisor using the VMGEXIT instruction. The hypervisor then accesses the GHCB in order to perform the requested function. Four new fixed PCDs are needed to support the SEC GHCB page: - PcdOvmfSecGhcbBase UINT32 value that is the base address of the GHCB used during the SEC phase. - PcdOvmfSecGhcbSize UINT32 value that is the size, in bytes, of the GHCB area used during the SEC phase. - PcdOvmfSecGhcbPageTableBase UINT32 value that is address of a page table page used to break down the 2MB page into 512 4K pages. - PcdOvmfSecGhcbPageTableSize UINT32 value that is the size, in bytes, of the page table page. Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com> Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
225 lines
6.3 KiB
NASM
225 lines
6.3 KiB
NASM
;------------------------------------------------------------------------------
|
|
; @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
|
|
|
|
%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_4K_PDE_ATTR (PAGE_ACCESSED + \
|
|
PAGE_DIRTY + \
|
|
PAGE_READ_WRITE + \
|
|
PAGE_PRESENT)
|
|
|
|
%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)
|
|
|
|
; Check if Secure Encrypted Virtualization (SEV) feature is enabled
|
|
;
|
|
; If SEV is enabled then EAX will be at least 32
|
|
; If SEV is disabled then EAX will be zero.
|
|
;
|
|
CheckSevFeature:
|
|
; Check if we have a valid (0x8000_001F) CPUID leaf
|
|
mov eax, 0x80000000
|
|
cpuid
|
|
|
|
; This check should fail on Intel or Non SEV AMD CPUs. In future if
|
|
; Intel CPUs supports this CPUID leaf then we are guranteed to have exact
|
|
; same bit definition.
|
|
cmp eax, 0x8000001f
|
|
jl NoSev
|
|
|
|
; Check for memory encryption feature:
|
|
; CPUID Fn8000_001F[EAX] - Bit 1
|
|
;
|
|
mov eax, 0x8000001f
|
|
cpuid
|
|
bt eax, 1
|
|
jnc NoSev
|
|
|
|
; Check if memory encryption is enabled
|
|
; MSR_0xC0010131 - Bit 0 (SEV enabled)
|
|
mov ecx, 0xc0010131
|
|
rdmsr
|
|
bt eax, 0
|
|
jnc NoSev
|
|
|
|
; Get pte bit position to enable memory encryption
|
|
; CPUID Fn8000_001F[EBX] - Bits 5:0
|
|
;
|
|
mov eax, ebx
|
|
and eax, 0x3f
|
|
jmp SevExit
|
|
|
|
NoSev:
|
|
xor eax, eax
|
|
|
|
SevExit:
|
|
OneTimeCallRet CheckSevFeature
|
|
|
|
; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature
|
|
; is enabled.
|
|
;
|
|
; Modified: EAX, EBX, ECX
|
|
;
|
|
; If SEV-ES is enabled then EAX will be non-zero.
|
|
; If SEV-ES is disabled then EAX will be zero.
|
|
;
|
|
CheckSevEsFeature:
|
|
xor eax, eax
|
|
|
|
; SEV-ES can't be enabled if SEV isn't, so first check the encryption
|
|
; mask.
|
|
test edx, edx
|
|
jz NoSevEs
|
|
|
|
; Save current value of encryption mask
|
|
mov ebx, edx
|
|
|
|
; Check if SEV-ES is enabled
|
|
; MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
|
|
mov ecx, 0xc0010131
|
|
rdmsr
|
|
and eax, 2
|
|
|
|
; Restore encryption mask
|
|
mov edx, ebx
|
|
|
|
NoSevEs:
|
|
OneTimeCallRet CheckSevEsFeature
|
|
|
|
;
|
|
; Modified: EAX, EBX, ECX, EDX
|
|
;
|
|
SetCr3ForPageTables64:
|
|
|
|
OneTimeCall CheckSevFeature
|
|
xor edx, edx
|
|
test eax, eax
|
|
jz SevNotActive
|
|
|
|
; If SEV is enabled, C-bit is always above 31
|
|
sub eax, 32
|
|
bts edx, eax
|
|
|
|
SevNotActive:
|
|
|
|
;
|
|
; For OVMF, build some initial page tables at
|
|
; PcdOvmfSecPageTablesBase - (PcdOvmfSecPageTablesBase + 0x6000).
|
|
;
|
|
; This range should match with PcdOvmfSecPageTablesSize which is
|
|
; declared in the FDF files.
|
|
;
|
|
; At the end of PEI, the pages tables will be rebuilt into a
|
|
; more permanent location by DxeIpl.
|
|
;
|
|
|
|
mov ecx, 6 * 0x1000 / 4
|
|
xor eax, eax
|
|
clearPageTablesMemoryLoop:
|
|
mov dword[ecx * 4 + PT_ADDR (0) - 4], eax
|
|
loop clearPageTablesMemoryLoop
|
|
|
|
;
|
|
; Top level Page Directory Pointers (1 * 512GB entry)
|
|
;
|
|
mov dword[PT_ADDR (0)], PT_ADDR (0x1000) + PAGE_PDP_ATTR
|
|
mov dword[PT_ADDR (4)], edx
|
|
|
|
;
|
|
; Next level Page Directory Pointers (4 * 1GB entries => 4GB)
|
|
;
|
|
mov dword[PT_ADDR (0x1000)], PT_ADDR (0x2000) + PAGE_PDP_ATTR
|
|
mov dword[PT_ADDR (0x1004)], edx
|
|
mov dword[PT_ADDR (0x1008)], PT_ADDR (0x3000) + PAGE_PDP_ATTR
|
|
mov dword[PT_ADDR (0x100C)], edx
|
|
mov dword[PT_ADDR (0x1010)], PT_ADDR (0x4000) + PAGE_PDP_ATTR
|
|
mov dword[PT_ADDR (0x1014)], edx
|
|
mov dword[PT_ADDR (0x1018)], PT_ADDR (0x5000) + PAGE_PDP_ATTR
|
|
mov dword[PT_ADDR (0x101C)], edx
|
|
|
|
;
|
|
; Page Table Entries (2048 * 2MB entries => 4GB)
|
|
;
|
|
mov ecx, 0x800
|
|
pageTableEntriesLoop:
|
|
mov eax, ecx
|
|
dec eax
|
|
shl eax, 21
|
|
add eax, PAGE_2M_PDE_ATTR
|
|
mov [ecx * 8 + PT_ADDR (0x2000 - 8)], eax
|
|
mov [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
|
|
loop pageTableEntriesLoop
|
|
|
|
OneTimeCall CheckSevEsFeature
|
|
test eax, eax
|
|
jz SetCr3
|
|
|
|
;
|
|
; The initial GHCB will live at GHCB_BASE and needs to be un-encrypted.
|
|
; This requires the 2MB page for this range be broken down into 512 4KB
|
|
; pages. All will be marked encrypted, except for the GHCB.
|
|
;
|
|
mov ecx, (GHCB_BASE >> 21)
|
|
mov eax, GHCB_PT_ADDR + PAGE_PDP_ATTR
|
|
mov [ecx * 8 + PT_ADDR (0x2000)], eax
|
|
|
|
;
|
|
; Page Table Entries (512 * 4KB entries => 2MB)
|
|
;
|
|
mov ecx, 512
|
|
pageTableEntries4kLoop:
|
|
mov eax, ecx
|
|
dec eax
|
|
shl eax, 12
|
|
add eax, GHCB_BASE & 0xFFE0_0000
|
|
add eax, PAGE_4K_PDE_ATTR
|
|
mov [ecx * 8 + GHCB_PT_ADDR - 8], eax
|
|
mov [(ecx * 8 + GHCB_PT_ADDR - 8) + 4], edx
|
|
loop pageTableEntries4kLoop
|
|
|
|
;
|
|
; Clear the encryption bit from the GHCB entry
|
|
;
|
|
mov ecx, (GHCB_BASE & 0x1F_FFFF) >> 12
|
|
mov [ecx * 8 + GHCB_PT_ADDR + 4], strict dword 0
|
|
|
|
mov ecx, GHCB_SIZE / 4
|
|
xor eax, eax
|
|
clearGhcbMemoryLoop:
|
|
mov dword[ecx * 4 + GHCB_BASE - 4], eax
|
|
loop clearGhcbMemoryLoop
|
|
|
|
SetCr3:
|
|
;
|
|
; Set CR3 now that the paging structures are available
|
|
;
|
|
mov eax, PT_ADDR (0)
|
|
mov cr3, eax
|
|
|
|
OneTimeCallRet SetCr3ForPageTables64
|