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 * We do not use this implementation with x86 till we can fix two issues:
28 * 1. Reliably identify the serial ports in correct order.
29 * 2. Ensure we get properly working reads from serial io.
30 */
31
32 #include <sys/cdefs.h>
33
34 #include <stand.h>
35 #include <sys/errno.h>
36 #include <bootstrap.h>
37 #include <stdbool.h>
38
39 #include <efi.h>
40 #include <efilib.h>
41
42 #include "loader_efi.h"
43
44 static EFI_GUID serial = SERIAL_IO_PROTOCOL;
45
46 #define COMC_TXWAIT 0x40000 /* transmit timeout */
47
48 #ifndef COMSPEED
49 #define COMSPEED 9600
50 #endif
51
52 #define PNP0501 0x501 /* 16550A-compatible COM port */
53
54 struct serial {
55 uint64_t baudrate;
56 uint8_t databits;
57 EFI_PARITY_TYPE parity;
58 EFI_STOP_BITS_TYPE stopbits;
59 uint8_t ignore_cd; /* boolean */
60 uint8_t rtsdtr_off; /* boolean */
61 int ioaddr; /* index in handles array */
62 SERIAL_IO_INTERFACE *sio;
63 };
64
65 static void comc_probe(struct console *);
66 static int comc_init(struct console *, int);
67 static void comc_putchar(struct console *, int);
68 static int comc_getchar(struct console *);
69 static int comc_ischar(struct console *);
70 static int comc_ioctl(struct console *, int, void *);
71 static void comc_devinfo(struct console *);
72 static bool comc_setup(struct console *);
73 static char *comc_asprint_mode(struct serial *);
74 static int comc_parse_mode(struct serial *, const char *);
75 static int comc_mode_set(struct env_var *, int, const void *);
76 static int comc_cd_set(struct env_var *, int, const void *);
77 static int comc_rtsdtr_set(struct env_var *, int, const void *);
78
79 struct console ttya = {
80 .c_name = "ttya",
81 .c_desc = "serial port a",
82 .c_flags = 0,
83 .c_probe = comc_probe,
84 .c_init = comc_init,
85 .c_out = comc_putchar,
86 .c_in = comc_getchar,
87 .c_ready = comc_ischar,
88 .c_ioctl = comc_ioctl,
89 .c_devinfo = comc_devinfo,
90 .c_private = NULL
91 };
92
93 struct console ttyb = {
94 .c_name = "ttyb",
95 .c_desc = "serial port b",
96 .c_flags = 0,
97 .c_probe = comc_probe,
98 .c_init = comc_init,
99 .c_out = comc_putchar,
100 .c_in = comc_getchar,
101 .c_ready = comc_ischar,
102 .c_ioctl = comc_ioctl,
103 .c_devinfo = comc_devinfo,
104 .c_private = NULL
105 };
106
107 struct console ttyc = {
108 .c_name = "ttyc",
109 .c_desc = "serial port c",
110 .c_flags = 0,
111 .c_probe = comc_probe,
112 .c_init = comc_init,
113 .c_out = comc_putchar,
114 .c_in = comc_getchar,
115 .c_ready = comc_ischar,
116 .c_ioctl = comc_ioctl,
117 .c_devinfo = comc_devinfo,
118 .c_private = NULL
119 };
120
121 struct console ttyd = {
122 .c_name = "ttyd",
123 .c_desc = "serial port d",
124 .c_flags = 0,
125 .c_probe = comc_probe,
126 .c_init = comc_init,
127 .c_out = comc_putchar,
128 .c_in = comc_getchar,
129 .c_ready = comc_ischar,
130 .c_ioctl = comc_ioctl,
131 .c_devinfo = comc_devinfo,
132 .c_private = NULL
133 };
134
135 static EFI_STATUS
efi_serial_init(EFI_HANDLE ** handlep,int * nhandles)136 efi_serial_init(EFI_HANDLE **handlep, int *nhandles)
137 {
138 UINTN bufsz = 0;
139 EFI_STATUS status;
140 EFI_HANDLE *handles;
141
142 /*
143 * get buffer size
144 */
145 *nhandles = 0;
146 handles = NULL;
147 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
148 if (status != EFI_BUFFER_TOO_SMALL)
149 return (status);
150
151 if ((handles = malloc(bufsz)) == NULL)
152 return (ENOMEM);
153
154 *nhandles = (int)(bufsz / sizeof (EFI_HANDLE));
155 /*
156 * get handle array
157 */
158 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
159 if (EFI_ERROR(status)) {
160 free(handles);
161 *nhandles = 0;
162 } else
163 *handlep = handles;
164 return (status);
165 }
166
167 /*
168 * Find serial device number from device path.
169 * Return -1 if not found.
170 */
171 static int
efi_serial_get_index(EFI_DEVICE_PATH * devpath)172 efi_serial_get_index(EFI_DEVICE_PATH *devpath)
173 {
174 ACPI_HID_DEVICE_PATH *acpi;
175
176 while (!IsDevicePathEnd(devpath)) {
177 if (DevicePathType(devpath) == ACPI_DEVICE_PATH &&
178 DevicePathSubType(devpath) == ACPI_DP) {
179
180 acpi = (ACPI_HID_DEVICE_PATH *)devpath;
181 if (acpi->HID == EISA_PNP_ID(PNP0501)) {
182 return (acpi->UID);
183 }
184 }
185
186 devpath = NextDevicePathNode(devpath);
187 }
188 return (-1);
189 }
190
191 /*
192 * The order of handles from LocateHandle() is not known, we need to
193 * iterate handles, pick device path for handle, and check the device
194 * number.
195 */
196 static EFI_HANDLE
efi_serial_get_handle(int port)197 efi_serial_get_handle(int port)
198 {
199 EFI_STATUS status;
200 EFI_HANDLE *handles, handle;
201 EFI_DEVICE_PATH *devpath;
202 int index, nhandles;
203
204 if (port == -1)
205 return (NULL);
206
207 handles = NULL;
208 nhandles = 0;
209 status = efi_serial_init(&handles, &nhandles);
210 if (EFI_ERROR(status))
211 return (NULL);
212
213 handle = NULL;
214 for (index = 0; index < nhandles; index++) {
215 devpath = efi_lookup_devpath(handles[index]);
216 if (port == efi_serial_get_index(devpath)) {
217 handle = (handles[index]);
218 break;
219 }
220 }
221
222 /*
223 * In case we did fail to identify the device by path, use port as
224 * array index. Note, we did check port == -1 above.
225 */
226 if (port < nhandles && handle == NULL)
227 handle = handles[port];
228
229 free(handles);
230 return (handle);
231 }
232
233 static void
comc_probe(struct console * cp)234 comc_probe(struct console *cp)
235 {
236 EFI_STATUS status;
237 EFI_HANDLE handle;
238 struct serial *port;
239 char name[20];
240 char value[20];
241 char *env;
242
243 /* are we already set up? */
244 if (cp->c_private != NULL)
245 return;
246
247 cp->c_private = malloc(sizeof (struct serial));
248 port = cp->c_private;
249 port->baudrate = COMSPEED;
250
251 port->ioaddr = -1; /* invalid port */
252 if (strcmp(cp->c_name, "ttya") == 0)
253 port->ioaddr = 0;
254 else if (strcmp(cp->c_name, "ttyb") == 0)
255 port->ioaddr = 1;
256 else if (strcmp(cp->c_name, "ttyc") == 0)
257 port->ioaddr = 2;
258 else if (strcmp(cp->c_name, "ttyd") == 0)
259 port->ioaddr = 3;
260
261 port->databits = 8; /* 8,n,1 */
262 port->parity = NoParity; /* 8,n,1 */
263 port->stopbits = OneStopBit; /* 8,n,1 */
264 port->ignore_cd = 1; /* ignore cd */
265 port->rtsdtr_off = 0; /* rts-dtr is on */
266 port->sio = NULL;
267
268 handle = efi_serial_get_handle(port->ioaddr);
269
270 if (handle != NULL) {
271 status = BS->OpenProtocol(handle, &serial,
272 (void**)&port->sio, IH, NULL,
273 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
274
275 if (EFI_ERROR(status))
276 port->sio = NULL;
277 }
278
279 snprintf(name, sizeof (name), "%s-mode", cp->c_name);
280 env = getenv(name);
281
282 if (env != NULL)
283 (void) comc_parse_mode(port, env);
284
285 env = comc_asprint_mode(port);
286
287 if (env != NULL) {
288 unsetenv(name);
289 env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset);
290 free(env);
291 }
292
293 snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name);
294 env = getenv(name);
295 if (env != NULL) {
296 if (strcmp(env, "true") == 0)
297 port->ignore_cd = 1;
298 else if (strcmp(env, "false") == 0)
299 port->ignore_cd = 0;
300 }
301
302 snprintf(value, sizeof (value), "%s",
303 port->ignore_cd? "true" : "false");
304 unsetenv(name);
305 env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset);
306
307 snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name);
308 env = getenv(name);
309 if (env != NULL) {
310 if (strcmp(env, "true") == 0)
311 port->rtsdtr_off = 1;
312 else if (strcmp(env, "false") == 0)
313 port->rtsdtr_off = 0;
314 }
315
316 snprintf(value, sizeof (value), "%s",
317 port->rtsdtr_off? "true" : "false");
318 unsetenv(name);
319 env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset);
320
321 cp->c_flags = 0;
322 if (comc_setup(cp))
323 cp->c_flags = C_PRESENTIN | C_PRESENTOUT;
324 }
325
326 static int
comc_init(struct console * cp,int arg __attribute ((unused)))327 comc_init(struct console *cp, int arg __attribute((unused)))
328 {
329
330 if (comc_setup(cp))
331 return (CMD_OK);
332
333 cp->c_flags = 0;
334 return (CMD_ERROR);
335 }
336
337 static void
comc_putchar(struct console * cp,int c)338 comc_putchar(struct console *cp, int c)
339 {
340 int wait;
341 EFI_STATUS status;
342 UINTN bufsz = 1;
343 char cb = c;
344 struct serial *sp = cp->c_private;
345
346 if (sp->sio == NULL)
347 return;
348
349 for (wait = COMC_TXWAIT; wait > 0; wait--) {
350 status = sp->sio->Write(sp->sio, &bufsz, &cb);
351 if (status != EFI_TIMEOUT)
352 break;
353 }
354 }
355
356 static int
comc_getchar(struct console * cp)357 comc_getchar(struct console *cp)
358 {
359 EFI_STATUS status;
360 UINTN bufsz = 1;
361 char c;
362 struct serial *sp = cp->c_private;
363
364 if (sp->sio == NULL || !comc_ischar(cp))
365 return (-1);
366
367 status = sp->sio->Read(sp->sio, &bufsz, &c);
368 if (EFI_ERROR(status) || bufsz == 0)
369 return (-1);
370
371 return (c);
372 }
373
374 static int
comc_ischar(struct console * cp)375 comc_ischar(struct console *cp)
376 {
377 EFI_STATUS status;
378 uint32_t control;
379 struct serial *sp = cp->c_private;
380
381 if (sp->sio == NULL)
382 return (0);
383
384 status = sp->sio->GetControl(sp->sio, &control);
385 if (EFI_ERROR(status))
386 return (0);
387
388 return (!(control & EFI_SERIAL_INPUT_BUFFER_EMPTY));
389 }
390
391 static int
comc_ioctl(struct console * cp __unused,int cmd __unused,void * data __unused)392 comc_ioctl(struct console *cp __unused, int cmd __unused, void *data __unused)
393 {
394 return (ENOTTY);
395 }
396
397 static void
comc_devinfo(struct console * cp)398 comc_devinfo(struct console *cp)
399 {
400 struct serial *port = cp->c_private;
401 EFI_HANDLE handle;
402 EFI_DEVICE_PATH *dp;
403 CHAR16 *text;
404
405 handle = efi_serial_get_handle(port->ioaddr);
406 if (handle == NULL) {
407 printf("\tdevice is not present");
408 return;
409 }
410
411 dp = efi_lookup_devpath(handle);
412 if (dp == NULL)
413 return;
414
415 text = efi_devpath_name(dp);
416 if (text == NULL)
417 return;
418
419 printf("\t%S", text);
420 efi_free_devpath_name(text);
421 }
422
423 static char *
comc_asprint_mode(struct serial * sp)424 comc_asprint_mode(struct serial *sp)
425 {
426 char par, *buf;
427 char *stop;
428
429 if (sp == NULL)
430 return (NULL);
431
432 switch (sp->parity) {
433 case NoParity:
434 par = 'n';
435 break;
436 case EvenParity:
437 par = 'e';
438 break;
439 case OddParity:
440 par = 'o';
441 break;
442 case MarkParity:
443 par = 'm';
444 break;
445 case SpaceParity:
446 par = 's';
447 break;
448 default:
449 par = 'n';
450 break;
451 }
452
453 switch (sp->stopbits) {
454 case OneStopBit:
455 stop = "1";
456 break;
457 case TwoStopBits:
458 stop = "2";
459 break;
460 case OneFiveStopBits:
461 stop = "1.5";
462 break;
463 default:
464 stop = "1";
465 break;
466 }
467
468 asprintf(&buf, "%ju,%d,%c,%s,-", sp->baudrate, sp->databits, par, stop);
469 return (buf);
470 }
471
472 static int
comc_parse_mode(struct serial * sp,const char * value)473 comc_parse_mode(struct serial *sp, const char *value)
474 {
475 unsigned long n;
476 uint64_t baudrate;
477 uint8_t databits = 8;
478 int parity = NoParity;
479 int stopbits = OneStopBit;
480 char *ep;
481
482 if (value == NULL || *value == '\0')
483 return (CMD_ERROR);
484
485 errno = 0;
486 n = strtoul(value, &ep, 10);
487 if (errno != 0 || *ep != ',')
488 return (CMD_ERROR);
489 baudrate = n;
490
491 ep++;
492 n = strtoul(ep, &ep, 10);
493 if (errno != 0 || *ep != ',')
494 return (CMD_ERROR);
495
496 switch (n) {
497 case 5: databits = 5;
498 break;
499 case 6: databits = 6;
500 break;
501 case 7: databits = 7;
502 break;
503 case 8: databits = 8;
504 break;
505 default:
506 return (CMD_ERROR);
507 }
508
509 ep++;
510 switch (*ep++) {
511 case 'n': parity = NoParity;
512 break;
513 case 'e': parity = EvenParity;
514 break;
515 case 'o': parity = OddParity;
516 break;
517 case 'm': parity = MarkParity;
518 break;
519 case 's': parity = SpaceParity;
520 break;
521 default:
522 return (CMD_ERROR);
523 }
524
525 if (*ep == ',')
526 ep++;
527 else
528 return (CMD_ERROR);
529
530 switch (*ep++) {
531 case '1': stopbits = OneStopBit;
532 if (ep[0] == '.' && ep[1] == '5') {
533 ep += 2;
534 stopbits = OneFiveStopBits;
535 }
536 break;
537 case '2': stopbits = TwoStopBits;
538 break;
539 default:
540 return (CMD_ERROR);
541 }
542
543 /* handshake is ignored, but we check syntax anyhow */
544 if (*ep == ',')
545 ep++;
546 else
547 return (CMD_ERROR);
548
549 switch (*ep++) {
550 case '-':
551 case 'h':
552 case 's':
553 break;
554 default:
555 return (CMD_ERROR);
556 }
557
558 if (*ep != '\0')
559 return (CMD_ERROR);
560
561 sp->baudrate = baudrate;
562 sp->databits = databits;
563 sp->parity = parity;
564 sp->stopbits = stopbits;
565 return (CMD_OK);
566 }
567
568 static struct console *
get_console(char * name)569 get_console(char *name)
570 {
571 struct console *cp = NULL;
572
573 switch (name[3]) {
574 case 'a': cp = &ttya;
575 break;
576 case 'b': cp = &ttyb;
577 break;
578 case 'c': cp = &ttyc;
579 break;
580 case 'd': cp = &ttyd;
581 break;
582 }
583 return (cp);
584 }
585
586 static int
comc_mode_set(struct env_var * ev,int flags,const void * value)587 comc_mode_set(struct env_var *ev, int flags, const void *value)
588 {
589 struct console *cp;
590
591 if (value == NULL)
592 return (CMD_ERROR);
593
594 if ((cp = get_console(ev->ev_name)) == NULL)
595 return (CMD_ERROR);
596
597 if (comc_parse_mode(cp->c_private, value) == CMD_ERROR)
598 return (CMD_ERROR);
599
600 (void) comc_setup(cp);
601
602 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
603
604 return (CMD_OK);
605 }
606
607 static int
comc_cd_set(struct env_var * ev,int flags,const void * value)608 comc_cd_set(struct env_var *ev, int flags, const void *value)
609 {
610 struct console *cp;
611 struct serial *sp;
612
613 if (value == NULL)
614 return (CMD_ERROR);
615
616 if ((cp = get_console(ev->ev_name)) == NULL)
617 return (CMD_ERROR);
618
619 sp = cp->c_private;
620 if (strcmp(value, "true") == 0)
621 sp->ignore_cd = 1;
622 else if (strcmp(value, "false") == 0)
623 sp->ignore_cd = 0;
624 else
625 return (CMD_ERROR);
626
627 (void) comc_setup(cp);
628
629 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
630
631 return (CMD_OK);
632 }
633
634 static int
comc_rtsdtr_set(struct env_var * ev,int flags,const void * value)635 comc_rtsdtr_set(struct env_var *ev, int flags, const void *value)
636 {
637 struct console *cp;
638 struct serial *sp;
639
640 if (value == NULL)
641 return (CMD_ERROR);
642
643 if ((cp = get_console(ev->ev_name)) == NULL)
644 return (CMD_ERROR);
645
646 sp = cp->c_private;
647 if (strcmp(value, "true") == 0)
648 sp->rtsdtr_off = 1;
649 else if (strcmp(value, "false") == 0)
650 sp->rtsdtr_off = 0;
651 else
652 return (CMD_ERROR);
653
654 (void) comc_setup(cp);
655
656 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
657
658 return (CMD_OK);
659 }
660
661 /*
662 * In case of error, we also reset ACTIVE flags, so the console
663 * framefork will try alternate consoles.
664 */
665 static bool
comc_setup(struct console * cp)666 comc_setup(struct console *cp)
667 {
668 EFI_STATUS status;
669 UINT32 control;
670 struct serial *sp = cp->c_private;
671
672 /* port is not usable */
673 if (sp->sio == NULL)
674 return (false);
675
676 status = sp->sio->Reset(sp->sio);
677 if (EFI_ERROR(status))
678 return (false);
679
680 status = sp->sio->SetAttributes(sp->sio, sp->baudrate, 0, 0, sp->parity,
681 sp->databits, sp->stopbits);
682 if (EFI_ERROR(status))
683 return (false);
684
685 status = sp->sio->GetControl(sp->sio, &control);
686 if (EFI_ERROR(status))
687 return (false);
688 if (sp->rtsdtr_off) {
689 control &= ~(EFI_SERIAL_REQUEST_TO_SEND |
690 EFI_SERIAL_DATA_TERMINAL_READY);
691 } else {
692 control |= EFI_SERIAL_REQUEST_TO_SEND;
693 }
694
695 (void) sp->sio->SetControl(sp->sio, control);
696
697 /* Mark this port usable. */
698 cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
699 return (true);
700 }
701