1 /* 2 * AMD SEV support in kvm-unit-tests 3 * 4 * Copyright (c) 2021, Google Inc 5 * 6 * Authors: 7 * Zixuan Wang <zixuanwang@google.com> 8 * 9 * SPDX-License-Identifier: LGPL-2.0-or-later 10 */ 11 12 #include "amd_sev.h" 13 #include "x86/processor.h" 14 #include "x86/vm.h" 15 16 static unsigned short amd_sev_c_bit_pos; 17 18 bool amd_sev_enabled(void) 19 { 20 static bool sev_enabled; 21 static bool initialized = false; 22 23 /* Check CPUID and MSR for SEV status and store it for future function calls. */ 24 if (!initialized) { 25 initialized = true; 26 27 sev_enabled = this_cpu_has(X86_FEATURE_SEV) && 28 rdmsr(MSR_SEV_STATUS) & SEV_STATUS_SEV_ENABLED; 29 } 30 31 return sev_enabled; 32 } 33 34 efi_status_t setup_amd_sev(void) 35 { 36 if (!amd_sev_enabled()) { 37 return EFI_UNSUPPORTED; 38 } 39 40 amd_sev_c_bit_pos = this_cpu_property(X86_PROPERTY_SEV_C_BIT); 41 42 return EFI_SUCCESS; 43 } 44 45 bool amd_sev_es_enabled(void) 46 { 47 static bool sev_es_enabled; 48 static bool initialized = false; 49 50 if (!initialized) { 51 initialized = true; 52 53 sev_es_enabled = amd_sev_enabled() && 54 this_cpu_has(X86_FEATURE_SEV_ES) && 55 rdmsr(MSR_SEV_STATUS) & SEV_STATUS_SEV_ES_ENABLED; 56 } 57 58 return sev_es_enabled; 59 } 60 61 efi_status_t setup_amd_sev_es(void) 62 { 63 struct descriptor_table_ptr idtr; 64 idt_entry_t *idt; 65 idt_entry_t vc_handler_idt; 66 67 if (!amd_sev_es_enabled()) { 68 return EFI_UNSUPPORTED; 69 } 70 71 /* 72 * Copy UEFI's #VC IDT entry, so KVM-Unit-Tests can reuse it and does 73 * not have to re-implement a #VC handler. Also update the #VC IDT code 74 * segment to use KVM-Unit-Tests segments, KERNEL_CS, so that we do not 75 * have to copy the UEFI GDT entries into KVM-Unit-Tests GDT. 76 * 77 * TODO: Reusing UEFI #VC handler is a temporary workaround to simplify 78 * the boot up process, the long-term solution is to implement a #VC 79 * handler in kvm-unit-tests and load it, so that kvm-unit-tests does 80 * not depend on specific UEFI #VC handler implementation. 81 */ 82 sidt(&idtr); 83 idt = (idt_entry_t *)idtr.base; 84 vc_handler_idt = idt[VC_VECTOR]; 85 vc_handler_idt.selector = KERNEL_CS; 86 boot_idt[VC_VECTOR] = vc_handler_idt; 87 88 return EFI_SUCCESS; 89 } 90 91 void setup_ghcb_pte(pgd_t *page_table) 92 { 93 /* 94 * SEV-ES guest uses GHCB page to communicate with the host. This page 95 * must be unencrypted, i.e. its c-bit should be unset. To do so, this 96 * function searches GHCB's L1 pte, creates corresponding L1 ptes if not 97 * found, and unsets the c-bit of GHCB's L1 pte. 98 */ 99 phys_addr_t ghcb_addr, ghcb_base_addr; 100 pteval_t *pte; 101 102 /* Read the current GHCB page addr */ 103 ghcb_addr = rdmsr(MSR_SEV_ES_GHCB); 104 105 /* Search Level 1 page table entry for GHCB page */ 106 pte = get_pte_level(page_table, (void *)ghcb_addr, 1); 107 108 /* Create Level 1 pte for GHCB page if not found */ 109 if (pte == NULL) { 110 /* Find Level 2 page base address */ 111 ghcb_base_addr = ghcb_addr & ~(LARGE_PAGE_SIZE - 1); 112 /* Install Level 1 ptes */ 113 install_pages(page_table, ghcb_base_addr, LARGE_PAGE_SIZE, (void *)ghcb_base_addr); 114 /* Find Level 2 pte, set as 4KB pages */ 115 pte = get_pte_level(page_table, (void *)ghcb_addr, 2); 116 assert(pte); 117 *pte &= ~(PT_PAGE_SIZE_MASK); 118 /* Find Level 1 GHCB pte */ 119 pte = get_pte_level(page_table, (void *)ghcb_addr, 1); 120 assert(pte); 121 } 122 123 /* Unset c-bit in Level 1 GHCB pte */ 124 *pte &= ~(get_amd_sev_c_bit_mask()); 125 } 126 127 unsigned long long get_amd_sev_c_bit_mask(void) 128 { 129 if (amd_sev_enabled()) { 130 return 1ull << amd_sev_c_bit_pos; 131 } else { 132 return 0; 133 } 134 } 135 136 unsigned long long get_amd_sev_addr_upperbound(void) 137 { 138 if (amd_sev_enabled()) { 139 return amd_sev_c_bit_pos - 1; 140 } else { 141 /* Default memory upper bound */ 142 return PT_ADDR_UPPER_BOUND_DEFAULT; 143 } 144 } 145