TDX depends on a platform firmware module that is invoked via instructions similar to vmenter (i.e. enter into a new privileged "root-mode" context to manage private memory and private device mechanisms). It is a software construct that depends on the CPU vmxon state to enable invocation of TDX-module ABIs. Unlike other Trusted Execution Environment (TEE) platform implementations that employ a firmware module running on a PCI device with an MMIO mailbox for communication, TDX has no hardware device to point to as the "TSM". The "/sys/devices/virtual" hierarchy is intended for "software constructs which need sysfs interface", which aligns with what TDX needs. The new tdx_subsys will export global attributes populated by the TDX-module "sysinfo". A tdx_tsm device is published on this bus to enable a typical driver model for the low level "TEE Security Manager" (TSM) flows that talk TDISP to capable PCIe devices. For now, this is only the base tdx_subsys and tdx_tsm device registration with attribute definition and TSM driver to follow later. Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx> --- arch/x86/virt/vmx/tdx/tdx.c | 63 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index 4d6826a76f78..e23bddf31daa 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -28,6 +28,8 @@ #include <linux/acpi.h> #include <linux/suspend.h> #include <linux/acpi.h> +#include <linux/device.h> +#include <linux/cleanup.h> #include <asm/page.h> #include <asm/special_insns.h> #include <asm/msr-index.h> @@ -1097,9 +1099,56 @@ static int init_tdmrs(struct tdmr_info_list *tdmr_list) return 0; } +static const struct bus_type tdx_subsys = { + .name = "tdx", +}; + +struct tdx_tsm { + struct device dev; +}; + +static struct tdx_tsm *alloc_tdx_tsm(void) +{ + struct tdx_tsm *tsm = kzalloc(sizeof(*tsm), GFP_KERNEL); + struct device *dev; + + if (!tsm) + return ERR_PTR(-ENOMEM); + + dev = &tsm->dev; + dev->bus = &tdx_subsys; + device_initialize(dev); + + return tsm; +} + +DEFINE_FREE(tdx_tsm_put, struct tdx_tsm *, + if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev)) +static struct tdx_tsm *init_tdx_tsm(void) +{ + struct device *dev; + int ret; + + struct tdx_tsm *tsm __free(tdx_tsm_put) = alloc_tdx_tsm(); + if (IS_ERR(tsm)) + return tsm; + + dev = &tsm->dev; + ret = dev_set_name(dev, "tdx_tsm"); + if (ret) + return ERR_PTR(ret); + + ret = device_add(dev); + if (ret) + return ERR_PTR(ret); + + return no_free_ptr(tsm); +} + static int init_tdx_module(void) { struct tdx_tdmr_sysinfo tdmr_sysinfo; + struct tdx_tsm *tsm; int ret; /* @@ -1122,10 +1171,15 @@ static int init_tdx_module(void) if (ret) goto err_free_tdxmem; + /* Establish subsystem for global TDX module attributes */ + ret = subsys_virtual_register(&tdx_subsys, NULL); + if (ret) + goto err_free_tdxmem; + /* Allocate enough space for constructing TDMRs */ ret = alloc_tdmr_list(&tdx_tdmr_list, &tdmr_sysinfo); if (ret) - goto err_free_tdxmem; + goto err_unregister_subsys; /* Cover all TDX-usable memory regions in TDMRs */ ret = construct_tdmrs(&tdx_memlist, &tdx_tdmr_list, &tdmr_sysinfo); @@ -1149,6 +1203,11 @@ static int init_tdx_module(void) pr_info("%lu KB allocated for PAMT\n", tdmrs_count_pamt_kb(&tdx_tdmr_list)); + /* Register 'tdx_tsm' for driving optional TDX Connect functionality */ + tsm = init_tdx_tsm(); + if (IS_ERR(tsm)) + pr_err("failed to initialize TSM device (%pe)\n", tsm); + out_put_tdxmem: /* * @tdx_memlist is written here and read at memory hotplug time. @@ -1177,6 +1236,8 @@ static int init_tdx_module(void) tdmrs_free_pamt_all(&tdx_tdmr_list); err_free_tdmrs: free_tdmr_list(&tdx_tdmr_list); +err_unregister_subsys: + bus_unregister(&tdx_subsys); err_free_tdxmem: free_tdx_memlist(&tdx_memlist); goto out_put_tdxmem;