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 @@ -1,122 +1,123 @@ # # .include PROG= bhyve PACKAGE= bhyve MAN= bhyve.8 bhyve_config.5 BHYVE_SYSDIR?=${SRCTOP} .PATH: ${.CURDIR}/${MACHINE_CPUARCH} \ ${SRCTOP}/sys/libkern \ ${SRCTOP}/sys/cam/ctl SRCS= \ acpi.c \ acpi_device.c \ audio.c \ basl.c \ bhyvegc.c \ bhyverun.c \ bhyverun_machdep.c \ block_if.c \ bootrom.c \ config.c \ console.c \ crc16.c \ ctl_scsi_all.c \ ctl_util.c \ hda_codec.c \ iov.c \ mem.c \ mevent.c \ net_backend_netmap.c \ net_backend_slirp.c \ net_backends.c \ net_utils.c \ pci_emul.c \ pci_hostbridge.c \ pci_nvme.c \ pci_passthru.c \ pci_virtio_9p.c \ pci_virtio_block.c \ pci_virtio_console.c \ pci_virtio_input.c \ pci_virtio_net.c \ pci_virtio_rnd.c \ pci_virtio_scsi.c \ qemu_fwcfg.c \ qemu_loader.c \ smbiostbl.c \ sockstream.c \ tpm_device.c \ tpm_emul_passthru.c \ tpm_intf_crb.c \ tpm_ppi_qemu.c \ + uart_backend.c \ uart_emul.c \ usb_emul.c \ usb_mouse.c \ virtio.c \ vmexit.c \ vmgenc.c .if ${MK_BHYVE_SNAPSHOT} != "no" SRCS+= snapshot.c .endif .include "${MACHINE_CPUARCH}/Makefile.inc" .if defined(BHYVE_GDB_SUPPORT) SRCS+= gdb.c CFLAGS+= -DBHYVE_GDB .ifdef GDB_LOG CFLAGS+=-DGDB_LOG .endif SUBDIR+= gdb .endif CFLAGS+=-I${.CURDIR} \ -I${.CURDIR}/../../contrib/lib9p \ -I${SRCTOP}/sys LIBADD= vmmapi md nv pthread z util sbuf cam 9p .if ${MK_BHYVE_SNAPSHOT} != "no" LIBADD+= ucl xo .endif .if ${MK_INET_SUPPORT} != "no" CFLAGS+=-DINET .endif .if ${MK_INET6_SUPPORT} != "no" CFLAGS+=-DINET6 .endif .if ${MK_NETGRAPH_SUPPORT} != "no" SRCS+= net_backend_netgraph.c LIBADD+= netgraph .endif .if ${MK_OPENSSL} == "no" CFLAGS+=-DNO_OPENSSL .else LIBADD+= crypto CFLAGS+=-DOPENSSL_API_COMPAT=0x10100000L .endif CFLAGS+= -I${BHYVE_SYSDIR}/sys/dev/e1000 CFLAGS+= -I${BHYVE_SYSDIR}/sys/dev/mii CFLAGS+= -I${BHYVE_SYSDIR}/sys/dev/usb/controller .if ${MK_BHYVE_SNAPSHOT} != "no" CFLAGS+= -I${SRCTOP}/contrib/libucl/include CFLAGS+= -DBHYVE_SNAPSHOT .endif # Disable thread safety analysis since it only finds very simple bugs and # yields many false positives. NO_WTHREAD_SAFETY= NO_WCAST_ALIGN= .include 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 @@ -1,618 +1,621 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 Neel Natu * Copyright (c) 2013 Tycho Nightingale * 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 "acpi.h" #include "debug.h" #include "bootrom.h" #include "config.h" #include "inout.h" #include "pci_emul.h" #include "pci_irq.h" #include "pci_lpc.h" #include "pci_passthru.h" #include "pctestdev.h" #include "tpm_device.h" #include "uart_emul.h" #define IO_ICU1 0x20 #define IO_ICU2 0xA0 SET_DECLARE(lpc_dsdt_set, struct lpc_dsdt); SET_DECLARE(lpc_sysres_set, struct lpc_sysres); #define ELCR_PORT 0x4d0 SYSRES_IO(ELCR_PORT, 2); #define IO_TIMER1_PORT 0x40 #define NMISC_PORT 0x61 SYSRES_IO(NMISC_PORT, 1); 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; } lpc_uart_softc[LPC_UART_NUM]; static const char *lpc_uart_names[LPC_UART_NUM] = { "com1", "com2", "com3", "com4" }; static const char *lpc_uart_acpi_names[LPC_UART_NUM] = { "COM1", "COM2", "COM3", "COM4" }; /* * LPC device configuration is in the following form: * [,] * For e.g. "com1,stdio" or "bootrom,/var/romfile" */ int lpc_device_parse(const char *opts) { int unit, error; char *str, *cpy, *lpcdev, *node_name; const char *romfile, *varfile, *tpm_type, *tpm_path; error = -1; str = cpy = strdup(opts); lpcdev = strsep(&str, ","); if (lpcdev != NULL) { if (strcasecmp(lpcdev, "bootrom") == 0) { romfile = strsep(&str, ","); if (romfile == NULL) { errx(4, "invalid bootrom option \"%s\"", opts); } set_config_value("lpc.bootrom", romfile); varfile = strsep(&str, ","); if (varfile == NULL) { error = 0; goto done; } if (strchr(varfile, '=') == NULL) { set_config_value("lpc.bootvars", varfile); } else { /* varfile doesn't exist, it's another config * option */ pci_parse_legacy_config(find_config_node("lpc"), varfile); } pci_parse_legacy_config(find_config_node("lpc"), str); error = 0; goto done; } if (strcasecmp(lpcdev, "tpm") == 0) { nvlist_t *nvl = create_config_node("tpm"); tpm_type = strsep(&str, ","); if (tpm_type == NULL) { errx(4, "invalid tpm type \"%s\"", opts); } set_config_value_node(nvl, "type", tpm_type); tpm_path = strsep(&str, ","); if (tpm_path == NULL) { errx(4, "invalid tpm path \"%s\"", opts); } set_config_value_node(nvl, "path", tpm_path); pci_parse_legacy_config(find_config_node("tpm"), str); set_config_value_node_if_unset(nvl, "version", "2.0"); error = 0; goto done; } for (unit = 0; unit < LPC_UART_NUM; unit++) { if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) { asprintf(&node_name, "lpc.%s.path", lpc_uart_names[unit]); set_config_value(node_name, str); free(node_name); error = 0; goto done; } } if (strcasecmp(lpcdev, pctestdev_getname()) == 0) { asprintf(&node_name, "lpc.%s", pctestdev_getname()); set_config_bool(node_name, true); free(node_name); error = 0; goto done; } } done: free(cpy); return (error); } void lpc_print_supported_devices(void) { size_t i; printf("bootrom\n"); for (i = 0; i < LPC_UART_NUM; i++) printf("%s\n", lpc_uart_names[i]); printf("tpm\n"); printf("%s\n", pctestdev_getname()); } const char * lpc_bootrom(void) { return (get_config_value("lpc.bootrom")); } const char * lpc_fwcfg(void) { return (get_config_value("lpc.fwcfg")); } static void lpc_uart_intr_assert(void *arg) { struct lpc_uart_softc *sc = arg; assert(sc->irq >= 0); vm_isa_pulse_irq(lpc_bridge->pi_vmctx, sc->irq, sc->irq); } static void lpc_uart_intr_deassert(void *arg __unused) { /* * The COM devices on the LPC bus generate edge triggered interrupts, * so nothing more to do here. */ } static int lpc_uart_io_handler(struct vmctx *ctx __unused, int in, int port, int bytes, uint32_t *eax, void *arg) { int offset; struct lpc_uart_softc *sc = arg; offset = port - sc->iobase; 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: return (-1); } return (0); } static int lpc_init(struct vmctx *ctx) { struct lpc_uart_softc *sc; struct inout_port iop; const char *backend, *name; char *node_name; int unit, error; const nvlist_t *nvl; nvl = find_config_node("lpc"); if (nvl != NULL && nvlist_exists(nvl, "bootrom")) { error = bootrom_loadrom(ctx, nvl); if (error) return (error); } /* COM1 and COM2 */ for (unit = 0; unit < LPC_UART_NUM; unit++) { sc = &lpc_uart_softc[unit]; name = lpc_uart_names[unit]; if (uart_legacy_alloc(unit, &sc->iobase, &sc->irq) != 0) { EPRINTLN("Unable to allocate resources for " "LPC device %s", name); return (-1); } 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); } 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; error = register_inout(&iop); assert(error == 0); sc->enabled = 1; } /* pc-testdev */ asprintf(&node_name, "lpc.%s", pctestdev_getname()); if (get_config_bool_default(node_name, false)) { error = pctestdev_init(ctx); if (error) return (error); } free(node_name); return (0); } static void pci_lpc_write_dsdt(struct pci_devinst *pi) { struct lpc_dsdt **ldpp, *ldp; dsdt_line(""); dsdt_line("Device (ISA)"); dsdt_line("{"); dsdt_line(" Name (_ADR, 0x%04X%04X)", pi->pi_slot, pi->pi_func); dsdt_line(" OperationRegion (LPCR, PCI_Config, 0x00, 0x100)"); dsdt_line(" Field (LPCR, AnyAcc, NoLock, Preserve)"); dsdt_line(" {"); dsdt_line(" Offset (0x60),"); dsdt_line(" PIRA, 8,"); dsdt_line(" PIRB, 8,"); dsdt_line(" PIRC, 8,"); dsdt_line(" PIRD, 8,"); dsdt_line(" Offset (0x68),"); dsdt_line(" PIRE, 8,"); dsdt_line(" PIRF, 8,"); dsdt_line(" PIRG, 8,"); dsdt_line(" PIRH, 8"); dsdt_line(" }"); dsdt_line(""); dsdt_indent(1); SET_FOREACH(ldpp, lpc_dsdt_set) { ldp = *ldpp; ldp->handler(); } dsdt_line(""); dsdt_line("Device (PIC)"); dsdt_line("{"); dsdt_line(" Name (_HID, EisaId (\"PNP0000\"))"); dsdt_line(" Name (_CRS, ResourceTemplate ()"); dsdt_line(" {"); dsdt_indent(2); dsdt_fixed_ioport(IO_ICU1, 2); dsdt_fixed_ioport(IO_ICU2, 2); dsdt_fixed_irq(2); dsdt_unindent(2); dsdt_line(" })"); dsdt_line("}"); dsdt_line(""); dsdt_line("Device (TIMR)"); dsdt_line("{"); dsdt_line(" Name (_HID, EisaId (\"PNP0100\"))"); dsdt_line(" Name (_CRS, ResourceTemplate ()"); dsdt_line(" {"); dsdt_indent(2); dsdt_fixed_ioport(IO_TIMER1_PORT, 4); dsdt_fixed_irq(0); dsdt_unindent(2); dsdt_line(" })"); dsdt_line("}"); dsdt_unindent(1); dsdt_line("}"); } static void pci_lpc_sysres_dsdt(void) { struct lpc_sysres **lspp, *lsp; dsdt_line(""); dsdt_line("Device (SIO)"); dsdt_line("{"); dsdt_line(" Name (_HID, EisaId (\"PNP0C02\"))"); dsdt_line(" Name (_CRS, ResourceTemplate ()"); dsdt_line(" {"); dsdt_indent(2); SET_FOREACH(lspp, lpc_sysres_set) { lsp = *lspp; switch (lsp->type) { case LPC_SYSRES_IO: dsdt_fixed_ioport(lsp->base, lsp->length); break; case LPC_SYSRES_MEM: dsdt_fixed_mem32(lsp->base, lsp->length); break; } } dsdt_unindent(2); dsdt_line(" })"); dsdt_line("}"); } LPC_DSDT(pci_lpc_sysres_dsdt); static void pci_lpc_uart_dsdt(void) { struct lpc_uart_softc *sc; int unit; for (unit = 0; unit < LPC_UART_NUM; unit++) { sc = &lpc_uart_softc[unit]; if (!sc->enabled) continue; dsdt_line(""); dsdt_line("Device (%s)", lpc_uart_acpi_names[unit]); dsdt_line("{"); dsdt_line(" Name (_HID, EisaId (\"PNP0501\"))"); dsdt_line(" Name (_UID, %d)", unit + 1); 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(" })"); dsdt_line("}"); } } LPC_DSDT(pci_lpc_uart_dsdt); static int pci_lpc_cfgwrite(struct pci_devinst *pi, int coff, int bytes, uint32_t val) { int pirq_pin; if (bytes == 1) { pirq_pin = 0; if (coff >= 0x60 && coff <= 0x63) pirq_pin = coff - 0x60 + 1; if (coff >= 0x68 && coff <= 0x6b) pirq_pin = coff - 0x68 + 5; if (pirq_pin != 0) { pirq_write(pi->pi_vmctx, pirq_pin, val); pci_set_cfgdata8(pi, coff, pirq_read(pirq_pin)); return (0); } } return (-1); } static void pci_lpc_write(struct pci_devinst *pi __unused, int baridx __unused, uint64_t offset __unused, int size __unused, uint64_t value __unused) { } static uint64_t pci_lpc_read(struct pci_devinst *pi __unused, int baridx __unused, uint64_t offset __unused, int size __unused) { return (0); } #define LPC_DEV 0x7000 #define LPC_VENDOR 0x8086 #define LPC_REVID 0x00 #define LPC_SUBVEND_0 0x0000 #define LPC_SUBDEV_0 0x0000 static int pci_lpc_get_sel(struct pcisel *const sel) { assert(sel != NULL); memset(sel, 0, sizeof(*sel)); for (uint8_t slot = 0; slot <= PCI_SLOTMAX; ++slot) { uint8_t max_func = 0; sel->pc_dev = slot; sel->pc_func = 0; if (pci_host_read_config(sel, PCIR_HDRTYPE, 1) & PCIM_MFDEV) max_func = PCI_FUNCMAX; for (uint8_t func = 0; func <= max_func; ++func) { sel->pc_func = func; if (pci_host_read_config(sel, PCIR_CLASS, 1) == PCIC_BRIDGE && pci_host_read_config(sel, PCIR_SUBCLASS, 1) == PCIS_BRIDGE_ISA) { return (0); } } } warnx("%s: Unable to find host selector of LPC bridge.", __func__); return (-1); } static int pci_lpc_init(struct pci_devinst *pi, nvlist_t *nvl) { struct pcisel sel = { 0 }; struct pcisel *selp = NULL; uint16_t device, subdevice, subvendor, vendor; uint8_t revid; /* * Do not allow more than one LPC bridge to be configured. */ if (lpc_bridge != NULL) { EPRINTLN("Only one LPC bridge is allowed."); return (-1); } /* * Enforce that the LPC can only be configured on bus 0. This * simplifies the ACPI DSDT because it can provide a decode for * all legacy i/o ports behind bus 0. */ if (pi->pi_bus != 0) { EPRINTLN("LPC bridge can be present only on bus 0."); return (-1); } if (lpc_init(pi->pi_vmctx) != 0) return (-1); if (pci_lpc_get_sel(&sel) == 0) selp = &sel; vendor = pci_config_read_reg(selp, nvl, PCIR_VENDOR, 2, LPC_VENDOR); device = pci_config_read_reg(selp, nvl, PCIR_DEVICE, 2, LPC_DEV); revid = pci_config_read_reg(selp, nvl, PCIR_REVID, 1, LPC_REVID); subvendor = pci_config_read_reg(selp, nvl, PCIR_SUBVEND_0, 2, LPC_SUBVEND_0); subdevice = pci_config_read_reg(selp, nvl, PCIR_SUBDEV_0, 2, LPC_SUBDEV_0); /* initialize config space */ pci_set_cfgdata16(pi, PCIR_VENDOR, vendor); pci_set_cfgdata16(pi, PCIR_DEVICE, device); pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE); pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_ISA); pci_set_cfgdata8(pi, PCIR_REVID, revid); pci_set_cfgdata16(pi, PCIR_SUBVEND_0, subvendor); pci_set_cfgdata16(pi, PCIR_SUBDEV_0, subdevice); lpc_bridge = pi; return (0); } char * lpc_pirq_name(int pin) { char *name; if (lpc_bridge == NULL) return (NULL); asprintf(&name, "\\_SB.PC00.ISA.LNK%c,", 'A' + pin - 1); return (name); } void lpc_pirq_routed(void) { int pin; if (lpc_bridge == NULL) return; for (pin = 0; pin < 4; pin++) pci_set_cfgdata8(lpc_bridge, 0x60 + pin, pirq_read(pin + 1)); for (pin = 0; pin < 4; pin++) pci_set_cfgdata8(lpc_bridge, 0x68 + pin, pirq_read(pin + 5)); } #ifdef BHYVE_SNAPSHOT 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; } done: return (ret); } #endif static const struct pci_devemu pci_de_lpc = { .pe_emu = "lpc", .pe_init = pci_lpc_init, .pe_write_dsdt = pci_lpc_write_dsdt, .pe_cfgwrite = pci_lpc_cfgwrite, .pe_barwrite = pci_lpc_write, .pe_barread = pci_lpc_read, #ifdef BHYVE_SNAPSHOT .pe_snapshot = pci_lpc_snapshot, #endif }; PCI_EMUL_SET(pci_de_lpc); 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 @@ -1,129 +1,130 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2012 NetApp, Inc. * 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 "bhyverun.h" #include "config.h" #include "debug.h" #include "pci_emul.h" #include "uart_emul.h" /* * Pick a PCI vid/did of a chip with a single uart at * BAR0, that most versions of FreeBSD can understand: * Siig CyberSerial 1-port. */ #define COM_VENDOR 0x131f #define COM_DEV 0x2000 static void pci_uart_intr_assert(void *arg) { struct pci_devinst *pi = arg; pci_lintr_assert(pi); } static void pci_uart_intr_deassert(void *arg) { struct pci_devinst *pi = arg; pci_lintr_deassert(pi); } static void pci_uart_write(struct pci_devinst *pi, int baridx, uint64_t offset, int size, uint64_t value) { assert(baridx == 0); assert(size == 1); - uart_write(pi->pi_arg, offset, value); + uart_ns16550_write(pi->pi_arg, offset, value); } static uint64_t pci_uart_read(struct pci_devinst *pi, int baridx, uint64_t offset, int size) { uint8_t val; assert(baridx == 0); assert(size == 1); - val = uart_read(pi->pi_arg, offset); + val = uart_ns16550_read(pi->pi_arg, offset); return (val); } static int pci_uart_legacy_config(nvlist_t *nvl, const char *opts) { if (opts != NULL) set_config_value_node(nvl, "path", opts); return (0); } 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 */ pci_set_cfgdata16(pi, PCIR_DEVICE, COM_DEV); 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); } return (0); } static const struct pci_devemu pci_de_com = { .pe_emu = "uart", .pe_init = pci_uart_init, .pe_legacy_config = pci_uart_legacy_config, .pe_barwrite = pci_uart_write, .pe_barread = pci_uart_read }; PCI_EMUL_SET(pci_de_com); 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_emul.h b/usr.sbin/bhyve/uart_backend.h similarity index 66% copy from usr.sbin/bhyve/uart_emul.h copy to usr.sbin/bhyve/uart_backend.h index bf9f7ca47664..fa7949ad6d1c 100644 --- a/usr.sbin/bhyve/uart_emul.h +++ b/usr.sbin/bhyve/uart_backend.h @@ -1,48 +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_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_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 @@ -1,757 +1,489 @@ /*- * 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 -#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 #define COM2_BASE 0x2F8 #define COM2_IRQ 3 #define COM3_BASE 0x3E8 #define COM3_IRQ 4 #define COM4_BASE 0x2E8 #define COM4_IRQ 3 #define DEFAULT_RCLK 1843200 #define DEFAULT_BAUD 115200 #define FCR_RX_MASK 0xC0 #define MCR_OUT1 0x04 #define MCR_OUT2 0x08 #define MSR_DELTA_MASK 0x0f #ifndef REG_SCR #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; bool inuse; } uart_lres[] = { { COM1_BASE, COM1_IRQ, false}, { COM2_BASE, COM2_IRQ, false}, { COM3_BASE, COM3_IRQ, false}, { COM4_BASE, COM4_IRQ, false}, }; #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) */ uint8_t lcr; /* Line control register (R/W) */ uint8_t mcr; /* Modem control register (R/W) */ uint8_t lsr; /* Line status register (R/W) */ uint8_t msr; /* Modem status register (R/W) */ uint8_t fcr; /* FIFO control register (W) */ uint8_t scr; /* Scratch register (R/W) */ 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; uart_intr_func_t intr_assert; 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) { uint8_t msr; if (mcr & MCR_LOOPBACK) { /* * In the loopback mode certain bits from the MCR are * reflected back into MSR. */ msr = 0; if (mcr & MCR_RTS) msr |= MSR_CTS; if (mcr & MCR_DTR) msr |= MSR_DSR; if (mcr & MCR_OUT1) msr |= MSR_RI; if (mcr & MCR_OUT2) msr |= MSR_DCD; } else { /* * Always assert DCD and DSR so tty open doesn't block * even if CLOCAL is turned off. */ msr = MSR_DCD | MSR_DSR; } assert((msr & MSR_DELTA_MASK) == 0); return (msr); } /* * The IIR returns a prioritized interrupt reason: * - receive data available * - transmit holding register empty * - modem status change * * 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); else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0) return (IIR_MLSC); else return (IIR_NOPEND); } static void -uart_reset(struct uart_softc *sc) +uart_reset(struct uart_ns16550_softc *sc) { uint16_t divisor; divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16; sc->dll = divisor; 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); } /* * Toggle the COM port's intr pin depending on whether or not we have an * 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; intr_reason = uart_intr_reason(sc); if (intr_reason == IIR_NOPEND) (*sc->intr_deassert)(sc->arg); else (*sc->intr_assert)(sc->arg); } 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); /* * This routine is called in the context of the mevent thread * to take out the softc lock to protect against concurrent * access from a vCPU i/o exit */ 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; pthread_mutex_lock(&sc->mtx); /* * Take care of the special case DLAB accesses first */ if ((sc->lcr & LCR_DLAB) != 0) { if (offset == REG_DLL) { sc->dll = value; goto done; } if (offset == REG_DLH) { sc->dlh = value; goto done; } } 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: /* Set pending when IER_ETXRDY is raised (edge-triggered). */ if ((sc->ier & IER_ETXRDY) == 0 && (value & IER_ETXRDY) != 0) sc->thre_int_pending = true; /* * Apply mask so that bits 4-7 are 0 * Also enables bits 0-3 only if they're 1 */ sc->ier = value & 0x0F; break; case REG_FCR: /* * When moving from FIFO and 16450 mode and vice versa, * 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); } /* * The FCR_ENABLE bit must be '1' for the programming * of other FCR bits to be effective. */ if ((value & FCR_ENABLE) == 0) { 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); } break; case REG_LCR: sc->lcr = value; break; case REG_MCR: /* Apply mask so that bits 5-7 are 0 */ sc->mcr = value & 0x1F; msr = modem_status(sc->mcr); /* * Detect if there has been any change between the * previous and the new value of MSR. If there is * then assert the appropriate MSR delta bit. */ if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS)) sc->msr |= MSR_DCTS; if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR)) sc->msr |= MSR_DDSR; if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD)) sc->msr |= MSR_DDCD; if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0) sc->msr |= MSR_TERI; /* * Update the value of MSR while retaining the delta * bits. */ sc->msr &= MSR_DELTA_MASK; sc->msr |= msr; break; case REG_LSR: /* * Line status register is not meant to be written to * during normal operation. */ break; case REG_MSR: /* * As far as I can tell MSR is a read-only register. */ break; case REG_SCR: sc->scr = value; break; default: break; } done: uart_toggle_intr(sc); pthread_mutex_unlock(&sc->mtx); } 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; pthread_mutex_lock(&sc->mtx); /* * Take care of the special case DLAB accesses first */ if ((sc->lcr & LCR_DLAB) != 0) { if (offset == REG_DLL) { reg = sc->dll; goto done; } if (offset == REG_DLH) { reg = sc->dlh; goto done; } } switch (offset) { case REG_DATA: - reg = rxfifo_getchar(sc); + reg = uart_rxfifo_getchar(sc->backend); break; case REG_IER: reg = sc->ier; break; case REG_IIR: iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0; intr_reason = uart_intr_reason(sc); /* * Deal with side effects of reading the IIR register */ if (intr_reason == IIR_TXRDY) sc->thre_int_pending = false; iir |= intr_reason; reg = iir; break; case REG_LCR: reg = sc->lcr; break; case REG_MCR: reg = sc->mcr; break; case REG_LSR: /* Transmitter is always ready for more data */ 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; reg = sc->lsr; /* The LSR_OE bit is cleared on LSR read */ sc->lsr &= ~LSR_OE; break; case REG_MSR: /* * MSR delta bits are cleared on read */ reg = sc->msr; sc->msr &= ~MSR_DELTA_MASK; break; case REG_SCR: reg = sc->scr; break; default: reg = 0xFF; break; } done: uart_toggle_intr(sc); pthread_mutex_unlock(&sc->mtx); return (reg); } int uart_legacy_alloc(int which, int *baseaddr, int *irq) { if (which < 0 || which >= (int)UART_NLDEVS || uart_lres[which].inuse) return (-1); uart_lres[which].inuse = true; *baseaddr = uart_lres[which].baseaddr; *irq = uart_lres[which].irq; 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); uart_reset(sc); 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; SNAPSHOT_VAR_OR_LEAVE(sc->data, meta, ret, done); SNAPSHOT_VAR_OR_LEAVE(sc->ier, meta, ret, done); SNAPSHOT_VAR_OR_LEAVE(sc->lcr, meta, ret, done); SNAPSHOT_VAR_OR_LEAVE(sc->mcr, meta, ret, done); SNAPSHOT_VAR_OR_LEAVE(sc->lsr, meta, ret, done); SNAPSHOT_VAR_OR_LEAVE(sc->msr, meta, ret, done); SNAPSHOT_VAR_OR_LEAVE(sc->fcr, meta, ret, done); SNAPSHOT_VAR_OR_LEAVE(sc->scr, meta, ret, done); 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; done: return (ret); } #endif diff --git a/usr.sbin/bhyve/uart_emul.h b/usr.sbin/bhyve/uart_emul.h index bf9f7ca47664..3487af9e1cb1 100644 --- a/usr.sbin/bhyve/uart_emul.h +++ b/usr.sbin/bhyve/uart_emul.h @@ -1,48 +1,52 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * 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_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