From nobody Fri Mar 29 18:11:55 2024 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4V5pQP33fLz5FNYK; Fri, 29 Mar 2024 18:11:57 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4V5pQM2tldz4hJF; Fri, 29 Mar 2024 18:11:55 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1711735915; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=5q9h9v71HZyvzIbGCSITupd4WelVvjxGUhrTy7HN/Rk=; b=DOoGFDbuAFHRommjgf0WfIfoGY7VTR6NmIpkA3Q5ZbJsstrrK202azDWmqW1whr09OWKB1 Y6P+htIPT5c0U36Dg32kDTViEykGRRNgGnmKjsK3Em6tWk+aKt1AAv4T9BD3wxnTleNPSy pRB+3YlkuS9RvS/dJgp69gyfEBo7/ZJGBUGCJYfwtZC19fLKjwbAnXTpuFVRacw7XXovc7 wzF+cgr6ybFEiNIApyj70/lFCaovzwdDZh1B2RmEXE835pa8PbASRWWDr1mZ6PXHEdlAan 8KL3NSsNqiK7a6NECKwSqa1FnzibIgJYcYJU4xUBeIpW4uBn7TwOZKh1cFPtYg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1711735915; a=rsa-sha256; cv=none; b=A5V+xVHuw0eei7N4FEN1YZYyOqoCWlIPa4dxnNulFoL2DxafUS0d7T80pAEqUq2EeByc5J nqvi3cPilAU7evoJcKXBMT5rnpJPUpq/hYBJN1RoIB9hVrF5bIa63A9J1SfgTxbDLeWGMN z/ojapbnZpsp6oBLBVnK1QV+D0qFZUoLi12t2bJPoK6KYCyKXmXfwCp+K11u7VlOHjWF+7 T1BGtG+2zovXgNmYi3k0Oji0+MbXyJTfXTI+Cds4OFvi7zx5dsGGD/4vLb0x00jhw9nVTy AF+2LwC5VCsuo1vg0TbyGfeeN/w7mLAsJFKuBHYrH6VKXbgP1dV0D/MYvNw13g== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1711735915; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=5q9h9v71HZyvzIbGCSITupd4WelVvjxGUhrTy7HN/Rk=; b=C8UxIAHHGqTW0nuXFkzySotTo4j2zn1mD9sJBwmXkz/P+8cTtUWVW7Z7ecoEjmse6mdMwL q4oNys2MaOhBNnghSj3Y+sm2BflZLWFcWyAXkgWAF1Q8NFsOO7IxW8TCTiOGXJGIFticwi P7HRxlopDliUOu0Tb3nIyYRYEr239gdSfDFJAgAbFXr3cO5MurDJMcbm4flmfkojLdRS/g dkAqgc4xIoqdYe7hdxdjdxrA77yb1IPD8H3towRHQjP+rCqCmoVII7SRbvqZoH6FaA39d5 5ik5LheX9BFnVzEpZa3vab5EzK2kolMR/18FjFv6Oiuzq8fiMtg5tUBM+sJoNA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4V5pQM2SsVzLbh; Fri, 29 Mar 2024 18:11:55 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 42TIBtBT078429; Fri, 29 Mar 2024 18:11:55 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 42TIBtxa078426; Fri, 29 Mar 2024 18:11:55 GMT (envelope-from git) Date: Fri, 29 Mar 2024 18:11:55 GMT Message-Id: <202403291811.42TIBtxa078426@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: 2b4745d2c193 - stable/14 - bhyve: Move device model-independent UART code into a separate file List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-branches@freebsd.org X-BeenThere: dev-commits-src-branches@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/stable/14 X-Git-Reftype: branch X-Git-Commit: 2b4745d2c193bede4b887f882e302c02c1f300ef Auto-Submitted: auto-generated The branch stable/14 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=2b4745d2c193bede4b887f882e302c02c1f300ef commit 2b4745d2c193bede4b887f882e302c02c1f300ef Author: Mark Johnston AuthorDate: 2024-03-21 04:20:37 +0000 Commit: Mark Johnston CommitDate: 2024-03-29 13:53:28 +0000 bhyve: Move device model-independent UART code into a separate file Currently bhyve implements a ns16550-compatible UART in uart_emul.c. This file also contains generic code to manage RX FIFOs and to handle reading from and writing to a TTY. bhyve instantiates UARTs to implement COM devices (via pci_lpc.c) and PCI UART devices. The arm64 port will bring with it a PL011 device model which is used as the default console (i.e., no COM ports). To simplify its integration, add a UART "backend" layer which lets UART device models allocate an RX FIFO and interact with TTYs without duplicating code. In particular, code in uart_backend.* is to be shared among device models, and the namespace for uart_emul.* is changed to uart_ns16550_*. This is based on andrew@'s work in https://github.com/zxombie/freebsd/tree/bhyvearm64 but I've made a number of changes, particularly with respect to naming and source code organization. No functional change intended. Reviewed by: corvink, jhb MFC after: 1 week Sponsored by: Innovate UK Differential Revision: https://reviews.freebsd.org/D40993 (cherry picked from commit d1c5d0cfcc1733c243d87f20477b115db4cf24b6) --- usr.sbin/bhyve/Makefile | 1 + usr.sbin/bhyve/amd64/pci_lpc.c | 31 ++-- usr.sbin/bhyve/pci_uart.c | 13 +- usr.sbin/bhyve/uart_backend.c | 348 +++++++++++++++++++++++++++++++++++++++++ usr.sbin/bhyve/uart_backend.h | 55 +++++++ usr.sbin/bhyve/uart_emul.c | 344 +++++----------------------------------- usr.sbin/bhyve/uart_emul.h | 20 ++- 7 files changed, 478 insertions(+), 334 deletions(-) diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile index c9d571daebbc..03ea77769754 100644 --- a/usr.sbin/bhyve/Makefile +++ b/usr.sbin/bhyve/Makefile @@ -56,6 +56,7 @@ SRCS= \ 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 index 5c2a2a7965b0..c29fd115809d 100644 --- a/usr.sbin/bhyve/amd64/pci_lpc.c +++ b/usr.sbin/bhyve/amd64/pci_lpc.c @@ -70,7 +70,7 @@ static struct pci_devinst *lpc_bridge; #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; @@ -227,17 +227,19 @@ lpc_uart_io_handler(struct vmctx *ctx __unused, int in, 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: @@ -276,13 +278,14 @@ lpc_init(struct vmctx *ctx) } 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); @@ -291,7 +294,7 @@ lpc_init(struct vmctx *ctx) 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; @@ -424,7 +427,7 @@ pci_lpc_uart_dsdt(void) 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(" })"); @@ -589,12 +592,12 @@ static int 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 index 362238bf3168..1e5278ba4d59 100644 --- a/usr.sbin/bhyve/pci_uart.c +++ b/usr.sbin/bhyve/pci_uart.c @@ -68,7 +68,7 @@ pci_uart_write(struct pci_devinst *pi, int baridx, uint64_t offset, int size, assert(baridx == 0); assert(size == 1); - uart_write(pi->pi_arg, offset, value); + uart_ns16550_write(pi->pi_arg, offset, value); } static uint64_t @@ -79,7 +79,7 @@ pci_uart_read(struct pci_devinst *pi, int baridx, uint64_t offset, int size) assert(baridx == 0); assert(size == 1); - val = uart_read(pi->pi_arg, offset); + val = uart_ns16550_read(pi->pi_arg, offset); return (val); } @@ -95,10 +95,10 @@ pci_uart_legacy_config(nvlist_t *nvl, const char *opts) 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 */ @@ -106,11 +106,12 @@ pci_uart_init(struct pci_devinst *pi, nvlist_t *nvl) 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_backend.c b/usr.sbin/bhyve/uart_backend.c new file mode 100644 index 000000000000..8d91f4f671e1 --- /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_backend.h b/usr.sbin/bhyve/uart_backend.h new file mode 100644 index 000000000000..fa7949ad6d1c --- /dev/null +++ b/usr.sbin/bhyve/uart_backend.h @@ -0,0 +1,55 @@ +/*- + * 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. + */ + +#ifndef _UART_BACKEND_H_ +#define _UART_BACKEND_H_ + +#include + +#include "mevent.h" + +struct uart_softc; +struct vm_snapshot_meta; + +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_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_emul.c b/usr.sbin/bhyve/uart_emul.c index 917ce36b67f6..05f552021bbf 100644 --- a/usr.sbin/bhyve/uart_emul.c +++ b/usr.sbin/bhyve/uart_emul.c @@ -30,29 +30,20 @@ #include #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 @@ -77,11 +68,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; @@ -95,21 +81,9 @@ static struct { #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) */ @@ -123,10 +97,6 @@ struct uart_softc { 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; @@ -134,158 +104,6 @@ struct uart_softc { 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) { @@ -326,12 +144,13 @@ modem_status(uint8_t mcr) * 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); @@ -342,7 +161,7 @@ uart_intr_reason(struct uart_softc *sc) } static void -uart_reset(struct uart_softc *sc) +uart_reset(struct uart_ns16550_softc *sc) { uint16_t divisor; @@ -351,7 +170,7 @@ uart_reset(struct uart_softc *sc) 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); } /* @@ -359,7 +178,7 @@ uart_reset(struct uart_softc *sc) * 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; @@ -372,14 +191,13 @@ uart_toggle_intr(struct uart_softc *sc) } 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); /* @@ -389,21 +207,16 @@ uart_drain(int fd, enum ev_type ev, void *arg) */ 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; @@ -427,12 +240,9 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value) 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: @@ -451,8 +261,9 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value) * 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); } /* @@ -463,7 +274,8 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value) 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); @@ -522,7 +334,7 @@ done: } uint8_t -uart_read(struct uart_softc *sc, int offset) *** 193 LINES SKIPPED ***