/* * AMD SEV support in kvm-unit-tests * * Copyright (c) 2021, Google Inc * * Authors: * Zixuan Wang * * 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; } }