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*1ed50fcbSJohn Levon static int vfio_user_device_io_region_read(VFIODevice *vbasedev, uint8_t index, 125*1ed50fcbSJohn Levon off_t off, uint32_t count, 126*1ed50fcbSJohn Levon void *data) 127*1ed50fcbSJohn Levon { 128*1ed50fcbSJohn Levon g_autofree VFIOUserRegionRW *msgp = NULL; 129*1ed50fcbSJohn Levon VFIOUserProxy *proxy = vbasedev->proxy; 130*1ed50fcbSJohn Levon int size = sizeof(*msgp) + count; 131*1ed50fcbSJohn Levon Error *local_err = NULL; 132*1ed50fcbSJohn Levon 133*1ed50fcbSJohn Levon if (count > proxy->max_xfer_size) { 134*1ed50fcbSJohn Levon return -EINVAL; 135*1ed50fcbSJohn Levon } 136*1ed50fcbSJohn Levon 137*1ed50fcbSJohn Levon msgp = g_malloc0(size); 138*1ed50fcbSJohn Levon vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_READ, sizeof(*msgp), 0); 139*1ed50fcbSJohn Levon msgp->offset = off; 140*1ed50fcbSJohn Levon msgp->region = index; 141*1ed50fcbSJohn Levon msgp->count = count; 142*1ed50fcbSJohn Levon trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count); 143*1ed50fcbSJohn Levon 144*1ed50fcbSJohn Levon if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, size, &local_err)) { 145*1ed50fcbSJohn Levon error_prepend(&local_err, "%s: ", __func__); 146*1ed50fcbSJohn Levon error_report_err(local_err); 147*1ed50fcbSJohn Levon return -EFAULT; 148*1ed50fcbSJohn Levon } 149*1ed50fcbSJohn Levon 150*1ed50fcbSJohn Levon if (msgp->hdr.flags & VFIO_USER_ERROR) { 151*1ed50fcbSJohn Levon return -msgp->hdr.error_reply; 152*1ed50fcbSJohn Levon } else if (msgp->count > count) { 153*1ed50fcbSJohn Levon return -E2BIG; 154*1ed50fcbSJohn Levon } else { 155*1ed50fcbSJohn Levon memcpy(data, &msgp->data, msgp->count); 156*1ed50fcbSJohn Levon } 157*1ed50fcbSJohn Levon 158*1ed50fcbSJohn Levon return msgp->count; 159*1ed50fcbSJohn Levon } 160*1ed50fcbSJohn Levon 161*1ed50fcbSJohn Levon static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index, 162*1ed50fcbSJohn Levon off_t off, unsigned count, 163*1ed50fcbSJohn Levon void *data, bool post) 164*1ed50fcbSJohn Levon { 165*1ed50fcbSJohn Levon g_autofree VFIOUserRegionRW *msgp = NULL; 166*1ed50fcbSJohn Levon VFIOUserProxy *proxy = vbasedev->proxy; 167*1ed50fcbSJohn Levon int size = sizeof(*msgp) + count; 168*1ed50fcbSJohn Levon Error *local_err = NULL; 169*1ed50fcbSJohn Levon int ret; 170*1ed50fcbSJohn Levon 171*1ed50fcbSJohn Levon if (count > proxy->max_xfer_size) { 172*1ed50fcbSJohn Levon return -EINVAL; 173*1ed50fcbSJohn Levon } 174*1ed50fcbSJohn Levon 175*1ed50fcbSJohn Levon msgp = g_malloc0(size); 176*1ed50fcbSJohn Levon vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_WRITE, size, 0); 177*1ed50fcbSJohn Levon msgp->offset = off; 178*1ed50fcbSJohn Levon msgp->region = index; 179*1ed50fcbSJohn Levon msgp->count = count; 180*1ed50fcbSJohn Levon memcpy(&msgp->data, data, count); 181*1ed50fcbSJohn Levon trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count); 182*1ed50fcbSJohn Levon 183*1ed50fcbSJohn Levon /* Ignore post: all writes are synchronous/non-posted. */ 184*1ed50fcbSJohn Levon 185*1ed50fcbSJohn Levon if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) { 186*1ed50fcbSJohn Levon error_prepend(&local_err, "%s: ", __func__); 187*1ed50fcbSJohn Levon error_report_err(local_err); 188*1ed50fcbSJohn Levon return -EFAULT; 189*1ed50fcbSJohn Levon } 190*1ed50fcbSJohn Levon 191*1ed50fcbSJohn Levon if (msgp->hdr.flags & VFIO_USER_ERROR) { 192*1ed50fcbSJohn Levon ret = -msgp->hdr.error_reply; 193*1ed50fcbSJohn Levon } else { 194*1ed50fcbSJohn Levon ret = count; 195*1ed50fcbSJohn Levon } 196*1ed50fcbSJohn Levon 197*1ed50fcbSJohn Levon return ret; 198*1ed50fcbSJohn Levon } 199*1ed50fcbSJohn Levon 200667866d6SJohn Levon /* 201667866d6SJohn Levon * Socket-based io_ops 202667866d6SJohn Levon */ 203667866d6SJohn Levon VFIODeviceIOOps vfio_user_device_io_ops_sock = { 204667866d6SJohn Levon .get_region_info = vfio_user_device_io_get_region_info, 205*1ed50fcbSJohn Levon .region_read = vfio_user_device_io_region_read, 206*1ed50fcbSJohn Levon .region_write = vfio_user_device_io_region_write, 207*1ed50fcbSJohn Levon 208667866d6SJohn Levon }; 209