Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Merge tag 'pull-vfio-20231219' of https://github.com/legoater/qemu in…
…to staging vfio queue: * Introduce an IOMMU interface backend for VFIO devices * Convert IOMMU type1 and sPAPR IOMMU to respective backends * Introduce a new IOMMUFD backend for ARM, x86_64 and s390x platforms # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmWB34AACgkQUaNDx8/7 # 7KGOMxAAqXegvAneHqIlu4c8TzTuUR2rkYgev9RdfIHRDuY2XtaX14xlWn/rpTXZ # qSgeta+iT8Cv4YV1POJeHWFDNs9E29p1w+R7nLcH1qTIIaZHtxwbVVQ3s7kAo1Vb # 1S1G0/zIznzGVI50a0lj1gO2yQJnu/79nXpnICgA5REW0CscMssnvboQODlwq17V # ZLNVM8CSAvKl6ppkmzRdfNXCfq6x7bf4MsvnuXsqda4TBbvyyTjAqdo/8sjKiGly # gSDQqhgy6cvEXIF0UUHPJzFApf0YdXUDlL8hzH90hvRVu4W/t24dPmT7UkVIX9Ek # TA7RVxv7iJlHtFDqfSTAJFr7nKO9Tm2V9N7xbD1OJUKrMoPZRT6+0R1hMKqsZ5z+ # nG6khqHGzuo/aI9n70YxYIPXt+vs/EHI4WUtslGLUTL0xv8lUzk6cxyIJupFRmDS # ix6GM9TXOV8RyOveL2knHVymlFnAR6dekkMB+6ljUTuzDwG0oco4vno8z9bi7Vct # j36bM56U3lhY+w+Ljoy0gPwgrw/FROnGG3mp1mwp1KRHqtEDnUQu8CaLbJOBsBGE # JJDP6AKAYMczdmYVkd4CvE0WaeSxtOUxW5H5NCPjtaFQt0qEcght2lA2K15g521q # jeojoJ/QK5949jnNCqm1Z66/YQVL79lPyL0E+mxEohwu+yTORk4= # =U0x5 # -----END PGP SIGNATURE----- # gpg: Signature made Tue 19 Dec 2023 13:22:56 EST # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@redhat.com>" [unknown] # gpg: aka "Cédric Le Goater <clg@kaod.org>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: A0F6 6548 F048 95EB FE6B 0B60 51A3 43C7 CFFB ECA1 * tag 'pull-vfio-20231219' of https://github.com/legoater/qemu: (47 commits) hw/ppc/Kconfig: Imply VFIO_PCI docs/devel: Add VFIO iommufd backend documentation vfio: Introduce a helper function to initialize VFIODevice vfio/ccw: Move VFIODevice initializations in vfio_ccw_instance_init vfio/ap: Move VFIODevice initializations in vfio_ap_instance_init vfio/platform: Move VFIODevice initializations in vfio_platform_instance_init vfio/pci: Move VFIODevice initializations in vfio_instance_init hw/i386: Activate IOMMUFD for q35 machines kconfig: Activate IOMMUFD for s390x machines hw/arm: Activate IOMMUFD for virt machines vfio: Make VFIOContainerBase poiner parameter const in VFIOIOMMUOps callbacks vfio/ccw: Make vfio cdev pre-openable by passing a file handle vfio/ccw: Allow the selection of a given iommu backend vfio/ap: Make vfio cdev pre-openable by passing a file handle vfio/ap: Allow the selection of a given iommu backend vfio/platform: Make vfio cdev pre-openable by passing a file handle vfio/platform: Allow the selection of a given iommu backend vfio/pci: Make vfio cdev pre-openable by passing a file handle vfio/pci: Allow the selection of a given iommu backend vfio/iommufd: Enable pci hot reset through iommufd cdev interface ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
- Loading branch information
Showing
33 changed files
with
2,229 additions
and
514 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,5 @@ | ||
| source tpm/Kconfig | ||
|
|
||
| config IOMMUFD | ||
| bool | ||
| depends on VFIO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,245 @@ | ||
| /* | ||
| * iommufd container backend | ||
| * | ||
| * Copyright (C) 2023 Intel Corporation. | ||
| * Copyright Red Hat, Inc. 2023 | ||
| * | ||
| * Authors: Yi Liu <yi.l.liu@intel.com> | ||
| * Eric Auger <eric.auger@redhat.com> | ||
| * | ||
| * SPDX-License-Identifier: GPL-2.0-or-later | ||
| */ | ||
|
|
||
| #include "qemu/osdep.h" | ||
| #include "sysemu/iommufd.h" | ||
| #include "qapi/error.h" | ||
| #include "qapi/qmp/qerror.h" | ||
| #include "qemu/module.h" | ||
| #include "qom/object_interfaces.h" | ||
| #include "qemu/error-report.h" | ||
| #include "monitor/monitor.h" | ||
| #include "trace.h" | ||
| #include <sys/ioctl.h> | ||
| #include <linux/iommufd.h> | ||
|
|
||
| static void iommufd_backend_init(Object *obj) | ||
| { | ||
| IOMMUFDBackend *be = IOMMUFD_BACKEND(obj); | ||
|
|
||
| be->fd = -1; | ||
| be->users = 0; | ||
| be->owned = true; | ||
| qemu_mutex_init(&be->lock); | ||
| } | ||
|
|
||
| static void iommufd_backend_finalize(Object *obj) | ||
| { | ||
| IOMMUFDBackend *be = IOMMUFD_BACKEND(obj); | ||
|
|
||
| if (be->owned) { | ||
| close(be->fd); | ||
| be->fd = -1; | ||
| } | ||
| } | ||
|
|
||
| static void iommufd_backend_set_fd(Object *obj, const char *str, Error **errp) | ||
| { | ||
| IOMMUFDBackend *be = IOMMUFD_BACKEND(obj); | ||
| int fd = -1; | ||
|
|
||
| fd = monitor_fd_param(monitor_cur(), str, errp); | ||
| if (fd == -1) { | ||
| error_prepend(errp, "Could not parse remote object fd %s:", str); | ||
| return; | ||
| } | ||
| qemu_mutex_lock(&be->lock); | ||
| be->fd = fd; | ||
| be->owned = false; | ||
| qemu_mutex_unlock(&be->lock); | ||
| trace_iommu_backend_set_fd(be->fd); | ||
| } | ||
|
|
||
| static bool iommufd_backend_can_be_deleted(UserCreatable *uc) | ||
| { | ||
| IOMMUFDBackend *be = IOMMUFD_BACKEND(uc); | ||
|
|
||
| return !be->users; | ||
| } | ||
|
|
||
| static void iommufd_backend_class_init(ObjectClass *oc, void *data) | ||
| { | ||
| UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); | ||
|
|
||
| ucc->can_be_deleted = iommufd_backend_can_be_deleted; | ||
|
|
||
| object_class_property_add_str(oc, "fd", NULL, iommufd_backend_set_fd); | ||
| } | ||
|
|
||
| int iommufd_backend_connect(IOMMUFDBackend *be, Error **errp) | ||
| { | ||
| int fd, ret = 0; | ||
|
|
||
| qemu_mutex_lock(&be->lock); | ||
| if (be->users == UINT32_MAX) { | ||
| error_setg(errp, "too many connections"); | ||
| ret = -E2BIG; | ||
| goto out; | ||
| } | ||
| if (be->owned && !be->users) { | ||
| fd = qemu_open_old("/dev/iommu", O_RDWR); | ||
| if (fd < 0) { | ||
| error_setg_errno(errp, errno, "/dev/iommu opening failed"); | ||
| ret = fd; | ||
| goto out; | ||
| } | ||
| be->fd = fd; | ||
| } | ||
| be->users++; | ||
| out: | ||
| trace_iommufd_backend_connect(be->fd, be->owned, | ||
| be->users, ret); | ||
| qemu_mutex_unlock(&be->lock); | ||
| return ret; | ||
| } | ||
|
|
||
| void iommufd_backend_disconnect(IOMMUFDBackend *be) | ||
| { | ||
| qemu_mutex_lock(&be->lock); | ||
| if (!be->users) { | ||
| goto out; | ||
| } | ||
| be->users--; | ||
| if (!be->users && be->owned) { | ||
| close(be->fd); | ||
| be->fd = -1; | ||
| } | ||
| out: | ||
| trace_iommufd_backend_disconnect(be->fd, be->users); | ||
| qemu_mutex_unlock(&be->lock); | ||
| } | ||
|
|
||
| int iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id, | ||
| Error **errp) | ||
| { | ||
| int ret, fd = be->fd; | ||
| struct iommu_ioas_alloc alloc_data = { | ||
| .size = sizeof(alloc_data), | ||
| .flags = 0, | ||
| }; | ||
|
|
||
| ret = ioctl(fd, IOMMU_IOAS_ALLOC, &alloc_data); | ||
| if (ret) { | ||
| error_setg_errno(errp, errno, "Failed to allocate ioas"); | ||
| return ret; | ||
| } | ||
|
|
||
| *ioas_id = alloc_data.out_ioas_id; | ||
| trace_iommufd_backend_alloc_ioas(fd, *ioas_id, ret); | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id) | ||
| { | ||
| int ret, fd = be->fd; | ||
| struct iommu_destroy des = { | ||
| .size = sizeof(des), | ||
| .id = id, | ||
| }; | ||
|
|
||
| ret = ioctl(fd, IOMMU_DESTROY, &des); | ||
| trace_iommufd_backend_free_id(fd, id, ret); | ||
| if (ret) { | ||
| error_report("Failed to free id: %u %m", id); | ||
| } | ||
| } | ||
|
|
||
| int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, | ||
| ram_addr_t size, void *vaddr, bool readonly) | ||
| { | ||
| int ret, fd = be->fd; | ||
| struct iommu_ioas_map map = { | ||
| .size = sizeof(map), | ||
| .flags = IOMMU_IOAS_MAP_READABLE | | ||
| IOMMU_IOAS_MAP_FIXED_IOVA, | ||
| .ioas_id = ioas_id, | ||
| .__reserved = 0, | ||
| .user_va = (uintptr_t)vaddr, | ||
| .iova = iova, | ||
| .length = size, | ||
| }; | ||
|
|
||
| if (!readonly) { | ||
| map.flags |= IOMMU_IOAS_MAP_WRITEABLE; | ||
| } | ||
|
|
||
| ret = ioctl(fd, IOMMU_IOAS_MAP, &map); | ||
| trace_iommufd_backend_map_dma(fd, ioas_id, iova, size, | ||
| vaddr, readonly, ret); | ||
| if (ret) { | ||
| ret = -errno; | ||
|
|
||
| /* TODO: Not support mapping hardware PCI BAR region for now. */ | ||
| if (errno == EFAULT) { | ||
| warn_report("IOMMU_IOAS_MAP failed: %m, PCI BAR?"); | ||
| } else { | ||
| error_report("IOMMU_IOAS_MAP failed: %m"); | ||
| } | ||
| } | ||
| return ret; | ||
| } | ||
|
|
||
| int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, | ||
| hwaddr iova, ram_addr_t size) | ||
| { | ||
| int ret, fd = be->fd; | ||
| struct iommu_ioas_unmap unmap = { | ||
| .size = sizeof(unmap), | ||
| .ioas_id = ioas_id, | ||
| .iova = iova, | ||
| .length = size, | ||
| }; | ||
|
|
||
| ret = ioctl(fd, IOMMU_IOAS_UNMAP, &unmap); | ||
| /* | ||
| * IOMMUFD takes mapping as some kind of object, unmapping | ||
| * nonexistent mapping is treated as deleting a nonexistent | ||
| * object and return ENOENT. This is different from legacy | ||
| * backend which allows it. vIOMMU may trigger a lot of | ||
| * redundant unmapping, to avoid flush the log, treat them | ||
| * as succeess for IOMMUFD just like legacy backend. | ||
| */ | ||
| if (ret && errno == ENOENT) { | ||
| trace_iommufd_backend_unmap_dma_non_exist(fd, ioas_id, iova, size, ret); | ||
| ret = 0; | ||
| } else { | ||
| trace_iommufd_backend_unmap_dma(fd, ioas_id, iova, size, ret); | ||
| } | ||
|
|
||
| if (ret) { | ||
| ret = -errno; | ||
| error_report("IOMMU_IOAS_UNMAP failed: %m"); | ||
| } | ||
| return ret; | ||
| } | ||
|
|
||
| static const TypeInfo iommufd_backend_info = { | ||
| .name = TYPE_IOMMUFD_BACKEND, | ||
| .parent = TYPE_OBJECT, | ||
| .instance_size = sizeof(IOMMUFDBackend), | ||
| .instance_init = iommufd_backend_init, | ||
| .instance_finalize = iommufd_backend_finalize, | ||
| .class_size = sizeof(IOMMUFDBackendClass), | ||
| .class_init = iommufd_backend_class_init, | ||
| .interfaces = (InterfaceInfo[]) { | ||
| { TYPE_USER_CREATABLE }, | ||
| { } | ||
| } | ||
| }; | ||
|
|
||
| static void register_types(void) | ||
| { | ||
| type_register_static(&iommufd_backend_info); | ||
| } | ||
|
|
||
| type_init(register_types); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.