1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2016 Intel Corporation
3  */
4 
5 #include <sys/socket.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <sys/un.h>
11 #include <string.h>
12 #include <errno.h>
13 
14 #include "vhost.h"
15 #include "virtio_user_dev.h"
16 
17 /* The version of the protocol we support */
18 #define VHOST_USER_VERSION    0x1
19 
20 #define VHOST_MEMORY_MAX_NREGIONS 8
21 struct vhost_memory {
22 	uint32_t nregions;
23 	uint32_t padding;
24 	struct vhost_memory_region regions[VHOST_MEMORY_MAX_NREGIONS];
25 };
26 
27 struct vhost_user_msg {
28 	enum vhost_user_request request;
29 
30 #define VHOST_USER_VERSION_MASK     0x3
31 #define VHOST_USER_REPLY_MASK       (0x1 << 2)
32 	uint32_t flags;
33 	uint32_t size; /* the following payload size */
34 	union {
35 #define VHOST_USER_VRING_IDX_MASK   0xff
36 #define VHOST_USER_VRING_NOFD_MASK  (0x1 << 8)
37 		uint64_t u64;
38 		struct vhost_vring_state state;
39 		struct vhost_vring_addr addr;
40 		struct vhost_memory memory;
41 	} payload;
42 	int fds[VHOST_MEMORY_MAX_NREGIONS];
43 } __attribute((packed));
44 
45 #define VHOST_USER_HDR_SIZE offsetof(struct vhost_user_msg, payload.u64)
46 #define VHOST_USER_PAYLOAD_SIZE \
47 	(sizeof(struct vhost_user_msg) - VHOST_USER_HDR_SIZE)
48 
49 static int
50 vhost_user_write(int fd, void *buf, int len, int *fds, int fd_num)
51 {
52 	int r;
53 	struct msghdr msgh;
54 	struct iovec iov;
55 	size_t fd_size = fd_num * sizeof(int);
56 	char control[CMSG_SPACE(fd_size)];
57 	struct cmsghdr *cmsg;
58 
59 	memset(&msgh, 0, sizeof(msgh));
60 	memset(control, 0, sizeof(control));
61 
62 	iov.iov_base = (uint8_t *)buf;
63 	iov.iov_len = len;
64 
65 	msgh.msg_iov = &iov;
66 	msgh.msg_iovlen = 1;
67 	msgh.msg_control = control;
68 	msgh.msg_controllen = sizeof(control);
69 
70 	cmsg = CMSG_FIRSTHDR(&msgh);
71 	cmsg->cmsg_len = CMSG_LEN(fd_size);
72 	cmsg->cmsg_level = SOL_SOCKET;
73 	cmsg->cmsg_type = SCM_RIGHTS;
74 	memcpy(CMSG_DATA(cmsg), fds, fd_size);
75 
76 	do {
77 		r = sendmsg(fd, &msgh, 0);
78 	} while (r < 0 && errno == EINTR);
79 
80 	return r;
81 }
82 
83 static int
84 vhost_user_read(int fd, struct vhost_user_msg *msg)
85 {
86 	uint32_t valid_flags = VHOST_USER_REPLY_MASK | VHOST_USER_VERSION;
87 	int ret, sz_hdr = VHOST_USER_HDR_SIZE, sz_payload;
88 
89 	ret = recv(fd, (void *)msg, sz_hdr, 0);
90 	if (ret < sz_hdr) {
91 		PMD_DRV_LOG(ERR, "Failed to recv msg hdr: %d instead of %d.",
92 			    ret, sz_hdr);
93 		goto fail;
94 	}
95 
96 	/* validate msg flags */
97 	if (msg->flags != (valid_flags)) {
98 		PMD_DRV_LOG(ERR, "Failed to recv msg: flags %x instead of %x.",
99 			    msg->flags, valid_flags);
100 		goto fail;
101 	}
102 
103 	sz_payload = msg->size;
104 
105 	if ((size_t)sz_payload > sizeof(msg->payload))
106 		goto fail;
107 
108 	if (sz_payload) {
109 		ret = recv(fd, (void *)((char *)msg + sz_hdr), sz_payload, 0);
110 		if (ret < sz_payload) {
111 			PMD_DRV_LOG(ERR,
112 				"Failed to recv msg payload: %d instead of %d.",
113 				ret, msg->size);
114 			goto fail;
115 		}
116 	}
117 
118 	return 0;
119 
120 fail:
121 	return -1;
122 }
123 
124 struct hugepage_file_info {
125 	uint64_t addr;            /**< virtual addr */
126 	size_t   size;            /**< the file size */
127 	char     path[PATH_MAX];  /**< path to backing file */
128 };
129 
130 /* Two possible options:
131  * 1. Match HUGEPAGE_INFO_FMT to find the file storing struct hugepage_file
132  * array. This is simple but cannot be used in secondary process because
133  * secondary process will close and munmap that file.
134  * 2. Match HUGEFILE_FMT to find hugepage files directly.
135  *
136  * We choose option 2.
137  */
138 static int
139 get_hugepage_file_info(struct hugepage_file_info huges[], int max)
140 {
141 	int idx;
142 	FILE *f;
143 	char buf[BUFSIZ], *tmp, *tail;
144 	char *str_underline, *str_start;
145 	int huge_index;
146 	uint64_t v_start, v_end;
147 
148 	f = fopen("/proc/self/maps", "r");
149 	if (!f) {
150 		PMD_DRV_LOG(ERR, "cannot open /proc/self/maps");
151 		return -1;
152 	}
153 
154 	idx = 0;
155 	while (fgets(buf, sizeof(buf), f) != NULL) {
156 		if (sscanf(buf, "%" PRIx64 "-%" PRIx64, &v_start, &v_end) < 2) {
157 			PMD_DRV_LOG(ERR, "Failed to parse address");
158 			goto error;
159 		}
160 
161 		tmp = strchr(buf, ' ') + 1; /** skip address */
162 		tmp = strchr(tmp, ' ') + 1; /** skip perm */
163 		tmp = strchr(tmp, ' ') + 1; /** skip offset */
164 		tmp = strchr(tmp, ' ') + 1; /** skip dev */
165 		tmp = strchr(tmp, ' ') + 1; /** skip inode */
166 		while (*tmp == ' ')         /** skip spaces */
167 			tmp++;
168 		tail = strrchr(tmp, '\n');  /** remove newline if exists */
169 		if (tail)
170 			*tail = '\0';
171 
172 		/* Match HUGEFILE_FMT, aka "%s/%smap_%d",
173 		 * which is defined in eal_filesystem.h
174 		 */
175 		str_underline = strrchr(tmp, '_');
176 		if (!str_underline)
177 			continue;
178 
179 		str_start = str_underline - strlen("map");
180 		if (str_start < tmp)
181 			continue;
182 
183 		if (sscanf(str_start, "map_%d", &huge_index) != 1)
184 			continue;
185 
186 		if (idx >= max) {
187 			PMD_DRV_LOG(ERR, "Exceed maximum of %d", max);
188 			goto error;
189 		}
190 		huges[idx].addr = v_start;
191 		huges[idx].size = v_end - v_start;
192 		snprintf(huges[idx].path, PATH_MAX, "%s", tmp);
193 		idx++;
194 	}
195 
196 	fclose(f);
197 	return idx;
198 
199 error:
200 	fclose(f);
201 	return -1;
202 }
203 
204 static int
205 prepare_vhost_memory_user(struct vhost_user_msg *msg, int fds[])
206 {
207 	int i, num;
208 	struct hugepage_file_info huges[VHOST_MEMORY_MAX_NREGIONS];
209 	struct vhost_memory_region *mr;
210 
211 	num = get_hugepage_file_info(huges, VHOST_MEMORY_MAX_NREGIONS);
212 	if (num < 0) {
213 		PMD_INIT_LOG(ERR, "Failed to prepare memory for vhost-user");
214 		return -1;
215 	}
216 
217 	for (i = 0; i < num; ++i) {
218 		mr = &msg->payload.memory.regions[i];
219 		mr->guest_phys_addr = huges[i].addr; /* use vaddr! */
220 		mr->userspace_addr = huges[i].addr;
221 		mr->memory_size = huges[i].size;
222 		mr->mmap_offset = 0;
223 		fds[i] = open(huges[i].path, O_RDWR);
224 	}
225 
226 	msg->payload.memory.nregions = num;
227 	msg->payload.memory.padding = 0;
228 
229 	return 0;
230 }
231 
232 static struct vhost_user_msg m;
233 
234 const char * const vhost_msg_strings[] = {
235 	[VHOST_USER_SET_OWNER] = "VHOST_SET_OWNER",
236 	[VHOST_USER_RESET_OWNER] = "VHOST_RESET_OWNER",
237 	[VHOST_USER_SET_FEATURES] = "VHOST_SET_FEATURES",
238 	[VHOST_USER_GET_FEATURES] = "VHOST_GET_FEATURES",
239 	[VHOST_USER_SET_VRING_CALL] = "VHOST_SET_VRING_CALL",
240 	[VHOST_USER_SET_VRING_NUM] = "VHOST_SET_VRING_NUM",
241 	[VHOST_USER_SET_VRING_BASE] = "VHOST_SET_VRING_BASE",
242 	[VHOST_USER_GET_VRING_BASE] = "VHOST_GET_VRING_BASE",
243 	[VHOST_USER_SET_VRING_ADDR] = "VHOST_SET_VRING_ADDR",
244 	[VHOST_USER_SET_VRING_KICK] = "VHOST_SET_VRING_KICK",
245 	[VHOST_USER_SET_MEM_TABLE] = "VHOST_SET_MEM_TABLE",
246 	[VHOST_USER_SET_VRING_ENABLE] = "VHOST_SET_VRING_ENABLE",
247 };
248 
249 static int
250 vhost_user_sock(struct virtio_user_dev *dev,
251 		enum vhost_user_request req,
252 		void *arg)
253 {
254 	struct vhost_user_msg msg;
255 	struct vhost_vring_file *file = 0;
256 	int need_reply = 0;
257 	int fds[VHOST_MEMORY_MAX_NREGIONS];
258 	int fd_num = 0;
259 	int i, len;
260 	int vhostfd = dev->vhostfd;
261 
262 	RTE_SET_USED(m);
263 
264 	PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]);
265 
266 	msg.request = req;
267 	msg.flags = VHOST_USER_VERSION;
268 	msg.size = 0;
269 
270 	switch (req) {
271 	case VHOST_USER_GET_FEATURES:
272 		need_reply = 1;
273 		break;
274 
275 	case VHOST_USER_SET_FEATURES:
276 	case VHOST_USER_SET_LOG_BASE:
277 		msg.payload.u64 = *((__u64 *)arg);
278 		msg.size = sizeof(m.payload.u64);
279 		break;
280 
281 	case VHOST_USER_SET_OWNER:
282 	case VHOST_USER_RESET_OWNER:
283 		break;
284 
285 	case VHOST_USER_SET_MEM_TABLE:
286 		if (prepare_vhost_memory_user(&msg, fds) < 0)
287 			return -1;
288 		fd_num = msg.payload.memory.nregions;
289 		msg.size = sizeof(m.payload.memory.nregions);
290 		msg.size += sizeof(m.payload.memory.padding);
291 		msg.size += fd_num * sizeof(struct vhost_memory_region);
292 		break;
293 
294 	case VHOST_USER_SET_LOG_FD:
295 		fds[fd_num++] = *((int *)arg);
296 		break;
297 
298 	case VHOST_USER_SET_VRING_NUM:
299 	case VHOST_USER_SET_VRING_BASE:
300 	case VHOST_USER_SET_VRING_ENABLE:
301 		memcpy(&msg.payload.state, arg, sizeof(msg.payload.state));
302 		msg.size = sizeof(m.payload.state);
303 		break;
304 
305 	case VHOST_USER_GET_VRING_BASE:
306 		memcpy(&msg.payload.state, arg, sizeof(msg.payload.state));
307 		msg.size = sizeof(m.payload.state);
308 		need_reply = 1;
309 		break;
310 
311 	case VHOST_USER_SET_VRING_ADDR:
312 		memcpy(&msg.payload.addr, arg, sizeof(msg.payload.addr));
313 		msg.size = sizeof(m.payload.addr);
314 		break;
315 
316 	case VHOST_USER_SET_VRING_KICK:
317 	case VHOST_USER_SET_VRING_CALL:
318 	case VHOST_USER_SET_VRING_ERR:
319 		file = arg;
320 		msg.payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK;
321 		msg.size = sizeof(m.payload.u64);
322 		if (file->fd > 0)
323 			fds[fd_num++] = file->fd;
324 		else
325 			msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK;
326 		break;
327 
328 	default:
329 		PMD_DRV_LOG(ERR, "trying to send unhandled msg type");
330 		return -1;
331 	}
332 
333 	len = VHOST_USER_HDR_SIZE + msg.size;
334 	if (vhost_user_write(vhostfd, &msg, len, fds, fd_num) < 0) {
335 		PMD_DRV_LOG(ERR, "%s failed: %s",
336 			    vhost_msg_strings[req], strerror(errno));
337 		return -1;
338 	}
339 
340 	if (req == VHOST_USER_SET_MEM_TABLE)
341 		for (i = 0; i < fd_num; ++i)
342 			close(fds[i]);
343 
344 	if (need_reply) {
345 		if (vhost_user_read(vhostfd, &msg) < 0) {
346 			PMD_DRV_LOG(ERR, "Received msg failed: %s",
347 				    strerror(errno));
348 			return -1;
349 		}
350 
351 		if (req != msg.request) {
352 			PMD_DRV_LOG(ERR, "Received unexpected msg type");
353 			return -1;
354 		}
355 
356 		switch (req) {
357 		case VHOST_USER_GET_FEATURES:
358 			if (msg.size != sizeof(m.payload.u64)) {
359 				PMD_DRV_LOG(ERR, "Received bad msg size");
360 				return -1;
361 			}
362 			*((__u64 *)arg) = msg.payload.u64;
363 			break;
364 		case VHOST_USER_GET_VRING_BASE:
365 			if (msg.size != sizeof(m.payload.state)) {
366 				PMD_DRV_LOG(ERR, "Received bad msg size");
367 				return -1;
368 			}
369 			memcpy(arg, &msg.payload.state,
370 			       sizeof(struct vhost_vring_state));
371 			break;
372 		default:
373 			PMD_DRV_LOG(ERR, "Received unexpected msg type");
374 			return -1;
375 		}
376 	}
377 
378 	return 0;
379 }
380 
381 /**
382  * Set up environment to talk with a vhost user backend.
383  *
384  * @return
385  *   - (-1) if fail;
386  *   - (0) if succeed.
387  */
388 static int
389 vhost_user_setup(struct virtio_user_dev *dev)
390 {
391 	int fd;
392 	int flag;
393 	struct sockaddr_un un;
394 
395 	fd = socket(AF_UNIX, SOCK_STREAM, 0);
396 	if (fd < 0) {
397 		PMD_DRV_LOG(ERR, "socket() error, %s", strerror(errno));
398 		return -1;
399 	}
400 
401 	flag = fcntl(fd, F_GETFD);
402 	if (fcntl(fd, F_SETFD, flag | FD_CLOEXEC) < 0)
403 		PMD_DRV_LOG(WARNING, "fcntl failed, %s", strerror(errno));
404 
405 	memset(&un, 0, sizeof(un));
406 	un.sun_family = AF_UNIX;
407 	snprintf(un.sun_path, sizeof(un.sun_path), "%s", dev->path);
408 	if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
409 		PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno));
410 		close(fd);
411 		return -1;
412 	}
413 
414 	dev->vhostfd = fd;
415 	return 0;
416 }
417 
418 static int
419 vhost_user_enable_queue_pair(struct virtio_user_dev *dev,
420 			     uint16_t pair_idx,
421 			     int enable)
422 {
423 	int i;
424 
425 	for (i = 0; i < 2; ++i) {
426 		struct vhost_vring_state state = {
427 			.index = pair_idx * 2 + i,
428 			.num   = enable,
429 		};
430 
431 		if (vhost_user_sock(dev, VHOST_USER_SET_VRING_ENABLE, &state))
432 			return -1;
433 	}
434 
435 	return 0;
436 }
437 
438 struct virtio_user_backend_ops ops_user = {
439 	.setup = vhost_user_setup,
440 	.send_request = vhost_user_sock,
441 	.enable_qp = vhost_user_enable_queue_pair
442 };
443