summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/vmd/ns8250.c121
-rw-r--r--usr.sbin/vmd/ns8250.h8
-rw-r--r--usr.sbin/vmd/vm.c23
-rw-r--r--usr.sbin/vmd/vmm.h1
4 files changed, 136 insertions, 17 deletions
diff --git a/usr.sbin/vmd/ns8250.c b/usr.sbin/vmd/ns8250.c
index a5978fa2189..91c417b13a1 100644
--- a/usr.sbin/vmd/ns8250.c
+++ b/usr.sbin/vmd/ns8250.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ns8250.c,v 1.8 2017/05/08 09:08:40 reyk Exp $ */
+/* $OpenBSD: ns8250.c,v 1.9 2017/06/07 14:53:28 mlarkin Exp $ */
/*
* Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org>
*
@@ -39,6 +39,26 @@ struct ns8250_dev com1_dev;
static void com_rcv_event(int, short, void *);
static void com_rcv(struct ns8250_dev *, uint32_t, uint32_t);
+/*
+ * ratelimit
+ *
+ * Timeout callback function used when we have to slow down the output rate
+ * from the emulated serial port.
+ *
+ * Parameters:
+ * fd: unused
+ * type: unused
+ * arg: unused
+ */
+static void
+ratelimit(int fd, short type, void *arg)
+{
+ /* Set TXRDY and clear "no pending interrupt" */
+ com1_dev.regs.iir |= IIR_TXRDY;
+ com1_dev.regs.iir &= ~IIR_NOPEND;
+ vcpu_assert_pic_irq(com1_dev.vmid, 0, com1_dev.irq);
+}
+
void
ns8250_init(int fd, uint32_t vmid)
{
@@ -53,10 +73,34 @@ ns8250_init(int fd, uint32_t vmid)
com1_dev.fd = fd;
com1_dev.irq = 4;
com1_dev.rcv_pending = 0;
+ com1_dev.vmid = vmid;
+ com1_dev.byte_out = 0;
+ com1_dev.regs.divlo = 1;
+ com1_dev.baudrate = 115200;
+
+ /*
+ * Our serial port is essentially instantaneous, with infinite
+ * baudrate capability. To adjust for the selected baudrate,
+ * we calculate how many characters could be transmitted in a 10ms
+ * period (pause_ct) and then delay 10ms after each pause_ct sized
+ * group of characters have been transmitted. Since it takes nearly
+ * zero time to send the actual characters, the total amount of time
+ * spent is roughly equal to what it would be on real hardware.
+ *
+ * To make things simple, we don't adjust for different sized bytes
+ * (and parity, stop bits, etc) and simply assume each character
+ * output is 8 bits.
+ */
+ com1_dev.pause_ct = (com1_dev.baudrate / 8) / 1000 * 10;
event_set(&com1_dev.event, com1_dev.fd, EV_READ | EV_PERSIST,
com_rcv_event, (void *)(intptr_t)vmid);
event_add(&com1_dev.event, NULL);
+
+ /* Rate limiter for simulating baud rate */
+ timerclear(&com1_dev.rate_tv);
+ com1_dev.rate_tv.tv_usec = 10000;
+ evtimer_set(&com1_dev.rate, ratelimit, NULL);
}
static void
@@ -79,7 +123,7 @@ com_rcv_event(int fd, short kind, void *arg)
com_rcv(&com1_dev, (uintptr_t)arg, 0);
/* If pending interrupt, inject */
- if ((com1_dev.regs.iir & 0x1) == 0) {
+ if ((com1_dev.regs.iir & IIR_NOPEND) == 0) {
/* XXX: vcpu_id */
vcpu_assert_pic_irq((uintptr_t)arg, 0, com1_dev.irq);
}
@@ -119,17 +163,16 @@ com_rcv(struct ns8250_dev *com, uint32_t vm_id, uint32_t vcpu_id)
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;
+
+ if (com->regs.ier & IER_ERXRDY) {
+ com->regs.iir |= IIR_RXRDY;
+ com->regs.iir &= ~IIR_NOPEND;
}
}
com->rcv_pending = fd_hasdata(com->fd);
}
-
/*
* vcpu_process_com_data
*
@@ -154,14 +197,29 @@ vcpu_process_com_data(union vm_exit *vei, uint32_t vm_id, uint32_t vcpu_id)
* reporting)
*/
if (vei->vei.vei_dir == VEI_DIR_OUT) {
+ if (com1_dev.regs.lcr & LCR_DLAB) {
+ com1_dev.regs.divlo = vei->vei.vei_data;
+ return 0xFF;
+ }
+
write(com1_dev.fd, &vei->vei.vei_data, 1);
+ com1_dev.byte_out++;
+
if (com1_dev.regs.ier & IER_ETXRDY) {
- /* Set TXRDY */
- com1_dev.regs.iir |= IIR_TXRDY;
- /* Set "interrupt pending" (IIR low bit cleared) */
- com1_dev.regs.iir &= ~0x1;
+ /* Limit output rate if needed */
+ if (com1_dev.byte_out % com1_dev.pause_ct == 0) {
+ evtimer_add(&com1_dev.rate, &com1_dev.rate_tv);
+ } else {
+ /* Set TXRDY and clear "no pending interrupt" */
+ com1_dev.regs.iir |= IIR_TXRDY;
+ com1_dev.regs.iir &= ~IIR_NOPEND;
+ }
}
} else {
+ if (com1_dev.regs.lcr & LCR_DLAB) {
+ set_return_data(vei, com1_dev.regs.divlo);
+ return 0xFF;
+ }
/*
* vei_dir == VEI_DIR_IN : in instruction
*
@@ -175,7 +233,6 @@ vcpu_process_com_data(union vm_exit *vei, uint32_t vm_id, uint32_t vcpu_id)
com1_dev.regs.data = 0x0;
com1_dev.regs.lsr &= ~LSR_RXRDY;
} else {
- /* XXX should this be com1_dev.data or 0xff? */
set_return_data(vei, com1_dev.regs.data);
log_warnx("%s: guest reading com1 when not ready", __func__);
}
@@ -195,7 +252,7 @@ vcpu_process_com_data(union vm_exit *vei, uint32_t vm_id, uint32_t vcpu_id)
}
/* If pending interrupt, make sure it gets injected */
- if ((com1_dev.regs.iir & 0x1) == 0)
+ if ((com1_dev.regs.iir & IIR_NOPEND) == 0)
return (com1_dev.irq);
return (0xFF);
@@ -213,12 +270,33 @@ vcpu_process_com_data(union vm_exit *vei, uint32_t vm_id, uint32_t vcpu_id)
void
vcpu_process_com_lcr(union vm_exit *vei)
{
+ uint8_t data = (uint8_t)vei->vei.vei_data;
+ uint16_t divisor;
+
/*
* vei_dir == VEI_DIR_OUT : out instruction
*
* Write content to line control register
*/
if (vei->vei.vei_dir == VEI_DIR_OUT) {
+ if (com1_dev.regs.lcr & LCR_DLAB) {
+ if (!(data & LCR_DLAB)) {
+ if (com1_dev.regs.divlo == 0 &&
+ com1_dev.regs.divhi == 0) {
+ log_warnx("%s: ignoring invalid "
+ "baudrate", __func__);
+ } else {
+ divisor = com1_dev.regs.divlo |
+ com1_dev.regs.divhi << 8;
+ com1_dev.baudrate = 115200 / divisor;
+ com1_dev.pause_ct =
+ (com1_dev.baudrate / 8) / 1000 * 10;
+ }
+
+ log_debug("%s: set baudrate = %d", __func__,
+ com1_dev.baudrate);
+ }
+ }
com1_dev.regs.lcr = (uint8_t)vei->vei.vei_data;
} else {
/*
@@ -419,10 +497,18 @@ vcpu_process_com_ier(union vm_exit *vei)
* Write to IER
*/
if (vei->vei.vei_dir == VEI_DIR_OUT) {
+ if (com1_dev.regs.lcr & LCR_DLAB) {
+ com1_dev.regs.divhi = vei->vei.vei_data;
+ return;
+ }
com1_dev.regs.ier = vei->vei.vei_data;
if (com1_dev.regs.ier & IER_ETXRDY)
com1_dev.regs.iir |= IIR_TXRDY;
} else {
+ if (com1_dev.regs.lcr & LCR_DLAB) {
+ set_return_data(vei, com1_dev.regs.divhi);
+ return;
+ }
/*
* vei_dir == VEI_DIR_IN : in instruction
*
@@ -436,8 +522,7 @@ vcpu_process_com_ier(union vm_exit *vei)
* vcpu_exit_com
*
* Process com1 (ns8250) UART exits. vmd handles most basic 8250
- * features with the exception of the divisor latch (eg, no baud
- * rate support)
+ * features
*
* Parameters:
* vrp: vcpu run parameters containing guest state for this exit
@@ -483,6 +568,12 @@ vcpu_exit_com(struct vm_run_params *vrp)
}
mutex_unlock(&com1_dev.mutex);
+
+ if ((com1_dev.regs.iir & IIR_NOPEND)) {
+ /* XXX: vcpu_id */
+ vcpu_deassert_pic_irq(com1_dev.vmid, 0, com1_dev.irq);
+ }
+
return (intr);
}
diff --git a/usr.sbin/vmd/ns8250.h b/usr.sbin/vmd/ns8250.h
index 5d481d918b3..6c861107e67 100644
--- a/usr.sbin/vmd/ns8250.h
+++ b/usr.sbin/vmd/ns8250.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ns8250.h,v 1.4 2017/05/08 09:08:40 reyk Exp $ */
+/* $OpenBSD: ns8250.h,v 1.5 2017/06/07 14:53:28 mlarkin Exp $ */
/*
* Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org>
*
@@ -47,9 +47,15 @@ struct ns8250_dev {
pthread_mutex_t mutex;
struct ns8250_regs regs;
struct event event;
+ struct event rate;
+ struct timeval rate_tv;
int fd;
int irq;
int rcv_pending;
+ uint32_t vmid;
+ uint64_t byte_out;
+ uint32_t baudrate;
+ uint32_t pause_ct;
};
void ns8250_init(int, uint32_t);
diff --git a/usr.sbin/vmd/vm.c b/usr.sbin/vmd/vm.c
index 2d38bc0f5e3..1c4e354422b 100644
--- a/usr.sbin/vmd/vm.c
+++ b/usr.sbin/vmd/vm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vm.c,v 1.19 2017/05/30 17:56:47 tedu Exp $ */
+/* $OpenBSD: vm.c,v 1.20 2017/06/07 14:53:28 mlarkin Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -1347,6 +1347,27 @@ vcpu_assert_pic_irq(uint32_t vm_id, uint32_t vcpu_id, int irq)
}
/*
+ * vcpu_deassert_pic_irq
+ *
+ * Clears the specified IRQ on the supplied vcpu/vm
+ *
+ * Parameters:
+ * vm_id: VM ID to clear in
+ * vcpu_id: VCPU ID to clear in
+ * irq: IRQ to clear
+ */
+void
+vcpu_deassert_pic_irq(uint32_t vm_id, uint32_t vcpu_id, int irq)
+{
+ i8259_deassert_irq(irq);
+
+ if (!i8259_is_pending()) {
+ if (vcpu_pic_intr(vm_id, vcpu_id, 0))
+ fatalx("%s: can't deassert INTR", __func__);
+ }
+}
+
+/*
* fd_hasdata
*
* Determines if data can be read from a file descriptor.
diff --git a/usr.sbin/vmd/vmm.h b/usr.sbin/vmd/vmm.h
index fd94125c9df..084554fc87c 100644
--- a/usr.sbin/vmd/vmm.h
+++ b/usr.sbin/vmd/vmm.h
@@ -19,5 +19,6 @@
typedef uint8_t (*io_fn_t)(struct vm_run_params *);
void vcpu_assert_pic_irq(uint32_t, uint32_t, int);
+void vcpu_deassert_pic_irq(uint32_t, uint32_t, int);
void set_return_data(union vm_exit *, uint32_t);
void get_input_data(union vm_exit *, uint32_t *);