xref: /freebsd/usr.sbin/bhyve/uart_backend.c (revision ad152571b8fd69d93ca0e44d80c9b49308b36eae)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2012 NetApp, Inc.
5  * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 
33 #include <machine/vmm.h>
34 #include <machine/vmm_snapshot.h>
35 
36 #include <netinet/in.h>
37 
38 #include <arpa/inet.h>
39 #include <assert.h>
40 #include <capsicum_helpers.h>
41 #include <err.h>
42 #include <netdb.h>
43 #include <pthread.h>
44 #include <stdbool.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <sysexits.h>
48 #include <termios.h>
49 #include <unistd.h>
50 
51 #include "debug.h"
52 #include "mevent.h"
53 #include "uart_backend.h"
54 
55 struct ttyfd {
56 	bool	opened;
57 	bool	is_socket;
58 	int	rfd;		/* fd for reading */
59 	int	wfd;		/* fd for writing, may be == rfd */
60 };
61 
62 #define	FIFOSZ	16
63 
64 struct fifo {
65 	uint8_t	buf[FIFOSZ];
66 	int	rindex;		/* index to read from */
67 	int	windex;		/* index to write to */
68 	int	num;		/* number of characters in the fifo */
69 	int	size;		/* size of the fifo */
70 };
71 
72 struct uart_softc {
73 	struct ttyfd	tty;
74 	struct fifo	rxfifo;
75 	struct mevent	*mev;
76 	pthread_mutex_t mtx;
77 };
78 
79 struct uart_socket_softc {
80 	struct uart_softc *softc;
81 	void (*drain)(int, enum ev_type, void *);
82 	void *arg;
83 };
84 
85 static bool uart_stdio;		/* stdio in use for i/o */
86 static struct termios tio_stdio_orig;
87 
88 static void uart_tcp_disconnect(struct uart_softc *);
89 
90 static void
ttyclose(void)91 ttyclose(void)
92 {
93 	tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig);
94 }
95 
96 static void
ttyopen(struct ttyfd * tf)97 ttyopen(struct ttyfd *tf)
98 {
99 	struct termios orig, new;
100 
101 	tcgetattr(tf->rfd, &orig);
102 	new = orig;
103 	cfmakeraw(&new);
104 	new.c_cflag |= CLOCAL;
105 	tcsetattr(tf->rfd, TCSANOW, &new);
106 	if (uart_stdio) {
107 		tio_stdio_orig = orig;
108 		atexit(ttyclose);
109 	}
110 	raw_stdio = 1;
111 }
112 
113 static int
ttyread(struct ttyfd * tf,uint8_t * ret)114 ttyread(struct ttyfd *tf, uint8_t *ret)
115 {
116 	uint8_t rb;
117 	int len;
118 
119 	len = read(tf->rfd, &rb, 1);
120 	if (ret && len == 1)
121 		*ret = rb;
122 
123 	return (len);
124 }
125 
126 static int
ttywrite(struct ttyfd * tf,unsigned char wb)127 ttywrite(struct ttyfd *tf, unsigned char wb)
128 {
129 	return (write(tf->wfd, &wb, 1));
130 }
131 
132 static bool
rxfifo_available(struct uart_softc * sc)133 rxfifo_available(struct uart_softc *sc)
134 {
135 	return (sc->rxfifo.num < sc->rxfifo.size);
136 }
137 
138 int
uart_rxfifo_getchar(struct uart_softc * sc)139 uart_rxfifo_getchar(struct uart_softc *sc)
140 {
141 	struct fifo *fifo;
142 	int c, error, wasfull;
143 
144 	wasfull = 0;
145 	fifo = &sc->rxfifo;
146 	if (fifo->num > 0) {
147 		if (!rxfifo_available(sc))
148 			wasfull = 1;
149 		c = fifo->buf[fifo->rindex];
150 		fifo->rindex = (fifo->rindex + 1) % fifo->size;
151 		fifo->num--;
152 		if (wasfull) {
153 			if (sc->tty.opened) {
154 				error = mevent_enable(sc->mev);
155 				assert(error == 0);
156 			}
157 		}
158 		return (c);
159 	} else
160 		return (-1);
161 }
162 
163 int
uart_rxfifo_numchars(struct uart_softc * sc)164 uart_rxfifo_numchars(struct uart_softc *sc)
165 {
166 	return (sc->rxfifo.num);
167 }
168 
169 static int
rxfifo_putchar(struct uart_softc * sc,uint8_t ch)170 rxfifo_putchar(struct uart_softc *sc, uint8_t ch)
171 {
172 	struct fifo *fifo;
173 	int error;
174 
175 	fifo = &sc->rxfifo;
176 
177 	if (fifo->num < fifo->size) {
178 		fifo->buf[fifo->windex] = ch;
179 		fifo->windex = (fifo->windex + 1) % fifo->size;
180 		fifo->num++;
181 		if (!rxfifo_available(sc)) {
182 			if (sc->tty.opened) {
183 				/*
184 				 * Disable mevent callback if the FIFO is full.
185 				 */
186 				error = mevent_disable(sc->mev);
187 				assert(error == 0);
188 			}
189 		}
190 		return (0);
191 	} else
192 		return (-1);
193 }
194 
195 void
uart_rxfifo_drain(struct uart_softc * sc,bool loopback)196 uart_rxfifo_drain(struct uart_softc *sc, bool loopback)
197 {
198 	uint8_t ch;
199 	int len;
200 
201 	if (loopback) {
202 		if (ttyread(&sc->tty, &ch) == 0 && sc->tty.is_socket)
203 			uart_tcp_disconnect(sc);
204 	} else {
205 		while (rxfifo_available(sc)) {
206 			len = ttyread(&sc->tty, &ch);
207 			if (len <= 0) {
208 				/* read returning 0 means disconnected. */
209 				if (len == 0 && sc->tty.is_socket)
210 					uart_tcp_disconnect(sc);
211 				break;
212 			}
213 
214 			rxfifo_putchar(sc, ch);
215 		}
216 	}
217 }
218 
219 int
uart_rxfifo_putchar(struct uart_softc * sc,uint8_t ch,bool loopback)220 uart_rxfifo_putchar(struct uart_softc *sc, uint8_t ch, bool loopback)
221 {
222 	if (loopback) {
223 		return (rxfifo_putchar(sc, ch));
224 	} else if (sc->tty.opened) {
225 		/* write returning -1 means disconnected. */
226 		if (ttywrite(&sc->tty, ch) == -1 && sc->tty.is_socket)
227 			uart_tcp_disconnect(sc);
228 		return (0);
229 	} else {
230 		/* Drop on the floor. */
231 		return (0);
232 	}
233 }
234 
235 void
uart_rxfifo_reset(struct uart_softc * sc,int size)236 uart_rxfifo_reset(struct uart_softc *sc, int size)
237 {
238 	char flushbuf[32];
239 	struct fifo *fifo;
240 	ssize_t nread;
241 	int error;
242 
243 	fifo = &sc->rxfifo;
244 	bzero(fifo, sizeof(struct fifo));
245 	fifo->size = size;
246 
247 	if (sc->tty.opened) {
248 		/*
249 		 * Flush any unread input from the tty buffer.
250 		 */
251 		while (1) {
252 			nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf));
253 			if (nread != sizeof(flushbuf))
254 				break;
255 		}
256 
257 		/*
258 		 * Enable mevent to trigger when new characters are available
259 		 * on the tty fd.
260 		 */
261 		error = mevent_enable(sc->mev);
262 		assert(error == 0);
263 	}
264 }
265 
266 int
uart_rxfifo_size(struct uart_softc * sc __unused)267 uart_rxfifo_size(struct uart_softc *sc __unused)
268 {
269 	return (FIFOSZ);
270 }
271 
272 #ifdef BHYVE_SNAPSHOT
273 int
uart_rxfifo_snapshot(struct uart_softc * sc,struct vm_snapshot_meta * meta)274 uart_rxfifo_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta)
275 {
276 	int ret;
277 
278 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done);
279 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done);
280 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done);
281 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done);
282 	SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf),
283 	    meta, ret, done);
284 
285 done:
286 	return (ret);
287 }
288 #endif
289 
290 /*
291  * Listen on the TCP port, wait for a connection, then accept it.
292  */
293 static void
uart_tcp_listener(int fd,enum ev_type type __unused,void * arg)294 uart_tcp_listener(int fd, enum ev_type type __unused, void *arg)
295 {
296 	static const char tcp_error_msg[] = "Socket already connected\n";
297 	struct uart_socket_softc *socket_softc = (struct uart_socket_softc *)
298 	    arg;
299 	struct uart_softc *sc = socket_softc->softc;
300 	int conn_fd;
301 
302 	conn_fd = accept(fd, NULL, NULL);
303 	if (conn_fd == -1)
304 		goto clean;
305 
306 	if (fcntl(conn_fd, F_SETFL, O_NONBLOCK) != 0)
307 		goto clean;
308 
309 	pthread_mutex_lock(&sc->mtx);
310 
311 	if (sc->tty.opened) {
312 		(void)send(conn_fd, tcp_error_msg, sizeof(tcp_error_msg), 0);
313 		pthread_mutex_unlock(&sc->mtx);
314 		goto clean;
315 	} else {
316 		sc->tty.rfd = sc->tty.wfd = conn_fd;
317 		sc->tty.opened = true;
318 		sc->mev = mevent_add(sc->tty.rfd, EVF_READ, socket_softc->drain,
319 		    socket_softc->arg);
320 	}
321 
322 	pthread_mutex_unlock(&sc->mtx);
323 	return;
324 
325 clean:
326 	if (conn_fd != -1)
327 		close(conn_fd);
328 }
329 
330 /*
331  * When a connection-oriented protocol disconnects, this handler is used to
332  * clean it up.
333  *
334  * Note that this function is a helper, so the caller is responsible for
335  * locking the softc.
336  */
337 static void
uart_tcp_disconnect(struct uart_softc * sc)338 uart_tcp_disconnect(struct uart_softc *sc)
339 {
340 	mevent_delete_close(sc->mev);
341 	sc->mev = NULL;
342 	sc->tty.opened = false;
343 	sc->tty.rfd = sc->tty.wfd = -1;
344 }
345 
346 static int
uart_stdio_backend(struct uart_softc * sc)347 uart_stdio_backend(struct uart_softc *sc)
348 {
349 #ifndef WITHOUT_CAPSICUM
350 	cap_rights_t rights;
351 	cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
352 #endif
353 
354 	if (uart_stdio)
355 		return (-1);
356 
357 	sc->tty.rfd = STDIN_FILENO;
358 	sc->tty.wfd = STDOUT_FILENO;
359 	sc->tty.opened = true;
360 
361 	if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0)
362 		return (-1);
363 	if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0)
364 		return (-1);
365 
366 #ifndef WITHOUT_CAPSICUM
367 	cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ);
368 	if (caph_rights_limit(sc->tty.rfd, &rights) == -1)
369 		errx(EX_OSERR, "Unable to apply rights for sandbox");
370 	if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1)
371 		errx(EX_OSERR, "Unable to apply rights for sandbox");
372 #endif
373 
374 	uart_stdio = true;
375 
376 	return (0);
377 }
378 
379 static int
uart_tty_backend(struct uart_softc * sc,const char * path)380 uart_tty_backend(struct uart_softc *sc, const char *path)
381 {
382 #ifndef WITHOUT_CAPSICUM
383 	cap_rights_t rights;
384 	cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
385 #endif
386 	int fd;
387 
388 	fd = open(path, O_RDWR | O_NONBLOCK);
389 	if (fd < 0)
390 		return (-1);
391 
392 	if (!isatty(fd)) {
393 		close(fd);
394 		return (-1);
395 	}
396 
397 	sc->tty.rfd = sc->tty.wfd = fd;
398 	sc->tty.opened = true;
399 
400 #ifndef WITHOUT_CAPSICUM
401 	cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE);
402 	if (caph_rights_limit(fd, &rights) == -1)
403 		errx(EX_OSERR, "Unable to apply rights for sandbox");
404 	if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1)
405 		errx(EX_OSERR, "Unable to apply rights for sandbox");
406 #endif
407 
408 	return (0);
409 }
410 
411 /*
412  * Listen on the address and add it to the kqueue.
413  *
414  * If a connection is established (e.g., the TCP handler is triggered),
415  * replace the handler with the connected handler.
416  */
417 static int
uart_tcp_backend(struct uart_softc * sc,const char * path,void (* drain)(int,enum ev_type,void *),void * arg)418 uart_tcp_backend(struct uart_softc *sc, const char *path,
419     void (*drain)(int, enum ev_type, void *), void *arg)
420 {
421 #ifndef WITHOUT_CAPSICUM
422 	cap_rights_t rights;
423 	cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
424 #endif
425 	int bind_fd = -1;
426 	char addr[256], port[6];
427 	int domain;
428 	struct addrinfo hints, *src_addr = NULL;
429 	struct uart_socket_softc *socket_softc = NULL;
430 
431 	if (sscanf(path, "tcp=[%255[^]]]:%5s", addr, port) == 2) {
432 		domain = AF_INET6;
433 	} else if (sscanf(path, "tcp=%255[^:]:%5s", addr, port) == 2) {
434 		domain = AF_INET;
435 	} else {
436 		warnx("Invalid number of parameter");
437 		goto clean;
438 	}
439 
440 	bind_fd = socket(domain, SOCK_STREAM, 0);
441 	if (bind_fd < 0)
442 		goto clean;
443 
444 	memset(&hints, 0, sizeof(hints));
445 	hints.ai_family = domain;
446 	hints.ai_socktype = SOCK_STREAM;
447 	hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
448 
449 	if (getaddrinfo(addr, port, &hints, &src_addr) != 0) {
450 		warnx("Invalid address %s:%s", addr, port);
451 		goto clean;
452 	}
453 
454 	if (bind(bind_fd, src_addr->ai_addr, src_addr->ai_addrlen) == -1) {
455 		warn(
456 		    "bind(%s:%s)",
457 		    addr, port);
458 		goto clean;
459 	}
460 
461 	freeaddrinfo(src_addr);
462 	src_addr = NULL;
463 
464 	if (fcntl(bind_fd, F_SETFL, O_NONBLOCK) == -1)
465 		goto clean;
466 
467 	if (listen(bind_fd, 1) == -1) {
468 		warnx("listen(%s:%s)", addr, port);
469 		goto clean;
470 	}
471 
472 	/*
473 	 * Set the connection softc structure, which includes both the softc
474 	 * and the drain function provided by the frontend.
475 	 */
476 	if ((socket_softc = calloc(1, sizeof(struct uart_socket_softc))) ==
477 	    NULL)
478 		goto clean;
479 
480 	sc->tty.is_socket = true;
481 
482 	socket_softc->softc = sc;
483 	socket_softc->drain = drain;
484 	socket_softc->arg = arg;
485 
486 #ifndef WITHOUT_CAPSICUM
487 	cap_rights_init(&rights, CAP_EVENT, CAP_ACCEPT, CAP_RECV, CAP_SEND,
488 	    CAP_FCNTL, CAP_IOCTL);
489 	if (caph_rights_limit(bind_fd, &rights) == -1)
490 		errx(EX_OSERR, "Unable to apply rights for sandbox");
491 	if (caph_ioctls_limit(bind_fd, cmds, nitems(cmds)) == -1)
492 		errx(EX_OSERR, "Unable to apply ioctls for sandbox");
493 	if (caph_fcntls_limit(bind_fd, CAP_FCNTL_SETFL) == -1)
494 		errx(EX_OSERR, "Unable to apply fcntls for sandbox");
495 #endif
496 
497 	if ((sc->mev = mevent_add(bind_fd, EVF_READ, uart_tcp_listener,
498 	    socket_softc)) == NULL)
499 		goto clean;
500 
501 	return (0);
502 
503 clean:
504 	if (bind_fd != -1)
505 		close(bind_fd);
506 	if (socket_softc != NULL)
507 		free(socket_softc);
508 	if (src_addr)
509 		freeaddrinfo(src_addr);
510 	return (-1);
511 }
512 
513 struct uart_softc *
uart_init(void)514 uart_init(void)
515 {
516 	struct uart_softc *sc = calloc(1, sizeof(struct uart_softc));
517 	if (sc == NULL)
518 		return (NULL);
519 
520 	pthread_mutex_init(&sc->mtx, NULL);
521 
522 	return (sc);
523 }
524 
525 int
uart_tty_open(struct uart_softc * sc,const char * path,void (* drain)(int,enum ev_type,void *),void * arg)526 uart_tty_open(struct uart_softc *sc, const char *path,
527     void (*drain)(int, enum ev_type, void *), void *arg)
528 {
529 	int retval;
530 
531 	if (strcmp("stdio", path) == 0)
532 		retval = uart_stdio_backend(sc);
533 	else if (strncmp("tcp", path, 3) == 0)
534 		retval = uart_tcp_backend(sc, path, drain, arg);
535 	else
536 		retval = uart_tty_backend(sc, path);
537 
538 	/*
539 	 * A connection-oriented protocol should wait for a connection,
540 	 * so it may not listen to anything during initialization.
541 	 */
542 	if (retval == 0 && !sc->tty.is_socket) {
543 		ttyopen(&sc->tty);
544 		sc->mev = mevent_add(sc->tty.rfd, EVF_READ, drain, arg);
545 		assert(sc->mev != NULL);
546 	}
547 
548 	return (retval);
549 }
550 
551 void
uart_softc_lock(struct uart_softc * sc)552 uart_softc_lock(struct uart_softc *sc)
553 {
554 	pthread_mutex_lock(&sc->mtx);
555 }
556 
557 void
uart_softc_unlock(struct uart_softc * sc)558 uart_softc_unlock(struct uart_softc *sc)
559 {
560 	pthread_mutex_unlock(&sc->mtx);
561 }
562