2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2012 NetApp, Inc.
5 * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * This file and its contents are supplied under the terms of the
34 * Common Development and Distribution License ("CDDL"), version 1.0.
35 * You may only use this file in accordance with the terms of version
38 * A full copy of the text of the CDDL should have accompanied this
39 * source. A copy of the CDDL is also available via the Internet at
40 * http://www.illumos.org/license/CDDL.
42 * Copyright 2015 Pluribus Networks Inc.
43 * Copyright 2018 Joyent, Inc.
46 #include <sys/cdefs.h>
47 __FBSDID("$FreeBSD$");
49 #include <sys/types.h>
50 #include <dev/ic/ns16550.h>
51 #ifndef WITHOUT_CAPSICUM
52 #include <sys/capsicum.h>
53 #include <capsicum_helpers.h>
69 #include <sys/socket.h>
73 #include "uart_emul.h"
76 #define COM1_BASE 0x3F8
78 #define COM2_BASE 0x2F8
80 #define COM3_BASE 0x3E8
82 #define COM4_BASE 0x2E8
85 #define DEFAULT_RCLK 1843200
86 #define DEFAULT_BAUD 9600
88 #define FCR_RX_MASK 0xC0
93 #define MSR_DELTA_MASK 0x0f
96 #define REG_SCR com_scr
101 static bool uart_stdio
; /* stdio in use for i/o */
102 static struct termios tio_stdio_orig
;
109 { COM1_BASE
, COM1_IRQ
, false},
110 { COM2_BASE
, COM2_IRQ
, false},
111 { COM3_BASE
, COM3_IRQ
, false},
112 { COM4_BASE
, COM4_IRQ
, false},
115 #define UART_NLDEVS (sizeof(uart_lres) / sizeof(uart_lres[0]))
119 int rindex
; /* index to read from */
120 int windex
; /* index to write to */
121 int num
; /* number of characters in the fifo */
122 int size
; /* size of the fifo */
127 int rfd
; /* fd for reading */
128 int wfd
; /* fd for writing, may be == rfd */
132 pthread_mutex_t mtx
; /* protects all softc elements */
133 uint8_t data
; /* Data register (R/W) */
134 uint8_t ier
; /* Interrupt enable register (R/W) */
135 uint8_t lcr
; /* Line control register (R/W) */
136 uint8_t mcr
; /* Modem control register (R/W) */
137 uint8_t lsr
; /* Line status register (R/W) */
138 uint8_t msr
; /* Modem status register (R/W) */
139 uint8_t fcr
; /* FIFO control register (W) */
140 uint8_t scr
; /* Scratch register (R/W) */
142 uint8_t dll
; /* Baudrate divisor latch LSB */
143 uint8_t dlh
; /* Baudrate divisor latch MSB */
152 int clifd
; /* console client unix domain socket */
153 int servfd
; /* console server unix domain socket */
154 struct mevent
*servmev
; /* mevent for server socket */
158 bool thre_int_pending
; /* THRE interrupt pending */
161 uart_intr_func_t intr_assert
;
162 uart_intr_func_t intr_deassert
;
165 static void uart_drain(int fd
, enum ev_type ev
, void *arg
);
171 tcsetattr(STDIN_FILENO
, TCSANOW
, &tio_stdio_orig
);
175 ttyopen(struct ttyfd
*tf
)
177 struct termios orig
, new;
179 tcgetattr(tf
->rfd
, &orig
);
182 new.c_cflag
|= CLOCAL
;
183 tcsetattr(tf
->rfd
, TCSANOW
, &new);
185 tio_stdio_orig
= orig
;
192 ttyread(struct ttyfd
*tf
)
196 if (read(tf
->rfd
, &rb
, 1) == 1)
203 ttywrite(struct ttyfd
*tf
, unsigned char wb
)
206 (void)write(tf
->wfd
, &wb
, 1);
211 sockwrite(struct uart_softc
*sc
, unsigned char wb
)
213 (void) write(sc
->usc_sock
.clifd
, &wb
, 1);
218 rxfifo_reset(struct uart_softc
*sc
, int size
)
226 bzero(fifo
, sizeof(struct fifo
));
229 if (sc
->tty
.opened
) {
231 * Flush any unread input from the tty buffer.
234 nread
= read(sc
->tty
.rfd
, flushbuf
, sizeof(flushbuf
));
235 if (nread
!= sizeof(flushbuf
))
240 * Enable mevent to trigger when new characters are available
243 error
= mevent_enable(sc
->mev
);
247 if (sc
->sock
&& sc
->usc_sock
.clifd
!= -1) {
248 /* Flush any unread input from the socket buffer. */
250 nread
= read(sc
->usc_sock
.clifd
, flushbuf
,
252 } while (nread
== sizeof (flushbuf
));
254 /* Enable mevent to trigger when new data available on sock */
255 error
= mevent_enable(sc
->mev
);
258 #endif /* __FreeBSD__ */
262 rxfifo_available(struct uart_softc
*sc
)
267 return (fifo
->num
< fifo
->size
);
271 rxfifo_putchar(struct uart_softc
*sc
, uint8_t ch
)
278 if (fifo
->num
< fifo
->size
) {
279 fifo
->buf
[fifo
->windex
] = ch
;
280 fifo
->windex
= (fifo
->windex
+ 1) % fifo
->size
;
282 if (!rxfifo_available(sc
)) {
283 if (sc
->tty
.opened
) {
285 * Disable mevent callback if the FIFO is full.
287 error
= mevent_disable(sc
->mev
);
291 if (sc
->sock
&& sc
->usc_sock
.clifd
!= -1) {
293 * Disable mevent callback if the FIFO is full.
295 error
= mevent_disable(sc
->mev
);
298 #endif /* __FreeBSD__ */
306 rxfifo_getchar(struct uart_softc
*sc
)
309 int c
, error
, wasfull
;
314 if (!rxfifo_available(sc
))
316 c
= fifo
->buf
[fifo
->rindex
];
317 fifo
->rindex
= (fifo
->rindex
+ 1) % fifo
->size
;
320 if (sc
->tty
.opened
) {
321 error
= mevent_enable(sc
->mev
);
325 if (sc
->sock
&& sc
->usc_sock
.clifd
!= -1) {
326 error
= mevent_enable(sc
->mev
);
329 #endif /* __FreeBSD__ */
337 rxfifo_numchars(struct uart_softc
*sc
)
339 struct fifo
*fifo
= &sc
->rxfifo
;
345 uart_opentty(struct uart_softc
*sc
)
349 sc
->mev
= mevent_add(sc
->tty
.rfd
, EVF_READ
, uart_drain
, sc
);
350 assert(sc
->mev
!= NULL
);
354 modem_status(uint8_t mcr
)
358 if (mcr
& MCR_LOOPBACK
) {
360 * In the loopback mode certain bits from the MCR are
361 * reflected back into MSR.
374 * Always assert DCD and DSR so tty open doesn't block
375 * even if CLOCAL is turned off.
377 msr
= MSR_DCD
| MSR_DSR
;
379 assert((msr
& MSR_DELTA_MASK
) == 0);
385 * The IIR returns a prioritized interrupt reason:
386 * - receive data available
387 * - transmit holding register empty
388 * - modem status change
390 * Return an interrupt reason if one is available.
393 uart_intr_reason(struct uart_softc
*sc
)
396 if ((sc
->lsr
& LSR_OE
) != 0 && (sc
->ier
& IER_ERLS
) != 0)
398 else if (rxfifo_numchars(sc
) > 0 && (sc
->ier
& IER_ERXRDY
) != 0)
400 else if (sc
->thre_int_pending
&& (sc
->ier
& IER_ETXRDY
) != 0)
402 else if ((sc
->msr
& MSR_DELTA_MASK
) != 0 && (sc
->ier
& IER_EMSC
) != 0)
409 uart_reset(struct uart_softc
*sc
)
413 divisor
= DEFAULT_RCLK
/ DEFAULT_BAUD
/ 16;
418 sc
->dlh
= divisor
>> 16;
420 sc
->msr
= modem_status(sc
->mcr
);
422 rxfifo_reset(sc
, 1); /* no fifo until enabled by software */
426 * Toggle the COM port's intr pin depending on whether or not we have an
427 * interrupt condition to report to the processor.
430 uart_toggle_intr(struct uart_softc
*sc
)
434 intr_reason
= uart_intr_reason(sc
);
436 if (intr_reason
== IIR_NOPEND
)
437 (*sc
->intr_deassert
)(sc
->arg
);
439 (*sc
->intr_assert
)(sc
->arg
);
443 uart_drain(int fd
, enum ev_type ev
, void *arg
)
445 struct uart_softc
*sc
;
450 assert(fd
== sc
->tty
.rfd
);
451 assert(ev
== EVF_READ
);
454 * This routine is called in the context of the mevent thread
455 * to take out the softc lock to protect against concurrent
456 * access from a vCPU i/o exit
458 pthread_mutex_lock(&sc
->mtx
);
460 if ((sc
->mcr
& MCR_LOOPBACK
) != 0) {
461 (void) ttyread(&sc
->tty
);
463 while (rxfifo_available(sc
) &&
464 ((ch
= ttyread(&sc
->tty
)) != -1)) {
465 rxfifo_putchar(sc
, ch
);
467 uart_toggle_intr(sc
);
470 pthread_mutex_unlock(&sc
->mtx
);
474 uart_write(struct uart_softc
*sc
, int offset
, uint8_t value
)
479 pthread_mutex_lock(&sc
->mtx
);
482 * Take care of the special case DLAB accesses first
484 if ((sc
->lcr
& LCR_DLAB
) != 0) {
485 if (offset
== REG_DLL
) {
490 if (offset
== REG_DLH
) {
498 if (sc
->mcr
& MCR_LOOPBACK
) {
499 if (rxfifo_putchar(sc
, value
) != 0)
501 } else if (sc
->tty
.opened
) {
502 ttywrite(&sc
->tty
, value
);
504 } else if (sc
->sock
) {
505 sockwrite(sc
, value
);
507 } /* else drop on floor */
508 sc
->thre_int_pending
= true;
511 /* Set pending when IER_ETXRDY is raised (edge-triggered). */
512 if ((sc
->ier
& IER_ETXRDY
) == 0 && (value
& IER_ETXRDY
) != 0)
513 sc
->thre_int_pending
= true;
515 * Apply mask so that bits 4-7 are 0
516 * Also enables bits 0-3 only if they're 1
518 sc
->ier
= value
& 0x0F;
522 * When moving from FIFO and 16450 mode and vice versa,
523 * the FIFO contents are reset.
525 if ((sc
->fcr
& FCR_ENABLE
) ^ (value
& FCR_ENABLE
)) {
526 fifosz
= (value
& FCR_ENABLE
) ? FIFOSZ
: 1;
527 rxfifo_reset(sc
, fifosz
);
531 * The FCR_ENABLE bit must be '1' for the programming
532 * of other FCR bits to be effective.
534 if ((value
& FCR_ENABLE
) == 0) {
537 if ((value
& FCR_RCV_RST
) != 0)
538 rxfifo_reset(sc
, FIFOSZ
);
541 (FCR_ENABLE
| FCR_DMA
| FCR_RX_MASK
);
548 /* Apply mask so that bits 5-7 are 0 */
549 sc
->mcr
= value
& 0x1F;
550 msr
= modem_status(sc
->mcr
);
553 * Detect if there has been any change between the
554 * previous and the new value of MSR. If there is
555 * then assert the appropriate MSR delta bit.
557 if ((msr
& MSR_CTS
) ^ (sc
->msr
& MSR_CTS
))
559 if ((msr
& MSR_DSR
) ^ (sc
->msr
& MSR_DSR
))
561 if ((msr
& MSR_DCD
) ^ (sc
->msr
& MSR_DCD
))
563 if ((sc
->msr
& MSR_RI
) != 0 && (msr
& MSR_RI
) == 0)
567 * Update the value of MSR while retaining the delta
570 sc
->msr
&= MSR_DELTA_MASK
;
575 * Line status register is not meant to be written to
576 * during normal operation.
581 * As far as I can tell MSR is a read-only register.
592 uart_toggle_intr(sc
);
593 pthread_mutex_unlock(&sc
->mtx
);
597 uart_read(struct uart_softc
*sc
, int offset
)
599 uint8_t iir
, intr_reason
, reg
;
601 pthread_mutex_lock(&sc
->mtx
);
604 * Take care of the special case DLAB accesses first
606 if ((sc
->lcr
& LCR_DLAB
) != 0) {
607 if (offset
== REG_DLL
) {
612 if (offset
== REG_DLH
) {
620 reg
= rxfifo_getchar(sc
);
626 iir
= (sc
->fcr
& FCR_ENABLE
) ? IIR_FIFO_MASK
: 0;
628 intr_reason
= uart_intr_reason(sc
);
631 * Deal with side effects of reading the IIR register
633 if (intr_reason
== IIR_TXRDY
)
634 sc
->thre_int_pending
= false;
647 /* Transmitter is always ready for more data */
648 sc
->lsr
|= LSR_TEMT
| LSR_THRE
;
650 /* Check for new receive data */
651 if (rxfifo_numchars(sc
) > 0)
652 sc
->lsr
|= LSR_RXRDY
;
654 sc
->lsr
&= ~LSR_RXRDY
;
658 /* The LSR_OE bit is cleared on LSR read */
663 * MSR delta bits are cleared on read
666 sc
->msr
&= ~MSR_DELTA_MASK
;
677 uart_toggle_intr(sc
);
678 pthread_mutex_unlock(&sc
->mtx
);
685 uart_sock_drain(int fd
, enum ev_type ev
, void *arg
)
687 struct uart_softc
*sc
= arg
;
691 * Take the softc lock to protect against concurrent
692 * access from a vCPU i/o exit
694 pthread_mutex_lock(&sc
->mtx
);
696 if ((sc
->mcr
& MCR_LOOPBACK
) != 0) {
697 (void) read(sc
->usc_sock
.clifd
, &ch
, 1);
699 bool err_close
= false;
701 while (rxfifo_available(sc
)) {
704 res
= read(sc
->usc_sock
.clifd
, &ch
, 1);
708 } else if (res
== -1) {
709 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
715 rxfifo_putchar(sc
, ch
);
717 uart_toggle_intr(sc
);
720 (void) fprintf(stderr
, "uart: closing client conn\n");
721 (void) shutdown(sc
->usc_sock
.clifd
, SHUT_RDWR
);
722 mevent_delete_close(sc
->mev
);
724 sc
->usc_sock
.clifd
= -1;
728 pthread_mutex_unlock(&sc
->mtx
);
732 uart_sock_accept(int fd
, enum ev_type ev
, void *arg
)
734 struct uart_softc
*sc
= arg
;
737 connfd
= accept(sc
->usc_sock
.servfd
, NULL
, NULL
);
743 * Do client connection management under protection of the softc lock
744 * to avoid racing with concurrent UART events.
746 pthread_mutex_lock(&sc
->mtx
);
748 if (sc
->usc_sock
.clifd
!= -1) {
749 /* we're already handling a client */
750 (void) fprintf(stderr
, "uart: unexpected client conn\n");
751 (void) shutdown(connfd
, SHUT_RDWR
);
752 (void) close(connfd
);
754 if (fcntl(connfd
, F_SETFL
, O_NONBLOCK
) < 0) {
755 perror("uart: fcntl(O_NONBLOCK)");
756 (void) shutdown(connfd
, SHUT_RDWR
);
757 (void) close(connfd
);
759 sc
->usc_sock
.clifd
= connfd
;
760 sc
->mev
= mevent_add(sc
->usc_sock
.clifd
, EVF_READ
,
761 uart_sock_drain
, sc
);
765 pthread_mutex_unlock(&sc
->mtx
);
769 init_sock(const char *path
)
772 struct sockaddr_un servaddr
;
774 bzero(&servaddr
, sizeof (servaddr
));
775 servaddr
.sun_family
= AF_UNIX
;
777 if (strlcpy(servaddr
.sun_path
, path
, sizeof (servaddr
.sun_path
)) >=
778 sizeof (servaddr
.sun_path
)) {
779 (void) fprintf(stderr
, "uart: path '%s' too long\n",
784 if ((servfd
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
785 (void) fprintf(stderr
, "uart: socket() error - %s\n",
789 (void) unlink(servaddr
.sun_path
);
791 if (bind(servfd
, (struct sockaddr
*)&servaddr
,
792 sizeof (servaddr
)) == -1) {
793 (void) fprintf(stderr
, "uart: bind() error - %s\n",
798 if (listen(servfd
, 1) == -1) {
799 (void) fprintf(stderr
, "uart: listen() error - %s\n",
806 (void) unlink(servaddr
.sun_path
);
807 (void) close(servfd
);
810 #endif /* not __FreeBSD__ */
813 uart_legacy_alloc(int which
, int *baseaddr
, int *irq
)
816 if (which
< 0 || which
>= (int)UART_NLDEVS
|| uart_lres
[which
].inuse
)
819 uart_lres
[which
].inuse
= true;
820 *baseaddr
= uart_lres
[which
].baseaddr
;
821 *irq
= uart_lres
[which
].irq
;
827 uart_init(uart_intr_func_t intr_assert
, uart_intr_func_t intr_deassert
,
830 struct uart_softc
*sc
;
832 sc
= calloc(1, sizeof(struct uart_softc
));
835 sc
->intr_assert
= intr_assert
;
836 sc
->intr_deassert
= intr_deassert
;
838 pthread_mutex_init(&sc
->mtx
, NULL
);
847 uart_sock_backend(struct uart_softc
*sc
, const char *inopts
)
854 if (strncmp(inopts
, "socket,", 7) != 0) {
857 if ((opts
= strdup(inopts
+ 7)) == NULL
) {
861 tofree
= nextopt
= opts
;
862 for (opt
= strsep(&nextopt
, ","); opt
!= NULL
;
863 opt
= strsep(&nextopt
, ",")) {
864 if (path
== NULL
&& *opt
== '/') {
869 * XXX check for server and client options here. For now,
870 * everything is a server
876 sc
->usc_sock
.clifd
= -1;
877 if ((sc
->usc_sock
.servfd
= init_sock(path
)) == -1) {
882 sc
->tty
.rfd
= sc
->tty
.wfd
= -1;
883 sc
->usc_sock
.servmev
= mevent_add(sc
->usc_sock
.servfd
, EVF_READ
,
884 uart_sock_accept
, sc
);
885 assert(sc
->usc_sock
.servmev
!= NULL
);
890 #endif /* not __FreeBSD__ */
893 uart_stdio_backend(struct uart_softc
*sc
)
895 #ifndef WITHOUT_CAPSICUM
897 cap_ioctl_t cmds
[] = { TIOCGETA
, TIOCSETA
, TIOCGWINSZ
};
903 sc
->tty
.rfd
= STDIN_FILENO
;
904 sc
->tty
.wfd
= STDOUT_FILENO
;
905 sc
->tty
.opened
= true;
907 if (fcntl(sc
->tty
.rfd
, F_SETFL
, O_NONBLOCK
) != 0)
909 if (fcntl(sc
->tty
.wfd
, F_SETFL
, O_NONBLOCK
) != 0)
912 #ifndef WITHOUT_CAPSICUM
913 cap_rights_init(&rights
, CAP_EVENT
, CAP_IOCTL
, CAP_READ
);
914 if (caph_rights_limit(sc
->tty
.rfd
, &rights
) == -1)
915 errx(EX_OSERR
, "Unable to apply rights for sandbox");
916 if (caph_ioctls_limit(sc
->tty
.rfd
, cmds
, nitems(cmds
)) == -1)
917 errx(EX_OSERR
, "Unable to apply rights for sandbox");
926 uart_tty_backend(struct uart_softc
*sc
, const char *path
)
928 #ifndef WITHOUT_CAPSICUM
930 cap_ioctl_t cmds
[] = { TIOCGETA
, TIOCSETA
, TIOCGWINSZ
};
934 fd
= open(path
, O_RDWR
| O_NONBLOCK
);
943 sc
->tty
.rfd
= sc
->tty
.wfd
= fd
;
944 sc
->tty
.opened
= true;
946 #ifndef WITHOUT_CAPSICUM
947 cap_rights_init(&rights
, CAP_EVENT
, CAP_IOCTL
, CAP_READ
, CAP_WRITE
);
948 if (caph_rights_limit(fd
, &rights
) == -1)
949 errx(EX_OSERR
, "Unable to apply rights for sandbox");
950 if (caph_ioctls_limit(fd
, cmds
, nitems(cmds
)) == -1)
951 errx(EX_OSERR
, "Unable to apply rights for sandbox");
958 uart_set_backend(struct uart_softc
*sc
, const char *device
)
966 if (strncmp("socket,", device
, 7) == 0)
967 return (uart_sock_backend(sc
, device
));
969 if (strcmp("stdio", device
) == 0)
970 retval
= uart_stdio_backend(sc
);
972 retval
= uart_tty_backend(sc
, device
);