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
amd_sev_enabled(void)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
setup_amd_sev(void)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
amd_sev_es_enabled(void)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
setup_amd_sev_es(void)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
setup_ghcb_pte(pgd_t * page_table)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
get_amd_sev_c_bit_mask(void)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
get_amd_sev_addr_upperbound(void)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