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