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 };
75
76 static void comc_probe(struct console *);
77 static int comc_init(struct console *, int);
78 static void comc_putchar(struct console *, int);
79 static int comc_getchar(struct console *);
80 int comc_getspeed(int);
81 static int comc_ischar(struct console *);
82 static int comc_ioctl(struct console *, int, void *);
83 static bool comc_setup(struct console *);
84 static char *comc_asprint_mode(struct serial *);
85 static int comc_parse_mode(struct serial *, const char *);
86 static int comc_mode_set(struct env_var *, int, const void *);
87 static int comc_cd_set(struct env_var *, int, const void *);
88 static int comc_rtsdtr_set(struct env_var *, int, const void *);
89 static void comc_devinfo(struct console *);
90
91 static void
comc_devinfo(struct console * cp)92 comc_devinfo(struct console *cp)
93 {
94 struct serial *port = cp->c_private;
95
96 printf("\tport %#x", port->ioaddr);
97 }
98
99 static bool
comc_port_is_present(int ioaddr)100 comc_port_is_present(int ioaddr)
101 {
102 /*
103 * Write byte to scratch register and read it out.
104 */
105 #define COMC_TEST 0xbb
106 outb(ioaddr + com_scr, COMC_TEST);
107 return (inb(ioaddr + com_scr) == COMC_TEST);
108 }
109
110 /*
111 * Set up following environment variables:
112 * ttyX-mode
113 * ttyX-rts-dtr-off
114 * ttyX-ignore-cd
115 */
116 static void
comc_setup_env(struct console * tty)117 comc_setup_env(struct console *tty)
118 {
119 struct serial *port;
120 char name[20];
121 char value[20];
122 char *env;
123
124 port = tty->c_private;
125 snprintf(name, sizeof (name), "%s-mode", tty->c_name);
126 env = comc_asprint_mode(port);
127 if (env != NULL) {
128 unsetenv(name);
129 env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset);
130 free(env);
131 }
132
133 snprintf(name, sizeof (name), "%s-rts-dtr-off", tty->c_name);
134 snprintf(value, sizeof (value), "%s",
135 port->rtsdtr_off? "true" : "false");
136 unsetenv(name);
137 env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset);
138
139 snprintf(name, sizeof (name), "%s-ignore-cd", tty->c_name);
140 snprintf(value, sizeof (value), "%s",
141 port->ignore_cd? "true" : "false");
142 unsetenv(name);
143 env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset);
144 }
145
146 /*
147 * Set up list of possible serial consoles.
148 * This function is run very early, so we do not expect to
149 * run out of memory, and on error, we can not print output.
150 */
151 void
comc_ini(void)152 comc_ini(void)
153 {
154 uint_t n = 0, c;
155 bool ports[COM_NPORTS];
156 struct console **tmp;
157 struct console *tty;
158 struct serial *port;
159
160 /*
161 * Test the presence of 4 serial devices com1-com4
162 */
163 ports[0] = comc_port_is_present(COM1_IOADDR);
164 ports[1] = comc_port_is_present(COM2_IOADDR);
165 ports[2] = comc_port_is_present(COM3_IOADDR);
166 ports[3] = comc_port_is_present(COM4_IOADDR);
167
168 for (uint_t i = 0; i < COM_NPORTS; i++)
169 if (ports[i])
170 n++;
171
172 if (n == 0) /* there are no serial ports */
173 return;
174
175 c = cons_array_size();
176 if (c == 0)
177 n++; /* For NULL pointer */
178
179 tmp = realloc(consoles, (c + n) * sizeof (*consoles));
180 if (tmp == NULL)
181 return;
182 consoles = tmp;
183 if (c > 0)
184 c--;
185
186 for (uint_t i = 0; i < COM_NPORTS; i++) {
187 if (!ports[i])
188 continue;
189 tty = malloc(sizeof (*tty));
190 if (tty == NULL) {
191 /* Out of memory?! can not continue */
192 consoles[c] = tty;
193 return;
194 }
195 if (asprintf(&tty->c_name, "tty%c", 'a' + i) < 0) {
196 free(tty);
197 consoles[c] = NULL;
198 return;
199 }
200 if (asprintf(&tty->c_desc, "serial port %c", 'a' + i) < 0) {
201 free(tty->c_name);
202 free(tty);
203 consoles[c] = NULL;
204 return;
205 }
206 tty->c_flags = 0;
207 tty->c_probe = comc_probe;
208 tty->c_init = comc_init;
209 tty->c_out = comc_putchar;
210 tty->c_in = comc_getchar;
211 tty->c_ready = comc_ischar;
212 tty->c_ioctl = comc_ioctl;
213 tty->c_devinfo = comc_devinfo;
214 port = malloc(sizeof (*port));
215 if (port == NULL) {
216 free(tty->c_name);
217 free(tty->c_desc);
218 free(tty);
219 consoles[c] = NULL;
220 return;
221 }
222 port->speed = 0; /* Leave this for comc_probe */
223 switch (i) {
224 case 0:
225 port->ioaddr = COM1_IOADDR;
226 break;
227 case 1:
228 port->ioaddr = COM2_IOADDR;
229 break;
230 case 2:
231 port->ioaddr = COM3_IOADDR;
232 break;
233 case 3:
234 port->ioaddr = COM4_IOADDR;
235 break;
236 }
237 port->speed = comc_getspeed(port->ioaddr);
238 port->lcr = BITS8; /* 8,n,1 */
239 port->ignore_cd = 1; /* ignore cd */
240 port->rtsdtr_off = 0; /* rts-dtr is on */
241
242 tty->c_private = port;
243 consoles[c++] = tty;
244 comc_setup_env(tty);
245
246 /* Reset terminal to initial normal settings with ESC [ 0 m */
247 comc_putchar(tty, 0x1b);
248 comc_putchar(tty, '[');
249 comc_putchar(tty, '0');
250 comc_putchar(tty, 'm');
251 /* drain input from random data */
252 while (comc_getchar(tty) != -1)
253 ;
254 }
255 consoles[c] = NULL;
256 }
257
258 static void
comc_probe(struct console * cp)259 comc_probe(struct console *cp)
260 {
261 cp->c_flags = 0;
262 if (comc_setup(cp))
263 cp->c_flags = C_PRESENTIN | C_PRESENTOUT;
264 }
265
266 static int
comc_init(struct console * cp,int arg __attribute ((unused)))267 comc_init(struct console *cp, int arg __attribute((unused)))
268 {
269
270 if (comc_setup(cp))
271 return (CMD_OK);
272
273 cp->c_flags = 0;
274 return (CMD_ERROR);
275 }
276
277 static void
comc_putchar(struct console * cp,int c)278 comc_putchar(struct console *cp, int c)
279 {
280 int wait;
281 struct serial *sp = cp->c_private;
282
283 for (wait = COMC_TXWAIT; wait > 0; wait--)
284 if (inb(sp->ioaddr + com_lsr) & LSR_TXRDY) {
285 outb(sp->ioaddr + com_data, (uchar_t)c);
286 break;
287 }
288 }
289
290 static int
comc_getchar(struct console * cp)291 comc_getchar(struct console *cp)
292 {
293 struct serial *sp = cp->c_private;
294 return (comc_ischar(cp) ? inb(sp->ioaddr + com_data) : -1);
295 }
296
297 static int
comc_ischar(struct console * cp)298 comc_ischar(struct console *cp)
299 {
300 struct serial *sp = cp->c_private;
301 return (inb(sp->ioaddr + com_lsr) & LSR_RXRDY);
302 }
303
304 static int
comc_ioctl(struct console * cp __unused,int cmd __unused,void * data __unused)305 comc_ioctl(struct console *cp __unused, int cmd __unused, void *data __unused)
306 {
307 return (ENOTTY);
308 }
309
310 static char *
comc_asprint_mode(struct serial * sp)311 comc_asprint_mode(struct serial *sp)
312 {
313 char par, *buf;
314
315 if (sp == NULL)
316 return (NULL);
317
318 if ((sp->lcr & (PAREN|PAREVN)) == (PAREN|PAREVN))
319 par = 'e';
320 else if ((sp->lcr & PAREN) == PAREN)
321 par = 'o';
322 else
323 par = 'n';
324
325 asprintf(&buf, "%d,%d,%c,%d,-", sp->speed,
326 (sp->lcr & BITS8) == BITS8? 8:7,
327 par, (sp->lcr & STOP2) == STOP2? 2:1);
328 return (buf);
329 }
330
331 static int
comc_parse_mode(struct serial * sp,const char * value)332 comc_parse_mode(struct serial *sp, const char *value)
333 {
334 unsigned long n;
335 int speed;
336 int lcr;
337 char *ep;
338
339 if (value == NULL || *value == '\0')
340 return (CMD_ERROR);
341
342 errno = 0;
343 n = strtoul(value, &ep, 10);
344 if (errno != 0 || *ep != ',')
345 return (CMD_ERROR);
346 speed = n;
347
348 ep++;
349 errno = 0;
350 n = strtoul(ep, &ep, 10);
351 if (errno != 0 || *ep != ',')
352 return (CMD_ERROR);
353
354 switch (n) {
355 case 7: lcr = BITS7;
356 break;
357 case 8: lcr = BITS8;
358 break;
359 default:
360 return (CMD_ERROR);
361 }
362
363 ep++;
364 switch (*ep++) {
365 case 'n':
366 break;
367 case 'e': lcr |= PAREN|PAREVN;
368 break;
369 case 'o': lcr |= PAREN|PARODD;
370 break;
371 default:
372 return (CMD_ERROR);
373 }
374
375 if (*ep == ',')
376 ep++;
377 else
378 return (CMD_ERROR);
379
380 switch (*ep++) {
381 case '1':
382 break;
383 case '2': lcr |= STOP2;
384 break;
385 default:
386 return (CMD_ERROR);
387 }
388
389 /* handshake is ignored, but we check syntax anyhow */
390 if (*ep == ',')
391 ep++;
392 else
393 return (CMD_ERROR);
394
395 switch (*ep++) {
396 case '-':
397 case 'h':
398 case 's':
399 break;
400 default:
401 return (CMD_ERROR);
402 }
403
404 if (*ep != '\0')
405 return (CMD_ERROR);
406
407 sp->speed = speed;
408 sp->lcr = lcr;
409 return (CMD_OK);
410 }
411
412 /*
413 * CMD_ERROR will cause set/setenv/setprop command to fail,
414 * when used in loader scripts (forth), this will cause processing
415 * of boot scripts to fail, rendering bootloading impossible.
416 * To prevent such unfortunate situation, we return CMD_OK when
417 * there is no such port, or there is invalid value in mode line.
418 */
419 static int
comc_mode_set(struct env_var * ev,int flags,const void * value)420 comc_mode_set(struct env_var *ev, int flags, const void *value)
421 {
422 struct console *cp;
423 char name[15];
424
425 if (value == NULL)
426 return (CMD_ERROR);
427
428 if ((cp = cons_get_console(ev->ev_name)) == NULL)
429 return (CMD_OK);
430
431 /* Do not override serial setup from SPCR */
432 snprintf(name, sizeof (name), "%s-spcr-mode", cp->c_name);
433 if (getenv(name) == NULL) {
434 if (comc_parse_mode(cp->c_private, value) == CMD_ERROR) {
435 printf("%s: invalid mode: %s\n", ev->ev_name,
436 (char *)value);
437 return (CMD_OK);
438 }
439 (void) comc_setup(cp);
440 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
441 }
442
443 return (CMD_OK);
444 }
445
446 /*
447 * CMD_ERROR will cause set/setenv/setprop command to fail,
448 * when used in loader scripts (forth), this will cause processing
449 * of boot scripts to fail, rendering bootloading impossible.
450 * To prevent such unfortunate situation, we return CMD_OK when
451 * there is no such port or invalid value was used.
452 */
453 static int
comc_cd_set(struct env_var * ev,int flags,const void * value)454 comc_cd_set(struct env_var *ev, int flags, const void *value)
455 {
456 struct console *cp;
457 struct serial *sp;
458
459 if (value == NULL)
460 return (CMD_OK);
461
462 if ((cp = cons_get_console(ev->ev_name)) == NULL)
463 return (CMD_OK);
464
465 sp = cp->c_private;
466 if (strcmp(value, "true") == 0) {
467 sp->ignore_cd = 1;
468 } else if (strcmp(value, "false") == 0) {
469 sp->ignore_cd = 0;
470 } else {
471 printf("%s: invalid value: %s\n", ev->ev_name,
472 (char *)value);
473 return (CMD_OK);
474 }
475
476 (void) comc_setup(cp);
477
478 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
479
480 return (CMD_OK);
481 }
482
483 /*
484 * CMD_ERROR will cause set/setenv/setprop command to fail,
485 * when used in loader scripts (forth), this will cause processing
486 * of boot scripts to fail, rendering bootloading impossible.
487 * To prevent such unfortunate situation, we return CMD_OK when
488 * there is no such port, or invalid value was used.
489 */
490 static int
comc_rtsdtr_set(struct env_var * ev,int flags,const void * value)491 comc_rtsdtr_set(struct env_var *ev, int flags, const void *value)
492 {
493 struct console *cp;
494 struct serial *sp;
495
496 if (value == NULL)
497 return (CMD_OK);
498
499 if ((cp = cons_get_console(ev->ev_name)) == NULL)
500 return (CMD_OK);
501
502 sp = cp->c_private;
503 if (strcmp(value, "true") == 0) {
504 sp->rtsdtr_off = 1;
505 } else if (strcmp(value, "false") == 0) {
506 sp->rtsdtr_off = 0;
507 } else {
508 printf("%s: invalid value: %s\n", ev->ev_name,
509 (char *)value);
510 return (CMD_OK);
511 }
512
513 (void) comc_setup(cp);
514
515 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
516
517 return (CMD_OK);
518 }
519
520 /*
521 * In case of error, we also reset ACTIVE flags, so the console
522 * framefork will try alternate consoles.
523 */
524 static bool
comc_setup(struct console * cp)525 comc_setup(struct console *cp)
526 {
527 struct serial *sp = cp->c_private;
528 static int TRY_COUNT = 1000000;
529 int tries;
530
531 outb(sp->ioaddr + com_cfcr, CFCR_DLAB | sp->lcr);
532 outb(sp->ioaddr + com_dlbl, COMC_BPS(sp->speed) & 0xff);
533 outb(sp->ioaddr + com_dlbh, COMC_BPS(sp->speed) >> 8);
534 outb(sp->ioaddr + com_cfcr, sp->lcr);
535 outb(sp->ioaddr + com_mcr,
536 sp->rtsdtr_off? ~(MCR_RTS | MCR_DTR) : MCR_RTS | MCR_DTR);
537
538 tries = 0;
539 do {
540 inb(sp->ioaddr + com_data);
541 } while (inb(sp->ioaddr + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT);
542
543 if (tries == TRY_COUNT)
544 return (false);
545 /* Mark this port usable. */
546 cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
547 return (true);
548 }
549
550 int
comc_getspeed(int ioaddr)551 comc_getspeed(int ioaddr)
552 {
553 uint_t divisor;
554 uchar_t dlbh;
555 uchar_t dlbl;
556 uchar_t cfcr;
557
558 cfcr = inb(ioaddr + com_cfcr);
559 outb(ioaddr + com_cfcr, CFCR_DLAB | cfcr);
560
561 dlbl = inb(ioaddr + com_dlbl);
562 dlbh = inb(ioaddr + com_dlbh);
563
564 outb(ioaddr + com_cfcr, cfcr);
565
566 divisor = dlbh << 8 | dlbl;
567
568 /* XXX there should be more sanity checking. */
569 if (divisor == 0)
570 return (COMSPEED);
571 return (COMC_DIV2BPS(divisor));
572 }
573