284 lines
9.1 KiB
Diff
284 lines
9.1 KiB
Diff
From 7f2aa231529a03552d642eccae4c4cc209a3ccdb Mon Sep 17 00:00:00 2001
|
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
|
Date: Fri, 18 Jul 2025 18:03:46 +0200
|
|
Subject: [PATCH 037/115] i386/tdx: Initialize TDX before creating TD vcpus
|
|
|
|
RH-Author: Paolo Bonzini <pbonzini@redhat.com>
|
|
RH-MergeRequest: 391: TDX support, including attestation and device assignment
|
|
RH-Jira: RHEL-15710 RHEL-20798 RHEL-49728
|
|
RH-Acked-by: Yash Mankad <None>
|
|
RH-Acked-by: Peter Xu <peterx@redhat.com>
|
|
RH-Acked-by: David Hildenbrand <david@redhat.com>
|
|
RH-Commit: [37/115] 1626ea547590c6f25d7d51b66a23820a701398af (bonzini/rhel-qemu-kvm)
|
|
|
|
Invoke KVM_TDX_INIT_VM in kvm_arch_pre_create_vcpu() that
|
|
KVM_TDX_INIT_VM configures global TD configurations, e.g. the canonical
|
|
CPUID config, and must be executed prior to creating vCPUs.
|
|
|
|
Use kvm_x86_arch_cpuid() to setup the CPUID settings for TDX VM.
|
|
|
|
Note, this doesn't address the fact that QEMU may change the CPUID
|
|
configuration when creating vCPUs, i.e. punts on refactoring QEMU to
|
|
provide a stable CPUID config prior to kvm_arch_init().
|
|
|
|
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
|
|
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
|
|
Acked-by: Markus Armbruster <armbru@redhat.com>
|
|
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
|
|
Link: https://lore.kernel.org/r/20250508150002.689633-9-xiaoyao.li@intel.com
|
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
|
(cherry picked from commit f15898b0f50609d66465326221aa54b6699da674)
|
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
|
---
|
|
target/i386/kvm/kvm.c | 16 +++---
|
|
target/i386/kvm/kvm_i386.h | 5 ++
|
|
target/i386/kvm/meson.build | 2 +-
|
|
target/i386/kvm/tdx-stub.c | 10 ++++
|
|
target/i386/kvm/tdx.c | 105 ++++++++++++++++++++++++++++++++++++
|
|
target/i386/kvm/tdx.h | 6 +++
|
|
6 files changed, 137 insertions(+), 7 deletions(-)
|
|
create mode 100644 target/i386/kvm/tdx-stub.c
|
|
|
|
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
|
|
index 1fddec6b9c..3b71c182ab 100644
|
|
--- a/target/i386/kvm/kvm.c
|
|
+++ b/target/i386/kvm/kvm.c
|
|
@@ -38,6 +38,7 @@
|
|
#include "kvm_i386.h"
|
|
#include "../confidential-guest.h"
|
|
#include "sev.h"
|
|
+#include "tdx.h"
|
|
#include "xen-emu.h"
|
|
#include "hyperv.h"
|
|
#include "hyperv-proto.h"
|
|
@@ -406,9 +407,9 @@ static uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg)
|
|
|
|
/* Find matching entry for function/index on kvm_cpuid2 struct
|
|
*/
|
|
-static struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid,
|
|
- uint32_t function,
|
|
- uint32_t index)
|
|
+struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid,
|
|
+ uint32_t function,
|
|
+ uint32_t index)
|
|
{
|
|
int i;
|
|
for (i = 0; i < cpuid->nent; ++i) {
|
|
@@ -1813,9 +1814,8 @@ static void kvm_init_nested_state(CPUX86State *env)
|
|
}
|
|
}
|
|
|
|
-static uint32_t kvm_x86_build_cpuid(CPUX86State *env,
|
|
- struct kvm_cpuid_entry2 *entries,
|
|
- uint32_t cpuid_i)
|
|
+uint32_t kvm_x86_build_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries,
|
|
+ uint32_t cpuid_i)
|
|
{
|
|
uint32_t limit, i, j;
|
|
uint32_t unused;
|
|
@@ -2041,6 +2041,10 @@ full:
|
|
|
|
int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp)
|
|
{
|
|
+ if (is_tdx_vm()) {
|
|
+ return tdx_pre_create_vcpu(cpu, errp);
|
|
+ }
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h
|
|
index 499691e8d0..f2fbf3f99f 100644
|
|
--- a/target/i386/kvm/kvm_i386.h
|
|
+++ b/target/i386/kvm/kvm_i386.h
|
|
@@ -59,6 +59,11 @@ uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address);
|
|
void kvm_update_msi_routes_all(void *private, bool global,
|
|
uint32_t index, uint32_t mask);
|
|
|
|
+struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid,
|
|
+ uint32_t function,
|
|
+ uint32_t index);
|
|
+uint32_t kvm_x86_build_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries,
|
|
+ uint32_t cpuid_i);
|
|
#endif /* CONFIG_KVM */
|
|
|
|
void kvm_pc_setup_irq_routing(bool pci_enabled);
|
|
diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build
|
|
index 466bccb9cb..3f44cdedb7 100644
|
|
--- a/target/i386/kvm/meson.build
|
|
+++ b/target/i386/kvm/meson.build
|
|
@@ -8,7 +8,7 @@ i386_kvm_ss.add(files(
|
|
|
|
i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c'))
|
|
|
|
-i386_kvm_ss.add(when: 'CONFIG_TDX', if_true: files('tdx.c'))
|
|
+i386_kvm_ss.add(when: 'CONFIG_TDX', if_true: files('tdx.c'), if_false: files('tdx-stub.c'))
|
|
|
|
i386_system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c'))
|
|
|
|
diff --git a/target/i386/kvm/tdx-stub.c b/target/i386/kvm/tdx-stub.c
|
|
new file mode 100644
|
|
index 0000000000..2344433594
|
|
--- /dev/null
|
|
+++ b/target/i386/kvm/tdx-stub.c
|
|
@@ -0,0 +1,10 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+
|
|
+#include "qemu/osdep.h"
|
|
+
|
|
+#include "tdx.h"
|
|
+
|
|
+int tdx_pre_create_vcpu(CPUState *cpu, Error **errp)
|
|
+{
|
|
+ return -EINVAL;
|
|
+}
|
|
diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c
|
|
index 16f67e18ae..8f02c76249 100644
|
|
--- a/target/i386/kvm/tdx.c
|
|
+++ b/target/i386/kvm/tdx.c
|
|
@@ -149,6 +149,109 @@ static int tdx_kvm_type(X86ConfidentialGuest *cg)
|
|
return KVM_X86_TDX_VM;
|
|
}
|
|
|
|
+static int setup_td_xfam(X86CPU *x86cpu, Error **errp)
|
|
+{
|
|
+ CPUX86State *env = &x86cpu->env;
|
|
+ uint64_t xfam;
|
|
+
|
|
+ xfam = env->features[FEAT_XSAVE_XCR0_LO] |
|
|
+ env->features[FEAT_XSAVE_XCR0_HI] |
|
|
+ env->features[FEAT_XSAVE_XSS_LO] |
|
|
+ env->features[FEAT_XSAVE_XSS_HI];
|
|
+
|
|
+ if (xfam & ~tdx_caps->supported_xfam) {
|
|
+ error_setg(errp, "Invalid XFAM 0x%lx for TDX VM (supported: 0x%llx))",
|
|
+ xfam, tdx_caps->supported_xfam);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ tdx_guest->xfam = xfam;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void tdx_filter_cpuid(struct kvm_cpuid2 *cpuids)
|
|
+{
|
|
+ int i, dest_cnt = 0;
|
|
+ struct kvm_cpuid_entry2 *src, *dest, *conf;
|
|
+
|
|
+ for (i = 0; i < cpuids->nent; i++) {
|
|
+ src = cpuids->entries + i;
|
|
+ conf = cpuid_find_entry(&tdx_caps->cpuid, src->function, src->index);
|
|
+ if (!conf) {
|
|
+ continue;
|
|
+ }
|
|
+ dest = cpuids->entries + dest_cnt;
|
|
+
|
|
+ dest->function = src->function;
|
|
+ dest->index = src->index;
|
|
+ dest->flags = src->flags;
|
|
+ dest->eax = src->eax & conf->eax;
|
|
+ dest->ebx = src->ebx & conf->ebx;
|
|
+ dest->ecx = src->ecx & conf->ecx;
|
|
+ dest->edx = src->edx & conf->edx;
|
|
+
|
|
+ dest_cnt++;
|
|
+ }
|
|
+ cpuids->nent = dest_cnt++;
|
|
+}
|
|
+
|
|
+int tdx_pre_create_vcpu(CPUState *cpu, Error **errp)
|
|
+{
|
|
+ X86CPU *x86cpu = X86_CPU(cpu);
|
|
+ CPUX86State *env = &x86cpu->env;
|
|
+ g_autofree struct kvm_tdx_init_vm *init_vm = NULL;
|
|
+ Error *local_err = NULL;
|
|
+ int retry = 10000;
|
|
+ int r = 0;
|
|
+
|
|
+ QEMU_LOCK_GUARD(&tdx_guest->lock);
|
|
+ if (tdx_guest->initialized) {
|
|
+ return r;
|
|
+ }
|
|
+
|
|
+ init_vm = g_malloc0(sizeof(struct kvm_tdx_init_vm) +
|
|
+ sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES);
|
|
+
|
|
+ r = setup_td_xfam(x86cpu, errp);
|
|
+ if (r) {
|
|
+ return r;
|
|
+ }
|
|
+
|
|
+ init_vm->cpuid.nent = kvm_x86_build_cpuid(env, init_vm->cpuid.entries, 0);
|
|
+ tdx_filter_cpuid(&init_vm->cpuid);
|
|
+
|
|
+ init_vm->attributes = tdx_guest->attributes;
|
|
+ init_vm->xfam = tdx_guest->xfam;
|
|
+
|
|
+ /*
|
|
+ * KVM_TDX_INIT_VM gets -EAGAIN when KVM side SEAMCALL(TDH_MNG_CREATE)
|
|
+ * gets TDX_RND_NO_ENTROPY due to Random number generation (e.g., RDRAND or
|
|
+ * RDSEED) is busy.
|
|
+ *
|
|
+ * Retry for the case.
|
|
+ */
|
|
+ do {
|
|
+ error_free(local_err);
|
|
+ local_err = NULL;
|
|
+ r = tdx_vm_ioctl(KVM_TDX_INIT_VM, 0, init_vm, &local_err);
|
|
+ } while (r == -EAGAIN && --retry);
|
|
+
|
|
+ if (r < 0) {
|
|
+ if (!retry) {
|
|
+ error_append_hint(&local_err, "Hardware RNG (Random Number "
|
|
+ "Generator) is busy occupied by someone (via RDRAND/RDSEED) "
|
|
+ "maliciously, which leads to KVM_TDX_INIT_VM keeping failure "
|
|
+ "due to lack of entropy.\n");
|
|
+ }
|
|
+ error_propagate(errp, local_err);
|
|
+ return r;
|
|
+ }
|
|
+
|
|
+ tdx_guest->initialized = true;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/* tdx guest */
|
|
OBJECT_DEFINE_TYPE_WITH_INTERFACES(TdxGuest,
|
|
tdx_guest,
|
|
@@ -162,6 +265,8 @@ static void tdx_guest_init(Object *obj)
|
|
ConfidentialGuestSupport *cgs = CONFIDENTIAL_GUEST_SUPPORT(obj);
|
|
TdxGuest *tdx = TDX_GUEST(obj);
|
|
|
|
+ qemu_mutex_init(&tdx->lock);
|
|
+
|
|
cgs->require_guest_memfd = true;
|
|
tdx->attributes = 0;
|
|
|
|
diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h
|
|
index de8ae91961..4e2b5c61ff 100644
|
|
--- a/target/i386/kvm/tdx.h
|
|
+++ b/target/i386/kvm/tdx.h
|
|
@@ -19,7 +19,11 @@ typedef struct TdxGuestClass {
|
|
typedef struct TdxGuest {
|
|
X86ConfidentialGuest parent_obj;
|
|
|
|
+ QemuMutex lock;
|
|
+
|
|
+ bool initialized;
|
|
uint64_t attributes; /* TD attributes */
|
|
+ uint64_t xfam;
|
|
} TdxGuest;
|
|
|
|
#ifdef CONFIG_TDX
|
|
@@ -28,4 +32,6 @@ bool is_tdx_vm(void);
|
|
#define is_tdx_vm() 0
|
|
#endif /* CONFIG_TDX */
|
|
|
|
+int tdx_pre_create_vcpu(CPUState *cpu, Error **errp);
|
|
+
|
|
#endif /* QEMU_I386_TDX_H */
|
|
--
|
|
2.50.1
|
|
|