/* * 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) { static bool sev_enabled; static bool initialized = false; /* Check CPUID and MSR for SEV status and store it for future function calls. */ if (!initialized) { initialized = true; sev_enabled = this_cpu_has(X86_FEATURE_SEV) && rdmsr(MSR_SEV_STATUS) & SEV_STATUS_SEV_ENABLED; } return sev_enabled; } efi_status_t setup_amd_sev(void) { if (!amd_sev_enabled()) { return EFI_UNSUPPORTED; } amd_sev_c_bit_pos = this_cpu_property(X86_PROPERTY_SEV_C_BIT); return EFI_SUCCESS; } bool amd_sev_es_enabled(void) { static bool sev_es_enabled; static bool initialized = false; if (!initialized) { initialized = true; sev_es_enabled = amd_sev_enabled() && this_cpu_has(X86_FEATURE_SEV_ES) && rdmsr(MSR_SEV_STATUS) & SEV_STATUS_SEV_ES_ENABLED; } 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[VC_VECTOR]; vc_handler_idt.selector = KERNEL_CS; boot_idt[VC_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(MSR_SEV_ES_GHCB); /* 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; } }