]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/bhyve/pci_uart.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.git] / usr.sbin / bhyve / pci_uart.c
1 /*-
2  * Copyright (c) 2012 NetApp, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/types.h>
33 #include <sys/select.h>
34 #include <dev/ic/ns16550.h>
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <assert.h>
39 #include <termios.h>
40 #include <unistd.h>
41 #include <stdbool.h>
42 #include <string.h>
43 #include <pthread.h>
44
45 #include "bhyverun.h"
46 #include "pci_emul.h"
47 #include "mevent.h"
48
49 #define COM1_BASE       0x3F8
50 #define COM1_IRQ        4
51 #define COM2_BASE       0x2F8
52 #define COM2_IRQ        3
53
54 #define DEFAULT_RCLK    1843200
55 #define DEFAULT_BAUD    9600
56
57 #define FCR_RX_MASK     0xC0
58
59 #define MCR_OUT1        0x04
60 #define MCR_OUT2        0x08
61
62 #define MSR_DELTA_MASK  0x0f
63
64 #ifndef REG_SCR
65 #define REG_SCR         com_scr
66 #endif
67
68 #define FIFOSZ  16
69
70 /*
71  * Pick a PCI vid/did of a chip with a single uart at
72  * BAR0, that most versions of FreeBSD can understand:
73  * Siig CyberSerial 1-port.
74  */
75 #define COM_VENDOR      0x131f
76 #define COM_DEV         0x2000
77
78 static int pci_uart_stdio;      /* stdio in use for i/o */
79
80 static int pci_uart_nldevs;     /* number of legacy devices - 2 max */
81
82 static struct {
83         uint64_t        baddr;
84         int             vector;
85 } pci_uart_lres[] = {
86         { COM1_BASE, COM1_IRQ},
87         { COM2_BASE, COM2_IRQ},
88         { 0, 0 }
89 };
90
91 struct fifo {
92         uint8_t buf[FIFOSZ];
93         int     rindex;         /* index to read from */
94         int     windex;         /* index to write to */
95         int     num;            /* number of characters in the fifo */
96         int     size;           /* size of the fifo */
97 };
98
99 struct pci_uart_softc {
100         struct pci_devinst *pi;
101         pthread_mutex_t mtx;    /* protects all softc elements */
102         uint8_t data;           /* Data register (R/W) */
103         uint8_t ier;            /* Interrupt enable register (R/W) */
104         uint8_t lcr;            /* Line control register (R/W) */
105         uint8_t mcr;            /* Modem control register (R/W) */
106         uint8_t lsr;            /* Line status register (R/W) */
107         uint8_t msr;            /* Modem status register (R/W) */
108         uint8_t fcr;            /* FIFO control register (W) */
109         uint8_t scr;            /* Scratch register (R/W) */
110
111         uint8_t dll;            /* Baudrate divisor latch LSB */
112         uint8_t dlh;            /* Baudrate divisor latch MSB */
113
114         struct fifo rxfifo;
115
116         int     opened;
117         int     stdio;
118         bool    thre_int_pending;       /* THRE interrupt pending */
119 };
120
121 static void pci_uart_drain(int fd, enum ev_type ev, void *arg);
122
123 static struct termios tio_orig, tio_new;        /* I/O Terminals */
124
125 static void
126 ttyclose(void)
127 {
128         tcsetattr(STDIN_FILENO, TCSANOW, &tio_orig);
129 }
130
131 static void
132 ttyopen(void)
133 {
134         tcgetattr(STDIN_FILENO, &tio_orig);
135
136         cfmakeraw(&tio_new);
137         tcsetattr(STDIN_FILENO, TCSANOW, &tio_new);
138
139         atexit(ttyclose);
140 }
141
142 static bool
143 tty_char_available(void)
144 {
145         fd_set rfds;
146         struct timeval tv;
147
148         FD_ZERO(&rfds);
149         FD_SET(STDIN_FILENO, &rfds);
150         tv.tv_sec = 0;
151         tv.tv_usec = 0;
152         if (select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv) > 0 ) {
153                 return (true);
154         } else {
155                 return (false);
156         }
157 }
158
159 static int
160 ttyread(void)
161 {
162         char rb;
163
164         if (tty_char_available()) {
165                 read(STDIN_FILENO, &rb, 1);
166                 return (rb & 0xff);
167         } else {
168                 return (-1);
169         }
170 }
171
172 static void
173 ttywrite(unsigned char wb)
174 {
175         (void) write(STDIN_FILENO, &wb, 1);
176 }
177
178 static void
179 fifo_reset(struct fifo *fifo, int size)
180 {
181         bzero(fifo, sizeof(struct fifo));
182         fifo->size = size;
183 }
184
185 static int
186 fifo_putchar(struct fifo *fifo, uint8_t ch)
187 {
188
189         if (fifo->num < fifo->size) {
190                 fifo->buf[fifo->windex] = ch;
191                 fifo->windex = (fifo->windex + 1) % fifo->size;
192                 fifo->num++;
193                 return (0);
194         } else
195                 return (-1);
196 }
197
198 static int
199 fifo_getchar(struct fifo *fifo)
200 {
201         int c;
202
203         if (fifo->num > 0) {
204                 c = fifo->buf[fifo->rindex];
205                 fifo->rindex = (fifo->rindex + 1) % fifo->size;
206                 fifo->num--;
207                 return (c);
208         } else
209                 return (-1);
210 }
211
212 static int
213 fifo_numchars(struct fifo *fifo)
214 {
215
216         return (fifo->num);
217 }
218
219 static int
220 fifo_available(struct fifo *fifo)
221 {
222
223         return (fifo->num < fifo->size);
224 }
225
226 static void
227 pci_uart_opentty(struct pci_uart_softc *sc)
228 {
229         struct mevent *mev;
230
231         assert(sc->opened == 0);
232         assert(sc->stdio);
233
234         ttyopen();
235         mev = mevent_add(STDIN_FILENO, EVF_READ, pci_uart_drain, sc);
236         assert(mev);
237 }
238
239 static void
240 pci_uart_legacy_res(uint64_t *bar, int *ivec)
241 {
242         if (pci_uart_lres[pci_uart_nldevs].baddr != 0) {
243                 *bar = pci_uart_lres[pci_uart_nldevs].baddr;
244                 *ivec = pci_uart_lres[pci_uart_nldevs].vector;
245                 pci_uart_nldevs++;
246         } else {
247                 /* TODO: print warning ? */
248                 *bar = 0;
249                 *ivec= -1;
250         }
251 }
252
253 /*
254  * The IIR returns a prioritized interrupt reason:
255  * - receive data available
256  * - transmit holding register empty
257  * - modem status change
258  *
259  * Return an interrupt reason if one is available.
260  */
261 static int
262 pci_uart_intr_reason(struct pci_uart_softc *sc)
263 {
264
265         if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0)
266                 return (IIR_RLS);
267         else if (fifo_numchars(&sc->rxfifo) > 0 && (sc->ier & IER_ERXRDY) != 0)
268                 return (IIR_RXTOUT);
269         else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0)
270                 return (IIR_TXRDY);
271         else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0)
272                 return (IIR_MLSC);
273         else
274                 return (IIR_NOPEND);
275 }
276
277 static void
278 pci_uart_reset(struct pci_uart_softc *sc)
279 {
280         uint16_t divisor;
281
282         divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16;
283         sc->dll = divisor;
284         sc->dlh = divisor >> 16;
285
286         fifo_reset(&sc->rxfifo, 1);     /* no fifo until enabled by software */
287 }
288
289 /*
290  * Toggle the COM port's intr pin depending on whether or not we have an
291  * interrupt condition to report to the processor.
292  */
293 static void
294 pci_uart_toggle_intr(struct pci_uart_softc *sc)
295 {
296         uint8_t intr_reason;
297
298         intr_reason = pci_uart_intr_reason(sc);
299
300         if (intr_reason == IIR_NOPEND)
301                 pci_lintr_deassert(sc->pi);
302         else
303                 pci_lintr_assert(sc->pi);
304 }
305
306 static void
307 pci_uart_drain(int fd, enum ev_type ev, void *arg)
308 {
309         struct pci_uart_softc *sc;
310         int ch;
311
312         sc = arg;       
313
314         assert(fd == STDIN_FILENO);
315         assert(ev == EVF_READ);
316         
317         /*
318          * This routine is called in the context of the mevent thread
319          * to take out the softc lock to protect against concurrent
320          * access from a vCPU i/o exit
321          */
322         pthread_mutex_lock(&sc->mtx);
323
324         if ((sc->mcr & MCR_LOOPBACK) != 0) {
325                 (void) ttyread();
326         } else {
327                 while (fifo_available(&sc->rxfifo) &&
328                        ((ch = ttyread()) != -1)) {
329                         fifo_putchar(&sc->rxfifo, ch);
330                 }
331                 pci_uart_toggle_intr(sc);
332         }
333
334         pthread_mutex_unlock(&sc->mtx);
335 }
336
337 static void
338 pci_uart_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
339                int baridx, uint64_t offset, int size, uint64_t value)
340 {
341         struct pci_uart_softc *sc;
342         int fifosz;
343         uint8_t msr;
344
345         sc = pi->pi_arg;
346
347         assert(baridx == 0);
348         assert(size == 1);
349
350         /* Open terminal */
351         if (!sc->opened && sc->stdio) {
352                 pci_uart_opentty(sc);
353                 sc->opened = 1;
354         }
355
356         pthread_mutex_lock(&sc->mtx);
357         
358         /*
359          * Take care of the special case DLAB accesses first
360          */
361         if ((sc->lcr & LCR_DLAB) != 0) {
362                 if (offset == REG_DLL) {
363                         sc->dll = value;
364                         goto done;
365                 }
366                 
367                 if (offset == REG_DLH) {
368                         sc->dlh = value;
369                         goto done;
370                 }
371         }
372
373         switch (offset) {
374         case REG_DATA:
375                 if (sc->mcr & MCR_LOOPBACK) {
376                         if (fifo_putchar(&sc->rxfifo, value) != 0)
377                                 sc->lsr |= LSR_OE;
378                 } else if (sc->stdio) {
379                         ttywrite(value);
380                 } /* else drop on floor */
381                 sc->thre_int_pending = true;
382                 break;
383         case REG_IER:
384                 /*
385                  * Apply mask so that bits 4-7 are 0
386                  * Also enables bits 0-3 only if they're 1
387                  */
388                 sc->ier = value & 0x0F;
389                 break;
390                 case REG_FCR:
391                         /*
392                          * When moving from FIFO and 16450 mode and vice versa,
393                          * the FIFO contents are reset.
394                          */
395                         if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) {
396                                 fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1;
397                                 fifo_reset(&sc->rxfifo, fifosz);
398                         }
399
400                         /*
401                          * The FCR_ENABLE bit must be '1' for the programming
402                          * of other FCR bits to be effective.
403                          */
404                         if ((value & FCR_ENABLE) == 0) {
405                                 sc->fcr = 0;
406                         } else {
407                                 if ((value & FCR_RCV_RST) != 0)
408                                         fifo_reset(&sc->rxfifo, FIFOSZ);
409
410                                 sc->fcr = value &
411                                          (FCR_ENABLE | FCR_DMA | FCR_RX_MASK);
412                         }
413                         break;
414                 case REG_LCR:
415                         sc->lcr = value;
416                         break;
417                 case REG_MCR:
418                         /* Apply mask so that bits 5-7 are 0 */
419                         sc->mcr = value & 0x1F;
420
421                         msr = 0;
422                         if (sc->mcr & MCR_LOOPBACK) {
423                                 /*
424                                  * In the loopback mode certain bits from the
425                                  * MCR are reflected back into MSR
426                                  */
427                                 if (sc->mcr & MCR_RTS)
428                                         msr |= MSR_CTS;
429                                 if (sc->mcr & MCR_DTR)
430                                         msr |= MSR_DSR;
431                                 if (sc->mcr & MCR_OUT1)
432                                         msr |= MSR_RI;
433                                 if (sc->mcr & MCR_OUT2)
434                                         msr |= MSR_DCD;
435                         }
436
437                         /*
438                          * Detect if there has been any change between the
439                          * previous and the new value of MSR. If there is
440                          * then assert the appropriate MSR delta bit.
441                          */
442                         if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS))
443                                 sc->msr |= MSR_DCTS;
444                         if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR))
445                                 sc->msr |= MSR_DDSR;
446                         if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD))
447                                 sc->msr |= MSR_DDCD;
448                         if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0)
449                                 sc->msr |= MSR_TERI;
450
451                         /*
452                          * Update the value of MSR while retaining the delta
453                          * bits.
454                          */
455                         sc->msr &= MSR_DELTA_MASK;
456                         sc->msr |= msr;
457                         break;
458                 case REG_LSR:
459                         /*
460                          * Line status register is not meant to be written to
461                          * during normal operation.
462                          */
463                         break;
464                 case REG_MSR:
465                         /*
466                          * As far as I can tell MSR is a read-only register.
467                          */
468                         break;
469                 case REG_SCR:
470                         sc->scr = value;
471                         break;
472                 default:
473                         break;
474         }
475
476 done:
477         pci_uart_toggle_intr(sc);
478         pthread_mutex_unlock(&sc->mtx);
479 }
480
481 uint64_t
482 pci_uart_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
483               int baridx, uint64_t offset, int size)
484 {
485         struct pci_uart_softc *sc;
486         uint8_t iir, intr_reason;
487         uint64_t reg;
488
489         sc = pi->pi_arg;
490
491         assert(baridx == 0);
492         assert(size == 1);
493
494         /* Open terminal */
495         if (!sc->opened && sc->stdio) {
496                 pci_uart_opentty(sc);
497                 sc->opened = 1;
498         }
499
500         pthread_mutex_lock(&sc->mtx);
501
502         /*
503          * Take care of the special case DLAB accesses first
504          */
505         if ((sc->lcr & LCR_DLAB) != 0) {
506                 if (offset == REG_DLL) {
507                         reg = sc->dll;
508                         goto done;
509                 }
510                 
511                 if (offset == REG_DLH) {
512                         reg = sc->dlh;
513                         goto done;
514                 }
515         }
516
517         switch (offset) {
518         case REG_DATA:
519                 reg = fifo_getchar(&sc->rxfifo);
520                 break;
521         case REG_IER:
522                 reg = sc->ier;
523                 break;
524         case REG_IIR:
525                 iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0;
526
527                 intr_reason = pci_uart_intr_reason(sc);
528                         
529                 /*
530                  * Deal with side effects of reading the IIR register
531                  */
532                 if (intr_reason == IIR_TXRDY)
533                         sc->thre_int_pending = false;
534
535                 iir |= intr_reason;
536
537                 reg = iir;
538                 break;
539         case REG_LCR:
540                 reg = sc->lcr;
541                 break;
542         case REG_MCR:
543                 reg = sc->mcr;
544                 break;
545         case REG_LSR:
546                 /* Transmitter is always ready for more data */
547                 sc->lsr |= LSR_TEMT | LSR_THRE;
548
549                 /* Check for new receive data */
550                 if (fifo_numchars(&sc->rxfifo) > 0)
551                         sc->lsr |= LSR_RXRDY;
552                 else
553                         sc->lsr &= ~LSR_RXRDY;
554
555                 reg = sc->lsr;
556
557                 /* The LSR_OE bit is cleared on LSR read */
558                 sc->lsr &= ~LSR_OE;
559                 break;
560         case REG_MSR:
561                 /*
562                  * MSR delta bits are cleared on read
563                  */
564                 reg = sc->msr;
565                 sc->msr &= ~MSR_DELTA_MASK;
566                 break;
567         case REG_SCR:
568                 reg = sc->scr;
569                 break;
570         default:
571                 reg = 0xFF;
572                 break;
573         }
574
575 done:
576         pci_uart_toggle_intr(sc);
577         pthread_mutex_unlock(&sc->mtx);
578
579         return (reg);
580 }
581
582 static int
583 pci_uart_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
584 {
585         struct pci_uart_softc *sc;
586         uint64_t bar;
587         int ivec;
588
589         sc = malloc(sizeof(struct pci_uart_softc));
590         memset(sc, 0, sizeof(struct pci_uart_softc));
591
592         pi->pi_arg = sc;
593         sc->pi = pi;
594
595         pthread_mutex_init(&sc->mtx, NULL);
596
597         /* initialize config space */
598         pci_set_cfgdata16(pi, PCIR_DEVICE, COM_DEV);
599         pci_set_cfgdata16(pi, PCIR_VENDOR, COM_VENDOR);
600         pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_SIMPLECOMM);
601         if (pci_is_legacy(pi)) {
602                 pci_uart_legacy_res(&bar, &ivec);
603                 pci_emul_alloc_pbar(pi, 0, bar, PCIBAR_IO, 8);
604         } else {
605                 ivec = -1;
606                 pci_emul_alloc_bar(pi, 0, PCIBAR_IO, 8);
607         }
608         pci_lintr_request(pi, ivec);
609
610         if (opts != NULL && !strcmp("stdio", opts) && !pci_uart_stdio) {
611                 pci_uart_stdio = 1;
612                 sc->stdio = 1;
613         }
614
615         pci_uart_reset(sc);
616
617         return (0);
618 }
619
620 struct pci_devemu pci_de_com = {
621         .pe_emu =       "uart",
622         .pe_init =      pci_uart_init,
623         .pe_barwrite =  pci_uart_write,
624         .pe_barread =   pci_uart_read
625 };
626 PCI_EMUL_SET(pci_de_com);