@@ -546,6 +546,8 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd);
void iommufd_viommu_destroy(struct iommufd_object *obj);
int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd);
void iommufd_vdevice_destroy(struct iommufd_object *obj);
+int iommufd_vdevice_tsm_bind_ioctl(struct iommufd_ucmd *ucmd);
+int iommufd_vdevice_tsm_guest_request_ioctl(struct iommufd_ucmd *ucmd);
struct iommufd_vdevice {
struct iommufd_object obj;
@@ -553,6 +555,7 @@ struct iommufd_vdevice {
struct iommufd_viommu *viommu;
struct device *dev;
u64 id; /* per-vIOMMU virtual ID */
+ bool tsm_bound;
};
#ifdef CONFIG_IOMMUFD_TEST
@@ -55,6 +55,8 @@ enum {
IOMMUFD_CMD_VIOMMU_ALLOC = 0x90,
IOMMUFD_CMD_VDEVICE_ALLOC = 0x91,
IOMMUFD_CMD_IOAS_CHANGE_PROCESS = 0x92,
+ IOMMUFD_CMD_VDEVICE_TSM_BIND = 0x93,
+ IOMMUFD_CMD_VDEVICE_TSM_GUEST_REQUEST = 0x94,
};
/**
@@ -1015,4 +1017,27 @@ struct iommu_ioas_change_process {
#define IOMMU_IOAS_CHANGE_PROCESS \
_IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_CHANGE_PROCESS)
+struct iommu_vdevice_tsm_bind {
+ __u32 size;
+ __u32 viommu_id;
+ __u32 dev_id;
+ __u32 vdevice_id;
+ __s32 kvmfd;
+ __u32 pad;
+} __packed;
+#define IOMMU_VDEVICE_TSM_BIND _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VDEVICE_TSM_BIND)
+
+struct iommu_vdevice_tsm_guest_request {
+ __u32 size;
+ __u32 viommu_id;
+ __u32 dev_id;
+ __u32 vdevice_id;
+ __u8 *req;
+ __u8 *rsp;
+ __u32 rsp_len;
+ __u32 req_len;
+ __s32 fw_err;
+} __packed;
+#define IOMMU_VDEVICE_TSM_GUEST_REQUEST _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VDEVICE_TSM_GUEST_REQUEST)
+
#endif
@@ -310,6 +310,8 @@ union ucmd_buffer {
struct iommu_vdevice_alloc vdev;
struct iommu_vfio_ioas vfio_ioas;
struct iommu_viommu_alloc viommu;
+ struct iommu_vdevice_tsm_bind bind;
+ struct iommu_vdevice_tsm_guest_request gr;
#ifdef CONFIG_IOMMUFD_TEST
struct iommu_test_cmd test;
#endif
@@ -367,6 +369,10 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = {
__reserved),
IOCTL_OP(IOMMU_VIOMMU_ALLOC, iommufd_viommu_alloc_ioctl,
struct iommu_viommu_alloc, out_viommu_id),
+ IOCTL_OP(IOMMU_VDEVICE_TSM_BIND, iommufd_vdevice_tsm_bind_ioctl,
+ struct iommu_vdevice_tsm_bind, pad),
+ IOCTL_OP(IOMMU_VDEVICE_TSM_GUEST_REQUEST, iommufd_vdevice_tsm_guest_request_ioctl,
+ struct iommu_vdevice_tsm_guest_request, fw_err),
#ifdef CONFIG_IOMMUFD_TEST
IOCTL_OP(IOMMU_TEST_CMD, iommufd_test, struct iommu_test_cmd, last),
#endif
@@ -2,6 +2,7 @@
/* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES
*/
#include "iommufd_private.h"
+#include "linux/tsm.h"
void iommufd_viommu_destroy(struct iommufd_object *obj)
{
@@ -88,6 +89,15 @@ void iommufd_vdevice_destroy(struct iommufd_object *obj)
container_of(obj, struct iommufd_vdevice, obj);
struct iommufd_viommu *viommu = vdev->viommu;
+ if (vdev->tsm_bound) {
+ struct tsm_tdi *tdi = tsm_tdi_get(vdev->dev);
+
+ if (tdi) {
+ tsm_tdi_unbind(tdi);
+ tsm_tdi_put(tdi);
+ }
+ }
+
/* xa_cmpxchg is okay to fail if alloc failed xa_cmpxchg previously */
xa_cmpxchg(&viommu->vdevs, vdev->id, vdev, NULL, GFP_KERNEL);
refcount_dec(&viommu->obj.users);
@@ -155,3 +165,105 @@ int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd)
iommufd_put_object(ucmd->ictx, &viommu->obj);
return rc;
}
+
+int iommufd_vdevice_tsm_bind_ioctl(struct iommufd_ucmd *ucmd)
+{
+ struct iommu_vdevice_tsm_bind *cmd = ucmd->cmd;
+ struct iommufd_viommu *viommu;
+ struct iommufd_vdevice *vdev;
+ struct iommufd_device *idev;
+ struct tsm_tdi *tdi;
+ int rc = 0;
+
+ viommu = iommufd_get_viommu(ucmd, cmd->viommu_id);
+ if (IS_ERR(viommu))
+ return PTR_ERR(viommu);
+
+ idev = iommufd_get_device(ucmd, cmd->dev_id);
+ if (IS_ERR(idev)) {
+ rc = PTR_ERR(idev);
+ goto out_put_viommu;
+ }
+
+ vdev = container_of(iommufd_get_object(ucmd->ictx, cmd->vdevice_id,
+ IOMMUFD_OBJ_VDEVICE),
+ struct iommufd_vdevice, obj);
+ if (IS_ERR(idev)) {
+ rc = PTR_ERR(idev);
+ goto out_put_dev;
+ }
+
+ tdi = tsm_tdi_get(idev->dev);
+ if (!tdi) {
+ rc = -ENODEV;
+ goto out_put_vdev;
+ }
+
+ rc = tsm_tdi_bind(tdi, vdev->id, cmd->kvmfd);
+ if (rc)
+ goto out_put_tdi;
+
+ vdev->tsm_bound = true;
+
+ rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
+out_put_tdi:
+ tsm_tdi_put(tdi);
+out_put_vdev:
+ iommufd_put_object(ucmd->ictx, &vdev->obj);
+out_put_dev:
+ iommufd_put_object(ucmd->ictx, &idev->obj);
+out_put_viommu:
+ iommufd_put_object(ucmd->ictx, &viommu->obj);
+ return rc;
+}
+
+int iommufd_vdevice_tsm_guest_request_ioctl(struct iommufd_ucmd *ucmd)
+{
+ struct iommu_vdevice_tsm_guest_request *cmd = ucmd->cmd;
+ struct iommufd_viommu *viommu;
+ struct iommufd_vdevice *vdev;
+ struct iommufd_device *idev;
+ struct tsm_tdi *tdi;
+ int rc = 0, fw_err = 0;
+
+ viommu = iommufd_get_viommu(ucmd, cmd->viommu_id);
+ if (IS_ERR(viommu))
+ return PTR_ERR(viommu);
+
+ idev = iommufd_get_device(ucmd, cmd->dev_id);
+ if (IS_ERR(idev)) {
+ rc = PTR_ERR(idev);
+ goto out_put_viommu;
+ }
+
+ vdev = container_of(iommufd_get_object(ucmd->ictx, cmd->vdevice_id,
+ IOMMUFD_OBJ_VDEVICE),
+ struct iommufd_vdevice, obj);
+ if (IS_ERR(idev)) {
+ rc = PTR_ERR(idev);
+ goto out_put_dev;
+ }
+
+ tdi = tsm_tdi_get(idev->dev);
+ if (!tdi) {
+ rc = -ENODEV;
+ goto out_put_vdev;
+ }
+
+ rc = tsm_guest_request(tdi, cmd->req, cmd->req_len, cmd->rsp, cmd->rsp_len, &fw_err);
+ if (rc)
+ goto out_put_tdi;
+
+ cmd->fw_err = fw_err;
+ rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
+
+out_put_tdi:
+ tsm_tdi_put(tdi);
+out_put_vdev:
+ iommufd_put_object(ucmd->ictx, &vdev->obj);
+out_put_dev:
+ iommufd_put_object(ucmd->ictx, &idev->obj);
+out_put_viommu:
+ iommufd_put_object(ucmd->ictx, &viommu->obj);
+ return rc;
+}
When a TDISP-capable device is passed through, it is configured as a shared device to begin with. Later on when a VM probes the device, detects its TDISP capability (reported via the PCIe ExtCap bit called "TEE-IO"), performs the device attestation and transitions it to a secure state when the device can run encrypted DMA and respond to encrypted MMIO accesses. Since KVM is out of the TCB, secure enablement is done in the secure firmware. The API requires PCI host/guest BDFns, a KVM id hence such calls are routed via IOMMUFD, primarily because allowing secure DMA is the major performance bottleneck and it is a function of IOMMU. Add TDI bind to do the initial binding of a passed through PCI function to a VM. Add a forwarder for TIO GUEST REQUEST. These two call into the TSM which forwards the calls to the PSP. Signed-off-by: Alexey Kardashevskiy <aik@amd.com> --- Both enabling secure DMA (== "SDTE Write") and secure MMIO (== "MMIO validate") are TIO GUEST REQUEST messages. These are encrypted and the HV (==IOMMUFD or KVM or VFIO) cannot see them unless the guest shares some via kvm_run::kvm_user_vmgexit (and then QEMU passes those via ioctls). This RFC routes all TIO GUEST REQUESTs via IOMMUFD which arguably should only do so only for "SDTE Write" and leave "MMIO validate" for VFIO. --- drivers/iommu/iommufd/iommufd_private.h | 3 + include/uapi/linux/iommufd.h | 25 +++++ drivers/iommu/iommufd/main.c | 6 ++ drivers/iommu/iommufd/viommu.c | 112 ++++++++++++++++++++ 4 files changed, 146 insertions(+)