summaryrefslogtreecommitdiffstats
path: root/src/basic/confidential-virt.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:49:52 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:49:52 +0000
commit55944e5e40b1be2afc4855d8d2baf4b73d1876b5 (patch)
tree33f869f55a1b149e9b7c2b7e201867ca5dd52992 /src/basic/confidential-virt.c
parentInitial commit. (diff)
downloadsystemd-55944e5e40b1be2afc4855d8d2baf4b73d1876b5.tar.xz
systemd-55944e5e40b1be2afc4855d8d2baf4b73d1876b5.zip
Adding upstream version 255.4.upstream/255.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/basic/confidential-virt.c')
-rw-r--r--src/basic/confidential-virt.c228
1 files changed, 228 insertions, 0 deletions
diff --git a/src/basic/confidential-virt.c b/src/basic/confidential-virt.c
new file mode 100644
index 0000000..b6521cf
--- /dev/null
+++ b/src/basic/confidential-virt.c
@@ -0,0 +1,228 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#if defined(__i386__) || defined(__x86_64__)
+#include <cpuid.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "confidential-virt-fundamental.h"
+#include "confidential-virt.h"
+#include "fd-util.h"
+#include "missing_threads.h"
+#include "string-table.h"
+#include "utf8.h"
+
+
+#if defined(__x86_64__)
+
+static void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
+ log_debug("CPUID func %" PRIx32 " %" PRIx32, *eax, *ecx);
+ __cpuid_count(*eax, *ecx, *eax, *ebx, *ecx, *edx);
+ log_debug("CPUID result %" PRIx32 " %" PRIx32 " %" PRIx32 " %" PRIx32, *eax, *ebx, *ecx, *edx);
+}
+
+static uint32_t cpuid_leaf(uint32_t eax, char ret_sig[static 13], bool swapped) {
+ /* zero-init as some queries explicitly require subleaf == 0 */
+ uint32_t sig[3] = {};
+
+ if (swapped)
+ cpuid(&eax, &sig[0], &sig[2], &sig[1]);
+ else
+ cpuid(&eax, &sig[0], &sig[1], &sig[2]);
+ memcpy(ret_sig, sig, sizeof(sig));
+ ret_sig[12] = 0; /* \0-terminate the string to make string comparison possible */
+
+ /* In some CI tests ret_sig doesn't contain valid UTF8 and prints garbage to the console */
+ log_debug("CPUID sig '%s'", strna(utf8_is_valid(ret_sig)));
+
+ return eax;
+}
+
+#define MSR_DEVICE "/dev/cpu/0/msr"
+
+static uint64_t msr(uint64_t index) {
+ uint64_t ret;
+ ssize_t rv;
+ _cleanup_close_ int fd = -EBADF;
+
+ fd = open(MSR_DEVICE, O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ log_debug_errno(errno,
+ "Cannot open MSR device %s (index %" PRIu64 "), ignoring: %m",
+ MSR_DEVICE,
+ index);
+ return 0;
+ }
+
+ rv = pread(fd, &ret, sizeof(ret), index);
+ if (rv < 0) {
+ log_debug_errno(errno,
+ "Cannot read MSR device %s (index %" PRIu64 "), ignoring: %m",
+ MSR_DEVICE,
+ index);
+ return 0;
+ } else if (rv != sizeof(ret)) {
+ log_debug("Short read %zd bytes from MSR device %s (index %" PRIu64 "), ignoring",
+ rv,
+ MSR_DEVICE,
+ index);
+ return 0;
+ }
+
+ log_debug("MSR %" PRIu64 " result %" PRIu64 "", index, ret);
+ return ret;
+}
+
+static bool detect_hyperv_sev(void) {
+ uint32_t eax, ebx, ecx, edx, feat;
+ char sig[13] = {};
+
+ feat = cpuid_leaf(CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS, sig, false);
+
+ if (feat < CPUID_HYPERV_MIN || feat > CPUID_HYPERV_MAX)
+ return false;
+
+ if (memcmp(sig, CPUID_SIG_HYPERV, sizeof(sig)) != 0)
+ return false;
+
+ log_debug("CPUID is on hyperv");
+ eax = CPUID_HYPERV_FEATURES;
+ ebx = ecx = edx = 0;
+
+ cpuid(&eax, &ebx, &ecx, &edx);
+
+ if (ebx & CPUID_HYPERV_ISOLATION && !(ebx & CPUID_HYPERV_CPU_MANAGEMENT)) {
+
+ eax = CPUID_HYPERV_ISOLATION_CONFIG;
+ ebx = ecx = edx = 0;
+ cpuid(&eax, &ebx, &ecx, &edx);
+
+ if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == CPUID_HYPERV_ISOLATION_TYPE_SNP)
+ return true;
+ }
+
+ return false;
+}
+
+static ConfidentialVirtualization detect_sev(void) {
+ uint32_t eax, ebx, ecx, edx;
+ uint64_t msrval;
+
+ eax = CPUID_GET_HIGHEST_FUNCTION;
+ ebx = ecx = edx = 0;
+
+ cpuid(&eax, &ebx, &ecx, &edx);
+
+ if (eax < CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES)
+ return CONFIDENTIAL_VIRTUALIZATION_NONE;
+
+ eax = CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES;
+ ebx = ecx = edx = 0;
+
+ cpuid(&eax, &ebx, &ecx, &edx);
+
+ /* bit 1 == CPU supports SEV feature
+ *
+ * Note, Azure blocks this CPUID leaf from its SEV-SNP
+ * guests, so we must fallback to trying some HyperV
+ * specific CPUID checks.
+ */
+ if (!(eax & EAX_SEV)) {
+ log_debug("No sev in CPUID, trying hyperv CPUID");
+
+ if (detect_hyperv_sev())
+ return CONFIDENTIAL_VIRTUALIZATION_SEV_SNP;
+
+ log_debug("No hyperv CPUID");
+ return CONFIDENTIAL_VIRTUALIZATION_NONE;
+ }
+
+ msrval = msr(MSR_AMD64_SEV);
+
+ /* Test reverse order, since the SEV-SNP bit implies
+ * the SEV-ES bit, which implies the SEV bit */
+ if (msrval & MSR_SEV_SNP)
+ return CONFIDENTIAL_VIRTUALIZATION_SEV_SNP;
+ if (msrval & MSR_SEV_ES)
+ return CONFIDENTIAL_VIRTUALIZATION_SEV_ES;
+ if (msrval & MSR_SEV)
+ return CONFIDENTIAL_VIRTUALIZATION_SEV;
+
+ return CONFIDENTIAL_VIRTUALIZATION_NONE;
+}
+
+static ConfidentialVirtualization detect_tdx(void) {
+ uint32_t eax, ebx, ecx, edx;
+ char sig[13] = {};
+
+ eax = CPUID_GET_HIGHEST_FUNCTION;
+ ebx = ecx = edx = 0;
+
+ cpuid(&eax, &ebx, &ecx, &edx);
+
+ if (eax < CPUID_INTEL_TDX_ENUMERATION)
+ return CONFIDENTIAL_VIRTUALIZATION_NONE;
+
+ cpuid_leaf(CPUID_INTEL_TDX_ENUMERATION, sig, true);
+
+ if (memcmp(sig, CPUID_SIG_INTEL_TDX, sizeof(sig)) == 0)
+ return CONFIDENTIAL_VIRTUALIZATION_TDX;
+
+ return CONFIDENTIAL_VIRTUALIZATION_NONE;
+}
+
+static bool detect_hypervisor(void) {
+ uint32_t eax, ebx, ecx, edx;
+ bool is_hv;
+
+ eax = CPUID_PROCESSOR_INFO_AND_FEATURE_BITS;
+ ebx = ecx = edx = 0;
+
+ cpuid(&eax, &ebx, &ecx, &edx);
+
+ is_hv = ecx & CPUID_FEATURE_HYPERVISOR;
+
+ log_debug("CPUID is hypervisor: %s", yes_no(is_hv));
+ return is_hv;
+}
+
+ConfidentialVirtualization detect_confidential_virtualization(void) {
+ static thread_local ConfidentialVirtualization cached_found = _CONFIDENTIAL_VIRTUALIZATION_INVALID;
+ char sig[13] = {};
+ ConfidentialVirtualization cv = CONFIDENTIAL_VIRTUALIZATION_NONE;
+
+ if (cached_found >= 0)
+ return cached_found;
+
+ /* Skip everything on bare metal */
+ if (detect_hypervisor()) {
+ cpuid_leaf(0, sig, true);
+
+ if (memcmp(sig, CPUID_SIG_AMD, sizeof(sig)) == 0)
+ cv = detect_sev();
+ else if (memcmp(sig, CPUID_SIG_INTEL, sizeof(sig)) == 0)
+ cv = detect_tdx();
+ }
+
+ cached_found = cv;
+ return cv;
+}
+#else /* ! x86_64 */
+ConfidentialVirtualization detect_confidential_virtualization(void) {
+ log_debug("No confidential virtualization detection on this architecture");
+ return CONFIDENTIAL_VIRTUALIZATION_NONE;
+}
+#endif /* ! x86_64 */
+
+static const char *const confidential_virtualization_table[_CONFIDENTIAL_VIRTUALIZATION_MAX] = {
+ [CONFIDENTIAL_VIRTUALIZATION_NONE] = "none",
+ [CONFIDENTIAL_VIRTUALIZATION_SEV] = "sev",
+ [CONFIDENTIAL_VIRTUALIZATION_SEV_ES] = "sev-es",
+ [CONFIDENTIAL_VIRTUALIZATION_SEV_SNP] = "sev-snp",
+ [CONFIDENTIAL_VIRTUALIZATION_TDX] = "tdx",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(confidential_virtualization, ConfidentialVirtualization);