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#include <machine/vmm_snapshot.h> 34 35#include <assert.h> 36#include <capsicum_helpers.h> 37#include <err.h> 38#include <pthread.h> 39#include <stdbool.h> 40#include <stdlib.h> 41#include <string.h> 42#include <sysexits.h> 43#include <termios.h> 44#include <unistd.h> 45 46#include "debug.h" 47#include "mevent.h" 48#include "uart_backend.h" 49 50struct ttyfd { 51 bool opened; 52 int rfd; /* fd for reading */ 53 int wfd; /* fd for writing, may be == rfd */ 54}; 55 56#define FIFOSZ 16 57 58struct fifo { 59 uint8_t buf[FIFOSZ]; 60 int rindex; /* index to read from */ 61 int windex; /* index to write to */ 62 int num; /* number of characters in the fifo */ 63 int size; /* size of the fifo */ 64}; 65 66struct uart_softc { 67 struct ttyfd tty; 68 struct fifo rxfifo; 69 struct mevent *mev; 70 pthread_mutex_t mtx; 71}; 72 73static bool uart_stdio; /* stdio in use for i/o */ 74static struct termios tio_stdio_orig; 75 76static void 77ttyclose(void) 78{ 79 tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); 80} 81 82static void 83ttyopen(struct ttyfd *tf) 84{ 85 struct termios orig, new; 86 87 tcgetattr(tf->rfd, &orig); 88 new = orig; 89 cfmakeraw(&new); 90 new.c_cflag |= CLOCAL; 91 tcsetattr(tf->rfd, TCSANOW, &new); 92 if (uart_stdio) { 93 tio_stdio_orig = orig; 94 atexit(ttyclose); 95 } 96 raw_stdio = 1; 97} 98 99static int 100ttyread(struct ttyfd *tf) 101{ 102 unsigned char rb; 103 104 if (read(tf->rfd, &rb, 1) == 1) 105 return (rb); 106 else 107 return (-1); 108} 109 110static void 111ttywrite(struct ttyfd *tf, unsigned char wb) 112{ 113 (void)write(tf->wfd, &wb, 1); 114} 115 116static bool 117rxfifo_available(struct uart_softc *sc) 118{ 119 return (sc->rxfifo.num < sc->rxfifo.size); 120} 121 122int 123uart_rxfifo_getchar(struct uart_softc *sc) 124{ 125 struct fifo *fifo; 126 int c, error, wasfull; 127 128 wasfull = 0; 129 fifo = &sc->rxfifo; 130 if (fifo->num > 0) { 131 if (!rxfifo_available(sc)) 132 wasfull = 1; 133 c = fifo->buf[fifo->rindex]; 134 fifo->rindex = (fifo->rindex + 1) % fifo->size; 135 fifo->num--; 136 if (wasfull) { 137 if (sc->tty.opened) { 138 error = mevent_enable(sc->mev); 139 assert(error == 0); 140 } 141 } 142 return (c); 143 } else 144 return (-1); 145} 146 147int 148uart_rxfifo_numchars(struct uart_softc *sc) 149{ 150 return (sc->rxfifo.num); 151} 152 153static int 154rxfifo_putchar(struct uart_softc *sc, uint8_t ch) 155{ 156 struct fifo *fifo; 157 int error; 158 159 fifo = &sc->rxfifo; 160 161 if (fifo->num < fifo->size) { 162 fifo->buf[fifo->windex] = ch; 163 fifo->windex = (fifo->windex + 1) % fifo->size; 164 fifo->num++; 165 if (!rxfifo_available(sc)) { 166 if (sc->tty.opened) { 167 /* 168 * Disable mevent callback if the FIFO is full. 169 */ 170 error = mevent_disable(sc->mev); 171 assert(error == 0); 172 } 173 } 174 return (0); 175 } else 176 return (-1); 177} 178 179void 180uart_rxfifo_drain(struct uart_softc *sc, bool loopback) 181{ 182 int ch; 183 184 if (loopback) { 185 (void)ttyread(&sc->tty); 186 } else { 187 while (rxfifo_available(sc) && 188 ((ch = ttyread(&sc->tty)) != -1)) 189 rxfifo_putchar(sc, ch); 190 } 191} 192 193int 194uart_rxfifo_putchar(struct uart_softc *sc, uint8_t ch, bool loopback) 195{ 196 if (loopback) { 197 return (rxfifo_putchar(sc, ch)); 198 } else if (sc->tty.opened) { 199 ttywrite(&sc->tty, ch); 200 return (0); 201 } else { 202 /* Drop on the floor. */ 203 return (0); 204 } 205} 206 207void 208uart_rxfifo_reset(struct uart_softc *sc, int size) 209{ 210 char flushbuf[32]; 211 struct fifo *fifo; 212 ssize_t nread; 213 int error; 214 215 fifo = &sc->rxfifo; 216 bzero(fifo, sizeof(struct fifo)); 217 fifo->size = size; 218 219 if (sc->tty.opened) { 220 /* 221 * Flush any unread input from the tty buffer. 222 */ 223 while (1) { 224 nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf)); 225 if (nread != sizeof(flushbuf)) 226 break; 227 } 228 229 /* 230 * Enable mevent to trigger when new characters are available 231 * on the tty fd. 232 */ 233 error = mevent_enable(sc->mev); 234 assert(error == 0); 235 } 236} 237 238int 239uart_rxfifo_size(struct uart_softc *sc __unused) 240{ 241 return (FIFOSZ); 242} 243 244#ifdef BHYVE_SNAPSHOT 245int 246uart_rxfifo_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta) 247{ 248 int ret; 249 250 SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done); 251 SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done); 252 SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done); 253 SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done); 254 SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf), 255 meta, ret, done); 256 257done: 258 return (ret); 259} 260#endif 261 262static int 263uart_stdio_backend(struct uart_softc *sc) 264{ 265#ifndef WITHOUT_CAPSICUM 266 cap_rights_t rights; 267 cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; 268#endif 269 270 if (uart_stdio) 271 return (-1); 272 273 sc->tty.rfd = STDIN_FILENO; 274 sc->tty.wfd = STDOUT_FILENO; 275 sc->tty.opened = true; 276 277 if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0) 278 return (-1); 279 if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0) 280 return (-1); 281 282#ifndef WITHOUT_CAPSICUM 283 cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ); 284 if (caph_rights_limit(sc->tty.rfd, &rights) == -1) 285 errx(EX_OSERR, "Unable to apply rights for sandbox"); 286 if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1) 287 errx(EX_OSERR, "Unable to apply rights for sandbox"); 288#endif 289 290 uart_stdio = true; 291 292 return (0); 293} 294 295static int 296uart_tty_backend(struct uart_softc *sc, const char *path) 297{ 298#ifndef WITHOUT_CAPSICUM 299 cap_rights_t rights; 300 cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; 301#endif 302 int fd; 303 304 fd = open(path, O_RDWR | O_NONBLOCK); 305 if (fd < 0) 306 return (-1); 307 308 if (!isatty(fd)) { 309 close(fd); 310 return (-1); 311 } 312 313 sc->tty.rfd = sc->tty.wfd = fd; 314 sc->tty.opened = true; 315 316#ifndef WITHOUT_CAPSICUM 317 cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE); 318 if (caph_rights_limit(fd, &rights) == -1) 319 errx(EX_OSERR, "Unable to apply rights for sandbox"); 320 if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1) 321 errx(EX_OSERR, "Unable to apply rights for sandbox"); 322#endif 323 324 return (0); 325} 326 327struct uart_softc * 328uart_init(void) 329{ 330 struct uart_softc *sc = calloc(1, sizeof(struct uart_softc)); 331 if (sc == NULL) 332 return (NULL); 333 334 pthread_mutex_init(&sc->mtx, NULL); 335 336 return (sc); 337} 338 339int 340uart_tty_open(struct uart_softc *sc, const char *path, 341 void (*drain)(int, enum ev_type, void *), void *arg) 342{ 343 int retval; 344 345 if (strcmp("stdio", path) == 0) 346 retval = uart_stdio_backend(sc); 347 else 348 retval = uart_tty_backend(sc, path); 349 if (retval == 0) { 350 ttyopen(&sc->tty); 351 sc->mev = mevent_add(sc->tty.rfd, EVF_READ, drain, arg); 352 assert(sc->mev != NULL); 353 } 354 355 return (retval); 356} 357 358void 359uart_softc_lock(struct uart_softc *sc) 360{ 361 pthread_mutex_lock(&sc->mtx); 362} 363 364void 365uart_softc_unlock(struct uart_softc *sc) 366{ 367 pthread_mutex_unlock(&sc->mtx); 368} 369