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