Add IOMMU_VDEVICE_TSM_GUEST_REQUEST for issuing TSM guest request/response transactions against an iommufd vdevice. The ioctl takes a vdevice_id plus request/response user buffers and length fields, and forwards the request through tsm_guest_req() to the PCI TSM backend. This provides the host-side passthrough path used by CoCo guests for TSM device attestation and acceptance flows after the device has been bound to TSM. Also add the supporting tsm_guest_req() helper and associated TSM core interface definitions. Based on changes from: Alexey Kardashevskiy Cc: Kevin Tian Cc: Joerg Roedel Cc: Will Deacon Cc: Bjorn Helgaas Cc: Jonathan Cameron Cc: Dan Williams Cc: Alexey Kardashevskiy Cc: Samuel Ortiz Cc: Xu Yilun Cc: Jason Gunthorpe Cc: Suzuki K Poulose Cc: Steven Price Signed-off-by: Aneesh Kumar K.V (Arm) --- drivers/iommu/iommufd/iommufd_private.h | 7 ++++ drivers/iommu/iommufd/main.c | 3 ++ drivers/iommu/iommufd/tsm.c | 48 +++++++++++++++++++++++++ drivers/virt/coco/tsm-core.c | 14 ++++++++ include/linux/tsm.h | 26 ++++++++++++++ include/uapi/linux/iommufd.h | 23 ++++++++++++ 6 files changed, 121 insertions(+) diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h index aa1ceed924c2..7042993913dd 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -699,11 +699,18 @@ int iommufd_hw_queue_alloc_ioctl(struct iommufd_ucmd *ucmd); void iommufd_hw_queue_destroy(struct iommufd_object *obj); #ifdef CONFIG_TSM int iommufd_vdevice_tsm_op_ioctl(struct iommufd_ucmd *ucmd); +int iommufd_vdevice_tsm_guest_request_ioctl(struct iommufd_ucmd *ucmd); #else static inline int iommufd_vdevice_tsm_op_ioctl(struct iommufd_ucmd *ucmd) { return -EOPNOTSUPP; } + +static inline int +iommufd_vdevice_tsm_guest_request_ioctl(struct iommufd_ucmd *ucmd) +{ + return -EOPNOTSUPP; +} #endif static inline struct iommufd_vdevice * diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c index d73e6b391c6f..7f4bbdd0bda5 100644 --- a/drivers/iommu/iommufd/main.c +++ b/drivers/iommu/iommufd/main.c @@ -433,6 +433,7 @@ union ucmd_buffer { struct iommu_vfio_ioas vfio_ioas; struct iommu_viommu_alloc viommu; struct iommu_vdevice_tsm_op tsm_op; + struct iommu_vdevice_tsm_guest_request gr; #ifdef CONFIG_IOMMUFD_TEST struct iommu_test_cmd test; #endif @@ -496,6 +497,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = { struct iommu_viommu_alloc, out_viommu_id), IOCTL_OP(IOMMU_VDEVICE_TSM_OP, iommufd_vdevice_tsm_op_ioctl, struct iommu_vdevice_tsm_op, vdevice_id), + IOCTL_OP(IOMMU_VDEVICE_TSM_GUEST_REQUEST, iommufd_vdevice_tsm_guest_request_ioctl, + struct iommu_vdevice_tsm_guest_request, resp_uptr), #ifdef CONFIG_IOMMUFD_TEST IOCTL_OP(IOMMU_TEST_CMD, iommufd_test, struct iommu_test_cmd, last), #endif diff --git a/drivers/iommu/iommufd/tsm.c b/drivers/iommu/iommufd/tsm.c index 401469110752..6b96d0aef25f 100644 --- a/drivers/iommu/iommufd/tsm.c +++ b/drivers/iommu/iommufd/tsm.c @@ -65,3 +65,51 @@ int iommufd_vdevice_tsm_op_ioctl(struct iommufd_ucmd *ucmd) iommufd_put_object(ucmd->ictx, &vdev->obj); return rc; } + +/** + * iommufd_vdevice_tsm_guest_request_ioctl - Forward guest TSM requests + * @ucmd: user command data for IOMMU_VDEVICE_TSM_GUEST_REQUEST + * + * Resolve @iommu_vdevice_tsm_guest_request::vdevice_id to a vdevice and pass + * the request/response buffers to the TSM core. + * + * Return: + * -errno on error. + * positive residue if response/request bytes were left unconsumed. + * if response buffer is provided, residue indicates the number of bytes + * not used in response buffer + * if there is no response buffer, residue indicates the number of bytes + * not consumed in req buffer + * 0 otherwise. + */ +int iommufd_vdevice_tsm_guest_request_ioctl(struct iommufd_ucmd *ucmd) +{ + int rc; + struct iommufd_vdevice *vdev; + struct iommu_vdevice_tsm_guest_request *cmd = ucmd->cmd; + struct tsm_guest_req_info info = { + .scope = cmd->scope, + .req = { + .user = u64_to_user_ptr(cmd->req_uptr), + .is_kernel = false, + }, + .req_len = cmd->req_len, + .resp = { + .user = u64_to_user_ptr(cmd->resp_uptr), + .is_kernel = false, + }, + .resp_len = cmd->resp_len, + }; + + vdev = container_of(iommufd_get_object(ucmd->ictx, cmd->vdevice_id, + IOMMUFD_OBJ_VDEVICE), + struct iommufd_vdevice, obj); + if (IS_ERR(vdev)) + return PTR_ERR(vdev); + + rc = tsm_guest_req(vdev->idev->dev, &info); + + /* No inline response, hence we don't need to copy the response */ + iommufd_put_object(ucmd->ictx, &vdev->obj); + return rc; +} diff --git a/drivers/virt/coco/tsm-core.c b/drivers/virt/coco/tsm-core.c index f0e35fc38776..317fcb53e4bf 100644 --- a/drivers/virt/coco/tsm-core.c +++ b/drivers/virt/coco/tsm-core.c @@ -259,6 +259,20 @@ int tsm_unbind(struct device *dev) } EXPORT_SYMBOL_GPL(tsm_unbind); +ssize_t tsm_guest_req(struct device *dev, struct tsm_guest_req_info *info) +{ + ssize_t ret; + + if (!dev_is_pci(dev)) + return -EINVAL; + + ret = pci_tsm_guest_req(to_pci_dev(dev), info->scope, + info->req, info->req_len, + info->resp, info->resp_len, NULL); + return ret; +} +EXPORT_SYMBOL_GPL(tsm_guest_req); + static void tsm_release(struct device *dev) { struct tsm_dev *tsm_dev = container_of(dev, typeof(*tsm_dev), dev); diff --git a/include/linux/tsm.h b/include/linux/tsm.h index 9f2a7868021a..5231d5abf84d 100644 --- a/include/linux/tsm.h +++ b/include/linux/tsm.h @@ -6,6 +6,7 @@ #include #include #include +#include #define TSM_REPORT_INBLOB_MAX 64 #define TSM_REPORT_OUTBLOB_MAX SZ_16M @@ -131,6 +132,24 @@ void tsm_ide_stream_unregister(struct pci_ide *ide); int tsm_bind(struct device *dev, struct kvm *kvm, u64 tdi_id); int tsm_unbind(struct device *dev); +/** + * struct tsm_guest_req_info - parameter for tsm_guest_req() + * @scope: scope for tsm guest request + * @req: request data buffer filled by guest + * @req_len: the size of @req filled by guest + * @resp: response data buffer filled by host + * @resp_len: for input, the size of @resp buffer filled by guest + * for output, the size of actual response data filled by host + */ +struct tsm_guest_req_info { + u32 scope; + sockptr_t req; + size_t req_len; + sockptr_t resp; + size_t resp_len; +}; +ssize_t tsm_guest_req(struct device *dev, struct tsm_guest_req_info *info); + #else static inline int tsm_bind(struct device *dev, struct kvm *kvm, u64 tdi_id) @@ -142,6 +161,13 @@ static inline int tsm_unbind(struct device *dev) { return 0; } + +struct tsm_guest_req_info; +static inline ssize_t tsm_guest_req(struct device *dev, + struct tsm_guest_req_info *info) +{ + return -EINVAL; +} #endif #endif /* __TSM_H */ diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h index 653402e7048a..b0d1ecd81638 100644 --- a/include/uapi/linux/iommufd.h +++ b/include/uapi/linux/iommufd.h @@ -58,6 +58,7 @@ enum { IOMMUFD_CMD_VEVENTQ_ALLOC = 0x93, IOMMUFD_CMD_HW_QUEUE_ALLOC = 0x94, IOMMUFD_CMD_VDEVICE_TSM_OP = 0x95, + IOMMUFD_CMD_VDEVICE_TSM_GUEST_REQUEST = 0x96, }; /** @@ -1367,4 +1368,26 @@ struct iommu_hw_queue_alloc { __aligned_u64 length; }; #define IOMMU_HW_QUEUE_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HW_QUEUE_ALLOC) + +/** + * struct iommu_vdevice_tsm_guest_request - ioctl(IOMMU_VDEVICE_TSM_GUEST_REQUEST) + * @size: sizeof(struct iommu_vdevice_tsm_guest_request) + * @vdevice_id: vDevice ID the guest request is for + * @scope: scope of tsm guest request + * @req_len: the blob size for @req_uptr, filled by guest + * @resp_len: the blob size for @resp_uptr, filled by guest + * @req_uptr: request data buffer filled by guest + * @resp_uptr: response data buffer + */ +struct iommu_vdevice_tsm_guest_request { + __u32 size; + __u32 vdevice_id; + __u32 scope; + __u32 req_len; + __u32 resp_len; + __aligned_u64 req_uptr; + __aligned_u64 resp_uptr; +}; +#define IOMMU_VDEVICE_TSM_GUEST_REQUEST _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VDEVICE_TSM_GUEST_REQUEST) + #endif -- 2.43.0