]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/confidential-virt.c
b6521cf5bfcfe008bac76472f07ed2fbcf3dd135
[thirdparty/systemd.git] / src / basic / confidential-virt.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #if defined(__i386__) || defined(__x86_64__)
4 #include <cpuid.h>
5 #endif
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10
11 #include "confidential-virt-fundamental.h"
12 #include "confidential-virt.h"
13 #include "fd-util.h"
14 #include "missing_threads.h"
15 #include "string-table.h"
16 #include "utf8.h"
17
18
19 #if defined(__x86_64__)
20
21 static void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
22 log_debug("CPUID func %" PRIx32 " %" PRIx32, *eax, *ecx);
23 __cpuid_count(*eax, *ecx, *eax, *ebx, *ecx, *edx);
24 log_debug("CPUID result %" PRIx32 " %" PRIx32 " %" PRIx32 " %" PRIx32, *eax, *ebx, *ecx, *edx);
25 }
26
27 static uint32_t cpuid_leaf(uint32_t eax, char ret_sig[static 13], bool swapped) {
28 /* zero-init as some queries explicitly require subleaf == 0 */
29 uint32_t sig[3] = {};
30
31 if (swapped)
32 cpuid(&eax, &sig[0], &sig[2], &sig[1]);
33 else
34 cpuid(&eax, &sig[0], &sig[1], &sig[2]);
35 memcpy(ret_sig, sig, sizeof(sig));
36 ret_sig[12] = 0; /* \0-terminate the string to make string comparison possible */
37
38 /* In some CI tests ret_sig doesn't contain valid UTF8 and prints garbage to the console */
39 log_debug("CPUID sig '%s'", strna(utf8_is_valid(ret_sig)));
40
41 return eax;
42 }
43
44 #define MSR_DEVICE "/dev/cpu/0/msr"
45
46 static uint64_t msr(uint64_t index) {
47 uint64_t ret;
48 ssize_t rv;
49 _cleanup_close_ int fd = -EBADF;
50
51 fd = open(MSR_DEVICE, O_RDONLY|O_CLOEXEC);
52 if (fd < 0) {
53 log_debug_errno(errno,
54 "Cannot open MSR device %s (index %" PRIu64 "), ignoring: %m",
55 MSR_DEVICE,
56 index);
57 return 0;
58 }
59
60 rv = pread(fd, &ret, sizeof(ret), index);
61 if (rv < 0) {
62 log_debug_errno(errno,
63 "Cannot read MSR device %s (index %" PRIu64 "), ignoring: %m",
64 MSR_DEVICE,
65 index);
66 return 0;
67 } else if (rv != sizeof(ret)) {
68 log_debug("Short read %zd bytes from MSR device %s (index %" PRIu64 "), ignoring",
69 rv,
70 MSR_DEVICE,
71 index);
72 return 0;
73 }
74
75 log_debug("MSR %" PRIu64 " result %" PRIu64 "", index, ret);
76 return ret;
77 }
78
79 static bool detect_hyperv_sev(void) {
80 uint32_t eax, ebx, ecx, edx, feat;
81 char sig[13] = {};
82
83 feat = cpuid_leaf(CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS, sig, false);
84
85 if (feat < CPUID_HYPERV_MIN || feat > CPUID_HYPERV_MAX)
86 return false;
87
88 if (memcmp(sig, CPUID_SIG_HYPERV, sizeof(sig)) != 0)
89 return false;
90
91 log_debug("CPUID is on hyperv");
92 eax = CPUID_HYPERV_FEATURES;
93 ebx = ecx = edx = 0;
94
95 cpuid(&eax, &ebx, &ecx, &edx);
96
97 if (ebx & CPUID_HYPERV_ISOLATION && !(ebx & CPUID_HYPERV_CPU_MANAGEMENT)) {
98
99 eax = CPUID_HYPERV_ISOLATION_CONFIG;
100 ebx = ecx = edx = 0;
101 cpuid(&eax, &ebx, &ecx, &edx);
102
103 if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == CPUID_HYPERV_ISOLATION_TYPE_SNP)
104 return true;
105 }
106
107 return false;
108 }
109
110 static ConfidentialVirtualization detect_sev(void) {
111 uint32_t eax, ebx, ecx, edx;
112 uint64_t msrval;
113
114 eax = CPUID_GET_HIGHEST_FUNCTION;
115 ebx = ecx = edx = 0;
116
117 cpuid(&eax, &ebx, &ecx, &edx);
118
119 if (eax < CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES)
120 return CONFIDENTIAL_VIRTUALIZATION_NONE;
121
122 eax = CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES;
123 ebx = ecx = edx = 0;
124
125 cpuid(&eax, &ebx, &ecx, &edx);
126
127 /* bit 1 == CPU supports SEV feature
128 *
129 * Note, Azure blocks this CPUID leaf from its SEV-SNP
130 * guests, so we must fallback to trying some HyperV
131 * specific CPUID checks.
132 */
133 if (!(eax & EAX_SEV)) {
134 log_debug("No sev in CPUID, trying hyperv CPUID");
135
136 if (detect_hyperv_sev())
137 return CONFIDENTIAL_VIRTUALIZATION_SEV_SNP;
138
139 log_debug("No hyperv CPUID");
140 return CONFIDENTIAL_VIRTUALIZATION_NONE;
141 }
142
143 msrval = msr(MSR_AMD64_SEV);
144
145 /* Test reverse order, since the SEV-SNP bit implies
146 * the SEV-ES bit, which implies the SEV bit */
147 if (msrval & MSR_SEV_SNP)
148 return CONFIDENTIAL_VIRTUALIZATION_SEV_SNP;
149 if (msrval & MSR_SEV_ES)
150 return CONFIDENTIAL_VIRTUALIZATION_SEV_ES;
151 if (msrval & MSR_SEV)
152 return CONFIDENTIAL_VIRTUALIZATION_SEV;
153
154 return CONFIDENTIAL_VIRTUALIZATION_NONE;
155 }
156
157 static ConfidentialVirtualization detect_tdx(void) {
158 uint32_t eax, ebx, ecx, edx;
159 char sig[13] = {};
160
161 eax = CPUID_GET_HIGHEST_FUNCTION;
162 ebx = ecx = edx = 0;
163
164 cpuid(&eax, &ebx, &ecx, &edx);
165
166 if (eax < CPUID_INTEL_TDX_ENUMERATION)
167 return CONFIDENTIAL_VIRTUALIZATION_NONE;
168
169 cpuid_leaf(CPUID_INTEL_TDX_ENUMERATION, sig, true);
170
171 if (memcmp(sig, CPUID_SIG_INTEL_TDX, sizeof(sig)) == 0)
172 return CONFIDENTIAL_VIRTUALIZATION_TDX;
173
174 return CONFIDENTIAL_VIRTUALIZATION_NONE;
175 }
176
177 static bool detect_hypervisor(void) {
178 uint32_t eax, ebx, ecx, edx;
179 bool is_hv;
180
181 eax = CPUID_PROCESSOR_INFO_AND_FEATURE_BITS;
182 ebx = ecx = edx = 0;
183
184 cpuid(&eax, &ebx, &ecx, &edx);
185
186 is_hv = ecx & CPUID_FEATURE_HYPERVISOR;
187
188 log_debug("CPUID is hypervisor: %s", yes_no(is_hv));
189 return is_hv;
190 }
191
192 ConfidentialVirtualization detect_confidential_virtualization(void) {
193 static thread_local ConfidentialVirtualization cached_found = _CONFIDENTIAL_VIRTUALIZATION_INVALID;
194 char sig[13] = {};
195 ConfidentialVirtualization cv = CONFIDENTIAL_VIRTUALIZATION_NONE;
196
197 if (cached_found >= 0)
198 return cached_found;
199
200 /* Skip everything on bare metal */
201 if (detect_hypervisor()) {
202 cpuid_leaf(0, sig, true);
203
204 if (memcmp(sig, CPUID_SIG_AMD, sizeof(sig)) == 0)
205 cv = detect_sev();
206 else if (memcmp(sig, CPUID_SIG_INTEL, sizeof(sig)) == 0)
207 cv = detect_tdx();
208 }
209
210 cached_found = cv;
211 return cv;
212 }
213 #else /* ! x86_64 */
214 ConfidentialVirtualization detect_confidential_virtualization(void) {
215 log_debug("No confidential virtualization detection on this architecture");
216 return CONFIDENTIAL_VIRTUALIZATION_NONE;
217 }
218 #endif /* ! x86_64 */
219
220 static const char *const confidential_virtualization_table[_CONFIDENTIAL_VIRTUALIZATION_MAX] = {
221 [CONFIDENTIAL_VIRTUALIZATION_NONE] = "none",
222 [CONFIDENTIAL_VIRTUALIZATION_SEV] = "sev",
223 [CONFIDENTIAL_VIRTUALIZATION_SEV_ES] = "sev-es",
224 [CONFIDENTIAL_VIRTUALIZATION_SEV_SNP] = "sev-snp",
225 [CONFIDENTIAL_VIRTUALIZATION_TDX] = "tdx",
226 };
227
228 DEFINE_STRING_TABLE_LOOKUP(confidential_virtualization, ConfidentialVirtualization);