xref: /qemu/hw/vfio-user/device.c (revision 98a906d9e5827b18c51d7d7485be2f21f8900cc7)
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 
5701923235SJohn Levon void vfio_user_device_reset(VFIOUserProxy *proxy)
5801923235SJohn Levon {
5901923235SJohn Levon     Error *local_err = NULL;
6001923235SJohn Levon     VFIOUserHdr hdr;
6101923235SJohn Levon 
6201923235SJohn Levon     vfio_user_request_msg(&hdr, VFIO_USER_DEVICE_RESET, sizeof(hdr), 0);
6301923235SJohn Levon 
6401923235SJohn Levon     if (!vfio_user_send_wait(proxy, &hdr, NULL, 0, &local_err)) {
6501923235SJohn Levon         error_prepend(&local_err, "%s: ", __func__);
6601923235SJohn Levon         error_report_err(local_err);
6701923235SJohn Levon         return;
6801923235SJohn Levon     }
6901923235SJohn Levon 
7001923235SJohn Levon     if (hdr.flags & VFIO_USER_ERROR) {
7101923235SJohn Levon         error_printf("reset reply error %d\n", hdr.error_reply);
7201923235SJohn Levon     }
7301923235SJohn Levon }
7401923235SJohn 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);
113*98a906d9SJohn Levon 
114*98a906d9SJohn Levon     /*
115*98a906d9SJohn Levon      * If at least one region is directly mapped into the VM, then we can no
116*98a906d9SJohn Levon      * longer rely on the sequential nature of vfio-user request handling to
117*98a906d9SJohn Levon      * ensure that posted writes are completed before a subsequent read. In this
118*98a906d9SJohn Levon      * case, disable posted write support. This is a per-device property, not
119*98a906d9SJohn Levon      * per-region.
120*98a906d9SJohn Levon      */
121*98a906d9SJohn Levon     if (info->flags & VFIO_REGION_INFO_FLAG_MMAP) {
122*98a906d9SJohn Levon         vfio_user_disable_posted_writes(proxy);
123667866d6SJohn Levon     }
124667866d6SJohn Levon 
125*98a906d9SJohn Levon     return 0;
126*98a906d9SJohn Levon }
127667866d6SJohn Levon 
128667866d6SJohn Levon static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev,
129667866d6SJohn Levon                                                struct vfio_region_info *info,
130667866d6SJohn Levon                                                int *fd)
131667866d6SJohn Levon {
132667866d6SJohn Levon     VFIOUserFDs fds = { 0, 1, fd};
133667866d6SJohn Levon     int ret;
134667866d6SJohn Levon 
135667866d6SJohn Levon     if (info->index > vbasedev->num_regions) {
136667866d6SJohn Levon         return -EINVAL;
137667866d6SJohn Levon     }
138667866d6SJohn Levon 
139667866d6SJohn Levon     ret = vfio_user_get_region_info(vbasedev->proxy, info, &fds);
140667866d6SJohn Levon     if (ret) {
141667866d6SJohn Levon         return ret;
142667866d6SJohn Levon     }
143667866d6SJohn Levon 
144667866d6SJohn Levon     /* cap_offset in valid area */
145667866d6SJohn Levon     if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) &&
146667866d6SJohn Levon         (info->cap_offset < sizeof(*info) || info->cap_offset > info->argsz)) {
147667866d6SJohn Levon         return -EINVAL;
148667866d6SJohn Levon     }
149667866d6SJohn Levon 
150667866d6SJohn Levon     return 0;
151667866d6SJohn Levon }
152667866d6SJohn Levon 
153ca1add16SJohn Levon static int vfio_user_device_io_get_irq_info(VFIODevice *vbasedev,
154ca1add16SJohn Levon                                             struct vfio_irq_info *info)
155ca1add16SJohn Levon {
156ca1add16SJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
157ca1add16SJohn Levon     Error *local_err = NULL;
158ca1add16SJohn Levon     VFIOUserIRQInfo msg;
159ca1add16SJohn Levon 
160ca1add16SJohn Levon     memset(&msg, 0, sizeof(msg));
161ca1add16SJohn Levon     vfio_user_request_msg(&msg.hdr, VFIO_USER_DEVICE_GET_IRQ_INFO,
162ca1add16SJohn Levon                           sizeof(msg), 0);
163ca1add16SJohn Levon     msg.argsz = info->argsz;
164ca1add16SJohn Levon     msg.index = info->index;
165ca1add16SJohn Levon 
166ca1add16SJohn Levon     if (!vfio_user_send_wait(proxy, &msg.hdr, NULL, 0, &local_err)) {
167ca1add16SJohn Levon         error_prepend(&local_err, "%s: ", __func__);
168ca1add16SJohn Levon         error_report_err(local_err);
169ca1add16SJohn Levon         return -EFAULT;
170ca1add16SJohn Levon     }
171ca1add16SJohn Levon 
172ca1add16SJohn Levon     if (msg.hdr.flags & VFIO_USER_ERROR) {
173ca1add16SJohn Levon         return -msg.hdr.error_reply;
174ca1add16SJohn Levon     }
175ca1add16SJohn Levon     trace_vfio_user_get_irq_info(msg.index, msg.flags, msg.count);
176ca1add16SJohn Levon 
177ca1add16SJohn Levon     memcpy(info, &msg.argsz, sizeof(*info));
178ca1add16SJohn Levon     return 0;
179ca1add16SJohn Levon }
180ca1add16SJohn Levon 
181ca1add16SJohn Levon static int irq_howmany(int *fdp, uint32_t cur, uint32_t max)
182ca1add16SJohn Levon {
183ca1add16SJohn Levon     int n = 0;
184ca1add16SJohn Levon 
185ca1add16SJohn Levon     if (fdp[cur] != -1) {
186ca1add16SJohn Levon         do {
187ca1add16SJohn Levon             n++;
188ca1add16SJohn Levon         } while (n < max && fdp[cur + n] != -1);
189ca1add16SJohn Levon     } else {
190ca1add16SJohn Levon         do {
191ca1add16SJohn Levon             n++;
192ca1add16SJohn Levon         } while (n < max && fdp[cur + n] == -1);
193ca1add16SJohn Levon     }
194ca1add16SJohn Levon 
195ca1add16SJohn Levon     return n;
196ca1add16SJohn Levon }
197ca1add16SJohn Levon 
198ca1add16SJohn Levon static int vfio_user_device_io_set_irqs(VFIODevice *vbasedev,
199ca1add16SJohn Levon                                         struct vfio_irq_set *irq)
200ca1add16SJohn Levon {
201ca1add16SJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
202ca1add16SJohn Levon     g_autofree VFIOUserIRQSet *msgp = NULL;
203ca1add16SJohn Levon     uint32_t size, nfds, send_fds, sent_fds, max;
204ca1add16SJohn Levon     Error *local_err = NULL;
205ca1add16SJohn Levon 
206ca1add16SJohn Levon     if (irq->argsz < sizeof(*irq)) {
207ca1add16SJohn Levon         error_printf("vfio_user_set_irqs argsz too small\n");
208ca1add16SJohn Levon         return -EINVAL;
209ca1add16SJohn Levon     }
210ca1add16SJohn Levon 
211ca1add16SJohn Levon     /*
212ca1add16SJohn Levon      * Handle simple case
213ca1add16SJohn Levon      */
214ca1add16SJohn Levon     if ((irq->flags & VFIO_IRQ_SET_DATA_EVENTFD) == 0) {
215ca1add16SJohn Levon         size = sizeof(VFIOUserHdr) + irq->argsz;
216ca1add16SJohn Levon         msgp = g_malloc0(size);
217ca1add16SJohn Levon 
218ca1add16SJohn Levon         vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS, size, 0);
219ca1add16SJohn Levon         msgp->argsz = irq->argsz;
220ca1add16SJohn Levon         msgp->flags = irq->flags;
221ca1add16SJohn Levon         msgp->index = irq->index;
222ca1add16SJohn Levon         msgp->start = irq->start;
223ca1add16SJohn Levon         msgp->count = irq->count;
224ca1add16SJohn Levon         trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count,
225ca1add16SJohn Levon                                  msgp->flags);
226ca1add16SJohn Levon 
227ca1add16SJohn Levon         if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) {
228ca1add16SJohn Levon             error_prepend(&local_err, "%s: ", __func__);
229ca1add16SJohn Levon             error_report_err(local_err);
230ca1add16SJohn Levon             return -EFAULT;
231ca1add16SJohn Levon         }
232ca1add16SJohn Levon 
233ca1add16SJohn Levon         if (msgp->hdr.flags & VFIO_USER_ERROR) {
234ca1add16SJohn Levon             return -msgp->hdr.error_reply;
235ca1add16SJohn Levon         }
236ca1add16SJohn Levon 
237ca1add16SJohn Levon         return 0;
238ca1add16SJohn Levon     }
239ca1add16SJohn Levon 
240ca1add16SJohn Levon     /*
241ca1add16SJohn Levon      * Calculate the number of FDs to send
242ca1add16SJohn Levon      * and adjust argsz
243ca1add16SJohn Levon      */
244ca1add16SJohn Levon     nfds = (irq->argsz - sizeof(*irq)) / sizeof(int);
245ca1add16SJohn Levon     irq->argsz = sizeof(*irq);
246ca1add16SJohn Levon     msgp = g_malloc0(sizeof(*msgp));
247ca1add16SJohn Levon     /*
248ca1add16SJohn Levon      * Send in chunks if over max_send_fds
249ca1add16SJohn Levon      */
250ca1add16SJohn Levon     for (sent_fds = 0; nfds > sent_fds; sent_fds += send_fds) {
251ca1add16SJohn Levon         VFIOUserFDs *arg_fds, loop_fds;
252ca1add16SJohn Levon 
253ca1add16SJohn Levon         /* must send all valid FDs or all invalid FDs in single msg */
254ca1add16SJohn Levon         max = nfds - sent_fds;
255ca1add16SJohn Levon         if (max > proxy->max_send_fds) {
256ca1add16SJohn Levon             max = proxy->max_send_fds;
257ca1add16SJohn Levon         }
258ca1add16SJohn Levon         send_fds = irq_howmany((int *)irq->data, sent_fds, max);
259ca1add16SJohn Levon 
260ca1add16SJohn Levon         vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS,
261ca1add16SJohn Levon                               sizeof(*msgp), 0);
262ca1add16SJohn Levon         msgp->argsz = irq->argsz;
263ca1add16SJohn Levon         msgp->flags = irq->flags;
264ca1add16SJohn Levon         msgp->index = irq->index;
265ca1add16SJohn Levon         msgp->start = irq->start + sent_fds;
266ca1add16SJohn Levon         msgp->count = send_fds;
267ca1add16SJohn Levon         trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count,
268ca1add16SJohn Levon                                  msgp->flags);
269ca1add16SJohn Levon 
270ca1add16SJohn Levon         loop_fds.send_fds = send_fds;
271ca1add16SJohn Levon         loop_fds.recv_fds = 0;
272ca1add16SJohn Levon         loop_fds.fds = (int *)irq->data + sent_fds;
273ca1add16SJohn Levon         arg_fds = loop_fds.fds[0] != -1 ? &loop_fds : NULL;
274ca1add16SJohn Levon 
275ca1add16SJohn Levon         if (!vfio_user_send_wait(proxy, &msgp->hdr, arg_fds, 0, &local_err)) {
276ca1add16SJohn Levon             error_prepend(&local_err, "%s: ", __func__);
277ca1add16SJohn Levon             error_report_err(local_err);
278ca1add16SJohn Levon             return -EFAULT;
279ca1add16SJohn Levon         }
280ca1add16SJohn Levon 
281ca1add16SJohn Levon         if (msgp->hdr.flags & VFIO_USER_ERROR) {
282ca1add16SJohn Levon             return -msgp->hdr.error_reply;
283ca1add16SJohn Levon         }
284ca1add16SJohn Levon     }
285ca1add16SJohn Levon 
286ca1add16SJohn Levon     return 0;
287ca1add16SJohn Levon }
288ca1add16SJohn Levon 
2891ed50fcbSJohn Levon static int vfio_user_device_io_region_read(VFIODevice *vbasedev, uint8_t index,
2901ed50fcbSJohn Levon                                            off_t off, uint32_t count,
2911ed50fcbSJohn Levon                                            void *data)
2921ed50fcbSJohn Levon {
2931ed50fcbSJohn Levon     g_autofree VFIOUserRegionRW *msgp = NULL;
2941ed50fcbSJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
2951ed50fcbSJohn Levon     int size = sizeof(*msgp) + count;
2961ed50fcbSJohn Levon     Error *local_err = NULL;
2971ed50fcbSJohn Levon 
2981ed50fcbSJohn Levon     if (count > proxy->max_xfer_size) {
2991ed50fcbSJohn Levon         return -EINVAL;
3001ed50fcbSJohn Levon     }
3011ed50fcbSJohn Levon 
3021ed50fcbSJohn Levon     msgp = g_malloc0(size);
3031ed50fcbSJohn Levon     vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_READ, sizeof(*msgp), 0);
3041ed50fcbSJohn Levon     msgp->offset = off;
3051ed50fcbSJohn Levon     msgp->region = index;
3061ed50fcbSJohn Levon     msgp->count = count;
3071ed50fcbSJohn Levon     trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count);
3081ed50fcbSJohn Levon 
3091ed50fcbSJohn Levon     if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, size, &local_err)) {
3101ed50fcbSJohn Levon         error_prepend(&local_err, "%s: ", __func__);
3111ed50fcbSJohn Levon         error_report_err(local_err);
3121ed50fcbSJohn Levon         return -EFAULT;
3131ed50fcbSJohn Levon     }
3141ed50fcbSJohn Levon 
3151ed50fcbSJohn Levon     if (msgp->hdr.flags & VFIO_USER_ERROR) {
3161ed50fcbSJohn Levon         return -msgp->hdr.error_reply;
3171ed50fcbSJohn Levon     } else if (msgp->count > count) {
3181ed50fcbSJohn Levon         return -E2BIG;
3191ed50fcbSJohn Levon     } else {
3201ed50fcbSJohn Levon         memcpy(data, &msgp->data, msgp->count);
3211ed50fcbSJohn Levon     }
3221ed50fcbSJohn Levon 
3231ed50fcbSJohn Levon     return msgp->count;
3241ed50fcbSJohn Levon }
3251ed50fcbSJohn Levon 
326*98a906d9SJohn Levon /*
327*98a906d9SJohn Levon  * If this is a posted write, and VFIO_PROXY_NO_POST is not set, then we are OK
328*98a906d9SJohn Levon  * to send the write to the socket without waiting for the server's reply:
329*98a906d9SJohn Levon  * a subsequent read (of any region) will not pass the posted write, as all
330*98a906d9SJohn Levon  * messages are handled sequentially.
331*98a906d9SJohn Levon  */
3321ed50fcbSJohn Levon static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index,
3331ed50fcbSJohn Levon                                             off_t off, unsigned count,
3341ed50fcbSJohn Levon                                             void *data, bool post)
3351ed50fcbSJohn Levon {
336*98a906d9SJohn Levon     VFIOUserRegionRW *msgp = NULL;
3371ed50fcbSJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
3381ed50fcbSJohn Levon     int size = sizeof(*msgp) + count;
3391ed50fcbSJohn Levon     Error *local_err = NULL;
340*98a906d9SJohn Levon     int flags = 0;
3411ed50fcbSJohn Levon     int ret;
3421ed50fcbSJohn Levon 
3431ed50fcbSJohn Levon     if (count > proxy->max_xfer_size) {
3441ed50fcbSJohn Levon         return -EINVAL;
3451ed50fcbSJohn Levon     }
3461ed50fcbSJohn Levon 
347*98a906d9SJohn Levon     if (proxy->flags & VFIO_PROXY_NO_POST) {
348*98a906d9SJohn Levon         post = false;
349*98a906d9SJohn Levon     }
350*98a906d9SJohn Levon 
351*98a906d9SJohn Levon     if (post) {
352*98a906d9SJohn Levon         flags |= VFIO_USER_NO_REPLY;
353*98a906d9SJohn Levon     }
354*98a906d9SJohn Levon 
3551ed50fcbSJohn Levon     msgp = g_malloc0(size);
356*98a906d9SJohn Levon     vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_WRITE, size, flags);
3571ed50fcbSJohn Levon     msgp->offset = off;
3581ed50fcbSJohn Levon     msgp->region = index;
3591ed50fcbSJohn Levon     msgp->count = count;
3601ed50fcbSJohn Levon     memcpy(&msgp->data, data, count);
3611ed50fcbSJohn Levon     trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count);
3621ed50fcbSJohn Levon 
363*98a906d9SJohn Levon     /* async send will free msg after it's sent */
364*98a906d9SJohn Levon     if (post) {
365*98a906d9SJohn Levon         if (!vfio_user_send_async(proxy, &msgp->hdr, NULL, &local_err)) {
366*98a906d9SJohn Levon             error_prepend(&local_err, "%s: ", __func__);
367*98a906d9SJohn Levon             error_report_err(local_err);
368*98a906d9SJohn Levon             return -EFAULT;
369*98a906d9SJohn Levon         }
370*98a906d9SJohn Levon 
371*98a906d9SJohn Levon         return count;
372*98a906d9SJohn Levon     }
3731ed50fcbSJohn Levon 
3741ed50fcbSJohn Levon     if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) {
3751ed50fcbSJohn Levon         error_prepend(&local_err, "%s: ", __func__);
3761ed50fcbSJohn Levon         error_report_err(local_err);
377*98a906d9SJohn Levon         g_free(msgp);
3781ed50fcbSJohn Levon         return -EFAULT;
3791ed50fcbSJohn Levon     }
3801ed50fcbSJohn Levon 
3811ed50fcbSJohn Levon     if (msgp->hdr.flags & VFIO_USER_ERROR) {
3821ed50fcbSJohn Levon         ret = -msgp->hdr.error_reply;
3831ed50fcbSJohn Levon     } else {
3841ed50fcbSJohn Levon         ret = count;
3851ed50fcbSJohn Levon     }
3861ed50fcbSJohn Levon 
387*98a906d9SJohn Levon     g_free(msgp);
3881ed50fcbSJohn Levon     return ret;
3891ed50fcbSJohn Levon }
3901ed50fcbSJohn Levon 
391667866d6SJohn Levon /*
392667866d6SJohn Levon  * Socket-based io_ops
393667866d6SJohn Levon  */
394667866d6SJohn Levon VFIODeviceIOOps vfio_user_device_io_ops_sock = {
395667866d6SJohn Levon     .get_region_info = vfio_user_device_io_get_region_info,
396ca1add16SJohn Levon     .get_irq_info = vfio_user_device_io_get_irq_info,
397ca1add16SJohn Levon     .set_irqs = vfio_user_device_io_set_irqs,
3981ed50fcbSJohn Levon     .region_read = vfio_user_device_io_region_read,
3991ed50fcbSJohn Levon     .region_write = vfio_user_device_io_region_write,
4001ed50fcbSJohn Levon 
401667866d6SJohn Levon };
402