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