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