From nobody Fri Dec 19 12:14:58 2025 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 43EED21D585 for ; Sat, 6 Dec 2025 01:11:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983472; cv=none; b=iGT9ra1AOektNVp46rRAPo94k4btYb6dXUIziLiWuuipDpGMqvwKSsHCAXXZZoToAcl/p+5OsqhmJPmIzxCHZjaVSlWp2M44CBuz0ABXNmcrKn1p0n1k6FUcfW5sFBPOIjbnDkeJb+HLeqQxV9qaS8BPqp7rQ2Z0eXefxMQCRJE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983472; c=relaxed/simple; bh=Xoj3VzQYzVhpJbx4kNzoLA3tCFxy7VEFT4XaIsLRops=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=mPU7Fg3uQZRCWzhVR3mNvT+IKeaIxMvJT5k5HKwSt7eZT+Vadtdf3XFt1PinZwpZ4UA26Jbqfexj9Sk0HOy1pvcL4ve2kbsYdLeiUEP71/x9mkPEHcaK2Fvysus3y6aRkHfUxilyk7eeG3AfusYV30YTvOoTdKKwHD3V6sR7Dxw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=P0f8EGub; arc=none smtp.client-ip=209.85.214.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="P0f8EGub" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-295915934bfso37435245ad.0 for ; Fri, 05 Dec 2025 17:11:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1764983468; x=1765588268; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:from:to:cc:subject:date:message-id:reply-to; bh=pC6DfslZcu3nvvZIIA0BpqpSQzylE5RCvOKLxGkj7nQ=; b=P0f8EGubhEzbIREB811oRmqYboTX0t576lIZqsFwwTeg2uQxPDM2AToOrIYQnGZmGt Ge1f2+4B3WPRwKi1oucFmWHHCCHbGz65EHaWjmpI9huipUg0UFfDckIimPwefGgkSDLZ FyZiB1QhhoeWVy5G8N8czISVFgtGHsD2p5SAYkhurK/HqMX6oB209etYOiFrRRh+UxJG Ye/o+pKHHt8GEITwpUrIFVZAV21mX++GIMhNpEnmgCCflYsd9LijbZOz+qYyq62rmLQy AmOdDxri/4/wshYu2nZBYGnZCuPnW8jgx0dGKBSJui5lE5qU6MT1BgbcwyIcP09aK3og r4vQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764983468; x=1765588268; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=pC6DfslZcu3nvvZIIA0BpqpSQzylE5RCvOKLxGkj7nQ=; b=gsA/9lVsuOu0yOGGeUvk0mk7BJhqZwLIAxML7o7wZ08rSPdmWruqJ2Y29yoDRHbagv efg9ZmHaZ7loGJUSCoZ06DZPRt2N7ARy1f1LBsLDk+Ybl1iE7RXg4lzebGzsSbKaM3V1 TVx1kbtBXIANEC1ACVfMrQsljSWaH/eV9KJoHvckmbRgaC2jArUzLoGBHuOuNdL8Az5G n8MGdjtkXphgXjFpHK28+1IxrlCojckgZ7cyD415GdVdXQhbXC8VI1JYb8xvZSrOVRMR 7awJEzPLy9Q1hdE05XVkYa5SjCKJBw+STeSd4oZiNDH9ZmQxxgEKkE3jG8JWPxXztAxm nq2A== X-Gm-Message-State: AOJu0YxepKPvDu+dQ6/6OTuMIskCeh+aF5k/ipySkfpfYRBBahO3410I EQP4Ea+Bn6ZjRASUZd/E5cQds/4/ksTwNAejlFAGePotoZsK/IcxbcP6nJxbCwe6IlzY8J8NSeY l6akiBw== X-Google-Smtp-Source: AGHT+IHqg7v/oM3O+Iza+gbHQ+Fut3uvvdzLynuyyGHbiI9eTt5zTPTdsfs0StG85k5cWuU3XEHxZlf+cm8= X-Received: from pldo13.prod.google.com ([2002:a17:903:8d:b0:27e:ed03:b5a5]) (user=seanjc job=prod-delivery.src-stubby-dispatcher) by 2002:a17:902:e805:b0:295:ac6f:c899 with SMTP id d9443c01a7336-29df5dd24f3mr8573855ad.47.1764983468487; Fri, 05 Dec 2025 17:11:08 -0800 (PST) Reply-To: Sean Christopherson Date: Fri, 5 Dec 2025 17:10:48 -0800 In-Reply-To: <20251206011054.494190-1-seanjc@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251206011054.494190-1-seanjc@google.com> X-Mailer: git-send-email 2.52.0.223.gf5cc29aaa4-goog Message-ID: <20251206011054.494190-2-seanjc@google.com> Subject: [PATCH v2 1/7] KVM: x86: Move kvm_rebooting to x86 From: Sean Christopherson To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, Kiryl Shutsemau , Sean Christopherson , Paolo Bonzini Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev, kvm@vger.kernel.org, Chao Gao , Dan Williams Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Move kvm_rebooting, which is only read by x86, to KVM x86 so that it can be moved again to core x86 code. Add a "shutdown" arch hook to facilate setting the flag in KVM x86. Signed-off-by: Sean Christopherson Reviewed-by: Chao Gao Tested-by: Chao Gao --- arch/x86/kvm/x86.c | 13 +++++++++++++ arch/x86/kvm/x86.h | 1 + include/linux/kvm_host.h | 2 +- virt/kvm/kvm_main.c | 14 +++++++------- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 0c6d899d53dd..80cb882f19e2 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -694,6 +694,9 @@ static void drop_user_return_notifiers(void) kvm_on_user_return(&msrs->urn); } =20 +__visible bool kvm_rebooting; +EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_rebooting); + /* * Handle a fault on a hardware virtualization (VMX or SVM) instruction. * @@ -13100,6 +13103,16 @@ int kvm_arch_enable_virtualization_cpu(void) return 0; } =20 +void kvm_arch_shutdown(void) +{ + /* + * Set kvm_rebooting to indicate that KVM has asynchronously disabled + * hardware virtualization, i.e. that relevant errors and exceptions + * aren't entirely unexpected. + */ + kvm_rebooting =3D true; +} + void kvm_arch_disable_virtualization_cpu(void) { kvm_x86_call(disable_virtualization_cpu)(); diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index fdab0ad49098..40993348a967 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -54,6 +54,7 @@ struct kvm_host_values { u64 arch_capabilities; }; =20 +extern bool kvm_rebooting; void kvm_spurious_fault(void); =20 #define SIZE_OF_MEMSLOTS_HASHTABLE \ diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index d93f75b05ae2..a453fe6ce05a 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1621,6 +1621,7 @@ static inline void kvm_create_vcpu_debugfs(struct kvm= _vcpu *vcpu) {} #endif =20 #ifdef CONFIG_KVM_GENERIC_HARDWARE_ENABLING +void kvm_arch_shutdown(void); /* * kvm_arch_{enable,disable}_virtualization() are called on one CPU, under * kvm_usage_lock, immediately after/before 0=3D>1 and 1=3D>0 transitions = of @@ -2302,7 +2303,6 @@ static inline bool kvm_check_request(int req, struct = kvm_vcpu *vcpu) =20 #ifdef CONFIG_KVM_GENERIC_HARDWARE_ENABLING extern bool enable_virt_at_load; -extern bool kvm_rebooting; #endif =20 extern unsigned int halt_poll_ns; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index f1f6a71b2b5f..3278ee9381bd 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -5586,13 +5586,15 @@ bool enable_virt_at_load =3D true; module_param(enable_virt_at_load, bool, 0444); EXPORT_SYMBOL_FOR_KVM_INTERNAL(enable_virt_at_load); =20 -__visible bool kvm_rebooting; -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_rebooting); - static DEFINE_PER_CPU(bool, virtualization_enabled); static DEFINE_MUTEX(kvm_usage_lock); static int kvm_usage_count; =20 +__weak void kvm_arch_shutdown(void) +{ + +} + __weak void kvm_arch_enable_virtualization(void) { =20 @@ -5646,10 +5648,9 @@ static int kvm_offline_cpu(unsigned int cpu) =20 static void kvm_shutdown(void) { + kvm_arch_shutdown(); + /* - * Disable hardware virtualization and set kvm_rebooting to indicate - * that KVM has asynchronously disabled hardware virtualization, i.e. - * that relevant errors and exceptions aren't entirely unexpected. * Some flavors of hardware virtualization need to be disabled before * transferring control to firmware (to perform shutdown/reboot), e.g. * on x86, virtualization can block INIT interrupts, which are used by @@ -5658,7 +5659,6 @@ static void kvm_shutdown(void) * 100% comprehensive. */ pr_info("kvm: exiting hardware virtualization\n"); - kvm_rebooting =3D true; on_each_cpu(kvm_disable_virtualization_cpu, NULL, 1); } =20 --=20 2.52.0.223.gf5cc29aaa4-goog From nobody Fri Dec 19 12:14:58 2025 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 19BD024501B for ; Sat, 6 Dec 2025 01:11:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983474; cv=none; b=tDTtul0/jfYxIJy9SzFALTEoVjmECNi3idhciEQhqs2wy8GBPMYfl+kD1PbvcG3e84RTrKVfZ9snZls81uvG3pgNtOqkJrzaxxV003qpJ9KjVt7K/DdHPMFV1WPhrboZybtgAGmyVKVZa0rWADeNUPsTHjggZCldEvmTDIZSJfg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983474; c=relaxed/simple; bh=201CsGCKz4W0+urVUOwLVm4MClguf+5ukCDnh75zDfM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=oGUBcdeTIr+j9l35QtIdCUrlC9K5UXt94HmGruZhI0yKF14HLeSrgyosvtpbrDRl6h2jvC15ZFMXAt4GgA8LRckQ5pHeoky0MfpCKsksxlMQWilsQnUH3UVH/m3g5cATfTJ77VdROy/ML+dOiXCNXItmiNh4I1vizDXwXT/agSg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=wi29wvsd; arc=none smtp.client-ip=209.85.216.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="wi29wvsd" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-34378c914b4so4976649a91.1 for ; Fri, 05 Dec 2025 17:11:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1764983470; x=1765588270; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:from:to:cc:subject:date:message-id:reply-to; bh=njfJRAYPK82NhbJUu19myDsBJhYLqhss6OzhV1U4+RA=; b=wi29wvsdmh326T6obLcFFPtP8lI+XtA0EMywOSo3nWmSreOpEdoS9ZZ/F0z+pXmrdV Z+78A4OlKx1nw6X8mq8bIlSqE0BMLo3dloU9GlrcnTBNGPZiiX70GiRUDebXz1nfDTpR s3aP2ybLFBi8g/Q1IKkp0XNzLJq1OSW3xHapZcdpQciV9O8x6LOCgG7YjUpcWiC06qfc SjcyOWENAVWONgFG7rOfLWsiB/vUKlrizBeG5n9nAcl6fmS5W5rd37QeWrMgPp6J0o9D 24PJ7bKue9BLFIN4ebfJVKOAqHlIkf8AzX+MOP2B9yqkqp8FgSWZwaRa1ZlruFIp9VVA txZg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764983470; x=1765588270; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=njfJRAYPK82NhbJUu19myDsBJhYLqhss6OzhV1U4+RA=; b=BLgcAcF32p4ssVIOb/wieAFItTrxokxwlofb3eRbp4ziFUbTYshg2q+5kF9jEDdUy8 ClBKNhZHxCQRIuXPuJmhyto/lBY4o/9LubN+2CKxBhjr8n7y3qyfpEOOpAtGLas4RL4h 0wVU2kaK9MO313H7l8Q5Qh8Ixy9yTcyk2CV1CnQpDFYijv55ct0xX4Nv07tNORh4QvJp VipMwgCezB6UfZZPigG8A6H3OHZ74x7QXgh7nd/TeBpMTYS+1Gz8uwNetKuIO/G0wMvo DWiTMiEBkDTiNH/vzxj+QyAENPTTZ4FjFL/wRxwf+SF2xkMkQXYe9/L5kZ7ENnexaCpI X/Ag== X-Gm-Message-State: AOJu0YxF5HkFdzjPJF2nvMrjUEFQrcBhuir3zPWB85pYU+HfjsJV6Xg4 fwSm0fLbRosxuml8L0S1BrGQ4kDM8/udLxpogEXM4I2lXTD/pvROwUBb8mbeIQbOWgp73WlG2a7 vNoh0Gg== X-Google-Smtp-Source: AGHT+IFkOLtDvnmfFYHTTYDL9BHtdHKeC87WdwOUtI3zottIjGVbfKKCRYXtpOMrfk4Zl4Y9+LJbAonsrEw= X-Received: from pjbfy7.prod.google.com ([2002:a17:90b:207:b0:340:9a17:4b10]) (user=seanjc job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:3d8b:b0:339:cece:a99 with SMTP id 98e67ed59e1d1-349a25104d4mr604889a91.13.1764983470345; Fri, 05 Dec 2025 17:11:10 -0800 (PST) Reply-To: Sean Christopherson Date: Fri, 5 Dec 2025 17:10:49 -0800 In-Reply-To: <20251206011054.494190-1-seanjc@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251206011054.494190-1-seanjc@google.com> X-Mailer: git-send-email 2.52.0.223.gf5cc29aaa4-goog Message-ID: <20251206011054.494190-3-seanjc@google.com> Subject: [PATCH v2 2/7] KVM: x86: Extract VMXON and EFER.SVME enablement to kernel From: Sean Christopherson To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, Kiryl Shutsemau , Sean Christopherson , Paolo Bonzini Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev, kvm@vger.kernel.org, Chao Gao , Dan Williams Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Move the innermost VMXON and EFER.SVME management logic out of KVM and into to core x86 so that TDX can force VMXON without having to rely on KVM being loaded, e.g. to do SEAMCALLs during initialization. Implement a per-CPU refcounting scheme so that "users", e.g. KVM and the future TDX code, can co-exist without pulling the rug out from under each other. To avoid having to choose between SVM and VMX, simply refuse to enable either if both are somehow supported. No known CPU supports both SVM and VMX, and it's comically unlikely such a CPU will ever exist. For lack of a better name, call the new file "hw.c", to yield "virt hardware" when combined with its parent directory. Signed-off-by: Sean Christopherson Reviewed-by: Dan Williams Tested-by: Chao Gao --- arch/x86/events/intel/pt.c | 1 - arch/x86/include/asm/kvm_host.h | 3 +- arch/x86/include/asm/reboot.h | 11 -- arch/x86/include/asm/virt.h | 26 +++ arch/x86/include/asm/vmx.h | 11 ++ arch/x86/kernel/cpu/common.c | 2 + arch/x86/kernel/crash.c | 3 +- arch/x86/kernel/reboot.c | 63 +----- arch/x86/kernel/smp.c | 5 +- arch/x86/kvm/svm/svm.c | 34 +--- arch/x86/kvm/svm/vmenter.S | 10 +- arch/x86/kvm/vmx/tdx.c | 3 +- arch/x86/kvm/vmx/vmcs.h | 11 -- arch/x86/kvm/vmx/vmenter.S | 2 +- arch/x86/kvm/vmx/vmx.c | 127 +----------- arch/x86/kvm/x86.c | 17 +- arch/x86/kvm/x86.h | 1 - arch/x86/virt/Makefile | 2 + arch/x86/virt/hw.c | 340 ++++++++++++++++++++++++++++++++ 19 files changed, 422 insertions(+), 250 deletions(-) create mode 100644 arch/x86/include/asm/virt.h create mode 100644 arch/x86/virt/hw.c diff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c index e8cf29d2b10c..9092f0f9de72 100644 --- a/arch/x86/events/intel/pt.c +++ b/arch/x86/events/intel/pt.c @@ -1590,7 +1590,6 @@ void intel_pt_handle_vmx(int on) =20 local_irq_restore(flags); } -EXPORT_SYMBOL_GPL(intel_pt_handle_vmx); =20 /* * PMU callbacks diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_hos= t.h index 5a3bfa293e8b..47b535c1c3bd 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -40,7 +40,8 @@ #include #include #include -#include +#include + #include =20 #define __KVM_HAVE_ARCH_VCPU_DEBUGFS diff --git a/arch/x86/include/asm/reboot.h b/arch/x86/include/asm/reboot.h index ecd58ea9a837..a671a1145906 100644 --- a/arch/x86/include/asm/reboot.h +++ b/arch/x86/include/asm/reboot.h @@ -25,17 +25,6 @@ void __noreturn machine_real_restart(unsigned int type); #define MRR_BIOS 0 #define MRR_APM 1 =20 -typedef void (cpu_emergency_virt_cb)(void); -#if IS_ENABLED(CONFIG_KVM_X86) -void cpu_emergency_register_virt_callback(cpu_emergency_virt_cb *callback); -void cpu_emergency_unregister_virt_callback(cpu_emergency_virt_cb *callbac= k); -void cpu_emergency_disable_virtualization(void); -#else -static inline void cpu_emergency_register_virt_callback(cpu_emergency_virt= _cb *callback) {} -static inline void cpu_emergency_unregister_virt_callback(cpu_emergency_vi= rt_cb *callback) {} -static inline void cpu_emergency_disable_virtualization(void) {} -#endif /* CONFIG_KVM_X86 */ - typedef void (*nmi_shootdown_cb)(int, struct pt_regs*); void nmi_shootdown_cpus(nmi_shootdown_cb callback); void run_crash_ipi_callback(struct pt_regs *regs); diff --git a/arch/x86/include/asm/virt.h b/arch/x86/include/asm/virt.h new file mode 100644 index 000000000000..77a366afd9f7 --- /dev/null +++ b/arch/x86/include/asm/virt.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_X86_VIRT_H +#define _ASM_X86_VIRT_H + +#include + +typedef void (cpu_emergency_virt_cb)(void); + +#if IS_ENABLED(CONFIG_KVM_X86) +extern bool virt_rebooting; + +void __init x86_virt_init(void); + +int x86_virt_get_cpu(int feat); +void x86_virt_put_cpu(int feat); + +int x86_virt_emergency_disable_virtualization_cpu(void); + +void x86_virt_register_emergency_callback(cpu_emergency_virt_cb *callback); +void x86_virt_unregister_emergency_callback(cpu_emergency_virt_cb *callbac= k); +#else +static __always_inline void x86_virt_init(void) {} +static inline int x86_virt_emergency_disable_virtualization_cpu(void) { re= turn -ENOENT; } +#endif + +#endif /* _ASM_X86_VIRT_H */ diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index c85c50019523..d2c7eb1c5f12 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -20,6 +20,17 @@ #include #include =20 +struct vmcs_hdr { + u32 revision_id:31; + u32 shadow_vmcs:1; +}; + +struct vmcs { + struct vmcs_hdr hdr; + u32 abort; + char data[]; +}; + #define VMCS_CONTROL_BIT(x) BIT(VMX_FEATURE_##x & 0x1f) =20 /* diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 02d97834a1d4..a55cb572d2b4 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -70,6 +70,7 @@ #include #include #include +#include #include #include =20 @@ -2124,6 +2125,7 @@ static __init void identify_boot_cpu(void) cpu_detect_tlb(&boot_cpu_data); setup_cr_pinning(); =20 + x86_virt_init(); tsx_init(); tdx_init(); lkgs_init(); diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c index 335fd2ee9766..cd796818d94d 100644 --- a/arch/x86/kernel/crash.c +++ b/arch/x86/kernel/crash.c @@ -42,6 +42,7 @@ #include #include #include +#include =20 /* Used while preparing memory map entries for second kernel */ struct crash_memmap_data { @@ -111,7 +112,7 @@ void native_machine_crash_shutdown(struct pt_regs *regs) =20 crash_smp_send_stop(); =20 - cpu_emergency_disable_virtualization(); + x86_virt_emergency_disable_virtualization_cpu(); =20 /* * Disable Intel PT to stop its logging diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 964f6b0a3d68..0f1d14ed955b 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -26,6 +26,7 @@ #include #include #include +#include =20 #include #include @@ -531,51 +532,6 @@ static inline void kb_wait(void) static inline void nmi_shootdown_cpus_on_restart(void); =20 #if IS_ENABLED(CONFIG_KVM_X86) -/* RCU-protected callback to disable virtualization prior to reboot. */ -static cpu_emergency_virt_cb __rcu *cpu_emergency_virt_callback; - -void cpu_emergency_register_virt_callback(cpu_emergency_virt_cb *callback) -{ - if (WARN_ON_ONCE(rcu_access_pointer(cpu_emergency_virt_callback))) - return; - - rcu_assign_pointer(cpu_emergency_virt_callback, callback); -} -EXPORT_SYMBOL_GPL(cpu_emergency_register_virt_callback); - -void cpu_emergency_unregister_virt_callback(cpu_emergency_virt_cb *callbac= k) -{ - if (WARN_ON_ONCE(rcu_access_pointer(cpu_emergency_virt_callback) !=3D cal= lback)) - return; - - rcu_assign_pointer(cpu_emergency_virt_callback, NULL); - synchronize_rcu(); -} -EXPORT_SYMBOL_GPL(cpu_emergency_unregister_virt_callback); - -/* - * Disable virtualization, i.e. VMX or SVM, to ensure INIT is recognized d= uring - * reboot. VMX blocks INIT if the CPU is post-VMXON, and SVM blocks INIT = if - * GIF=3D0, i.e. if the crash occurred between CLGI and STGI. - */ -void cpu_emergency_disable_virtualization(void) -{ - cpu_emergency_virt_cb *callback; - - /* - * IRQs must be disabled as KVM enables virtualization in hardware via - * function call IPIs, i.e. IRQs need to be disabled to guarantee - * virtualization stays disabled. - */ - lockdep_assert_irqs_disabled(); - - rcu_read_lock(); - callback =3D rcu_dereference(cpu_emergency_virt_callback); - if (callback) - callback(); - rcu_read_unlock(); -} - static void emergency_reboot_disable_virtualization(void) { local_irq_disable(); @@ -587,16 +543,11 @@ static void emergency_reboot_disable_virtualization(v= oid) * We can't take any locks and we may be on an inconsistent state, so * use NMIs as IPIs to tell the other CPUs to disable VMX/SVM and halt. * - * Do the NMI shootdown even if virtualization is off on _this_ CPU, as - * other CPUs may have virtualization enabled. + * Safely force _this_ CPU out of VMX/SVM operation, and if necessary, + * blast NMIs to force other CPUs out of VMX/SVM as well.k */ - if (rcu_access_pointer(cpu_emergency_virt_callback)) { - /* Safely force _this_ CPU out of VMX/SVM operation. */ - cpu_emergency_disable_virtualization(); - - /* Disable VMX/SVM and halt on other CPUs. */ + if (!x86_virt_emergency_disable_virtualization_cpu()) nmi_shootdown_cpus_on_restart(); - } } #else static void emergency_reboot_disable_virtualization(void) { } @@ -874,10 +825,10 @@ static int crash_nmi_callback(unsigned int val, struc= t pt_regs *regs) shootdown_callback(cpu, regs); =20 /* - * Prepare the CPU for reboot _after_ invoking the callback so that the - * callback can safely use virtualization instructions, e.g. VMCLEAR. + * Disable virtualization, as both VMX and SVM can block INIT and thus + * prevent AP bringup, e.g. in a kdump kernel or in firmware. */ - cpu_emergency_disable_virtualization(); + x86_virt_emergency_disable_virtualization_cpu(); =20 atomic_dec(&waiting_for_crash_ipi); =20 diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index b014e6d229f9..cbf95fe2b207 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -35,6 +35,7 @@ #include #include #include +#include =20 /* * Some notes on x86 processor bugs affecting SMP operation: @@ -124,7 +125,7 @@ static int smp_stop_nmi_callback(unsigned int val, stru= ct pt_regs *regs) if (raw_smp_processor_id() =3D=3D atomic_read(&stopping_cpu)) return NMI_HANDLED; =20 - cpu_emergency_disable_virtualization(); + x86_virt_emergency_disable_virtualization_cpu(); stop_this_cpu(NULL); =20 return NMI_HANDLED; @@ -136,7 +137,7 @@ static int smp_stop_nmi_callback(unsigned int val, stru= ct pt_regs *regs) DEFINE_IDTENTRY_SYSVEC(sysvec_reboot) { apic_eoi(); - cpu_emergency_disable_virtualization(); + x86_virt_emergency_disable_virtualization_cpu(); stop_this_cpu(NULL); } =20 diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 24d59ccfa40d..c09648cc3bd2 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -44,6 +44,7 @@ #include #include #include +#include =20 #include =20 @@ -476,27 +477,9 @@ static __always_inline struct sev_es_save_area *sev_es= _host_save_area(struct svm return &sd->save_area->host_sev_es_save; } =20 -static inline void kvm_cpu_svm_disable(void) -{ - uint64_t efer; - - wrmsrq(MSR_VM_HSAVE_PA, 0); - rdmsrq(MSR_EFER, efer); - if (efer & EFER_SVME) { - /* - * Force GIF=3D1 prior to disabling SVM, e.g. to ensure INIT and - * NMI aren't blocked. - */ - stgi(); - wrmsrq(MSR_EFER, efer & ~EFER_SVME); - } -} - static void svm_emergency_disable_virtualization_cpu(void) { - kvm_rebooting =3D true; - - kvm_cpu_svm_disable(); + wrmsrq(MSR_VM_HSAVE_PA, 0); } =20 static void svm_disable_virtualization_cpu(void) @@ -505,7 +488,7 @@ static void svm_disable_virtualization_cpu(void) if (tsc_scaling) __svm_write_tsc_multiplier(SVM_TSC_RATIO_DEFAULT); =20 - kvm_cpu_svm_disable(); + x86_virt_put_cpu(X86_FEATURE_SVM); =20 amd_pmu_disable_virt(); } @@ -514,12 +497,12 @@ static int svm_enable_virtualization_cpu(void) { =20 struct svm_cpu_data *sd; - uint64_t efer; int me =3D raw_smp_processor_id(); + int r; =20 - rdmsrq(MSR_EFER, efer); - if (efer & EFER_SVME) - return -EBUSY; + r =3D x86_virt_get_cpu(X86_FEATURE_SVM); + if (r) + return r; =20 sd =3D per_cpu_ptr(&svm_data, me); sd->asid_generation =3D 1; @@ -527,8 +510,6 @@ static int svm_enable_virtualization_cpu(void) sd->next_asid =3D sd->max_asid + 1; sd->min_asid =3D max_sev_asid + 1; =20 - wrmsrq(MSR_EFER, efer | EFER_SVME); - wrmsrq(MSR_VM_HSAVE_PA, sd->save_area_pa); =20 if (static_cpu_has(X86_FEATURE_TSCRATEMSR)) { @@ -539,7 +520,6 @@ static int svm_enable_virtualization_cpu(void) __svm_write_tsc_multiplier(SVM_TSC_RATIO_DEFAULT); } =20 - /* * Get OSVW bits. * diff --git a/arch/x86/kvm/svm/vmenter.S b/arch/x86/kvm/svm/vmenter.S index 3392bcadfb89..d47c5c93c991 100644 --- a/arch/x86/kvm/svm/vmenter.S +++ b/arch/x86/kvm/svm/vmenter.S @@ -298,16 +298,16 @@ SYM_FUNC_START(__svm_vcpu_run) RESTORE_GUEST_SPEC_CTRL_BODY RESTORE_HOST_SPEC_CTRL_BODY (%_ASM_SP) =20 -10: cmpb $0, _ASM_RIP(kvm_rebooting) +10: cmpb $0, _ASM_RIP(virt_rebooting) jne 2b ud2 -30: cmpb $0, _ASM_RIP(kvm_rebooting) +30: cmpb $0, _ASM_RIP(virt_rebooting) jne 4b ud2 -50: cmpb $0, _ASM_RIP(kvm_rebooting) +50: cmpb $0, _ASM_RIP(virt_rebooting) jne 6b ud2 -70: cmpb $0, _ASM_RIP(kvm_rebooting) +70: cmpb $0, _ASM_RIP(virt_rebooting) jne 8b ud2 =20 @@ -394,7 +394,7 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) RESTORE_GUEST_SPEC_CTRL_BODY RESTORE_HOST_SPEC_CTRL_BODY %sil =20 -3: cmpb $0, kvm_rebooting(%rip) +3: cmpb $0, virt_rebooting(%rip) jne 2b ud2 =20 diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 2d7a4d52ccfb..21e67a47ad4e 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "capabilities.h" #include "mmu.h" #include "x86_ops.h" @@ -1994,7 +1995,7 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t= fastpath) * TDX_SEAMCALL_VMFAILINVALID. */ if (unlikely((vp_enter_ret & TDX_SW_ERROR) =3D=3D TDX_SW_ERROR)) { - KVM_BUG_ON(!kvm_rebooting, vcpu->kvm); + KVM_BUG_ON(!virt_rebooting, vcpu->kvm); goto unhandled_exit; } =20 diff --git a/arch/x86/kvm/vmx/vmcs.h b/arch/x86/kvm/vmx/vmcs.h index b25625314658..2ab6ade006c7 100644 --- a/arch/x86/kvm/vmx/vmcs.h +++ b/arch/x86/kvm/vmx/vmcs.h @@ -13,17 +13,6 @@ =20 #define ROL16(val, n) ((u16)(((u16)(val) << (n)) | ((u16)(val) >> (16 - (n= ))))) =20 -struct vmcs_hdr { - u32 revision_id:31; - u32 shadow_vmcs:1; -}; - -struct vmcs { - struct vmcs_hdr hdr; - u32 abort; - char data[]; -}; - DECLARE_PER_CPU(struct vmcs *, current_vmcs); =20 /* diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S index 4426d34811fc..8a481dae9cae 100644 --- a/arch/x86/kvm/vmx/vmenter.S +++ b/arch/x86/kvm/vmx/vmenter.S @@ -310,7 +310,7 @@ SYM_INNER_LABEL_ALIGN(vmx_vmexit, SYM_L_GLOBAL) RET =20 .Lfixup: - cmpb $0, _ASM_RIP(kvm_rebooting) + cmpb $0, _ASM_RIP(virt_rebooting) jne .Lvmfail ud2 .Lvmfail: diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 4cbe8c84b636..dd13bae22a1e 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -48,6 +48,7 @@ #include #include #include +#include #include =20 #include @@ -577,7 +578,6 @@ noinline void invept_error(unsigned long ext, u64 eptp) vmx_insn_failed("invept failed: ext=3D0x%lx eptp=3D%llx\n", ext, eptp); } =20 -static DEFINE_PER_CPU(struct vmcs *, vmxarea); DEFINE_PER_CPU(struct vmcs *, current_vmcs); /* * We maintain a per-CPU linked-list of VMCS loaded on that CPU. This is n= eeded @@ -784,53 +784,17 @@ static int vmx_set_guest_uret_msr(struct vcpu_vmx *vm= x, return ret; } =20 -/* - * Disable VMX and clear CR4.VMXE (even if VMXOFF faults) - * - * Note, VMXOFF causes a #UD if the CPU is !post-VMXON, but it's impossibl= e to - * atomically track post-VMXON state, e.g. this may be called in NMI conte= xt. - * Eat all faults as all other faults on VMXOFF faults are mode related, i= .e. - * faults are guaranteed to be due to the !post-VMXON check unless the CPU= is - * magically in RM, VM86, compat mode, or at CPL>0. - */ -static int kvm_cpu_vmxoff(void) -{ - asm goto("1: vmxoff\n\t" - _ASM_EXTABLE(1b, %l[fault]) - ::: "cc", "memory" : fault); - - cr4_clear_bits(X86_CR4_VMXE); - return 0; - -fault: - cr4_clear_bits(X86_CR4_VMXE); - return -EIO; -} - void vmx_emergency_disable_virtualization_cpu(void) { int cpu =3D raw_smp_processor_id(); struct loaded_vmcs *v; =20 - kvm_rebooting =3D true; - - /* - * Note, CR4.VMXE can be _cleared_ in NMI context, but it can only be - * set in task context. If this races with VMX is disabled by an NMI, - * VMCLEAR and VMXOFF may #UD, but KVM will eat those faults due to - * kvm_rebooting set. - */ - if (!(__read_cr4() & X86_CR4_VMXE)) - return; - list_for_each_entry(v, &per_cpu(loaded_vmcss_on_cpu, cpu), loaded_vmcss_on_cpu_link) { vmcs_clear(v->vmcs); if (v->shadow_vmcs) vmcs_clear(v->shadow_vmcs); } - - kvm_cpu_vmxoff(); } =20 static void __loaded_vmcs_clear(void *arg) @@ -2928,34 +2892,9 @@ int vmx_check_processor_compat(void) return 0; } =20 -static int kvm_cpu_vmxon(u64 vmxon_pointer) -{ - u64 msr; - - cr4_set_bits(X86_CR4_VMXE); - - asm goto("1: vmxon %[vmxon_pointer]\n\t" - _ASM_EXTABLE(1b, %l[fault]) - : : [vmxon_pointer] "m"(vmxon_pointer) - : : fault); - return 0; - -fault: - WARN_ONCE(1, "VMXON faulted, MSR_IA32_FEAT_CTL (0x3a) =3D 0x%llx\n", - rdmsrq_safe(MSR_IA32_FEAT_CTL, &msr) ? 0xdeadbeef : msr); - cr4_clear_bits(X86_CR4_VMXE); - - return -EFAULT; -} - int vmx_enable_virtualization_cpu(void) { int cpu =3D raw_smp_processor_id(); - u64 phys_addr =3D __pa(per_cpu(vmxarea, cpu)); - int r; - - if (cr4_read_shadow() & X86_CR4_VMXE) - return -EBUSY; =20 /* * This can happen if we hot-added a CPU but failed to allocate @@ -2964,15 +2903,7 @@ int vmx_enable_virtualization_cpu(void) if (kvm_is_using_evmcs() && !hv_get_vp_assist_page(cpu)) return -EFAULT; =20 - intel_pt_handle_vmx(1); - - r =3D kvm_cpu_vmxon(phys_addr); - if (r) { - intel_pt_handle_vmx(0); - return r; - } - - return 0; + return x86_virt_get_cpu(X86_FEATURE_VMX); } =20 static void vmclear_local_loaded_vmcss(void) @@ -2989,12 +2920,9 @@ void vmx_disable_virtualization_cpu(void) { vmclear_local_loaded_vmcss(); =20 - if (kvm_cpu_vmxoff()) - kvm_spurious_fault(); + x86_virt_put_cpu(X86_FEATURE_VMX); =20 hv_reset_evmcs(); - - intel_pt_handle_vmx(0); } =20 struct vmcs *alloc_vmcs_cpu(bool shadow, int cpu, gfp_t flags) @@ -3072,47 +3000,6 @@ int alloc_loaded_vmcs(struct loaded_vmcs *loaded_vmc= s) return -ENOMEM; } =20 -static void free_kvm_area(void) -{ - int cpu; - - for_each_possible_cpu(cpu) { - free_vmcs(per_cpu(vmxarea, cpu)); - per_cpu(vmxarea, cpu) =3D NULL; - } -} - -static __init int alloc_kvm_area(void) -{ - int cpu; - - for_each_possible_cpu(cpu) { - struct vmcs *vmcs; - - vmcs =3D alloc_vmcs_cpu(false, cpu, GFP_KERNEL); - if (!vmcs) { - free_kvm_area(); - return -ENOMEM; - } - - /* - * When eVMCS is enabled, alloc_vmcs_cpu() sets - * vmcs->revision_id to KVM_EVMCS_VERSION instead of - * revision_id reported by MSR_IA32_VMX_BASIC. - * - * However, even though not explicitly documented by - * TLFS, VMXArea passed as VMXON argument should - * still be marked with revision_id reported by - * physical CPU. - */ - if (kvm_is_using_evmcs()) - vmcs->hdr.revision_id =3D vmx_basic_vmcs_revision_id(vmcs_config.basic); - - per_cpu(vmxarea, cpu) =3D vmcs; - } - return 0; -} - static void fix_pmode_seg(struct kvm_vcpu *vcpu, int seg, struct kvm_segment *save) { @@ -8380,8 +8267,6 @@ void vmx_hardware_unsetup(void) =20 if (nested) nested_vmx_hardware_unsetup(); - - free_kvm_area(); } =20 void vmx_vm_destroy(struct kvm *kvm) @@ -8686,10 +8571,6 @@ __init int vmx_hardware_setup(void) return r; } =20 - r =3D alloc_kvm_area(); - if (r && nested) - nested_vmx_hardware_unsetup(); - kvm_set_posted_intr_wakeup_handler(pi_wakeup_handler); =20 /* @@ -8715,7 +8596,7 @@ __init int vmx_hardware_setup(void) =20 kvm_caps.inapplicable_quirks &=3D ~KVM_X86_QUIRK_IGNORE_GUEST_PAT; =20 - return r; + return 0; } =20 void vmx_exit(void) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 80cb882f19e2..f650f79d3d5a 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -83,6 +83,8 @@ #include #include #include +#include + #include =20 #define CREATE_TRACE_POINTS @@ -694,9 +696,6 @@ static void drop_user_return_notifiers(void) kvm_on_user_return(&msrs->urn); } =20 -__visible bool kvm_rebooting; -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_rebooting); - /* * Handle a fault on a hardware virtualization (VMX or SVM) instruction. * @@ -707,7 +706,7 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_rebooting); noinstr void kvm_spurious_fault(void) { /* Fault while not rebooting. We want the trace. */ - BUG_ON(!kvm_rebooting); + BUG_ON(!virt_rebooting); } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_spurious_fault); =20 @@ -12999,12 +12998,12 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_vcpu_deliver_s= ipi_vector); =20 void kvm_arch_enable_virtualization(void) { - cpu_emergency_register_virt_callback(kvm_x86_ops.emergency_disable_virtua= lization_cpu); + x86_virt_register_emergency_callback(kvm_x86_ops.emergency_disable_virtua= lization_cpu); } =20 void kvm_arch_disable_virtualization(void) { - cpu_emergency_unregister_virt_callback(kvm_x86_ops.emergency_disable_virt= ualization_cpu); + x86_virt_unregister_emergency_callback(kvm_x86_ops.emergency_disable_virt= ualization_cpu); } =20 int kvm_arch_enable_virtualization_cpu(void) @@ -13106,11 +13105,11 @@ int kvm_arch_enable_virtualization_cpu(void) void kvm_arch_shutdown(void) { /* - * Set kvm_rebooting to indicate that KVM has asynchronously disabled + * Set virt_rebooting to indicate that KVM has asynchronously disabled * hardware virtualization, i.e. that relevant errors and exceptions * aren't entirely unexpected. */ - kvm_rebooting =3D true; + virt_rebooting =3D true; } =20 void kvm_arch_disable_virtualization_cpu(void) @@ -13127,7 +13126,7 @@ void kvm_arch_disable_virtualization_cpu(void) * disable virtualization arrives. Handle the extreme edge case here * instead of trying to account for it in the normal flows. */ - if (in_task() || WARN_ON_ONCE(!kvm_rebooting)) + if (in_task() || WARN_ON_ONCE(!virt_rebooting)) drop_user_return_notifiers(); else __module_get(THIS_MODULE); diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 40993348a967..fdab0ad49098 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -54,7 +54,6 @@ struct kvm_host_values { u64 arch_capabilities; }; =20 -extern bool kvm_rebooting; void kvm_spurious_fault(void); =20 #define SIZE_OF_MEMSLOTS_HASHTABLE \ diff --git a/arch/x86/virt/Makefile b/arch/x86/virt/Makefile index ea343fc392dc..6e485751650c 100644 --- a/arch/x86/virt/Makefile +++ b/arch/x86/virt/Makefile @@ -1,2 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y +=3D svm/ vmx/ + +obj-$(subst m,y,$(CONFIG_KVM_X86)) +=3D hw.o \ No newline at end of file diff --git a/arch/x86/virt/hw.c b/arch/x86/virt/hw.c new file mode 100644 index 000000000000..986e780cf438 --- /dev/null +++ b/arch/x86/virt/hw.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static int x86_virt_feature __ro_after_init; + +__visible bool virt_rebooting; +EXPORT_SYMBOL_GPL(virt_rebooting); + +static DEFINE_PER_CPU(int, virtualization_nr_users); + +static cpu_emergency_virt_cb __rcu *kvm_emergency_callback; + +void x86_virt_register_emergency_callback(cpu_emergency_virt_cb *callback) +{ + if (WARN_ON_ONCE(rcu_access_pointer(kvm_emergency_callback))) + return; + + rcu_assign_pointer(kvm_emergency_callback, callback); +} +EXPORT_SYMBOL_FOR_MODULES(x86_virt_register_emergency_callback, "kvm"); + +void x86_virt_unregister_emergency_callback(cpu_emergency_virt_cb *callbac= k) +{ + if (WARN_ON_ONCE(rcu_access_pointer(kvm_emergency_callback) !=3D callback= )) + return; + + rcu_assign_pointer(kvm_emergency_callback, NULL); + synchronize_rcu(); +} +EXPORT_SYMBOL_FOR_MODULES(x86_virt_unregister_emergency_callback, "kvm"); + +static void x86_virt_invoke_kvm_emergency_callback(void) +{ + cpu_emergency_virt_cb *kvm_callback; + + kvm_callback =3D rcu_dereference(kvm_emergency_callback); + if (kvm_callback) + kvm_callback(); +} + +#if IS_ENABLED(CONFIG_KVM_INTEL) +static DEFINE_PER_CPU(struct vmcs *, root_vmcs); + +static int x86_virt_cpu_vmxon(void) +{ + u64 vmxon_pointer =3D __pa(per_cpu(root_vmcs, raw_smp_processor_id())); + u64 msr; + + cr4_set_bits(X86_CR4_VMXE); + + asm goto("1: vmxon %[vmxon_pointer]\n\t" + _ASM_EXTABLE(1b, %l[fault]) + : : [vmxon_pointer] "m"(vmxon_pointer) + : : fault); + return 0; + +fault: + WARN_ONCE(1, "VMXON faulted, MSR_IA32_FEAT_CTL (0x3a) =3D 0x%llx\n", + rdmsrq_safe(MSR_IA32_FEAT_CTL, &msr) ? 0xdeadbeef : msr); + cr4_clear_bits(X86_CR4_VMXE); + + return -EFAULT; +} + +static int x86_vmx_get_cpu(void) +{ + int r; + + if (cr4_read_shadow() & X86_CR4_VMXE) + return -EBUSY; + + intel_pt_handle_vmx(1); + + r =3D x86_virt_cpu_vmxon(); + if (r) { + intel_pt_handle_vmx(0); + return r; + } + + return 0; +} + +/* + * Disable VMX and clear CR4.VMXE (even if VMXOFF faults) + * + * Note, VMXOFF causes a #UD if the CPU is !post-VMXON, but it's impossibl= e to + * atomically track post-VMXON state, e.g. this may be called in NMI conte= xt. + * Eat all faults as all other faults on VMXOFF faults are mode related, i= .e. + * faults are guaranteed to be due to the !post-VMXON check unless the CPU= is + * magically in RM, VM86, compat mode, or at CPL>0. + */ +static int x86_vmx_put_cpu(void) +{ + int r =3D -EIO; + + asm goto("1: vmxoff\n\t" + _ASM_EXTABLE(1b, %l[fault]) + ::: "cc", "memory" : fault); + r =3D 0; + +fault: + cr4_clear_bits(X86_CR4_VMXE); + intel_pt_handle_vmx(0); + return r; +} + +static int x86_vmx_emergency_disable_virtualization_cpu(void) +{ + virt_rebooting =3D true; + + /* + * Note, CR4.VMXE can be _cleared_ in NMI context, but it can only be + * set in task context. If this races with VMX being disabled via NMI, + * VMCLEAR and VMXOFF may #UD, but the kernel will eat those faults due + * to virt_rebooting being set. + */ + if (!(__read_cr4() & X86_CR4_VMXE)) + return -ENOENT; + + x86_virt_invoke_kvm_emergency_callback(); + + x86_vmx_put_cpu(); + return 0; +} + +static __init void x86_vmx_exit(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + free_page((unsigned long)per_cpu(root_vmcs, cpu)); + per_cpu(root_vmcs, cpu) =3D NULL; + } +} + +static __init int x86_vmx_init(void) +{ + u64 basic_msr; + u32 rev_id; + int cpu; + + if (!cpu_feature_enabled(X86_FEATURE_VMX)) + return -EOPNOTSUPP; + + rdmsrq(MSR_IA32_VMX_BASIC, basic_msr); + + /* IA-32 SDM Vol 3B: VMCS size is never greater than 4kB. */ + if (WARN_ON_ONCE(vmx_basic_vmcs_size(basic_msr) > PAGE_SIZE)) + return -EIO; + + /* + * Even if eVMCS is enabled (or will be enabled?), and even though not + * explicitly documented by TLFS, the root VMCS passed to VMXON should + * still be marked with the revision_id reported by the physical CPU. + */ + rev_id =3D vmx_basic_vmcs_revision_id(basic_msr); + + for_each_possible_cpu(cpu) { + int node =3D cpu_to_node(cpu); + struct page *page; + struct vmcs *vmcs; + + page =3D __alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO , 0); + if (!page) { + x86_vmx_exit(); + return -ENOMEM; + } + + vmcs =3D page_address(page); + vmcs->hdr.revision_id =3D rev_id; + per_cpu(root_vmcs, cpu) =3D vmcs; + } + + return 0; +} +#else +static int x86_vmx_get_cpu(void) { BUILD_BUG_ON(1); return -EOPNOTSUPP; } +static int x86_vmx_put_cpu(void) { BUILD_BUG_ON(1); return -EOPNOTSUPP; } +static int x86_vmx_emergency_disable_virtualization_cpu(void) { BUILD_BUG_= ON(1); return -EOPNOTSUPP; } +static __init int x86_vmx_init(void) { return -EOPNOTSUPP; } +#endif + +#if IS_ENABLED(CONFIG_KVM_AMD) +static int x86_svm_get_cpu(void) +{ + u64 efer; + + rdmsrq(MSR_EFER, efer); + if (efer & EFER_SVME) + return -EBUSY; + + wrmsrq(MSR_EFER, efer | EFER_SVME); + return 0; +} + +static int x86_svm_put_cpu(void) +{ + int r =3D -EIO; + u64 efer; + + /* + * Force GIF=3D1 prior to disabling SVM, e.g. to ensure INIT and + * NMI aren't blocked. + */ + asm goto("1: stgi\n\t" + _ASM_EXTABLE(1b, %l[fault]) + ::: "memory" : fault); + r =3D 0; + +fault: + rdmsrq(MSR_EFER, efer); + wrmsrq(MSR_EFER, efer & ~EFER_SVME); + return r; +} + +static int x86_svm_emergency_disable_virtualization_cpu(void) +{ + u64 efer; + + virt_rebooting =3D true; + + rdmsrq(MSR_EFER, efer); + if (!(efer & EFER_SVME)) + return -ENOENT; + + x86_virt_invoke_kvm_emergency_callback(); + + return x86_svm_put_cpu(); +} + +static __init int x86_svm_init(void) +{ + if (!cpu_feature_enabled(X86_FEATURE_SVM)) + return -EOPNOTSUPP; + + return 0; +} + +#else +static int x86_svm_get_cpu(void) { BUILD_BUG_ON(1); return -EOPNOTSUPP; } +static int x86_svm_put_cpu(void) { BUILD_BUG_ON(1); return -EOPNOTSUPP; } +static int x86_svm_emergency_disable_virtualization_cpu(void) { BUILD_BUG_= ON(1); return -EOPNOTSUPP; } +static __init int x86_svm_init(void) { return -EOPNOTSUPP; } +#endif + +#define x86_virt_call(fn) \ +({ \ + int __r; \ + \ + if (IS_ENABLED(CONFIG_KVM_INTEL) && \ + cpu_feature_enabled(X86_FEATURE_VMX)) \ + __r =3D x86_vmx_##fn(); \ + else if (IS_ENABLED(CONFIG_KVM_AMD) && \ + cpu_feature_enabled(X86_FEATURE_SVM)) \ + __r =3D x86_svm_##fn(); \ + else \ + __r =3D -EOPNOTSUPP; \ + \ + __r; \ +}) + +int x86_virt_get_cpu(int feat) +{ + int r; + + if (!x86_virt_feature || x86_virt_feature !=3D feat) + return -EOPNOTSUPP; + + if (this_cpu_inc_return(virtualization_nr_users) > 1) + return 0; + + r =3D x86_virt_call(get_cpu); + if (r) + WARN_ON_ONCE(this_cpu_dec_return(virtualization_nr_users)); + + return r; +} +EXPORT_SYMBOL_GPL(x86_virt_get_cpu); + +void x86_virt_put_cpu(int feat) +{ + if (WARN_ON_ONCE(!this_cpu_read(virtualization_nr_users)) || + this_cpu_dec_return(virtualization_nr_users)) + return; + + BUG_ON(x86_virt_call(put_cpu) && !virt_rebooting); +} +EXPORT_SYMBOL_GPL(x86_virt_put_cpu); + +/* + * Disable virtualization, i.e. VMX or SVM, to ensure INIT is recognized d= uring + * reboot. VMX blocks INIT if the CPU is post-VMXON, and SVM blocks INIT = if + * GIF=3D0, i.e. if the crash occurred between CLGI and STGI. + */ +int x86_virt_emergency_disable_virtualization_cpu(void) +{ + if (!x86_virt_feature) + return -EOPNOTSUPP; + + /* + * IRQs must be disabled as virtualization is enabled in hardware via + * function call IPIs, i.e. IRQs need to be disabled to guarantee + * virtualization stays disabled. + */ + lockdep_assert_irqs_disabled(); + + /* + * Do the NMI shootdown even if virtualization is off on _this_ CPU, as + * other CPUs may have virtualization enabled. + * + * TODO: Track whether or not virtualization might be enabled on other + * CPUs? May not be worth avoiding the NMI shootdown... + */ + (void)x86_virt_call(emergency_disable_virtualization_cpu); + return 0; +} + +void __init x86_virt_init(void) +{ + bool is_vmx =3D !x86_vmx_init(); + bool is_svm =3D !x86_svm_init(); + + if (!is_vmx && !is_svm) + return; + + if (WARN_ON_ONCE(is_vmx && is_svm)) + return; + + x86_virt_feature =3D is_vmx ? X86_FEATURE_VMX : X86_FEATURE_SVM; +} --=20 2.52.0.223.gf5cc29aaa4-goog From nobody Fri Dec 19 12:14:58 2025 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1F4F5223710 for ; Sat, 6 Dec 2025 01:11:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983475; cv=none; b=Cq6xugCvg1OccbxEqSExz3c7c7uHF+zmt7Waxaoh5tjx2T4d2iPKCa/CQkvc6s/4eUXbrbRapaNLqpMSYEs7Z7+fuFwAuAG9VYBnAvlnvYy85TysLsHwcJsV0242WqxUa5Jibepj2jKYSqFU7P+Reu5g7k41OkHqgItzOZoYCjA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983475; c=relaxed/simple; bh=ZATKAQEg2XJr3LA90MeQutsxY8XZnMON4i4UR0lWMsY=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=W2tsxCvnGQ3E/yqGCMaCbNGGFcjl/OylFMohOvcepWUpKYJh2sfjKH/izXWBB3aGBZtaEASmM3oQ5IYKm1D5LA17jpgQ8HgJumrc4TKPnljsTD3e9fTWOlgodrBJKKRCVv+i6VVao0wBL6Oo8ehBAvJCCo/xiYg9VPga8LEZDJM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=mQNecxOs; arc=none smtp.client-ip=209.85.216.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="mQNecxOs" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-349aadb8ab9so161260a91.3 for ; Fri, 05 Dec 2025 17:11:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1764983472; x=1765588272; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:from:to:cc:subject:date:message-id:reply-to; bh=tAMo1PaS4H7jq5j8ypsO83hN6nYqoP5QoxyxWnbmvlI=; b=mQNecxOsrIajRliE2nnpcj7OJ4uoExozdfV9MbzjPYO88A245gmUsEkW52+QdQf7qO nZ+V6mwzulT0bOB7t1FcW6P3DvwRfmi4Yo12kPZfTcxhAPqu3fgBkJ1sGfImdu3VHyZ8 o9XstmWsR9X2p+XSF3gaNTgD5AFtMcIKZd+heVFhOP4mLmir/txeVmQ5mdMSCmW5XrDt tdzFDLhr7YgxyMg8nPRSf9wKWI2ZcL7XIu64k+rvBRnGIq2kG0NxP2aMmwURF49Paa6J AfGv+H3RCd/JCV8iNiMivZGefkPIoxt14eFqO226N42W5i/Z3E7vtkWdfajhvSlYfpyO qIVg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764983472; x=1765588272; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=tAMo1PaS4H7jq5j8ypsO83hN6nYqoP5QoxyxWnbmvlI=; b=f2J7SjbEQX/6BDyOappU1fJcxX7eTMgujNHZGAygYmL/ykkvUxVZtdxec2R2u1WeGt znJYmM+npuNMnXT0l+DTESIspJF4/8Wpcu9+lpoFnCOU50WqzzfUi3HXilltwPQZ2ONl zam+sCabvruUe4vHKHSLsTZudK4yQzfLydJfDN+5FIMQpk+FqzoaSk2/RaqlNg0+YfDj vYD2jM8tRQDGWzyQE35yXtqsNijSmq3J4K7DgNDN55cgxlSsHll/mDCRH9VXBjLaBkf5 Yy1dJqwB3GGu4nHEcmXjLHVcbP2OR+1riOu5aYPCTJXfNhACTHdGVxrvVMtFHHkjopfW m/ig== X-Gm-Message-State: AOJu0YzhXisMSMLFySb9t5esxpo5bfN2AWXibsE2tRT27KzFv9nlalF7 Zda5dRoPMOSNOSQv6T5ktXXRxBf81vdlBhkhjvk6B8PbkAimky3apCCvHzzaUWEco96j+XPxr1m jgErWyQ== X-Google-Smtp-Source: AGHT+IGm/V+n9o76MHvJYdn7e+iWw4CBd4GxYxhgTEzF/9IVcaC2gvinrCs5k9nTOHcKXsymWjL5Jq9uEqw= X-Received: from pjbmz11.prod.google.com ([2002:a17:90b:378b:b0:33b:b692:47b0]) (user=seanjc job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:4ecd:b0:340:a1a8:eb87 with SMTP id 98e67ed59e1d1-349a260d6b8mr828827a91.35.1764983472272; Fri, 05 Dec 2025 17:11:12 -0800 (PST) Reply-To: Sean Christopherson Date: Fri, 5 Dec 2025 17:10:50 -0800 In-Reply-To: <20251206011054.494190-1-seanjc@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251206011054.494190-1-seanjc@google.com> X-Mailer: git-send-email 2.52.0.223.gf5cc29aaa4-goog Message-ID: <20251206011054.494190-4-seanjc@google.com> Subject: [PATCH v2 3/7] KVM: x86/tdx: Do VMXON and TDX-Module initialization during subsys init From: Sean Christopherson To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, Kiryl Shutsemau , Sean Christopherson , Paolo Bonzini Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev, kvm@vger.kernel.org, Chao Gao , Dan Williams Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Now that VMXON can be done without bouncing through KVM, do TDX-Module initialization during subsys init (specifically before module_init() so that it runs before KVM when both are built-in). Aside from the obvious benefits of separating core TDX code from KVM, this will allow tagging a pile of TDX functions and globals as being __init and __ro_after_init. Signed-off-by: Sean Christopherson Reviewed-by: Dan Williams Tested-by: Chao Gao --- Documentation/arch/x86/tdx.rst | 26 ----- arch/x86/include/asm/tdx.h | 4 - arch/x86/kvm/vmx/tdx.c | 169 ++++++-------------------------- arch/x86/virt/vmx/tdx/tdx.c | 170 ++++++++++++++++++--------------- arch/x86/virt/vmx/tdx/tdx.h | 8 -- 5 files changed, 124 insertions(+), 253 deletions(-) diff --git a/Documentation/arch/x86/tdx.rst b/Documentation/arch/x86/tdx.rst index 61670e7df2f7..2e0a15d6f7d1 100644 --- a/Documentation/arch/x86/tdx.rst +++ b/Documentation/arch/x86/tdx.rst @@ -60,32 +60,6 @@ Besides initializing the TDX module, a per-cpu initializ= ation SEAMCALL must be done on one cpu before any other SEAMCALLs can be made on that cpu. =20 -The kernel provides two functions, tdx_enable() and tdx_cpu_enable() to -allow the user of TDX to enable the TDX module and enable TDX on local -cpu respectively. - -Making SEAMCALL requires VMXON has been done on that CPU. Currently only -KVM implements VMXON. For now both tdx_enable() and tdx_cpu_enable() -don't do VMXON internally (not trivial), but depends on the caller to -guarantee that. - -To enable TDX, the caller of TDX should: 1) temporarily disable CPU -hotplug; 2) do VMXON and tdx_enable_cpu() on all online cpus; 3) call -tdx_enable(). For example:: - - cpus_read_lock(); - on_each_cpu(vmxon_and_tdx_cpu_enable()); - ret =3D tdx_enable(); - cpus_read_unlock(); - if (ret) - goto no_tdx; - // TDX is ready to use - -And the caller of TDX must guarantee the tdx_cpu_enable() has been -successfully done on any cpu before it wants to run any other SEAMCALL. -A typical usage is do both VMXON and tdx_cpu_enable() in CPU hotplug -online callback, and refuse to online if tdx_cpu_enable() fails. - User can consult dmesg to see whether the TDX module has been initialized. =20 If the TDX module is initialized successfully, dmesg shows something diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 6b338d7f01b7..a149740b24e8 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -145,8 +145,6 @@ static __always_inline u64 sc_retry(sc_func_t func, u64= fn, #define seamcall(_fn, _args) sc_retry(__seamcall, (_fn), (_args)) #define seamcall_ret(_fn, _args) sc_retry(__seamcall_ret, (_fn), (_args)) #define seamcall_saved_ret(_fn, _args) sc_retry(__seamcall_saved_ret, (_fn= ), (_args)) -int tdx_cpu_enable(void); -int tdx_enable(void); const char *tdx_dump_mce_info(struct mce *m); const struct tdx_sys_info *tdx_get_sysinfo(void); =20 @@ -223,8 +221,6 @@ u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td); u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page); #else static inline void tdx_init(void) { } -static inline int tdx_cpu_enable(void) { return -ENODEV; } -static inline int tdx_enable(void) { return -ENODEV; } static inline u32 tdx_get_nr_guest_keyids(void) { return 0; } static inline const char *tdx_dump_mce_info(struct mce *m) { return NULL; } static inline const struct tdx_sys_info *tdx_get_sysinfo(void) { return NU= LL; } diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 21e67a47ad4e..d0161dc3d184 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -59,7 +59,7 @@ module_param_named(tdx, enable_tdx, bool, 0444); #define TDX_SHARED_BIT_PWL_5 gpa_to_gfn(BIT_ULL(51)) #define TDX_SHARED_BIT_PWL_4 gpa_to_gfn(BIT_ULL(47)) =20 -static enum cpuhp_state tdx_cpuhp_state; +static enum cpuhp_state tdx_cpuhp_state __ro_after_init; =20 static const struct tdx_sys_info *tdx_sysinfo; =20 @@ -3304,17 +3304,7 @@ int tdx_gmem_max_mapping_level(struct kvm *kvm, kvm_= pfn_t pfn, bool is_private) =20 static int tdx_online_cpu(unsigned int cpu) { - unsigned long flags; - int r; - - /* Sanity check CPU is already in post-VMXON */ - WARN_ON_ONCE(!(cr4_read_shadow() & X86_CR4_VMXE)); - - local_irq_save(flags); - r =3D tdx_cpu_enable(); - local_irq_restore(flags); - - return r; + return 0; } =20 static int tdx_offline_cpu(unsigned int cpu) @@ -3353,51 +3343,6 @@ static int tdx_offline_cpu(unsigned int cpu) return -EBUSY; } =20 -static void __do_tdx_cleanup(void) -{ - /* - * Once TDX module is initialized, it cannot be disabled and - * re-initialized again w/o runtime update (which isn't - * supported by kernel). Only need to remove the cpuhp here. - * The TDX host core code tracks TDX status and can handle - * 'multiple enabling' scenario. - */ - WARN_ON_ONCE(!tdx_cpuhp_state); - cpuhp_remove_state_nocalls_cpuslocked(tdx_cpuhp_state); - tdx_cpuhp_state =3D 0; -} - -static void __tdx_cleanup(void) -{ - cpus_read_lock(); - __do_tdx_cleanup(); - cpus_read_unlock(); -} - -static int __init __do_tdx_bringup(void) -{ - int r; - - /* - * TDX-specific cpuhp callback to call tdx_cpu_enable() on all - * online CPUs before calling tdx_enable(), and on any new - * going-online CPU to make sure it is ready for TDX guest. - */ - r =3D cpuhp_setup_state_cpuslocked(CPUHP_AP_ONLINE_DYN, - "kvm/cpu/tdx:online", - tdx_online_cpu, tdx_offline_cpu); - if (r < 0) - return r; - - tdx_cpuhp_state =3D r; - - r =3D tdx_enable(); - if (r) - __do_tdx_cleanup(); - - return r; -} - static int __init __tdx_bringup(void) { const struct tdx_sys_info_td_conf *td_conf; @@ -3417,34 +3362,18 @@ static int __init __tdx_bringup(void) } } =20 - /* - * Enabling TDX requires enabling hardware virtualization first, - * as making SEAMCALLs requires CPU being in post-VMXON state. - */ - r =3D kvm_enable_virtualization(); - if (r) - return r; - - cpus_read_lock(); - r =3D __do_tdx_bringup(); - cpus_read_unlock(); - - if (r) - goto tdx_bringup_err; - - r =3D -EINVAL; /* Get TDX global information for later use */ tdx_sysinfo =3D tdx_get_sysinfo(); - if (WARN_ON_ONCE(!tdx_sysinfo)) - goto get_sysinfo_err; + if (!tdx_sysinfo) + return -EINVAL; =20 /* Check TDX module and KVM capabilities */ if (!tdx_get_supported_attrs(&tdx_sysinfo->td_conf) || !tdx_get_supported_xfam(&tdx_sysinfo->td_conf)) - goto get_sysinfo_err; + return -EINVAL; =20 if (!(tdx_sysinfo->features.tdx_features0 & MD_FIELD_ID_FEATURES0_TOPOLOG= Y_ENUM)) - goto get_sysinfo_err; + return -EINVAL; =20 /* * TDX has its own limit of maximum vCPUs it can support for all @@ -3479,34 +3408,31 @@ static int __init __tdx_bringup(void) if (td_conf->max_vcpus_per_td < num_present_cpus()) { pr_err("Disable TDX: MAX_VCPU_PER_TD (%u) smaller than number of logical= CPUs (%u).\n", td_conf->max_vcpus_per_td, num_present_cpus()); - goto get_sysinfo_err; + return -EINVAL; } =20 if (misc_cg_set_capacity(MISC_CG_RES_TDX, tdx_get_nr_guest_keyids())) - goto get_sysinfo_err; + return -EINVAL; =20 /* - * Leave hardware virtualization enabled after TDX is enabled - * successfully. TDX CPU hotplug depends on this. + * TDX-specific cpuhp callback to disallow offlining the last CPU in a + * packing while KVM is running one or more TDs. Reclaiming HKIDs + * requires doing PAGE.WBINVD on every package, i.e. offlining all CPUs + * of a package would prevent reclaiming the HKID. */ + r =3D cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "kvm/cpu/tdx:online", + tdx_online_cpu, tdx_offline_cpu); + if (r < 0) + goto err_cpuhup; + + tdx_cpuhp_state =3D r; return 0; =20 -get_sysinfo_err: - __tdx_cleanup(); -tdx_bringup_err: - kvm_disable_virtualization(); +err_cpuhup: + misc_cg_set_capacity(MISC_CG_RES_TDX, 0); return r; } =20 -void tdx_cleanup(void) -{ - if (enable_tdx) { - misc_cg_set_capacity(MISC_CG_RES_TDX, 0); - __tdx_cleanup(); - kvm_disable_virtualization(); - } -} - int __init tdx_bringup(void) { int r, i; @@ -3538,56 +3464,14 @@ int __init tdx_bringup(void) goto success_disable_tdx; } =20 - if (!cpu_feature_enabled(X86_FEATURE_MOVDIR64B)) { - pr_err("tdx: MOVDIR64B is required for TDX\n"); - goto success_disable_tdx; - } - - if (!cpu_feature_enabled(X86_FEATURE_SELFSNOOP)) { - pr_err("Self-snoop is required for TDX\n"); - goto success_disable_tdx; - } - if (!cpu_feature_enabled(X86_FEATURE_TDX_HOST_PLATFORM)) { - pr_err("tdx: no TDX private KeyIDs available\n"); + pr_err("TDX not supported by the host platform\n"); goto success_disable_tdx; } =20 - if (!enable_virt_at_load) { - pr_err("tdx: tdx requires kvm.enable_virt_at_load=3D1\n"); - goto success_disable_tdx; - } - - /* - * Ideally KVM should probe whether TDX module has been loaded - * first and then try to bring it up. But TDX needs to use SEAMCALL - * to probe whether the module is loaded (there is no CPUID or MSR - * for that), and making SEAMCALL requires enabling virtualization - * first, just like the rest steps of bringing up TDX module. - * - * So, for simplicity do everything in __tdx_bringup(); the first - * SEAMCALL will return -ENODEV when the module is not loaded. The - * only complication is having to make sure that initialization - * SEAMCALLs don't return TDX_SEAMCALL_VMFAILINVALID in other - * cases. - */ r =3D __tdx_bringup(); - if (r) { - /* - * Disable TDX only but don't fail to load module if the TDX - * module could not be loaded. No need to print message saying - * "module is not loaded" because it was printed when the first - * SEAMCALL failed. Don't bother unwinding the S-EPT hooks or - * vm_size, as kvm_x86_ops have already been finalized (and are - * intentionally not exported). The S-EPT code is unreachable, - * and allocating a few more bytes per VM in a should-be-rare - * failure scenario is a non-issue. - */ - if (r =3D=3D -ENODEV) - goto success_disable_tdx; - + if (r) enable_tdx =3D 0; - } =20 return r; =20 @@ -3596,6 +3480,15 @@ int __init tdx_bringup(void) return 0; } =20 +void tdx_cleanup(void) +{ + if (!enable_tdx) + return; + + misc_cg_set_capacity(MISC_CG_RES_TDX, 0); + cpuhp_remove_state(tdx_cpuhp_state); +} + void __init tdx_hardware_setup(void) { KVM_SANITY_CHECK_VM_STRUCT_SIZE(kvm_tdx); diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index eac403248462..8282c9b1b48b 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include #include #include +#include #include "tdx.h" =20 static u32 tdx_global_keyid __ro_after_init; @@ -50,13 +52,11 @@ static DEFINE_PER_CPU(bool, tdx_lp_initialized); =20 static struct tdmr_info_list tdx_tdmr_list; =20 -static enum tdx_module_status_t tdx_module_status; -static DEFINE_MUTEX(tdx_module_lock); - /* All TDX-usable memory regions. Protected by mem_hotplug_lock. */ static LIST_HEAD(tdx_memlist); =20 static struct tdx_sys_info tdx_sysinfo; +static bool tdx_module_initialized; =20 typedef void (*sc_err_func_t)(u64 fn, u64 err, struct tdx_module_args *arg= s); =20 @@ -141,26 +141,15 @@ static int try_init_module_global(void) } =20 /** - * tdx_cpu_enable - Enable TDX on local cpu - * - * Do one-time TDX module per-cpu initialization SEAMCALL (and TDX module - * global initialization SEAMCALL if not done) on local cpu to make this - * cpu be ready to run any other SEAMCALLs. - * - * Always call this function via IPI function calls. - * - * Return 0 on success, otherwise errors. + * Enable VMXON and then do one-time TDX module per-cpu initialization SEA= MCALL + * (and TDX module global initialization SEAMCALL if not done) on local cp= u to + * make this cpu be ready to run any other SEAMCALLs. */ -int tdx_cpu_enable(void) +static int tdx_cpu_enable(void) { struct tdx_module_args args =3D {}; int ret; =20 - if (!boot_cpu_has(X86_FEATURE_TDX_HOST_PLATFORM)) - return -ENODEV; - - lockdep_assert_irqs_disabled(); - if (__this_cpu_read(tdx_lp_initialized)) return 0; =20 @@ -181,7 +170,56 @@ int tdx_cpu_enable(void) =20 return 0; } -EXPORT_SYMBOL_GPL(tdx_cpu_enable); + +static int tdx_online_cpu(unsigned int cpu) +{ + int ret; + + guard(irqsave)(); + + ret =3D x86_virt_get_cpu(X86_FEATURE_VMX); + if (ret) + return ret; + + ret =3D tdx_cpu_enable(); + if (ret) + x86_virt_put_cpu(X86_FEATURE_VMX); + + return ret; +} + +static int tdx_offline_cpu(unsigned int cpu) +{ + x86_virt_put_cpu(X86_FEATURE_VMX); + return 0; +} + +static void tdx_shutdown_cpu(void *ign) +{ + x86_virt_put_cpu(X86_FEATURE_VMX); +} + +static void tdx_shutdown(void) +{ + on_each_cpu(tdx_shutdown_cpu, NULL, 1); +} + +static int tdx_suspend(void) +{ + x86_virt_put_cpu(X86_FEATURE_VMX); + return 0; +} + +static void tdx_resume(void) +{ + WARN_ON_ONCE(x86_virt_get_cpu(X86_FEATURE_VMX)); +} + +static struct syscore_ops tdx_syscore_ops =3D { + .suspend =3D tdx_suspend, + .resume =3D tdx_resume, + .shutdown =3D tdx_shutdown, +}; =20 /* * Add a memory region as a TDX memory block. The caller must make sure @@ -1156,67 +1194,50 @@ static int init_tdx_module(void) goto out_put_tdxmem; } =20 -static int __tdx_enable(void) +static int tdx_enable(void) { + enum cpuhp_state state; int ret; =20 + if (!cpu_feature_enabled(X86_FEATURE_TDX_HOST_PLATFORM)) { + pr_err("TDX not supported by the host platform\n"); + return -ENODEV; + } + + if (!cpu_feature_enabled(X86_FEATURE_XSAVE)) { + pr_err("XSAVE is required for TDX\n"); + return -EINVAL; + } + + if (!cpu_feature_enabled(X86_FEATURE_MOVDIR64B)) { + pr_err("MOVDIR64B is required for TDX\n"); + return -EINVAL; + } + + if (!cpu_feature_enabled(X86_FEATURE_SELFSNOOP)) { + pr_err("Self-snoop is required for TDX\n"); + return -ENODEV; + } + + state =3D cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "virt/tdx:online", + tdx_online_cpu, tdx_offline_cpu); + if (state < 0) + return state; + ret =3D init_tdx_module(); if (ret) { - pr_err("module initialization failed (%d)\n", ret); - tdx_module_status =3D TDX_MODULE_ERROR; + pr_err("TDX-Module initialization failed (%d)\n", ret); + cpuhp_remove_state(state); return ret; } =20 - pr_info("module initialized\n"); - tdx_module_status =3D TDX_MODULE_INITIALIZED; + register_syscore_ops(&tdx_syscore_ops); =20 + tdx_module_initialized =3D true; + pr_info("TDX-Module initialized\n"); return 0; } - -/** - * tdx_enable - Enable TDX module to make it ready to run TDX guests - * - * This function assumes the caller has: 1) held read lock of CPU hotplug - * lock to prevent any new cpu from becoming online; 2) done both VMXON - * and tdx_cpu_enable() on all online cpus. - * - * This function requires there's at least one online cpu for each CPU - * package to succeed. - * - * This function can be called in parallel by multiple callers. - * - * Return 0 if TDX is enabled successfully, otherwise error. - */ -int tdx_enable(void) -{ - int ret; - - if (!boot_cpu_has(X86_FEATURE_TDX_HOST_PLATFORM)) - return -ENODEV; - - lockdep_assert_cpus_held(); - - mutex_lock(&tdx_module_lock); - - switch (tdx_module_status) { - case TDX_MODULE_UNINITIALIZED: - ret =3D __tdx_enable(); - break; - case TDX_MODULE_INITIALIZED: - /* Already initialized, great, tell the caller. */ - ret =3D 0; - break; - default: - /* Failed to initialize in the previous attempts */ - ret =3D -EINVAL; - break; - } - - mutex_unlock(&tdx_module_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(tdx_enable); +subsys_initcall(tdx_enable); =20 static bool is_pamt_page(unsigned long phys) { @@ -1467,15 +1488,10 @@ void __init tdx_init(void) =20 const struct tdx_sys_info *tdx_get_sysinfo(void) { - const struct tdx_sys_info *p =3D NULL; + if (!tdx_module_initialized) + return NULL; =20 - /* Make sure all fields in @tdx_sysinfo have been populated */ - mutex_lock(&tdx_module_lock); - if (tdx_module_status =3D=3D TDX_MODULE_INITIALIZED) - p =3D (const struct tdx_sys_info *)&tdx_sysinfo; - mutex_unlock(&tdx_module_lock); - - return p; + return (const struct tdx_sys_info *)&tdx_sysinfo; } EXPORT_SYMBOL_GPL(tdx_get_sysinfo); =20 diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h index 82bb82be8567..dde219c823b4 100644 --- a/arch/x86/virt/vmx/tdx/tdx.h +++ b/arch/x86/virt/vmx/tdx/tdx.h @@ -91,14 +91,6 @@ struct tdmr_info { * Do not put any hardware-defined TDX structure representations below * this comment! */ - -/* Kernel defined TDX module status during module initialization. */ -enum tdx_module_status_t { - TDX_MODULE_UNINITIALIZED, - TDX_MODULE_INITIALIZED, - TDX_MODULE_ERROR -}; - struct tdx_memblock { struct list_head list; unsigned long start_pfn; --=20 2.52.0.223.gf5cc29aaa4-goog From nobody Fri Dec 19 12:14:58 2025 Received: from mail-pl1-f202.google.com (mail-pl1-f202.google.com [209.85.214.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA0BC25743D for ; Sat, 6 Dec 2025 01:11:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983477; cv=none; b=LALH+aFahiNI4xdpRiyCV7TESn7V/tX0UdQcB/4QZsnadO93ZJnqi2vLJ4OEAgBRCLHWJoasnCL6HUO22WdxTgCbVCpzKj8QLoj7ehSQbjzXVlRBgu9XsPbBEuHrJN43SFAoFQFItpLC7JU2g/KtfxxfWzIUtCxBDkahDq4/JmI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983477; c=relaxed/simple; bh=l7luhA58Ogc/VIls8G8zjcbt0uhbWWok9pxvmMLiVs0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=YGwBN4KNyWLojaCneDmKceQ3yH4Ab199TTxpSwJg50JTsE4uSQun10ArtfEeEQkLAB3kIEgRDQwSUkakcSScH4uqRmoX6vEhUhXE8O4IMiQIhj8qB8T8+DWjozJ8tthK5A4OHI6NApJSlpaXCtpHWlptO5afR/6sQ1u2jyxbhf0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Xw8n6Z49; arc=none smtp.client-ip=209.85.214.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Xw8n6Z49" Received: by mail-pl1-f202.google.com with SMTP id d9443c01a7336-29809acd049so61063545ad.3 for ; Fri, 05 Dec 2025 17:11:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1764983474; x=1765588274; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:from:to:cc:subject:date:message-id:reply-to; bh=9IgG/EwS0rhU6/zESmY7/VVF5fzyu73j5FogJ2Eynxk=; b=Xw8n6Z49pq4+TZuEBFTIDxxeVIp0tYF/lmn7gcgr9I2zvYMfyoXj7hfO9LChYSfan5 DGk74N7chVuIAWL2pdhNHFj/Ky9ogvYZX03HRP96HPsq/xkP3xwiLto2mKHgH4JCFamn cjQufMEptFk28XSxrnispW85f613PSBtqQanDGvql8UCX72VTAVBD7/yg4ppuOsm3Oqp BdwNexO+2O3h7BQVRepvVI7ThRWxl4oKRbYGSMjwF5XcGFmxKPqFiJck3+zX4YFl0qBh wFTdaOoXwz4pVBFrlwPSNhdVUywe0zhnYkP3S7cEqEPHCluWYGhL0pO2QFXOV3ftU3AN 1PYw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764983474; x=1765588274; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=9IgG/EwS0rhU6/zESmY7/VVF5fzyu73j5FogJ2Eynxk=; b=hPEjBeb3rKvRBEE4d+foL88j11KtCnor7b2jRTrgWkV2gmv1/iRgXXo8wyA4bchi4S ONuxmYjDk7V4XIuneAl/FydssPic4zMPy9XoXWFDKt02ZUbPieyyHxHjtV7maX7yON3R Xsn+0R3KXUXH89xBEK3upYfuEgxjc4z8GK99SDBUCmxN8b3WrJKx7/eLYn+XepiRJpl8 wUBi7rCvrBjiOGPuGHoYklp9gpY4KDgiGGBv3PbGLH1AfE6HRtfVXv9QNuTZgB2uhYWQ WOvVED/GFU/Nz5MRUhz6dRFlXs4EoTRqu2VfiFJAC4yX7J5CAGttBapfA6aMcv93WXSw DSIA== X-Gm-Message-State: AOJu0Yyh5OdnFx2kL+3/mkfistYatK2sum7Qzd4s+sPF8xZca+PfW09P aepK1GNOhfwV3O9sN3fN41MKGBWBAzKnZY/KyYobTtVXe5CXBSGttL0TXbQMFgi13Z1dgahpbFN HEG04Vg== X-Google-Smtp-Source: AGHT+IGRS9sQuIP9/GMS0SenwgYnysTWUy3gYbpCBzzZG+YUTgplWaBrw+naN0TdcM0x09NFhWX66ud1UBI= X-Received: from plse4.prod.google.com ([2002:a17:902:b784:b0:298:3a03:dc2e]) (user=seanjc job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:240e:b0:298:5599:3ab0 with SMTP id d9443c01a7336-29df59a867bmr7455415ad.16.1764983474152; Fri, 05 Dec 2025 17:11:14 -0800 (PST) Reply-To: Sean Christopherson Date: Fri, 5 Dec 2025 17:10:51 -0800 In-Reply-To: <20251206011054.494190-1-seanjc@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251206011054.494190-1-seanjc@google.com> X-Mailer: git-send-email 2.52.0.223.gf5cc29aaa4-goog Message-ID: <20251206011054.494190-5-seanjc@google.com> Subject: [PATCH v2 4/7] x86/virt/tdx: Tag a pile of functions as __init, and globals as __ro_after_init From: Sean Christopherson To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, Kiryl Shutsemau , Sean Christopherson , Paolo Bonzini Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev, kvm@vger.kernel.org, Chao Gao , Dan Williams Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Now that TDX-Module initialization is done during subsys init, tag all related functions as __init, and relevant data as __ro_after_init. Signed-off-by: Sean Christopherson Reviewed-by: Chao Gao Reviewed-by: Dan Williams Tested-by: Chao Gao --- arch/x86/virt/vmx/tdx/tdx.c | 115 ++++++++++---------- arch/x86/virt/vmx/tdx/tdx_global_metadata.c | 10 +- 2 files changed, 64 insertions(+), 61 deletions(-) diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index 8282c9b1b48b..d49645797fe4 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -55,8 +55,8 @@ static struct tdmr_info_list tdx_tdmr_list; /* All TDX-usable memory regions. Protected by mem_hotplug_lock. */ static LIST_HEAD(tdx_memlist); =20 -static struct tdx_sys_info tdx_sysinfo; -static bool tdx_module_initialized; +static struct tdx_sys_info tdx_sysinfo __ro_after_init; +static bool tdx_module_initialized __ro_after_init; =20 typedef void (*sc_err_func_t)(u64 fn, u64 err, struct tdx_module_args *arg= s); =20 @@ -226,8 +226,9 @@ static struct syscore_ops tdx_syscore_ops =3D { * all memory regions are added in address ascending order and don't * overlap. */ -static int add_tdx_memblock(struct list_head *tmb_list, unsigned long star= t_pfn, - unsigned long end_pfn, int nid) +static __init int add_tdx_memblock(struct list_head *tmb_list, + unsigned long start_pfn, + unsigned long end_pfn, int nid) { struct tdx_memblock *tmb; =20 @@ -245,7 +246,7 @@ static int add_tdx_memblock(struct list_head *tmb_list,= unsigned long start_pfn, return 0; } =20 -static void free_tdx_memlist(struct list_head *tmb_list) +static __init void free_tdx_memlist(struct list_head *tmb_list) { /* @tmb_list is protected by mem_hotplug_lock */ while (!list_empty(tmb_list)) { @@ -263,7 +264,7 @@ static void free_tdx_memlist(struct list_head *tmb_list) * ranges off in a secondary structure because memblock is modified * in memory hotplug while TDX memory regions are fixed. */ -static int build_tdx_memlist(struct list_head *tmb_list) +static __init int build_tdx_memlist(struct list_head *tmb_list) { unsigned long start_pfn, end_pfn; int i, nid, ret; @@ -295,7 +296,7 @@ static int build_tdx_memlist(struct list_head *tmb_list) return ret; } =20 -static int read_sys_metadata_field(u64 field_id, u64 *data) +static __init int read_sys_metadata_field(u64 field_id, u64 *data) { struct tdx_module_args args =3D {}; int ret; @@ -317,7 +318,7 @@ static int read_sys_metadata_field(u64 field_id, u64 *d= ata) =20 #include "tdx_global_metadata.c" =20 -static int check_features(struct tdx_sys_info *sysinfo) +static __init int check_features(struct tdx_sys_info *sysinfo) { u64 tdx_features0 =3D sysinfo->features.tdx_features0; =20 @@ -330,7 +331,7 @@ static int check_features(struct tdx_sys_info *sysinfo) } =20 /* Calculate the actual TDMR size */ -static int tdmr_size_single(u16 max_reserved_per_tdmr) +static __init int tdmr_size_single(u16 max_reserved_per_tdmr) { int tdmr_sz; =20 @@ -344,8 +345,8 @@ static int tdmr_size_single(u16 max_reserved_per_tdmr) return ALIGN(tdmr_sz, TDMR_INFO_ALIGNMENT); } =20 -static int alloc_tdmr_list(struct tdmr_info_list *tdmr_list, - struct tdx_sys_info_tdmr *sysinfo_tdmr) +static __init int alloc_tdmr_list(struct tdmr_info_list *tdmr_list, + struct tdx_sys_info_tdmr *sysinfo_tdmr) { size_t tdmr_sz, tdmr_array_sz; void *tdmr_array; @@ -376,7 +377,7 @@ static int alloc_tdmr_list(struct tdmr_info_list *tdmr_= list, return 0; } =20 -static void free_tdmr_list(struct tdmr_info_list *tdmr_list) +static __init void free_tdmr_list(struct tdmr_info_list *tdmr_list) { free_pages_exact(tdmr_list->tdmrs, tdmr_list->max_tdmrs * tdmr_list->tdmr_sz); @@ -405,8 +406,8 @@ static inline u64 tdmr_end(struct tdmr_info *tdmr) * preallocated @tdmr_list, following all the special alignment * and size rules for TDMR. */ -static int fill_out_tdmrs(struct list_head *tmb_list, - struct tdmr_info_list *tdmr_list) +static __init int fill_out_tdmrs(struct list_head *tmb_list, + struct tdmr_info_list *tdmr_list) { struct tdx_memblock *tmb; int tdmr_idx =3D 0; @@ -482,8 +483,8 @@ static int fill_out_tdmrs(struct list_head *tmb_list, * Calculate PAMT size given a TDMR and a page size. The returned * PAMT size is always aligned up to 4K page boundary. */ -static unsigned long tdmr_get_pamt_sz(struct tdmr_info *tdmr, int pgsz, - u16 pamt_entry_size) +static __init unsigned long tdmr_get_pamt_sz(struct tdmr_info *tdmr, int p= gsz, + u16 pamt_entry_size) { unsigned long pamt_sz, nr_pamt_entries; =20 @@ -514,7 +515,7 @@ static unsigned long tdmr_get_pamt_sz(struct tdmr_info = *tdmr, int pgsz, * PAMT. This node will have some memory covered by the TDMR. The * relative amount of memory covered is not considered. */ -static int tdmr_get_nid(struct tdmr_info *tdmr, struct list_head *tmb_list) +static __init int tdmr_get_nid(struct tdmr_info *tdmr, struct list_head *t= mb_list) { struct tdx_memblock *tmb; =20 @@ -543,9 +544,9 @@ static int tdmr_get_nid(struct tdmr_info *tdmr, struct = list_head *tmb_list) * Allocate PAMTs from the local NUMA node of some memory in @tmb_list * within @tdmr, and set up PAMTs for @tdmr. */ -static int tdmr_set_up_pamt(struct tdmr_info *tdmr, - struct list_head *tmb_list, - u16 pamt_entry_size[]) +static __init int tdmr_set_up_pamt(struct tdmr_info *tdmr, + struct list_head *tmb_list, + u16 pamt_entry_size[]) { unsigned long pamt_base[TDX_PS_NR]; unsigned long pamt_size[TDX_PS_NR]; @@ -615,7 +616,7 @@ static void tdmr_get_pamt(struct tdmr_info *tdmr, unsig= ned long *pamt_base, *pamt_size =3D pamt_sz; } =20 -static void tdmr_do_pamt_func(struct tdmr_info *tdmr, +static __init void tdmr_do_pamt_func(struct tdmr_info *tdmr, void (*pamt_func)(unsigned long base, unsigned long size)) { unsigned long pamt_base, pamt_size; @@ -632,17 +633,17 @@ static void tdmr_do_pamt_func(struct tdmr_info *tdmr, pamt_func(pamt_base, pamt_size); } =20 -static void free_pamt(unsigned long pamt_base, unsigned long pamt_size) +static __init void free_pamt(unsigned long pamt_base, unsigned long pamt_= size) { free_contig_range(pamt_base >> PAGE_SHIFT, pamt_size >> PAGE_SHIFT); } =20 -static void tdmr_free_pamt(struct tdmr_info *tdmr) +static __init void tdmr_free_pamt(struct tdmr_info *tdmr) { tdmr_do_pamt_func(tdmr, free_pamt); } =20 -static void tdmrs_free_pamt_all(struct tdmr_info_list *tdmr_list) +static __init void tdmrs_free_pamt_all(struct tdmr_info_list *tdmr_list) { int i; =20 @@ -651,9 +652,9 @@ static void tdmrs_free_pamt_all(struct tdmr_info_list *= tdmr_list) } =20 /* Allocate and set up PAMTs for all TDMRs */ -static int tdmrs_set_up_pamt_all(struct tdmr_info_list *tdmr_list, - struct list_head *tmb_list, - u16 pamt_entry_size[]) +static __init int tdmrs_set_up_pamt_all(struct tdmr_info_list *tdmr_list, + struct list_head *tmb_list, + u16 pamt_entry_size[]) { int i, ret =3D 0; =20 @@ -702,12 +703,13 @@ void tdx_quirk_reset_page(struct page *page) } EXPORT_SYMBOL_GPL(tdx_quirk_reset_page); =20 -static void tdmr_quirk_reset_pamt(struct tdmr_info *tdmr) +static __init void tdmr_quirk_reset_pamt(struct tdmr_info *tdmr) + { tdmr_do_pamt_func(tdmr, tdx_quirk_reset_paddr); } =20 -static void tdmrs_quirk_reset_pamt_all(struct tdmr_info_list *tdmr_list) +static __init void tdmrs_quirk_reset_pamt_all(struct tdmr_info_list *tdmr_= list) { int i; =20 @@ -715,7 +717,7 @@ static void tdmrs_quirk_reset_pamt_all(struct tdmr_info= _list *tdmr_list) tdmr_quirk_reset_pamt(tdmr_entry(tdmr_list, i)); } =20 -static unsigned long tdmrs_count_pamt_kb(struct tdmr_info_list *tdmr_list) +static __init unsigned long tdmrs_count_pamt_kb(struct tdmr_info_list *tdm= r_list) { unsigned long pamt_size =3D 0; int i; @@ -730,8 +732,8 @@ static unsigned long tdmrs_count_pamt_kb(struct tdmr_in= fo_list *tdmr_list) return pamt_size / 1024; } =20 -static int tdmr_add_rsvd_area(struct tdmr_info *tdmr, int *p_idx, u64 addr, - u64 size, u16 max_reserved_per_tdmr) +static __init int tdmr_add_rsvd_area(struct tdmr_info *tdmr, int *p_idx, + u64 addr, u64 size, u16 max_reserved_per_tdmr) { struct tdmr_reserved_area *rsvd_areas =3D tdmr->reserved_areas; int idx =3D *p_idx; @@ -764,10 +766,10 @@ static int tdmr_add_rsvd_area(struct tdmr_info *tdmr,= int *p_idx, u64 addr, * those holes fall within @tdmr, set up a TDMR reserved area to cover * the hole. */ -static int tdmr_populate_rsvd_holes(struct list_head *tmb_list, - struct tdmr_info *tdmr, - int *rsvd_idx, - u16 max_reserved_per_tdmr) +static __init int tdmr_populate_rsvd_holes(struct list_head *tmb_list, + struct tdmr_info *tdmr, + int *rsvd_idx, + u16 max_reserved_per_tdmr) { struct tdx_memblock *tmb; u64 prev_end; @@ -828,10 +830,10 @@ static int tdmr_populate_rsvd_holes(struct list_head = *tmb_list, * overlaps with @tdmr, set up a TDMR reserved area to cover the * overlapping part. */ -static int tdmr_populate_rsvd_pamts(struct tdmr_info_list *tdmr_list, - struct tdmr_info *tdmr, - int *rsvd_idx, - u16 max_reserved_per_tdmr) +static __init int tdmr_populate_rsvd_pamts(struct tdmr_info_list *tdmr_lis= t, + struct tdmr_info *tdmr, + int *rsvd_idx, + u16 max_reserved_per_tdmr) { int i, ret; =20 @@ -866,7 +868,7 @@ static int tdmr_populate_rsvd_pamts(struct tdmr_info_li= st *tdmr_list, } =20 /* Compare function called by sort() for TDMR reserved areas */ -static int rsvd_area_cmp_func(const void *a, const void *b) +static __init int rsvd_area_cmp_func(const void *a, const void *b) { struct tdmr_reserved_area *r1 =3D (struct tdmr_reserved_area *)a; struct tdmr_reserved_area *r2 =3D (struct tdmr_reserved_area *)b; @@ -885,10 +887,10 @@ static int rsvd_area_cmp_func(const void *a, const vo= id *b) * Populate reserved areas for the given @tdmr, including memory holes * (via @tmb_list) and PAMTs (via @tdmr_list). */ -static int tdmr_populate_rsvd_areas(struct tdmr_info *tdmr, - struct list_head *tmb_list, - struct tdmr_info_list *tdmr_list, - u16 max_reserved_per_tdmr) +static __init int tdmr_populate_rsvd_areas(struct tdmr_info *tdmr, + struct list_head *tmb_list, + struct tdmr_info_list *tdmr_list, + u16 max_reserved_per_tdmr) { int ret, rsvd_idx =3D 0; =20 @@ -913,9 +915,9 @@ static int tdmr_populate_rsvd_areas(struct tdmr_info *t= dmr, * Populate reserved areas for all TDMRs in @tdmr_list, including memory * holes (via @tmb_list) and PAMTs. */ -static int tdmrs_populate_rsvd_areas_all(struct tdmr_info_list *tdmr_list, - struct list_head *tmb_list, - u16 max_reserved_per_tdmr) +static __init int tdmrs_populate_rsvd_areas_all(struct tdmr_info_list *tdm= r_list, + struct list_head *tmb_list, + u16 max_reserved_per_tdmr) { int i; =20 @@ -936,9 +938,9 @@ static int tdmrs_populate_rsvd_areas_all(struct tdmr_in= fo_list *tdmr_list, * to cover all TDX memory regions in @tmb_list based on the TDX module * TDMR global information in @sysinfo_tdmr. */ -static int construct_tdmrs(struct list_head *tmb_list, - struct tdmr_info_list *tdmr_list, - struct tdx_sys_info_tdmr *sysinfo_tdmr) +static __init int construct_tdmrs(struct list_head *tmb_list, + struct tdmr_info_list *tdmr_list, + struct tdx_sys_info_tdmr *sysinfo_tdmr) { u16 pamt_entry_size[TDX_PS_NR] =3D { sysinfo_tdmr->pamt_4k_entry_size, @@ -970,7 +972,8 @@ static int construct_tdmrs(struct list_head *tmb_list, return ret; } =20 -static int config_tdx_module(struct tdmr_info_list *tdmr_list, u64 global_= keyid) +static __init int config_tdx_module(struct tdmr_info_list *tdmr_list, + u64 global_keyid) { struct tdx_module_args args =3D {}; u64 *tdmr_pa_array; @@ -1063,7 +1066,7 @@ static int config_global_keyid(void) return ret; } =20 -static int init_tdmr(struct tdmr_info *tdmr) +static __init int init_tdmr(struct tdmr_info *tdmr) { u64 next; =20 @@ -1094,7 +1097,7 @@ static int init_tdmr(struct tdmr_info *tdmr) return 0; } =20 -static int init_tdmrs(struct tdmr_info_list *tdmr_list) +static __init int init_tdmrs(struct tdmr_info_list *tdmr_list) { int i; =20 @@ -1113,7 +1116,7 @@ static int init_tdmrs(struct tdmr_info_list *tdmr_lis= t) return 0; } =20 -static int init_tdx_module(void) +static __init int init_tdx_module(void) { int ret; =20 @@ -1194,7 +1197,7 @@ static int init_tdx_module(void) goto out_put_tdxmem; } =20 -static int tdx_enable(void) +static __init int tdx_enable(void) { enum cpuhp_state state; int ret; diff --git a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c b/arch/x86/virt/vm= x/tdx/tdx_global_metadata.c index 13ad2663488b..360963bc9328 100644 --- a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c +++ b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c @@ -7,7 +7,7 @@ * Include this file to other C file instead. */ =20 -static int get_tdx_sys_info_features(struct tdx_sys_info_features *sysinfo= _features) +static __init int get_tdx_sys_info_features(struct tdx_sys_info_features *= sysinfo_features) { int ret =3D 0; u64 val; @@ -18,7 +18,7 @@ static int get_tdx_sys_info_features(struct tdx_sys_info_= features *sysinfo_featu return ret; } =20 -static int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr *sysinfo_tdmr) +static __init int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr *sysinfo_= tdmr) { int ret =3D 0; u64 val; @@ -37,7 +37,7 @@ static int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr= *sysinfo_tdmr) return ret; } =20 -static int get_tdx_sys_info_td_ctrl(struct tdx_sys_info_td_ctrl *sysinfo_t= d_ctrl) +static __init int get_tdx_sys_info_td_ctrl(struct tdx_sys_info_td_ctrl *sy= sinfo_td_ctrl) { int ret =3D 0; u64 val; @@ -52,7 +52,7 @@ static int get_tdx_sys_info_td_ctrl(struct tdx_sys_info_t= d_ctrl *sysinfo_td_ctrl return ret; } =20 -static int get_tdx_sys_info_td_conf(struct tdx_sys_info_td_conf *sysinfo_t= d_conf) +static __init int get_tdx_sys_info_td_conf(struct tdx_sys_info_td_conf *sy= sinfo_td_conf) { int ret =3D 0; u64 val; @@ -85,7 +85,7 @@ static int get_tdx_sys_info_td_conf(struct tdx_sys_info_t= d_conf *sysinfo_td_conf return ret; } =20 -static int get_tdx_sys_info(struct tdx_sys_info *sysinfo) +static __init int get_tdx_sys_info(struct tdx_sys_info *sysinfo) { int ret =3D 0; =20 --=20 2.52.0.223.gf5cc29aaa4-goog From nobody Fri Dec 19 12:14:58 2025 Received: from mail-pf1-f201.google.com (mail-pf1-f201.google.com [209.85.210.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E62842609DC for ; Sat, 6 Dec 2025 01:11:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983479; cv=none; b=CvIM/2/oESINAAfa41+jjXMg57M/y61MMap9EltSWyTAu6ZiY+hkBayMCODFIZDD9qkrPamPtBN6gSvMum3CEcoqx8yEwYaV30eie7FKE16QVLN8XrQbROHuDkipasDOvdhY4ylzWn9Jc+CVbfIco7HECmzy4TkPHtipRAovdJ8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983479; c=relaxed/simple; bh=KQpJgpfMzgTc3IeJzO8g2QU2GWsQrVFHCetE2jQYut8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=asoULZ8zPRrH747FSfsbi3lG5KrpE4myo2u0EzeS5QG/pMnvT7h7SZxiCk/JpyQ/gfQZu/PBazGhQ352aoTrJhUYqbIu1BFA/TzcOgH7XAHtGHvHbTlvV+dTLkR++Gdpz4xm0I789jRgqUmwjcKQP6Uq43wzeWDIWiSFxL8r4qg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=RsusmDd0; arc=none smtp.client-ip=209.85.210.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="RsusmDd0" Received: by mail-pf1-f201.google.com with SMTP id d2e1a72fcca58-7b8a12f0cb4so2939756b3a.3 for ; Fri, 05 Dec 2025 17:11:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1764983476; x=1765588276; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:from:to:cc:subject:date:message-id:reply-to; bh=JojZY3uhd5k3hZOQJMhjmwW30PBC8/7wDoPAbDrCEls=; b=RsusmDd0jNs193QTuzMQnjHBHF+7bLfJP7s333Av5KQbrEXg01ty+CW3caEForlHaa KMkFhZkEki9PojYefqZoQKBgYY6W8AZjZ5KJbrqJoxzh+ncZkNZA5JnCrHXMeNJzauyz TS9vuqpnK3Famayi0BhpGkjkfEKT2bAs8SU3/6/51cxQRk/XO8jiQJiqBJ5ydm5p6gab GyDosSleqqes5Z7861n+gUS4LEsxoa3OE2uG0kgMgdj5plD9KZv7CyXw5QlXC91DolCQ AGniVPc4Oz927q/8Xm1sgWhDvcUPzZyWN6lRLez5A5pV4sC/wiZLlrFgqiw8VSxMEOzp DQlA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764983476; x=1765588276; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=JojZY3uhd5k3hZOQJMhjmwW30PBC8/7wDoPAbDrCEls=; b=wcf/h9pN4rpeaE/f3edQpT7KYag58DmCozV5Zv6TVlXhKvB9Ri+BwVHXsX3FFzTq6M 5EGsg1aAaxGyo1ouPQnzP5CTy3sEhsGNOB+rNAJ4PnOJjhSqkz6W0X5a6M9drR4XO6hq cvzKKn2I4+Uqtm5a5QB3Zjhxqq+KYs8kFJTdFOj0dTfC1gG6zRYGW8qaQLI01NJaccDe 77jKMlDqO+sy8aHNw4eWlidYGqzhxiFgbwlftsUf9TERXnrEp5JQcjnazcwHdZLqSlJW HzHCmFashw5rpfhN7IvEgQiTUZm1BZE5+wFhBMNGf3PEo9twEQ2VwPB2Ma6lyS/VsCp3 EwYQ== X-Gm-Message-State: AOJu0Yxah+D+FcT/uvaifHMuGBo/yIF0sFGbb93H1BacathpSCiQgfya abXc8G1Hf4UVjEQaPQkcoRv0EhfngAtA3OoqekuCcuostDq3ci86tY3ETQlvyKsHO3yFR6aPUCJ 8LBr/QA== X-Google-Smtp-Source: AGHT+IEiragae11BDcwJ25pWhkPQUadOVyTO8WpDoz2naWj0OyOQpfwATE9mA9Trjd4vlWLzMj3ulCcvo0M= X-Received: from pfmm5.prod.google.com ([2002:a05:6a00:2485:b0:7cf:2dad:ff87]) (user=seanjc job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a00:94f1:b0:7e8:43f5:bd54 with SMTP id d2e1a72fcca58-7e8c5048a31mr914583b3a.64.1764983476061; Fri, 05 Dec 2025 17:11:16 -0800 (PST) Reply-To: Sean Christopherson Date: Fri, 5 Dec 2025 17:10:52 -0800 In-Reply-To: <20251206011054.494190-1-seanjc@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251206011054.494190-1-seanjc@google.com> X-Mailer: git-send-email 2.52.0.223.gf5cc29aaa4-goog Message-ID: <20251206011054.494190-6-seanjc@google.com> Subject: [PATCH v2 5/7] x86/virt/tdx: KVM: Consolidate TDX CPU hotplug handling From: Sean Christopherson To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, Kiryl Shutsemau , Sean Christopherson , Paolo Bonzini Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev, kvm@vger.kernel.org, Chao Gao , Dan Williams Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Chao Gao The core kernel registers a CPU hotplug callback to do VMX and TDX init and deinit while KVM registers a separate CPU offline callback to block offlining the last online CPU in a socket. Splitting TDX-related CPU hotplug handling across two components is odd and adds unnecessary complexity. Consolidate TDX-related CPU hotplug handling by integrating KVM's tdx_offline_cpu() to the one in the core kernel. Also move nr_configured_hkid to the core kernel because tdx_offline_cpu() references it. Since HKID allocation and free are handled in the core kernel, it's more natural to track used HKIDs there. Signed-off-by: Chao Gao Signed-off-by: Sean Christopherson Reviewed-by: Dan Williams Tested-by: Chao Gao --- arch/x86/kvm/vmx/tdx.c | 67 +------------------------------------ arch/x86/virt/vmx/tdx/tdx.c | 49 +++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 69 deletions(-) diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index d0161dc3d184..d9dd6070baa0 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -59,8 +59,6 @@ module_param_named(tdx, enable_tdx, bool, 0444); #define TDX_SHARED_BIT_PWL_5 gpa_to_gfn(BIT_ULL(51)) #define TDX_SHARED_BIT_PWL_4 gpa_to_gfn(BIT_ULL(47)) =20 -static enum cpuhp_state tdx_cpuhp_state __ro_after_init; - static const struct tdx_sys_info *tdx_sysinfo; =20 void tdh_vp_rd_failed(struct vcpu_tdx *tdx, char *uclass, u32 field, u64 e= rr) @@ -219,8 +217,6 @@ static int init_kvm_tdx_caps(const struct tdx_sys_info_= td_conf *td_conf, */ static DEFINE_MUTEX(tdx_lock); =20 -static atomic_t nr_configured_hkid; - static bool tdx_operand_busy(u64 err) { return (err & TDX_SEAMCALL_STATUS_MASK) =3D=3D TDX_OPERAND_BUSY; @@ -268,7 +264,6 @@ static inline void tdx_hkid_free(struct kvm_tdx *kvm_td= x) { tdx_guest_keyid_free(kvm_tdx->hkid); kvm_tdx->hkid =3D -1; - atomic_dec(&nr_configured_hkid); misc_cg_uncharge(MISC_CG_RES_TDX, kvm_tdx->misc_cg, 1); put_misc_cg(kvm_tdx->misc_cg); kvm_tdx->misc_cg =3D NULL; @@ -2399,8 +2394,6 @@ static int __tdx_td_init(struct kvm *kvm, struct td_p= arams *td_params, =20 ret =3D -ENOMEM; =20 - atomic_inc(&nr_configured_hkid); - tdr_page =3D alloc_page(GFP_KERNEL); if (!tdr_page) goto free_hkid; @@ -3302,51 +3295,10 @@ int tdx_gmem_max_mapping_level(struct kvm *kvm, kvm= _pfn_t pfn, bool is_private) return PG_LEVEL_4K; } =20 -static int tdx_online_cpu(unsigned int cpu) -{ - return 0; -} - -static int tdx_offline_cpu(unsigned int cpu) -{ - int i; - - /* No TD is running. Allow any cpu to be offline. */ - if (!atomic_read(&nr_configured_hkid)) - return 0; - - /* - * In order to reclaim TDX HKID, (i.e. when deleting guest TD), need to - * call TDH.PHYMEM.PAGE.WBINVD on all packages to program all memory - * controller with pconfig. If we have active TDX HKID, refuse to - * offline the last online cpu. - */ - for_each_online_cpu(i) { - /* - * Found another online cpu on the same package. - * Allow to offline. - */ - if (i !=3D cpu && topology_physical_package_id(i) =3D=3D - topology_physical_package_id(cpu)) - return 0; - } - - /* - * This is the last cpu of this package. Don't offline it. - * - * Because it's hard for human operator to understand the - * reason, warn it. - */ -#define MSG_ALLPKG_ONLINE \ - "TDX requires all packages to have an online CPU. Delete all TDs in order= to offline all CPUs of a package.\n" - pr_warn_ratelimited(MSG_ALLPKG_ONLINE); - return -EBUSY; -} - static int __init __tdx_bringup(void) { const struct tdx_sys_info_td_conf *td_conf; - int r, i; + int i; =20 for (i =3D 0; i < ARRAY_SIZE(tdx_uret_msrs); i++) { /* @@ -3414,23 +3366,7 @@ static int __init __tdx_bringup(void) if (misc_cg_set_capacity(MISC_CG_RES_TDX, tdx_get_nr_guest_keyids())) return -EINVAL; =20 - /* - * TDX-specific cpuhp callback to disallow offlining the last CPU in a - * packing while KVM is running one or more TDs. Reclaiming HKIDs - * requires doing PAGE.WBINVD on every package, i.e. offlining all CPUs - * of a package would prevent reclaiming the HKID. - */ - r =3D cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "kvm/cpu/tdx:online", - tdx_online_cpu, tdx_offline_cpu); - if (r < 0) - goto err_cpuhup; - - tdx_cpuhp_state =3D r; return 0; - -err_cpuhup: - misc_cg_set_capacity(MISC_CG_RES_TDX, 0); - return r; } =20 int __init tdx_bringup(void) @@ -3486,7 +3422,6 @@ void tdx_cleanup(void) return; =20 misc_cg_set_capacity(MISC_CG_RES_TDX, 0); - cpuhp_remove_state(tdx_cpuhp_state); } =20 void __init tdx_hardware_setup(void) diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index d49645797fe4..5cf008bffa94 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -58,6 +58,8 @@ static LIST_HEAD(tdx_memlist); static struct tdx_sys_info tdx_sysinfo __ro_after_init; static bool tdx_module_initialized __ro_after_init; =20 +static atomic_t nr_configured_hkid; + typedef void (*sc_err_func_t)(u64 fn, u64 err, struct tdx_module_args *arg= s); =20 static inline void seamcall_err(u64 fn, u64 err, struct tdx_module_args *a= rgs) @@ -190,6 +192,40 @@ static int tdx_online_cpu(unsigned int cpu) =20 static int tdx_offline_cpu(unsigned int cpu) { + int i; + + /* No TD is running. Allow any cpu to be offline. */ + if (!atomic_read(&nr_configured_hkid)) + goto done; + + /* + * In order to reclaim TDX HKID, (i.e. when deleting guest TD), need to + * call TDH.PHYMEM.PAGE.WBINVD on all packages to program all memory + * controller with pconfig. If we have active TDX HKID, refuse to + * offline the last online cpu. + */ + for_each_online_cpu(i) { + /* + * Found another online cpu on the same package. + * Allow to offline. + */ + if (i !=3D cpu && topology_physical_package_id(i) =3D=3D + topology_physical_package_id(cpu)) + goto done; + } + + /* + * This is the last cpu of this package. Don't offline it. + * + * Because it's hard for human operator to understand the + * reason, warn it. + */ +#define MSG_ALLPKG_ONLINE \ + "TDX requires all packages to have an online CPU. Delete all TDs in order= to offline all CPUs of a package.\n" + pr_warn_ratelimited(MSG_ALLPKG_ONLINE); + return -EBUSY; + +done: x86_virt_put_cpu(X86_FEATURE_VMX); return 0; } @@ -1506,15 +1542,22 @@ EXPORT_SYMBOL_GPL(tdx_get_nr_guest_keyids); =20 int tdx_guest_keyid_alloc(void) { - return ida_alloc_range(&tdx_guest_keyid_pool, tdx_guest_keyid_start, - tdx_guest_keyid_start + tdx_nr_guest_keyids - 1, - GFP_KERNEL); + int ret; + + ret =3D ida_alloc_range(&tdx_guest_keyid_pool, tdx_guest_keyid_start, + tdx_guest_keyid_start + tdx_nr_guest_keyids - 1, + GFP_KERNEL); + if (ret >=3D 0) + atomic_inc(&nr_configured_hkid); + + return ret; } EXPORT_SYMBOL_GPL(tdx_guest_keyid_alloc); =20 void tdx_guest_keyid_free(unsigned int keyid) { ida_free(&tdx_guest_keyid_pool, keyid); + atomic_dec(&nr_configured_hkid); } EXPORT_SYMBOL_GPL(tdx_guest_keyid_free); =20 --=20 2.52.0.223.gf5cc29aaa4-goog From nobody Fri Dec 19 12:14:58 2025 Received: from mail-pj1-f73.google.com (mail-pj1-f73.google.com [209.85.216.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 97BA126F280 for ; Sat, 6 Dec 2025 01:11:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983481; cv=none; b=gpxjE7FTkwXe+d3dmXLheYKBDiatzhZ2NqCslHJoCIbXWpgN0Yj41yCdQ6NVFIw0VX+drUN6++o6eP9D9+6znViimOcsGyLIvn/QIhfzrfJjxkvhXgrzRdi30wUOJ/HL6RAjOo5Y/kiFLN3+fq5/qLlhapGOO+UU36YCiUBjdtI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983481; c=relaxed/simple; bh=dD0zdMBxcirbZcOCLQPI4IDX7thvsilC04qKPz4Ct6Y=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=j/OcARz7YIeQYOwh345oBCmrokYUaU6QS7u6G1HsiYvXynl4mWbAwi0lo6SwEx99bkWpLljJhB3K9cU1T7gCFbbnoNDi+O3vIm9HP6+0C1kIy3OqtWuO3p0sLBB9u5g9CpVJR0BlmbS81GJhwJrKkRyI7Qu1VBl6QGC/bOBiIf0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Gp/Cgt9X; arc=none smtp.client-ip=209.85.216.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Gp/Cgt9X" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-34188ba5990so6179519a91.0 for ; Fri, 05 Dec 2025 17:11:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1764983479; x=1765588279; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:from:to:cc:subject:date:message-id:reply-to; bh=rVtJBDhchDrLagfgGom7nKilzuyDMEWeeU45x47h0PI=; b=Gp/Cgt9XPNKT3gYaJlFTPCgPX5qxP0Zee1SlEuQ2NP/wIR5QTdhxXgWjqKj3FJZjZ5 aevEP9M2jTsduiQFyyaa90J54F5r0XWx3vsK5RItojEvrjXLe9IO/zOe0TijsyaNpzxX lH5HYdRbSS1Ek7xqdRpIz/NfpYNFM19Nantxyd1QwuOUdu79AkRqjEwWCqB4MMMMkN9n 9W6C8+z0z49J/k6HZ1Tzh28UNstxNyfzBk+QW+20CjeWlXe2Butuo4xitNzNZxXobJrp 1KXjvsj8/tt9jU0tUZx4zskTb4ew5NJNrb+ex0yv7cMcc/0WQN6TvIbMwQmULIPg1GfP 4gtQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764983479; x=1765588279; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=rVtJBDhchDrLagfgGom7nKilzuyDMEWeeU45x47h0PI=; b=kK3VOJcYCu3Q5fgbfcRyrBoTCUtm6UrX8CKXcilRG0KbLpFC5WHwqWxo+2go8hGKub enVc3sx7l2bmNu7TpJ9o06gEDykT5VyGnlEM1U4HUCjKzGubluTcr+v+5XYUXjsgCZNs bUa6nt8jKyatslUckocuN51QSL47R3QjGRotRpo2dZurJp5Lbwq0SjrYxcuMGKgt4pmX IhAth+fuft/rRjjhRDrD0M6eyisrGSW6FH2qJDrRrB4ZI787vVc13gStEXvTjyGeTyWN mjQsSpWV/9FUr2xG33+eSsgXO9cjM+oj+MCZU+pbd2f90k+v7+1/8VJr53QtVmaxO5T1 NzrA== X-Gm-Message-State: AOJu0YzTWPoqdcge5QrwxjJgNVoEI06d9zWR2EY3cihsEyfR8OzrF4mW uzWcN8duVuIrPyYPGv0bz2TLiOQPHStz6md5UnzIg1wEBFnictowkv8g9vDN7juRWaV86EcfwU6 K4AveSw== X-Google-Smtp-Source: AGHT+IERf67I/KSjwUV4VBGzOV5LWpvLTz4kgH2qJZnW3MJYfCI+Tp3EG4cQDFHBfXE9LirgMVmyzwQKLSk= X-Received: from pjbfz21.prod.google.com ([2002:a17:90b:255:b0:340:4910:738f]) (user=seanjc job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:2f08:b0:339:ec9c:b275 with SMTP id 98e67ed59e1d1-349a253dc8emr794762a91.6.1764983478902; Fri, 05 Dec 2025 17:11:18 -0800 (PST) Reply-To: Sean Christopherson Date: Fri, 5 Dec 2025 17:10:53 -0800 In-Reply-To: <20251206011054.494190-1-seanjc@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251206011054.494190-1-seanjc@google.com> X-Mailer: git-send-email 2.52.0.223.gf5cc29aaa4-goog Message-ID: <20251206011054.494190-7-seanjc@google.com> Subject: [PATCH v2 6/7] x86/virt/tdx: Use ida_is_empty() to detect if any TDs may be running From: Sean Christopherson To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, Kiryl Shutsemau , Sean Christopherson , Paolo Bonzini Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev, kvm@vger.kernel.org, Chao Gao , Dan Williams Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Drop nr_configured_hkid and instead use ida_is_empty() to detect if any HKIDs have been allocated/configured. Suggested-by: Dan Williams Signed-off-by: Sean Christopherson Reviewed-by: Chao Gao Reviewed-by: Dan Williams Tested-by: Chao Gao --- arch/x86/virt/vmx/tdx/tdx.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index 5cf008bffa94..ef77135ec373 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -58,8 +58,6 @@ static LIST_HEAD(tdx_memlist); static struct tdx_sys_info tdx_sysinfo __ro_after_init; static bool tdx_module_initialized __ro_after_init; =20 -static atomic_t nr_configured_hkid; - typedef void (*sc_err_func_t)(u64 fn, u64 err, struct tdx_module_args *arg= s); =20 static inline void seamcall_err(u64 fn, u64 err, struct tdx_module_args *a= rgs) @@ -195,7 +193,7 @@ static int tdx_offline_cpu(unsigned int cpu) int i; =20 /* No TD is running. Allow any cpu to be offline. */ - if (!atomic_read(&nr_configured_hkid)) + if (ida_is_empty(&tdx_guest_keyid_pool)) goto done; =20 /* @@ -1542,22 +1540,15 @@ EXPORT_SYMBOL_GPL(tdx_get_nr_guest_keyids); =20 int tdx_guest_keyid_alloc(void) { - int ret; - - ret =3D ida_alloc_range(&tdx_guest_keyid_pool, tdx_guest_keyid_start, - tdx_guest_keyid_start + tdx_nr_guest_keyids - 1, - GFP_KERNEL); - if (ret >=3D 0) - atomic_inc(&nr_configured_hkid); - - return ret; + return ida_alloc_range(&tdx_guest_keyid_pool, tdx_guest_keyid_start, + tdx_guest_keyid_start + tdx_nr_guest_keyids - 1, + GFP_KERNEL); } EXPORT_SYMBOL_GPL(tdx_guest_keyid_alloc); =20 void tdx_guest_keyid_free(unsigned int keyid) { ida_free(&tdx_guest_keyid_pool, keyid); - atomic_dec(&nr_configured_hkid); } EXPORT_SYMBOL_GPL(tdx_guest_keyid_free); =20 --=20 2.52.0.223.gf5cc29aaa4-goog From nobody Fri Dec 19 12:14:58 2025 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A3BF22820B1 for ; Sat, 6 Dec 2025 01:11:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983483; cv=none; b=jyoJ1qhgBky55E9zmoJZxR6LCi+hH9TC3Nk4GmPKHbdouYl4Fr4zsWToWrhVeO7pq2kT62SKv3mzbgNwoCEjkM9AOZawCx82+p10ScSu7nm1GU7xxwjeco7k9y5d5L05tck+Q+q2Y7+ryQt80a+qa7hBwqNzHs8I6s0co8Nv+kk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764983483; c=relaxed/simple; bh=bkTcXhbD0K+GaH8lROgSlwAp7KEAKH17Wyu0+TXtIrs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=CauT94yT99TuYIgBeuVrrbuF3gqJ9jKCKnT9wuDwauzPAuDbSCgNUfjXp18ArwutRQ5VOzKL3fiWEc0BzA2yfmkPbzlo1t/qE0idwZ9prGcKmfqCWUq7u+rAqZ+BcHL+e1XDUZyd/1FpWcKoYtPA21j6SLKLKPeEYuNIX3l0b7Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=lodkx/V+; arc=none smtp.client-ip=209.85.216.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--seanjc.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="lodkx/V+" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-349aadb8ab9so161416a91.3 for ; Fri, 05 Dec 2025 17:11:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1764983481; x=1765588281; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:from:to:cc:subject:date:message-id:reply-to; bh=VkOwSpZnzzGFCGAjywqik6ZsByr3pLNa/lziUDNxQoA=; b=lodkx/V+B/DOn30DfIr1aL1Hq8F2dbs8/M5TEUWJMw+Mgdj5ABSjt7nePgtZqwoSyx w2F7tZXrHsDL9O4MDxZ7lWtsEltBbCS7oV+AahT1jzO6Yofz32I9YGhvLtfSkckgKkCX V5xV75qyWSGsEUrOJw2dGGTSw4NhCMTqnp54qIF0Ee8pXcHM+M83xig7l+q2I26clfdy DFu011FY/c4LwtgcCbXKxr5xR/9ojL6mQjGM1uV87g2LFwiQ433lvTmorknsdlsD7HyR TNx/ChM00ROaPYVYwAI2faKHJarbrCOwKElxYxxmyRWtcqtLHDC8jaUNQlDRlFoeLIh/ ioDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764983481; x=1765588281; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:reply-to:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=VkOwSpZnzzGFCGAjywqik6ZsByr3pLNa/lziUDNxQoA=; b=snEt2g1+dHdOM0/awC9hUp4pluRAKfUphLQOLIE26UFiJeQXmfDRrKD3aowr8Cu8U+ WGYTBqPeMQFIFyx4pOPvq90VOH0SO1GSovTwMsNTPGy0aUpuMcfSo2RDEitWIMCoYDh5 Bcyekvx1MnZbdyWOcTdrV8Y2nYQMOnAVex7LL6kNegiEhkLptW0gXjAndDEi9wX6nPQh 79C6rGCkk63bNYn+8OjOKZPLmHJzU47wPzNViVj8m4FJTQnwy7LF5MXNM+fyxzhetFEM DExpf/H/TCPvILwIAzsZLwrvgQ4qZ1c09G4xXpDieeLqEfBNYhTGyqZKwS6TzayKRzfJ CCUQ== X-Gm-Message-State: AOJu0YxThaEGjW1kjsgUbshGcRcYcaYCvkymI064nukrMTTRM5yf9DrR 0gXXXQ8bhEw3ftZfuYUq7FW3UjWVvVra5XAg3ipdbAX8VocoSCX4tSHIDG+nNKvxlzdSz3VsoZf 8NCv3EQ== X-Google-Smtp-Source: AGHT+IFWR4uQiL6EU4wkVoYXTg3vRHDDsenTnV3mWzHCmctEhaBhx5JH5zxDIa3kq4f+/zZVRo4jq1+s238= X-Received: from pjbin12.prod.google.com ([2002:a17:90b:438c:b0:33f:e888:4aad]) (user=seanjc job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:4ecd:b0:340:a1a8:eb87 with SMTP id 98e67ed59e1d1-349a260d6b8mr829195a91.35.1764983481038; Fri, 05 Dec 2025 17:11:21 -0800 (PST) Reply-To: Sean Christopherson Date: Fri, 5 Dec 2025 17:10:54 -0800 In-Reply-To: <20251206011054.494190-1-seanjc@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251206011054.494190-1-seanjc@google.com> X-Mailer: git-send-email 2.52.0.223.gf5cc29aaa4-goog Message-ID: <20251206011054.494190-8-seanjc@google.com> Subject: [PATCH v2 7/7] KVM: Bury kvm_{en,dis}able_virtualization() in kvm_main.c once more From: Sean Christopherson To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, Kiryl Shutsemau , Sean Christopherson , Paolo Bonzini Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev, kvm@vger.kernel.org, Chao Gao , Dan Williams Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Now that TDX handles doing VMXON without KVM's involvement, bury the top-level APIs to enable and disable virtualization back in kvm_main.c. No functional change intended. Signed-off-by: Sean Christopherson Reviewed-by: Chao Gao Reviewed-by: Dan Williams Tested-by: Chao Gao --- include/linux/kvm_host.h | 8 -------- virt/kvm/kvm_main.c | 17 +++++++++++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index a453fe6ce05a..ac9332104793 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -2596,12 +2596,4 @@ long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu = *vcpu, struct kvm_pre_fault_memory *range); #endif =20 -#ifdef CONFIG_KVM_GENERIC_HARDWARE_ENABLING -int kvm_enable_virtualization(void); -void kvm_disable_virtualization(void); -#else -static inline int kvm_enable_virtualization(void) { return 0; } -static inline void kvm_disable_virtualization(void) { } -#endif - #endif diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 3278ee9381bd..ac2633e9cd80 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1111,6 +1111,9 @@ static inline struct kvm_io_bus *kvm_get_bus_for_dest= ruction(struct kvm *kvm, !refcount_read(&kvm->users_count)); } =20 +static int kvm_enable_virtualization(void); +static void kvm_disable_virtualization(void); + static struct kvm *kvm_create_vm(unsigned long type, const char *fdname) { struct kvm *kvm =3D kvm_arch_alloc_vm(); @@ -5693,7 +5696,7 @@ static struct syscore_ops kvm_syscore_ops =3D { .shutdown =3D kvm_shutdown, }; =20 -int kvm_enable_virtualization(void) +static int kvm_enable_virtualization(void) { int r; =20 @@ -5738,9 +5741,8 @@ int kvm_enable_virtualization(void) --kvm_usage_count; return r; } -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_enable_virtualization); =20 -void kvm_disable_virtualization(void) +static void kvm_disable_virtualization(void) { guard(mutex)(&kvm_usage_lock); =20 @@ -5751,7 +5753,6 @@ void kvm_disable_virtualization(void) cpuhp_remove_state(CPUHP_AP_KVM_ONLINE); kvm_arch_disable_virtualization(); } -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_disable_virtualization); =20 static int kvm_init_virtualization(void) { @@ -5767,6 +5768,14 @@ static void kvm_uninit_virtualization(void) kvm_disable_virtualization(); } #else /* CONFIG_KVM_GENERIC_HARDWARE_ENABLING */ +static int kvm_enable_virtualization(void) +{ + return 0; +} +static void kvm_disable_virtualization(void) +{ + +} static int kvm_init_virtualization(void) { return 0; --=20 2.52.0.223.gf5cc29aaa4-goog