Page MenuHomeFreeBSD

D40997.diff
No OneTemporary

D40997.diff

diff --git a/usr.sbin/bhyve/uart_emul.h b/usr.sbin/bhyve/uart_emul.h
--- a/usr.sbin/bhyve/uart_emul.h
+++ b/usr.sbin/bhyve/uart_emul.h
@@ -32,6 +32,7 @@
#define UART_NS16550_IO_BAR_SIZE 8
struct uart_ns16550_softc;
+struct uart_pl011_softc;
struct vm_snapshot_meta;
typedef void (*uart_intr_func_t)(void *arg);
@@ -49,4 +50,12 @@
int uart_ns16550_snapshot(struct uart_ns16550_softc *sc,
struct vm_snapshot_meta *meta);
#endif
-#endif
+
+uint32_t uart_pl011_read(struct uart_pl011_softc *sc, int offset);
+void uart_pl011_write(struct uart_pl011_softc *sc, int offset,
+ uint32_t value);
+struct uart_pl011_softc *uart_pl011_init(uart_intr_func_t intr_assert,
+ uart_intr_func_t intr_deassert, void *arg);
+int uart_pl011_tty_open(struct uart_pl011_softc *sc, const char *device);
+
+#endif /* _UART_EMUL_H_ */
diff --git a/usr.sbin/bhyve/uart_pl011.c b/usr.sbin/bhyve/uart_pl011.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/uart_pl011.c
@@ -0,0 +1,394 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Andrew Turner
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * 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 THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "uart_backend.h"
+#include "uart_emul.h"
+
+#define UART_FIFO_SIZE 16
+
+#define UARTDR 0x00
+#define UARTDR_RSR_SHIFT 8
+
+#define UARTRSR 0x01
+#define UARTRSR_OE (1 << 3)
+
+#define UARTFR 0x06
+#define UARTFR_TXFE (1 << 7)
+#define UARTFR_RXFF (1 << 6)
+#define UARTFR_TXFF (1 << 5)
+#define UARTFR_RXFE (1 << 4)
+
+#define UARTRTINTR (1 << 6)
+#define UARTTXINTR (1 << 5)
+#define UARTRXINTR (1 << 4)
+
+#define UARTIBRD 0x09
+
+#define UARTFBRD 0x0a
+#define UARTFBRD_MASK 0x003f
+
+#define UARTLCR_H 0x0b
+#define UARTLCR_H_MASK 0x00ff
+#define UARTLCR_H_FEN (1 << 4)
+
+#define UARTCR 0x0c
+/* TODO: Check the flags in the UARTCR register */
+#define UARTCR_MASK 0xffc7
+#define UARTCR_LBE (1 << 7)
+
+#define UARTIFLS 0x0d
+#define UARTIFLS_MASK 0x003f
+#define UARTIFLS_RXIFLSEL(x) (((x) >> 3) & 0x7)
+#define UARTIFLS_TXIFLSEL(x) (((x) >> 0) & 0x7)
+
+#define UARTIMSC 0x0e
+#define UARTIMSC_MASK 0x07ff
+
+#define UARTRIS 0x0f
+#define UARTMIS 0x10
+
+#define UARTICR 0x11
+
+#define UARTPeriphID 0x00241011
+#define UARTPeriphID0 0x3f8
+#define UARTPeriphID0_VAL (((UARTPeriphID) >> 0) & 0xff)
+#define UARTPeriphID1 0x3f9
+#define UARTPeriphID1_VAL (((UARTPeriphID) >> 8) & 0xff)
+#define UARTPeriphID2 0x3fa
+#define UARTPeriphID2_VAL (((UARTPeriphID) >> 16) & 0xff)
+#define UARTPeriphID3 0x3fb
+#define UARTPeriphID3_VAL (((UARTPeriphID) >> 24) & 0xff)
+
+#define UARTPCellID 0xb105f00d
+#define UARTPCellID0 0x3fc
+#define UARTPCellID0_VAL (((UARTPCellID) >> 0) & 0xff)
+#define UARTPCellID1 0x3fd
+#define UARTPCellID1_VAL (((UARTPCellID) >> 8) & 0xff)
+#define UARTPCellID2 0x3fe
+#define UARTPCellID2_VAL (((UARTPCellID) >> 16) & 0xff)
+#define UARTPCellID3 0x3ff
+#define UARTPCellID3_VAL (((UARTPCellID) >> 24) & 0xff)
+
+struct uart_pl011_softc {
+ struct uart_softc *backend;
+ pthread_mutex_t mtx; /* protects all softc elements */
+
+ uint16_t irq_state;
+
+ uint16_t rsr;
+
+ uint16_t cr;
+ uint16_t ifls;
+ uint16_t imsc;
+ uint16_t lcr_h;
+
+ uint16_t ibrd;
+ uint16_t fbrd;
+
+ void *arg;
+ uart_intr_func_t intr_assert;
+ uart_intr_func_t intr_deassert;
+};
+
+static void
+uart_reset(struct uart_pl011_softc *sc)
+{
+ sc->ifls = 0x12;
+
+ /* no fifo until enabled by software */
+ uart_rxfifo_reset(sc->backend, 1);
+}
+
+static int
+uart_rx_trigger_level(struct uart_pl011_softc *sc)
+{
+ /* If the FIFO is disabled trigger when we have any data */
+ if ((sc->lcr_h & UARTLCR_H_FEN) != 0)
+ return (1);
+
+ /* Trigger base on how full the fifo is */
+ switch (UARTIFLS_RXIFLSEL(sc->ifls)) {
+ case 0:
+ return (UART_FIFO_SIZE / 8);
+ case 1:
+ return (UART_FIFO_SIZE / 4);
+ case 2:
+ return (UART_FIFO_SIZE / 2);
+ case 3:
+ return (UART_FIFO_SIZE * 3 / 4);
+ case 4:
+ return (UART_FIFO_SIZE * 7 / 8);
+ default:
+ /* TODO: Find out what happens in this case */
+ return (UART_FIFO_SIZE);
+ }
+}
+
+static void
+uart_toggle_intr(struct uart_pl011_softc *sc)
+{
+ if ((sc->irq_state & sc->imsc) == 0)
+ (*sc->intr_deassert)(sc->arg);
+ else
+ (*sc->intr_assert)(sc->arg);
+}
+
+static void
+uart_drain(int fd __unused, enum ev_type ev, void *arg)
+{
+ struct uart_pl011_softc *sc;
+ int old_size, trig_lvl;
+ bool loopback;
+
+ sc = arg;
+
+ 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);
+
+ old_size = uart_rxfifo_numchars(sc->backend);
+
+ loopback = (sc->cr & UARTCR_LBE) != 0;
+ uart_rxfifo_drain(sc->backend, loopback);
+
+ /* If we cross the trigger level raise UARTRXINTR */
+ trig_lvl = uart_rx_trigger_level(sc);
+ if (old_size < trig_lvl &&
+ uart_rxfifo_numchars(sc->backend) >= trig_lvl)
+ sc->irq_state |= UARTRXINTR;
+
+ if (uart_rxfifo_numchars(sc->backend) > 0)
+ sc->irq_state |= UARTRTINTR;
+ if (!loopback)
+ uart_toggle_intr(sc);
+
+ pthread_mutex_unlock(&sc->mtx);
+}
+
+void
+uart_pl011_write(struct uart_pl011_softc *sc, int offset, uint32_t value)
+{
+ bool loopback;
+
+ pthread_mutex_lock(&sc->mtx);
+ switch (offset) {
+ case UARTDR:
+ loopback = (sc->cr & UARTCR_LBE) != 0;
+ if (uart_rxfifo_putchar(sc->backend, value & 0xff, loopback))
+ sc->rsr |= UARTRSR_OE;
+
+ /* We don't have a TX fifo, so trigger when we have data */
+ sc->irq_state |= UARTTXINTR;
+ break;
+ case UARTRSR:
+ /* Any write clears this register */
+ sc->rsr = 0;
+ break;
+ case UARTFR:
+ /* UARTFR is a read-only register */
+ break;
+ /* TODO: UARTILPR */
+ case UARTIBRD:
+ sc->ibrd = value;
+ break;
+ case UARTFBRD:
+ sc->fbrd = value & UARTFBRD_MASK;
+ break;
+ case UARTLCR_H:
+ /* Check if the FIFO enable bit changed */
+ if (((sc->lcr_h ^ value) & UARTLCR_H_FEN) != 0) {
+ if ((value & UARTLCR_H_FEN) != 0) {
+ uart_rxfifo_reset(sc->backend, UART_FIFO_SIZE);
+ } else {
+ uart_rxfifo_reset(sc->backend, 1);
+ }
+ }
+ sc->lcr_h = value & UARTLCR_H_MASK;
+ break;
+ case UARTCR:
+ sc->cr = value & UARTCR_MASK;
+ break;
+ case UARTIFLS:
+ sc->ifls = value & UARTCR_MASK;
+ break;
+ case UARTIMSC:
+ sc->imsc = value & UARTIMSC_MASK;
+ break;
+ case UARTRIS:
+ case UARTMIS:
+ /* UARTRIS and UARTMIS are read-only registers */
+ break;
+ case UARTICR:
+ sc->irq_state &= ~value;
+ break;
+ default:
+ /* Ignore writes to unassigned/ID registers */
+ break;
+ }
+ uart_toggle_intr(sc);
+ pthread_mutex_unlock(&sc->mtx);
+}
+
+uint32_t
+uart_pl011_read(struct uart_pl011_softc *sc, int offset)
+{
+ uint32_t reg;
+ int fifo_sz;
+
+ reg = 0;
+ pthread_mutex_lock(&sc->mtx);
+ switch (offset) {
+ case UARTDR:
+ reg = uart_rxfifo_getchar(sc->backend);
+ /* Deassert the irq if below the trigger level */
+ fifo_sz = uart_rxfifo_numchars(sc->backend);
+ if (fifo_sz < uart_rx_trigger_level(sc))
+ sc->irq_state &= ~UARTRXINTR;
+ if (fifo_sz == 0)
+ sc->irq_state &= ~UARTRTINTR;
+
+ reg |= sc->rsr << UARTDR_RSR_SHIFT;
+
+ /* After reading from the fifo there is now space in it */
+ sc->rsr &= UARTRSR_OE;
+ break;
+ case UARTRSR:
+ /* Any write clears this register */
+ reg = sc->rsr;
+ break;
+ case UARTFR:
+ /* Transmit is intstant, so the fifo is always empty */
+ reg = UARTFR_TXFE;
+
+ /* Set the receive fifo full/empty flags */
+ fifo_sz = uart_rxfifo_numchars(sc->backend);
+ if (fifo_sz == UART_FIFO_SIZE)
+ reg |= UARTFR_RXFF;
+ else if (fifo_sz == 0)
+ reg |= UARTFR_RXFE;
+ break;
+ /* TODO: UARTILPR */
+ case UARTIBRD:
+ reg = sc->ibrd;
+ break;
+ case UARTFBRD:
+ reg = sc->fbrd;
+ break;
+ case UARTLCR_H:
+ reg = sc->lcr_h;
+ break;
+ case UARTCR:
+ reg = sc->cr;
+ break;
+ case UARTIMSC:
+ reg = sc->imsc;
+ break;
+ case UARTRIS:
+ reg = sc->irq_state;
+ break;
+ case UARTMIS:
+ reg = sc->irq_state & sc->imsc;
+ break;
+ case UARTICR:
+ reg = 0;
+ break;
+ case UARTPeriphID0:
+ reg = UARTPeriphID0_VAL;
+ break;
+ case UARTPeriphID1:
+ reg =UARTPeriphID1_VAL;
+ break;
+ case UARTPeriphID2:
+ reg = UARTPeriphID2_VAL;
+ break;
+ case UARTPeriphID3:
+ reg = UARTPeriphID3_VAL;
+ break;
+ case UARTPCellID0:
+ reg = UARTPCellID0_VAL;
+ break;
+ case UARTPCellID1:
+ reg = UARTPCellID1_VAL;
+ break;
+ case UARTPCellID2:
+ reg = UARTPCellID2_VAL;
+ break;
+ case UARTPCellID3:
+ reg = UARTPCellID3_VAL;
+ break;
+ default:
+ /* Return 0 in reads from unasigned registers */
+ reg = 0;
+ break;
+ }
+ uart_toggle_intr(sc);
+ pthread_mutex_unlock(&sc->mtx);
+
+ return (reg);
+}
+
+struct uart_pl011_softc *
+uart_pl011_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert,
+ void *arg)
+{
+ struct uart_pl011_softc *sc;
+
+ sc = calloc(1, sizeof(struct uart_pl011_softc));
+
+ sc->arg = arg;
+ sc->intr_assert = intr_assert;
+ sc->intr_deassert = intr_deassert;
+ sc->backend = uart_init();
+
+ pthread_mutex_init(&sc->mtx, NULL);
+
+ uart_reset(sc);
+
+ return (sc);
+}
+
+int
+uart_pl011_tty_open(struct uart_pl011_softc *sc, const char *device)
+{
+ return (uart_tty_open(sc->backend, device, uart_drain, sc));
+}

File Metadata

Mime Type
text/plain
Expires
Fri, Jan 24, 10:29 PM (20 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16107265
Default Alt Text
D40997.diff (10 KB)

Event Timeline