xref: /kvm-unit-tests/lib/x86/amd_sev.c (revision cebc6ef778a742e78351728c71bd3cf1611bbc02)
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