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