]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/uart_emul.c
usr.sbin: Remove repeated words
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / uart_emul.c
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