1 /*
2 * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 /*
27 * This code is shared on BIOS and UEFI systems on x86 because
28 * we can access io ports on both platforms and the UEFI Serial IO protocol
29 * is not giving us reliable port order and we see issues with input.
30 */
31 #include <sys/cdefs.h>
32
33 #include <stand.h>
34 #include <bootstrap.h>
35 #include <stdbool.h>
36 #include <machine/cpufunc.h>
37 #include <dev/ic/ns16550.h>
38 #include <dev/pci/pcireg.h>
39 #include "libi386.h"
40
41 #define COMC_TXWAIT 0x40000 /* transmit timeout */
42 #define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */
43 #define COMC_DIV2BPS(x) (115200 / (x)) /* DLAB divisor to speed */
44
45 #ifndef COMSPEED
46 #define COMSPEED 9600
47 #endif
48
49 #define COM_NPORTS 4
50 #define COM1_IOADDR 0x3f8
51 #define COM2_IOADDR 0x2f8
52 #define COM3_IOADDR 0x3e8
53 #define COM4_IOADDR 0x2e8
54
55 #define STOP1 0x00
56 #define STOP2 0x04
57
58 #define PARODD 0x00
59 #define PAREN 0x08
60 #define PAREVN 0x10
61 #define PARMARK 0x20
62
63 #define BITS5 0x00 /* 5 bits per char */
64 #define BITS6 0x01 /* 6 bits per char */
65 #define BITS7 0x02 /* 7 bits per char */
66 #define BITS8 0x03 /* 8 bits per char */
67
68 struct serial {
69 int speed; /* baud rate */
70 uint8_t lcr; /* line control */
71 uint8_t ignore_cd; /* boolean */
72 uint8_t rtsdtr_off; /* boolean */
73 int ioaddr;
74 uint32_t locator;
75 };
76
77 static void comc_probe(struct console *);
78 static int comc_init(struct console *, int);
79 static void comc_putchar(struct console *, int);
80 static int comc_getchar(struct console *);
81 int comc_getspeed(int);
82 static int comc_ischar(struct console *);
83 static int comc_ioctl(struct console *, int, void *);
84 static uint32_t comc_parse_pcidev(const char *);
85 static int comc_pcidev_set(struct env_var *, int, const void *);
86 static int comc_pcidev_handle(struct console *, uint32_t);
87 static bool comc_setup(struct console *);
88 static char *comc_asprint_mode(struct serial *);
89 static int comc_parse_mode(struct serial *, const char *);
90 static int comc_mode_set(struct env_var *, int, const void *);
91 static int comc_cd_set(struct env_var *, int, const void *);
92 static int comc_rtsdtr_set(struct env_var *, int, const void *);
93 static void comc_devinfo(struct console *);
94
95 static void
comc_devinfo(struct console * cp)96 comc_devinfo(struct console *cp)
97 {
98 struct serial *port = cp->c_private;
99
100 printf("\tport %#x", port->ioaddr);
101 }
102
103 static bool
comc_port_is_present(int ioaddr)104 comc_port_is_present(int ioaddr)
105 {
106 /*
107 * Write byte to scratch register and read it out.
108 */
109 #define COMC_TEST 0xbb
110 outb(ioaddr + com_scr, COMC_TEST);
111 return (inb(ioaddr + com_scr) == COMC_TEST);
112 }
113
114 /*
115 * Set up list of possible serial consoles.
116 * This function is run very early, so we do not expect to
117 * run out of memory, and on error, we can not print output.
118 */
119 void
comc_ini(void)120 comc_ini(void)
121 {
122 uint_t n = 0, c;
123 bool ports[COM_NPORTS];
124 struct console **tmp;
125 struct console *tty;
126 struct serial *port;
127
128 /*
129 * Test the presence of 4 serial devices com1-com4
130 */
131 ports[0] = comc_port_is_present(COM1_IOADDR);
132 ports[1] = comc_port_is_present(COM2_IOADDR);
133 ports[2] = comc_port_is_present(COM3_IOADDR);
134 ports[3] = comc_port_is_present(COM4_IOADDR);
135
136 for (uint_t i = 0; i < COM_NPORTS; i++)
137 if (ports[i])
138 n++;
139
140 if (n == 0) /* there are no serial ports */
141 return;
142
143 c = cons_array_size();
144 if (c == 0)
145 n++; /* For NULL pointer */
146
147 tmp = realloc(consoles, (c + n) * sizeof (*consoles));
148 if (tmp == NULL)
149 return;
150 consoles = tmp;
151 if (c > 0)
152 c--;
153
154 for (uint_t i = 0; i < COM_NPORTS; i++) {
155 if (!ports[i])
156 continue;
157 tty = malloc(sizeof (*tty));
158 if (tty == NULL) {
159 /* Out of memory?! can not continue */
160 consoles[c] = tty;
161 return;
162 }
163 if (asprintf(&tty->c_name, "tty%c", 'a' + i) < 0) {
164 free(tty);
165 consoles[c] = NULL;
166 return;
167 }
168 if (asprintf(&tty->c_desc, "serial port %c", 'a' + i) < 0) {
169 free(tty->c_name);
170 free(tty);
171 consoles[c] = NULL;
172 return;
173 }
174 tty->c_flags = 0;
175 tty->c_probe = comc_probe;
176 tty->c_init = comc_init;
177 tty->c_out = comc_putchar;
178 tty->c_in = comc_getchar;
179 tty->c_ready = comc_ischar;
180 tty->c_ioctl = comc_ioctl;
181 tty->c_devinfo = comc_devinfo;
182 port = malloc(sizeof (*port));
183 if (port == NULL) {
184 free(tty->c_name);
185 free(tty->c_desc);
186 free(tty);
187 consoles[c] = NULL;
188 return;
189 }
190 port->speed = 0; /* Leave this for comc_probe */
191 switch (i) {
192 case 0:
193 port->ioaddr = COM1_IOADDR;
194 break;
195 case 1:
196 port->ioaddr = COM2_IOADDR;
197 break;
198 case 2:
199 port->ioaddr = COM3_IOADDR;
200 break;
201 case 3:
202 port->ioaddr = COM4_IOADDR;
203 break;
204 }
205 port->speed = comc_getspeed(port->ioaddr);
206 port->lcr = BITS8; /* 8,n,1 */
207 port->ignore_cd = 1; /* ignore cd */
208 port->rtsdtr_off = 0; /* rts-dtr is on */
209
210 tty->c_private = port;
211 consoles[c++] = tty;
212
213 /* Reset terminal to initial normal settings with ESC [ 0 m */
214 comc_putchar(tty, 0x1b);
215 comc_putchar(tty, '[');
216 comc_putchar(tty, '0');
217 comc_putchar(tty, 'm');
218 /* drain input from random data */
219 while (comc_getchar(tty) != -1)
220 ;
221 }
222 consoles[c] = NULL;
223 }
224
225 static void
comc_probe(struct console * cp)226 comc_probe(struct console *cp)
227 {
228 struct serial *port;
229 char name[20];
230 char value[20];
231 char *env;
232
233 port = cp->c_private;
234 if (port->speed != 0)
235 return;
236
237 port->speed = COMSPEED;
238
239 /*
240 * Assume that the speed was set by an earlier boot loader if
241 * comconsole is already the preferred console.
242 */
243 snprintf(name, sizeof (name), "%s-mode", cp->c_name);
244 env = getenv(name);
245 if (env != NULL) {
246 port->speed = comc_getspeed(port->ioaddr);
247 }
248 env = comc_asprint_mode(port);
249
250 if (env != NULL) {
251 unsetenv(name);
252 env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset);
253 free(env);
254 }
255
256 snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name);
257 env = getenv(name);
258 if (env != NULL) {
259 if (strcmp(env, "true") == 0)
260 port->ignore_cd = 1;
261 else if (strcmp(env, "false") == 0)
262 port->ignore_cd = 0;
263 }
264
265 snprintf(value, sizeof (value), "%s",
266 port->ignore_cd? "true" : "false");
267 unsetenv(name);
268 env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset);
269
270 snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name);
271 env = getenv(name);
272 if (env != NULL) {
273 if (strcmp(env, "true") == 0)
274 port->rtsdtr_off = 1;
275 else if (strcmp(env, "false") == 0)
276 port->rtsdtr_off = 0;
277 }
278
279 snprintf(value, sizeof (value), "%s",
280 port->rtsdtr_off? "true" : "false");
281 unsetenv(name);
282 env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset);
283
284 snprintf(name, sizeof (name), "%s-pcidev", cp->c_name);
285 env = getenv(name);
286 if (env != NULL) {
287 port->locator = comc_parse_pcidev(env);
288 if (port->locator != 0)
289 comc_pcidev_handle(cp, port->locator);
290 }
291
292 unsetenv(name);
293 env_setenv(name, EV_VOLATILE, env, comc_pcidev_set, env_nounset);
294
295 cp->c_flags = 0;
296 if (comc_setup(cp))
297 cp->c_flags = C_PRESENTIN | C_PRESENTOUT;
298 }
299
300 static int
comc_init(struct console * cp,int arg __attribute ((unused)))301 comc_init(struct console *cp, int arg __attribute((unused)))
302 {
303
304 if (comc_setup(cp))
305 return (CMD_OK);
306
307 cp->c_flags = 0;
308 return (CMD_ERROR);
309 }
310
311 static void
comc_putchar(struct console * cp,int c)312 comc_putchar(struct console *cp, int c)
313 {
314 int wait;
315 struct serial *sp = cp->c_private;
316
317 for (wait = COMC_TXWAIT; wait > 0; wait--)
318 if (inb(sp->ioaddr + com_lsr) & LSR_TXRDY) {
319 outb(sp->ioaddr + com_data, (uchar_t)c);
320 break;
321 }
322 }
323
324 static int
comc_getchar(struct console * cp)325 comc_getchar(struct console *cp)
326 {
327 struct serial *sp = cp->c_private;
328 return (comc_ischar(cp) ? inb(sp->ioaddr + com_data) : -1);
329 }
330
331 static int
comc_ischar(struct console * cp)332 comc_ischar(struct console *cp)
333 {
334 struct serial *sp = cp->c_private;
335 return (inb(sp->ioaddr + com_lsr) & LSR_RXRDY);
336 }
337
338 static int
comc_ioctl(struct console * cp __unused,int cmd __unused,void * data __unused)339 comc_ioctl(struct console *cp __unused, int cmd __unused, void *data __unused)
340 {
341 return (ENOTTY);
342 }
343
344 static char *
comc_asprint_mode(struct serial * sp)345 comc_asprint_mode(struct serial *sp)
346 {
347 char par, *buf;
348
349 if (sp == NULL)
350 return (NULL);
351
352 if ((sp->lcr & (PAREN|PAREVN)) == (PAREN|PAREVN))
353 par = 'e';
354 else if ((sp->lcr & PAREN) == PAREN)
355 par = 'o';
356 else
357 par = 'n';
358
359 asprintf(&buf, "%d,%d,%c,%d,-", sp->speed,
360 (sp->lcr & BITS8) == BITS8? 8:7,
361 par, (sp->lcr & STOP2) == STOP2? 2:1);
362 return (buf);
363 }
364
365 static int
comc_parse_mode(struct serial * sp,const char * value)366 comc_parse_mode(struct serial *sp, const char *value)
367 {
368 unsigned long n;
369 int speed;
370 int lcr;
371 char *ep;
372
373 if (value == NULL || *value == '\0')
374 return (CMD_ERROR);
375
376 errno = 0;
377 n = strtoul(value, &ep, 10);
378 if (errno != 0 || *ep != ',')
379 return (CMD_ERROR);
380 speed = n;
381
382 ep++;
383 errno = 0;
384 n = strtoul(ep, &ep, 10);
385 if (errno != 0 || *ep != ',')
386 return (CMD_ERROR);
387
388 switch (n) {
389 case 7: lcr = BITS7;
390 break;
391 case 8: lcr = BITS8;
392 break;
393 default:
394 return (CMD_ERROR);
395 }
396
397 ep++;
398 switch (*ep++) {
399 case 'n':
400 break;
401 case 'e': lcr |= PAREN|PAREVN;
402 break;
403 case 'o': lcr |= PAREN|PARODD;
404 break;
405 default:
406 return (CMD_ERROR);
407 }
408
409 if (*ep == ',')
410 ep++;
411 else
412 return (CMD_ERROR);
413
414 switch (*ep++) {
415 case '1':
416 break;
417 case '2': lcr |= STOP2;
418 break;
419 default:
420 return (CMD_ERROR);
421 }
422
423 /* handshake is ignored, but we check syntax anyhow */
424 if (*ep == ',')
425 ep++;
426 else
427 return (CMD_ERROR);
428
429 switch (*ep++) {
430 case '-':
431 case 'h':
432 case 's':
433 break;
434 default:
435 return (CMD_ERROR);
436 }
437
438 if (*ep != '\0')
439 return (CMD_ERROR);
440
441 sp->speed = speed;
442 sp->lcr = lcr;
443 return (CMD_OK);
444 }
445
446 static struct console *
get_console(const char * name)447 get_console(const char *name)
448 {
449 char port[5];
450
451 (void) strlcpy(port, name, sizeof (port));
452 for (uint_t i = 0; consoles[i] != NULL; i++) {
453 if (strcmp(port, consoles[i]->c_name) == 0)
454 return (consoles[i]);
455 }
456
457 printf("No such port: %s\n", port);
458 return (NULL);
459 }
460
461 /*
462 * CMD_ERROR will cause set/setenv/setprop command to fail,
463 * when used in loader scripts (forth), this will cause processing
464 * of boot scripts to fail, rendering bootloading impossible.
465 * To prevent such unfortunate situation, we return CMD_OK when
466 * there is no such port, or there is invalid value in mode line.
467 */
468 static int
comc_mode_set(struct env_var * ev,int flags,const void * value)469 comc_mode_set(struct env_var *ev, int flags, const void *value)
470 {
471 struct console *cp;
472 char name[15];
473
474 if (value == NULL)
475 return (CMD_ERROR);
476
477 if ((cp = get_console(ev->ev_name)) == NULL)
478 return (CMD_OK);
479
480 /* Do not override serial setup from SPCR */
481 snprintf(name, sizeof (name), "%s-spcr-mode", cp->c_name);
482 if (getenv(name) == NULL) {
483 if (comc_parse_mode(cp->c_private, value) == CMD_ERROR) {
484 printf("%s: invalid mode: %s\n", ev->ev_name,
485 (char *)value);
486 return (CMD_OK);
487 }
488 (void) comc_setup(cp);
489 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
490 }
491
492 return (CMD_OK);
493 }
494
495 /*
496 * CMD_ERROR will cause set/setenv/setprop command to fail,
497 * when used in loader scripts (forth), this will cause processing
498 * of boot scripts to fail, rendering bootloading impossible.
499 * To prevent such unfortunate situation, we return CMD_OK when
500 * there is no such port or invalid value was used.
501 */
502 static int
comc_cd_set(struct env_var * ev,int flags,const void * value)503 comc_cd_set(struct env_var *ev, int flags, const void *value)
504 {
505 struct console *cp;
506 struct serial *sp;
507
508 if (value == NULL)
509 return (CMD_ERROR);
510
511 if ((cp = get_console(ev->ev_name)) == NULL)
512 return (CMD_OK);
513
514 sp = cp->c_private;
515 if (strcmp(value, "true") == 0) {
516 sp->ignore_cd = 1;
517 } else if (strcmp(value, "false") == 0) {
518 sp->ignore_cd = 0;
519 } else {
520 printf("%s: invalid value: %s\n", ev->ev_name,
521 (char *)value);
522 return (CMD_ERROR);
523 }
524
525 (void) comc_setup(cp);
526
527 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
528
529 return (CMD_OK);
530 }
531
532 /*
533 * CMD_ERROR will cause set/setenv/setprop command to fail,
534 * when used in loader scripts (forth), this will cause processing
535 * of boot scripts to fail, rendering bootloading impossible.
536 * To prevent such unfortunate situation, we return CMD_OK when
537 * there is no such port, or invalid value was used.
538 */
539 static int
comc_rtsdtr_set(struct env_var * ev,int flags,const void * value)540 comc_rtsdtr_set(struct env_var *ev, int flags, const void *value)
541 {
542 struct console *cp;
543 struct serial *sp;
544
545 if (value == NULL)
546 return (CMD_ERROR);
547
548 if ((cp = get_console(ev->ev_name)) == NULL)
549 return (CMD_OK);
550
551 sp = cp->c_private;
552 if (strcmp(value, "true") == 0) {
553 sp->rtsdtr_off = 1;
554 } else if (strcmp(value, "false") == 0) {
555 sp->rtsdtr_off = 0;
556 } else {
557 printf("%s: invalid value: %s\n", ev->ev_name,
558 (char *)value);
559 return (CMD_ERROR);
560 }
561
562 (void) comc_setup(cp);
563
564 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
565
566 return (CMD_OK);
567 }
568
569 /*
570 * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10.
571 * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0]
572 */
573 static uint32_t
comc_parse_pcidev(const char * string)574 comc_parse_pcidev(const char *string)
575 {
576 #ifdef EFI
577 (void) string;
578 return (0);
579 #else
580 char *p, *p1;
581 uint8_t bus, dev, func, bar;
582 uint32_t locator;
583 int pres;
584
585 errno = 0;
586 pres = strtoul(string, &p, 10);
587 if (errno != 0 || p == string || *p != ':' || pres < 0)
588 return (0);
589 bus = pres;
590 p1 = ++p;
591
592 pres = strtoul(p1, &p, 10);
593 if (errno != 0 || p == string || *p != ':' || pres < 0)
594 return (0);
595 dev = pres;
596 p1 = ++p;
597
598 pres = strtoul(p1, &p, 10);
599 if (errno != 0 || p == string || (*p != ':' && *p != '\0') || pres < 0)
600 return (0);
601 func = pres;
602
603 if (*p == ':') {
604 p1 = ++p;
605 pres = strtoul(p1, &p, 10);
606 if (errno != 0 || p == string || *p != '\0' || pres <= 0)
607 return (0);
608 bar = pres;
609 } else
610 bar = 0x10;
611
612 locator = (bar << 16) | biospci_locator(bus, dev, func);
613 return (locator);
614 #endif
615 }
616
617 static int
comc_pcidev_handle(struct console * cp,uint32_t locator)618 comc_pcidev_handle(struct console *cp, uint32_t locator)
619 {
620 #ifdef EFI
621 (void) cp;
622 (void) locator;
623 return (CMD_ERROR);
624 #else
625 struct serial *sp = cp->c_private;
626 uint32_t port;
627
628 if (biospci_read_config(locator & 0xffff,
629 (locator & 0xff0000) >> 16, 2, &port) == -1) {
630 printf("Cannot read bar at 0x%x\n", locator);
631 return (CMD_ERROR);
632 }
633 if (!PCI_BAR_IO(port)) {
634 printf("Memory bar at 0x%x\n", locator);
635 return (CMD_ERROR);
636 }
637 port &= PCIM_BAR_IO_BASE;
638
639 (void) comc_setup(cp);
640
641 sp->locator = locator;
642
643 return (CMD_OK);
644 #endif
645 }
646
647 static int
comc_pcidev_set(struct env_var * ev,int flags,const void * value)648 comc_pcidev_set(struct env_var *ev, int flags, const void *value)
649 {
650 struct console *cp;
651 struct serial *sp;
652 uint32_t locator;
653 int error;
654
655 if ((cp = get_console(ev->ev_name)) == NULL)
656 return (CMD_ERROR);
657 sp = cp->c_private;
658
659 if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) {
660 printf("Invalid pcidev\n");
661 return (CMD_ERROR);
662 }
663 if ((cp->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 &&
664 sp->locator != locator) {
665 error = comc_pcidev_handle(cp, locator);
666 if (error != CMD_OK)
667 return (error);
668 }
669 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
670 return (CMD_OK);
671 }
672
673 /*
674 * In case of error, we also reset ACTIVE flags, so the console
675 * framefork will try alternate consoles.
676 */
677 static bool
comc_setup(struct console * cp)678 comc_setup(struct console *cp)
679 {
680 struct serial *sp = cp->c_private;
681 static int TRY_COUNT = 1000000;
682 int tries;
683
684 outb(sp->ioaddr + com_cfcr, CFCR_DLAB | sp->lcr);
685 outb(sp->ioaddr + com_dlbl, COMC_BPS(sp->speed) & 0xff);
686 outb(sp->ioaddr + com_dlbh, COMC_BPS(sp->speed) >> 8);
687 outb(sp->ioaddr + com_cfcr, sp->lcr);
688 outb(sp->ioaddr + com_mcr,
689 sp->rtsdtr_off? ~(MCR_RTS | MCR_DTR) : MCR_RTS | MCR_DTR);
690
691 tries = 0;
692 do {
693 inb(sp->ioaddr + com_data);
694 } while (inb(sp->ioaddr + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT);
695
696 if (tries == TRY_COUNT)
697 return (false);
698 /* Mark this port usable. */
699 cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
700 return (true);
701 }
702
703 int
comc_getspeed(int ioaddr)704 comc_getspeed(int ioaddr)
705 {
706 uint_t divisor;
707 uchar_t dlbh;
708 uchar_t dlbl;
709 uchar_t cfcr;
710
711 cfcr = inb(ioaddr + com_cfcr);
712 outb(ioaddr + com_cfcr, CFCR_DLAB | cfcr);
713
714 dlbl = inb(ioaddr + com_dlbl);
715 dlbh = inb(ioaddr + com_dlbh);
716
717 outb(ioaddr + com_cfcr, cfcr);
718
719 divisor = dlbh << 8 | dlbl;
720
721 /* XXX there should be more sanity checking. */
722 if (divisor == 0)
723 return (COMSPEED);
724 return (COMC_DIV2BPS(divisor));
725 }
726