xref: /freebsd/usr.sbin/bhyve/uart_emul.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
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 #include <dev/ic/ns16550.h>
32 
33 #include <machine/vmm_snapshot.h>
34 
35 #include <assert.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <stdbool.h>
41 #include <string.h>
42 #include <pthread.h>
43 
44 #include "uart_backend.h"
45 #include "uart_emul.h"
46 
47 #define	COM1_BASE      	0x3F8
48 #define	COM1_IRQ	4
49 #define	COM2_BASE      	0x2F8
50 #define	COM2_IRQ	3
51 #define	COM3_BASE	0x3E8
52 #define	COM3_IRQ	4
53 #define	COM4_BASE	0x2E8
54 #define	COM4_IRQ	3
55 
56 #define	DEFAULT_RCLK	1843200
57 #define	DEFAULT_BAUD	115200
58 
59 #define	FCR_RX_MASK	0xC0
60 
61 #define	MCR_OUT1	0x04
62 #define	MCR_OUT2	0x08
63 
64 #define	MSR_DELTA_MASK	0x0f
65 
66 #ifndef REG_SCR
67 #define	REG_SCR		com_scr
68 #endif
69 
70 static struct {
71 	int	baseaddr;
72 	int	irq;
73 	bool	inuse;
74 } uart_lres[] = {
75 	{ COM1_BASE, COM1_IRQ, false},
76 	{ COM2_BASE, COM2_IRQ, false},
77 	{ COM3_BASE, COM3_IRQ, false},
78 	{ COM4_BASE, COM4_IRQ, false},
79 };
80 
81 #define	UART_NLDEVS	(sizeof(uart_lres) / sizeof(uart_lres[0]))
82 
83 struct uart_ns16550_softc {
84 	struct uart_softc *backend;
85 
86 	pthread_mutex_t mtx;	/* protects all softc elements */
87 	uint8_t	data;		/* Data register (R/W) */
88 	uint8_t ier;		/* Interrupt enable register (R/W) */
89 	uint8_t lcr;		/* Line control register (R/W) */
90 	uint8_t mcr;		/* Modem control register (R/W) */
91 	uint8_t lsr;		/* Line status register (R/W) */
92 	uint8_t msr;		/* Modem status register (R/W) */
93 	uint8_t fcr;		/* FIFO control register (W) */
94 	uint8_t scr;		/* Scratch register (R/W) */
95 
96 	uint8_t dll;		/* Baudrate divisor latch LSB */
97 	uint8_t dlh;		/* Baudrate divisor latch MSB */
98 
99 	bool	thre_int_pending;	/* THRE interrupt pending */
100 
101 	void	*arg;
102 	uart_intr_func_t intr_assert;
103 	uart_intr_func_t intr_deassert;
104 };
105 
106 static uint8_t
107 modem_status(uint8_t mcr)
108 {
109 	uint8_t msr;
110 
111 	if (mcr & MCR_LOOPBACK) {
112 		/*
113 		 * In the loopback mode certain bits from the MCR are
114 		 * reflected back into MSR.
115 		 */
116 		msr = 0;
117 		if (mcr & MCR_RTS)
118 			msr |= MSR_CTS;
119 		if (mcr & MCR_DTR)
120 			msr |= MSR_DSR;
121 		if (mcr & MCR_OUT1)
122 			msr |= MSR_RI;
123 		if (mcr & MCR_OUT2)
124 			msr |= MSR_DCD;
125 	} else {
126 		/*
127 		 * Always assert DCD and DSR so tty open doesn't block
128 		 * even if CLOCAL is turned off.
129 		 */
130 		msr = MSR_DCD | MSR_DSR;
131 	}
132 	assert((msr & MSR_DELTA_MASK) == 0);
133 
134 	return (msr);
135 }
136 
137 /*
138  * The IIR returns a prioritized interrupt reason:
139  * - receive data available
140  * - transmit holding register empty
141  * - modem status change
142  *
143  * Return an interrupt reason if one is available.
144  */
145 static int
146 uart_intr_reason(struct uart_ns16550_softc *sc)
147 {
148 
149 	if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0)
150 		return (IIR_RLS);
151 	else if (uart_rxfifo_numchars(sc->backend) > 0 &&
152 	    (sc->ier & IER_ERXRDY) != 0)
153 		return (IIR_RXTOUT);
154 	else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0)
155 		return (IIR_TXRDY);
156 	else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0)
157 		return (IIR_MLSC);
158 	else
159 		return (IIR_NOPEND);
160 }
161 
162 static void
163 uart_reset(struct uart_ns16550_softc *sc)
164 {
165 	uint16_t divisor;
166 
167 	divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16;
168 	sc->dll = divisor;
169 	sc->dlh = divisor >> 16;
170 	sc->msr = modem_status(sc->mcr);
171 
172 	uart_rxfifo_reset(sc->backend, 1);
173 }
174 
175 /*
176  * Toggle the COM port's intr pin depending on whether or not we have an
177  * interrupt condition to report to the processor.
178  */
179 static void
180 uart_toggle_intr(struct uart_ns16550_softc *sc)
181 {
182 	uint8_t intr_reason;
183 
184 	intr_reason = uart_intr_reason(sc);
185 
186 	if (intr_reason == IIR_NOPEND)
187 		(*sc->intr_deassert)(sc->arg);
188 	else
189 		(*sc->intr_assert)(sc->arg);
190 }
191 
192 static void
193 uart_drain(int fd __unused, enum ev_type ev, void *arg)
194 {
195 	struct uart_ns16550_softc *sc;
196 	bool loopback;
197 
198 	sc = arg;
199 
200 	assert(ev == EVF_READ);
201 
202 	/*
203 	 * This routine is called in the context of the mevent thread
204 	 * to take out the softc lock to protect against concurrent
205 	 * access from a vCPU i/o exit
206 	 */
207 	pthread_mutex_lock(&sc->mtx);
208 
209 	loopback = (sc->mcr & MCR_LOOPBACK) != 0;
210 	uart_rxfifo_drain(sc->backend, loopback);
211 	if (!loopback)
212 		uart_toggle_intr(sc);
213 
214 	pthread_mutex_unlock(&sc->mtx);
215 }
216 
217 void
218 uart_ns16550_write(struct uart_ns16550_softc *sc, int offset, uint8_t value)
219 {
220 	int fifosz;
221 	uint8_t msr;
222 
223 	pthread_mutex_lock(&sc->mtx);
224 
225 	/*
226 	 * Take care of the special case DLAB accesses first
227 	 */
228 	if ((sc->lcr & LCR_DLAB) != 0) {
229 		if (offset == REG_DLL) {
230 			sc->dll = value;
231 			goto done;
232 		}
233 
234 		if (offset == REG_DLH) {
235 			sc->dlh = value;
236 			goto done;
237 		}
238 	}
239 
240         switch (offset) {
241 	case REG_DATA:
242 		if (uart_rxfifo_putchar(sc->backend, value,
243 		    (sc->mcr & MCR_LOOPBACK) != 0))
244 			sc->lsr |= LSR_OE;
245 		sc->thre_int_pending = true;
246 		break;
247 	case REG_IER:
248 		/* Set pending when IER_ETXRDY is raised (edge-triggered). */
249 		if ((sc->ier & IER_ETXRDY) == 0 && (value & IER_ETXRDY) != 0)
250 			sc->thre_int_pending = true;
251 		/*
252 		 * Apply mask so that bits 4-7 are 0
253 		 * Also enables bits 0-3 only if they're 1
254 		 */
255 		sc->ier = value & 0x0F;
256 		break;
257 	case REG_FCR:
258 		/*
259 		 * When moving from FIFO and 16450 mode and vice versa,
260 		 * the FIFO contents are reset.
261 		 */
262 		if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) {
263 			fifosz = (value & FCR_ENABLE) ?
264 			    uart_rxfifo_size(sc->backend) : 1;
265 			uart_rxfifo_reset(sc->backend, fifosz);
266 		}
267 
268 		/*
269 		 * The FCR_ENABLE bit must be '1' for the programming
270 		 * of other FCR bits to be effective.
271 		 */
272 		if ((value & FCR_ENABLE) == 0) {
273 			sc->fcr = 0;
274 		} else {
275 			if ((value & FCR_RCV_RST) != 0)
276 				uart_rxfifo_reset(sc->backend,
277 				    uart_rxfifo_size(sc->backend));
278 
279 			sc->fcr = value &
280 				 (FCR_ENABLE | FCR_DMA | FCR_RX_MASK);
281 		}
282 		break;
283 	case REG_LCR:
284 		sc->lcr = value;
285 		break;
286 	case REG_MCR:
287 		/* Apply mask so that bits 5-7 are 0 */
288 		sc->mcr = value & 0x1F;
289 		msr = modem_status(sc->mcr);
290 
291 		/*
292 		 * Detect if there has been any change between the
293 		 * previous and the new value of MSR. If there is
294 		 * then assert the appropriate MSR delta bit.
295 		 */
296 		if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS))
297 			sc->msr |= MSR_DCTS;
298 		if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR))
299 			sc->msr |= MSR_DDSR;
300 		if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD))
301 			sc->msr |= MSR_DDCD;
302 		if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0)
303 			sc->msr |= MSR_TERI;
304 
305 		/*
306 		 * Update the value of MSR while retaining the delta
307 		 * bits.
308 		 */
309 		sc->msr &= MSR_DELTA_MASK;
310 		sc->msr |= msr;
311 		break;
312 	case REG_LSR:
313 		/*
314 		 * Line status register is not meant to be written to
315 		 * during normal operation.
316 		 */
317 		break;
318 	case REG_MSR:
319 		/*
320 		 * As far as I can tell MSR is a read-only register.
321 		 */
322 		break;
323 	case REG_SCR:
324 		sc->scr = value;
325 		break;
326 	default:
327 		break;
328 	}
329 
330 done:
331 	uart_toggle_intr(sc);
332 	pthread_mutex_unlock(&sc->mtx);
333 }
334 
335 uint8_t
336 uart_ns16550_read(struct uart_ns16550_softc *sc, int offset)
337 {
338 	uint8_t iir, intr_reason, reg;
339 
340 	pthread_mutex_lock(&sc->mtx);
341 
342 	/*
343 	 * Take care of the special case DLAB accesses first
344 	 */
345 	if ((sc->lcr & LCR_DLAB) != 0) {
346 		if (offset == REG_DLL) {
347 			reg = sc->dll;
348 			goto done;
349 		}
350 
351 		if (offset == REG_DLH) {
352 			reg = sc->dlh;
353 			goto done;
354 		}
355 	}
356 
357 	switch (offset) {
358 	case REG_DATA:
359 		reg = uart_rxfifo_getchar(sc->backend);
360 		break;
361 	case REG_IER:
362 		reg = sc->ier;
363 		break;
364 	case REG_IIR:
365 		iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0;
366 
367 		intr_reason = uart_intr_reason(sc);
368 
369 		/*
370 		 * Deal with side effects of reading the IIR register
371 		 */
372 		if (intr_reason == IIR_TXRDY)
373 			sc->thre_int_pending = false;
374 
375 		iir |= intr_reason;
376 
377 		reg = iir;
378 		break;
379 	case REG_LCR:
380 		reg = sc->lcr;
381 		break;
382 	case REG_MCR:
383 		reg = sc->mcr;
384 		break;
385 	case REG_LSR:
386 		/* Transmitter is always ready for more data */
387 		sc->lsr |= LSR_TEMT | LSR_THRE;
388 
389 		/* Check for new receive data */
390 		if (uart_rxfifo_numchars(sc->backend) > 0)
391 			sc->lsr |= LSR_RXRDY;
392 		else
393 			sc->lsr &= ~LSR_RXRDY;
394 
395 		reg = sc->lsr;
396 
397 		/* The LSR_OE bit is cleared on LSR read */
398 		sc->lsr &= ~LSR_OE;
399 		break;
400 	case REG_MSR:
401 		/*
402 		 * MSR delta bits are cleared on read
403 		 */
404 		reg = sc->msr;
405 		sc->msr &= ~MSR_DELTA_MASK;
406 		break;
407 	case REG_SCR:
408 		reg = sc->scr;
409 		break;
410 	default:
411 		reg = 0xFF;
412 		break;
413 	}
414 
415 done:
416 	uart_toggle_intr(sc);
417 	pthread_mutex_unlock(&sc->mtx);
418 
419 	return (reg);
420 }
421 
422 int
423 uart_legacy_alloc(int which, int *baseaddr, int *irq)
424 {
425 
426 	if (which < 0 || which >= (int)UART_NLDEVS || uart_lres[which].inuse)
427 		return (-1);
428 
429 	uart_lres[which].inuse = true;
430 	*baseaddr = uart_lres[which].baseaddr;
431 	*irq = uart_lres[which].irq;
432 
433 	return (0);
434 }
435 
436 struct uart_ns16550_softc *
437 uart_ns16550_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert,
438     void *arg)
439 {
440 	struct uart_ns16550_softc *sc;
441 
442 	sc = calloc(1, sizeof(struct uart_ns16550_softc));
443 
444 	sc->arg = arg;
445 	sc->intr_assert = intr_assert;
446 	sc->intr_deassert = intr_deassert;
447 	sc->backend = uart_init();
448 
449 	pthread_mutex_init(&sc->mtx, NULL);
450 
451 	uart_reset(sc);
452 
453 	return (sc);
454 }
455 
456 int
457 uart_ns16550_tty_open(struct uart_ns16550_softc *sc, const char *device)
458 {
459 	return (uart_tty_open(sc->backend, device, uart_drain, sc));
460 }
461 
462 #ifdef BHYVE_SNAPSHOT
463 int
464 uart_ns16550_snapshot(struct uart_ns16550_softc *sc,
465     struct vm_snapshot_meta *meta)
466 {
467 	int ret;
468 
469 	SNAPSHOT_VAR_OR_LEAVE(sc->data, meta, ret, done);
470 	SNAPSHOT_VAR_OR_LEAVE(sc->ier, meta, ret, done);
471 	SNAPSHOT_VAR_OR_LEAVE(sc->lcr, meta, ret, done);
472 	SNAPSHOT_VAR_OR_LEAVE(sc->mcr, meta, ret, done);
473 	SNAPSHOT_VAR_OR_LEAVE(sc->lsr, meta, ret, done);
474 	SNAPSHOT_VAR_OR_LEAVE(sc->msr, meta, ret, done);
475 	SNAPSHOT_VAR_OR_LEAVE(sc->fcr, meta, ret, done);
476 	SNAPSHOT_VAR_OR_LEAVE(sc->scr, meta, ret, done);
477 
478 	SNAPSHOT_VAR_OR_LEAVE(sc->dll, meta, ret, done);
479 	SNAPSHOT_VAR_OR_LEAVE(sc->dlh, meta, ret, done);
480 
481 	ret = uart_rxfifo_snapshot(sc->backend, meta);
482 
483 	sc->thre_int_pending = 1;
484 
485 done:
486 	return (ret);
487 }
488 #endif
489