xref: /qemu/hw/vfio-user/device.c (revision 019232358124e4f4b929e40fa23253de60eec73e)
13bdb738bSJohn Levon /*
23bdb738bSJohn Levon  * vfio protocol over a UNIX socket device handling.
33bdb738bSJohn Levon  *
43bdb738bSJohn Levon  * Copyright © 2018, 2021 Oracle and/or its affiliates.
53bdb738bSJohn Levon  *
63bdb738bSJohn Levon  * SPDX-License-Identifier: GPL-2.0-or-later
73bdb738bSJohn Levon  */
83bdb738bSJohn Levon 
93bdb738bSJohn Levon #include "qemu/osdep.h"
103bdb738bSJohn Levon #include "qapi/error.h"
113bdb738bSJohn Levon #include "qemu/error-report.h"
123bdb738bSJohn Levon 
133bdb738bSJohn Levon #include "hw/vfio-user/device.h"
143bdb738bSJohn Levon #include "hw/vfio-user/trace.h"
153bdb738bSJohn Levon 
163bdb738bSJohn Levon /*
173bdb738bSJohn Levon  * These are to defend against a malign server trying
183bdb738bSJohn Levon  * to force us to run out of memory.
193bdb738bSJohn Levon  */
203bdb738bSJohn Levon #define VFIO_USER_MAX_REGIONS   100
213bdb738bSJohn Levon #define VFIO_USER_MAX_IRQS      50
223bdb738bSJohn Levon 
233bdb738bSJohn Levon bool vfio_user_get_device_info(VFIOUserProxy *proxy,
243bdb738bSJohn Levon                                struct vfio_device_info *info, Error **errp)
253bdb738bSJohn Levon {
263bdb738bSJohn Levon     VFIOUserDeviceInfo msg;
273bdb738bSJohn Levon     uint32_t argsz = sizeof(msg) - sizeof(msg.hdr);
283bdb738bSJohn Levon 
293bdb738bSJohn Levon     memset(&msg, 0, sizeof(msg));
303bdb738bSJohn Levon     vfio_user_request_msg(&msg.hdr, VFIO_USER_DEVICE_GET_INFO, sizeof(msg), 0);
313bdb738bSJohn Levon     msg.argsz = argsz;
323bdb738bSJohn Levon 
333bdb738bSJohn Levon     if (!vfio_user_send_wait(proxy, &msg.hdr, NULL, 0, errp)) {
343bdb738bSJohn Levon         return false;
353bdb738bSJohn Levon     }
363bdb738bSJohn Levon 
373bdb738bSJohn Levon     if (msg.hdr.flags & VFIO_USER_ERROR) {
383bdb738bSJohn Levon         error_setg_errno(errp, -msg.hdr.error_reply,
393bdb738bSJohn Levon                          "VFIO_USER_DEVICE_GET_INFO failed");
403bdb738bSJohn Levon         return false;
413bdb738bSJohn Levon     }
423bdb738bSJohn Levon 
433bdb738bSJohn Levon     trace_vfio_user_get_info(msg.num_regions, msg.num_irqs);
443bdb738bSJohn Levon 
453bdb738bSJohn Levon     memcpy(info, &msg.argsz, argsz);
463bdb738bSJohn Levon 
473bdb738bSJohn Levon     /* defend against a malicious server */
483bdb738bSJohn Levon     if (info->num_regions > VFIO_USER_MAX_REGIONS ||
493bdb738bSJohn Levon         info->num_irqs > VFIO_USER_MAX_IRQS) {
503bdb738bSJohn Levon         error_setg_errno(errp, EINVAL, "invalid reply");
513bdb738bSJohn Levon         return false;
523bdb738bSJohn Levon     }
533bdb738bSJohn Levon 
543bdb738bSJohn Levon     return true;
553bdb738bSJohn Levon }
56667866d6SJohn Levon 
57*01923235SJohn Levon void vfio_user_device_reset(VFIOUserProxy *proxy)
58*01923235SJohn Levon {
59*01923235SJohn Levon     Error *local_err = NULL;
60*01923235SJohn Levon     VFIOUserHdr hdr;
61*01923235SJohn Levon 
62*01923235SJohn Levon     vfio_user_request_msg(&hdr, VFIO_USER_DEVICE_RESET, sizeof(hdr), 0);
63*01923235SJohn Levon 
64*01923235SJohn Levon     if (!vfio_user_send_wait(proxy, &hdr, NULL, 0, &local_err)) {
65*01923235SJohn Levon         error_prepend(&local_err, "%s: ", __func__);
66*01923235SJohn Levon         error_report_err(local_err);
67*01923235SJohn Levon         return;
68*01923235SJohn Levon     }
69*01923235SJohn Levon 
70*01923235SJohn Levon     if (hdr.flags & VFIO_USER_ERROR) {
71*01923235SJohn Levon         error_printf("reset reply error %d\n", hdr.error_reply);
72*01923235SJohn Levon     }
73*01923235SJohn Levon }
74*01923235SJohn Levon 
75667866d6SJohn Levon static int vfio_user_get_region_info(VFIOUserProxy *proxy,
76667866d6SJohn Levon                                      struct vfio_region_info *info,
77667866d6SJohn Levon                                      VFIOUserFDs *fds)
78667866d6SJohn Levon {
79667866d6SJohn Levon     g_autofree VFIOUserRegionInfo *msgp = NULL;
80667866d6SJohn Levon     Error *local_err = NULL;
81667866d6SJohn Levon     uint32_t size;
82667866d6SJohn Levon 
83667866d6SJohn Levon     /* data returned can be larger than vfio_region_info */
84667866d6SJohn Levon     if (info->argsz < sizeof(*info)) {
85667866d6SJohn Levon         error_printf("vfio_user_get_region_info argsz too small\n");
86667866d6SJohn Levon         return -E2BIG;
87667866d6SJohn Levon     }
88667866d6SJohn Levon     if (fds != NULL && fds->send_fds != 0) {
89667866d6SJohn Levon         error_printf("vfio_user_get_region_info can't send FDs\n");
90667866d6SJohn Levon         return -EINVAL;
91667866d6SJohn Levon     }
92667866d6SJohn Levon 
93667866d6SJohn Levon     size = info->argsz + sizeof(VFIOUserHdr);
94667866d6SJohn Levon     msgp = g_malloc0(size);
95667866d6SJohn Levon 
96667866d6SJohn Levon     vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_GET_REGION_INFO,
97667866d6SJohn Levon                           sizeof(*msgp), 0);
98667866d6SJohn Levon     msgp->argsz = info->argsz;
99667866d6SJohn Levon     msgp->index = info->index;
100667866d6SJohn Levon 
101667866d6SJohn Levon     if (!vfio_user_send_wait(proxy, &msgp->hdr, fds, size, &local_err)) {
102667866d6SJohn Levon         error_prepend(&local_err, "%s: ", __func__);
103667866d6SJohn Levon         error_report_err(local_err);
104667866d6SJohn Levon         return -EFAULT;
105667866d6SJohn Levon     }
106667866d6SJohn Levon 
107667866d6SJohn Levon     if (msgp->hdr.flags & VFIO_USER_ERROR) {
108667866d6SJohn Levon         return -msgp->hdr.error_reply;
109667866d6SJohn Levon     }
110667866d6SJohn Levon     trace_vfio_user_get_region_info(msgp->index, msgp->flags, msgp->size);
111667866d6SJohn Levon 
112667866d6SJohn Levon     memcpy(info, &msgp->argsz, info->argsz);
113667866d6SJohn Levon     return 0;
114667866d6SJohn Levon }
115667866d6SJohn Levon 
116667866d6SJohn Levon 
117667866d6SJohn Levon static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev,
118667866d6SJohn Levon                                                struct vfio_region_info *info,
119667866d6SJohn Levon                                                int *fd)
120667866d6SJohn Levon {
121667866d6SJohn Levon     VFIOUserFDs fds = { 0, 1, fd};
122667866d6SJohn Levon     int ret;
123667866d6SJohn Levon 
124667866d6SJohn Levon     if (info->index > vbasedev->num_regions) {
125667866d6SJohn Levon         return -EINVAL;
126667866d6SJohn Levon     }
127667866d6SJohn Levon 
128667866d6SJohn Levon     ret = vfio_user_get_region_info(vbasedev->proxy, info, &fds);
129667866d6SJohn Levon     if (ret) {
130667866d6SJohn Levon         return ret;
131667866d6SJohn Levon     }
132667866d6SJohn Levon 
133667866d6SJohn Levon     /* cap_offset in valid area */
134667866d6SJohn Levon     if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) &&
135667866d6SJohn Levon         (info->cap_offset < sizeof(*info) || info->cap_offset > info->argsz)) {
136667866d6SJohn Levon         return -EINVAL;
137667866d6SJohn Levon     }
138667866d6SJohn Levon 
139667866d6SJohn Levon     return 0;
140667866d6SJohn Levon }
141667866d6SJohn Levon 
142ca1add16SJohn Levon static int vfio_user_device_io_get_irq_info(VFIODevice *vbasedev,
143ca1add16SJohn Levon                                             struct vfio_irq_info *info)
144ca1add16SJohn Levon {
145ca1add16SJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
146ca1add16SJohn Levon     Error *local_err = NULL;
147ca1add16SJohn Levon     VFIOUserIRQInfo msg;
148ca1add16SJohn Levon 
149ca1add16SJohn Levon     memset(&msg, 0, sizeof(msg));
150ca1add16SJohn Levon     vfio_user_request_msg(&msg.hdr, VFIO_USER_DEVICE_GET_IRQ_INFO,
151ca1add16SJohn Levon                           sizeof(msg), 0);
152ca1add16SJohn Levon     msg.argsz = info->argsz;
153ca1add16SJohn Levon     msg.index = info->index;
154ca1add16SJohn Levon 
155ca1add16SJohn Levon     if (!vfio_user_send_wait(proxy, &msg.hdr, NULL, 0, &local_err)) {
156ca1add16SJohn Levon         error_prepend(&local_err, "%s: ", __func__);
157ca1add16SJohn Levon         error_report_err(local_err);
158ca1add16SJohn Levon         return -EFAULT;
159ca1add16SJohn Levon     }
160ca1add16SJohn Levon 
161ca1add16SJohn Levon     if (msg.hdr.flags & VFIO_USER_ERROR) {
162ca1add16SJohn Levon         return -msg.hdr.error_reply;
163ca1add16SJohn Levon     }
164ca1add16SJohn Levon     trace_vfio_user_get_irq_info(msg.index, msg.flags, msg.count);
165ca1add16SJohn Levon 
166ca1add16SJohn Levon     memcpy(info, &msg.argsz, sizeof(*info));
167ca1add16SJohn Levon     return 0;
168ca1add16SJohn Levon }
169ca1add16SJohn Levon 
170ca1add16SJohn Levon static int irq_howmany(int *fdp, uint32_t cur, uint32_t max)
171ca1add16SJohn Levon {
172ca1add16SJohn Levon     int n = 0;
173ca1add16SJohn Levon 
174ca1add16SJohn Levon     if (fdp[cur] != -1) {
175ca1add16SJohn Levon         do {
176ca1add16SJohn Levon             n++;
177ca1add16SJohn Levon         } while (n < max && fdp[cur + n] != -1);
178ca1add16SJohn Levon     } else {
179ca1add16SJohn Levon         do {
180ca1add16SJohn Levon             n++;
181ca1add16SJohn Levon         } while (n < max && fdp[cur + n] == -1);
182ca1add16SJohn Levon     }
183ca1add16SJohn Levon 
184ca1add16SJohn Levon     return n;
185ca1add16SJohn Levon }
186ca1add16SJohn Levon 
187ca1add16SJohn Levon static int vfio_user_device_io_set_irqs(VFIODevice *vbasedev,
188ca1add16SJohn Levon                                         struct vfio_irq_set *irq)
189ca1add16SJohn Levon {
190ca1add16SJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
191ca1add16SJohn Levon     g_autofree VFIOUserIRQSet *msgp = NULL;
192ca1add16SJohn Levon     uint32_t size, nfds, send_fds, sent_fds, max;
193ca1add16SJohn Levon     Error *local_err = NULL;
194ca1add16SJohn Levon 
195ca1add16SJohn Levon     if (irq->argsz < sizeof(*irq)) {
196ca1add16SJohn Levon         error_printf("vfio_user_set_irqs argsz too small\n");
197ca1add16SJohn Levon         return -EINVAL;
198ca1add16SJohn Levon     }
199ca1add16SJohn Levon 
200ca1add16SJohn Levon     /*
201ca1add16SJohn Levon      * Handle simple case
202ca1add16SJohn Levon      */
203ca1add16SJohn Levon     if ((irq->flags & VFIO_IRQ_SET_DATA_EVENTFD) == 0) {
204ca1add16SJohn Levon         size = sizeof(VFIOUserHdr) + irq->argsz;
205ca1add16SJohn Levon         msgp = g_malloc0(size);
206ca1add16SJohn Levon 
207ca1add16SJohn Levon         vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS, size, 0);
208ca1add16SJohn Levon         msgp->argsz = irq->argsz;
209ca1add16SJohn Levon         msgp->flags = irq->flags;
210ca1add16SJohn Levon         msgp->index = irq->index;
211ca1add16SJohn Levon         msgp->start = irq->start;
212ca1add16SJohn Levon         msgp->count = irq->count;
213ca1add16SJohn Levon         trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count,
214ca1add16SJohn Levon                                  msgp->flags);
215ca1add16SJohn Levon 
216ca1add16SJohn Levon         if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) {
217ca1add16SJohn Levon             error_prepend(&local_err, "%s: ", __func__);
218ca1add16SJohn Levon             error_report_err(local_err);
219ca1add16SJohn Levon             return -EFAULT;
220ca1add16SJohn Levon         }
221ca1add16SJohn Levon 
222ca1add16SJohn Levon         if (msgp->hdr.flags & VFIO_USER_ERROR) {
223ca1add16SJohn Levon             return -msgp->hdr.error_reply;
224ca1add16SJohn Levon         }
225ca1add16SJohn Levon 
226ca1add16SJohn Levon         return 0;
227ca1add16SJohn Levon     }
228ca1add16SJohn Levon 
229ca1add16SJohn Levon     /*
230ca1add16SJohn Levon      * Calculate the number of FDs to send
231ca1add16SJohn Levon      * and adjust argsz
232ca1add16SJohn Levon      */
233ca1add16SJohn Levon     nfds = (irq->argsz - sizeof(*irq)) / sizeof(int);
234ca1add16SJohn Levon     irq->argsz = sizeof(*irq);
235ca1add16SJohn Levon     msgp = g_malloc0(sizeof(*msgp));
236ca1add16SJohn Levon     /*
237ca1add16SJohn Levon      * Send in chunks if over max_send_fds
238ca1add16SJohn Levon      */
239ca1add16SJohn Levon     for (sent_fds = 0; nfds > sent_fds; sent_fds += send_fds) {
240ca1add16SJohn Levon         VFIOUserFDs *arg_fds, loop_fds;
241ca1add16SJohn Levon 
242ca1add16SJohn Levon         /* must send all valid FDs or all invalid FDs in single msg */
243ca1add16SJohn Levon         max = nfds - sent_fds;
244ca1add16SJohn Levon         if (max > proxy->max_send_fds) {
245ca1add16SJohn Levon             max = proxy->max_send_fds;
246ca1add16SJohn Levon         }
247ca1add16SJohn Levon         send_fds = irq_howmany((int *)irq->data, sent_fds, max);
248ca1add16SJohn Levon 
249ca1add16SJohn Levon         vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS,
250ca1add16SJohn Levon                               sizeof(*msgp), 0);
251ca1add16SJohn Levon         msgp->argsz = irq->argsz;
252ca1add16SJohn Levon         msgp->flags = irq->flags;
253ca1add16SJohn Levon         msgp->index = irq->index;
254ca1add16SJohn Levon         msgp->start = irq->start + sent_fds;
255ca1add16SJohn Levon         msgp->count = send_fds;
256ca1add16SJohn Levon         trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count,
257ca1add16SJohn Levon                                  msgp->flags);
258ca1add16SJohn Levon 
259ca1add16SJohn Levon         loop_fds.send_fds = send_fds;
260ca1add16SJohn Levon         loop_fds.recv_fds = 0;
261ca1add16SJohn Levon         loop_fds.fds = (int *)irq->data + sent_fds;
262ca1add16SJohn Levon         arg_fds = loop_fds.fds[0] != -1 ? &loop_fds : NULL;
263ca1add16SJohn Levon 
264ca1add16SJohn Levon         if (!vfio_user_send_wait(proxy, &msgp->hdr, arg_fds, 0, &local_err)) {
265ca1add16SJohn Levon             error_prepend(&local_err, "%s: ", __func__);
266ca1add16SJohn Levon             error_report_err(local_err);
267ca1add16SJohn Levon             return -EFAULT;
268ca1add16SJohn Levon         }
269ca1add16SJohn Levon 
270ca1add16SJohn Levon         if (msgp->hdr.flags & VFIO_USER_ERROR) {
271ca1add16SJohn Levon             return -msgp->hdr.error_reply;
272ca1add16SJohn Levon         }
273ca1add16SJohn Levon     }
274ca1add16SJohn Levon 
275ca1add16SJohn Levon     return 0;
276ca1add16SJohn Levon }
277ca1add16SJohn Levon 
2781ed50fcbSJohn Levon static int vfio_user_device_io_region_read(VFIODevice *vbasedev, uint8_t index,
2791ed50fcbSJohn Levon                                            off_t off, uint32_t count,
2801ed50fcbSJohn Levon                                            void *data)
2811ed50fcbSJohn Levon {
2821ed50fcbSJohn Levon     g_autofree VFIOUserRegionRW *msgp = NULL;
2831ed50fcbSJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
2841ed50fcbSJohn Levon     int size = sizeof(*msgp) + count;
2851ed50fcbSJohn Levon     Error *local_err = NULL;
2861ed50fcbSJohn Levon 
2871ed50fcbSJohn Levon     if (count > proxy->max_xfer_size) {
2881ed50fcbSJohn Levon         return -EINVAL;
2891ed50fcbSJohn Levon     }
2901ed50fcbSJohn Levon 
2911ed50fcbSJohn Levon     msgp = g_malloc0(size);
2921ed50fcbSJohn Levon     vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_READ, sizeof(*msgp), 0);
2931ed50fcbSJohn Levon     msgp->offset = off;
2941ed50fcbSJohn Levon     msgp->region = index;
2951ed50fcbSJohn Levon     msgp->count = count;
2961ed50fcbSJohn Levon     trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count);
2971ed50fcbSJohn Levon 
2981ed50fcbSJohn Levon     if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, size, &local_err)) {
2991ed50fcbSJohn Levon         error_prepend(&local_err, "%s: ", __func__);
3001ed50fcbSJohn Levon         error_report_err(local_err);
3011ed50fcbSJohn Levon         return -EFAULT;
3021ed50fcbSJohn Levon     }
3031ed50fcbSJohn Levon 
3041ed50fcbSJohn Levon     if (msgp->hdr.flags & VFIO_USER_ERROR) {
3051ed50fcbSJohn Levon         return -msgp->hdr.error_reply;
3061ed50fcbSJohn Levon     } else if (msgp->count > count) {
3071ed50fcbSJohn Levon         return -E2BIG;
3081ed50fcbSJohn Levon     } else {
3091ed50fcbSJohn Levon         memcpy(data, &msgp->data, msgp->count);
3101ed50fcbSJohn Levon     }
3111ed50fcbSJohn Levon 
3121ed50fcbSJohn Levon     return msgp->count;
3131ed50fcbSJohn Levon }
3141ed50fcbSJohn Levon 
3151ed50fcbSJohn Levon static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index,
3161ed50fcbSJohn Levon                                             off_t off, unsigned count,
3171ed50fcbSJohn Levon                                             void *data, bool post)
3181ed50fcbSJohn Levon {
3191ed50fcbSJohn Levon     g_autofree VFIOUserRegionRW *msgp = NULL;
3201ed50fcbSJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
3211ed50fcbSJohn Levon     int size = sizeof(*msgp) + count;
3221ed50fcbSJohn Levon     Error *local_err = NULL;
3231ed50fcbSJohn Levon     int ret;
3241ed50fcbSJohn Levon 
3251ed50fcbSJohn Levon     if (count > proxy->max_xfer_size) {
3261ed50fcbSJohn Levon         return -EINVAL;
3271ed50fcbSJohn Levon     }
3281ed50fcbSJohn Levon 
3291ed50fcbSJohn Levon     msgp = g_malloc0(size);
3301ed50fcbSJohn Levon     vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_WRITE, size, 0);
3311ed50fcbSJohn Levon     msgp->offset = off;
3321ed50fcbSJohn Levon     msgp->region = index;
3331ed50fcbSJohn Levon     msgp->count = count;
3341ed50fcbSJohn Levon     memcpy(&msgp->data, data, count);
3351ed50fcbSJohn Levon     trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count);
3361ed50fcbSJohn Levon 
3371ed50fcbSJohn Levon     /* Ignore post: all writes are synchronous/non-posted. */
3381ed50fcbSJohn Levon 
3391ed50fcbSJohn Levon     if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) {
3401ed50fcbSJohn Levon         error_prepend(&local_err, "%s: ", __func__);
3411ed50fcbSJohn Levon         error_report_err(local_err);
3421ed50fcbSJohn Levon         return -EFAULT;
3431ed50fcbSJohn Levon     }
3441ed50fcbSJohn Levon 
3451ed50fcbSJohn Levon     if (msgp->hdr.flags & VFIO_USER_ERROR) {
3461ed50fcbSJohn Levon         ret = -msgp->hdr.error_reply;
3471ed50fcbSJohn Levon     } else {
3481ed50fcbSJohn Levon         ret = count;
3491ed50fcbSJohn Levon     }
3501ed50fcbSJohn Levon 
3511ed50fcbSJohn Levon     return ret;
3521ed50fcbSJohn Levon }
3531ed50fcbSJohn Levon 
354667866d6SJohn Levon /*
355667866d6SJohn Levon  * Socket-based io_ops
356667866d6SJohn Levon  */
357667866d6SJohn Levon VFIODeviceIOOps vfio_user_device_io_ops_sock = {
358667866d6SJohn Levon     .get_region_info = vfio_user_device_io_get_region_info,
359ca1add16SJohn Levon     .get_irq_info = vfio_user_device_io_get_irq_info,
360ca1add16SJohn Levon     .set_irqs = vfio_user_device_io_set_irqs,
3611ed50fcbSJohn Levon     .region_read = vfio_user_device_io_region_read,
3621ed50fcbSJohn Levon     .region_write = vfio_user_device_io_region_write,
3631ed50fcbSJohn Levon 
364667866d6SJohn Levon };
365