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