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