1ea7f1c8cSNeel Natu /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 31de7b4b8SPedro F. Giffuni * 4ea7f1c8cSNeel Natu * Copyright (c) 2012 NetApp, Inc. 5ea7f1c8cSNeel Natu * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 6ea7f1c8cSNeel Natu * All rights reserved. 7ea7f1c8cSNeel Natu * 8ea7f1c8cSNeel Natu * Redistribution and use in source and binary forms, with or without 9ea7f1c8cSNeel Natu * modification, are permitted provided that the following conditions 10ea7f1c8cSNeel Natu * are met: 11ea7f1c8cSNeel Natu * 1. Redistributions of source code must retain the above copyright 12ea7f1c8cSNeel Natu * notice, this list of conditions and the following disclaimer. 13ea7f1c8cSNeel Natu * 2. Redistributions in binary form must reproduce the above copyright 14ea7f1c8cSNeel Natu * notice, this list of conditions and the following disclaimer in the 15ea7f1c8cSNeel Natu * documentation and/or other materials provided with the distribution. 16ea7f1c8cSNeel Natu * 17ea7f1c8cSNeel Natu * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 18ea7f1c8cSNeel Natu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19ea7f1c8cSNeel Natu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20ea7f1c8cSNeel Natu * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 21ea7f1c8cSNeel Natu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22ea7f1c8cSNeel Natu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23ea7f1c8cSNeel Natu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24ea7f1c8cSNeel Natu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25ea7f1c8cSNeel Natu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26ea7f1c8cSNeel Natu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27ea7f1c8cSNeel Natu * SUCH DAMAGE. 28ea7f1c8cSNeel Natu */ 29ea7f1c8cSNeel Natu 30ea7f1c8cSNeel Natu #include <sys/cdefs.h> 31ea7f1c8cSNeel Natu #include <sys/types.h> 32ea7f1c8cSNeel Natu #include <dev/ic/ns16550.h> 3300ef17beSBartek Rutkowski #ifndef WITHOUT_CAPSICUM 3400ef17beSBartek Rutkowski #include <sys/capsicum.h> 3500ef17beSBartek Rutkowski #include <capsicum_helpers.h> 3600ef17beSBartek Rutkowski #endif 37ea7f1c8cSNeel Natu 38483d953aSJohn Baldwin #include <machine/vmm_snapshot.h> 39483d953aSJohn Baldwin 40ea7f1c8cSNeel Natu #include <stdio.h> 41ea7f1c8cSNeel Natu #include <stdlib.h> 42ea7f1c8cSNeel Natu #include <assert.h> 4300ef17beSBartek Rutkowski #include <err.h> 4400ef17beSBartek Rutkowski #include <errno.h> 456380102cSPeter Grehan #include <fcntl.h> 46ea7f1c8cSNeel Natu #include <termios.h> 47ea7f1c8cSNeel Natu #include <unistd.h> 48ea7f1c8cSNeel Natu #include <stdbool.h> 49ea7f1c8cSNeel Natu #include <string.h> 50ea7f1c8cSNeel Natu #include <pthread.h> 5100ef17beSBartek Rutkowski #include <sysexits.h> 52ea7f1c8cSNeel Natu 53ea7f1c8cSNeel Natu #include "mevent.h" 54ea7f1c8cSNeel Natu #include "uart_emul.h" 55332eff95SVincenzo Maffione #include "debug.h" 56ea7f1c8cSNeel Natu 57ea7f1c8cSNeel Natu #define COM1_BASE 0x3F8 58ea7f1c8cSNeel Natu #define COM1_IRQ 4 59ea7f1c8cSNeel Natu #define COM2_BASE 0x2F8 60ea7f1c8cSNeel Natu #define COM2_IRQ 3 61eed1cc6cSPeter Grehan #define COM3_BASE 0x3E8 62eed1cc6cSPeter Grehan #define COM3_IRQ 4 63eed1cc6cSPeter Grehan #define COM4_BASE 0x2E8 64eed1cc6cSPeter Grehan #define COM4_IRQ 3 65ea7f1c8cSNeel Natu 66ea7f1c8cSNeel Natu #define DEFAULT_RCLK 1843200 67*d1eb515fSEd Maste #define DEFAULT_BAUD 115200 68ea7f1c8cSNeel Natu 69ea7f1c8cSNeel Natu #define FCR_RX_MASK 0xC0 70ea7f1c8cSNeel Natu 71ea7f1c8cSNeel Natu #define MCR_OUT1 0x04 72ea7f1c8cSNeel Natu #define MCR_OUT2 0x08 73ea7f1c8cSNeel Natu 74ea7f1c8cSNeel Natu #define MSR_DELTA_MASK 0x0f 75ea7f1c8cSNeel Natu 76ea7f1c8cSNeel Natu #ifndef REG_SCR 77ea7f1c8cSNeel Natu #define REG_SCR com_scr 78ea7f1c8cSNeel Natu #endif 79ea7f1c8cSNeel Natu 80ea7f1c8cSNeel Natu #define FIFOSZ 16 81ea7f1c8cSNeel Natu 82ea7f1c8cSNeel Natu static bool uart_stdio; /* stdio in use for i/o */ 836380102cSPeter Grehan static struct termios tio_stdio_orig; 84ea7f1c8cSNeel Natu 85ea7f1c8cSNeel Natu static struct { 86ea7f1c8cSNeel Natu int baseaddr; 87ea7f1c8cSNeel Natu int irq; 88ea7f1c8cSNeel Natu bool inuse; 89ea7f1c8cSNeel Natu } uart_lres[] = { 90ea7f1c8cSNeel Natu { COM1_BASE, COM1_IRQ, false}, 91ea7f1c8cSNeel Natu { COM2_BASE, COM2_IRQ, false}, 92eed1cc6cSPeter Grehan { COM3_BASE, COM3_IRQ, false}, 93eed1cc6cSPeter Grehan { COM4_BASE, COM4_IRQ, false}, 94ea7f1c8cSNeel Natu }; 95ea7f1c8cSNeel Natu 96ea7f1c8cSNeel Natu #define UART_NLDEVS (sizeof(uart_lres) / sizeof(uart_lres[0])) 97ea7f1c8cSNeel Natu 98ea7f1c8cSNeel Natu struct fifo { 99ea7f1c8cSNeel Natu uint8_t buf[FIFOSZ]; 100ea7f1c8cSNeel Natu int rindex; /* index to read from */ 101ea7f1c8cSNeel Natu int windex; /* index to write to */ 102ea7f1c8cSNeel Natu int num; /* number of characters in the fifo */ 103ea7f1c8cSNeel Natu int size; /* size of the fifo */ 104ea7f1c8cSNeel Natu }; 105ea7f1c8cSNeel Natu 1066380102cSPeter Grehan struct ttyfd { 1076380102cSPeter Grehan bool opened; 108d6ef759eSMark Johnston int rfd; /* fd for reading */ 109d6ef759eSMark Johnston int wfd; /* fd for writing, may be == rfd */ 1106380102cSPeter Grehan }; 1116380102cSPeter Grehan 112ea7f1c8cSNeel Natu struct uart_softc { 113ea7f1c8cSNeel Natu pthread_mutex_t mtx; /* protects all softc elements */ 114ea7f1c8cSNeel Natu uint8_t data; /* Data register (R/W) */ 115ea7f1c8cSNeel Natu uint8_t ier; /* Interrupt enable register (R/W) */ 116ea7f1c8cSNeel Natu uint8_t lcr; /* Line control register (R/W) */ 117ea7f1c8cSNeel Natu uint8_t mcr; /* Modem control register (R/W) */ 118ea7f1c8cSNeel Natu uint8_t lsr; /* Line status register (R/W) */ 119ea7f1c8cSNeel Natu uint8_t msr; /* Modem status register (R/W) */ 120ea7f1c8cSNeel Natu uint8_t fcr; /* FIFO control register (W) */ 121ea7f1c8cSNeel Natu uint8_t scr; /* Scratch register (R/W) */ 122ea7f1c8cSNeel Natu 123ea7f1c8cSNeel Natu uint8_t dll; /* Baudrate divisor latch LSB */ 124ea7f1c8cSNeel Natu uint8_t dlh; /* Baudrate divisor latch MSB */ 125ea7f1c8cSNeel Natu 126ea7f1c8cSNeel Natu struct fifo rxfifo; 1272bd073e1SNeel Natu struct mevent *mev; 128ea7f1c8cSNeel Natu 1296380102cSPeter Grehan struct ttyfd tty; 130ea7f1c8cSNeel Natu bool thre_int_pending; /* THRE interrupt pending */ 131ea7f1c8cSNeel Natu 132ea7f1c8cSNeel Natu void *arg; 133ea7f1c8cSNeel Natu uart_intr_func_t intr_assert; 134ea7f1c8cSNeel Natu uart_intr_func_t intr_deassert; 135ea7f1c8cSNeel Natu }; 136ea7f1c8cSNeel Natu 137ea7f1c8cSNeel Natu static void uart_drain(int fd, enum ev_type ev, void *arg); 138ea7f1c8cSNeel Natu 139ea7f1c8cSNeel Natu static void 140ea7f1c8cSNeel Natu ttyclose(void) 141ea7f1c8cSNeel Natu { 142ea7f1c8cSNeel Natu 1436380102cSPeter Grehan tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); 144ea7f1c8cSNeel Natu } 145ea7f1c8cSNeel Natu 146ea7f1c8cSNeel Natu static void 1476380102cSPeter Grehan ttyopen(struct ttyfd *tf) 148ea7f1c8cSNeel Natu { 149d6ef759eSMark Johnston struct termios orig, new; 150ea7f1c8cSNeel Natu 151d6ef759eSMark Johnston tcgetattr(tf->rfd, &orig); 152d6ef759eSMark Johnston new = orig; 153d6ef759eSMark Johnston cfmakeraw(&new); 154d6ef759eSMark Johnston new.c_cflag |= CLOCAL; 155d6ef759eSMark Johnston tcsetattr(tf->rfd, TCSANOW, &new); 156d6ef759eSMark Johnston if (uart_stdio) { 157d6ef759eSMark Johnston tio_stdio_orig = orig; 158ea7f1c8cSNeel Natu atexit(ttyclose); 159ea7f1c8cSNeel Natu } 160332eff95SVincenzo Maffione raw_stdio = 1; 1616380102cSPeter Grehan } 162ea7f1c8cSNeel Natu 163ea7f1c8cSNeel Natu static int 1646380102cSPeter Grehan ttyread(struct ttyfd *tf) 165ea7f1c8cSNeel Natu { 1662bd073e1SNeel Natu unsigned char rb; 167ea7f1c8cSNeel Natu 168d6ef759eSMark Johnston if (read(tf->rfd, &rb, 1) == 1) 1692bd073e1SNeel Natu return (rb); 1702bd073e1SNeel Natu else 171ea7f1c8cSNeel Natu return (-1); 172ea7f1c8cSNeel Natu } 173ea7f1c8cSNeel Natu 174ea7f1c8cSNeel Natu static void 1756380102cSPeter Grehan ttywrite(struct ttyfd *tf, unsigned char wb) 176ea7f1c8cSNeel Natu { 177ea7f1c8cSNeel Natu 178d6ef759eSMark Johnston (void)write(tf->wfd, &wb, 1); 179ea7f1c8cSNeel Natu } 180ea7f1c8cSNeel Natu 181ea7f1c8cSNeel Natu static void 1822bd073e1SNeel Natu rxfifo_reset(struct uart_softc *sc, int size) 183ea7f1c8cSNeel Natu { 1842bd073e1SNeel Natu char flushbuf[32]; 1852bd073e1SNeel Natu struct fifo *fifo; 1862bd073e1SNeel Natu ssize_t nread; 1872bd073e1SNeel Natu int error; 188ea7f1c8cSNeel Natu 1892bd073e1SNeel Natu fifo = &sc->rxfifo; 190ea7f1c8cSNeel Natu bzero(fifo, sizeof(struct fifo)); 191ea7f1c8cSNeel Natu fifo->size = size; 1922bd073e1SNeel Natu 1932bd073e1SNeel Natu if (sc->tty.opened) { 1942bd073e1SNeel Natu /* 1952bd073e1SNeel Natu * Flush any unread input from the tty buffer. 1962bd073e1SNeel Natu */ 1972bd073e1SNeel Natu while (1) { 198d6ef759eSMark Johnston nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf)); 1992bd073e1SNeel Natu if (nread != sizeof(flushbuf)) 2002bd073e1SNeel Natu break; 2012bd073e1SNeel Natu } 2022bd073e1SNeel Natu 2032bd073e1SNeel Natu /* 2042bd073e1SNeel Natu * Enable mevent to trigger when new characters are available 2052bd073e1SNeel Natu * on the tty fd. 2062bd073e1SNeel Natu */ 2072bd073e1SNeel Natu error = mevent_enable(sc->mev); 2082bd073e1SNeel Natu assert(error == 0); 2092bd073e1SNeel Natu } 210ea7f1c8cSNeel Natu } 211ea7f1c8cSNeel Natu 212ea7f1c8cSNeel Natu static int 2132bd073e1SNeel Natu rxfifo_available(struct uart_softc *sc) 214ea7f1c8cSNeel Natu { 2152bd073e1SNeel Natu struct fifo *fifo; 2162bd073e1SNeel Natu 2172bd073e1SNeel Natu fifo = &sc->rxfifo; 2182bd073e1SNeel Natu return (fifo->num < fifo->size); 2192bd073e1SNeel Natu } 2202bd073e1SNeel Natu 2212bd073e1SNeel Natu static int 2222bd073e1SNeel Natu rxfifo_putchar(struct uart_softc *sc, uint8_t ch) 2232bd073e1SNeel Natu { 2242bd073e1SNeel Natu struct fifo *fifo; 2252bd073e1SNeel Natu int error; 2262bd073e1SNeel Natu 2272bd073e1SNeel Natu fifo = &sc->rxfifo; 228ea7f1c8cSNeel Natu 229ea7f1c8cSNeel Natu if (fifo->num < fifo->size) { 230ea7f1c8cSNeel Natu fifo->buf[fifo->windex] = ch; 231ea7f1c8cSNeel Natu fifo->windex = (fifo->windex + 1) % fifo->size; 232ea7f1c8cSNeel Natu fifo->num++; 2332bd073e1SNeel Natu if (!rxfifo_available(sc)) { 2342bd073e1SNeel Natu if (sc->tty.opened) { 2352bd073e1SNeel Natu /* 2362bd073e1SNeel Natu * Disable mevent callback if the FIFO is full. 2372bd073e1SNeel Natu */ 2382bd073e1SNeel Natu error = mevent_disable(sc->mev); 2392bd073e1SNeel Natu assert(error == 0); 2402bd073e1SNeel Natu } 2412bd073e1SNeel Natu } 242ea7f1c8cSNeel Natu return (0); 243ea7f1c8cSNeel Natu } else 244ea7f1c8cSNeel Natu return (-1); 245ea7f1c8cSNeel Natu } 246ea7f1c8cSNeel Natu 247ea7f1c8cSNeel Natu static int 2482bd073e1SNeel Natu rxfifo_getchar(struct uart_softc *sc) 249ea7f1c8cSNeel Natu { 2502bd073e1SNeel Natu struct fifo *fifo; 2512bd073e1SNeel Natu int c, error, wasfull; 252ea7f1c8cSNeel Natu 2532bd073e1SNeel Natu wasfull = 0; 2542bd073e1SNeel Natu fifo = &sc->rxfifo; 255ea7f1c8cSNeel Natu if (fifo->num > 0) { 2562bd073e1SNeel Natu if (!rxfifo_available(sc)) 2572bd073e1SNeel Natu wasfull = 1; 258ea7f1c8cSNeel Natu c = fifo->buf[fifo->rindex]; 259ea7f1c8cSNeel Natu fifo->rindex = (fifo->rindex + 1) % fifo->size; 260ea7f1c8cSNeel Natu fifo->num--; 2612bd073e1SNeel Natu if (wasfull) { 2622bd073e1SNeel Natu if (sc->tty.opened) { 2632bd073e1SNeel Natu error = mevent_enable(sc->mev); 2642bd073e1SNeel Natu assert(error == 0); 2652bd073e1SNeel Natu } 2662bd073e1SNeel Natu } 267ea7f1c8cSNeel Natu return (c); 268ea7f1c8cSNeel Natu } else 269ea7f1c8cSNeel Natu return (-1); 270ea7f1c8cSNeel Natu } 271ea7f1c8cSNeel Natu 272ea7f1c8cSNeel Natu static int 2732bd073e1SNeel Natu rxfifo_numchars(struct uart_softc *sc) 274ea7f1c8cSNeel Natu { 2752bd073e1SNeel Natu struct fifo *fifo = &sc->rxfifo; 276ea7f1c8cSNeel Natu 277ea7f1c8cSNeel Natu return (fifo->num); 278ea7f1c8cSNeel Natu } 279ea7f1c8cSNeel Natu 280ea7f1c8cSNeel Natu static void 281ea7f1c8cSNeel Natu uart_opentty(struct uart_softc *sc) 282ea7f1c8cSNeel Natu { 283ea7f1c8cSNeel Natu 2846380102cSPeter Grehan ttyopen(&sc->tty); 285d6ef759eSMark Johnston sc->mev = mevent_add(sc->tty.rfd, EVF_READ, uart_drain, sc); 2862bd073e1SNeel Natu assert(sc->mev != NULL); 287ea7f1c8cSNeel Natu } 288ea7f1c8cSNeel Natu 289ccfe4c3fSNeel Natu static uint8_t 290ccfe4c3fSNeel Natu modem_status(uint8_t mcr) 291ccfe4c3fSNeel Natu { 292ccfe4c3fSNeel Natu uint8_t msr; 293ccfe4c3fSNeel Natu 294ccfe4c3fSNeel Natu if (mcr & MCR_LOOPBACK) { 295ccfe4c3fSNeel Natu /* 296ccfe4c3fSNeel Natu * In the loopback mode certain bits from the MCR are 297ccfe4c3fSNeel Natu * reflected back into MSR. 298ccfe4c3fSNeel Natu */ 299ccfe4c3fSNeel Natu msr = 0; 300ccfe4c3fSNeel Natu if (mcr & MCR_RTS) 301ccfe4c3fSNeel Natu msr |= MSR_CTS; 302ccfe4c3fSNeel Natu if (mcr & MCR_DTR) 303ccfe4c3fSNeel Natu msr |= MSR_DSR; 304ccfe4c3fSNeel Natu if (mcr & MCR_OUT1) 305ccfe4c3fSNeel Natu msr |= MSR_RI; 306ccfe4c3fSNeel Natu if (mcr & MCR_OUT2) 307ccfe4c3fSNeel Natu msr |= MSR_DCD; 308ccfe4c3fSNeel Natu } else { 309ccfe4c3fSNeel Natu /* 310ccfe4c3fSNeel Natu * Always assert DCD and DSR so tty open doesn't block 311ccfe4c3fSNeel Natu * even if CLOCAL is turned off. 312ccfe4c3fSNeel Natu */ 313ccfe4c3fSNeel Natu msr = MSR_DCD | MSR_DSR; 314ccfe4c3fSNeel Natu } 315ccfe4c3fSNeel Natu assert((msr & MSR_DELTA_MASK) == 0); 316ccfe4c3fSNeel Natu 317ccfe4c3fSNeel Natu return (msr); 318ccfe4c3fSNeel Natu } 319ccfe4c3fSNeel Natu 320ea7f1c8cSNeel Natu /* 321ea7f1c8cSNeel Natu * The IIR returns a prioritized interrupt reason: 322ea7f1c8cSNeel Natu * - receive data available 323ea7f1c8cSNeel Natu * - transmit holding register empty 324ea7f1c8cSNeel Natu * - modem status change 325ea7f1c8cSNeel Natu * 326ea7f1c8cSNeel Natu * Return an interrupt reason if one is available. 327ea7f1c8cSNeel Natu */ 328ea7f1c8cSNeel Natu static int 329ea7f1c8cSNeel Natu uart_intr_reason(struct uart_softc *sc) 330ea7f1c8cSNeel Natu { 331ea7f1c8cSNeel Natu 332ea7f1c8cSNeel Natu if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0) 333ea7f1c8cSNeel Natu return (IIR_RLS); 3342bd073e1SNeel Natu else if (rxfifo_numchars(sc) > 0 && (sc->ier & IER_ERXRDY) != 0) 335ea7f1c8cSNeel Natu return (IIR_RXTOUT); 336ea7f1c8cSNeel Natu else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0) 337ea7f1c8cSNeel Natu return (IIR_TXRDY); 338ea7f1c8cSNeel Natu else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0) 339ea7f1c8cSNeel Natu return (IIR_MLSC); 340ea7f1c8cSNeel Natu else 341ea7f1c8cSNeel Natu return (IIR_NOPEND); 342ea7f1c8cSNeel Natu } 343ea7f1c8cSNeel Natu 344ea7f1c8cSNeel Natu static void 345ea7f1c8cSNeel Natu uart_reset(struct uart_softc *sc) 346ea7f1c8cSNeel Natu { 347ea7f1c8cSNeel Natu uint16_t divisor; 348ea7f1c8cSNeel Natu 349ea7f1c8cSNeel Natu divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16; 350ea7f1c8cSNeel Natu sc->dll = divisor; 351ea7f1c8cSNeel Natu sc->dlh = divisor >> 16; 352ccfe4c3fSNeel Natu sc->msr = modem_status(sc->mcr); 353ea7f1c8cSNeel Natu 3542bd073e1SNeel Natu rxfifo_reset(sc, 1); /* no fifo until enabled by software */ 355ea7f1c8cSNeel Natu } 356ea7f1c8cSNeel Natu 357ea7f1c8cSNeel Natu /* 358ea7f1c8cSNeel Natu * Toggle the COM port's intr pin depending on whether or not we have an 359ea7f1c8cSNeel Natu * interrupt condition to report to the processor. 360ea7f1c8cSNeel Natu */ 361ea7f1c8cSNeel Natu static void 362ea7f1c8cSNeel Natu uart_toggle_intr(struct uart_softc *sc) 363ea7f1c8cSNeel Natu { 364ea7f1c8cSNeel Natu uint8_t intr_reason; 365ea7f1c8cSNeel Natu 366ea7f1c8cSNeel Natu intr_reason = uart_intr_reason(sc); 367ea7f1c8cSNeel Natu 368ea7f1c8cSNeel Natu if (intr_reason == IIR_NOPEND) 369ea7f1c8cSNeel Natu (*sc->intr_deassert)(sc->arg); 370ea7f1c8cSNeel Natu else 371ea7f1c8cSNeel Natu (*sc->intr_assert)(sc->arg); 372ea7f1c8cSNeel Natu } 373ea7f1c8cSNeel Natu 374ea7f1c8cSNeel Natu static void 375ea7f1c8cSNeel Natu uart_drain(int fd, enum ev_type ev, void *arg) 376ea7f1c8cSNeel Natu { 377ea7f1c8cSNeel Natu struct uart_softc *sc; 378ea7f1c8cSNeel Natu int ch; 379ea7f1c8cSNeel Natu 380ea7f1c8cSNeel Natu sc = arg; 381ea7f1c8cSNeel Natu 382d6ef759eSMark Johnston assert(fd == sc->tty.rfd); 383ea7f1c8cSNeel Natu assert(ev == EVF_READ); 384ea7f1c8cSNeel Natu 385ea7f1c8cSNeel Natu /* 386ea7f1c8cSNeel Natu * This routine is called in the context of the mevent thread 387ea7f1c8cSNeel Natu * to take out the softc lock to protect against concurrent 388ea7f1c8cSNeel Natu * access from a vCPU i/o exit 389ea7f1c8cSNeel Natu */ 390ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 391ea7f1c8cSNeel Natu 392ea7f1c8cSNeel Natu if ((sc->mcr & MCR_LOOPBACK) != 0) { 3936380102cSPeter Grehan (void) ttyread(&sc->tty); 394ea7f1c8cSNeel Natu } else { 3952bd073e1SNeel Natu while (rxfifo_available(sc) && 3966380102cSPeter Grehan ((ch = ttyread(&sc->tty)) != -1)) { 3972bd073e1SNeel Natu rxfifo_putchar(sc, ch); 398ea7f1c8cSNeel Natu } 399ea7f1c8cSNeel Natu uart_toggle_intr(sc); 400ea7f1c8cSNeel Natu } 401ea7f1c8cSNeel Natu 402ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 403ea7f1c8cSNeel Natu } 404ea7f1c8cSNeel Natu 405ea7f1c8cSNeel Natu void 406ea7f1c8cSNeel Natu uart_write(struct uart_softc *sc, int offset, uint8_t value) 407ea7f1c8cSNeel Natu { 408ea7f1c8cSNeel Natu int fifosz; 409ea7f1c8cSNeel Natu uint8_t msr; 410ea7f1c8cSNeel Natu 411ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 412ea7f1c8cSNeel Natu 413ea7f1c8cSNeel Natu /* 414ea7f1c8cSNeel Natu * Take care of the special case DLAB accesses first 415ea7f1c8cSNeel Natu */ 416ea7f1c8cSNeel Natu if ((sc->lcr & LCR_DLAB) != 0) { 417ea7f1c8cSNeel Natu if (offset == REG_DLL) { 418ea7f1c8cSNeel Natu sc->dll = value; 419ea7f1c8cSNeel Natu goto done; 420ea7f1c8cSNeel Natu } 421ea7f1c8cSNeel Natu 422ea7f1c8cSNeel Natu if (offset == REG_DLH) { 423ea7f1c8cSNeel Natu sc->dlh = value; 424ea7f1c8cSNeel Natu goto done; 425ea7f1c8cSNeel Natu } 426ea7f1c8cSNeel Natu } 427ea7f1c8cSNeel Natu 428ea7f1c8cSNeel Natu switch (offset) { 429ea7f1c8cSNeel Natu case REG_DATA: 430ea7f1c8cSNeel Natu if (sc->mcr & MCR_LOOPBACK) { 4312bd073e1SNeel Natu if (rxfifo_putchar(sc, value) != 0) 432ea7f1c8cSNeel Natu sc->lsr |= LSR_OE; 4336380102cSPeter Grehan } else if (sc->tty.opened) { 4346380102cSPeter Grehan ttywrite(&sc->tty, value); 435ea7f1c8cSNeel Natu } /* else drop on floor */ 436ea7f1c8cSNeel Natu sc->thre_int_pending = true; 437ea7f1c8cSNeel Natu break; 438ea7f1c8cSNeel Natu case REG_IER: 43955792380SConrad Meyer /* Set pending when IER_ETXRDY is raised (edge-triggered). */ 44055792380SConrad Meyer if ((sc->ier & IER_ETXRDY) == 0 && (value & IER_ETXRDY) != 0) 44155792380SConrad Meyer sc->thre_int_pending = true; 442ea7f1c8cSNeel Natu /* 443ea7f1c8cSNeel Natu * Apply mask so that bits 4-7 are 0 444ea7f1c8cSNeel Natu * Also enables bits 0-3 only if they're 1 445ea7f1c8cSNeel Natu */ 446ea7f1c8cSNeel Natu sc->ier = value & 0x0F; 447ea7f1c8cSNeel Natu break; 448ea7f1c8cSNeel Natu case REG_FCR: 449ea7f1c8cSNeel Natu /* 450ea7f1c8cSNeel Natu * When moving from FIFO and 16450 mode and vice versa, 451ea7f1c8cSNeel Natu * the FIFO contents are reset. 452ea7f1c8cSNeel Natu */ 453ea7f1c8cSNeel Natu if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) { 454ea7f1c8cSNeel Natu fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1; 4552bd073e1SNeel Natu rxfifo_reset(sc, fifosz); 456ea7f1c8cSNeel Natu } 457ea7f1c8cSNeel Natu 458ea7f1c8cSNeel Natu /* 459ea7f1c8cSNeel Natu * The FCR_ENABLE bit must be '1' for the programming 460ea7f1c8cSNeel Natu * of other FCR bits to be effective. 461ea7f1c8cSNeel Natu */ 462ea7f1c8cSNeel Natu if ((value & FCR_ENABLE) == 0) { 463ea7f1c8cSNeel Natu sc->fcr = 0; 464ea7f1c8cSNeel Natu } else { 465ea7f1c8cSNeel Natu if ((value & FCR_RCV_RST) != 0) 4662bd073e1SNeel Natu rxfifo_reset(sc, FIFOSZ); 467ea7f1c8cSNeel Natu 468ea7f1c8cSNeel Natu sc->fcr = value & 469ea7f1c8cSNeel Natu (FCR_ENABLE | FCR_DMA | FCR_RX_MASK); 470ea7f1c8cSNeel Natu } 471ea7f1c8cSNeel Natu break; 472ea7f1c8cSNeel Natu case REG_LCR: 473ea7f1c8cSNeel Natu sc->lcr = value; 474ea7f1c8cSNeel Natu break; 475ea7f1c8cSNeel Natu case REG_MCR: 476ea7f1c8cSNeel Natu /* Apply mask so that bits 5-7 are 0 */ 477ea7f1c8cSNeel Natu sc->mcr = value & 0x1F; 478ccfe4c3fSNeel Natu msr = modem_status(sc->mcr); 479ea7f1c8cSNeel Natu 480ea7f1c8cSNeel Natu /* 481ea7f1c8cSNeel Natu * Detect if there has been any change between the 482ea7f1c8cSNeel Natu * previous and the new value of MSR. If there is 483ea7f1c8cSNeel Natu * then assert the appropriate MSR delta bit. 484ea7f1c8cSNeel Natu */ 485ea7f1c8cSNeel Natu if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS)) 486ea7f1c8cSNeel Natu sc->msr |= MSR_DCTS; 487ea7f1c8cSNeel Natu if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR)) 488ea7f1c8cSNeel Natu sc->msr |= MSR_DDSR; 489ea7f1c8cSNeel Natu if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD)) 490ea7f1c8cSNeel Natu sc->msr |= MSR_DDCD; 491ea7f1c8cSNeel Natu if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0) 492ea7f1c8cSNeel Natu sc->msr |= MSR_TERI; 493ea7f1c8cSNeel Natu 494ea7f1c8cSNeel Natu /* 495ea7f1c8cSNeel Natu * Update the value of MSR while retaining the delta 496ea7f1c8cSNeel Natu * bits. 497ea7f1c8cSNeel Natu */ 498ea7f1c8cSNeel Natu sc->msr &= MSR_DELTA_MASK; 499ea7f1c8cSNeel Natu sc->msr |= msr; 500ea7f1c8cSNeel Natu break; 501ea7f1c8cSNeel Natu case REG_LSR: 502ea7f1c8cSNeel Natu /* 503ea7f1c8cSNeel Natu * Line status register is not meant to be written to 504ea7f1c8cSNeel Natu * during normal operation. 505ea7f1c8cSNeel Natu */ 506ea7f1c8cSNeel Natu break; 507ea7f1c8cSNeel Natu case REG_MSR: 508ea7f1c8cSNeel Natu /* 509ea7f1c8cSNeel Natu * As far as I can tell MSR is a read-only register. 510ea7f1c8cSNeel Natu */ 511ea7f1c8cSNeel Natu break; 512ea7f1c8cSNeel Natu case REG_SCR: 513ea7f1c8cSNeel Natu sc->scr = value; 514ea7f1c8cSNeel Natu break; 515ea7f1c8cSNeel Natu default: 516ea7f1c8cSNeel Natu break; 517ea7f1c8cSNeel Natu } 518ea7f1c8cSNeel Natu 519ea7f1c8cSNeel Natu done: 520ea7f1c8cSNeel Natu uart_toggle_intr(sc); 521ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 522ea7f1c8cSNeel Natu } 523ea7f1c8cSNeel Natu 524ea7f1c8cSNeel Natu uint8_t 525ea7f1c8cSNeel Natu uart_read(struct uart_softc *sc, int offset) 526ea7f1c8cSNeel Natu { 527ea7f1c8cSNeel Natu uint8_t iir, intr_reason, reg; 528ea7f1c8cSNeel Natu 529ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 530ea7f1c8cSNeel Natu 531ea7f1c8cSNeel Natu /* 532ea7f1c8cSNeel Natu * Take care of the special case DLAB accesses first 533ea7f1c8cSNeel Natu */ 534ea7f1c8cSNeel Natu if ((sc->lcr & LCR_DLAB) != 0) { 535ea7f1c8cSNeel Natu if (offset == REG_DLL) { 536ea7f1c8cSNeel Natu reg = sc->dll; 537ea7f1c8cSNeel Natu goto done; 538ea7f1c8cSNeel Natu } 539ea7f1c8cSNeel Natu 540ea7f1c8cSNeel Natu if (offset == REG_DLH) { 541ea7f1c8cSNeel Natu reg = sc->dlh; 542ea7f1c8cSNeel Natu goto done; 543ea7f1c8cSNeel Natu } 544ea7f1c8cSNeel Natu } 545ea7f1c8cSNeel Natu 546ea7f1c8cSNeel Natu switch (offset) { 547ea7f1c8cSNeel Natu case REG_DATA: 5482bd073e1SNeel Natu reg = rxfifo_getchar(sc); 549ea7f1c8cSNeel Natu break; 550ea7f1c8cSNeel Natu case REG_IER: 551ea7f1c8cSNeel Natu reg = sc->ier; 552ea7f1c8cSNeel Natu break; 553ea7f1c8cSNeel Natu case REG_IIR: 554ea7f1c8cSNeel Natu iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0; 555ea7f1c8cSNeel Natu 556ea7f1c8cSNeel Natu intr_reason = uart_intr_reason(sc); 557ea7f1c8cSNeel Natu 558ea7f1c8cSNeel Natu /* 559ea7f1c8cSNeel Natu * Deal with side effects of reading the IIR register 560ea7f1c8cSNeel Natu */ 561ea7f1c8cSNeel Natu if (intr_reason == IIR_TXRDY) 562ea7f1c8cSNeel Natu sc->thre_int_pending = false; 563ea7f1c8cSNeel Natu 564ea7f1c8cSNeel Natu iir |= intr_reason; 565ea7f1c8cSNeel Natu 566ea7f1c8cSNeel Natu reg = iir; 567ea7f1c8cSNeel Natu break; 568ea7f1c8cSNeel Natu case REG_LCR: 569ea7f1c8cSNeel Natu reg = sc->lcr; 570ea7f1c8cSNeel Natu break; 571ea7f1c8cSNeel Natu case REG_MCR: 572ea7f1c8cSNeel Natu reg = sc->mcr; 573ea7f1c8cSNeel Natu break; 574ea7f1c8cSNeel Natu case REG_LSR: 575ea7f1c8cSNeel Natu /* Transmitter is always ready for more data */ 576ea7f1c8cSNeel Natu sc->lsr |= LSR_TEMT | LSR_THRE; 577ea7f1c8cSNeel Natu 578ea7f1c8cSNeel Natu /* Check for new receive data */ 5792bd073e1SNeel Natu if (rxfifo_numchars(sc) > 0) 580ea7f1c8cSNeel Natu sc->lsr |= LSR_RXRDY; 581ea7f1c8cSNeel Natu else 582ea7f1c8cSNeel Natu sc->lsr &= ~LSR_RXRDY; 583ea7f1c8cSNeel Natu 584ea7f1c8cSNeel Natu reg = sc->lsr; 585ea7f1c8cSNeel Natu 586ea7f1c8cSNeel Natu /* The LSR_OE bit is cleared on LSR read */ 587ea7f1c8cSNeel Natu sc->lsr &= ~LSR_OE; 588ea7f1c8cSNeel Natu break; 589ea7f1c8cSNeel Natu case REG_MSR: 590ea7f1c8cSNeel Natu /* 591ea7f1c8cSNeel Natu * MSR delta bits are cleared on read 592ea7f1c8cSNeel Natu */ 593ea7f1c8cSNeel Natu reg = sc->msr; 594ea7f1c8cSNeel Natu sc->msr &= ~MSR_DELTA_MASK; 595ea7f1c8cSNeel Natu break; 596ea7f1c8cSNeel Natu case REG_SCR: 597ea7f1c8cSNeel Natu reg = sc->scr; 598ea7f1c8cSNeel Natu break; 599ea7f1c8cSNeel Natu default: 600ea7f1c8cSNeel Natu reg = 0xFF; 601ea7f1c8cSNeel Natu break; 602ea7f1c8cSNeel Natu } 603ea7f1c8cSNeel Natu 604ea7f1c8cSNeel Natu done: 605ea7f1c8cSNeel Natu uart_toggle_intr(sc); 606ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 607ea7f1c8cSNeel Natu 608ea7f1c8cSNeel Natu return (reg); 609ea7f1c8cSNeel Natu } 610ea7f1c8cSNeel Natu 611ea7f1c8cSNeel Natu int 612ea7f1c8cSNeel Natu uart_legacy_alloc(int which, int *baseaddr, int *irq) 613ea7f1c8cSNeel Natu { 614ea7f1c8cSNeel Natu 615ed721684SMark Johnston if (which < 0 || which >= (int)UART_NLDEVS || uart_lres[which].inuse) 616ea7f1c8cSNeel Natu return (-1); 617ea7f1c8cSNeel Natu 618ea7f1c8cSNeel Natu uart_lres[which].inuse = true; 619ea7f1c8cSNeel Natu *baseaddr = uart_lres[which].baseaddr; 620ea7f1c8cSNeel Natu *irq = uart_lres[which].irq; 621ea7f1c8cSNeel Natu 622ea7f1c8cSNeel Natu return (0); 623ea7f1c8cSNeel Natu } 624ea7f1c8cSNeel Natu 625ea7f1c8cSNeel Natu struct uart_softc * 626ea7f1c8cSNeel Natu uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert, 627ea7f1c8cSNeel Natu void *arg) 628ea7f1c8cSNeel Natu { 629ea7f1c8cSNeel Natu struct uart_softc *sc; 630ea7f1c8cSNeel Natu 631994f858aSXin LI sc = calloc(1, sizeof(struct uart_softc)); 632ea7f1c8cSNeel Natu 633ea7f1c8cSNeel Natu sc->arg = arg; 634ea7f1c8cSNeel Natu sc->intr_assert = intr_assert; 635ea7f1c8cSNeel Natu sc->intr_deassert = intr_deassert; 636ea7f1c8cSNeel Natu 637ea7f1c8cSNeel Natu pthread_mutex_init(&sc->mtx, NULL); 638ea7f1c8cSNeel Natu 639ea7f1c8cSNeel Natu uart_reset(sc); 640ea7f1c8cSNeel Natu 641ea7f1c8cSNeel Natu return (sc); 642ea7f1c8cSNeel Natu } 643ea7f1c8cSNeel Natu 6446380102cSPeter Grehan static int 645d6ef759eSMark Johnston uart_stdio_backend(struct uart_softc *sc) 6466380102cSPeter Grehan { 647d6ef759eSMark Johnston #ifndef WITHOUT_CAPSICUM 648d6ef759eSMark Johnston cap_rights_t rights; 649d6ef759eSMark Johnston cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; 650d6ef759eSMark Johnston #endif 6516380102cSPeter Grehan 652d6ef759eSMark Johnston if (uart_stdio) 653d6ef759eSMark Johnston return (-1); 6546380102cSPeter Grehan 655d6ef759eSMark Johnston sc->tty.rfd = STDIN_FILENO; 656d6ef759eSMark Johnston sc->tty.wfd = STDOUT_FILENO; 6576380102cSPeter Grehan sc->tty.opened = true; 658d6ef759eSMark Johnston 659d6ef759eSMark Johnston if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0) 660d6ef759eSMark Johnston return (-1); 661d6ef759eSMark Johnston if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0) 662d6ef759eSMark Johnston return (-1); 663d6ef759eSMark Johnston 664d6ef759eSMark Johnston #ifndef WITHOUT_CAPSICUM 665d6ef759eSMark Johnston cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ); 666d6ef759eSMark Johnston if (caph_rights_limit(sc->tty.rfd, &rights) == -1) 667d6ef759eSMark Johnston errx(EX_OSERR, "Unable to apply rights for sandbox"); 668d6ef759eSMark Johnston if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1) 669d6ef759eSMark Johnston errx(EX_OSERR, "Unable to apply rights for sandbox"); 670d6ef759eSMark Johnston #endif 671d6ef759eSMark Johnston 672d6ef759eSMark Johnston uart_stdio = true; 673d6ef759eSMark Johnston 674d6ef759eSMark Johnston return (0); 6756380102cSPeter Grehan } 6766380102cSPeter Grehan 677d6ef759eSMark Johnston static int 678621b5090SJohn Baldwin uart_tty_backend(struct uart_softc *sc, const char *path) 679d6ef759eSMark Johnston { 680d6ef759eSMark Johnston #ifndef WITHOUT_CAPSICUM 681d6ef759eSMark Johnston cap_rights_t rights; 682d6ef759eSMark Johnston cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; 683d6ef759eSMark Johnston #endif 684d6ef759eSMark Johnston int fd; 685d6ef759eSMark Johnston 686621b5090SJohn Baldwin fd = open(path, O_RDWR | O_NONBLOCK); 687ae2c5fe3SSean Chittenden if (fd < 0) 688d6ef759eSMark Johnston return (-1); 689d6ef759eSMark Johnston 690ae2c5fe3SSean Chittenden if (!isatty(fd)) { 691ae2c5fe3SSean Chittenden close(fd); 692ae2c5fe3SSean Chittenden return (-1); 693ae2c5fe3SSean Chittenden } 694ae2c5fe3SSean Chittenden 695d6ef759eSMark Johnston sc->tty.rfd = sc->tty.wfd = fd; 696d6ef759eSMark Johnston sc->tty.opened = true; 697d6ef759eSMark Johnston 698d6ef759eSMark Johnston #ifndef WITHOUT_CAPSICUM 699d6ef759eSMark Johnston cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE); 700d6ef759eSMark Johnston if (caph_rights_limit(fd, &rights) == -1) 701d6ef759eSMark Johnston errx(EX_OSERR, "Unable to apply rights for sandbox"); 702d6ef759eSMark Johnston if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1) 703d6ef759eSMark Johnston errx(EX_OSERR, "Unable to apply rights for sandbox"); 704d6ef759eSMark Johnston #endif 705d6ef759eSMark Johnston 706d6ef759eSMark Johnston return (0); 7076380102cSPeter Grehan } 7086380102cSPeter Grehan 709ea7f1c8cSNeel Natu int 710621b5090SJohn Baldwin uart_set_backend(struct uart_softc *sc, const char *device) 711ea7f1c8cSNeel Natu { 7126380102cSPeter Grehan int retval; 7136380102cSPeter Grehan 714621b5090SJohn Baldwin if (device == NULL) 715ea7f1c8cSNeel Natu return (0); 716ea7f1c8cSNeel Natu 717621b5090SJohn Baldwin if (strcmp("stdio", device) == 0) 718d6ef759eSMark Johnston retval = uart_stdio_backend(sc); 719d6ef759eSMark Johnston else 720621b5090SJohn Baldwin retval = uart_tty_backend(sc, device); 7212bd073e1SNeel Natu if (retval == 0) 7226380102cSPeter Grehan uart_opentty(sc); 7236380102cSPeter Grehan 7246380102cSPeter Grehan return (retval); 725ea7f1c8cSNeel Natu } 726483d953aSJohn Baldwin 727483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 728483d953aSJohn Baldwin int 729483d953aSJohn Baldwin uart_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta) 730483d953aSJohn Baldwin { 731483d953aSJohn Baldwin int ret; 732483d953aSJohn Baldwin 733483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->data, meta, ret, done); 734483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->ier, meta, ret, done); 735483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->lcr, meta, ret, done); 736483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->mcr, meta, ret, done); 737483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->lsr, meta, ret, done); 738483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->msr, meta, ret, done); 739483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->fcr, meta, ret, done); 740483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->scr, meta, ret, done); 741483d953aSJohn Baldwin 742483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->dll, meta, ret, done); 743483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->dlh, meta, ret, done); 744483d953aSJohn Baldwin 745483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done); 746483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done); 747483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done); 748483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done); 749483d953aSJohn Baldwin SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf), 750483d953aSJohn Baldwin meta, ret, done); 751483d953aSJohn Baldwin 752483d953aSJohn Baldwin sc->thre_int_pending = 1; 753483d953aSJohn Baldwin 754483d953aSJohn Baldwin done: 755483d953aSJohn Baldwin return (ret); 756483d953aSJohn Baldwin } 757483d953aSJohn Baldwin #endif 758