diff options
Diffstat (limited to 'drivers/virt/coco/tdx-host')
| -rw-r--r-- | drivers/virt/coco/tdx-host/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/virt/coco/tdx-host/Makefile | 1 | ||||
| -rw-r--r-- | drivers/virt/coco/tdx-host/tdx-host.c | 239 |
3 files changed, 246 insertions, 0 deletions
diff --git a/drivers/virt/coco/tdx-host/Kconfig b/drivers/virt/coco/tdx-host/Kconfig new file mode 100644 index 000000000..57d0c01a4 --- /dev/null +++ b/drivers/virt/coco/tdx-host/Kconfig @@ -0,0 +1,6 @@ +config TDX_HOST_SERVICES + tristate + depends on INTEL_TDX_HOST + select FW_LOADER + select FW_UPLOAD + default m diff --git a/drivers/virt/coco/tdx-host/Makefile b/drivers/virt/coco/tdx-host/Makefile new file mode 100644 index 000000000..e61e749a8 --- /dev/null +++ b/drivers/virt/coco/tdx-host/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TDX_HOST_SERVICES) += tdx-host.o diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c new file mode 100644 index 000000000..d48952968 --- /dev/null +++ b/drivers/virt/coco/tdx-host/tdx-host.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TDX host user interface driver + * + * Copyright (C) 2025 Intel Corporation + */ + +#include <linux/device/faux.h> +#include <linux/firmware.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/sysfs.h> + +#include <asm/cpu_device_id.h> +#include <asm/seamldr.h> +#include <asm/tdx.h> + +static const struct x86_cpu_id tdx_host_ids[] = { + X86_MATCH_FEATURE(X86_FEATURE_TDX_HOST_PLATFORM, NULL), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, tdx_host_ids); + +static ssize_t version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + const struct tdx_sys_info *tdx_sysinfo = tdx_get_sysinfo(); + const struct tdx_sys_info_version *ver; + int ret; + + if (!tdx_sysinfo) + return -ENXIO; + + /* + * The version number can change during an update. + * Lock out updates while printing the version. + */ + seamldr_lock_module_update(); + + ver = &tdx_sysinfo->version; + ret = sysfs_emit(buf, TDX_VERSION_FMT "\n", ver->major_version, + ver->minor_version, + ver->update_version); + seamldr_unlock_module_update(); + + return ret; +} +static DEVICE_ATTR_RO(version); + +static struct attribute *tdx_host_attrs[] = { + &dev_attr_version.attr, + NULL, +}; + +static const struct attribute_group tdx_host_group = { + .attrs = tdx_host_attrs, +}; + +static ssize_t seamldr_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct seamldr_info info; + int ret; + + ret = seamldr_get_info(&info); + if (ret) + return ret; + + return sysfs_emit(buf, TDX_VERSION_FMT "\n", info.major_version, + info.minor_version, + info.update_version); +} + +static ssize_t num_remaining_updates_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct seamldr_info info; + int ret; + + ret = seamldr_get_info(&info); + if (ret) + return ret; + + return sysfs_emit(buf, "%u\n", info.num_remaining_updates); +} + +/* + * These attributes are intended for managing TDX module updates. Reading + * them issues a slow, serialized P-SEAMLDR query, so keep them admin-only. + */ +static DEVICE_ATTR_ADMIN_RO(seamldr_version); +static DEVICE_ATTR_ADMIN_RO(num_remaining_updates); + +static struct attribute *seamldr_attrs[] = { + &dev_attr_seamldr_version.attr, + &dev_attr_num_remaining_updates.attr, + NULL, +}; + +static bool supports_runtime_update(void) +{ + const struct tdx_sys_info *sysinfo = tdx_get_sysinfo(); + + if (!sysinfo) + return false; + + if (!tdx_supports_runtime_update(sysinfo)) + return false; + + /* + * This bug makes P-SEAMLDR calls clobber the current VMCS + * which breaks KVM. Avoid P-SEAMLDR calls by hiding all + * attributes if the CPU has this bug. + */ + if (boot_cpu_has_bug(X86_BUG_SEAMRET_INVD_VMCS)) + return false; + + return true; +} + +static umode_t seamldr_group_visible(struct kobject *kobj, struct attribute *attr, int idx) +{ + if (!supports_runtime_update()) + return 0; + + return attr->mode; +} + +static const struct attribute_group seamldr_group = { + .attrs = seamldr_attrs, + .is_visible = seamldr_group_visible, +}; + +static const struct attribute_group *tdx_host_groups[] = { + &tdx_host_group, + &seamldr_group, + NULL, +}; + +static enum fw_upload_err tdx_fw_prepare(struct fw_upload *fwl, + const u8 *data, u32 data_len) +{ + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err tdx_fw_write(struct fw_upload *fwl, const u8 *data, + u32 offset, u32 data_len, u32 *written) +{ + int ret; + + ret = seamldr_install_module(data, data_len); + switch (ret) { + case 0: + *written = data_len; + return FW_UPLOAD_ERR_NONE; + default: + return FW_UPLOAD_ERR_FW_INVALID; + } +} + +static enum fw_upload_err tdx_fw_poll_complete(struct fw_upload *fwl) +{ + /* + * The upload completed during tdx_fw_write(). + * Never poll for completion. + */ + return FW_UPLOAD_ERR_NONE; +} + +static void tdx_fw_cancel(struct fw_upload *fwl) +{ + /* + * TDX module updates are not cancellable. + * Provide a no-op callback to satisfy fw_upload_ops. + */ +} + +static const struct fw_upload_ops tdx_fw_ops = { + .prepare = tdx_fw_prepare, + .write = tdx_fw_write, + .poll_complete = tdx_fw_poll_complete, + .cancel = tdx_fw_cancel, +}; + +static void seamldr_deinit(void *tdx_fwl) +{ + firmware_upload_unregister(tdx_fwl); +} + +static int seamldr_init(struct device *dev) +{ + struct fw_upload *tdx_fwl; + + if (!supports_runtime_update()) + return 0; + + tdx_fwl = firmware_upload_register(THIS_MODULE, dev, "tdx_module", + &tdx_fw_ops, NULL); + if (IS_ERR(tdx_fwl)) + return PTR_ERR(tdx_fwl); + + return devm_add_action_or_reset(dev, seamldr_deinit, tdx_fwl); +} + +static int tdx_host_probe(struct faux_device *fdev) +{ + return seamldr_init(&fdev->dev); +} + +static const struct faux_device_ops tdx_host_ops = { + .probe = tdx_host_probe, +}; + +static struct faux_device *fdev; + +static int __init tdx_host_init(void) +{ + if (!x86_match_cpu(tdx_host_ids) || !tdx_get_sysinfo()) + return -ENODEV; + + fdev = faux_device_create_with_groups(KBUILD_MODNAME, NULL, + &tdx_host_ops, + tdx_host_groups); + if (!fdev) + return -ENODEV; + + return 0; +} +module_init(tdx_host_init); + +static void __exit tdx_host_exit(void) +{ + faux_device_destroy(fdev); +} +module_exit(tdx_host_exit); + +MODULE_DESCRIPTION("TDX Host Services"); +MODULE_LICENSE("GPL"); |
