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