xref: /freebsd/usr.sbin/bhyve/uart_backend.c (revision f126890ac5386406dadf7c4cfa9566cbb56537c5)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2012 NetApp, Inc.
5  * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
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.
16  *
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
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/types.h>
31 
32 #include <machine/vmm.h>
33 #include <machine/vmm_snapshot.h>
34 
35 #include <assert.h>
36 #include <capsicum_helpers.h>
37 #include <err.h>
38 #include <stdbool.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sysexits.h>
42 #include <termios.h>
43 #include <unistd.h>
44 
45 #include "debug.h"
46 #include "mevent.h"
47 #include "uart_backend.h"
48 
49 struct ttyfd {
50 	bool	opened;
51 	int	rfd;		/* fd for reading */
52 	int	wfd;		/* fd for writing, may be == rfd */
53 };
54 
55 #define	FIFOSZ	16
56 
57 struct fifo {
58 	uint8_t	buf[FIFOSZ];
59 	int	rindex;		/* index to read from */
60 	int	windex;		/* index to write to */
61 	int	num;		/* number of characters in the fifo */
62 	int	size;		/* size of the fifo */
63 };
64 
65 struct uart_softc {
66 	struct ttyfd	tty;
67 	struct fifo	rxfifo;
68 	struct mevent	*mev;
69 };
70 
71 static bool uart_stdio;		/* stdio in use for i/o */
72 static struct termios tio_stdio_orig;
73 
74 static void
75 ttyclose(void)
76 {
77 	tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig);
78 }
79 
80 static void
81 ttyopen(struct ttyfd *tf)
82 {
83 	struct termios orig, new;
84 
85 	tcgetattr(tf->rfd, &orig);
86 	new = orig;
87 	cfmakeraw(&new);
88 	new.c_cflag |= CLOCAL;
89 	tcsetattr(tf->rfd, TCSANOW, &new);
90 	if (uart_stdio) {
91 		tio_stdio_orig = orig;
92 		atexit(ttyclose);
93 	}
94 	raw_stdio = 1;
95 }
96 
97 static int
98 ttyread(struct ttyfd *tf)
99 {
100 	unsigned char rb;
101 
102 	if (read(tf->rfd, &rb, 1) == 1)
103 		return (rb);
104 	else
105 		return (-1);
106 }
107 
108 static void
109 ttywrite(struct ttyfd *tf, unsigned char wb)
110 {
111 	(void)write(tf->wfd, &wb, 1);
112 }
113 
114 static bool
115 rxfifo_available(struct uart_softc *sc)
116 {
117 	return (sc->rxfifo.num < sc->rxfifo.size);
118 }
119 
120 int
121 uart_rxfifo_getchar(struct uart_softc *sc)
122 {
123 	struct fifo *fifo;
124 	int c, error, wasfull;
125 
126 	wasfull = 0;
127 	fifo = &sc->rxfifo;
128 	if (fifo->num > 0) {
129 		if (!rxfifo_available(sc))
130 			wasfull = 1;
131 		c = fifo->buf[fifo->rindex];
132 		fifo->rindex = (fifo->rindex + 1) % fifo->size;
133 		fifo->num--;
134 		if (wasfull) {
135 			if (sc->tty.opened) {
136 				error = mevent_enable(sc->mev);
137 				assert(error == 0);
138 			}
139 		}
140 		return (c);
141 	} else
142 		return (-1);
143 }
144 
145 int
146 uart_rxfifo_numchars(struct uart_softc *sc)
147 {
148 	return (sc->rxfifo.num);
149 }
150 
151 static int
152 rxfifo_putchar(struct uart_softc *sc, uint8_t ch)
153 {
154 	struct fifo *fifo;
155 	int error;
156 
157 	fifo = &sc->rxfifo;
158 
159 	if (fifo->num < fifo->size) {
160 		fifo->buf[fifo->windex] = ch;
161 		fifo->windex = (fifo->windex + 1) % fifo->size;
162 		fifo->num++;
163 		if (!rxfifo_available(sc)) {
164 			if (sc->tty.opened) {
165 				/*
166 				 * Disable mevent callback if the FIFO is full.
167 				 */
168 				error = mevent_disable(sc->mev);
169 				assert(error == 0);
170 			}
171 		}
172 		return (0);
173 	} else
174 		return (-1);
175 }
176 
177 void
178 uart_rxfifo_drain(struct uart_softc *sc, bool loopback)
179 {
180 	int ch;
181 
182 	if (loopback) {
183 		(void)ttyread(&sc->tty);
184 	} else {
185 		while (rxfifo_available(sc) &&
186 		    ((ch = ttyread(&sc->tty)) != -1))
187 			rxfifo_putchar(sc, ch);
188 	}
189 }
190 
191 int
192 uart_rxfifo_putchar(struct uart_softc *sc, uint8_t ch, bool loopback)
193 {
194 	if (loopback) {
195 		return (rxfifo_putchar(sc, ch));
196 	} else if (sc->tty.opened) {
197 		ttywrite(&sc->tty, ch);
198 		return (0);
199 	} else {
200 		/* Drop on the floor. */
201 		return (0);
202 	}
203 }
204 
205 void
206 uart_rxfifo_reset(struct uart_softc *sc, int size)
207 {
208 	char flushbuf[32];
209 	struct fifo *fifo;
210 	ssize_t nread;
211 	int error;
212 
213 	fifo = &sc->rxfifo;
214 	bzero(fifo, sizeof(struct fifo));
215 	fifo->size = size;
216 
217 	if (sc->tty.opened) {
218 		/*
219 		 * Flush any unread input from the tty buffer.
220 		 */
221 		while (1) {
222 			nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf));
223 			if (nread != sizeof(flushbuf))
224 				break;
225 		}
226 
227 		/*
228 		 * Enable mevent to trigger when new characters are available
229 		 * on the tty fd.
230 		 */
231 		error = mevent_enable(sc->mev);
232 		assert(error == 0);
233 	}
234 }
235 
236 int
237 uart_rxfifo_size(struct uart_softc *sc __unused)
238 {
239 	return (FIFOSZ);
240 }
241 
242 #ifdef BHYVE_SNAPSHOT
243 int
244 uart_rxfifo_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta)
245 {
246 	int ret;
247 
248 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done);
249 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done);
250 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done);
251 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done);
252 	SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf),
253 	    meta, ret, done);
254 
255 done:
256 	return (ret);
257 }
258 #endif
259 
260 static int
261 uart_stdio_backend(struct uart_softc *sc)
262 {
263 #ifndef WITHOUT_CAPSICUM
264 	cap_rights_t rights;
265 	cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
266 #endif
267 
268 	if (uart_stdio)
269 		return (-1);
270 
271 	sc->tty.rfd = STDIN_FILENO;
272 	sc->tty.wfd = STDOUT_FILENO;
273 	sc->tty.opened = true;
274 
275 	if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0)
276 		return (-1);
277 	if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0)
278 		return (-1);
279 
280 #ifndef WITHOUT_CAPSICUM
281 	cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ);
282 	if (caph_rights_limit(sc->tty.rfd, &rights) == -1)
283 		errx(EX_OSERR, "Unable to apply rights for sandbox");
284 	if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1)
285 		errx(EX_OSERR, "Unable to apply rights for sandbox");
286 #endif
287 
288 	uart_stdio = true;
289 
290 	return (0);
291 }
292 
293 static int
294 uart_tty_backend(struct uart_softc *sc, const char *path)
295 {
296 #ifndef WITHOUT_CAPSICUM
297 	cap_rights_t rights;
298 	cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
299 #endif
300 	int fd;
301 
302 	fd = open(path, O_RDWR | O_NONBLOCK);
303 	if (fd < 0)
304 		return (-1);
305 
306 	if (!isatty(fd)) {
307 		close(fd);
308 		return (-1);
309 	}
310 
311 	sc->tty.rfd = sc->tty.wfd = fd;
312 	sc->tty.opened = true;
313 
314 #ifndef WITHOUT_CAPSICUM
315 	cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE);
316 	if (caph_rights_limit(fd, &rights) == -1)
317 		errx(EX_OSERR, "Unable to apply rights for sandbox");
318 	if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1)
319 		errx(EX_OSERR, "Unable to apply rights for sandbox");
320 #endif
321 
322 	return (0);
323 }
324 
325 struct uart_softc *
326 uart_init(void)
327 {
328 	return (calloc(1, sizeof(struct uart_softc)));
329 }
330 
331 int
332 uart_tty_open(struct uart_softc *sc, const char *path,
333     void (*drain)(int, enum ev_type, void *), void *arg)
334 {
335 	int retval;
336 
337 	if (strcmp("stdio", path) == 0)
338 		retval = uart_stdio_backend(sc);
339 	else
340 		retval = uart_tty_backend(sc, path);
341 	if (retval == 0) {
342 		ttyopen(&sc->tty);
343 		sc->mev = mevent_add(sc->tty.rfd, EVF_READ, drain, arg);
344 		assert(sc->mev != NULL);
345 	}
346 
347 	return (retval);
348 }
349