xref: /openbmc/qemu/backends/iommufd.c (revision ed75658a)
1 /*
2  * iommufd container backend
3  *
4  * Copyright (C) 2023 Intel Corporation.
5  * Copyright Red Hat, Inc. 2023
6  *
7  * Authors: Yi Liu <yi.l.liu@intel.com>
8  *          Eric Auger <eric.auger@redhat.com>
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12 
13 #include "qemu/osdep.h"
14 #include "sysemu/iommufd.h"
15 #include "qapi/error.h"
16 #include "qapi/qmp/qerror.h"
17 #include "qemu/module.h"
18 #include "qom/object_interfaces.h"
19 #include "qemu/error-report.h"
20 #include "monitor/monitor.h"
21 #include "trace.h"
22 #include <sys/ioctl.h>
23 #include <linux/iommufd.h>
24 
25 static void iommufd_backend_init(Object *obj)
26 {
27     IOMMUFDBackend *be = IOMMUFD_BACKEND(obj);
28 
29     be->fd = -1;
30     be->users = 0;
31     be->owned = true;
32 }
33 
34 static void iommufd_backend_finalize(Object *obj)
35 {
36     IOMMUFDBackend *be = IOMMUFD_BACKEND(obj);
37 
38     if (be->owned) {
39         close(be->fd);
40         be->fd = -1;
41     }
42 }
43 
44 static void iommufd_backend_set_fd(Object *obj, const char *str, Error **errp)
45 {
46     ERRP_GUARD();
47     IOMMUFDBackend *be = IOMMUFD_BACKEND(obj);
48     int fd = -1;
49 
50     fd = monitor_fd_param(monitor_cur(), str, errp);
51     if (fd == -1) {
52         error_prepend(errp, "Could not parse remote object fd %s:", str);
53         return;
54     }
55     be->fd = fd;
56     be->owned = false;
57     trace_iommu_backend_set_fd(be->fd);
58 }
59 
60 static bool iommufd_backend_can_be_deleted(UserCreatable *uc)
61 {
62     IOMMUFDBackend *be = IOMMUFD_BACKEND(uc);
63 
64     return !be->users;
65 }
66 
67 static void iommufd_backend_class_init(ObjectClass *oc, void *data)
68 {
69     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
70 
71     ucc->can_be_deleted = iommufd_backend_can_be_deleted;
72 
73     object_class_property_add_str(oc, "fd", NULL, iommufd_backend_set_fd);
74 }
75 
76 int iommufd_backend_connect(IOMMUFDBackend *be, Error **errp)
77 {
78     int fd, ret = 0;
79 
80     if (be->owned && !be->users) {
81         fd = qemu_open_old("/dev/iommu", O_RDWR);
82         if (fd < 0) {
83             error_setg_errno(errp, errno, "/dev/iommu opening failed");
84             ret = fd;
85             goto out;
86         }
87         be->fd = fd;
88     }
89     be->users++;
90 out:
91     trace_iommufd_backend_connect(be->fd, be->owned,
92                                   be->users, ret);
93     return ret;
94 }
95 
96 void iommufd_backend_disconnect(IOMMUFDBackend *be)
97 {
98     if (!be->users) {
99         goto out;
100     }
101     be->users--;
102     if (!be->users && be->owned) {
103         close(be->fd);
104         be->fd = -1;
105     }
106 out:
107     trace_iommufd_backend_disconnect(be->fd, be->users);
108 }
109 
110 int iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id,
111                                Error **errp)
112 {
113     int ret, fd = be->fd;
114     struct iommu_ioas_alloc alloc_data  = {
115         .size = sizeof(alloc_data),
116         .flags = 0,
117     };
118 
119     ret = ioctl(fd, IOMMU_IOAS_ALLOC, &alloc_data);
120     if (ret) {
121         error_setg_errno(errp, errno, "Failed to allocate ioas");
122         return ret;
123     }
124 
125     *ioas_id = alloc_data.out_ioas_id;
126     trace_iommufd_backend_alloc_ioas(fd, *ioas_id, ret);
127 
128     return ret;
129 }
130 
131 void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id)
132 {
133     int ret, fd = be->fd;
134     struct iommu_destroy des = {
135         .size = sizeof(des),
136         .id = id,
137     };
138 
139     ret = ioctl(fd, IOMMU_DESTROY, &des);
140     trace_iommufd_backend_free_id(fd, id, ret);
141     if (ret) {
142         error_report("Failed to free id: %u %m", id);
143     }
144 }
145 
146 int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
147                             ram_addr_t size, void *vaddr, bool readonly)
148 {
149     int ret, fd = be->fd;
150     struct iommu_ioas_map map = {
151         .size = sizeof(map),
152         .flags = IOMMU_IOAS_MAP_READABLE |
153                  IOMMU_IOAS_MAP_FIXED_IOVA,
154         .ioas_id = ioas_id,
155         .__reserved = 0,
156         .user_va = (uintptr_t)vaddr,
157         .iova = iova,
158         .length = size,
159     };
160 
161     if (!readonly) {
162         map.flags |= IOMMU_IOAS_MAP_WRITEABLE;
163     }
164 
165     ret = ioctl(fd, IOMMU_IOAS_MAP, &map);
166     trace_iommufd_backend_map_dma(fd, ioas_id, iova, size,
167                                   vaddr, readonly, ret);
168     if (ret) {
169         ret = -errno;
170 
171         /* TODO: Not support mapping hardware PCI BAR region for now. */
172         if (errno == EFAULT) {
173             warn_report("IOMMU_IOAS_MAP failed: %m, PCI BAR?");
174         } else {
175             error_report("IOMMU_IOAS_MAP failed: %m");
176         }
177     }
178     return ret;
179 }
180 
181 int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
182                               hwaddr iova, ram_addr_t size)
183 {
184     int ret, fd = be->fd;
185     struct iommu_ioas_unmap unmap = {
186         .size = sizeof(unmap),
187         .ioas_id = ioas_id,
188         .iova = iova,
189         .length = size,
190     };
191 
192     ret = ioctl(fd, IOMMU_IOAS_UNMAP, &unmap);
193     /*
194      * IOMMUFD takes mapping as some kind of object, unmapping
195      * nonexistent mapping is treated as deleting a nonexistent
196      * object and return ENOENT. This is different from legacy
197      * backend which allows it. vIOMMU may trigger a lot of
198      * redundant unmapping, to avoid flush the log, treat them
199      * as succeess for IOMMUFD just like legacy backend.
200      */
201     if (ret && errno == ENOENT) {
202         trace_iommufd_backend_unmap_dma_non_exist(fd, ioas_id, iova, size, ret);
203         ret = 0;
204     } else {
205         trace_iommufd_backend_unmap_dma(fd, ioas_id, iova, size, ret);
206     }
207 
208     if (ret) {
209         ret = -errno;
210         error_report("IOMMU_IOAS_UNMAP failed: %m");
211     }
212     return ret;
213 }
214 
215 static const TypeInfo iommufd_backend_info = {
216     .name = TYPE_IOMMUFD_BACKEND,
217     .parent = TYPE_OBJECT,
218     .instance_size = sizeof(IOMMUFDBackend),
219     .instance_init = iommufd_backend_init,
220     .instance_finalize = iommufd_backend_finalize,
221     .class_size = sizeof(IOMMUFDBackendClass),
222     .class_init = iommufd_backend_class_init,
223     .interfaces = (InterfaceInfo[]) {
224         { TYPE_USER_CREATABLE },
225         { }
226     }
227 };
228 
229 static void register_types(void)
230 {
231     type_register_static(&iommufd_backend_info);
232 }
233 
234 type_init(register_types);
235