blob: 66722141ca6e802229e551cee3d06a605be7cd37 [file] [log] [blame]
/*
* AMD SEV support in kvm-unit-tests
*
* Copyright (c) 2021, Google Inc
*
* Authors:
* Zixuan Wang <zixuanwang@google.com>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "amd_sev.h"
#include "x86/processor.h"
#include "x86/vm.h"
static unsigned short amd_sev_c_bit_pos;
bool amd_sev_enabled(void)
{
struct cpuid cpuid_out;
static bool sev_enabled;
static bool initialized = false;
/* Check CPUID and MSR for SEV status and store it for future function calls. */
if (!initialized) {
sev_enabled = false;
initialized = true;
/* Test if we can query SEV features */
cpuid_out = cpuid(CPUID_FN_LARGEST_EXT_FUNC_NUM);
if (cpuid_out.a < CPUID_FN_ENCRYPT_MEM_CAPAB) {
return sev_enabled;
}
/* Test if SEV is supported */
cpuid_out = cpuid(CPUID_FN_ENCRYPT_MEM_CAPAB);
if (!(cpuid_out.a & SEV_SUPPORT_MASK)) {
return sev_enabled;
}
/* Test if SEV is enabled */
if (rdmsr(MSR_SEV_STATUS) & SEV_ENABLED_MASK) {
sev_enabled = true;
}
}
return sev_enabled;
}
efi_status_t setup_amd_sev(void)
{
struct cpuid cpuid_out;
if (!amd_sev_enabled()) {
return EFI_UNSUPPORTED;
}
/*
* Extract C-Bit position from ebx[5:0]
* AMD64 Architecture Programmer's Manual Volume 3
* - Section " Function 8000_001Fh - Encrypted Memory Capabilities"
*/
cpuid_out = cpuid(CPUID_FN_ENCRYPT_MEM_CAPAB);
amd_sev_c_bit_pos = (unsigned short)(cpuid_out.b & 0x3f);
return EFI_SUCCESS;
}
bool amd_sev_es_enabled(void)
{
static bool sev_es_enabled;
static bool initialized = false;
if (!initialized) {
sev_es_enabled = false;
initialized = true;
if (!amd_sev_enabled()) {
return sev_es_enabled;
}
/* Test if SEV-ES is enabled */
if (rdmsr(MSR_SEV_STATUS) & SEV_ES_ENABLED_MASK) {
sev_es_enabled = true;
}
}
return sev_es_enabled;
}
efi_status_t setup_amd_sev_es(void)
{
struct descriptor_table_ptr idtr;
idt_entry_t *idt;
idt_entry_t vc_handler_idt;
if (!amd_sev_es_enabled()) {
return EFI_UNSUPPORTED;
}
/*
* Copy UEFI's #VC IDT entry, so KVM-Unit-Tests can reuse it and does
* not have to re-implement a #VC handler. Also update the #VC IDT code
* segment to use KVM-Unit-Tests segments, KERNEL_CS, so that we do not
* have to copy the UEFI GDT entries into KVM-Unit-Tests GDT.
*
* TODO: Reusing UEFI #VC handler is a temporary workaround to simplify
* the boot up process, the long-term solution is to implement a #VC
* handler in kvm-unit-tests and load it, so that kvm-unit-tests does
* not depend on specific UEFI #VC handler implementation.
*/
sidt(&idtr);
idt = (idt_entry_t *)idtr.base;
vc_handler_idt = idt[SEV_ES_VC_HANDLER_VECTOR];
vc_handler_idt.selector = KERNEL_CS;
boot_idt[SEV_ES_VC_HANDLER_VECTOR] = vc_handler_idt;
return EFI_SUCCESS;
}
void setup_ghcb_pte(pgd_t *page_table)
{
/*
* SEV-ES guest uses GHCB page to communicate with the host. This page
* must be unencrypted, i.e. its c-bit should be unset. To do so, this
* function searches GHCB's L1 pte, creates corresponding L1 ptes if not
* found, and unsets the c-bit of GHCB's L1 pte.
*/
phys_addr_t ghcb_addr, ghcb_base_addr;
pteval_t *pte;
/* Read the current GHCB page addr */
ghcb_addr = rdmsr(SEV_ES_GHCB_MSR_INDEX);
/* Search Level 1 page table entry for GHCB page */
pte = get_pte_level(page_table, (void *)ghcb_addr, 1);
/* Create Level 1 pte for GHCB page if not found */
if (pte == NULL) {
/* Find Level 2 page base address */
ghcb_base_addr = ghcb_addr & ~(LARGE_PAGE_SIZE - 1);
/* Install Level 1 ptes */
install_pages(page_table, ghcb_base_addr, LARGE_PAGE_SIZE, (void *)ghcb_base_addr);
/* Find Level 2 pte, set as 4KB pages */
pte = get_pte_level(page_table, (void *)ghcb_addr, 2);
assert(pte);
*pte &= ~(PT_PAGE_SIZE_MASK);
/* Find Level 1 GHCB pte */
pte = get_pte_level(page_table, (void *)ghcb_addr, 1);
assert(pte);
}
/* Unset c-bit in Level 1 GHCB pte */
*pte &= ~(get_amd_sev_c_bit_mask());
}
unsigned long long get_amd_sev_c_bit_mask(void)
{
if (amd_sev_enabled()) {
return 1ull << amd_sev_c_bit_pos;
} else {
return 0;
}
}
unsigned long long get_amd_sev_addr_upperbound(void)
{
if (amd_sev_enabled()) {
return amd_sev_c_bit_pos - 1;
} else {
/* Default memory upper bound */
return PT_ADDR_UPPER_BOUND_DEFAULT;
}
}