1350bf64aSZixuan Wang /* 2350bf64aSZixuan Wang * AMD SEV support in kvm-unit-tests 3350bf64aSZixuan Wang * 4350bf64aSZixuan Wang * Copyright (c) 2021, Google Inc 5350bf64aSZixuan Wang * 6350bf64aSZixuan Wang * Authors: 7350bf64aSZixuan Wang * Zixuan Wang <zixuanwang@google.com> 8350bf64aSZixuan Wang * 9350bf64aSZixuan Wang * SPDX-License-Identifier: LGPL-2.0-or-later 10350bf64aSZixuan Wang */ 11350bf64aSZixuan Wang 12350bf64aSZixuan Wang #include "amd_sev.h" 13350bf64aSZixuan Wang #include "x86/processor.h" 14b114aa57SZixuan Wang #include "x86/vm.h" 15350bf64aSZixuan Wang 16350bf64aSZixuan Wang static unsigned short amd_sev_c_bit_pos; 17350bf64aSZixuan Wang 18350bf64aSZixuan Wang bool amd_sev_enabled(void) 19350bf64aSZixuan Wang { 20350bf64aSZixuan Wang struct cpuid cpuid_out; 21350bf64aSZixuan Wang static bool sev_enabled; 22350bf64aSZixuan Wang static bool initialized = false; 23350bf64aSZixuan Wang 24350bf64aSZixuan Wang /* Check CPUID and MSR for SEV status and store it for future function calls. */ 25350bf64aSZixuan Wang if (!initialized) { 26350bf64aSZixuan Wang sev_enabled = false; 27350bf64aSZixuan Wang initialized = true; 28350bf64aSZixuan Wang 29350bf64aSZixuan Wang /* Test if we can query SEV features */ 30350bf64aSZixuan Wang cpuid_out = cpuid(CPUID_FN_LARGEST_EXT_FUNC_NUM); 31350bf64aSZixuan Wang if (cpuid_out.a < CPUID_FN_ENCRYPT_MEM_CAPAB) { 32350bf64aSZixuan Wang return sev_enabled; 33350bf64aSZixuan Wang } 34350bf64aSZixuan Wang 35350bf64aSZixuan Wang /* Test if SEV is supported */ 36350bf64aSZixuan Wang cpuid_out = cpuid(CPUID_FN_ENCRYPT_MEM_CAPAB); 37350bf64aSZixuan Wang if (!(cpuid_out.a & SEV_SUPPORT_MASK)) { 38350bf64aSZixuan Wang return sev_enabled; 39350bf64aSZixuan Wang } 40350bf64aSZixuan Wang 41350bf64aSZixuan Wang /* Test if SEV is enabled */ 42350bf64aSZixuan Wang if (rdmsr(MSR_SEV_STATUS) & SEV_ENABLED_MASK) { 43350bf64aSZixuan Wang sev_enabled = true; 44350bf64aSZixuan Wang } 45350bf64aSZixuan Wang } 46350bf64aSZixuan Wang 47350bf64aSZixuan Wang return sev_enabled; 48350bf64aSZixuan Wang } 49350bf64aSZixuan Wang 50350bf64aSZixuan Wang efi_status_t setup_amd_sev(void) 51350bf64aSZixuan Wang { 52350bf64aSZixuan Wang struct cpuid cpuid_out; 53350bf64aSZixuan Wang 54350bf64aSZixuan Wang if (!amd_sev_enabled()) { 55350bf64aSZixuan Wang return EFI_UNSUPPORTED; 56350bf64aSZixuan Wang } 57350bf64aSZixuan Wang 58350bf64aSZixuan Wang /* 59350bf64aSZixuan Wang * Extract C-Bit position from ebx[5:0] 60350bf64aSZixuan Wang * AMD64 Architecture Programmer's Manual Volume 3 61350bf64aSZixuan Wang * - Section " Function 8000_001Fh - Encrypted Memory Capabilities" 62350bf64aSZixuan Wang */ 63350bf64aSZixuan Wang cpuid_out = cpuid(CPUID_FN_ENCRYPT_MEM_CAPAB); 64350bf64aSZixuan Wang amd_sev_c_bit_pos = (unsigned short)(cpuid_out.b & 0x3f); 65350bf64aSZixuan Wang 66350bf64aSZixuan Wang return EFI_SUCCESS; 67350bf64aSZixuan Wang } 68350bf64aSZixuan Wang 69bf812590SZixuan Wang bool amd_sev_es_enabled(void) 70bf812590SZixuan Wang { 71bf812590SZixuan Wang static bool sev_es_enabled; 72bf812590SZixuan Wang static bool initialized = false; 73bf812590SZixuan Wang 74bf812590SZixuan Wang if (!initialized) { 75bf812590SZixuan Wang sev_es_enabled = false; 76bf812590SZixuan Wang initialized = true; 77bf812590SZixuan Wang 78bf812590SZixuan Wang if (!amd_sev_enabled()) { 79bf812590SZixuan Wang return sev_es_enabled; 80bf812590SZixuan Wang } 81bf812590SZixuan Wang 82bf812590SZixuan Wang /* Test if SEV-ES is enabled */ 83bf812590SZixuan Wang if (rdmsr(MSR_SEV_STATUS) & SEV_ES_ENABLED_MASK) { 84bf812590SZixuan Wang sev_es_enabled = true; 85bf812590SZixuan Wang } 86bf812590SZixuan Wang } 87bf812590SZixuan Wang 88bf812590SZixuan Wang return sev_es_enabled; 89bf812590SZixuan Wang } 90bf812590SZixuan Wang 91706ede18SZixuan Wang efi_status_t setup_amd_sev_es(void) 92706ede18SZixuan Wang { 93706ede18SZixuan Wang struct descriptor_table_ptr idtr; 94706ede18SZixuan Wang idt_entry_t *idt; 95706ede18SZixuan Wang idt_entry_t vc_handler_idt; 96706ede18SZixuan Wang 97706ede18SZixuan Wang if (!amd_sev_es_enabled()) { 98706ede18SZixuan Wang return EFI_UNSUPPORTED; 99706ede18SZixuan Wang } 100706ede18SZixuan Wang 101706ede18SZixuan Wang /* 102706ede18SZixuan Wang * Copy UEFI's #VC IDT entry, so KVM-Unit-Tests can reuse it and does 103706ede18SZixuan Wang * not have to re-implement a #VC handler. Also update the #VC IDT code 104706ede18SZixuan Wang * segment to use KVM-Unit-Tests segments, KERNEL_CS, so that we do not 105706ede18SZixuan Wang * have to copy the UEFI GDT entries into KVM-Unit-Tests GDT. 106706ede18SZixuan Wang * 107706ede18SZixuan Wang * TODO: Reusing UEFI #VC handler is a temporary workaround to simplify 108706ede18SZixuan Wang * the boot up process, the long-term solution is to implement a #VC 109706ede18SZixuan Wang * handler in kvm-unit-tests and load it, so that kvm-unit-tests does 110706ede18SZixuan Wang * not depend on specific UEFI #VC handler implementation. 111706ede18SZixuan Wang */ 112706ede18SZixuan Wang sidt(&idtr); 113706ede18SZixuan Wang idt = (idt_entry_t *)idtr.base; 114*5d80d64dSSean Christopherson vc_handler_idt = idt[VC_VECTOR]; 115706ede18SZixuan Wang vc_handler_idt.selector = KERNEL_CS; 116*5d80d64dSSean Christopherson boot_idt[VC_VECTOR] = vc_handler_idt; 117706ede18SZixuan Wang 118706ede18SZixuan Wang return EFI_SUCCESS; 119706ede18SZixuan Wang } 120706ede18SZixuan Wang 121b114aa57SZixuan Wang void setup_ghcb_pte(pgd_t *page_table) 122b114aa57SZixuan Wang { 123b114aa57SZixuan Wang /* 124b114aa57SZixuan Wang * SEV-ES guest uses GHCB page to communicate with the host. This page 125b114aa57SZixuan Wang * must be unencrypted, i.e. its c-bit should be unset. To do so, this 126b114aa57SZixuan Wang * function searches GHCB's L1 pte, creates corresponding L1 ptes if not 127b114aa57SZixuan Wang * found, and unsets the c-bit of GHCB's L1 pte. 128b114aa57SZixuan Wang */ 129b114aa57SZixuan Wang phys_addr_t ghcb_addr, ghcb_base_addr; 130b114aa57SZixuan Wang pteval_t *pte; 131b114aa57SZixuan Wang 132b114aa57SZixuan Wang /* Read the current GHCB page addr */ 133b114aa57SZixuan Wang ghcb_addr = rdmsr(SEV_ES_GHCB_MSR_INDEX); 134b114aa57SZixuan Wang 135b114aa57SZixuan Wang /* Search Level 1 page table entry for GHCB page */ 136b114aa57SZixuan Wang pte = get_pte_level(page_table, (void *)ghcb_addr, 1); 137b114aa57SZixuan Wang 138b114aa57SZixuan Wang /* Create Level 1 pte for GHCB page if not found */ 139b114aa57SZixuan Wang if (pte == NULL) { 140b114aa57SZixuan Wang /* Find Level 2 page base address */ 141b114aa57SZixuan Wang ghcb_base_addr = ghcb_addr & ~(LARGE_PAGE_SIZE - 1); 142b114aa57SZixuan Wang /* Install Level 1 ptes */ 143b114aa57SZixuan Wang install_pages(page_table, ghcb_base_addr, LARGE_PAGE_SIZE, (void *)ghcb_base_addr); 144b114aa57SZixuan Wang /* Find Level 2 pte, set as 4KB pages */ 145b114aa57SZixuan Wang pte = get_pte_level(page_table, (void *)ghcb_addr, 2); 146b114aa57SZixuan Wang assert(pte); 147b114aa57SZixuan Wang *pte &= ~(PT_PAGE_SIZE_MASK); 148b114aa57SZixuan Wang /* Find Level 1 GHCB pte */ 149b114aa57SZixuan Wang pte = get_pte_level(page_table, (void *)ghcb_addr, 1); 150b114aa57SZixuan Wang assert(pte); 151b114aa57SZixuan Wang } 152b114aa57SZixuan Wang 153b114aa57SZixuan Wang /* Unset c-bit in Level 1 GHCB pte */ 154b114aa57SZixuan Wang *pte &= ~(get_amd_sev_c_bit_mask()); 155b114aa57SZixuan Wang } 156b114aa57SZixuan Wang 157350bf64aSZixuan Wang unsigned long long get_amd_sev_c_bit_mask(void) 158350bf64aSZixuan Wang { 159350bf64aSZixuan Wang if (amd_sev_enabled()) { 160350bf64aSZixuan Wang return 1ull << amd_sev_c_bit_pos; 161350bf64aSZixuan Wang } else { 162350bf64aSZixuan Wang return 0; 163350bf64aSZixuan Wang } 164350bf64aSZixuan Wang } 16530203ea5SZixuan Wang 16630203ea5SZixuan Wang unsigned long long get_amd_sev_addr_upperbound(void) 16730203ea5SZixuan Wang { 16830203ea5SZixuan Wang if (amd_sev_enabled()) { 16930203ea5SZixuan Wang return amd_sev_c_bit_pos - 1; 17030203ea5SZixuan Wang } else { 17130203ea5SZixuan Wang /* Default memory upper bound */ 17230203ea5SZixuan Wang return PT_ADDR_UPPER_BOUND_DEFAULT; 17330203ea5SZixuan Wang } 17430203ea5SZixuan Wang } 175