xref: /openbmc/qemu/hw/virtio/vhost-user.c (revision 07a32d6b)
1 /*
2  * vhost-user
3  *
4  * Copyright (c) 2013 Virtual Open Systems Sarl.
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7  * See the COPYING file in the top-level directory.
8  *
9  */
10 
11 #include "hw/virtio/vhost.h"
12 #include "hw/virtio/vhost-backend.h"
13 #include "sysemu/char.h"
14 #include "sysemu/kvm.h"
15 #include "qemu/error-report.h"
16 #include "qemu/sockets.h"
17 
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 #include <linux/vhost.h>
24 
25 #define VHOST_MEMORY_MAX_NREGIONS    8
26 
27 typedef enum VhostUserRequest {
28     VHOST_USER_NONE = 0,
29     VHOST_USER_GET_FEATURES = 1,
30     VHOST_USER_SET_FEATURES = 2,
31     VHOST_USER_SET_OWNER = 3,
32     VHOST_USER_RESET_OWNER = 4,
33     VHOST_USER_SET_MEM_TABLE = 5,
34     VHOST_USER_SET_LOG_BASE = 6,
35     VHOST_USER_SET_LOG_FD = 7,
36     VHOST_USER_SET_VRING_NUM = 8,
37     VHOST_USER_SET_VRING_ADDR = 9,
38     VHOST_USER_SET_VRING_BASE = 10,
39     VHOST_USER_GET_VRING_BASE = 11,
40     VHOST_USER_SET_VRING_KICK = 12,
41     VHOST_USER_SET_VRING_CALL = 13,
42     VHOST_USER_SET_VRING_ERR = 14,
43     VHOST_USER_MAX
44 } VhostUserRequest;
45 
46 typedef struct VhostUserMemoryRegion {
47     uint64_t guest_phys_addr;
48     uint64_t memory_size;
49     uint64_t userspace_addr;
50 } VhostUserMemoryRegion;
51 
52 typedef struct VhostUserMemory {
53     uint32_t nregions;
54     uint32_t padding;
55     VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
56 } VhostUserMemory;
57 
58 typedef struct VhostUserMsg {
59     VhostUserRequest request;
60 
61 #define VHOST_USER_VERSION_MASK     (0x3)
62 #define VHOST_USER_REPLY_MASK       (0x1<<2)
63     uint32_t flags;
64     uint32_t size; /* the following payload size */
65     union {
66 #define VHOST_USER_VRING_IDX_MASK   (0xff)
67 #define VHOST_USER_VRING_NOFD_MASK  (0x1<<8)
68         uint64_t u64;
69         struct vhost_vring_state state;
70         struct vhost_vring_addr addr;
71         VhostUserMemory memory;
72     };
73 } QEMU_PACKED VhostUserMsg;
74 
75 static VhostUserMsg m __attribute__ ((unused));
76 #define VHOST_USER_HDR_SIZE (sizeof(m.request) \
77                             + sizeof(m.flags) \
78                             + sizeof(m.size))
79 
80 #define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE)
81 
82 /* The version of the protocol we support */
83 #define VHOST_USER_VERSION    (0x1)
84 
85 static bool ioeventfd_enabled(void)
86 {
87     return kvm_enabled() && kvm_eventfds_enabled();
88 }
89 
90 static unsigned long int ioctl_to_vhost_user_request[VHOST_USER_MAX] = {
91     -1,                     /* VHOST_USER_NONE */
92     VHOST_GET_FEATURES,     /* VHOST_USER_GET_FEATURES */
93     VHOST_SET_FEATURES,     /* VHOST_USER_SET_FEATURES */
94     VHOST_SET_OWNER,        /* VHOST_USER_SET_OWNER */
95     VHOST_RESET_OWNER,      /* VHOST_USER_RESET_OWNER */
96     VHOST_SET_MEM_TABLE,    /* VHOST_USER_SET_MEM_TABLE */
97     VHOST_SET_LOG_BASE,     /* VHOST_USER_SET_LOG_BASE */
98     VHOST_SET_LOG_FD,       /* VHOST_USER_SET_LOG_FD */
99     VHOST_SET_VRING_NUM,    /* VHOST_USER_SET_VRING_NUM */
100     VHOST_SET_VRING_ADDR,   /* VHOST_USER_SET_VRING_ADDR */
101     VHOST_SET_VRING_BASE,   /* VHOST_USER_SET_VRING_BASE */
102     VHOST_GET_VRING_BASE,   /* VHOST_USER_GET_VRING_BASE */
103     VHOST_SET_VRING_KICK,   /* VHOST_USER_SET_VRING_KICK */
104     VHOST_SET_VRING_CALL,   /* VHOST_USER_SET_VRING_CALL */
105     VHOST_SET_VRING_ERR     /* VHOST_USER_SET_VRING_ERR */
106 };
107 
108 static VhostUserRequest vhost_user_request_translate(unsigned long int request)
109 {
110     VhostUserRequest idx;
111 
112     for (idx = 0; idx < VHOST_USER_MAX; idx++) {
113         if (ioctl_to_vhost_user_request[idx] == request) {
114             break;
115         }
116     }
117 
118     return (idx == VHOST_USER_MAX) ? VHOST_USER_NONE : idx;
119 }
120 
121 static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
122 {
123     CharDriverState *chr = dev->opaque;
124     uint8_t *p = (uint8_t *) msg;
125     int r, size = VHOST_USER_HDR_SIZE;
126 
127     r = qemu_chr_fe_read_all(chr, p, size);
128     if (r != size) {
129         error_report("Failed to read msg header. Read %d instead of %d.\n", r,
130                 size);
131         goto fail;
132     }
133 
134     /* validate received flags */
135     if (msg->flags != (VHOST_USER_REPLY_MASK | VHOST_USER_VERSION)) {
136         error_report("Failed to read msg header."
137                 " Flags 0x%x instead of 0x%x.\n", msg->flags,
138                 VHOST_USER_REPLY_MASK | VHOST_USER_VERSION);
139         goto fail;
140     }
141 
142     /* validate message size is sane */
143     if (msg->size > VHOST_USER_PAYLOAD_SIZE) {
144         error_report("Failed to read msg header."
145                 " Size %d exceeds the maximum %zu.\n", msg->size,
146                 VHOST_USER_PAYLOAD_SIZE);
147         goto fail;
148     }
149 
150     if (msg->size) {
151         p += VHOST_USER_HDR_SIZE;
152         size = msg->size;
153         r = qemu_chr_fe_read_all(chr, p, size);
154         if (r != size) {
155             error_report("Failed to read msg payload."
156                          " Read %d instead of %d.\n", r, msg->size);
157             goto fail;
158         }
159     }
160 
161     return 0;
162 
163 fail:
164     return -1;
165 }
166 
167 static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg,
168                             int *fds, int fd_num)
169 {
170     CharDriverState *chr = dev->opaque;
171     int size = VHOST_USER_HDR_SIZE + msg->size;
172 
173     if (fd_num) {
174         qemu_chr_fe_set_msgfds(chr, fds, fd_num);
175     }
176 
177     return qemu_chr_fe_write_all(chr, (const uint8_t *) msg, size) == size ?
178             0 : -1;
179 }
180 
181 static int vhost_user_call(struct vhost_dev *dev, unsigned long int request,
182         void *arg)
183 {
184     VhostUserMsg msg;
185     VhostUserRequest msg_request;
186     RAMBlock *block = 0;
187     struct vhost_vring_file *file = 0;
188     int need_reply = 0;
189     int fds[VHOST_MEMORY_MAX_NREGIONS];
190     size_t fd_num = 0;
191 
192     assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
193 
194     msg_request = vhost_user_request_translate(request);
195     msg.request = msg_request;
196     msg.flags = VHOST_USER_VERSION;
197     msg.size = 0;
198 
199     switch (request) {
200     case VHOST_GET_FEATURES:
201         need_reply = 1;
202         break;
203 
204     case VHOST_SET_FEATURES:
205     case VHOST_SET_LOG_BASE:
206         msg.u64 = *((__u64 *) arg);
207         msg.size = sizeof(m.u64);
208         break;
209 
210     case VHOST_SET_OWNER:
211     case VHOST_RESET_OWNER:
212         break;
213 
214     case VHOST_SET_MEM_TABLE:
215         QTAILQ_FOREACH(block, &ram_list.blocks, next)
216         {
217             if (block->fd > 0) {
218                 msg.memory.regions[fd_num].userspace_addr =
219                     (uintptr_t) block->host;
220                 msg.memory.regions[fd_num].memory_size = block->length;
221                 msg.memory.regions[fd_num].guest_phys_addr = block->offset;
222                 fds[fd_num++] = block->fd;
223             }
224         }
225 
226         msg.memory.nregions = fd_num;
227 
228         if (!fd_num) {
229             error_report("Failed initializing vhost-user memory map\n"
230                     "consider using -object memory-backend-file share=on\n");
231             return -1;
232         }
233 
234         msg.size = sizeof(m.memory.nregions);
235         msg.size += sizeof(m.memory.padding);
236         msg.size += fd_num * sizeof(VhostUserMemoryRegion);
237 
238         break;
239 
240     case VHOST_SET_LOG_FD:
241         fds[fd_num++] = *((int *) arg);
242         break;
243 
244     case VHOST_SET_VRING_NUM:
245     case VHOST_SET_VRING_BASE:
246         memcpy(&msg.state, arg, sizeof(struct vhost_vring_state));
247         msg.size = sizeof(m.state);
248         break;
249 
250     case VHOST_GET_VRING_BASE:
251         memcpy(&msg.state, arg, sizeof(struct vhost_vring_state));
252         msg.size = sizeof(m.state);
253         need_reply = 1;
254         break;
255 
256     case VHOST_SET_VRING_ADDR:
257         memcpy(&msg.addr, arg, sizeof(struct vhost_vring_addr));
258         msg.size = sizeof(m.addr);
259         break;
260 
261     case VHOST_SET_VRING_KICK:
262     case VHOST_SET_VRING_CALL:
263     case VHOST_SET_VRING_ERR:
264         file = arg;
265         msg.u64 = file->index & VHOST_USER_VRING_IDX_MASK;
266         msg.size = sizeof(m.u64);
267         if (ioeventfd_enabled() && file->fd > 0) {
268             fds[fd_num++] = file->fd;
269         } else {
270             msg.u64 |= VHOST_USER_VRING_NOFD_MASK;
271         }
272         break;
273     default:
274         error_report("vhost-user trying to send unhandled ioctl\n");
275         return -1;
276         break;
277     }
278 
279     if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
280         return 0;
281     }
282 
283     if (need_reply) {
284         if (vhost_user_read(dev, &msg) < 0) {
285             return 0;
286         }
287 
288         if (msg_request != msg.request) {
289             error_report("Received unexpected msg type."
290                     " Expected %d received %d\n", msg_request, msg.request);
291             return -1;
292         }
293 
294         switch (msg_request) {
295         case VHOST_USER_GET_FEATURES:
296             if (msg.size != sizeof(m.u64)) {
297                 error_report("Received bad msg size.\n");
298                 return -1;
299             }
300             *((__u64 *) arg) = msg.u64;
301             break;
302         case VHOST_USER_GET_VRING_BASE:
303             if (msg.size != sizeof(m.state)) {
304                 error_report("Received bad msg size.\n");
305                 return -1;
306             }
307             memcpy(arg, &msg.state, sizeof(struct vhost_vring_state));
308             break;
309         default:
310             error_report("Received unexpected msg type.\n");
311             return -1;
312             break;
313         }
314     }
315 
316     return 0;
317 }
318 
319 static int vhost_user_init(struct vhost_dev *dev, void *opaque)
320 {
321     assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
322 
323     dev->opaque = opaque;
324 
325     return 0;
326 }
327 
328 static int vhost_user_cleanup(struct vhost_dev *dev)
329 {
330     assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
331 
332     dev->opaque = 0;
333 
334     return 0;
335 }
336 
337 const VhostOps user_ops = {
338         .backend_type = VHOST_BACKEND_TYPE_USER,
339         .vhost_call = vhost_user_call,
340         .vhost_backend_init = vhost_user_init,
341         .vhost_backend_cleanup = vhost_user_cleanup
342         };
343