diff options
-rwxr-xr-x | share/examples/bhyve/vmrun.sh | 9 | ||||
-rw-r--r-- | usr.sbin/bhyve/Makefile | 6 | ||||
-rw-r--r-- | usr.sbin/bhyve/bhyverun.c | 24 | ||||
-rw-r--r-- | usr.sbin/bhyve/inout.c | 15 | ||||
-rw-r--r-- | usr.sbin/bhyve/inout.h | 8 | ||||
-rw-r--r-- | usr.sbin/bhyve/legacy_irq.c | 80 | ||||
-rw-r--r-- | usr.sbin/bhyve/legacy_irq.h | 41 | ||||
-rw-r--r-- | usr.sbin/bhyve/pci_emul.c | 66 | ||||
-rw-r--r-- | usr.sbin/bhyve/pci_emul.h | 2 | ||||
-rw-r--r-- | usr.sbin/bhyve/pci_lpc.c | 215 | ||||
-rw-r--r-- | usr.sbin/bhyve/pci_lpc.h | 34 | ||||
-rw-r--r-- | usr.sbin/bhyve/pci_uart.c | 564 | ||||
-rw-r--r-- | usr.sbin/bhyve/uart_emul.c | 599 | ||||
-rw-r--r-- | usr.sbin/bhyve/uart_emul.h | 45 |
14 files changed, 1108 insertions, 600 deletions
diff --git a/share/examples/bhyve/vmrun.sh b/share/examples/bhyve/vmrun.sh index 2e1bb38..6134915 100755 --- a/share/examples/bhyve/vmrun.sh +++ b/share/examples/bhyve/vmrun.sh @@ -157,7 +157,7 @@ while [ 1 ]; do exit 1 fi BOOTDISK=${isofile} - installer_opt="-s 3:0,virtio-blk,${BOOTDISK}" + installer_opt="-s 31:0,virtio-blk,${BOOTDISK}" else BOOTDISK=${virtio_diskdev} installer_opt="" @@ -171,10 +171,11 @@ while [ 1 ]; do ${FBSDRUN} -c ${cpus} -m ${memsize} ${apic_opt} -AI -H -P \ -g ${gdbport} \ -s 0:0,hostbridge \ - -s 1:0,virtio-net,${tapdev} \ - -s 2:0,virtio-blk,${virtio_diskdev} \ + -s 1:0,lpc \ + -s 2:0,virtio-net,${tapdev} \ + -s 3:0,virtio-blk,${virtio_diskdev} \ + -l com1,stdio \ ${installer_opt} \ - -S 31,uart,stdio \ ${vmname} if [ $? -ne 0 ]; then break diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile index 0644ed7..570182e 100644 --- a/usr.sbin/bhyve/Makefile +++ b/usr.sbin/bhyve/Makefile @@ -7,10 +7,10 @@ PROG= bhyve DEBUG_FLAGS= -g -O0 SRCS= acpi.c atpic.c bhyverun.c block_if.c consport.c dbgport.c elcr.c -SRCS+= inout.c ioapic.c mem.c mevent.c mptbl.c pci_ahci.c -SRCS+= pci_emul.c pci_hostbridge.c pci_passthru.c pci_virtio_block.c +SRCS+= inout.c ioapic.c legacy_irq.c mem.c mevent.c mptbl.c pci_ahci.c +SRCS+= pci_emul.c pci_hostbridge.c pci_lpc.c pci_passthru.c pci_virtio_block.c SRCS+= pci_virtio_net.c pci_uart.c pit_8254.c pmtmr.c post.c rtc.c -SRCS+= virtio.c xmsr.c spinup_ap.c +SRCS+= uart_emul.c virtio.c xmsr.c spinup_ap.c .PATH: ${.CURDIR}/../../sys/amd64/vmm SRCS+= vmm_instruction_emul.c diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c index bebd186..6e8ea59 100644 --- a/usr.sbin/bhyve/bhyverun.c +++ b/usr.sbin/bhyve/bhyverun.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <err.h> #include <libgen.h> #include <unistd.h> @@ -53,10 +54,12 @@ __FBSDID("$FreeBSD$"); #include "acpi.h" #include "inout.h" #include "dbgport.h" +#include "legacy_irq.h" #include "mem.h" #include "mevent.h" #include "mptbl.h" #include "pci_emul.h" +#include "pci_lpc.h" #include "xmsr.h" #include "ioapic.h" #include "spinup_ap.h" @@ -121,9 +124,8 @@ usage(int code) { fprintf(stderr, - "Usage: %s [-aehAHIPW][-g <gdb port>][-s <pci>][-S <pci>]" - "[-c vcpus][-p pincpu][-m mem]" - " <vmname>\n" + "Usage: %s [-aehAHIPW] [-g <gdb port>] [-s <pci>] [-S <pci>]\n" + " %*s [-c vcpus] [-p pincpu] [-m mem] [-l <lpc>] <vm>\n" " -a: local apic is in XAPIC mode (default is X2APIC)\n" " -A: create an ACPI table\n" " -g: gdb port\n" @@ -132,13 +134,14 @@ usage(int code) " -H: vmexit from the guest on hlt\n" " -I: present an ioapic to the guest\n" " -P: vmexit from the guest on pause\n" - " -W: force virtio to use single-vector MSI\n" - " -e: exit on unhandled i/o access\n" + " -W: force virtio to use single-vector MSI\n" + " -e: exit on unhandled I/O access\n" " -h: help\n" " -s: <slot,driver,configinfo> PCI slot config\n" " -S: <slot,driver,configinfo> legacy PCI slot config\n" + " -l: LPC device configuration\n" " -m: memory size in MB\n", - progname); + progname, (int)strlen(progname), ""); exit(code); } @@ -553,7 +556,7 @@ main(int argc, char *argv[]) ioapic = 0; memsize = 256 * MB; - while ((c = getopt(argc, argv, "abehAHIPWp:g:c:s:S:m:")) != -1) { + while ((c = getopt(argc, argv, "abehAHIPWp:g:c:s:S:m:l:")) != -1) { switch (c) { case 'a': disable_x2apic = 1; @@ -573,6 +576,12 @@ main(int argc, char *argv[]) case 'g': gdb_port = atoi(optarg); break; + case 'l': + if (lpc_device_parse(optarg) != 0) { + errx(EX_USAGE, "invalid lpc device " + "configuration '%s'", optarg); + } + break; case 's': if (pci_parse_slot(optarg, 0) != 0) exit(1); @@ -640,6 +649,7 @@ main(int argc, char *argv[]) init_mem(); init_inout(); + legacy_irq_init(); rtc_init(ctx); diff --git a/usr.sbin/bhyve/inout.c b/usr.sbin/bhyve/inout.c index 29d9f17..a200265 100644 --- a/usr.sbin/bhyve/inout.c +++ b/usr.sbin/bhyve/inout.c @@ -84,7 +84,7 @@ register_default_iohandler(int start, int size) iop.name = "default"; iop.port = start; iop.size = size; - iop.flags = IOPORT_F_INOUT; + iop.flags = IOPORT_F_INOUT | IOPORT_F_DEFAULT; iop.handler = default_inout; register_inout(&iop); @@ -159,7 +159,18 @@ register_inout(struct inout_port *iop) int i; VERIFY_IOPORT(iop->port, iop->size); - + + /* + * Verify that the new registration is not overwriting an already + * allocated i/o range. + */ + if ((iop->flags & IOPORT_F_DEFAULT) == 0) { + for (i = iop->port; i < iop->port + iop->size; i++) { + if ((inout_handlers[i].flags & IOPORT_F_DEFAULT) == 0) + return (-1); + } + } + for (i = iop->port; i < iop->port + iop->size; i++) { inout_handlers[i].name = iop->name; inout_handlers[i].flags = iop->flags; diff --git a/usr.sbin/bhyve/inout.h b/usr.sbin/bhyve/inout.h index 4de58d1..2f93ea0 100644 --- a/usr.sbin/bhyve/inout.h +++ b/usr.sbin/bhyve/inout.h @@ -46,7 +46,13 @@ struct inout_port { }; #define IOPORT_F_IN 0x1 #define IOPORT_F_OUT 0x2 -#define IOPORT_F_INOUT 0x3 +#define IOPORT_F_INOUT (IOPORT_F_IN | IOPORT_F_OUT) + +/* + * The following flags are used internally and must not be used by + * device models. + */ +#define IOPORT_F_DEFAULT 0x80000000 /* claimed by default handler */ #define INOUT_PORT(name, port, flags, handler) \ static struct inout_port __CONCAT(__inout_port, __LINE__) = { \ diff --git a/usr.sbin/bhyve/legacy_irq.c b/usr.sbin/bhyve/legacy_irq.c new file mode 100644 index 0000000..9e86efd --- /dev/null +++ b/usr.sbin/bhyve/legacy_irq.c @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 2013 Neel Natu <neel@freebsd.org> + * 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. + * + * $FreeBSD$ + */ + +#include <stdbool.h> +#include <assert.h> + +/* + * Used to keep track of legacy interrupt owners/requestors + */ +#define NLIRQ 16 + +static struct lirqinfo { + bool li_generic; + bool li_allocated; +} lirq[NLIRQ]; + +void +legacy_irq_init(void) +{ + + /* + * Allow ISA IRQs 5,10,11,12, and 15 to be available for generic use. + */ + lirq[5].li_generic = true; + lirq[10].li_generic = true; + lirq[11].li_generic = true; + lirq[12].li_generic = true; + lirq[15].li_generic = true; +} + +int +legacy_irq_alloc(int irq) +{ + int i; + + assert(irq < NLIRQ); + + if (irq < 0) { + for (i = 0; i < NLIRQ; i++) { + if (lirq[i].li_generic && !lirq[i].li_allocated) { + irq = i; + break; + } + } + } else { + if (lirq[irq].li_allocated) + irq = -1; + } + + if (irq >= 0) { + lirq[irq].li_allocated = true; + return (irq); + } else + return (-1); +} diff --git a/usr.sbin/bhyve/legacy_irq.h b/usr.sbin/bhyve/legacy_irq.h new file mode 100644 index 0000000..3531e11 --- /dev/null +++ b/usr.sbin/bhyve/legacy_irq.h @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2013 Neel Natu <neel@freebsd.org> + * 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. + * + * $FreeBSD$ + */ + +#ifndef _LEGACY_IRQ_H_ +#define _LEGACY_IRQ_H_ + +/* + * Allocate a legacy irq. The argument 'irq' can be set to -1 to allocate any + * available irq. + * + * Returns -1 on failure or the allocated irq number on success. + */ +int legacy_irq_alloc(int irq); +void legacy_irq_init(void); + +#endif diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c index fe21cee..bbaac23 100644 --- a/usr.sbin/bhyve/pci_emul.c +++ b/usr.sbin/bhyve/pci_emul.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include "bhyverun.h" #include "inout.h" +#include "legacy_irq.h" #include "mem.h" #include "pci_emul.h" #include "ioapic.h" @@ -76,17 +77,6 @@ static struct slotinfo { int si_legacy; } pci_slotinfo[MAXSLOTS][MAXFUNCS]; -/* - * Used to keep track of legacy interrupt owners/requestors - */ -#define NLIRQ 16 - -static struct lirqinfo { - int li_generic; - int li_acount; - struct pci_devinst *li_owner; /* XXX should be a list */ -} lirq[NLIRQ]; - SET_DECLARE(pci_devemu_set, struct pci_devemu); static uint64_t pci_emul_iobase; @@ -682,6 +672,7 @@ pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func, pdi->pi_bus = 0; pdi->pi_slot = slot; pdi->pi_func = func; + pdi->pi_lintr_pin = -1; pdi->pi_d = pde; snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); @@ -1023,16 +1014,6 @@ init_pci(struct vmctx *ctx) pci_emul_membase32 = vm_get_lowmem_limit(ctx); pci_emul_membase64 = PCI_EMUL_MEMBASE64; - /* - * Allow ISA IRQs 5,10,11,12, and 15 to be available for - * generic use - */ - lirq[5].li_generic = 1; - lirq[10].li_generic = 1; - lirq[11].li_generic = 1; - lirq[12].li_generic = 1; - lirq[15].li_generic = 1; - for (slot = 0; slot < MAXSLOTS; slot++) { for (func = 0; func < MAXFUNCS; func++) { si = &pci_slotinfo[slot][func]; @@ -1135,40 +1116,17 @@ pci_is_legacy(struct pci_devinst *pi) return (pci_slotinfo[pi->pi_slot][pi->pi_func].si_legacy); } -static int -pci_lintr_alloc(struct pci_devinst *pi, int vec) -{ - int i; - - assert(vec < NLIRQ); - - if (vec == -1) { - for (i = 0; i < NLIRQ; i++) { - if (lirq[i].li_generic && - lirq[i].li_owner == NULL) { - vec = i; - break; - } - } - } else { - if (lirq[vec].li_owner != NULL) { - vec = -1; - } - } - assert(vec != -1); - - lirq[vec].li_owner = pi; - pi->pi_lintr_pin = vec; - - return (vec); -} - int -pci_lintr_request(struct pci_devinst *pi, int vec) +pci_lintr_request(struct pci_devinst *pi, int req) { + int irq; + + irq = legacy_irq_alloc(req); + if (irq < 0) + return (-1); - vec = pci_lintr_alloc(pi, vec); - pci_set_cfgdata8(pi, PCIR_INTLINE, vec); + pi->pi_lintr_pin = irq; + pci_set_cfgdata8(pi, PCIR_INTLINE, irq); pci_set_cfgdata8(pi, PCIR_INTPIN, 1); return (0); } @@ -1177,7 +1135,7 @@ void pci_lintr_assert(struct pci_devinst *pi) { - assert(pi->pi_lintr_pin); + assert(pi->pi_lintr_pin >= 0); ioapic_assert_pin(pi->pi_vmctx, pi->pi_lintr_pin); } @@ -1185,7 +1143,7 @@ void pci_lintr_deassert(struct pci_devinst *pi) { - assert(pi->pi_lintr_pin); + assert(pi->pi_lintr_pin >= 0); ioapic_deassert_pin(pi->pi_vmctx, pi->pi_lintr_pin); } diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h index 4e04a1e..ebcf834 100644 --- a/usr.sbin/bhyve/pci_emul.h +++ b/usr.sbin/bhyve/pci_emul.h @@ -103,7 +103,7 @@ struct pci_devinst { struct pci_devemu *pi_d; struct vmctx *pi_vmctx; uint8_t pi_bus, pi_slot, pi_func; - uint8_t pi_lintr_pin; + int8_t pi_lintr_pin; char pi_name[PI_NAMESZ]; int pi_bar_getsize; diff --git a/usr.sbin/bhyve/pci_lpc.c b/usr.sbin/bhyve/pci_lpc.c new file mode 100644 index 0000000..9fd5f5c --- /dev/null +++ b/usr.sbin/bhyve/pci_lpc.c @@ -0,0 +1,215 @@ +/*- + * Copyright (c) 2013 Neel Natu <neel@freebsd.org> + * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "inout.h" +#include "ioapic.h" +#include "pci_emul.h" +#include "uart_emul.h" + +static struct pci_devinst *lpc_bridge; + +#define LPC_UART_NUM 2 +static struct lpc_uart_softc { + struct uart_softc *uart_softc; + const char *opts; + int iobase; + int irq; +} lpc_uart_softc[LPC_UART_NUM]; + +static const char *lpc_uart_names[LPC_UART_NUM] = { "COM1", "COM2" }; + +/* + * LPC device configuration is in the following form: + * <lpc_device_name>[,<options>] + * For e.g. "com1,stdio" + */ +int +lpc_device_parse(const char *opts) +{ + int unit, error; + char *str, *cpy, *lpcdev; + + error = -1; + str = cpy = strdup(opts); + lpcdev = strsep(&str, ","); + if (lpcdev != NULL) { + for (unit = 0; unit < LPC_UART_NUM; unit++) { + if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) { + lpc_uart_softc[unit].opts = str; + error = 0; + goto done; + } + } + } + +done: + if (error) + free(cpy); + + return (error); +} + +static void +lpc_uart_intr_assert(void *arg) +{ + struct lpc_uart_softc *sc = arg; + + assert(sc->irq >= 0); + + ioapic_assert_pin(lpc_bridge->pi_vmctx, sc->irq); +} + +static void +lpc_uart_intr_deassert(void *arg) +{ + struct lpc_uart_softc *sc = arg; + + assert(sc->irq >= 0); + + ioapic_deassert_pin(lpc_bridge->pi_vmctx, sc->irq); +} + +static int +lpc_uart_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, + uint32_t *eax, void *arg) +{ + int offset; + struct lpc_uart_softc *sc = arg; + + if (bytes != 1) + return (-1); + + offset = port - sc->iobase; + + if (in) + *eax = uart_read(sc->uart_softc, offset); + else + uart_write(sc->uart_softc, offset, *eax); + + return (0); +} + +static int +lpc_init(void) +{ + struct lpc_uart_softc *sc; + struct inout_port iop; + const char *name; + int unit, 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) { + fprintf(stderr, "Unable to allocate resources for " + "LPC device %s\n", name); + return (-1); + } + + sc->uart_softc = uart_init(lpc_uart_intr_assert, + lpc_uart_intr_deassert, sc); + + if (uart_set_backend(sc->uart_softc, sc->opts) != 0) { + fprintf(stderr, "Unable to initialize backend '%s' " + "for LPC device %s\n", sc->opts, name); + return (-1); + } + + bzero(&iop, sizeof(struct inout_port)); + iop.name = name; + iop.port = sc->iobase; + iop.size = UART_IO_BAR_SIZE; + iop.flags = IOPORT_F_INOUT; + iop.handler = lpc_uart_io_handler; + iop.arg = sc; + + error = register_inout(&iop); + assert(error == 0); + } + + return (0); +} + +static void +pci_lpc_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int baridx, uint64_t offset, int size, uint64_t value) +{ +} + +uint64_t +pci_lpc_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int baridx, uint64_t offset, int size) +{ + return (0); +} + +#define LPC_DEV 0x7000 +#define LPC_VENDOR 0x8086 + +static int +pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + /* + * Do not allow more than one LPC bridge to be configured. + */ + if (lpc_bridge != NULL) + return (-1); + + if (lpc_init() != 0) + return (-1); + + /* initialize config space */ + pci_set_cfgdata16(pi, PCIR_DEVICE, LPC_DEV); + pci_set_cfgdata16(pi, PCIR_VENDOR, LPC_VENDOR); + pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE); + pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_ISA); + + lpc_bridge = pi; + + return (0); +} + +struct pci_devemu pci_de_lpc = { + .pe_emu = "lpc", + .pe_init = pci_lpc_init, + .pe_barwrite = pci_lpc_write, + .pe_barread = pci_lpc_read +}; +PCI_EMUL_SET(pci_de_lpc); diff --git a/usr.sbin/bhyve/pci_lpc.h b/usr.sbin/bhyve/pci_lpc.h new file mode 100644 index 0000000..b228eac --- /dev/null +++ b/usr.sbin/bhyve/pci_lpc.h @@ -0,0 +1,34 @@ +/*- + * Copyright (c) 2013 Neel Natu <neel@freebsd.org> + * 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. + * + * $FreeBSD$ + */ + +#ifndef _LPC_H_ +#define _LPC_H_ + +int lpc_device_parse(const char *opt); + +#endif diff --git a/usr.sbin/bhyve/pci_uart.c b/usr.sbin/bhyve/pci_uart.c index dd30551..f8e5292 100644 --- a/usr.sbin/bhyve/pci_uart.c +++ b/usr.sbin/bhyve/pci_uart.c @@ -30,42 +30,12 @@ __FBSDID("$FreeBSD$"); #include <sys/types.h> -#include <sys/select.h> -#include <dev/ic/ns16550.h> #include <stdio.h> -#include <stdlib.h> -#include <assert.h> -#include <termios.h> -#include <unistd.h> -#include <stdbool.h> -#include <string.h> -#include <pthread.h> #include "bhyverun.h" #include "pci_emul.h" -#include "mevent.h" - -#define COM1_BASE 0x3F8 -#define COM1_IRQ 4 -#define COM2_BASE 0x2F8 -#define COM2_IRQ 3 - -#define DEFAULT_RCLK 1843200 -#define DEFAULT_BAUD 9600 - -#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 +#include "uart_emul.h" /* * Pick a PCI vid/did of a chip with a single uart at @@ -75,544 +45,82 @@ __FBSDID("$FreeBSD$"); #define COM_VENDOR 0x131f #define COM_DEV 0x2000 -static int pci_uart_stdio; /* stdio in use for i/o */ - -static int pci_uart_nldevs; /* number of legacy devices - 2 max */ - -static struct { - uint64_t baddr; - int vector; -} pci_uart_lres[] = { - { COM1_BASE, COM1_IRQ}, - { COM2_BASE, COM2_IRQ}, - { 0, 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 pci_uart_softc { - struct pci_devinst *pi; - 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; - - int opened; - int stdio; - bool thre_int_pending; /* THRE interrupt pending */ -}; - -static void pci_uart_drain(int fd, enum ev_type ev, void *arg); - -static struct termios tio_orig, tio_new; /* I/O Terminals */ - static void -ttyclose(void) +pci_uart_intr_assert(void *arg) { - tcsetattr(STDIN_FILENO, TCSANOW, &tio_orig); -} + struct pci_devinst *pi = arg; -static void -ttyopen(void) -{ - tcgetattr(STDIN_FILENO, &tio_orig); - - cfmakeraw(&tio_new); - tcsetattr(STDIN_FILENO, TCSANOW, &tio_new); - - atexit(ttyclose); -} - -static bool -tty_char_available(void) -{ - fd_set rfds; - struct timeval tv; - - FD_ZERO(&rfds); - FD_SET(STDIN_FILENO, &rfds); - tv.tv_sec = 0; - tv.tv_usec = 0; - if (select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv) > 0 ) { - return (true); - } else { - return (false); - } -} - -static int -ttyread(void) -{ - char rb; - - if (tty_char_available()) { - read(STDIN_FILENO, &rb, 1); - return (rb & 0xff); - } else { - return (-1); - } + pci_lintr_assert(pi); } static void -ttywrite(unsigned char wb) +pci_uart_intr_deassert(void *arg) { - (void) write(STDIN_FILENO, &wb, 1); -} + struct pci_devinst *pi = arg; -static void -fifo_reset(struct fifo *fifo, int size) -{ - bzero(fifo, sizeof(struct fifo)); - fifo->size = size; -} - -static int -fifo_putchar(struct fifo *fifo, uint8_t ch) -{ - - if (fifo->num < fifo->size) { - fifo->buf[fifo->windex] = ch; - fifo->windex = (fifo->windex + 1) % fifo->size; - fifo->num++; - return (0); - } else - return (-1); -} - -static int -fifo_getchar(struct fifo *fifo) -{ - int c; - - if (fifo->num > 0) { - c = fifo->buf[fifo->rindex]; - fifo->rindex = (fifo->rindex + 1) % fifo->size; - fifo->num--; - return (c); - } else - return (-1); -} - -static int -fifo_numchars(struct fifo *fifo) -{ - - return (fifo->num); -} - -static int -fifo_available(struct fifo *fifo) -{ - - return (fifo->num < fifo->size); -} - -static void -pci_uart_opentty(struct pci_uart_softc *sc) -{ - struct mevent *mev; - - assert(sc->opened == 0); - assert(sc->stdio); - - ttyopen(); - mev = mevent_add(STDIN_FILENO, EVF_READ, pci_uart_drain, sc); - assert(mev); -} - -static void -pci_uart_legacy_res(uint64_t *bar, int *ivec) -{ - if (pci_uart_lres[pci_uart_nldevs].baddr != 0) { - *bar = pci_uart_lres[pci_uart_nldevs].baddr; - *ivec = pci_uart_lres[pci_uart_nldevs].vector; - pci_uart_nldevs++; - } else { - /* TODO: print warning ? */ - *bar = 0; - *ivec= -1; - } -} - -/* - * 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 -pci_uart_intr_reason(struct pci_uart_softc *sc) -{ - - if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0) - return (IIR_RLS); - else if (fifo_numchars(&sc->rxfifo) > 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 -pci_uart_reset(struct pci_uart_softc *sc) -{ - uint16_t divisor; - - divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16; - sc->dll = divisor; - sc->dlh = divisor >> 16; - - fifo_reset(&sc->rxfifo, 1); /* no fifo until enabled by software */ -} - -/* - * Toggle the COM port's intr pin depending on whether or not we have an - * interrupt condition to report to the processor. - */ -static void -pci_uart_toggle_intr(struct pci_uart_softc *sc) -{ - uint8_t intr_reason; - - intr_reason = pci_uart_intr_reason(sc); - - if (intr_reason == IIR_NOPEND) - pci_lintr_deassert(sc->pi); - else - pci_lintr_assert(sc->pi); -} - -static void -pci_uart_drain(int fd, enum ev_type ev, void *arg) -{ - struct pci_uart_softc *sc; - int ch; - - sc = arg; - - assert(fd == STDIN_FILENO); - 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(); - } else { - while (fifo_available(&sc->rxfifo) && - ((ch = ttyread()) != -1)) { - fifo_putchar(&sc->rxfifo, ch); - } - pci_uart_toggle_intr(sc); - } - - pthread_mutex_unlock(&sc->mtx); + pci_lintr_deassert(pi); } static void pci_uart_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size, uint64_t value) { - struct pci_uart_softc *sc; - int fifosz; - uint8_t msr; - - sc = pi->pi_arg; assert(baridx == 0); assert(size == 1); - /* Open terminal */ - if (!sc->opened && sc->stdio) { - pci_uart_opentty(sc); - sc->opened = 1; - } - - 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 (fifo_putchar(&sc->rxfifo, value) != 0) - sc->lsr |= LSR_OE; - } else if (sc->stdio) { - ttywrite(value); - } /* else drop on floor */ - sc->thre_int_pending = true; - break; - case REG_IER: - /* - * 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; - fifo_reset(&sc->rxfifo, 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) - fifo_reset(&sc->rxfifo, FIFOSZ); - - 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 = 0; - if (sc->mcr & MCR_LOOPBACK) { - /* - * In the loopback mode certain bits from the - * MCR are reflected back into MSR - */ - if (sc->mcr & MCR_RTS) - msr |= MSR_CTS; - if (sc->mcr & MCR_DTR) - msr |= MSR_DSR; - if (sc->mcr & MCR_OUT1) - msr |= MSR_RI; - if (sc->mcr & MCR_OUT2) - msr |= MSR_DCD; - } - - /* - * 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: - pci_uart_toggle_intr(sc); - pthread_mutex_unlock(&sc->mtx); + uart_write(pi->pi_arg, offset, value); } uint64_t pci_uart_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size) { - struct pci_uart_softc *sc; - uint8_t iir, intr_reason; - uint64_t reg; - - sc = pi->pi_arg; + uint8_t val; assert(baridx == 0); assert(size == 1); - /* Open terminal */ - if (!sc->opened && sc->stdio) { - pci_uart_opentty(sc); - sc->opened = 1; - } - - 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 = fifo_getchar(&sc->rxfifo); - break; - case REG_IER: - reg = sc->ier; - break; - case REG_IIR: - iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0; - - intr_reason = pci_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 (fifo_numchars(&sc->rxfifo) > 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: - pci_uart_toggle_intr(sc); - pthread_mutex_unlock(&sc->mtx); - - return (reg); + val = uart_read(pi->pi_arg, offset); + return (val); } +static int pci_uart_nldevs; /* number of legacy uart ports allocated */ + static int pci_uart_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) { - struct pci_uart_softc *sc; - uint64_t bar; - int ivec; - - sc = malloc(sizeof(struct pci_uart_softc)); - memset(sc, 0, sizeof(struct pci_uart_softc)); - - pi->pi_arg = sc; - sc->pi = pi; - - pthread_mutex_init(&sc->mtx, NULL); + struct uart_softc *sc; + int ioaddr, ivec; - /* 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); if (pci_is_legacy(pi)) { - pci_uart_legacy_res(&bar, &ivec); - pci_emul_alloc_pbar(pi, 0, bar, PCIBAR_IO, 8); + if (uart_legacy_alloc(pci_uart_nldevs, &ioaddr, &ivec) != 0) { + fprintf(stderr, "Unable to allocate resources for " + "legacy COM%d port at pci device %d:%d\n", + pci_uart_nldevs + 1, pi->pi_slot, pi->pi_func); + return (-1); + } + pci_uart_nldevs++; + pci_emul_alloc_pbar(pi, 0, ioaddr, PCIBAR_IO, UART_IO_BAR_SIZE); } else { ivec = -1; - pci_emul_alloc_bar(pi, 0, PCIBAR_IO, 8); + pci_emul_alloc_bar(pi, 0, PCIBAR_IO, UART_IO_BAR_SIZE); } pci_lintr_request(pi, ivec); - if (opts != NULL && !strcmp("stdio", opts) && !pci_uart_stdio) { - pci_uart_stdio = 1; - sc->stdio = 1; - } + /* 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); + pi->pi_arg = sc; - pci_uart_reset(sc); + if (uart_set_backend(sc, opts) != 0) { + fprintf(stderr, "Unable to initialize backend '%s' for " + "pci uart at %d:%d\n", opts, pi->pi_slot, pi->pi_func); + return (-1); + } return (0); } diff --git a/usr.sbin/bhyve/uart_emul.c b/usr.sbin/bhyve/uart_emul.c new file mode 100644 index 0000000..981607b --- /dev/null +++ b/usr.sbin/bhyve/uart_emul.c @@ -0,0 +1,599 @@ +/*- + * Copyright (c) 2012 NetApp, Inc. + * Copyright (c) 2013 Neel Natu <neel@freebsd.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <dev/ic/ns16550.h> + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <termios.h> +#include <unistd.h> +#include <stdbool.h> +#include <string.h> +#include <pthread.h> + +#include "mevent.h" +#include "uart_emul.h" + +#define COM1_BASE 0x3F8 +#define COM1_IRQ 4 +#define COM2_BASE 0x2F8 +#define COM2_IRQ 3 + +#define DEFAULT_RCLK 1843200 +#define DEFAULT_BAUD 9600 + +#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 { + int baseaddr; + int irq; + bool inuse; +} uart_lres[] = { + { COM1_BASE, COM1_IRQ, false}, + { COM2_BASE, COM2_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 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; + + bool opened; + bool stdio; + 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 struct termios tio_orig, tio_new; /* I/O Terminals */ + +static void +ttyclose(void) +{ + + tcsetattr(STDIN_FILENO, TCSANOW, &tio_orig); +} + +static void +ttyopen(void) +{ + + tcgetattr(STDIN_FILENO, &tio_orig); + + cfmakeraw(&tio_new); + tcsetattr(STDIN_FILENO, TCSANOW, &tio_new); + + atexit(ttyclose); +} + +static bool +tty_char_available(void) +{ + fd_set rfds; + struct timeval tv; + + FD_ZERO(&rfds); + FD_SET(STDIN_FILENO, &rfds); + tv.tv_sec = 0; + tv.tv_usec = 0; + if (select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv) > 0 ) { + return (true); + } else { + return (false); + } +} + +static int +ttyread(void) +{ + char rb; + + if (tty_char_available()) { + read(STDIN_FILENO, &rb, 1); + return (rb & 0xff); + } else { + return (-1); + } +} + +static void +ttywrite(unsigned char wb) +{ + + (void)write(STDIN_FILENO, &wb, 1); +} + +static void +fifo_reset(struct fifo *fifo, int size) +{ + + bzero(fifo, sizeof(struct fifo)); + fifo->size = size; +} + +static int +fifo_putchar(struct fifo *fifo, uint8_t ch) +{ + + if (fifo->num < fifo->size) { + fifo->buf[fifo->windex] = ch; + fifo->windex = (fifo->windex + 1) % fifo->size; + fifo->num++; + return (0); + } else + return (-1); +} + +static int +fifo_getchar(struct fifo *fifo) +{ + int c; + + if (fifo->num > 0) { + c = fifo->buf[fifo->rindex]; + fifo->rindex = (fifo->rindex + 1) % fifo->size; + fifo->num--; + return (c); + } else + return (-1); +} + +static int +fifo_numchars(struct fifo *fifo) +{ + + return (fifo->num); +} + +static int +fifo_available(struct fifo *fifo) +{ + + return (fifo->num < fifo->size); +} + +static void +uart_opentty(struct uart_softc *sc) +{ + struct mevent *mev; + + assert(!sc->opened && sc->stdio); + + ttyopen(); + mev = mevent_add(STDIN_FILENO, EVF_READ, uart_drain, sc); + assert(mev); +} + +/* + * 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) +{ + + if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0) + return (IIR_RLS); + else if (fifo_numchars(&sc->rxfifo) > 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) +{ + uint16_t divisor; + + divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16; + sc->dll = divisor; + sc->dlh = divisor >> 16; + + fifo_reset(&sc->rxfifo, 1); /* no fifo until enabled by software */ +} + +/* + * 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) +{ + 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) +{ + struct uart_softc *sc; + int ch; + + sc = arg; + + assert(fd == STDIN_FILENO); + 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(); + } else { + while (fifo_available(&sc->rxfifo) && + ((ch = ttyread()) != -1)) { + fifo_putchar(&sc->rxfifo, ch); + } + uart_toggle_intr(sc); + } + + pthread_mutex_unlock(&sc->mtx); +} + +void +uart_write(struct uart_softc *sc, int offset, uint8_t value) +{ + int fifosz; + uint8_t msr; + + /* Open terminal */ + if (!sc->opened && sc->stdio) { + uart_opentty(sc); + sc->opened = true; + } + + 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 (fifo_putchar(&sc->rxfifo, value) != 0) + sc->lsr |= LSR_OE; + } else if (sc->stdio) { + ttywrite(value); + } /* else drop on floor */ + sc->thre_int_pending = true; + break; + case REG_IER: + /* + * 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; + fifo_reset(&sc->rxfifo, 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) + fifo_reset(&sc->rxfifo, FIFOSZ); + + 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 = 0; + if (sc->mcr & MCR_LOOPBACK) { + /* + * In the loopback mode certain bits from the + * MCR are reflected back into MSR + */ + if (sc->mcr & MCR_RTS) + msr |= MSR_CTS; + if (sc->mcr & MCR_DTR) + msr |= MSR_DSR; + if (sc->mcr & MCR_OUT1) + msr |= MSR_RI; + if (sc->mcr & MCR_OUT2) + msr |= MSR_DCD; + } + + /* + * 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) +{ + uint8_t iir, intr_reason, reg; + + /* Open terminal */ + if (!sc->opened && sc->stdio) { + uart_opentty(sc); + sc->opened = true; + } + + 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 = fifo_getchar(&sc->rxfifo); + 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 (fifo_numchars(&sc->rxfifo) > 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 >= 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, + void *arg) +{ + struct uart_softc *sc; + + sc = malloc(sizeof(struct uart_softc)); + bzero(sc, sizeof(struct uart_softc)); + + sc->arg = arg; + sc->intr_assert = intr_assert; + sc->intr_deassert = intr_deassert; + + pthread_mutex_init(&sc->mtx, NULL); + + uart_reset(sc); + + return (sc); +} + +int +uart_set_backend(struct uart_softc *sc, const char *opts) +{ + /* + * XXX one stdio backend supported at this time. + */ + if (opts == NULL) + return (0); + + if (strcmp("stdio", opts) == 0 && !uart_stdio) { + sc->stdio = true; + uart_stdio = true; + return (0); + } else + return (-1); +} diff --git a/usr.sbin/bhyve/uart_emul.h b/usr.sbin/bhyve/uart_emul.h new file mode 100644 index 0000000..993b92e --- /dev/null +++ b/usr.sbin/bhyve/uart_emul.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2013 Neel Natu <neel@freebsd.org> + * 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. + * + * $FreeBSD$ + */ + +#ifndef _UART_EMUL_H_ +#define _UART_EMUL_H_ + + +#define UART_IO_BAR_SIZE 8 + +struct uart_softc; + +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 *opt); +#endif |