@@ -123,8 +123,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 -ENODEV; }
#endif /* CONFIG_INTEL_TDX_HOST */
#endif /* !__ASSEMBLY__ */
@@ -12,6 +12,9 @@
#include <linux/printk.h>
#include <linux/list.h>
#include <linux/memblock.h>
+#include <linux/mutex.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
#include <asm/msr-index.h>
#include <asm/msr.h>
#include <asm/apic.h>
@@ -25,12 +28,30 @@ struct tdx_memblock {
int nid;
};
+/*
+ * TDX module status during initialization
+ */
+enum tdx_module_status_t {
+ /* TDX module hasn't been detected and initialized */
+ TDX_MODULE_UNKNOWN,
+ /* TDX module is not loaded */
+ TDX_MODULE_NONE,
+ /* TDX module is initialized */
+ TDX_MODULE_INITIALIZED,
+ /* TDX module is shut down due to initialization error */
+ TDX_MODULE_SHUTDOWN,
+};
+
static u32 tdx_keyid_start __ro_after_init;
static u32 tdx_keyid_num __ro_after_init;
/* All TDX-usable memory regions */
static LIST_HEAD(tdx_memlist);
+static enum tdx_module_status_t tdx_module_status;
+/* Prevent concurrent attempts on TDX detection and initialization */
+static DEFINE_MUTEX(tdx_module_lock);
+
/*
* Detect TDX private KeyIDs to see whether TDX has been enabled by the
* BIOS. Both initializing the TDX module and running TDX guest require
@@ -245,3 +266,134 @@ bool platform_tdx_enabled(void)
{
return !!tdx_keyid_num;
}
+
+/*
+ * Detect and initialize the TDX module.
+ *
+ * Return -ENODEV when the TDX module is not loaded, 0 when it
+ * is successfully initialized, or other error when it fails to
+ * initialize.
+ */
+static int init_tdx_module(void)
+{
+ /* The TDX module hasn't been detected */
+ return -ENODEV;
+}
+
+static void shutdown_tdx_module(void)
+{
+ /* TODO: Shut down the TDX module */
+}
+
+static int __tdx_enable(void)
+{
+ int ret;
+
+ /*
+ * Initializing the TDX module requires doing SEAMCALL on all
+ * boot-time present CPUs. For simplicity temporarily disable
+ * CPU hotplug to prevent any CPU from going offline during
+ * the initialization.
+ */
+ cpus_read_lock();
+
+ /*
+ * Check whether all boot-time present CPUs are online and
+ * return early with a message so the user can be aware.
+ *
+ * Note a non-buggy BIOS should never support physical (ACPI)
+ * CPU hotplug when TDX is enabled, and all boot-time present
+ * CPU should be enabled in MADT, so there should be no
+ * disabled_cpus and num_processors won't change at runtime
+ * either.
+ */
+ if (disabled_cpus || num_online_cpus() != num_processors) {
+ pr_err("Unable to initialize the TDX module when there's offline CPU(s).\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = init_tdx_module();
+ if (ret == -ENODEV) {
+ pr_info("TDX module is not loaded.\n");
+ tdx_module_status = TDX_MODULE_NONE;
+ goto out;
+ }
+
+ /*
+ * Shut down the TDX module in case of any error during the
+ * initialization process. It's meaningless to leave the TDX
+ * module in any middle state of the initialization process.
+ *
+ * Shutting down the module also requires doing SEAMCALL on all
+ * MADT-enabled CPUs. Do it while CPU hotplug is disabled.
+ *
+ * Return all errors during the initialization as -EFAULT as the
+ * module is always shut down.
+ */
+ if (ret) {
+ pr_info("Failed to initialize TDX module. Shut it down.\n");
+ shutdown_tdx_module();
+ tdx_module_status = TDX_MODULE_SHUTDOWN;
+ ret = -EFAULT;
+ goto out;
+ }
+
+ pr_info("TDX module initialized.\n");
+ tdx_module_status = TDX_MODULE_INITIALIZED;
+out:
+ cpus_read_unlock();
+
+ return ret;
+}
+
+/**
+ * tdx_enable - Enable TDX by initializing the TDX module
+ *
+ * Caller to make sure all CPUs are online and in VMX operation before
+ * calling this function. CPU hotplug is temporarily disabled internally
+ * to prevent any cpu from going offline.
+ *
+ * This function can be called in parallel by multiple callers.
+ *
+ * Return:
+ *
+ * * 0: The TDX module has been successfully initialized.
+ * * -ENODEV: The TDX module is not loaded, or TDX is not supported.
+ * * -EINVAL: The TDX module cannot be initialized due to certain
+ * conditions are not met (i.e. when not all MADT-enabled
+ * CPUs are not online).
+ * * -EFAULT: Other internal fatal errors, or the TDX module is in
+ * shutdown mode due to it failed to initialize in previous
+ * attempts.
+ */
+int tdx_enable(void)
+{
+ int ret;
+
+ if (!platform_tdx_enabled())
+ return -ENODEV;
+
+ mutex_lock(&tdx_module_lock);
+
+ switch (tdx_module_status) {
+ case TDX_MODULE_UNKNOWN:
+ ret = __tdx_enable();
+ break;
+ case TDX_MODULE_NONE:
+ ret = -ENODEV;
+ break;
+ case TDX_MODULE_INITIALIZED:
+ ret = 0;
+ break;
+ default:
+ WARN_ON_ONCE(tdx_module_status != TDX_MODULE_SHUTDOWN);
+ ret = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&tdx_module_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tdx_enable);