xref: /qemu/hw/vfio-user/device.c (revision ca1add1696dd53f905c2a17f36159d54e9b6f527)
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 
57667866d6SJohn Levon static int vfio_user_get_region_info(VFIOUserProxy *proxy,
58667866d6SJohn Levon                                      struct vfio_region_info *info,
59667866d6SJohn Levon                                      VFIOUserFDs *fds)
60667866d6SJohn Levon {
61667866d6SJohn Levon     g_autofree VFIOUserRegionInfo *msgp = NULL;
62667866d6SJohn Levon     Error *local_err = NULL;
63667866d6SJohn Levon     uint32_t size;
64667866d6SJohn Levon 
65667866d6SJohn Levon     /* data returned can be larger than vfio_region_info */
66667866d6SJohn Levon     if (info->argsz < sizeof(*info)) {
67667866d6SJohn Levon         error_printf("vfio_user_get_region_info argsz too small\n");
68667866d6SJohn Levon         return -E2BIG;
69667866d6SJohn Levon     }
70667866d6SJohn Levon     if (fds != NULL && fds->send_fds != 0) {
71667866d6SJohn Levon         error_printf("vfio_user_get_region_info can't send FDs\n");
72667866d6SJohn Levon         return -EINVAL;
73667866d6SJohn Levon     }
74667866d6SJohn Levon 
75667866d6SJohn Levon     size = info->argsz + sizeof(VFIOUserHdr);
76667866d6SJohn Levon     msgp = g_malloc0(size);
77667866d6SJohn Levon 
78667866d6SJohn Levon     vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_GET_REGION_INFO,
79667866d6SJohn Levon                           sizeof(*msgp), 0);
80667866d6SJohn Levon     msgp->argsz = info->argsz;
81667866d6SJohn Levon     msgp->index = info->index;
82667866d6SJohn Levon 
83667866d6SJohn Levon     if (!vfio_user_send_wait(proxy, &msgp->hdr, fds, size, &local_err)) {
84667866d6SJohn Levon         error_prepend(&local_err, "%s: ", __func__);
85667866d6SJohn Levon         error_report_err(local_err);
86667866d6SJohn Levon         return -EFAULT;
87667866d6SJohn Levon     }
88667866d6SJohn Levon 
89667866d6SJohn Levon     if (msgp->hdr.flags & VFIO_USER_ERROR) {
90667866d6SJohn Levon         return -msgp->hdr.error_reply;
91667866d6SJohn Levon     }
92667866d6SJohn Levon     trace_vfio_user_get_region_info(msgp->index, msgp->flags, msgp->size);
93667866d6SJohn Levon 
94667866d6SJohn Levon     memcpy(info, &msgp->argsz, info->argsz);
95667866d6SJohn Levon     return 0;
96667866d6SJohn Levon }
97667866d6SJohn Levon 
98667866d6SJohn Levon 
99667866d6SJohn Levon static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev,
100667866d6SJohn Levon                                                struct vfio_region_info *info,
101667866d6SJohn Levon                                                int *fd)
102667866d6SJohn Levon {
103667866d6SJohn Levon     VFIOUserFDs fds = { 0, 1, fd};
104667866d6SJohn Levon     int ret;
105667866d6SJohn Levon 
106667866d6SJohn Levon     if (info->index > vbasedev->num_regions) {
107667866d6SJohn Levon         return -EINVAL;
108667866d6SJohn Levon     }
109667866d6SJohn Levon 
110667866d6SJohn Levon     ret = vfio_user_get_region_info(vbasedev->proxy, info, &fds);
111667866d6SJohn Levon     if (ret) {
112667866d6SJohn Levon         return ret;
113667866d6SJohn Levon     }
114667866d6SJohn Levon 
115667866d6SJohn Levon     /* cap_offset in valid area */
116667866d6SJohn Levon     if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) &&
117667866d6SJohn Levon         (info->cap_offset < sizeof(*info) || info->cap_offset > info->argsz)) {
118667866d6SJohn Levon         return -EINVAL;
119667866d6SJohn Levon     }
120667866d6SJohn Levon 
121667866d6SJohn Levon     return 0;
122667866d6SJohn Levon }
123667866d6SJohn Levon 
124*ca1add16SJohn Levon static int vfio_user_device_io_get_irq_info(VFIODevice *vbasedev,
125*ca1add16SJohn Levon                                             struct vfio_irq_info *info)
126*ca1add16SJohn Levon {
127*ca1add16SJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
128*ca1add16SJohn Levon     Error *local_err = NULL;
129*ca1add16SJohn Levon     VFIOUserIRQInfo msg;
130*ca1add16SJohn Levon 
131*ca1add16SJohn Levon     memset(&msg, 0, sizeof(msg));
132*ca1add16SJohn Levon     vfio_user_request_msg(&msg.hdr, VFIO_USER_DEVICE_GET_IRQ_INFO,
133*ca1add16SJohn Levon                           sizeof(msg), 0);
134*ca1add16SJohn Levon     msg.argsz = info->argsz;
135*ca1add16SJohn Levon     msg.index = info->index;
136*ca1add16SJohn Levon 
137*ca1add16SJohn Levon     if (!vfio_user_send_wait(proxy, &msg.hdr, NULL, 0, &local_err)) {
138*ca1add16SJohn Levon         error_prepend(&local_err, "%s: ", __func__);
139*ca1add16SJohn Levon         error_report_err(local_err);
140*ca1add16SJohn Levon         return -EFAULT;
141*ca1add16SJohn Levon     }
142*ca1add16SJohn Levon 
143*ca1add16SJohn Levon     if (msg.hdr.flags & VFIO_USER_ERROR) {
144*ca1add16SJohn Levon         return -msg.hdr.error_reply;
145*ca1add16SJohn Levon     }
146*ca1add16SJohn Levon     trace_vfio_user_get_irq_info(msg.index, msg.flags, msg.count);
147*ca1add16SJohn Levon 
148*ca1add16SJohn Levon     memcpy(info, &msg.argsz, sizeof(*info));
149*ca1add16SJohn Levon     return 0;
150*ca1add16SJohn Levon }
151*ca1add16SJohn Levon 
152*ca1add16SJohn Levon static int irq_howmany(int *fdp, uint32_t cur, uint32_t max)
153*ca1add16SJohn Levon {
154*ca1add16SJohn Levon     int n = 0;
155*ca1add16SJohn Levon 
156*ca1add16SJohn Levon     if (fdp[cur] != -1) {
157*ca1add16SJohn Levon         do {
158*ca1add16SJohn Levon             n++;
159*ca1add16SJohn Levon         } while (n < max && fdp[cur + n] != -1);
160*ca1add16SJohn Levon     } else {
161*ca1add16SJohn Levon         do {
162*ca1add16SJohn Levon             n++;
163*ca1add16SJohn Levon         } while (n < max && fdp[cur + n] == -1);
164*ca1add16SJohn Levon     }
165*ca1add16SJohn Levon 
166*ca1add16SJohn Levon     return n;
167*ca1add16SJohn Levon }
168*ca1add16SJohn Levon 
169*ca1add16SJohn Levon static int vfio_user_device_io_set_irqs(VFIODevice *vbasedev,
170*ca1add16SJohn Levon                                         struct vfio_irq_set *irq)
171*ca1add16SJohn Levon {
172*ca1add16SJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
173*ca1add16SJohn Levon     g_autofree VFIOUserIRQSet *msgp = NULL;
174*ca1add16SJohn Levon     uint32_t size, nfds, send_fds, sent_fds, max;
175*ca1add16SJohn Levon     Error *local_err = NULL;
176*ca1add16SJohn Levon 
177*ca1add16SJohn Levon     if (irq->argsz < sizeof(*irq)) {
178*ca1add16SJohn Levon         error_printf("vfio_user_set_irqs argsz too small\n");
179*ca1add16SJohn Levon         return -EINVAL;
180*ca1add16SJohn Levon     }
181*ca1add16SJohn Levon 
182*ca1add16SJohn Levon     /*
183*ca1add16SJohn Levon      * Handle simple case
184*ca1add16SJohn Levon      */
185*ca1add16SJohn Levon     if ((irq->flags & VFIO_IRQ_SET_DATA_EVENTFD) == 0) {
186*ca1add16SJohn Levon         size = sizeof(VFIOUserHdr) + irq->argsz;
187*ca1add16SJohn Levon         msgp = g_malloc0(size);
188*ca1add16SJohn Levon 
189*ca1add16SJohn Levon         vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS, size, 0);
190*ca1add16SJohn Levon         msgp->argsz = irq->argsz;
191*ca1add16SJohn Levon         msgp->flags = irq->flags;
192*ca1add16SJohn Levon         msgp->index = irq->index;
193*ca1add16SJohn Levon         msgp->start = irq->start;
194*ca1add16SJohn Levon         msgp->count = irq->count;
195*ca1add16SJohn Levon         trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count,
196*ca1add16SJohn Levon                                  msgp->flags);
197*ca1add16SJohn Levon 
198*ca1add16SJohn Levon         if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) {
199*ca1add16SJohn Levon             error_prepend(&local_err, "%s: ", __func__);
200*ca1add16SJohn Levon             error_report_err(local_err);
201*ca1add16SJohn Levon             return -EFAULT;
202*ca1add16SJohn Levon         }
203*ca1add16SJohn Levon 
204*ca1add16SJohn Levon         if (msgp->hdr.flags & VFIO_USER_ERROR) {
205*ca1add16SJohn Levon             return -msgp->hdr.error_reply;
206*ca1add16SJohn Levon         }
207*ca1add16SJohn Levon 
208*ca1add16SJohn Levon         return 0;
209*ca1add16SJohn Levon     }
210*ca1add16SJohn Levon 
211*ca1add16SJohn Levon     /*
212*ca1add16SJohn Levon      * Calculate the number of FDs to send
213*ca1add16SJohn Levon      * and adjust argsz
214*ca1add16SJohn Levon      */
215*ca1add16SJohn Levon     nfds = (irq->argsz - sizeof(*irq)) / sizeof(int);
216*ca1add16SJohn Levon     irq->argsz = sizeof(*irq);
217*ca1add16SJohn Levon     msgp = g_malloc0(sizeof(*msgp));
218*ca1add16SJohn Levon     /*
219*ca1add16SJohn Levon      * Send in chunks if over max_send_fds
220*ca1add16SJohn Levon      */
221*ca1add16SJohn Levon     for (sent_fds = 0; nfds > sent_fds; sent_fds += send_fds) {
222*ca1add16SJohn Levon         VFIOUserFDs *arg_fds, loop_fds;
223*ca1add16SJohn Levon 
224*ca1add16SJohn Levon         /* must send all valid FDs or all invalid FDs in single msg */
225*ca1add16SJohn Levon         max = nfds - sent_fds;
226*ca1add16SJohn Levon         if (max > proxy->max_send_fds) {
227*ca1add16SJohn Levon             max = proxy->max_send_fds;
228*ca1add16SJohn Levon         }
229*ca1add16SJohn Levon         send_fds = irq_howmany((int *)irq->data, sent_fds, max);
230*ca1add16SJohn Levon 
231*ca1add16SJohn Levon         vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS,
232*ca1add16SJohn Levon                               sizeof(*msgp), 0);
233*ca1add16SJohn Levon         msgp->argsz = irq->argsz;
234*ca1add16SJohn Levon         msgp->flags = irq->flags;
235*ca1add16SJohn Levon         msgp->index = irq->index;
236*ca1add16SJohn Levon         msgp->start = irq->start + sent_fds;
237*ca1add16SJohn Levon         msgp->count = send_fds;
238*ca1add16SJohn Levon         trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count,
239*ca1add16SJohn Levon                                  msgp->flags);
240*ca1add16SJohn Levon 
241*ca1add16SJohn Levon         loop_fds.send_fds = send_fds;
242*ca1add16SJohn Levon         loop_fds.recv_fds = 0;
243*ca1add16SJohn Levon         loop_fds.fds = (int *)irq->data + sent_fds;
244*ca1add16SJohn Levon         arg_fds = loop_fds.fds[0] != -1 ? &loop_fds : NULL;
245*ca1add16SJohn Levon 
246*ca1add16SJohn Levon         if (!vfio_user_send_wait(proxy, &msgp->hdr, arg_fds, 0, &local_err)) {
247*ca1add16SJohn Levon             error_prepend(&local_err, "%s: ", __func__);
248*ca1add16SJohn Levon             error_report_err(local_err);
249*ca1add16SJohn Levon             return -EFAULT;
250*ca1add16SJohn Levon         }
251*ca1add16SJohn Levon 
252*ca1add16SJohn Levon         if (msgp->hdr.flags & VFIO_USER_ERROR) {
253*ca1add16SJohn Levon             return -msgp->hdr.error_reply;
254*ca1add16SJohn Levon         }
255*ca1add16SJohn Levon     }
256*ca1add16SJohn Levon 
257*ca1add16SJohn Levon     return 0;
258*ca1add16SJohn Levon }
259*ca1add16SJohn Levon 
2601ed50fcbSJohn Levon static int vfio_user_device_io_region_read(VFIODevice *vbasedev, uint8_t index,
2611ed50fcbSJohn Levon                                            off_t off, uint32_t count,
2621ed50fcbSJohn Levon                                            void *data)
2631ed50fcbSJohn Levon {
2641ed50fcbSJohn Levon     g_autofree VFIOUserRegionRW *msgp = NULL;
2651ed50fcbSJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
2661ed50fcbSJohn Levon     int size = sizeof(*msgp) + count;
2671ed50fcbSJohn Levon     Error *local_err = NULL;
2681ed50fcbSJohn Levon 
2691ed50fcbSJohn Levon     if (count > proxy->max_xfer_size) {
2701ed50fcbSJohn Levon         return -EINVAL;
2711ed50fcbSJohn Levon     }
2721ed50fcbSJohn Levon 
2731ed50fcbSJohn Levon     msgp = g_malloc0(size);
2741ed50fcbSJohn Levon     vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_READ, sizeof(*msgp), 0);
2751ed50fcbSJohn Levon     msgp->offset = off;
2761ed50fcbSJohn Levon     msgp->region = index;
2771ed50fcbSJohn Levon     msgp->count = count;
2781ed50fcbSJohn Levon     trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count);
2791ed50fcbSJohn Levon 
2801ed50fcbSJohn Levon     if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, size, &local_err)) {
2811ed50fcbSJohn Levon         error_prepend(&local_err, "%s: ", __func__);
2821ed50fcbSJohn Levon         error_report_err(local_err);
2831ed50fcbSJohn Levon         return -EFAULT;
2841ed50fcbSJohn Levon     }
2851ed50fcbSJohn Levon 
2861ed50fcbSJohn Levon     if (msgp->hdr.flags & VFIO_USER_ERROR) {
2871ed50fcbSJohn Levon         return -msgp->hdr.error_reply;
2881ed50fcbSJohn Levon     } else if (msgp->count > count) {
2891ed50fcbSJohn Levon         return -E2BIG;
2901ed50fcbSJohn Levon     } else {
2911ed50fcbSJohn Levon         memcpy(data, &msgp->data, msgp->count);
2921ed50fcbSJohn Levon     }
2931ed50fcbSJohn Levon 
2941ed50fcbSJohn Levon     return msgp->count;
2951ed50fcbSJohn Levon }
2961ed50fcbSJohn Levon 
2971ed50fcbSJohn Levon static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index,
2981ed50fcbSJohn Levon                                             off_t off, unsigned count,
2991ed50fcbSJohn Levon                                             void *data, bool post)
3001ed50fcbSJohn Levon {
3011ed50fcbSJohn Levon     g_autofree VFIOUserRegionRW *msgp = NULL;
3021ed50fcbSJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
3031ed50fcbSJohn Levon     int size = sizeof(*msgp) + count;
3041ed50fcbSJohn Levon     Error *local_err = NULL;
3051ed50fcbSJohn Levon     int ret;
3061ed50fcbSJohn Levon 
3071ed50fcbSJohn Levon     if (count > proxy->max_xfer_size) {
3081ed50fcbSJohn Levon         return -EINVAL;
3091ed50fcbSJohn Levon     }
3101ed50fcbSJohn Levon 
3111ed50fcbSJohn Levon     msgp = g_malloc0(size);
3121ed50fcbSJohn Levon     vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_WRITE, size, 0);
3131ed50fcbSJohn Levon     msgp->offset = off;
3141ed50fcbSJohn Levon     msgp->region = index;
3151ed50fcbSJohn Levon     msgp->count = count;
3161ed50fcbSJohn Levon     memcpy(&msgp->data, data, count);
3171ed50fcbSJohn Levon     trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count);
3181ed50fcbSJohn Levon 
3191ed50fcbSJohn Levon     /* Ignore post: all writes are synchronous/non-posted. */
3201ed50fcbSJohn Levon 
3211ed50fcbSJohn Levon     if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) {
3221ed50fcbSJohn Levon         error_prepend(&local_err, "%s: ", __func__);
3231ed50fcbSJohn Levon         error_report_err(local_err);
3241ed50fcbSJohn Levon         return -EFAULT;
3251ed50fcbSJohn Levon     }
3261ed50fcbSJohn Levon 
3271ed50fcbSJohn Levon     if (msgp->hdr.flags & VFIO_USER_ERROR) {
3281ed50fcbSJohn Levon         ret = -msgp->hdr.error_reply;
3291ed50fcbSJohn Levon     } else {
3301ed50fcbSJohn Levon         ret = count;
3311ed50fcbSJohn Levon     }
3321ed50fcbSJohn Levon 
3331ed50fcbSJohn Levon     return ret;
3341ed50fcbSJohn Levon }
3351ed50fcbSJohn Levon 
336667866d6SJohn Levon /*
337667866d6SJohn Levon  * Socket-based io_ops
338667866d6SJohn Levon  */
339667866d6SJohn Levon VFIODeviceIOOps vfio_user_device_io_ops_sock = {
340667866d6SJohn Levon     .get_region_info = vfio_user_device_io_get_region_info,
341*ca1add16SJohn Levon     .get_irq_info = vfio_user_device_io_get_irq_info,
342*ca1add16SJohn Levon     .set_irqs = vfio_user_device_io_set_irqs,
3431ed50fcbSJohn Levon     .region_read = vfio_user_device_io_region_read,
3441ed50fcbSJohn Levon     .region_write = vfio_user_device_io_region_write,
3451ed50fcbSJohn Levon 
346667866d6SJohn Levon };
347