1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #if defined(__i386__) || defined(__x86_64__)
11 #include "confidential-virt-fundamental.h"
12 #include "confidential-virt.h"
14 #include "missing_threads.h"
15 #include "string-table.h"
19 #if defined(__x86_64__)
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
);
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 */
32 cpuid(&eax
, &sig
[0], &sig
[2], &sig
[1]);
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 */
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
)));
44 #define MSR_DEVICE "/dev/cpu/0/msr"
46 static uint64_t msr(uint64_t index
) {
49 _cleanup_close_
int fd
= -EBADF
;
51 fd
= open(MSR_DEVICE
, O_RDONLY
|O_CLOEXEC
);
53 log_debug_errno(errno
,
54 "Cannot open MSR device %s (index %" PRIu64
"), ignoring: %m",
60 rv
= pread(fd
, &ret
, sizeof(ret
), index
);
62 log_debug_errno(errno
,
63 "Cannot read MSR device %s (index %" PRIu64
"), ignoring: %m",
67 } else if (rv
!= sizeof(ret
)) {
68 log_debug("Short read %zd bytes from MSR device %s (index %" PRIu64
"), ignoring",
75 log_debug("MSR %" PRIu64
" result %" PRIu64
"", index
, ret
);
79 static bool detect_hyperv_sev(void) {
80 uint32_t eax
, ebx
, ecx
, edx
, feat
;
83 feat
= cpuid_leaf(CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS
, sig
, false);
85 if (feat
< CPUID_HYPERV_MIN
|| feat
> CPUID_HYPERV_MAX
)
88 if (memcmp(sig
, CPUID_SIG_HYPERV
, sizeof(sig
)) != 0)
91 log_debug("CPUID is on hyperv");
92 eax
= CPUID_HYPERV_FEATURES
;
95 cpuid(&eax
, &ebx
, &ecx
, &edx
);
97 if (ebx
& CPUID_HYPERV_ISOLATION
&& !(ebx
& CPUID_HYPERV_CPU_MANAGEMENT
)) {
99 eax
= CPUID_HYPERV_ISOLATION_CONFIG
;
101 cpuid(&eax
, &ebx
, &ecx
, &edx
);
103 if ((ebx
& CPUID_HYPERV_ISOLATION_TYPE_MASK
) == CPUID_HYPERV_ISOLATION_TYPE_SNP
)
110 static ConfidentialVirtualization
detect_sev(void) {
111 uint32_t eax
, ebx
, ecx
, edx
;
114 eax
= CPUID_GET_HIGHEST_FUNCTION
;
117 cpuid(&eax
, &ebx
, &ecx
, &edx
);
119 if (eax
< CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES
)
120 return CONFIDENTIAL_VIRTUALIZATION_NONE
;
122 eax
= CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES
;
125 cpuid(&eax
, &ebx
, &ecx
, &edx
);
127 /* bit 1 == CPU supports SEV feature
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.
133 if (!(eax
& EAX_SEV
)) {
134 log_debug("No sev in CPUID, trying hyperv CPUID");
136 if (detect_hyperv_sev())
137 return CONFIDENTIAL_VIRTUALIZATION_SEV_SNP
;
139 log_debug("No hyperv CPUID");
140 return CONFIDENTIAL_VIRTUALIZATION_NONE
;
143 msrval
= msr(MSR_AMD64_SEV
);
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
;
154 return CONFIDENTIAL_VIRTUALIZATION_NONE
;
157 static ConfidentialVirtualization
detect_tdx(void) {
158 uint32_t eax
, ebx
, ecx
, edx
;
161 eax
= CPUID_GET_HIGHEST_FUNCTION
;
164 cpuid(&eax
, &ebx
, &ecx
, &edx
);
166 if (eax
< CPUID_INTEL_TDX_ENUMERATION
)
167 return CONFIDENTIAL_VIRTUALIZATION_NONE
;
169 cpuid_leaf(CPUID_INTEL_TDX_ENUMERATION
, sig
, true);
171 if (memcmp(sig
, CPUID_SIG_INTEL_TDX
, sizeof(sig
)) == 0)
172 return CONFIDENTIAL_VIRTUALIZATION_TDX
;
174 return CONFIDENTIAL_VIRTUALIZATION_NONE
;
177 static bool detect_hypervisor(void) {
178 uint32_t eax
, ebx
, ecx
, edx
;
181 eax
= CPUID_PROCESSOR_INFO_AND_FEATURE_BITS
;
184 cpuid(&eax
, &ebx
, &ecx
, &edx
);
186 is_hv
= ecx
& CPUID_FEATURE_HYPERVISOR
;
188 log_debug("CPUID is hypervisor: %s", yes_no(is_hv
));
192 ConfidentialVirtualization
detect_confidential_virtualization(void) {
193 static thread_local ConfidentialVirtualization cached_found
= _CONFIDENTIAL_VIRTUALIZATION_INVALID
;
195 ConfidentialVirtualization cv
= CONFIDENTIAL_VIRTUALIZATION_NONE
;
197 if (cached_found
>= 0)
200 /* Skip everything on bare metal */
201 if (detect_hypervisor()) {
202 cpuid_leaf(0, sig
, true);
204 if (memcmp(sig
, CPUID_SIG_AMD
, sizeof(sig
)) == 0)
206 else if (memcmp(sig
, CPUID_SIG_INTEL
, sizeof(sig
)) == 0)
214 ConfidentialVirtualization
detect_confidential_virtualization(void) {
215 log_debug("No confidential virtualization detection on this architecture");
216 return CONFIDENTIAL_VIRTUALIZATION_NONE
;
218 #endif /* ! x86_64 */
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",
228 DEFINE_STRING_TABLE_LOOKUP(confidential_virtualization
, ConfidentialVirtualization
);