xref: /openbmc/qemu/backends/iommufd.c (revision c1cccad8)
16e6d8ac6SEric Auger /*
26e6d8ac6SEric Auger  * iommufd container backend
36e6d8ac6SEric Auger  *
46e6d8ac6SEric Auger  * Copyright (C) 2023 Intel Corporation.
56e6d8ac6SEric Auger  * Copyright Red Hat, Inc. 2023
66e6d8ac6SEric Auger  *
76e6d8ac6SEric Auger  * Authors: Yi Liu <yi.l.liu@intel.com>
86e6d8ac6SEric Auger  *          Eric Auger <eric.auger@redhat.com>
96e6d8ac6SEric Auger  *
106e6d8ac6SEric Auger  * SPDX-License-Identifier: GPL-2.0-or-later
116e6d8ac6SEric Auger  */
126e6d8ac6SEric Auger 
136e6d8ac6SEric Auger #include "qemu/osdep.h"
146e6d8ac6SEric Auger #include "sysemu/iommufd.h"
156e6d8ac6SEric Auger #include "qapi/error.h"
166e6d8ac6SEric Auger #include "qapi/qmp/qerror.h"
176e6d8ac6SEric Auger #include "qemu/module.h"
186e6d8ac6SEric Auger #include "qom/object_interfaces.h"
196e6d8ac6SEric Auger #include "qemu/error-report.h"
206e6d8ac6SEric Auger #include "monitor/monitor.h"
216e6d8ac6SEric Auger #include "trace.h"
226e6d8ac6SEric Auger #include <sys/ioctl.h>
236e6d8ac6SEric Auger #include <linux/iommufd.h>
246e6d8ac6SEric Auger 
256e6d8ac6SEric Auger static void iommufd_backend_init(Object *obj)
266e6d8ac6SEric Auger {
276e6d8ac6SEric Auger     IOMMUFDBackend *be = IOMMUFD_BACKEND(obj);
286e6d8ac6SEric Auger 
296e6d8ac6SEric Auger     be->fd = -1;
306e6d8ac6SEric Auger     be->users = 0;
316e6d8ac6SEric Auger     be->owned = true;
326e6d8ac6SEric Auger }
336e6d8ac6SEric Auger 
346e6d8ac6SEric Auger static void iommufd_backend_finalize(Object *obj)
356e6d8ac6SEric Auger {
366e6d8ac6SEric Auger     IOMMUFDBackend *be = IOMMUFD_BACKEND(obj);
376e6d8ac6SEric Auger 
386e6d8ac6SEric Auger     if (be->owned) {
396e6d8ac6SEric Auger         close(be->fd);
406e6d8ac6SEric Auger         be->fd = -1;
416e6d8ac6SEric Auger     }
426e6d8ac6SEric Auger }
436e6d8ac6SEric Auger 
446e6d8ac6SEric Auger static void iommufd_backend_set_fd(Object *obj, const char *str, Error **errp)
456e6d8ac6SEric Auger {
46*c1cccad8SZhao Liu     ERRP_GUARD();
476e6d8ac6SEric Auger     IOMMUFDBackend *be = IOMMUFD_BACKEND(obj);
486e6d8ac6SEric Auger     int fd = -1;
496e6d8ac6SEric Auger 
506e6d8ac6SEric Auger     fd = monitor_fd_param(monitor_cur(), str, errp);
516e6d8ac6SEric Auger     if (fd == -1) {
526e6d8ac6SEric Auger         error_prepend(errp, "Could not parse remote object fd %s:", str);
536e6d8ac6SEric Auger         return;
546e6d8ac6SEric Auger     }
556e6d8ac6SEric Auger     be->fd = fd;
566e6d8ac6SEric Auger     be->owned = false;
576e6d8ac6SEric Auger     trace_iommu_backend_set_fd(be->fd);
586e6d8ac6SEric Auger }
596e6d8ac6SEric Auger 
606e6d8ac6SEric Auger static bool iommufd_backend_can_be_deleted(UserCreatable *uc)
616e6d8ac6SEric Auger {
626e6d8ac6SEric Auger     IOMMUFDBackend *be = IOMMUFD_BACKEND(uc);
636e6d8ac6SEric Auger 
646e6d8ac6SEric Auger     return !be->users;
656e6d8ac6SEric Auger }
666e6d8ac6SEric Auger 
676e6d8ac6SEric Auger static void iommufd_backend_class_init(ObjectClass *oc, void *data)
686e6d8ac6SEric Auger {
696e6d8ac6SEric Auger     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
706e6d8ac6SEric Auger 
716e6d8ac6SEric Auger     ucc->can_be_deleted = iommufd_backend_can_be_deleted;
726e6d8ac6SEric Auger 
736e6d8ac6SEric Auger     object_class_property_add_str(oc, "fd", NULL, iommufd_backend_set_fd);
746e6d8ac6SEric Auger }
756e6d8ac6SEric Auger 
766e6d8ac6SEric Auger int iommufd_backend_connect(IOMMUFDBackend *be, Error **errp)
776e6d8ac6SEric Auger {
786e6d8ac6SEric Auger     int fd, ret = 0;
796e6d8ac6SEric Auger 
806e6d8ac6SEric Auger     if (be->owned && !be->users) {
816e6d8ac6SEric Auger         fd = qemu_open_old("/dev/iommu", O_RDWR);
826e6d8ac6SEric Auger         if (fd < 0) {
836e6d8ac6SEric Auger             error_setg_errno(errp, errno, "/dev/iommu opening failed");
846e6d8ac6SEric Auger             ret = fd;
856e6d8ac6SEric Auger             goto out;
866e6d8ac6SEric Auger         }
876e6d8ac6SEric Auger         be->fd = fd;
886e6d8ac6SEric Auger     }
896e6d8ac6SEric Auger     be->users++;
906e6d8ac6SEric Auger out:
916e6d8ac6SEric Auger     trace_iommufd_backend_connect(be->fd, be->owned,
926e6d8ac6SEric Auger                                   be->users, ret);
936e6d8ac6SEric Auger     return ret;
946e6d8ac6SEric Auger }
956e6d8ac6SEric Auger 
966e6d8ac6SEric Auger void iommufd_backend_disconnect(IOMMUFDBackend *be)
976e6d8ac6SEric Auger {
986e6d8ac6SEric Auger     if (!be->users) {
996e6d8ac6SEric Auger         goto out;
1006e6d8ac6SEric Auger     }
1016e6d8ac6SEric Auger     be->users--;
1026e6d8ac6SEric Auger     if (!be->users && be->owned) {
1036e6d8ac6SEric Auger         close(be->fd);
1046e6d8ac6SEric Auger         be->fd = -1;
1056e6d8ac6SEric Auger     }
1066e6d8ac6SEric Auger out:
1076e6d8ac6SEric Auger     trace_iommufd_backend_disconnect(be->fd, be->users);
1086e6d8ac6SEric Auger }
1096e6d8ac6SEric Auger 
1106e6d8ac6SEric Auger int iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id,
1116e6d8ac6SEric Auger                                Error **errp)
1126e6d8ac6SEric Auger {
1136e6d8ac6SEric Auger     int ret, fd = be->fd;
1146e6d8ac6SEric Auger     struct iommu_ioas_alloc alloc_data  = {
1156e6d8ac6SEric Auger         .size = sizeof(alloc_data),
1166e6d8ac6SEric Auger         .flags = 0,
1176e6d8ac6SEric Auger     };
1186e6d8ac6SEric Auger 
1196e6d8ac6SEric Auger     ret = ioctl(fd, IOMMU_IOAS_ALLOC, &alloc_data);
1206e6d8ac6SEric Auger     if (ret) {
1216e6d8ac6SEric Auger         error_setg_errno(errp, errno, "Failed to allocate ioas");
1226e6d8ac6SEric Auger         return ret;
1236e6d8ac6SEric Auger     }
1246e6d8ac6SEric Auger 
1256e6d8ac6SEric Auger     *ioas_id = alloc_data.out_ioas_id;
1266e6d8ac6SEric Auger     trace_iommufd_backend_alloc_ioas(fd, *ioas_id, ret);
1276e6d8ac6SEric Auger 
1286e6d8ac6SEric Auger     return ret;
1296e6d8ac6SEric Auger }
1306e6d8ac6SEric Auger 
1316e6d8ac6SEric Auger void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id)
1326e6d8ac6SEric Auger {
1336e6d8ac6SEric Auger     int ret, fd = be->fd;
1346e6d8ac6SEric Auger     struct iommu_destroy des = {
1356e6d8ac6SEric Auger         .size = sizeof(des),
1366e6d8ac6SEric Auger         .id = id,
1376e6d8ac6SEric Auger     };
1386e6d8ac6SEric Auger 
1396e6d8ac6SEric Auger     ret = ioctl(fd, IOMMU_DESTROY, &des);
1406e6d8ac6SEric Auger     trace_iommufd_backend_free_id(fd, id, ret);
1416e6d8ac6SEric Auger     if (ret) {
1426e6d8ac6SEric Auger         error_report("Failed to free id: %u %m", id);
1436e6d8ac6SEric Auger     }
1446e6d8ac6SEric Auger }
1456e6d8ac6SEric Auger 
1466e6d8ac6SEric Auger int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
1476e6d8ac6SEric Auger                             ram_addr_t size, void *vaddr, bool readonly)
1486e6d8ac6SEric Auger {
1496e6d8ac6SEric Auger     int ret, fd = be->fd;
1506e6d8ac6SEric Auger     struct iommu_ioas_map map = {
1516e6d8ac6SEric Auger         .size = sizeof(map),
1526e6d8ac6SEric Auger         .flags = IOMMU_IOAS_MAP_READABLE |
1536e6d8ac6SEric Auger                  IOMMU_IOAS_MAP_FIXED_IOVA,
1546e6d8ac6SEric Auger         .ioas_id = ioas_id,
1556e6d8ac6SEric Auger         .__reserved = 0,
1566e6d8ac6SEric Auger         .user_va = (uintptr_t)vaddr,
1576e6d8ac6SEric Auger         .iova = iova,
1586e6d8ac6SEric Auger         .length = size,
1596e6d8ac6SEric Auger     };
1606e6d8ac6SEric Auger 
1616e6d8ac6SEric Auger     if (!readonly) {
1626e6d8ac6SEric Auger         map.flags |= IOMMU_IOAS_MAP_WRITEABLE;
1636e6d8ac6SEric Auger     }
1646e6d8ac6SEric Auger 
1656e6d8ac6SEric Auger     ret = ioctl(fd, IOMMU_IOAS_MAP, &map);
1666e6d8ac6SEric Auger     trace_iommufd_backend_map_dma(fd, ioas_id, iova, size,
1676e6d8ac6SEric Auger                                   vaddr, readonly, ret);
1686e6d8ac6SEric Auger     if (ret) {
1696e6d8ac6SEric Auger         ret = -errno;
1706e6d8ac6SEric Auger 
1716e6d8ac6SEric Auger         /* TODO: Not support mapping hardware PCI BAR region for now. */
1726e6d8ac6SEric Auger         if (errno == EFAULT) {
1736e6d8ac6SEric Auger             warn_report("IOMMU_IOAS_MAP failed: %m, PCI BAR?");
1746e6d8ac6SEric Auger         } else {
1756e6d8ac6SEric Auger             error_report("IOMMU_IOAS_MAP failed: %m");
1766e6d8ac6SEric Auger         }
1776e6d8ac6SEric Auger     }
1786e6d8ac6SEric Auger     return ret;
1796e6d8ac6SEric Auger }
1806e6d8ac6SEric Auger 
1816e6d8ac6SEric Auger int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
1826e6d8ac6SEric Auger                               hwaddr iova, ram_addr_t size)
1836e6d8ac6SEric Auger {
1846e6d8ac6SEric Auger     int ret, fd = be->fd;
1856e6d8ac6SEric Auger     struct iommu_ioas_unmap unmap = {
1866e6d8ac6SEric Auger         .size = sizeof(unmap),
1876e6d8ac6SEric Auger         .ioas_id = ioas_id,
1886e6d8ac6SEric Auger         .iova = iova,
1896e6d8ac6SEric Auger         .length = size,
1906e6d8ac6SEric Auger     };
1916e6d8ac6SEric Auger 
1926e6d8ac6SEric Auger     ret = ioctl(fd, IOMMU_IOAS_UNMAP, &unmap);
1936e6d8ac6SEric Auger     /*
1946e6d8ac6SEric Auger      * IOMMUFD takes mapping as some kind of object, unmapping
1956e6d8ac6SEric Auger      * nonexistent mapping is treated as deleting a nonexistent
1966e6d8ac6SEric Auger      * object and return ENOENT. This is different from legacy
1976e6d8ac6SEric Auger      * backend which allows it. vIOMMU may trigger a lot of
1986e6d8ac6SEric Auger      * redundant unmapping, to avoid flush the log, treat them
1996e6d8ac6SEric Auger      * as succeess for IOMMUFD just like legacy backend.
2006e6d8ac6SEric Auger      */
2016e6d8ac6SEric Auger     if (ret && errno == ENOENT) {
2026e6d8ac6SEric Auger         trace_iommufd_backend_unmap_dma_non_exist(fd, ioas_id, iova, size, ret);
2036e6d8ac6SEric Auger         ret = 0;
2046e6d8ac6SEric Auger     } else {
2056e6d8ac6SEric Auger         trace_iommufd_backend_unmap_dma(fd, ioas_id, iova, size, ret);
2066e6d8ac6SEric Auger     }
2076e6d8ac6SEric Auger 
2086e6d8ac6SEric Auger     if (ret) {
2096e6d8ac6SEric Auger         ret = -errno;
2106e6d8ac6SEric Auger         error_report("IOMMU_IOAS_UNMAP failed: %m");
2116e6d8ac6SEric Auger     }
2126e6d8ac6SEric Auger     return ret;
2136e6d8ac6SEric Auger }
2146e6d8ac6SEric Auger 
2156e6d8ac6SEric Auger static const TypeInfo iommufd_backend_info = {
2166e6d8ac6SEric Auger     .name = TYPE_IOMMUFD_BACKEND,
2176e6d8ac6SEric Auger     .parent = TYPE_OBJECT,
2186e6d8ac6SEric Auger     .instance_size = sizeof(IOMMUFDBackend),
2196e6d8ac6SEric Auger     .instance_init = iommufd_backend_init,
2206e6d8ac6SEric Auger     .instance_finalize = iommufd_backend_finalize,
2216e6d8ac6SEric Auger     .class_size = sizeof(IOMMUFDBackendClass),
2226e6d8ac6SEric Auger     .class_init = iommufd_backend_class_init,
2236e6d8ac6SEric Auger     .interfaces = (InterfaceInfo[]) {
2246e6d8ac6SEric Auger         { TYPE_USER_CREATABLE },
2256e6d8ac6SEric Auger         { }
2266e6d8ac6SEric Auger     }
2276e6d8ac6SEric Auger };
2286e6d8ac6SEric Auger 
2296e6d8ac6SEric Auger static void register_types(void)
2306e6d8ac6SEric Auger {
2316e6d8ac6SEric Auger     type_register_static(&iommufd_backend_info);
2326e6d8ac6SEric Auger }
2336e6d8ac6SEric Auger 
2346e6d8ac6SEric Auger type_init(register_types);
235