1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2012 NetApp, Inc.
5 * Copyright (c) 2013 Neel Natu <[email protected]>
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
ttyclose(void)75 ttyclose(void)
76 {
77 tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig);
78 }
79
80 static void
ttyopen(struct ttyfd * tf)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
ttyread(struct ttyfd * tf)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
ttywrite(struct ttyfd * tf,unsigned char wb)109 ttywrite(struct ttyfd *tf, unsigned char wb)
110 {
111 (void)write(tf->wfd, &wb, 1);
112 }
113
114 static bool
rxfifo_available(struct uart_softc * sc)115 rxfifo_available(struct uart_softc *sc)
116 {
117 return (sc->rxfifo.num < sc->rxfifo.size);
118 }
119
120 int
uart_rxfifo_getchar(struct uart_softc * sc)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
uart_rxfifo_numchars(struct uart_softc * sc)146 uart_rxfifo_numchars(struct uart_softc *sc)
147 {
148 return (sc->rxfifo.num);
149 }
150
151 static int
rxfifo_putchar(struct uart_softc * sc,uint8_t ch)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
uart_rxfifo_drain(struct uart_softc * sc,bool loopback)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
uart_rxfifo_putchar(struct uart_softc * sc,uint8_t ch,bool loopback)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
uart_rxfifo_reset(struct uart_softc * sc,int size)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
uart_rxfifo_size(struct uart_softc * sc __unused)237 uart_rxfifo_size(struct uart_softc *sc __unused)
238 {
239 return (FIFOSZ);
240 }
241
242 #ifdef BHYVE_SNAPSHOT
243 int
uart_rxfifo_snapshot(struct uart_softc * sc,struct vm_snapshot_meta * meta)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
uart_stdio_backend(struct uart_softc * sc)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
uart_tty_backend(struct uart_softc * sc,const char * path)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 *
uart_init(void)326 uart_init(void)
327 {
328 return (calloc(1, sizeof(struct uart_softc)));
329 }
330
331 int
uart_tty_open(struct uart_softc * sc,const char * path,void (* drain)(int,enum ev_type,void *),void * arg)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