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