xref: /qemu/hw/vfio-user/device.c (revision 667866d66620e9f545eb3bd4f065d9ab81bda727)
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 }
56*667866d6SJohn Levon 
57*667866d6SJohn Levon static int vfio_user_get_region_info(VFIOUserProxy *proxy,
58*667866d6SJohn Levon                                      struct vfio_region_info *info,
59*667866d6SJohn Levon                                      VFIOUserFDs *fds)
60*667866d6SJohn Levon {
61*667866d6SJohn Levon     g_autofree VFIOUserRegionInfo *msgp = NULL;
62*667866d6SJohn Levon     Error *local_err = NULL;
63*667866d6SJohn Levon     uint32_t size;
64*667866d6SJohn Levon 
65*667866d6SJohn Levon     /* data returned can be larger than vfio_region_info */
66*667866d6SJohn Levon     if (info->argsz < sizeof(*info)) {
67*667866d6SJohn Levon         error_printf("vfio_user_get_region_info argsz too small\n");
68*667866d6SJohn Levon         return -E2BIG;
69*667866d6SJohn Levon     }
70*667866d6SJohn Levon     if (fds != NULL && fds->send_fds != 0) {
71*667866d6SJohn Levon         error_printf("vfio_user_get_region_info can't send FDs\n");
72*667866d6SJohn Levon         return -EINVAL;
73*667866d6SJohn Levon     }
74*667866d6SJohn Levon 
75*667866d6SJohn Levon     size = info->argsz + sizeof(VFIOUserHdr);
76*667866d6SJohn Levon     msgp = g_malloc0(size);
77*667866d6SJohn Levon 
78*667866d6SJohn Levon     vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_GET_REGION_INFO,
79*667866d6SJohn Levon                           sizeof(*msgp), 0);
80*667866d6SJohn Levon     msgp->argsz = info->argsz;
81*667866d6SJohn Levon     msgp->index = info->index;
82*667866d6SJohn Levon 
83*667866d6SJohn Levon     if (!vfio_user_send_wait(proxy, &msgp->hdr, fds, size, &local_err)) {
84*667866d6SJohn Levon         error_prepend(&local_err, "%s: ", __func__);
85*667866d6SJohn Levon         error_report_err(local_err);
86*667866d6SJohn Levon         return -EFAULT;
87*667866d6SJohn Levon     }
88*667866d6SJohn Levon 
89*667866d6SJohn Levon     if (msgp->hdr.flags & VFIO_USER_ERROR) {
90*667866d6SJohn Levon         return -msgp->hdr.error_reply;
91*667866d6SJohn Levon     }
92*667866d6SJohn Levon     trace_vfio_user_get_region_info(msgp->index, msgp->flags, msgp->size);
93*667866d6SJohn Levon 
94*667866d6SJohn Levon     memcpy(info, &msgp->argsz, info->argsz);
95*667866d6SJohn Levon     return 0;
96*667866d6SJohn Levon }
97*667866d6SJohn Levon 
98*667866d6SJohn Levon 
99*667866d6SJohn Levon static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev,
100*667866d6SJohn Levon                                                struct vfio_region_info *info,
101*667866d6SJohn Levon                                                int *fd)
102*667866d6SJohn Levon {
103*667866d6SJohn Levon     VFIOUserFDs fds = { 0, 1, fd};
104*667866d6SJohn Levon     int ret;
105*667866d6SJohn Levon 
106*667866d6SJohn Levon     if (info->index > vbasedev->num_regions) {
107*667866d6SJohn Levon         return -EINVAL;
108*667866d6SJohn Levon     }
109*667866d6SJohn Levon 
110*667866d6SJohn Levon     ret = vfio_user_get_region_info(vbasedev->proxy, info, &fds);
111*667866d6SJohn Levon     if (ret) {
112*667866d6SJohn Levon         return ret;
113*667866d6SJohn Levon     }
114*667866d6SJohn Levon 
115*667866d6SJohn Levon     /* cap_offset in valid area */
116*667866d6SJohn Levon     if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) &&
117*667866d6SJohn Levon         (info->cap_offset < sizeof(*info) || info->cap_offset > info->argsz)) {
118*667866d6SJohn Levon         return -EINVAL;
119*667866d6SJohn Levon     }
120*667866d6SJohn Levon 
121*667866d6SJohn Levon     return 0;
122*667866d6SJohn Levon }
123*667866d6SJohn Levon 
124*667866d6SJohn Levon /*
125*667866d6SJohn Levon  * Socket-based io_ops
126*667866d6SJohn Levon  */
127*667866d6SJohn Levon VFIODeviceIOOps vfio_user_device_io_ops_sock = {
128*667866d6SJohn Levon     .get_region_info = vfio_user_device_io_get_region_info,
129*667866d6SJohn Levon };
130