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 50 struct 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 58 struct 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 66 struct uart_softc { 67 struct ttyfd tty; 68 struct fifo rxfifo; 69 struct mevent *mev; 70 pthread_mutex_t mtx; 71 }; 72 73 static bool uart_stdio; /* stdio in use for i/o */ 74 static struct termios tio_stdio_orig; 75 76 static void 77 ttyclose(void) 78 { 79 tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); 80 } 81 82 static void 83 ttyopen(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 99 static int 100 ttyread(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 110 static void 111 ttywrite(struct ttyfd *tf, unsigned char wb) 112 { 113 (void)write(tf->wfd, &wb, 1); 114 } 115 116 static bool 117 rxfifo_available(struct uart_softc *sc) 118 { 119 return (sc->rxfifo.num < sc->rxfifo.size); 120 } 121 122 int 123 uart_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 147 int 148 uart_rxfifo_numchars(struct uart_softc *sc) 149 { 150 return (sc->rxfifo.num); 151 } 152 153 static int 154 rxfifo_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 179 void 180 uart_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 193 int 194 uart_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 207 void 208 uart_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 238 int 239 uart_rxfifo_size(struct uart_softc *sc __unused) 240 { 241 return (FIFOSZ); 242 } 243 244 #ifdef BHYVE_SNAPSHOT 245 int 246 uart_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 257 done: 258 return (ret); 259 } 260 #endif 261 262 static int 263 uart_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 295 static int 296 uart_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 327 struct uart_softc * 328 uart_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 339 int 340 uart_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 358 void 359 uart_softc_lock(struct uart_softc *sc) 360 { 361 pthread_mutex_lock(&sc->mtx); 362 } 363 364 void 365 uart_softc_unlock(struct uart_softc *sc) 366 { 367 pthread_mutex_unlock(&sc->mtx); 368 } 369