The "coco/tsm: Add tsm and tsm-host modules" added a common TSM library and the host module which initialises contexts for secure devices on the host. Guests use some of the host sysfs interface and define their own, hence a new module - TSM GUEST. Note that the module is made bus-agnostic, like TSM-HOST. New device nodes provide sysfs interface for fetching device certificates and measurements and TDI interface reports. A platform is expected to register itself in TSM-GUEST and provide necessary callbacks. No platform is added here, AMD SEV is coming in the next patches. Signed-off-by: Alexey Kardashevskiy <aik@xxxxxxx> --- drivers/virt/coco/guest/Makefile | 3 + include/linux/device.h | 4 + include/linux/tsm.h | 20 ++ drivers/virt/coco/guest/tsm-guest.c | 291 ++++++++++++++++++++ drivers/virt/coco/host/tsm-host.c | 1 + drivers/virt/coco/tsm.c | 2 + Documentation/virt/coco/tsm.rst | 33 +++ drivers/virt/coco/guest/Kconfig | 3 + drivers/virt/coco/sev-guest/Kconfig | 1 + 9 files changed, 358 insertions(+) diff --git a/drivers/virt/coco/guest/Makefile b/drivers/virt/coco/guest/Makefile index b3b217af77cf..60b688ab816a 100644 --- a/drivers/virt/coco/guest/Makefile +++ b/drivers/virt/coco/guest/Makefile @@ -1,3 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TSM_REPORTS) += tsm_report.o tsm_report-y := report.o + +obj-$(CONFIG_TSM_GUEST) += tsm_guest.o +tsm_guest-y := tsm-guest.o diff --git a/include/linux/device.h b/include/linux/device.h index 80a5b3268986..e813575b848b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -843,6 +843,10 @@ struct device { #ifdef CONFIG_IOMMU_DMA bool dma_iommu:1; #endif +#if defined(CONFIG_TSM_GUEST) || defined(CONFIG_TSM_GUEST_MODULE) + bool tdi_enabled:1; + bool tdi_validated:1; +#endif }; /** diff --git a/include/linux/tsm.h b/include/linux/tsm.h index 486e386d90fc..9e25b1a99c19 100644 --- a/include/linux/tsm.h +++ b/include/linux/tsm.h @@ -353,6 +353,20 @@ struct tsm_hv_ops { int (*tdi_status)(struct tsm_tdi *tdi, struct tsm_tdi_status *ts); }; +/* featuremask for tdi_validate */ +/* TODO: use it */ +#define TDI_VALIDATE_DMA BIT(0) +#define TDI_VALIDATE_MMIO BIT(1) + +struct tsm_vm_ops { + int (*tdi_validate)(struct tsm_tdi *tdi, unsigned int featuremask, + bool invalidate, void *private_data); + int (*tdi_mmio_config)(struct tsm_tdi *tdi, u64 start, u64 size, + bool tee, void *private_data); + int (*tdi_status)(struct tsm_tdi *tdi, void *private_data, + struct tsm_tdi_status *ts); +}; + struct tsm_subsys { struct device dev; struct list_head tdi_head; @@ -372,6 +386,12 @@ struct tsm_host_subsys; struct tsm_host_subsys *tsm_host_register(struct device *parent, struct tsm_hv_ops *hvops, void *private_data); +struct tsm_guest_subsys; +struct tsm_guest_subsys *tsm_guest_register(struct device *parent, + struct tsm_vm_ops *vmops, + void *private_data); +void tsm_guest_unregister(struct tsm_guest_subsys *gsubsys); + struct tsm_dev *tsm_dev_get(struct device *dev); void tsm_dev_put(struct tsm_dev *tdev); struct tsm_tdi *tsm_tdi_get(struct device *dev); diff --git a/drivers/virt/coco/guest/tsm-guest.c b/drivers/virt/coco/guest/tsm-guest.c new file mode 100644 index 000000000000..d3be089308e0 --- /dev/null +++ b/drivers/virt/coco/guest/tsm-guest.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/module.h> +#include <linux/tsm.h> + +#define DRIVER_VERSION "0.1" +#define DRIVER_AUTHOR "aik@xxxxxxx" +#define DRIVER_DESC "TSM guest library" + +struct tsm_guest_subsys { + struct tsm_subsys base; + struct tsm_vm_ops *ops; + void *private_data; + struct notifier_block notifier; +}; + +static int tsm_tdi_measurements_locked(struct tsm_dev *tdev) +{ + struct tsm_guest_subsys *gsubsys = (struct tsm_guest_subsys *) tdev->tsm; + struct tsm_tdi_status tstmp = { 0 }; + struct tsm_tdi *tdi = tsm_tdi_get(tdev->physdev); + + if (!tdi) + return -EFAULT; + + return gsubsys->ops->tdi_status(tdi, gsubsys->private_data, &tstmp); +} + +static int tsm_tdi_validate(struct tsm_tdi *tdi, unsigned int featuremask, bool invalidate) +{ + struct tsm_dev *tdev = tdi->tdev; + struct tsm_guest_subsys *gsubsys = (struct tsm_guest_subsys *) tdev->tsm; + int ret; + + if (!tdi || !gsubsys->ops->tdi_validate) + return -EPERM; + + ret = gsubsys->ops->tdi_validate(tdi, featuremask, invalidate, gsubsys->private_data); + if (ret) { + tdi->dev.parent->tdi_validated = false; + dev_err(tdi->dev.parent, "TDI is not validated, ret=%d\n", ret); + } else { + tdi->dev.parent->tdi_validated = true; + dev_info(tdi->dev.parent, "TDI validated\n"); + } + + return ret; +} + +//int tsm_tdi_mmio_config(struct tsm_tdi *tdi, u64 start, u64 end, bool tee) +//{ +// struct tsm_dev *tdev = tdi->tdev; +// struct tsm_guest_subsys *gsubsys = (struct tsm_guest_subsys *) tdev->tsm; +// int ret; +// +// if (!tdi || !gsubsys->ops->tdi_mmio_config) +// return -EPERM; +// +// ret = gsubsys->ops->tdi_mmio_config(tdi, start, end, tee, gsubsys->private_data); +// +// return ret; +//} +//EXPORT_SYMBOL_GPL(tsm_tdi_mmio_config); + +static int tsm_tdi_status(struct tsm_tdi *tdi, void *private_data, struct tsm_tdi_status *ts) +{ + struct tsm_tdi_status tstmp = { 0 }; + struct tsm_dev *tdev = tdi->tdev; + struct tsm_guest_subsys *gsubsys = (struct tsm_guest_subsys *) tdev->tsm; + int ret; + + ret = gsubsys->ops->tdi_status(tdi, private_data, &tstmp); + if (!ret) + *ts = tstmp; + + return ret; +} + +static ssize_t tsm_tdi_validate_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct tsm_tdi *tdi = container_of(dev, struct tsm_tdi, dev); + unsigned long val; + ssize_t ret; + + if (kstrtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val) { + ret = tsm_tdi_validate(tdi, TDI_VALIDATE_DMA | TDI_VALIDATE_MMIO, false); + if (ret) + return ret; + } else { + tsm_tdi_validate(tdi, TDI_VALIDATE_DMA | TDI_VALIDATE_MMIO, true); + } + + tdi->validated = val; + + return count; +} + +static ssize_t tsm_tdi_validate_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct tsm_tdi *tdi = container_of(dev, struct tsm_tdi, dev); + + return sysfs_emit(buf, "%u\n", tdi->validated); +} + +static DEVICE_ATTR_RW(tsm_tdi_validate); + +static char *spdm_algos_to_str(u64 algos, char *buf, size_t len) +{ + size_t n = 0; + + buf[0] = 0; +#define __ALGO(x) do { \ + if ((n < len) && (algos & (1ULL << (TSM_SPDM_ALGOS_##x)))) \ + n += snprintf(buf + n, len - n, #x" "); \ + } while (0) + + __ALGO(DHE_SECP256R1); + __ALGO(DHE_SECP384R1); + __ALGO(AEAD_AES_128_GCM); + __ALGO(AEAD_AES_256_GCM); + __ALGO(ASYM_TPM_ALG_RSASSA_3072); + __ALGO(ASYM_TPM_ALG_ECDSA_ECC_NIST_P256); + __ALGO(ASYM_TPM_ALG_ECDSA_ECC_NIST_P384); + __ALGO(HASH_TPM_ALG_SHA_256); + __ALGO(HASH_TPM_ALG_SHA_384); + __ALGO(KEY_SCHED_SPDM_KEY_SCHEDULE); +#undef __ALGO + return buf; +} + +static const char *tdisp_state_to_str(enum tsm_tdisp_state state) +{ + switch (state) { +#define __ST(x) case TDISP_STATE_##x: return #x + __ST(CONFIG_UNLOCKED); + __ST(CONFIG_LOCKED); + __ST(RUN); + __ST(ERROR); +#undef __ST + default: return "unknown"; + } +} + +static ssize_t tsm_tdi_status_user_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tsm_tdi *tdi = container_of(dev, struct tsm_tdi, dev); + struct tsm_dev *tdev = tdi->tdev; + struct tsm_guest_subsys *gsubsys = (struct tsm_guest_subsys *) tdev->tsm; + struct tsm_tdi_status ts = { 0 }; + char algos[256] = ""; + unsigned int n, m; + int ret; + + ret = tsm_tdi_status(tdi, gsubsys->private_data, &ts); + if (ret < 0) + return sysfs_emit(buf, "ret=%d\n\n", ret); + + if (!ts.valid) + return sysfs_emit(buf, "ret=%d\nstate=%d:%s\n", + ret, ts.state, tdisp_state_to_str(ts.state)); + + n = snprintf(buf, PAGE_SIZE, + "ret=%d\n" + "state=%d:%s\n" + "meas_digest_fresh=%x\n" + "meas_digest_valid=%x\n" + "all_request_redirect=%x\n" + "bind_p2p=%x\n" + "lock_msix=%x\n" + "no_fw_update=%x\n" + "cache_line_size=%d\n" + "algos=%#llx:%s\n" + "report_counter=%lld\n" + , + ret, + ts.state, tdisp_state_to_str(ts.state), + ts.meas_digest_fresh, + ts.meas_digest_valid, + ts.all_request_redirect, + ts.bind_p2p, + ts.lock_msix, + ts.no_fw_update, + ts.cache_line_size, + ts.spdm_algos, spdm_algos_to_str(ts.spdm_algos, algos, sizeof(algos) - 1), + ts.intf_report_counter); + + n += snprintf(buf + n, PAGE_SIZE - n, "Certs digest: "); + m = hex_dump_to_buffer(ts.certs_digest, sizeof(ts.certs_digest), 32, 1, + buf + n, PAGE_SIZE - n, false); + n += min(PAGE_SIZE - n, m); + n += snprintf(buf + n, PAGE_SIZE - n, "...\nMeasurements digest: "); + m = hex_dump_to_buffer(ts.meas_digest, sizeof(ts.meas_digest), 32, 1, + buf + n, PAGE_SIZE - n, false); + n += min(PAGE_SIZE - n, m); + n += snprintf(buf + n, PAGE_SIZE - n, "...\nInterface report digest: "); + m = hex_dump_to_buffer(ts.interface_report_digest, sizeof(ts.interface_report_digest), + 32, 1, buf + n, PAGE_SIZE - n, false); + n += min(PAGE_SIZE - n, m); + n += snprintf(buf + n, PAGE_SIZE - n, "...\n"); + + return n; +} + +static DEVICE_ATTR_RO(tsm_tdi_status_user); + +static ssize_t tsm_tdi_status_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct tsm_tdi *tdi = container_of(dev, struct tsm_tdi, dev); + struct tsm_dev *tdev = tdi->tdev; + struct tsm_guest_subsys *gsubsys = (struct tsm_guest_subsys *) tdev->tsm; + struct tsm_tdi_status ts = { 0 }; + u8 state; + int ret; + + ret = tsm_tdi_status(tdi, gsubsys->private_data, &ts); + if (ret) + return ret; + + state = ts.state; + memcpy(buf, &state, sizeof(state)); + + return sizeof(state); +} + +static DEVICE_ATTR_RO(tsm_tdi_status); + +static struct attribute *tdi_attrs[] = { + &dev_attr_tsm_tdi_validate.attr, + &dev_attr_tsm_tdi_status_user.attr, + &dev_attr_tsm_tdi_status.attr, + NULL, +}; + +static const struct attribute_group tdi_group = { + .attrs = tdi_attrs, +}; + +struct tsm_guest_subsys *tsm_guest_register(struct device *parent, + struct tsm_vm_ops *vmops, + void *private_data) +{ + struct tsm_subsys *subsys = tsm_register(parent, sizeof(struct tsm_guest_subsys), + NULL, &tdi_group, + tsm_tdi_measurements_locked); + struct tsm_guest_subsys *gsubsys; + + gsubsys = (struct tsm_guest_subsys *) subsys; + + if (IS_ERR(gsubsys)) + return gsubsys; + + gsubsys->ops = vmops; + gsubsys->private_data = private_data; + + return gsubsys; +} +EXPORT_SYMBOL_GPL(tsm_guest_register); + +void tsm_guest_unregister(struct tsm_guest_subsys *gsubsys) +{ + tsm_unregister(&gsubsys->base); +} +EXPORT_SYMBOL_GPL(tsm_guest_unregister); + +static int __init tsm_init(void) +{ + int ret = 0; + + pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); + + return ret; +} + +static void __exit tsm_exit(void) +{ + pr_info(DRIVER_DESC " version: " DRIVER_VERSION " shutdown\n"); +} + +module_init(tsm_init); +module_exit(tsm_exit); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/virt/coco/host/tsm-host.c b/drivers/virt/coco/host/tsm-host.c index 5d23a3871009..7a37327fded8 100644 --- a/drivers/virt/coco/host/tsm-host.c +++ b/drivers/virt/coco/host/tsm-host.c @@ -474,6 +474,7 @@ void tsm_tdi_unbind(struct tsm_tdi *tdi) } tdi->guest_rid = 0; + tdi->dev.parent->tdi_enabled = false; } EXPORT_SYMBOL_GPL(tsm_tdi_unbind); diff --git a/drivers/virt/coco/tsm.c b/drivers/virt/coco/tsm.c index b6235d1210ca..a6979d51f029 100644 --- a/drivers/virt/coco/tsm.c +++ b/drivers/virt/coco/tsm.c @@ -442,6 +442,8 @@ int tsm_tdi_init(struct tsm_dev *tdev, struct device *parent) goto free_exit; tdi->tdev = tdev; + tdi->dev.parent->tdi_enabled = true; + tdi->dev.parent->tdi_validated = false; return 0; diff --git a/Documentation/virt/coco/tsm.rst b/Documentation/virt/coco/tsm.rst index 7cb5f1862492..203cc9c8411d 100644 --- a/Documentation/virt/coco/tsm.rst +++ b/Documentation/virt/coco/tsm.rst @@ -90,6 +90,39 @@ The sysfs example from a host with a TDISP capable device: /sys/devices/pci0000:a0/0000:a0:07.1/0000:a9:00.5/tsm/tsm0 +The sysfs example from a guest with a TDISP capable device: + +~> find /sys -iname "*tsm*" +/sys/kernel/config/tsm +/sys/class/tsm-tdi +/sys/class/tsm +/sys/class/tsm/tsm0 +/sys/class/tsm-dev +/sys/devices/platform/sev-guest/tsm +/sys/devices/platform/sev-guest/tsm/tsm0 +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm_dev +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm-tdi +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm-tdi/tdi:0000:01:00.0/tsm_tdi_status +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm-tdi/tdi:0000:01:00.0/tsm_tdi_validate +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm-tdi/tdi:0000:01:00.0/tsm_tdi_status_user +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm-tdi/tdi:0000:01:00.0/tsm_report_user +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm-tdi/tdi:0000:01:00.0/tsm_report +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm-dev +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm-dev/tdev:0000:01:00.0/tsm_certs +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm-dev/tdev:0000:01:00.0/tsm_nonce +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm-dev/tdev:0000:01:00.0/tsm_meas_user +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm-dev/tdev:0000:01:00.0/tsm_certs_user +/sys/devices/pci0000:00/0000:00:02.0/0000:01:00.0/tsm-dev/tdev:0000:01:00.0/tsm_meas +/sys/module/tsm_pci +/sys/module/sev_guest/parameters/tsm_vtom +/sys/module/sev_guest/parameters/tsm_enable +/sys/module/tsm_report +/sys/module/tsm +/sys/module/tsm/holders/tsm_pci +/sys/module/tsm/holders/tsm_guest +/sys/module/tsm_guest + + References ========== diff --git a/drivers/virt/coco/guest/Kconfig b/drivers/virt/coco/guest/Kconfig index ed9bafbdd854..30f0235f0113 100644 --- a/drivers/virt/coco/guest/Kconfig +++ b/drivers/virt/coco/guest/Kconfig @@ -5,3 +5,6 @@ config TSM_REPORTS select CONFIGFS_FS tristate + +config TSM_GUEST + tristate diff --git a/drivers/virt/coco/sev-guest/Kconfig b/drivers/virt/coco/sev-guest/Kconfig index a6405ab6c2c3..148af36772ff 100644 --- a/drivers/virt/coco/sev-guest/Kconfig +++ b/drivers/virt/coco/sev-guest/Kconfig @@ -3,6 +3,7 @@ config SEV_GUEST default m depends on AMD_MEM_ENCRYPT select TSM_REPORTS + select TSM_GUEST help SEV-SNP firmware provides the guest a mechanism to communicate with the PSP without risk from a malicious hypervisor who wishes to read, -- 2.47.1