SEV guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. Certain types of memory (namely instruction pages and guest page tables) are always treated as private memory by the hardware. The C-bit in PTE indicate whether the page is private or shared. The C-bit position for the PTE can be obtained from CPUID Fn8000_001F[EBX]. When SEV is active, the BIOS is encrypted by the Qemu launch sequence, we must set the C-bit when building the page table. Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Tom Lendacky <Thomas.Lendacky@amd.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
155 lines
4.6 KiB
NASM
155 lines
4.6 KiB
NASM
;------------------------------------------------------------------------------
|
|
; @file
|
|
; Sets the CR3 register for 64-bit paging
|
|
;
|
|
; Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>
|
|
; This program and the accompanying materials
|
|
; are licensed and made available under the terms and conditions of the BSD License
|
|
; which accompanies this distribution. The full text of the license may be found at
|
|
; http://opensource.org/licenses/bsd-license.php
|
|
;
|
|
; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
;
|
|
;------------------------------------------------------------------------------
|
|
|
|
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_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
|
|
|
|
;
|
|
; 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
|
|
|
|
;
|
|
; Set CR3 now that the paging structures are available
|
|
;
|
|
mov eax, PT_ADDR (0)
|
|
mov cr3, eax
|
|
|
|
OneTimeCallRet SetCr3ForPageTables64
|