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