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