xref: /kvm-unit-tests/lib/x86/amd_sev.c (revision 92a6c9b95ab10eba66bff3ff44476ab0c015b276)
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 	struct cpuid cpuid_out;
21 	static bool sev_enabled;
22 	static bool initialized = false;
23 
24 	/* Check CPUID and MSR for SEV status and store it for future function calls. */
25 	if (!initialized) {
26 		sev_enabled = false;
27 		initialized = true;
28 
29 		/* Test if we can query SEV features */
30 		cpuid_out = cpuid(CPUID_FN_LARGEST_EXT_FUNC_NUM);
31 		if (cpuid_out.a < CPUID_FN_ENCRYPT_MEM_CAPAB) {
32 			return sev_enabled;
33 		}
34 
35 		/* Test if SEV is supported */
36 		cpuid_out = cpuid(CPUID_FN_ENCRYPT_MEM_CAPAB);
37 		if (!(cpuid_out.a & SEV_SUPPORT_MASK)) {
38 			return sev_enabled;
39 		}
40 
41 		/* Test if SEV is enabled */
42 		if (rdmsr(MSR_SEV_STATUS) & SEV_ENABLED_MASK) {
43 			sev_enabled = true;
44 		}
45 	}
46 
47 	return sev_enabled;
48 }
49 
setup_amd_sev(void)50 efi_status_t setup_amd_sev(void)
51 {
52 	struct cpuid cpuid_out;
53 
54 	if (!amd_sev_enabled()) {
55 		return EFI_UNSUPPORTED;
56 	}
57 
58 	/*
59 	 * Extract C-Bit position from ebx[5:0]
60 	 * AMD64 Architecture Programmer's Manual Volume 3
61 	 *   - Section " Function 8000_001Fh - Encrypted Memory Capabilities"
62 	 */
63 	cpuid_out = cpuid(CPUID_FN_ENCRYPT_MEM_CAPAB);
64 	amd_sev_c_bit_pos = (unsigned short)(cpuid_out.b & 0x3f);
65 
66 	return EFI_SUCCESS;
67 }
68 
amd_sev_es_enabled(void)69 bool amd_sev_es_enabled(void)
70 {
71 	static bool sev_es_enabled;
72 	static bool initialized = false;
73 
74 	if (!initialized) {
75 		sev_es_enabled = false;
76 		initialized = true;
77 
78 		if (!amd_sev_enabled()) {
79 			return sev_es_enabled;
80 		}
81 
82 		/* Test if SEV-ES is enabled */
83 		if (rdmsr(MSR_SEV_STATUS) & SEV_ES_ENABLED_MASK) {
84 			sev_es_enabled = true;
85 		}
86 	}
87 
88 	return sev_es_enabled;
89 }
90 
setup_amd_sev_es(void)91 efi_status_t setup_amd_sev_es(void)
92 {
93 	struct descriptor_table_ptr idtr;
94 	idt_entry_t *idt;
95 	idt_entry_t vc_handler_idt;
96 
97 	if (!amd_sev_es_enabled()) {
98 		return EFI_UNSUPPORTED;
99 	}
100 
101 	/*
102 	 * Copy UEFI's #VC IDT entry, so KVM-Unit-Tests can reuse it and does
103 	 * not have to re-implement a #VC handler. Also update the #VC IDT code
104 	 * segment to use KVM-Unit-Tests segments, KERNEL_CS, so that we do not
105 	 * have to copy the UEFI GDT entries into KVM-Unit-Tests GDT.
106 	 *
107 	 * TODO: Reusing UEFI #VC handler is a temporary workaround to simplify
108 	 * the boot up process, the long-term solution is to implement a #VC
109 	 * handler in kvm-unit-tests and load it, so that kvm-unit-tests does
110 	 * not depend on specific UEFI #VC handler implementation.
111 	 */
112 	sidt(&idtr);
113 	idt = (idt_entry_t *)idtr.base;
114 	vc_handler_idt = idt[SEV_ES_VC_HANDLER_VECTOR];
115 	vc_handler_idt.selector = KERNEL_CS;
116 	boot_idt[SEV_ES_VC_HANDLER_VECTOR] = vc_handler_idt;
117 
118 	return EFI_SUCCESS;
119 }
120 
setup_ghcb_pte(pgd_t * page_table)121 void setup_ghcb_pte(pgd_t *page_table)
122 {
123 	/*
124 	 * SEV-ES guest uses GHCB page to communicate with the host. This page
125 	 * must be unencrypted, i.e. its c-bit should be unset. To do so, this
126 	 * function searches GHCB's L1 pte, creates corresponding L1 ptes if not
127 	 * found, and unsets the c-bit of GHCB's L1 pte.
128 	 */
129 	phys_addr_t ghcb_addr, ghcb_base_addr;
130 	pteval_t *pte;
131 
132 	/* Read the current GHCB page addr */
133 	ghcb_addr = rdmsr(SEV_ES_GHCB_MSR_INDEX);
134 
135 	/* Search Level 1 page table entry for GHCB page */
136 	pte = get_pte_level(page_table, (void *)ghcb_addr, 1);
137 
138 	/* Create Level 1 pte for GHCB page if not found */
139 	if (pte == NULL) {
140 		/* Find Level 2 page base address */
141 		ghcb_base_addr = ghcb_addr & ~(LARGE_PAGE_SIZE - 1);
142 		/* Install Level 1 ptes */
143 		install_pages(page_table, ghcb_base_addr, LARGE_PAGE_SIZE, (void *)ghcb_base_addr);
144 		/* Find Level 2 pte, set as 4KB pages */
145 		pte = get_pte_level(page_table, (void *)ghcb_addr, 2);
146 		assert(pte);
147 		*pte &= ~(PT_PAGE_SIZE_MASK);
148 		/* Find Level 1 GHCB pte */
149 		pte = get_pte_level(page_table, (void *)ghcb_addr, 1);
150 		assert(pte);
151 	}
152 
153 	/* Unset c-bit in Level 1 GHCB pte */
154 	*pte &= ~(get_amd_sev_c_bit_mask());
155 }
156 
get_amd_sev_c_bit_mask(void)157 unsigned long long get_amd_sev_c_bit_mask(void)
158 {
159 	if (amd_sev_enabled()) {
160 		return 1ull << amd_sev_c_bit_pos;
161 	} else {
162 		return 0;
163 	}
164 }
165 
get_amd_sev_addr_upperbound(void)166 unsigned long long get_amd_sev_addr_upperbound(void)
167 {
168 	if (amd_sev_enabled()) {
169 		return amd_sev_c_bit_pos - 1;
170 	} else {
171 		/* Default memory upper bound */
172 		return PT_ADDR_UPPER_BOUND_DEFAULT;
173 	}
174 }
175