summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Kempf <stefan@cvs.openbsd.org>2016-09-02 17:10:09 +0000
committerStefan Kempf <stefan@cvs.openbsd.org>2016-09-02 17:10:09 +0000
commit07d965141c77ffe51f29fc260502a4b5761613c4 (patch)
treeb1301cefcbcbdf57ee9de71da876626d299b2dd7
parent6bc684acd42f9e0f827f818d90cc646951f89f37 (diff)
Process incoming com data asynchronously to running VCPU
This registers a handler with libevent that is triggered on incoming data on the com port. ok mlarkin@
-rw-r--r--usr.sbin/vmd/ns8250.c230
-rw-r--r--usr.sbin/vmd/ns8250.h18
-rw-r--r--usr.sbin/vmd/vmm.c9
3 files changed, 167 insertions, 90 deletions
diff --git a/usr.sbin/vmd/ns8250.c b/usr.sbin/vmd/ns8250.c
index 3adf0d8a8e3..4347b1f1c9b 100644
--- a/usr.sbin/vmd/ns8250.c
+++ b/usr.sbin/vmd/ns8250.c
@@ -20,23 +20,123 @@
#include <machine/vmmvar.h>
+#include <errno.h>
+#include <event.h>
+#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include "ns8250.h"
#include "proc.h"
+#include "vmd.h"
+#include "vmm.h"
extern char *__progname;
-struct ns8250_regs com1_regs;
+struct ns8250_dev com1_dev;
+
+static void com_rcv_event(int, short, void *);
+static void com_rcv(struct ns8250_dev *, uint32_t, uint32_t);
void
-ns8250_init(int fd)
+ns8250_init(int fd, uint32_t vmid)
{
- memset(&com1_regs, 0, sizeof(struct ns8250_regs));
- com1_regs.fd = fd;
+ int ret;
+
+ memset(&com1_dev, 0, sizeof(com1_dev));
+ ret = pthread_mutex_init(&com1_dev.mutex, NULL);
+ if (ret) {
+ errno = ret;
+ fatal("could not initialize com1 mutex");
+ }
+ com1_dev.fd = fd;
+ com1_dev.irq = 4;
+ com1_dev.rcv_pending = 0;
+
+ event_set(&com1_dev.event, com1_dev.fd, EV_READ | EV_PERSIST,
+ com_rcv_event, (void *)(uint64_t)vmid);
+ event_add(&com1_dev.event, NULL);
+}
+
+static void
+com_rcv_event(int fd, short kind, void *arg)
+{
+ mutex_lock(&com1_dev.mutex);
+
+ /*
+ * We already have other data pending to be received. The data that
+ * has become available now will be moved to the com port later.
+ */
+ if (com1_dev.rcv_pending) {
+ mutex_unlock(&com1_dev.mutex);
+ return;
+ }
+
+ if (com1_dev.regs.lsr & LSR_RXRDY)
+ com1_dev.rcv_pending = 1;
+ else {
+ com_rcv(&com1_dev, (uintptr_t)arg, 0);
+
+ /* If pending interrupt, inject */
+ if ((com1_dev.regs.iir & 0x1) == 0) {
+ /* XXX: vcpu_id */
+ vcpu_assert_pic_irq((uintptr_t)arg, 0, com1_dev.irq);
+ }
+ }
+
+ mutex_unlock(&com1_dev.mutex);
}
/*
+ * com_rcv
+ *
+ * Move received byte into com data register.
+ * Must be called with the mutex of the com device acquired
+ */
+static void
+com_rcv(struct ns8250_dev *com, uint32_t vm_id, uint32_t vcpu_id)
+{
+ char ch;
+ ssize_t sz;
+
+ /*
+ * Is there a new character available on com1?
+ * If so, consume the character, buffer it into the com1 data register
+ * assert IRQ4, and set the line status register RXRDY bit.
+ */
+ sz = read(com->fd, &ch, sizeof(char));
+ if (sz == -1) {
+ /*
+ * If we get EAGAIN, we'll retry and get the character later.
+ * This error can happen when typing two characters at once
+ * at the keyboard, for example.
+ */
+ if (errno != EAGAIN)
+ log_warn("unexpected read error on com device");
+ } else if (sz != 1)
+ log_warnx("unexpected read return value on com device");
+ else {
+ com->regs.lsr |= LSR_RXRDY;
+ com->regs.data = ch;
+ /* XXX these ier and iir bits should be IER_x and IIR_x */
+ if (com->regs.ier & 0x1) {
+ com->regs.iir |= (2 << 1);
+ com->regs.iir &= ~0x1;
+ }
+ }
+
+ /*
+ * Clear "interrupt pending" by setting IIR low bit to 1 if no
+ * interrupts are pending
+ */
+ /* XXX these iir magic numbers should be IIR_x */
+ if ((com->regs.iir & ~0x1) == 0x0)
+ com->regs.iir = 0x1;
+
+ com->rcv_pending = fd_hasdata(com->fd);
+}
+
+
+/*
* vcpu_process_com_data
*
* Emulate in/out instructions to the com1 (ns8250) UART data register
@@ -44,9 +144,12 @@ ns8250_init(int fd)
* Parameters:
* vei: vm exit information from vmm(4) containing information on the in/out
* instruction being performed
+ *
+ * Return value:
+ * interrupt to inject, or 0xFF if nothing to inject
*/
-void
-vcpu_process_com_data(union vm_exit *vei)
+uint8_t
+vcpu_process_com_data(union vm_exit *vei, uint32_t vm_id, uint32_t vcpu_id)
{
/*
* vei_dir == VEI_DIR_OUT : out instruction
@@ -57,12 +160,12 @@ vcpu_process_com_data(union vm_exit *vei)
* reporting)
*/
if (vei->vei.vei_dir == VEI_DIR_OUT) {
- write(com1_regs.fd, &vei->vei.vei_data, 1);
- if (com1_regs.ier & 0x2) {
+ write(com1_dev.fd, &vei->vei.vei_data, 1);
+ if (com1_dev.regs.ier & 0x2) {
/* Set TXRDY */
- com1_regs.iir |= IIR_TXRDY;
+ com1_dev.regs.iir |= IIR_TXRDY;
/* Set "interrupt pending" (IIR low bit cleared) */
- com1_regs.iir &= ~0x1;
+ com1_dev.regs.iir &= ~0x1;
}
} else {
/*
@@ -73,26 +176,34 @@ vcpu_process_com_data(union vm_exit *vei)
* input data and return to the guest. Also clear the
* interrupt info register regardless.
*/
- if (com1_regs.lsr & LSR_RXRDY) {
- vei->vei.vei_data = com1_regs.data;
- com1_regs.data = 0x0;
- com1_regs.lsr &= ~LSR_RXRDY;
+ if (com1_dev.regs.lsr & LSR_RXRDY) {
+ vei->vei.vei_data = com1_dev.regs.data;
+ com1_dev.regs.data = 0x0;
+ com1_dev.regs.lsr &= ~LSR_RXRDY;
} else {
- /* XXX should this be com1_regs.data or 0xff? */
- vei->vei.vei_data = com1_regs.data;
+ /* XXX should this be com1_dev.data or 0xff? */
+ vei->vei.vei_data = com1_dev.regs.data;
log_warnx("guest reading com1 when not ready");
}
/* Reading the data register always clears RXRDY from IIR */
- com1_regs.iir &= ~IIR_RXRDY;
+ com1_dev.regs.iir &= ~IIR_RXRDY;
/*
* Clear "interrupt pending" by setting IIR low bit to 1
* if no interrupt are pending
*/
- if (com1_regs.iir == 0x0)
- com1_regs.iir = 0x1;
+ if (com1_dev.regs.iir == 0x0)
+ com1_dev.regs.iir = 0x1;
+
+ if (com1_dev.rcv_pending)
+ com_rcv(&com1_dev, vm_id, vcpu_id);
}
+
+ /* If pending interrupt, make sure it gets injected */
+ if ((com1_dev.regs.iir & 0x1) == 0)
+ return (com1_dev.irq);
+ return (0xFF);
}
/*
@@ -113,14 +224,14 @@ vcpu_process_com_lcr(union vm_exit *vei)
* Write content to line control register
*/
if (vei->vei.vei_dir == VEI_DIR_OUT) {
- com1_regs.lcr = (uint8_t)vei->vei.vei_data;
+ com1_dev.regs.lcr = (uint8_t)vei->vei.vei_data;
} else {
/*
* vei_dir == VEI_DIR_IN : in instruction
*
* Read line control register
*/
- vei->vei.vei_data = com1_regs.lcr;
+ vei->vei.vei_data = com1_dev.regs.lcr;
}
}
@@ -145,7 +256,7 @@ vcpu_process_com_iir(union vm_exit *vei)
* Write to FCR
*/
if (vei->vei.vei_dir == VEI_DIR_OUT) {
- com1_regs.fcr = vei->vei.vei_data;
+ com1_dev.regs.fcr = vei->vei.vei_data;
} else {
/*
* vei_dir == VEI_DIR_IN : in instruction
@@ -153,15 +264,15 @@ vcpu_process_com_iir(union vm_exit *vei)
* Read IIR. Reading the IIR resets the TXRDY bit in the IIR
* after the data is read.
*/
- vei->vei.vei_data = com1_regs.iir;
- com1_regs.iir &= ~IIR_TXRDY;
+ vei->vei.vei_data = com1_dev.regs.iir;
+ com1_dev.regs.iir &= ~IIR_TXRDY;
/*
* Clear "interrupt pending" by setting IIR low bit to 1
* if no interrupts are pending
*/
- if (com1_regs.iir == 0x0)
- com1_regs.iir = 0x1;
+ if (com1_dev.regs.iir == 0x0)
+ com1_dev.regs.iir = 0x1;
}
}
@@ -184,14 +295,14 @@ vcpu_process_com_mcr(union vm_exit *vei)
* Write to MCR
*/
if (vei->vei.vei_dir == VEI_DIR_OUT) {
- com1_regs.mcr = vei->vei.vei_data;
+ com1_dev.regs.mcr = vei->vei.vei_data;
} else {
/*
* vei_dir == VEI_DIR_IN : in instruction
*
* Read from MCR
*/
- vei->vei.vei_data = com1_regs.mcr;
+ vei->vei.vei_data = com1_dev.regs.mcr;
}
}
@@ -223,7 +334,7 @@ vcpu_process_com_lsr(union vm_exit *vei)
* Read from LSR. We always report TXRDY and TSRE since we
* can process output characters immediately (at any time).
*/
- vei->vei.vei_data = com1_regs.lsr | LSR_TSRE | LSR_TXRDY;
+ vei->vei.vei_data = com1_dev.regs.lsr | LSR_TSRE | LSR_TXRDY;
}
}
@@ -254,7 +365,8 @@ vcpu_process_com_msr(union vm_exit *vei)
*
* Read from MSR. We always report DCD, DSR, and CTS.
*/
- vei->vei.vei_data = com1_regs.lsr | MSR_DCD | MSR_DSR | MSR_CTS;
+ vei->vei.vei_data =
+ com1_dev.regs.lsr | MSR_DCD | MSR_DSR | MSR_CTS;
}
}
@@ -280,7 +392,7 @@ vcpu_process_com_scr(union vm_exit *vei)
* Write to SCR
*/
if (vei->vei.vei_dir == VEI_DIR_OUT) {
- com1_regs.scr = vei->vei.vei_data;
+ com1_dev.regs.scr = vei->vei.vei_data;
} else {
/*
* vei_dir == VEI_DIR_IN : in instruction
@@ -289,7 +401,7 @@ vcpu_process_com_scr(union vm_exit *vei)
* a real scratch register, we negate what was written on
* subsequent readback.
*/
- vei->vei.vei_data = ~com1_regs.scr;
+ vei->vei.vei_data = ~com1_dev.regs.scr;
}
}
@@ -312,14 +424,14 @@ vcpu_process_com_ier(union vm_exit *vei)
* Write to IER
*/
if (vei->vei.vei_dir == VEI_DIR_OUT) {
- com1_regs.ier = vei->vei.vei_data;
+ com1_dev.regs.ier = vei->vei.vei_data;
} else {
/*
* vei_dir == VEI_DIR_IN : in instruction
*
* Read from IER
*/
- vei->vei.vei_data = com1_regs.ier;
+ vei->vei.vei_data = com1_dev.regs.ier;
}
}
@@ -340,8 +452,11 @@ vcpu_process_com_ier(union vm_exit *vei)
uint8_t
vcpu_exit_com(struct vm_run_params *vrp)
{
+ uint8_t intr = 0xFF;
union vm_exit *vei = vrp->vrp_exit;
+ mutex_lock(&com1_dev.mutex);
+
switch (vei->vei.vei_port) {
case COM1_LCR:
vcpu_process_com_lcr(vei);
@@ -365,50 +480,11 @@ vcpu_exit_com(struct vm_run_params *vrp)
vcpu_process_com_scr(vei);
break;
case COM1_DATA:
- vcpu_process_com_data(vei);
+ intr = vcpu_process_com_data(vei, vrp->vrp_vm_id,
+ vrp->vrp_vcpu_id);
break;
}
- return (0xFF);
-}
-
-/* XXX temporary until this is polled */
-int
-vcpu_com1_needs_intr(void)
-{
- int need_irq = 0;
- ssize_t sz;
- char ch;
-
- /*
- * Is there a new character available on com1?
- * If so, consume the character, buffer it into the com1 data register
- * assert IRQ4, and set the line status register RXRDY bit.
- *
- * XXX - move all this com intr checking to another function using poll
- */
- sz = read(com1_regs.fd, &ch, sizeof(char));
- if (sz == 1) {
- com1_regs.lsr |= LSR_RXRDY;
- com1_regs.data = ch;
- /* XXX these ier and iir bits should be IER_x and IIR_x */
- if (com1_regs.ier & 0x1) {
- com1_regs.iir |= (2 << 1);
- com1_regs.iir &= ~0x1;
- }
- }
-
- /*
- * Clear "interrupt pending" by setting IIR low bit to 1 if no
- * interrupts are pending
- */
- /* XXX these iir magic numbers should be IIR_x */
- if ((com1_regs.iir & ~0x1) == 0x0)
- com1_regs.iir = 0x1;
-
- /* If pending interrupt, inject */
- if ((com1_regs.iir & 0x1) == 0)
- need_irq = 1;
-
- return need_irq;
+ mutex_unlock(&com1_dev.mutex);
+ return (intr);
}
diff --git a/usr.sbin/vmd/ns8250.h b/usr.sbin/vmd/ns8250.h
index df50a0400f0..b791b9f8bd5 100644
--- a/usr.sbin/vmd/ns8250.h
+++ b/usr.sbin/vmd/ns8250.h
@@ -39,12 +39,21 @@ struct ns8250_regs {
uint8_t mcr; /* Modem Control Register */
uint8_t scr; /* Scratch Register */
uint8_t data; /* Unread input data */
- int fd; /* com port fd */
};
-void ns8250_init(int);
+/* ns8250 UART device state */
+struct ns8250_dev {
+ pthread_mutex_t mutex;
+ struct ns8250_regs regs;
+ struct event event;
+ int fd;
+ int irq;
+ int rcv_pending;
+};
+
+void ns8250_init(int, uint32_t);
uint8_t vcpu_exit_com(struct vm_run_params *);
-void vcpu_process_com_data(union vm_exit *);
+uint8_t vcpu_process_com_data(union vm_exit *, uint32_t, uint32_t);
void vcpu_process_com_lcr(union vm_exit *);
void vcpu_process_com_lsr(union vm_exit *);
void vcpu_process_com_ier(union vm_exit *);
@@ -52,6 +61,3 @@ void vcpu_process_com_mcr(union vm_exit *);
void vcpu_process_com_iir(union vm_exit *);
void vcpu_process_com_msr(union vm_exit *);
void vcpu_process_com_scr(union vm_exit *);
-
-/* XXX temporary until this is polled */
-int vcpu_com1_needs_intr(void);
diff --git a/usr.sbin/vmd/vmm.c b/usr.sbin/vmd/vmm.c
index 2f67f433064..f5383e1e8c4 100644
--- a/usr.sbin/vmd/vmm.c
+++ b/usr.sbin/vmd/vmm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmm.c,v 1.42 2016/09/02 16:23:40 stefan Exp $ */
+/* $OpenBSD: vmm.c,v 1.43 2016/09/02 17:10:08 stefan Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -849,7 +849,7 @@ init_emulated_hw(struct vm_create_params *vcp, int *child_disks,
ioports_map[IO_ICU2 + 1] = vcpu_exit_i8259;
/* Init ns8250 UART */
- ns8250_init(con_fd);
+ ns8250_init(con_fd, vcp->vcp_id);
for (i = COM1_DATA; i <= COM1_SCR; i++)
ioports_map[i] = vcpu_exit_com;
@@ -1322,14 +1322,9 @@ vcpu_exit(struct vm_run_params *vrp)
}
/* XXX this may not be irq 9 all the time */
- /* XXX change this to poll on the tap interface */
if (vionet_process_rx())
vcpu_assert_pic_irq(vrp->vrp_vm_id, vrp->vrp_vcpu_id, 9);
- /* XXX temporary until this is polled */
- if (vcpu_com1_needs_intr())
- vcpu_assert_pic_irq(vrp->vrp_vm_id, vrp->vrp_vcpu_id, 4);
-
vrp->vrp_continue = 1;
return (0);