diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile --- a/usr.sbin/bhyve/Makefile +++ b/usr.sbin/bhyve/Makefile @@ -56,6 +56,7 @@ tpm_emul_passthru.c \ tpm_intf_crb.c \ tpm_ppi_qemu.c \ + uart_backend.c \ uart_emul.c \ usb_emul.c \ usb_mouse.c \ diff --git a/usr.sbin/bhyve/amd64/pci_lpc.c b/usr.sbin/bhyve/amd64/pci_lpc.c --- a/usr.sbin/bhyve/amd64/pci_lpc.c +++ b/usr.sbin/bhyve/amd64/pci_lpc.c @@ -69,7 +69,7 @@ #define LPC_UART_NUM 4 static struct lpc_uart_softc { - struct uart_softc *uart_softc; + struct uart_ns16550_softc *uart_softc; int iobase; int irq; int enabled; @@ -226,17 +226,19 @@ switch (bytes) { case 1: if (in) - *eax = uart_read(sc->uart_softc, offset); + *eax = uart_ns16550_read(sc->uart_softc, offset); else - uart_write(sc->uart_softc, offset, *eax); + uart_ns16550_write(sc->uart_softc, offset, *eax); break; case 2: if (in) { - *eax = uart_read(sc->uart_softc, offset); - *eax |= uart_read(sc->uart_softc, offset + 1) << 8; + *eax = uart_ns16550_read(sc->uart_softc, offset); + *eax |= + uart_ns16550_read(sc->uart_softc, offset + 1) << 8; } else { - uart_write(sc->uart_softc, offset, *eax); - uart_write(sc->uart_softc, offset + 1, *eax >> 8); + uart_ns16550_write(sc->uart_softc, offset, *eax); + uart_ns16550_write(sc->uart_softc, offset + 1, + *eax >> 8); } break; default: @@ -275,13 +277,14 @@ } pci_irq_reserve(sc->irq); - sc->uart_softc = uart_init(lpc_uart_intr_assert, - lpc_uart_intr_deassert, sc); + sc->uart_softc = uart_ns16550_init(lpc_uart_intr_assert, + lpc_uart_intr_deassert, sc); asprintf(&node_name, "lpc.%s.path", name); backend = get_config_value(node_name); free(node_name); - if (uart_set_backend(sc->uart_softc, backend) != 0) { + if (backend != NULL && + uart_ns16550_tty_open(sc->uart_softc, backend) != 0) { EPRINTLN("Unable to initialize backend '%s' " "for LPC device %s", backend, name); return (-1); @@ -290,7 +293,7 @@ bzero(&iop, sizeof(struct inout_port)); iop.name = name; iop.port = sc->iobase; - iop.size = UART_IO_BAR_SIZE; + iop.size = UART_NS16550_IO_BAR_SIZE; iop.flags = IOPORT_F_INOUT; iop.handler = lpc_uart_io_handler; iop.arg = sc; @@ -423,7 +426,7 @@ dsdt_line(" Name (_CRS, ResourceTemplate ()"); dsdt_line(" {"); dsdt_indent(2); - dsdt_fixed_ioport(sc->iobase, UART_IO_BAR_SIZE); + dsdt_fixed_ioport(sc->iobase, UART_NS16550_IO_BAR_SIZE); dsdt_fixed_irq(sc->irq); dsdt_unindent(2); dsdt_line(" })"); @@ -588,12 +591,12 @@ pci_lpc_snapshot(struct vm_snapshot_meta *meta) { int unit, ret; - struct uart_softc *sc; + struct uart_ns16550_softc *sc; for (unit = 0; unit < LPC_UART_NUM; unit++) { sc = lpc_uart_softc[unit].uart_softc; - ret = uart_snapshot(sc, meta); + ret = uart_ns16550_snapshot(sc, meta); if (ret != 0) goto done; } diff --git a/usr.sbin/bhyve/pci_uart.c b/usr.sbin/bhyve/pci_uart.c --- a/usr.sbin/bhyve/pci_uart.c +++ b/usr.sbin/bhyve/pci_uart.c @@ -67,7 +67,7 @@ assert(baridx == 0); assert(size == 1); - uart_write(pi->pi_arg, offset, value); + uart_ns16550_write(pi->pi_arg, offset, value); } static uint64_t @@ -78,7 +78,7 @@ assert(baridx == 0); assert(size == 1); - val = uart_read(pi->pi_arg, offset); + val = uart_ns16550_read(pi->pi_arg, offset); return (val); } @@ -94,10 +94,10 @@ static int pci_uart_init(struct pci_devinst *pi, nvlist_t *nvl) { - struct uart_softc *sc; + struct uart_ns16550_softc *sc; const char *device; - pci_emul_alloc_bar(pi, 0, PCIBAR_IO, UART_IO_BAR_SIZE); + pci_emul_alloc_bar(pi, 0, PCIBAR_IO, UART_NS16550_IO_BAR_SIZE); pci_lintr_request(pi); /* initialize config space */ @@ -105,11 +105,12 @@ pci_set_cfgdata16(pi, PCIR_VENDOR, COM_VENDOR); pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_SIMPLECOMM); - sc = uart_init(pci_uart_intr_assert, pci_uart_intr_deassert, pi); + sc = uart_ns16550_init(pci_uart_intr_assert, pci_uart_intr_deassert, + pi); pi->pi_arg = sc; device = get_config_value_node(nvl, "path"); - if (uart_set_backend(sc, device) != 0) { + if (device != NULL && uart_ns16550_tty_open(sc, device) != 0) { EPRINTLN("Unable to initialize backend '%s' for " "pci uart at %d:%d", device, pi->pi_slot, pi->pi_func); return (-1); diff --git a/usr.sbin/bhyve/uart_emul.h b/usr.sbin/bhyve/uart_backend.h copy from usr.sbin/bhyve/uart_emul.h copy to usr.sbin/bhyve/uart_backend.h --- a/usr.sbin/bhyve/uart_emul.h +++ b/usr.sbin/bhyve/uart_backend.h @@ -1,6 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * + * Copyright (c) 2012 NetApp, Inc. * Copyright (c) 2013 Neel Natu * All rights reserved. * @@ -26,23 +27,29 @@ * SUCH DAMAGE. */ -#ifndef _UART_EMUL_H_ -#define _UART_EMUL_H_ +#ifndef _UART_BACKEND_H_ +#define _UART_BACKEND_H_ -#define UART_IO_BAR_SIZE 8 +#include + +#include "mevent.h" struct uart_softc; struct vm_snapshot_meta; -typedef void (*uart_intr_func_t)(void *arg); -struct uart_softc *uart_init(uart_intr_func_t intr_assert, - uart_intr_func_t intr_deassert, void *arg); - -int uart_legacy_alloc(int unit, int *ioaddr, int *irq); -uint8_t uart_read(struct uart_softc *sc, int offset); -void uart_write(struct uart_softc *sc, int offset, uint8_t value); -int uart_set_backend(struct uart_softc *sc, const char *device); +void uart_rxfifo_drain(struct uart_softc *sc, bool loopback); +int uart_rxfifo_getchar(struct uart_softc *sc); +int uart_rxfifo_numchars(struct uart_softc *sc); +int uart_rxfifo_putchar(struct uart_softc *sc, uint8_t ch, bool loopback); +void uart_rxfifo_reset(struct uart_softc *sc, int size); +int uart_rxfifo_size(struct uart_softc *sc); #ifdef BHYVE_SNAPSHOT -int uart_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta); -#endif +int uart_rxfifo_snapshot(struct uart_softc *sc, + struct vm_snapshot_meta *meta); #endif + +struct uart_softc *uart_init(void); +int uart_tty_open(struct uart_softc *sc, const char *path, + void (*drain)(int, enum ev_type, void *), void *arg); + +#endif /* _UART_BACKEND_H_ */ diff --git a/usr.sbin/bhyve/uart_backend.c b/usr.sbin/bhyve/uart_backend.c new file mode 100644 --- /dev/null +++ b/usr.sbin/bhyve/uart_backend.c @@ -0,0 +1,348 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2012 NetApp, Inc. + * Copyright (c) 2013 Neel Natu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "mevent.h" +#include "uart_backend.h" + +struct ttyfd { + bool opened; + int rfd; /* fd for reading */ + int wfd; /* fd for writing, may be == rfd */ +}; + +#define FIFOSZ 16 + +struct fifo { + uint8_t buf[FIFOSZ]; + int rindex; /* index to read from */ + int windex; /* index to write to */ + int num; /* number of characters in the fifo */ + int size; /* size of the fifo */ +}; + +struct uart_softc { + struct ttyfd tty; + struct fifo rxfifo; + struct mevent *mev; +}; + +static bool uart_stdio; /* stdio in use for i/o */ +static struct termios tio_stdio_orig; + +static void +ttyclose(void) +{ + tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); +} + +static void +ttyopen(struct ttyfd *tf) +{ + struct termios orig, new; + + tcgetattr(tf->rfd, &orig); + new = orig; + cfmakeraw(&new); + new.c_cflag |= CLOCAL; + tcsetattr(tf->rfd, TCSANOW, &new); + if (uart_stdio) { + tio_stdio_orig = orig; + atexit(ttyclose); + } + raw_stdio = 1; +} + +static int +ttyread(struct ttyfd *tf) +{ + unsigned char rb; + + if (read(tf->rfd, &rb, 1) == 1) + return (rb); + else + return (-1); +} + +static void +ttywrite(struct ttyfd *tf, unsigned char wb) +{ + (void)write(tf->wfd, &wb, 1); +} + +static bool +rxfifo_available(struct uart_softc *sc) +{ + return (sc->rxfifo.num < sc->rxfifo.size); +} + +int +uart_rxfifo_getchar(struct uart_softc *sc) +{ + struct fifo *fifo; + int c, error, wasfull; + + wasfull = 0; + fifo = &sc->rxfifo; + if (fifo->num > 0) { + if (!rxfifo_available(sc)) + wasfull = 1; + c = fifo->buf[fifo->rindex]; + fifo->rindex = (fifo->rindex + 1) % fifo->size; + fifo->num--; + if (wasfull) { + if (sc->tty.opened) { + error = mevent_enable(sc->mev); + assert(error == 0); + } + } + return (c); + } else + return (-1); +} + +int +uart_rxfifo_numchars(struct uart_softc *sc) +{ + return (sc->rxfifo.num); +} + +static int +rxfifo_putchar(struct uart_softc *sc, uint8_t ch) +{ + struct fifo *fifo; + int error; + + fifo = &sc->rxfifo; + + if (fifo->num < fifo->size) { + fifo->buf[fifo->windex] = ch; + fifo->windex = (fifo->windex + 1) % fifo->size; + fifo->num++; + if (!rxfifo_available(sc)) { + if (sc->tty.opened) { + /* + * Disable mevent callback if the FIFO is full. + */ + error = mevent_disable(sc->mev); + assert(error == 0); + } + } + return (0); + } else + return (-1); +} + +void +uart_rxfifo_drain(struct uart_softc *sc, bool loopback) +{ + int ch; + + if (loopback) { + (void)ttyread(&sc->tty); + } else { + while (rxfifo_available(sc) && + ((ch = ttyread(&sc->tty)) != -1)) + rxfifo_putchar(sc, ch); + } +} + +int +uart_rxfifo_putchar(struct uart_softc *sc, uint8_t ch, bool loopback) +{ + if (loopback) { + return (rxfifo_putchar(sc, ch)); + } else if (sc->tty.opened) { + ttywrite(&sc->tty, ch); + return (0); + } else { + /* Drop on the floor. */ + return (0); + } +} + +void +uart_rxfifo_reset(struct uart_softc *sc, int size) +{ + char flushbuf[32]; + struct fifo *fifo; + ssize_t nread; + int error; + + fifo = &sc->rxfifo; + bzero(fifo, sizeof(struct fifo)); + fifo->size = size; + + if (sc->tty.opened) { + /* + * Flush any unread input from the tty buffer. + */ + while (1) { + nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf)); + if (nread != sizeof(flushbuf)) + break; + } + + /* + * Enable mevent to trigger when new characters are available + * on the tty fd. + */ + error = mevent_enable(sc->mev); + assert(error == 0); + } +} + +int +uart_rxfifo_size(struct uart_softc *sc __unused) +{ + return (FIFOSZ); +} + +#ifdef BHYVE_SNAPSHOT +int +uart_rxfifo_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta) +{ + int ret; + + SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done); + SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf), + meta, ret, done); + +done: + return (ret); +} +#endif + +static int +uart_stdio_backend(struct uart_softc *sc) +{ +#ifndef WITHOUT_CAPSICUM + cap_rights_t rights; + cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; +#endif + + if (uart_stdio) + return (-1); + + sc->tty.rfd = STDIN_FILENO; + sc->tty.wfd = STDOUT_FILENO; + sc->tty.opened = true; + + if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0) + return (-1); + if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0) + return (-1); + +#ifndef WITHOUT_CAPSICUM + cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ); + if (caph_rights_limit(sc->tty.rfd, &rights) == -1) + errx(EX_OSERR, "Unable to apply rights for sandbox"); + if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1) + errx(EX_OSERR, "Unable to apply rights for sandbox"); +#endif + + uart_stdio = true; + + return (0); +} + +static int +uart_tty_backend(struct uart_softc *sc, const char *path) +{ +#ifndef WITHOUT_CAPSICUM + cap_rights_t rights; + cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; +#endif + int fd; + + fd = open(path, O_RDWR | O_NONBLOCK); + if (fd < 0) + return (-1); + + if (!isatty(fd)) { + close(fd); + return (-1); + } + + sc->tty.rfd = sc->tty.wfd = fd; + sc->tty.opened = true; + +#ifndef WITHOUT_CAPSICUM + cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE); + if (caph_rights_limit(fd, &rights) == -1) + errx(EX_OSERR, "Unable to apply rights for sandbox"); + if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1) + errx(EX_OSERR, "Unable to apply rights for sandbox"); +#endif + + return (0); +} + +struct uart_softc * +uart_init(void) +{ + return (calloc(1, sizeof(struct uart_softc))); +} + +int +uart_tty_open(struct uart_softc *sc, const char *path, + void (*drain)(int, enum ev_type, void *), void *arg) +{ + int retval; + + if (strcmp("stdio", path) == 0) + retval = uart_stdio_backend(sc); + else + retval = uart_tty_backend(sc, path); + if (retval == 0) { + ttyopen(&sc->tty); + sc->mev = mevent_add(sc->tty.rfd, EVF_READ, drain, arg); + assert(sc->mev != NULL); + } + + return (retval); +} diff --git a/usr.sbin/bhyve/uart_emul.h b/usr.sbin/bhyve/uart_emul.h --- a/usr.sbin/bhyve/uart_emul.h +++ b/usr.sbin/bhyve/uart_emul.h @@ -29,20 +29,24 @@ #ifndef _UART_EMUL_H_ #define _UART_EMUL_H_ -#define UART_IO_BAR_SIZE 8 +#define UART_NS16550_IO_BAR_SIZE 8 -struct uart_softc; +struct uart_ns16550_softc; struct vm_snapshot_meta; typedef void (*uart_intr_func_t)(void *arg); -struct uart_softc *uart_init(uart_intr_func_t intr_assert, - uart_intr_func_t intr_deassert, void *arg); int uart_legacy_alloc(int unit, int *ioaddr, int *irq); -uint8_t uart_read(struct uart_softc *sc, int offset); -void uart_write(struct uart_softc *sc, int offset, uint8_t value); -int uart_set_backend(struct uart_softc *sc, const char *device); + +struct uart_ns16550_softc *uart_ns16550_init(uart_intr_func_t intr_assert, + uart_intr_func_t intr_deassert, void *arg); +uint8_t uart_ns16550_read(struct uart_ns16550_softc *sc, int offset); +void uart_ns16550_write(struct uart_ns16550_softc *sc, int offset, + uint8_t value); +int uart_ns16550_tty_open(struct uart_ns16550_softc *sc, + const char *device); #ifdef BHYVE_SNAPSHOT -int uart_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta); +int uart_ns16550_snapshot(struct uart_ns16550_softc *sc, + struct vm_snapshot_meta *meta); #endif #endif diff --git a/usr.sbin/bhyve/uart_emul.c b/usr.sbin/bhyve/uart_emul.c --- a/usr.sbin/bhyve/uart_emul.c +++ b/usr.sbin/bhyve/uart_emul.c @@ -29,29 +29,20 @@ #include #include -#ifndef WITHOUT_CAPSICUM -#include -#include -#endif #include +#include #include #include -#include -#include #include -#include -#include #include #include #include #include -#include -#include "mevent.h" +#include "uart_backend.h" #include "uart_emul.h" -#include "debug.h" #define COM1_BASE 0x3F8 #define COM1_IRQ 4 @@ -76,11 +67,6 @@ #define REG_SCR com_scr #endif -#define FIFOSZ 16 - -static bool uart_stdio; /* stdio in use for i/o */ -static struct termios tio_stdio_orig; - static struct { int baseaddr; int irq; @@ -94,21 +80,9 @@ #define UART_NLDEVS (sizeof(uart_lres) / sizeof(uart_lres[0])) -struct fifo { - uint8_t buf[FIFOSZ]; - int rindex; /* index to read from */ - int windex; /* index to write to */ - int num; /* number of characters in the fifo */ - int size; /* size of the fifo */ -}; - -struct ttyfd { - bool opened; - int rfd; /* fd for reading */ - int wfd; /* fd for writing, may be == rfd */ -}; +struct uart_ns16550_softc { + struct uart_softc *backend; -struct uart_softc { pthread_mutex_t mtx; /* protects all softc elements */ uint8_t data; /* Data register (R/W) */ uint8_t ier; /* Interrupt enable register (R/W) */ @@ -122,10 +96,6 @@ uint8_t dll; /* Baudrate divisor latch LSB */ uint8_t dlh; /* Baudrate divisor latch MSB */ - struct fifo rxfifo; - struct mevent *mev; - - struct ttyfd tty; bool thre_int_pending; /* THRE interrupt pending */ void *arg; @@ -133,158 +103,6 @@ uart_intr_func_t intr_deassert; }; -static void uart_drain(int fd, enum ev_type ev, void *arg); - -static void -ttyclose(void) -{ - - tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); -} - -static void -ttyopen(struct ttyfd *tf) -{ - struct termios orig, new; - - tcgetattr(tf->rfd, &orig); - new = orig; - cfmakeraw(&new); - new.c_cflag |= CLOCAL; - tcsetattr(tf->rfd, TCSANOW, &new); - if (uart_stdio) { - tio_stdio_orig = orig; - atexit(ttyclose); - } - raw_stdio = 1; -} - -static int -ttyread(struct ttyfd *tf) -{ - unsigned char rb; - - if (read(tf->rfd, &rb, 1) == 1) - return (rb); - else - return (-1); -} - -static void -ttywrite(struct ttyfd *tf, unsigned char wb) -{ - - (void)write(tf->wfd, &wb, 1); -} - -static void -rxfifo_reset(struct uart_softc *sc, int size) -{ - char flushbuf[32]; - struct fifo *fifo; - ssize_t nread; - int error; - - fifo = &sc->rxfifo; - bzero(fifo, sizeof(struct fifo)); - fifo->size = size; - - if (sc->tty.opened) { - /* - * Flush any unread input from the tty buffer. - */ - while (1) { - nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf)); - if (nread != sizeof(flushbuf)) - break; - } - - /* - * Enable mevent to trigger when new characters are available - * on the tty fd. - */ - error = mevent_enable(sc->mev); - assert(error == 0); - } -} - -static int -rxfifo_available(struct uart_softc *sc) -{ - struct fifo *fifo; - - fifo = &sc->rxfifo; - return (fifo->num < fifo->size); -} - -static int -rxfifo_putchar(struct uart_softc *sc, uint8_t ch) -{ - struct fifo *fifo; - int error; - - fifo = &sc->rxfifo; - - if (fifo->num < fifo->size) { - fifo->buf[fifo->windex] = ch; - fifo->windex = (fifo->windex + 1) % fifo->size; - fifo->num++; - if (!rxfifo_available(sc)) { - if (sc->tty.opened) { - /* - * Disable mevent callback if the FIFO is full. - */ - error = mevent_disable(sc->mev); - assert(error == 0); - } - } - return (0); - } else - return (-1); -} - -static int -rxfifo_getchar(struct uart_softc *sc) -{ - struct fifo *fifo; - int c, error, wasfull; - - wasfull = 0; - fifo = &sc->rxfifo; - if (fifo->num > 0) { - if (!rxfifo_available(sc)) - wasfull = 1; - c = fifo->buf[fifo->rindex]; - fifo->rindex = (fifo->rindex + 1) % fifo->size; - fifo->num--; - if (wasfull) { - if (sc->tty.opened) { - error = mevent_enable(sc->mev); - assert(error == 0); - } - } - return (c); - } else - return (-1); -} - -static int -rxfifo_numchars(struct uart_softc *sc) -{ - struct fifo *fifo = &sc->rxfifo; - - return (fifo->num); -} - -static void -uart_opentty(struct uart_softc *sc) -{ - - ttyopen(&sc->tty); - sc->mev = mevent_add(sc->tty.rfd, EVF_READ, uart_drain, sc); - assert(sc->mev != NULL); -} - static uint8_t modem_status(uint8_t mcr) { @@ -325,12 +143,13 @@ * Return an interrupt reason if one is available. */ static int -uart_intr_reason(struct uart_softc *sc) +uart_intr_reason(struct uart_ns16550_softc *sc) { if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0) return (IIR_RLS); - else if (rxfifo_numchars(sc) > 0 && (sc->ier & IER_ERXRDY) != 0) + else if (uart_rxfifo_numchars(sc->backend) > 0 && + (sc->ier & IER_ERXRDY) != 0) return (IIR_RXTOUT); else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0) return (IIR_TXRDY); @@ -341,7 +160,7 @@ } static void -uart_reset(struct uart_softc *sc) +uart_reset(struct uart_ns16550_softc *sc) { uint16_t divisor; @@ -350,7 +169,7 @@ sc->dlh = divisor >> 16; sc->msr = modem_status(sc->mcr); - rxfifo_reset(sc, 1); /* no fifo until enabled by software */ + uart_rxfifo_reset(sc->backend, 1); } /* @@ -358,7 +177,7 @@ * interrupt condition to report to the processor. */ static void -uart_toggle_intr(struct uart_softc *sc) +uart_toggle_intr(struct uart_ns16550_softc *sc) { uint8_t intr_reason; @@ -371,14 +190,13 @@ } static void -uart_drain(int fd, enum ev_type ev, void *arg) +uart_drain(int fd __unused, enum ev_type ev, void *arg) { - struct uart_softc *sc; - int ch; + struct uart_ns16550_softc *sc; + bool loopback; sc = arg; - assert(fd == sc->tty.rfd); assert(ev == EVF_READ); /* @@ -388,21 +206,16 @@ */ pthread_mutex_lock(&sc->mtx); - if ((sc->mcr & MCR_LOOPBACK) != 0) { - (void) ttyread(&sc->tty); - } else { - while (rxfifo_available(sc) && - ((ch = ttyread(&sc->tty)) != -1)) { - rxfifo_putchar(sc, ch); - } + loopback = (sc->mcr & MCR_LOOPBACK) != 0; + uart_rxfifo_drain(sc->backend, loopback); + if (!loopback) uart_toggle_intr(sc); - } pthread_mutex_unlock(&sc->mtx); } void -uart_write(struct uart_softc *sc, int offset, uint8_t value) +uart_ns16550_write(struct uart_ns16550_softc *sc, int offset, uint8_t value) { int fifosz; uint8_t msr; @@ -426,12 +239,9 @@ switch (offset) { case REG_DATA: - if (sc->mcr & MCR_LOOPBACK) { - if (rxfifo_putchar(sc, value) != 0) - sc->lsr |= LSR_OE; - } else if (sc->tty.opened) { - ttywrite(&sc->tty, value); - } /* else drop on floor */ + if (uart_rxfifo_putchar(sc->backend, value, + (sc->mcr & MCR_LOOPBACK) != 0)) + sc->lsr |= LSR_OE; sc->thre_int_pending = true; break; case REG_IER: @@ -450,8 +260,9 @@ * the FIFO contents are reset. */ if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) { - fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1; - rxfifo_reset(sc, fifosz); + fifosz = (value & FCR_ENABLE) ? + uart_rxfifo_size(sc->backend) : 1; + uart_rxfifo_reset(sc->backend, fifosz); } /* @@ -462,7 +273,8 @@ sc->fcr = 0; } else { if ((value & FCR_RCV_RST) != 0) - rxfifo_reset(sc, FIFOSZ); + uart_rxfifo_reset(sc->backend, + uart_rxfifo_size(sc->backend)); sc->fcr = value & (FCR_ENABLE | FCR_DMA | FCR_RX_MASK); @@ -521,7 +333,7 @@ } uint8_t -uart_read(struct uart_softc *sc, int offset) +uart_ns16550_read(struct uart_ns16550_softc *sc, int offset) { uint8_t iir, intr_reason, reg; @@ -544,7 +356,7 @@ switch (offset) { case REG_DATA: - reg = rxfifo_getchar(sc); + reg = uart_rxfifo_getchar(sc->backend); break; case REG_IER: reg = sc->ier; @@ -575,7 +387,7 @@ sc->lsr |= LSR_TEMT | LSR_THRE; /* Check for new receive data */ - if (rxfifo_numchars(sc) > 0) + if (uart_rxfifo_numchars(sc->backend) > 0) sc->lsr |= LSR_RXRDY; else sc->lsr &= ~LSR_RXRDY; @@ -621,17 +433,18 @@ return (0); } -struct uart_softc * -uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert, +struct uart_ns16550_softc * +uart_ns16550_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert, void *arg) { - struct uart_softc *sc; + struct uart_ns16550_softc *sc; - sc = calloc(1, sizeof(struct uart_softc)); + sc = calloc(1, sizeof(struct uart_ns16550_softc)); sc->arg = arg; sc->intr_assert = intr_assert; sc->intr_deassert = intr_deassert; + sc->backend = uart_init(); pthread_mutex_init(&sc->mtx, NULL); @@ -640,92 +453,16 @@ return (sc); } -static int -uart_stdio_backend(struct uart_softc *sc) -{ -#ifndef WITHOUT_CAPSICUM - cap_rights_t rights; - cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; -#endif - - if (uart_stdio) - return (-1); - - sc->tty.rfd = STDIN_FILENO; - sc->tty.wfd = STDOUT_FILENO; - sc->tty.opened = true; - - if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0) - return (-1); - if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0) - return (-1); - -#ifndef WITHOUT_CAPSICUM - cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ); - if (caph_rights_limit(sc->tty.rfd, &rights) == -1) - errx(EX_OSERR, "Unable to apply rights for sandbox"); - if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1) - errx(EX_OSERR, "Unable to apply rights for sandbox"); -#endif - - uart_stdio = true; - - return (0); -} - -static int -uart_tty_backend(struct uart_softc *sc, const char *path) -{ -#ifndef WITHOUT_CAPSICUM - cap_rights_t rights; - cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; -#endif - int fd; - - fd = open(path, O_RDWR | O_NONBLOCK); - if (fd < 0) - return (-1); - - if (!isatty(fd)) { - close(fd); - return (-1); - } - - sc->tty.rfd = sc->tty.wfd = fd; - sc->tty.opened = true; - -#ifndef WITHOUT_CAPSICUM - cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE); - if (caph_rights_limit(fd, &rights) == -1) - errx(EX_OSERR, "Unable to apply rights for sandbox"); - if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1) - errx(EX_OSERR, "Unable to apply rights for sandbox"); -#endif - - return (0); -} - int -uart_set_backend(struct uart_softc *sc, const char *device) +uart_ns16550_tty_open(struct uart_ns16550_softc *sc, const char *device) { - int retval; - - if (device == NULL) - return (0); - - if (strcmp("stdio", device) == 0) - retval = uart_stdio_backend(sc); - else - retval = uart_tty_backend(sc, device); - if (retval == 0) - uart_opentty(sc); - - return (retval); + return (uart_tty_open(sc->backend, device, uart_drain, sc)); } #ifdef BHYVE_SNAPSHOT int -uart_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta) +uart_ns16550_snapshot(struct uart_ns16550_softc *sc, + struct vm_snapshot_meta *meta) { int ret; @@ -741,12 +478,7 @@ SNAPSHOT_VAR_OR_LEAVE(sc->dll, meta, ret, done); SNAPSHOT_VAR_OR_LEAVE(sc->dlh, meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done); - SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done); - SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf), - meta, ret, done); + ret = uart_rxfifo_snapshot(sc->backend, meta); sc->thre_int_pending = 1;