xref: /illumos-gate/usr/src/cmd/bhyve/common/uart_backend.c (revision 5c4a5fe16715fb423db76577a6883b5bbecdbe45)
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 
32 #include <machine/vmm.h>
33 
34 #include <assert.h>
35 #ifndef WITHOUT_CAPSICUM
36 #include <capsicum_helpers.h>
37 #endif
38 #include <err.h>
39 #include <errno.h>
40 #include <pthread.h>
41 #include <stdbool.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sysexits.h>
45 #include <termios.h>
46 #include <unistd.h>
47 #ifndef	__FreeBSD__
48 #include <sys/socket.h>
49 #include <fcntl.h>
50 #endif
51 
52 
53 #include "debug.h"
54 #include "mevent.h"
55 #include "uart_backend.h"
56 #include "uart_emul.h"
57 
58 struct ttyfd {
59 	bool	opened;
60 	int	rfd;		/* fd for reading */
61 	int	wfd;		/* fd for writing, may be == rfd */
62 };
63 
64 #ifndef	__FreeBSD__
65 struct sockfd {
66 	bool	sock;
67 	int	clifd;		/* console client unix domain socket */
68 	int	servfd;		/* console server unix domain socket */
69 	struct mevent *servmev;	/* mevent for server socket */
70 	void (*drain)(int, enum ev_type, void *);
71 	void *drainarg;
72 };
73 #endif
74 
75 #define	FIFOSZ	16
76 
77 struct fifo {
78 	uint8_t	buf[FIFOSZ];
79 	int	rindex;		/* index to read from */
80 	int	windex;		/* index to write to */
81 	int	num;		/* number of characters in the fifo */
82 	int	size;		/* size of the fifo */
83 };
84 
85 struct uart_softc {
86 	struct ttyfd	tty;
87 #ifndef	__FreeBSD__
88 	struct sockfd	usc_sock;
89 #endif
90 	struct fifo	rxfifo;
91 	struct mevent	*mev;
92 	pthread_mutex_t mtx;
93 };
94 
95 static bool uart_stdio;		/* stdio in use for i/o */
96 static struct termios tio_stdio_orig;
97 
98 static void
ttyclose(void)99 ttyclose(void)
100 {
101 	tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig);
102 }
103 
104 static void
ttyopen(struct ttyfd * tf)105 ttyopen(struct ttyfd *tf)
106 {
107 	struct termios orig, new;
108 
109 	tcgetattr(tf->rfd, &orig);
110 	new = orig;
111 	cfmakeraw(&new);
112 	new.c_cflag |= CLOCAL;
113 	tcsetattr(tf->rfd, TCSANOW, &new);
114 	if (uart_stdio) {
115 		tio_stdio_orig = orig;
116 		atexit(ttyclose);
117 	}
118 	raw_stdio = 1;
119 }
120 
121 static int
ttyread(struct ttyfd * tf)122 ttyread(struct ttyfd *tf)
123 {
124 	unsigned char rb;
125 
126 	if (read(tf->rfd, &rb, 1) == 1)
127 		return (rb);
128 	else
129 		return (-1);
130 }
131 
132 static void
ttywrite(struct ttyfd * tf,unsigned char wb)133 ttywrite(struct ttyfd *tf, unsigned char wb)
134 {
135 	(void)write(tf->wfd, &wb, 1);
136 }
137 
138 #ifndef	__FreeBSD__
139 static void
sockwrite(struct uart_softc * sc,unsigned char wb)140 sockwrite(struct uart_softc *sc, unsigned char wb)
141 {
142 	(void) write(sc->usc_sock.clifd, &wb, 1);
143 }
144 #endif
145 
146 static bool
rxfifo_available(struct uart_softc * sc)147 rxfifo_available(struct uart_softc *sc)
148 {
149 	return (sc->rxfifo.num < sc->rxfifo.size);
150 }
151 
152 int
uart_rxfifo_getchar(struct uart_softc * sc)153 uart_rxfifo_getchar(struct uart_softc *sc)
154 {
155 	struct fifo *fifo;
156 	int c, error, wasfull;
157 
158 	wasfull = 0;
159 	fifo = &sc->rxfifo;
160 	if (fifo->num > 0) {
161 		if (!rxfifo_available(sc))
162 			wasfull = 1;
163 		c = fifo->buf[fifo->rindex];
164 		fifo->rindex = (fifo->rindex + 1) % fifo->size;
165 		fifo->num--;
166 		if (wasfull) {
167 			if (sc->tty.opened) {
168 				error = mevent_enable(sc->mev);
169 				assert(error == 0);
170 			}
171 #ifndef	__FreeBSD__
172 			if (sc->usc_sock.sock && sc->usc_sock.clifd != -1) {
173 				error = mevent_enable(sc->mev);
174 				assert(error == 0);
175 			}
176 #endif /* __FreeBSD__ */
177 		}
178 		return (c);
179 	} else
180 		return (-1);
181 }
182 
183 int
uart_rxfifo_numchars(struct uart_softc * sc)184 uart_rxfifo_numchars(struct uart_softc *sc)
185 {
186 	return (sc->rxfifo.num);
187 }
188 
189 static int
rxfifo_putchar(struct uart_softc * sc,uint8_t ch)190 rxfifo_putchar(struct uart_softc *sc, uint8_t ch)
191 {
192 	struct fifo *fifo;
193 	int error;
194 
195 	fifo = &sc->rxfifo;
196 
197 	if (fifo->num < fifo->size) {
198 		fifo->buf[fifo->windex] = ch;
199 		fifo->windex = (fifo->windex + 1) % fifo->size;
200 		fifo->num++;
201 		if (!rxfifo_available(sc)) {
202 			if (sc->tty.opened) {
203 				/*
204 				 * Disable mevent callback if the FIFO is full.
205 				 */
206 				error = mevent_disable(sc->mev);
207 				assert(error == 0);
208 			}
209 #ifndef	__FreeBSD__
210 			if (sc->usc_sock.sock && sc->usc_sock.clifd != -1) {
211 				/*
212 				 * Disable mevent callback if the FIFO is full.
213 				 */
214 				error = mevent_disable(sc->mev);
215 				assert(error == 0);
216 			}
217 #endif /* __FreeBSD__ */
218 		}
219 		return (0);
220 	} else
221 		return (-1);
222 }
223 
224 void
uart_rxfifo_drain(struct uart_softc * sc,bool loopback)225 uart_rxfifo_drain(struct uart_softc *sc, bool loopback)
226 {
227 	int ch;
228 
229 	if (loopback) {
230 		(void)ttyread(&sc->tty);
231 	} else {
232 		while (rxfifo_available(sc) &&
233 		    ((ch = ttyread(&sc->tty)) != -1))
234 			rxfifo_putchar(sc, ch);
235 	}
236 }
237 
238 #ifndef	__FreeBSD__
239 void
uart_rxfifo_sock_drain(struct uart_softc * sc,bool loopback)240 uart_rxfifo_sock_drain(struct uart_softc *sc, bool loopback)
241 {
242 	int ch;
243 
244 	if (loopback) {
245 		(void) read(sc->usc_sock.clifd, &ch, 1);
246 	} else {
247 		bool err_close = false;
248 
249 		while (rxfifo_available(sc)) {
250 			int res;
251 
252 			res = read(sc->usc_sock.clifd, &ch, 1);
253 			if (res == 0) {
254 				err_close = true;
255 				break;
256 			} else if (res == -1) {
257 				if (errno != EAGAIN && errno != EINTR) {
258 					err_close = true;
259 				}
260 				break;
261 			}
262 
263 			rxfifo_putchar(sc, ch);
264 		}
265 
266 		if (err_close) {
267 			(void) fprintf(stderr, "uart: closing client conn\n");
268 			(void) shutdown(sc->usc_sock.clifd, SHUT_RDWR);
269 			mevent_delete_close(sc->mev);
270 			sc->mev = NULL;
271 			sc->usc_sock.clifd = -1;
272 		}
273 	}
274 }
275 #endif
276 
277 int
uart_rxfifo_putchar(struct uart_softc * sc,uint8_t ch,bool loopback)278 uart_rxfifo_putchar(struct uart_softc *sc, uint8_t ch, bool loopback)
279 {
280 	if (loopback) {
281 		return (rxfifo_putchar(sc, ch));
282 	} else if (sc->tty.opened) {
283 		ttywrite(&sc->tty, ch);
284 		return (0);
285 #ifndef	__FreeBSD__
286 	} else if (sc->usc_sock.sock) {
287 		sockwrite(sc, ch);
288 		return (0);
289 #endif
290 	} else {
291 		/* Drop on the floor. */
292 		return (0);
293 	}
294 }
295 
296 void
uart_rxfifo_reset(struct uart_softc * sc,int size)297 uart_rxfifo_reset(struct uart_softc *sc, int size)
298 {
299 	char flushbuf[32];
300 	struct fifo *fifo;
301 	ssize_t nread;
302 	int error;
303 
304 	fifo = &sc->rxfifo;
305 	bzero(fifo, sizeof(struct fifo));
306 	fifo->size = size;
307 
308 	if (sc->tty.opened) {
309 		/*
310 		 * Flush any unread input from the tty buffer.
311 		 */
312 		while (1) {
313 			nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf));
314 			if (nread != sizeof(flushbuf))
315 				break;
316 		}
317 
318 		/*
319 		 * Enable mevent to trigger when new characters are available
320 		 * on the tty fd.
321 		 */
322 		error = mevent_enable(sc->mev);
323 		assert(error == 0);
324 	}
325 #ifndef	__FreeBSD__
326 	if (sc->usc_sock.sock && sc->usc_sock.clifd != -1) {
327 		/* Flush any unread input from the socket buffer. */
328 		do {
329 			nread = read(sc->usc_sock.clifd, flushbuf,
330 			    sizeof (flushbuf));
331 		} while (nread == sizeof (flushbuf));
332 
333 		/* Enable mevent to trigger when new data available on sock */
334 		error = mevent_enable(sc->mev);
335 		assert(error == 0);
336 	}
337 #endif /* __FreeBSD__ */
338 }
339 
340 int
uart_rxfifo_size(struct uart_softc * sc __unused)341 uart_rxfifo_size(struct uart_softc *sc __unused)
342 {
343 	return (FIFOSZ);
344 }
345 
346 #ifdef BHYVE_SNAPSHOT
347 int
uart_rxfifo_snapshot(struct uart_softc * sc,struct vm_snapshot_meta * meta)348 uart_rxfifo_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta)
349 {
350 	int ret;
351 
352 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done);
353 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done);
354 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done);
355 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done);
356 	SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf),
357 	    meta, ret, done);
358 
359 done:
360 	return (ret);
361 }
362 #endif
363 
364 static int
uart_stdio_backend(struct uart_softc * sc)365 uart_stdio_backend(struct uart_softc *sc)
366 {
367 #ifndef WITHOUT_CAPSICUM
368 	cap_rights_t rights;
369 	cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
370 #endif
371 
372 	if (uart_stdio)
373 		return (-1);
374 
375 	sc->tty.rfd = STDIN_FILENO;
376 	sc->tty.wfd = STDOUT_FILENO;
377 	sc->tty.opened = true;
378 
379 	if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0)
380 		return (-1);
381 	if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0)
382 		return (-1);
383 
384 #ifndef WITHOUT_CAPSICUM
385 	cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ);
386 	if (caph_rights_limit(sc->tty.rfd, &rights) == -1)
387 		errx(EX_OSERR, "Unable to apply rights for sandbox");
388 	if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1)
389 		errx(EX_OSERR, "Unable to apply rights for sandbox");
390 #endif
391 
392 	uart_stdio = true;
393 
394 	return (0);
395 }
396 
397 static int
uart_tty_backend(struct uart_softc * sc,const char * path)398 uart_tty_backend(struct uart_softc *sc, const char *path)
399 {
400 #ifndef WITHOUT_CAPSICUM
401 	cap_rights_t rights;
402 	cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
403 #endif
404 	int fd;
405 
406 	fd = open(path, O_RDWR | O_NONBLOCK);
407 	if (fd < 0)
408 		return (-1);
409 
410 	if (!isatty(fd)) {
411 		close(fd);
412 		return (-1);
413 	}
414 
415 	sc->tty.rfd = sc->tty.wfd = fd;
416 	sc->tty.opened = true;
417 
418 #ifndef WITHOUT_CAPSICUM
419 	cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE);
420 	if (caph_rights_limit(fd, &rights) == -1)
421 		errx(EX_OSERR, "Unable to apply rights for sandbox");
422 	if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1)
423 		errx(EX_OSERR, "Unable to apply rights for sandbox");
424 #endif
425 
426 	return (0);
427 }
428 
429 #ifndef	__FreeBSD__
430 static void
uart_sock_accept(int fd,enum ev_type ev,void * arg)431 uart_sock_accept(int fd, enum ev_type ev, void *arg)
432 {
433 	struct uart_softc *sc = arg;
434 	int connfd;
435 
436 	connfd = accept(sc->usc_sock.servfd, NULL, NULL);
437 	if (connfd == -1) {
438 		return;
439 	}
440 
441 	/*
442 	 * Do client connection management under protection of the softc lock
443 	 * to avoid racing with concurrent UART events.
444 	 */
445 	pthread_mutex_lock(&sc->mtx);
446 
447 	if (sc->usc_sock.clifd != -1) {
448 		/* we're already handling a client */
449 		(void) fprintf(stderr, "uart: unexpected client conn\n");
450 		(void) shutdown(connfd, SHUT_RDWR);
451 		(void) close(connfd);
452 	} else {
453 		if (fcntl(connfd, F_SETFL, O_NONBLOCK) < 0) {
454 			perror("uart: fcntl(O_NONBLOCK)");
455 			(void) shutdown(connfd, SHUT_RDWR);
456 			(void) close(connfd);
457 		} else {
458 			sc->usc_sock.clifd = connfd;
459 			sc->mev = mevent_add(sc->usc_sock.clifd, EVF_READ,
460 			    sc->usc_sock.drain, sc->usc_sock.drainarg);
461 		}
462 	}
463 
464 	pthread_mutex_unlock(&sc->mtx);
465 }
466 
467 static int
init_sock(const char * path)468 init_sock(const char *path)
469 {
470 	int servfd;
471 	struct sockaddr_un servaddr;
472 
473 	bzero(&servaddr, sizeof (servaddr));
474 	servaddr.sun_family = AF_UNIX;
475 
476 	if (strlcpy(servaddr.sun_path, path, sizeof (servaddr.sun_path)) >=
477 	    sizeof (servaddr.sun_path)) {
478 		(void) fprintf(stderr, "uart: path '%s' too long\n",
479 		    path);
480 		return (-1);
481 	}
482 
483 	if ((servfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
484 		(void) fprintf(stderr, "uart: socket() error - %s\n",
485 		    strerror(errno));
486 		return (-1);
487 	}
488 	(void) unlink(servaddr.sun_path);
489 
490 	if (bind(servfd, (struct sockaddr *)&servaddr,
491 	    sizeof (servaddr)) == -1) {
492 		(void) fprintf(stderr, "uart: bind() error - %s\n",
493 		    strerror(errno));
494 		goto out;
495 	}
496 
497 	if (listen(servfd, 1) == -1) {
498 		(void) fprintf(stderr, "uart: listen() error - %s\n",
499 		    strerror(errno));
500 		goto out;
501 	}
502 	return (servfd);
503 
504 out:
505 	(void) unlink(servaddr.sun_path);
506 	(void) close(servfd);
507 	return (-1);
508 }
509 
510 static int
uart_sock_backend(struct uart_softc * sc,const char * inopts,void (* drain)(int,enum ev_type,void *),void * drainarg)511 uart_sock_backend(struct uart_softc *sc, const char *inopts,
512     void (*drain)(int, enum ev_type, void *), void *drainarg)
513 {
514 	char *opts, *tofree;
515 	char *opt;
516 	char *nextopt;
517 	char *path = NULL;
518 
519 	if (strncmp(inopts, "socket,", 7) != 0) {
520 		return (-1);
521 	}
522 	if ((opts = strdup(inopts + 7)) == NULL) {
523 		return (-1);
524 	}
525 
526 	tofree = nextopt = opts;
527 	for (opt = strsep(&nextopt, ","); opt != NULL;
528 	    opt = strsep(&nextopt, ",")) {
529 		if (path == NULL && *opt == '/') {
530 			path = opt;
531 			continue;
532 		}
533 		/*
534 		 * XXX check for server and client options here.  For now,
535 		 * everything is a server
536 		 */
537 		free(tofree);
538 		return (-1);
539 	}
540 
541 	sc->usc_sock.clifd = -1;
542 	if ((sc->usc_sock.servfd = init_sock(path)) == -1) {
543 		free(tofree);
544 		return (-1);
545 	}
546 	sc->usc_sock.sock = true;
547 	sc->tty.rfd = sc->tty.wfd = -1;
548 	sc->usc_sock.servmev = mevent_add(sc->usc_sock.servfd, EVF_READ,
549 	    uart_sock_accept, sc);
550 	assert(sc->usc_sock.servmev != NULL);
551 
552 	sc->usc_sock.drain = drain;
553 	sc->usc_sock.drainarg = drainarg;
554 
555 	free(tofree);
556 	return (0);
557 }
558 #endif /* not __FreeBSD__ */
559 
560 struct uart_softc *
uart_init(void)561 uart_init(void)
562 {
563 	struct uart_softc *sc = calloc(1, sizeof(struct uart_softc));
564 	if (sc == NULL)
565 		return (NULL);
566 
567 	pthread_mutex_init(&sc->mtx, NULL);
568 
569 	return (sc);
570 }
571 
572 int
uart_tty_open(struct uart_softc * sc,const char * path,void (* drain)(int,enum ev_type,void *),void * arg)573 uart_tty_open(struct uart_softc *sc, const char *path,
574     void (*drain)(int, enum ev_type, void *), void *arg)
575 {
576 	int retval;
577 
578 #ifndef __FreeBSD__
579 	if (strncmp("socket,", path, 7) == 0)
580 		return (uart_sock_backend(sc, path, drain, arg));
581 #endif
582 	if (strcmp("stdio", path) == 0)
583 		retval = uart_stdio_backend(sc);
584 	else
585 		retval = uart_tty_backend(sc, path);
586 	if (retval == 0) {
587 		ttyopen(&sc->tty);
588 		sc->mev = mevent_add(sc->tty.rfd, EVF_READ, drain, arg);
589 		assert(sc->mev != NULL);
590 	}
591 
592 	return (retval);
593 }
594 
595 void
uart_softc_lock(struct uart_softc * sc)596 uart_softc_lock(struct uart_softc *sc)
597 {
598 	pthread_mutex_lock(&sc->mtx);
599 }
600 
601 void
uart_softc_unlock(struct uart_softc * sc)602 uart_softc_unlock(struct uart_softc *sc)
603 {
604 	pthread_mutex_unlock(&sc->mtx);
605 }
606