TDX module updates require userspace to select the appropriate module to load. Expose necessary information to facilitate this decision. Two values are needed: - P-SEAMLDR version: for compatibility checks between TDX module and P-SEAMLDR - num_remaining_updates: indicates how many updates can be performed Expose them as tdx-host device attributes. Register these attributes during device probe rather than creation, making it easier to hide them when P-SEAMLDR calls are unsafe (due to CPU erratum). Signed-off-by: Chao Gao --- v5: - fix typos [Binbin] - register seamldr_group during device probe v4: - Make seamldr attribute permission "0400" [Dave] - Don't include implementation details in OS ABI docs [Dave] - Tag tdx_host_group as static [Kai] v3: - use #ifdef rather than .is_visible() to control P-SEAMLDR sysfs visibility [Yilun] --- .../ABI/testing/sysfs-devices-faux-tdx-host | 23 +++++++ drivers/virt/coco/tdx-host/tdx-host.c | 68 ++++++++++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-devices-faux-tdx-host b/Documentation/ABI/testing/sysfs-devices-faux-tdx-host index 2cf682b65acf..44b8356aed6b 100644 --- a/Documentation/ABI/testing/sysfs-devices-faux-tdx-host +++ b/Documentation/ABI/testing/sysfs-devices-faux-tdx-host @@ -4,3 +4,26 @@ Description: (RO) Report the version of the loaded TDX module. The TDX module version is formatted as x.y.z, where "x" is the major version, "y" is the minor version and "z" is the update version. Versions are used for bug reporting, TDX module updates etc. + +What: /sys/devices/faux/tdx_host/seamldr/version +Contact: linux-coco@lists.linux.dev +Description: (RO) Report the version of the loaded SEAM loader. The SEAM + loader version is formatted as x.y.z, where "x" is the major + version, "y" is the minor version and "z" is the update version. + Versions are used for bug reporting and compatibility checks. + +What: /sys/devices/faux/tdx_host/seamldr/num_remaining_updates +Contact: linux-coco@lists.linux.dev +Description: (RO) Report the number of remaining updates. TDX maintains a + log about each TDX module that has been loaded. This log has + a finite size, which limits the number of TDX module updates + that can be performed. + + After each successful update, the number reduces by one. Once it + reaches zero, further updates will fail until next reboot. The + number is always zero if the P-SEAMLDR doesn't support updates. + + See IntelĀ® Trust Domain Extensions - SEAM Loader (SEAMLDR) + Interface Specification, Revision 343755-003, Chapter 3.3 + "SEAMLDR_INFO" and Chapter 4.2 "SEAMLDR.INSTALL" for more + information. diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c index 0424933b2560..8d46e3c039ba 100644 --- a/drivers/virt/coco/tdx-host/tdx-host.c +++ b/drivers/virt/coco/tdx-host/tdx-host.c @@ -11,6 +11,7 @@ #include #include +#include #include static const struct x86_cpu_id tdx_host_ids[] = { @@ -42,6 +43,71 @@ static struct attribute *tdx_host_attrs[] = { }; ATTRIBUTE_GROUPS(tdx_host); +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, "%u.%u.%02u\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); +} + +/* + * Open-code DEVICE_ATTR_ADMIN_RO to specify a different 'show' function + * for P-SEAMLDR version as version_show() is used for TDX module version. + * + * Admin-only readable as reading these attributes calls into P-SEAMLDR, + * which may have potential performance and system impact. + */ +static struct device_attribute dev_attr_seamldr_version = + __ATTR(version, 0400, seamldr_version_show, NULL); +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 const struct attribute_group seamldr_group = { + .name = "seamldr", + .attrs = seamldr_attrs, +}; + +static int seamldr_init(struct device *dev) +{ + return devm_device_add_group(dev, &seamldr_group); +} + +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) @@ -49,7 +115,7 @@ 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, NULL, tdx_host_groups); + fdev = faux_device_create_with_groups(KBUILD_MODNAME, NULL, &tdx_host_ops, tdx_host_groups); if (!fdev) return -ENODEV; -- 2.47.3