Files
system76-edk2/OvmfPkg/ResetVector/Ia32/PageTables64.asm
Tom Lendacky 6995a1b79b OvmfPkg: Create a GHCB page for use during Sec phase
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>
2020-08-17 02:46:39 +00:00

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