summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bhyve
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/bhyve')
-rw-r--r--usr.sbin/bhyve/Makefile6
-rw-r--r--usr.sbin/bhyve/bhyverun.c24
-rw-r--r--usr.sbin/bhyve/inout.c15
-rw-r--r--usr.sbin/bhyve/inout.h8
-rw-r--r--usr.sbin/bhyve/legacy_irq.c80
-rw-r--r--usr.sbin/bhyve/legacy_irq.h41
-rw-r--r--usr.sbin/bhyve/pci_emul.c66
-rw-r--r--usr.sbin/bhyve/pci_emul.h2
-rw-r--r--usr.sbin/bhyve/pci_lpc.c215
-rw-r--r--usr.sbin/bhyve/pci_lpc.h34
-rw-r--r--usr.sbin/bhyve/pci_uart.c564
-rw-r--r--usr.sbin/bhyve/uart_emul.c599
-rw-r--r--usr.sbin/bhyve/uart_emul.h45
13 files changed, 1103 insertions, 596 deletions
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
OpenPOWER on IntegriCloud