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