Changeset View
Changeset View
Standalone View
Standalone View
head/usr.sbin/bhyve/uart_emul.c
Show First 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | struct fifo { | ||||
int rindex; /* index to read from */ | int rindex; /* index to read from */ | ||||
int windex; /* index to write to */ | int windex; /* index to write to */ | ||||
int num; /* number of characters in the fifo */ | int num; /* number of characters in the fifo */ | ||||
int size; /* size of the fifo */ | int size; /* size of the fifo */ | ||||
}; | }; | ||||
struct ttyfd { | struct ttyfd { | ||||
bool opened; | bool opened; | ||||
int fd; /* tty device file descriptor */ | int rfd; /* fd for reading */ | ||||
struct termios tio_orig, tio_new; /* I/O Terminals */ | int wfd; /* fd for writing, may be == rfd */ | ||||
}; | }; | ||||
struct uart_softc { | struct uart_softc { | ||||
pthread_mutex_t mtx; /* protects all softc elements */ | pthread_mutex_t mtx; /* protects all softc elements */ | ||||
uint8_t data; /* Data register (R/W) */ | uint8_t data; /* Data register (R/W) */ | ||||
uint8_t ier; /* Interrupt enable register (R/W) */ | uint8_t ier; /* Interrupt enable register (R/W) */ | ||||
uint8_t lcr; /* Line control register (R/W) */ | uint8_t lcr; /* Line control register (R/W) */ | ||||
uint8_t mcr; /* Modem control register (R/W) */ | uint8_t mcr; /* Modem control register (R/W) */ | ||||
Show All 23 Lines | |||||
{ | { | ||||
tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); | tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); | ||||
} | } | ||||
static void | static void | ||||
ttyopen(struct ttyfd *tf) | ttyopen(struct ttyfd *tf) | ||||
{ | { | ||||
struct termios orig, new; | |||||
tcgetattr(tf->fd, &tf->tio_orig); | tcgetattr(tf->rfd, &orig); | ||||
new = orig; | |||||
tf->tio_new = tf->tio_orig; | cfmakeraw(&new); | ||||
cfmakeraw(&tf->tio_new); | new.c_cflag |= CLOCAL; | ||||
tf->tio_new.c_cflag |= CLOCAL; | tcsetattr(tf->rfd, TCSANOW, &new); | ||||
tcsetattr(tf->fd, TCSANOW, &tf->tio_new); | if (uart_stdio) { | ||||
tio_stdio_orig = orig; | |||||
if (tf->fd == STDIN_FILENO) { | |||||
tio_stdio_orig = tf->tio_orig; | |||||
atexit(ttyclose); | atexit(ttyclose); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
ttyread(struct ttyfd *tf) | ttyread(struct ttyfd *tf) | ||||
{ | { | ||||
unsigned char rb; | unsigned char rb; | ||||
if (read(tf->fd, &rb, 1) == 1) | if (read(tf->rfd, &rb, 1) == 1) | ||||
return (rb); | return (rb); | ||||
else | else | ||||
return (-1); | return (-1); | ||||
} | } | ||||
static void | static void | ||||
ttywrite(struct ttyfd *tf, unsigned char wb) | ttywrite(struct ttyfd *tf, unsigned char wb) | ||||
{ | { | ||||
(void)write(tf->fd, &wb, 1); | (void)write(tf->wfd, &wb, 1); | ||||
} | } | ||||
static void | static void | ||||
rxfifo_reset(struct uart_softc *sc, int size) | rxfifo_reset(struct uart_softc *sc, int size) | ||||
{ | { | ||||
char flushbuf[32]; | char flushbuf[32]; | ||||
struct fifo *fifo; | struct fifo *fifo; | ||||
ssize_t nread; | ssize_t nread; | ||||
int error; | int error; | ||||
fifo = &sc->rxfifo; | fifo = &sc->rxfifo; | ||||
bzero(fifo, sizeof(struct fifo)); | bzero(fifo, sizeof(struct fifo)); | ||||
fifo->size = size; | fifo->size = size; | ||||
if (sc->tty.opened) { | if (sc->tty.opened) { | ||||
/* | /* | ||||
* Flush any unread input from the tty buffer. | * Flush any unread input from the tty buffer. | ||||
*/ | */ | ||||
while (1) { | while (1) { | ||||
nread = read(sc->tty.fd, flushbuf, sizeof(flushbuf)); | nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf)); | ||||
if (nread != sizeof(flushbuf)) | if (nread != sizeof(flushbuf)) | ||||
break; | break; | ||||
} | } | ||||
/* | /* | ||||
* Enable mevent to trigger when new characters are available | * Enable mevent to trigger when new characters are available | ||||
* on the tty fd. | * on the tty fd. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | rxfifo_numchars(struct uart_softc *sc) | ||||
return (fifo->num); | return (fifo->num); | ||||
} | } | ||||
static void | static void | ||||
uart_opentty(struct uart_softc *sc) | uart_opentty(struct uart_softc *sc) | ||||
{ | { | ||||
ttyopen(&sc->tty); | ttyopen(&sc->tty); | ||||
sc->mev = mevent_add(sc->tty.fd, EVF_READ, uart_drain, sc); | sc->mev = mevent_add(sc->tty.rfd, EVF_READ, uart_drain, sc); | ||||
assert(sc->mev != NULL); | assert(sc->mev != NULL); | ||||
} | } | ||||
static uint8_t | static uint8_t | ||||
modem_status(uint8_t mcr) | modem_status(uint8_t mcr) | ||||
{ | { | ||||
uint8_t msr; | uint8_t msr; | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
uart_drain(int fd, enum ev_type ev, void *arg) | uart_drain(int fd, enum ev_type ev, void *arg) | ||||
{ | { | ||||
struct uart_softc *sc; | struct uart_softc *sc; | ||||
int ch; | int ch; | ||||
sc = arg; | sc = arg; | ||||
assert(fd == sc->tty.fd); | assert(fd == sc->tty.rfd); | ||||
assert(ev == EVF_READ); | assert(ev == EVF_READ); | ||||
/* | /* | ||||
* This routine is called in the context of the mevent thread | * This routine is called in the context of the mevent thread | ||||
* to take out the softc lock to protect against concurrent | * to take out the softc lock to protect against concurrent | ||||
* access from a vCPU i/o exit | * access from a vCPU i/o exit | ||||
*/ | */ | ||||
pthread_mutex_lock(&sc->mtx); | pthread_mutex_lock(&sc->mtx); | ||||
▲ Show 20 Lines • Show All 246 Lines • ▼ Show 20 Lines | uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert, | ||||
pthread_mutex_init(&sc->mtx, NULL); | pthread_mutex_init(&sc->mtx, NULL); | ||||
uart_reset(sc); | uart_reset(sc); | ||||
return (sc); | return (sc); | ||||
} | } | ||||
static int | static int | ||||
uart_tty_backend(struct uart_softc *sc, const char *opts) | uart_stdio_backend(struct uart_softc *sc) | ||||
{ | { | ||||
int fd; | #ifndef WITHOUT_CAPSICUM | ||||
int retval; | cap_rights_t rights; | ||||
cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; | |||||
#endif | |||||
retval = -1; | if (uart_stdio) | ||||
return (-1); | |||||
fd = open(opts, O_RDWR | O_NONBLOCK); | sc->tty.rfd = STDIN_FILENO; | ||||
if (fd > 0 && isatty(fd)) { | sc->tty.wfd = STDOUT_FILENO; | ||||
sc->tty.fd = fd; | |||||
sc->tty.opened = true; | sc->tty.opened = true; | ||||
retval = 0; | |||||
} | |||||
return (retval); | if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0) | ||||
return (-1); | |||||
if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0) | |||||
return (-1); | |||||
#ifndef WITHOUT_CAPSICUM | |||||
cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ); | |||||
if (caph_rights_limit(sc->tty.rfd, &rights) == -1) | |||||
errx(EX_OSERR, "Unable to apply rights for sandbox"); | |||||
if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1) | |||||
errx(EX_OSERR, "Unable to apply rights for sandbox"); | |||||
#endif | |||||
uart_stdio = true; | |||||
return (0); | |||||
} | } | ||||
int | static int | ||||
uart_set_backend(struct uart_softc *sc, const char *opts) | uart_tty_backend(struct uart_softc *sc, const char *opts) | ||||
{ | { | ||||
int retval; | |||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
cap_rights_t rights; | cap_rights_t rights; | ||||
cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; | cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; | ||||
#endif | #endif | ||||
int fd; | |||||
retval = -1; | fd = open(opts, O_RDWR | O_NONBLOCK); | ||||
if (fd < 0 || !isatty(fd)) | |||||
return (-1); | |||||
if (opts == NULL) | sc->tty.rfd = sc->tty.wfd = fd; | ||||
return (0); | |||||
if (strcmp("stdio", opts) == 0) { | |||||
if (!uart_stdio) { | |||||
sc->tty.fd = STDIN_FILENO; | |||||
sc->tty.opened = true; | sc->tty.opened = true; | ||||
uart_stdio = true; | |||||
retval = 0; | |||||
} | |||||
} else if (uart_tty_backend(sc, opts) == 0) { | |||||
retval = 0; | |||||
} | |||||
/* Make the backend file descriptor non-blocking */ | |||||
if (retval == 0) | |||||
retval = fcntl(sc->tty.fd, F_SETFL, O_NONBLOCK); | |||||
if (retval == 0) { | |||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, | cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE); | ||||
CAP_WRITE); | if (caph_rights_limit(fd, &rights) == -1) | ||||
if (caph_rights_limit(sc->tty.fd, &rights) == -1) | |||||
errx(EX_OSERR, "Unable to apply rights for sandbox"); | errx(EX_OSERR, "Unable to apply rights for sandbox"); | ||||
if (caph_ioctls_limit(sc->tty.fd, cmds, nitems(cmds)) == -1) | if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1) | ||||
errx(EX_OSERR, "Unable to apply rights for sandbox"); | errx(EX_OSERR, "Unable to apply rights for sandbox"); | ||||
if (!uart_stdio) { | |||||
if (caph_limit_stdin() == -1) | |||||
errx(EX_OSERR, | |||||
"Unable to apply rights for sandbox"); | |||||
} | |||||
#endif | #endif | ||||
uart_opentty(sc); | |||||
return (0); | |||||
} | } | ||||
int | |||||
uart_set_backend(struct uart_softc *sc, const char *opts) | |||||
{ | |||||
int retval; | |||||
if (opts == NULL) | |||||
return (0); | |||||
if (strcmp("stdio", opts) == 0) | |||||
retval = uart_stdio_backend(sc); | |||||
else | |||||
retval = uart_tty_backend(sc, opts); | |||||
if (retval == 0) | |||||
uart_opentty(sc); | |||||
return (retval); | return (retval); | ||||
} | } |