Message ID | eb565156d84e35a846a886025513a712f2ac2f83.1676286526.git.kai.huang@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | TDX host kernel support | expand |
On Tue, Feb 14, 2023 at 12:59:11AM +1300, Kai Huang wrote: > Use a state machine protected by mutex to make sure the initialization > will only be done once, as tdx_enable() can be called multiple times > (i.e. KVM module can be reloaded) and be called concurrently by other > kernel components in the future. I still object to doing tdx_enable() at kvm module load. kvm.ko gets loaded unconditionally on boot, even if I then never use kvm. This stuff needs to be done when an actual VM is created, not before.
On 2/14/23 04:46, Peter Zijlstra wrote: > On Tue, Feb 14, 2023 at 12:59:11AM +1300, Kai Huang wrote: >> Use a state machine protected by mutex to make sure the initialization >> will only be done once, as tdx_enable() can be called multiple times >> (i.e. KVM module can be reloaded) and be called concurrently by other >> kernel components in the future. > I still object to doing tdx_enable() at kvm module load. > > kvm.ko gets loaded unconditionally on boot, even if I then never use > kvm. > > This stuff needs to be done when an actual VM is created, not before. The actually implementation of this is hidden over in the KVM side of this. But, tdx_enable() and all of this jazz should not be called on kvm.ko load. It'll happen when the KVM tries to start the first TDX VM. I think what Kai was thinking of was *this* sequence: 1. insmod kvm.ko 2. Start a TDX guest, tdx_enable() gets run 3. rmmod kvm 4. insmod kvm.ko (again) 5. Start another TDX guest, run tdx_enable() (again) The rmmod/insmod pair is what triggers the second call of tdx_enable().
On Tue, 2023-02-14 at 09:23 -0800, Dave Hansen wrote: > On 2/14/23 04:46, Peter Zijlstra wrote: > > On Tue, Feb 14, 2023 at 12:59:11AM +1300, Kai Huang wrote: > > > Use a state machine protected by mutex to make sure the initialization > > > will only be done once, as tdx_enable() can be called multiple times > > > (i.e. KVM module can be reloaded) and be called concurrently by other > > > kernel components in the future. > > I still object to doing tdx_enable() at kvm module load. > > > > kvm.ko gets loaded unconditionally on boot, even if I then never use > > kvm. > > > > This stuff needs to be done when an actual VM is created, not before. > > The actually implementation of this is hidden over in the KVM side of > this. But, tdx_enable() and all of this jazz should not be called on > kvm.ko load. It'll happen when the KVM tries to start the first TDX VM. > > I think what Kai was thinking of was *this* sequence: > > 1. insmod kvm.ko > 2. Start a TDX guest, tdx_enable() gets run > 3. rmmod kvm > 4. insmod kvm.ko (again) > 5. Start another TDX guest, run tdx_enable() (again) > > The rmmod/insmod pair is what triggers the second call of tdx_enable(). Yes. The point is tdx_enable() can get called multi times. We can discuss more when to enable TDX at KVM side, and I don't want to speak for KVM maintainers, but this is actually not that relevant to this series. In the changelog, I just said: "...initialize TDX until there is a real need (e.g when requested by KVM)". I didn't say exactly when KVM will call this.
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 4dfe2e794411..4a3ee64c1ca7 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -97,8 +97,10 @@ static inline long tdx_kvm_hypercall(unsigned int nr, unsigned long p1, #ifdef CONFIG_INTEL_TDX_HOST bool platform_tdx_enabled(void); +int tdx_enable(void); #else /* !CONFIG_INTEL_TDX_HOST */ static inline bool platform_tdx_enabled(void) { return false; } +static inline int tdx_enable(void) { return -EINVAL; } #endif /* CONFIG_INTEL_TDX_HOST */ #endif /* !__ASSEMBLY__ */ diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index a600b5d0879d..f5a20d56097c 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -12,14 +12,20 @@ #include <linux/init.h> #include <linux/errno.h> #include <linux/printk.h> +#include <linux/mutex.h> #include <asm/msr-index.h> #include <asm/msr.h> #include <asm/tdx.h> +#include "tdx.h" static u32 tdx_global_keyid __ro_after_init; static u32 tdx_guest_keyid_start __ro_after_init; static u32 tdx_nr_guest_keyids __ro_after_init; +static enum tdx_module_status_t tdx_module_status; +/* Prevent concurrent attempts on TDX module initialization */ +static DEFINE_MUTEX(tdx_module_lock); + /* * Use tdx_global_keyid to indicate that TDX is uninitialized. * This is used in TDX initialization error paths to take it from @@ -103,3 +109,86 @@ bool platform_tdx_enabled(void) { return !!tdx_global_keyid; } + +static int init_tdx_module(void) +{ + /* + * TODO: + * + * - TDX module global initialization. + * - TDX module per-cpu initialization. + * - Get TDX module information and TDX-capable memory regions. + * - Build the list of TDX-usable memory regions. + * - Construct a list of "TD Memory Regions" (TDMRs) to cover + * all TDX-usable memory regions. + * - Configure the TDMRs and the global KeyID to the TDX module. + * - Configure the global KeyID on all packages. + * - Initialize all TDMRs. + * + * Return error before all steps are done. + */ + return -EINVAL; +} + +static int __tdx_enable(void) +{ + int ret; + + ret = init_tdx_module(); + if (ret) { + pr_err("initialization failed (%d)\n", ret); + tdx_module_status = TDX_MODULE_ERROR; + /* + * Just return one universal error code. + * For now the caller cannot recover anyway. + */ + return -EINVAL; + } + + pr_info("TDX module initialized.\n"); + tdx_module_status = TDX_MODULE_INITIALIZED; + + return 0; +} + +/** + * tdx_enable - Enable TDX to be ready to run TDX guests + * + * Initialize the TDX module to enable TDX. After this function, the TDX + * module is ready to create and run TDX guests. + * + * This function assumes all online cpus are already in VMX operation. + * 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 (!platform_tdx_enabled()) { + pr_err_once("initialization failed: TDX is disabled.\n"); + return -EINVAL; + } + + mutex_lock(&tdx_module_lock); + + switch (tdx_module_status) { + case TDX_MODULE_UNKNOWN: + ret = __tdx_enable(); + break; + case TDX_MODULE_INITIALIZED: + /* Already initialized, great, tell the caller. */ + ret = 0; + break; + default: + /* Failed to initialize in the previous attempts */ + ret = -EINVAL; + break; + } + + mutex_unlock(&tdx_module_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(tdx_enable); diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h new file mode 100644 index 000000000000..881cca276956 --- /dev/null +++ b/arch/x86/virt/vmx/tdx/tdx.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _X86_VIRT_TDX_H +#define _X86_VIRT_TDX_H + +/* Kernel defined TDX module status during module initialization. */ +enum tdx_module_status_t { + TDX_MODULE_UNKNOWN, + TDX_MODULE_INITIALIZED, + TDX_MODULE_ERROR +}; + +#endif