xref: /illumos-gate/usr/src/boot/efi/libefi/efiisaio.c (revision 4ac2186d79f65de18b11f2693e78f73b27d5308b)
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 #include <sys/cdefs.h>
27 
28 #include <stand.h>
29 #include <sys/errno.h>
30 #include <sys/param.h>
31 #include <bootstrap.h>
32 #include <stdbool.h>
33 
34 #include <efi.h>
35 #include <efilib.h>
36 #include <efidevp.h>
37 #include <Protocol/IsaIo.h>
38 #include <dev/ic/ns16550.h>
39 #include <uuid.h>
40 
41 #ifdef MDE_CPU_X64
42 #include <machine/cpufunc.h>
43 #endif
44 
45 EFI_GUID  gEfiIsaIoProtocolGuid = EFI_ISA_IO_PROTOCOL_GUID;
46 
47 #define	COMC_TXWAIT	0x40000		/* transmit timeout */
48 #define	COMC_BPS(x)	(115200 / (x))	/* speed to DLAB divisor */
49 #define	COMC_DIV2BPS(x)	(115200 / (x))	/* DLAB divisor to speed */
50 
51 #ifndef COMSPEED
52 #define	COMSPEED	9600
53 #endif
54 
55 #define	COM1_IOADDR	0x3f8
56 #define	COM2_IOADDR	0x2f8
57 #define	COM3_IOADDR	0x3e8
58 #define	COM4_IOADDR	0x2e8
59 
60 #ifdef MDE_CPU_X64
61 static uint_t io_ports[] = {
62 	COM1_IOADDR, COM2_IOADDR, COM3_IOADDR, COM4_IOADDR
63 };
64 #endif
65 
66 #define	STOP1		0x00
67 #define	STOP2		0x04
68 
69 #define	PARODD		0x00
70 #define	PAREN		0x08
71 #define	PAREVN		0x10
72 #define	PARMARK		0x20
73 
74 #define	BITS5		0x00	/* 5 bits per char */
75 #define	BITS6		0x01	/* 6 bits per char */
76 #define	BITS7		0x02	/* 7 bits per char */
77 #define	BITS8		0x03	/* 8 bits per char */
78 
79 #define	PNP0501		0x501		/* 16550A-compatible COM port */
80 
81 static void	efi_isa_probe(struct console *);
82 static int	efi_isa_init(struct console *, int);
83 static void	efi_isa_putchar(struct console *, int);
84 static int	efi_isa_getchar(struct console *);
85 static int	efi_isa_ischar(struct console *);
86 static int	efi_isa_ioctl(struct console *, int, void *);
87 static void	efi_isa_devinfo(struct console *);
88 static bool	efi_isa_setup(struct console *);
89 static char	*efi_isa_asprint_mode(struct serial *);
90 static int	efi_isa_parse_mode(struct serial *, const char *);
91 static int	efi_isa_mode_set(struct env_var *, int, const void *);
92 static int	efi_isa_cd_set(struct env_var *, int, const void *);
93 static int	efi_isa_rtsdtr_set(struct env_var *, int, const void *);
94 
95 extern struct console efi_console;
96 
97 static bool
efi_isa_port_is_present(struct serial * sp)98 efi_isa_port_is_present(struct serial *sp)
99 {
100 	EFI_STATUS status;
101 #define	COMC_TEST	0xbb
102 	uint8_t test = COMC_TEST;
103 
104 	/*
105 	 * Write byte to scratch register and read it out.
106 	 */
107 	status = sp->io.isa->Io.Write(sp->io.isa,
108 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT,
109 	    sp->ioaddr + com_scr, 1, &test);
110 	test = 0;
111 	if (status == EFI_SUCCESS) {
112 		status = sp->io.isa->Io.Read(sp->io.isa,
113 		    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT, sp->ioaddr + com_scr,
114 		    1, &test);
115 	}
116 	return (test == COMC_TEST);
117 #undef COMC_TEST
118 }
119 
120 static bool
efi_isa_should_append(const char * name,struct serial * port)121 efi_isa_should_append(const char *name, struct serial *port)
122 {
123 	EFI_DEVICE_PATH *node, *dev;
124 	EFI_STATUS status;
125 	char *buf;
126 	size_t sz;
127 	bool rv = true;
128 
129 	if (port->currdev == NULL)
130 		return (rv);
131 
132 	buf = NULL;
133 	sz = 0;
134 	status = efi_global_getenv(name, buf, &sz);
135 	if (status == EFI_BUFFER_TOO_SMALL) {
136 		buf = malloc(sz);
137 		if (buf == NULL)
138 			return (rv);
139 		status = efi_global_getenv(name, buf, &sz);
140 	}
141 	if (EFI_ERROR(status)) {
142 		free(buf);
143 		return (rv);
144 	}
145 
146 	dev = efi_lookup_devpath(port->currdev);
147 	if (dev == NULL) {
148 		free(buf);
149 		return (rv);
150 	}
151 
152 	node = (EFI_DEVICE_PATH *)buf;
153 	/*
154 	 * We only need to know if this port is first in list.
155 	 * This is only important when "os_console" is not set.
156 	 */
157 	if (!IsDevicePathEnd(node) && efi_devpath_is_prefix(dev, node))
158 		rv = false;
159 
160 	efi_close_devpath(dev);
161 	free(buf);
162 	return (rv);
163 }
164 
165 static void
efi_isa_setup_env(struct console * tty)166 efi_isa_setup_env(struct console *tty)
167 {
168 	struct serial *port = tty->c_private;
169 	char name[20];
170 	char value[20];
171 	char *env;
172 
173 	(void) snprintf(name, sizeof (name), "%s-mode", tty->c_name);
174 	env = getenv(name);
175 	if (env != NULL)
176 		(void) efi_isa_parse_mode(port, env);
177 	env = efi_isa_asprint_mode(port);
178 	if (env != NULL) {
179 		(void) unsetenv(name);
180 		(void) env_setenv(name, EV_VOLATILE, env, efi_isa_mode_set,
181 		    env_nounset);
182 		if (port->is_efi_console) {
183 			(void) snprintf(name, sizeof (name), "%s-spcr-mode",
184 			    tty->c_name);
185 			(void) setenv(name, env, 1);
186 			free(env);
187 
188 			/* Add us to console list. */
189 			(void) snprintf(name, sizeof (name), "console");
190 			env = getenv(name);
191 			if (env == NULL) {
192 				(void) setenv(name, tty->c_name, 1);
193 			} else {
194 				char *ptr;
195 				int rv;
196 
197 				/*
198 				 * we have "text" already in place,
199 				 * consult ConOut if we need to add
200 				 * serial console before or after.
201 				 */
202 				if (efi_isa_should_append("ConOut", port))
203 					rv = asprintf(&ptr, "%s,%s", env,
204 					    tty->c_name);
205 				else
206 					rv = asprintf(&ptr, "%s,%s",
207 					    tty->c_name, env);
208 				if (rv > 0) {
209 					(void) setenv(name, ptr, 1);
210 					free(ptr);
211 				} else {
212 					printf("%s: %s\n", __func__,
213 					    strerror(ENOMEM));
214 				}
215 			}
216 		} else {
217 			free(env);
218 		}
219 	}
220 
221 	(void) snprintf(name, sizeof (name), "%s-ignore-cd", tty->c_name);
222 	env = getenv(name);
223 	if (env != NULL) {
224 		if (strcmp(env, "true") == 0)
225 			port->ignore_cd = 1;
226 		else if (strcmp(env, "false") == 0)
227 			port->ignore_cd = 0;
228 	}
229 
230 	(void) snprintf(value, sizeof (value), "%s",
231 	    port->ignore_cd? "true" : "false");
232 	(void) unsetenv(name);
233 	(void) env_setenv(name, EV_VOLATILE, value, efi_isa_cd_set,
234 	    env_nounset);
235 
236 	(void) snprintf(name, sizeof (name), "%s-rts-dtr-off", tty->c_name);
237 	env = getenv(name);
238 	if (env != NULL) {
239 		if (strcmp(env, "true") == 0)
240 			port->rtsdtr_off = 1;
241 		else if (strcmp(env, "false") == 0)
242 			port->rtsdtr_off = 0;
243 	}
244 
245 	(void) snprintf(value, sizeof (value), "%s",
246 	    port->rtsdtr_off? "true" : "false");
247 	(void) unsetenv(name);
248 	(void) env_setenv(name, EV_VOLATILE, value, efi_isa_rtsdtr_set,
249 	    env_nounset);
250 }
251 
252 static void
efi_check_and_set_condev(struct serial * port,const char * name)253 efi_check_and_set_condev(struct serial *port, const char *name)
254 {
255 	EFI_DEVICE_PATH *node, *dev;
256 	EFI_STATUS status;
257 	char *buf;
258 	size_t sz;
259 
260 	if (port->currdev == NULL)
261 		return;
262 
263 	buf = NULL;
264 	sz = 0;
265 	status = efi_global_getenv(name, buf, &sz);
266 	if (status == EFI_BUFFER_TOO_SMALL) {
267 		buf = malloc(sz);
268 		if (buf == NULL)
269 			return;
270 		status = efi_global_getenv(name, buf, &sz);
271 	}
272 	if (EFI_ERROR(status)) {
273 		free(buf);
274 		return;
275 	}
276 
277 	dev = efi_lookup_devpath(port->currdev);
278 	if (dev == NULL) {
279 		free(buf);
280 		return;
281 	}
282 
283 	node = (EFI_DEVICE_PATH *)buf;
284 	while (!IsDevicePathEnd(node)) {
285 		/* Sanity check the node before moving to the next node. */
286 		if (DevicePathNodeLength(node) < sizeof (*node))
287 			break;
288 
289 		if (efi_devpath_is_prefix(dev, node)) {
290 			port->is_efi_console = true;
291 			break;
292 		}
293 
294 		node = efi_devpath_next_instance(node);
295 	}
296 
297 	efi_close_devpath(dev);
298 	free(buf);
299 }
300 
301 /*
302  * Return number of ports created (0 or 1).
303  */
304 static uint_t
efi_isa_create_port(EFI_HANDLE handle)305 efi_isa_create_port(EFI_HANDLE handle)
306 {
307 	struct serial *port;
308 	EFI_ISA_IO_PROTOCOL *io;
309 	EFI_STATUS status;
310 
311 	status = BS->OpenProtocol(handle, &gEfiIsaIoProtocolGuid,
312 	    (void**)&io, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
313 	if (EFI_ERROR(status)) {
314 		return (0);
315 	}
316 
317 	/* Is this serial port? */
318 	if (io->ResourceList->Device.HID != EISA_PNP_ID(PNP0501))
319 		return (0);
320 
321 	/* We assume I/O port */
322 	if (io->ResourceList->ResourceItem->Type != EfiIsaAcpiResourceIo)
323 		return (0);
324 
325 	port = calloc(1, sizeof (*port));
326 	if (port == NULL) {
327 		return (0);
328 	}
329 
330 	/* Set up port descriptor */
331 	port->ignore_cd = true;
332 	port->currdev = handle;
333 	port->ioaddr = 0;
334 	for (uint_t i = 0; io->ResourceList->ResourceItem[i].Type !=
335 	    EfiIsaAcpiResourceEndOfList; i++) {
336 		if (io->ResourceList->ResourceItem[i].Type ==
337 		    EfiIsaAcpiResourceIo) {
338 			port->ioaddr =
339 			    io->ResourceList->ResourceItem[i].StartRange;
340 			break;
341 		}
342 	}
343 	port->guid = &gEfiIsaIoProtocolGuid;
344 	port->io.isa = io;
345 	/* Use 8,n,1 for defaults */
346 	port->databits = 8;
347 	port->parity = NoParity;
348 	port->stopbits = OneStopBit;
349 
350 	STAILQ_INSERT_TAIL(&serials, port, next);
351 	return (1);
352 }
353 
354 #ifdef MDE_CPU_X64
355 static EFI_STATUS
efi_isa_create_io(EFI_ISA_IO_PROTOCOL ** iop)356 efi_isa_create_io(EFI_ISA_IO_PROTOCOL **iop)
357 {
358 	EFI_ISA_IO_PROTOCOL *io;
359 
360 	io = calloc(1, sizeof (*io));
361 	if (io == NULL) {
362 		return (EFI_OUT_OF_RESOURCES);
363 	}
364 	io->ResourceList = malloc(sizeof (*io->ResourceList));
365 	if (io->ResourceList == NULL) {
366 		free(io);
367 		return (EFI_OUT_OF_RESOURCES);
368 	}
369 	io->ResourceList->ResourceItem =
370 	    calloc(2, sizeof (*io->ResourceList->ResourceItem));
371 	if (io->ResourceList == NULL) {
372 		free(io->ResourceList);
373 		free(io);
374 		return (EFI_OUT_OF_RESOURCES);
375 	}
376 
377 	*iop = io;
378 	return (EFI_SUCCESS);
379 }
380 
381 static EFI_STATUS EFIAPI
_Read(EFI_ISA_IO_PROTOCOL * this __unused,EFI_ISA_IO_PROTOCOL_WIDTH Width __unused,UINT32 Offset,UINTN Count,VOID * Buffer)382 _Read(EFI_ISA_IO_PROTOCOL *this __unused,
383     EFI_ISA_IO_PROTOCOL_WIDTH Width __unused,
384     UINT32 Offset, UINTN Count, VOID *Buffer)
385 {
386 	uint8_t *buf = (uint8_t *)Buffer;
387 
388 	while ((Count--) != 0) {
389 		*buf++ = inb(Offset);
390 	}
391 
392 	return (EFI_SUCCESS);
393 }
394 
395 static EFI_STATUS EFIAPI
_Write(EFI_ISA_IO_PROTOCOL * this __unused,EFI_ISA_IO_PROTOCOL_WIDTH Width __unused,UINT32 Offset,UINTN Count,VOID * Buffer)396 _Write(EFI_ISA_IO_PROTOCOL *this __unused,
397     EFI_ISA_IO_PROTOCOL_WIDTH Width __unused,
398     UINT32 Offset, UINTN Count, VOID *Buffer)
399 {
400 	uint8_t *buf = (uint8_t *)Buffer;
401 
402 	while ((Count--) != 0) {
403 		outb(Offset, *buf++);
404 	}
405 	return (EFI_SUCCESS);
406 }
407 
408 static EFI_STATUS
efi_isa_create_io_ports(void)409 efi_isa_create_io_ports(void)
410 {
411 	struct serial *port;
412 	EFI_ISA_IO_PROTOCOL *io;
413 	EFI_STATUS status = EFI_SUCCESS;
414 
415 	port = NULL;
416 	for (uint_t i = 0; i < nitems(io_ports); i++) {
417 		if (port == NULL) {
418 			status = efi_isa_create_io(&io);
419 			if (EFI_ERROR(status))
420 				return (status);
421 			io->Io.Read = _Read;
422 			io->Io.Write = _Write;
423 			io->ResourceList->Device.HID = EISA_PNP_ID(PNP0501);
424 			io->ResourceList->ResourceItem[0].Type =
425 			    EfiIsaAcpiResourceIo;
426 			io->ResourceList->ResourceItem[1].Type =
427 			    EfiIsaAcpiResourceEndOfList;
428 
429 			port = calloc(1, sizeof (*port));
430 			if (port == NULL) {
431 				free(io->ResourceList->ResourceItem);
432 				free(io->ResourceList);
433 				free(io);
434 				return (EFI_OUT_OF_RESOURCES);
435 			}
436 			/* Set up port descriptor */
437 			port->io.isa = io;
438 			/* Use 8,n,1 for defaults */
439 			port->databits = 8;
440 			port->parity = NoParity;
441 			port->stopbits = OneStopBit;
442 			port->ignore_cd = true;
443 			port->guid = &gEfiIsaIoProtocolGuid;
444 		}
445 		io->ResourceList->Device.UID = i;
446 		io->ResourceList->ResourceItem[0].StartRange = io_ports[i];
447 		port->ioaddr = io_ports[i];
448 		if (efi_isa_port_is_present(port)) {
449 			STAILQ_INSERT_TAIL(&serials, port, next);
450 			port = NULL;
451 		}
452 	}
453 
454 	if (port != NULL) {
455 		free(io->ResourceList->ResourceItem);
456 		free(io->ResourceList);
457 		free(io);
458 		free(port);
459 	}
460 	return (status);
461 }
462 #endif	/* MDE_CPU_X64 */
463 
464 /*
465  * Get IsaIo protocol handles and build port list for us.
466  * returns EFI_SUCCESS or EFI_NOT_FOUND.
467  */
468 static EFI_STATUS
efi_isa_probe_ports(void)469 efi_isa_probe_ports(void)
470 {
471 	EFI_STATUS status;
472 	EFI_HANDLE *handles;
473 	struct serial *port;
474 	uint_t count, nhandles, index;
475 
476 	count = 0;
477 	nhandles = 0;
478 	handles = NULL;
479 	status = efi_get_protocol_handles(&gEfiIsaIoProtocolGuid,
480 	    &nhandles, &handles);
481 	if (!EFI_ERROR(status)) {
482 		for (index = 0; index < nhandles; index++) {
483 			/* skip if we are iodev for serialio port */
484 			STAILQ_FOREACH(port, &serials, next) {
485 				if (port->iodev == handles[index])
486 					break;
487 			}
488 			if (port == NULL)
489 				count += efi_isa_create_port(handles[index]);
490 		}
491 		free(handles);
492 		if (count == 0)
493 			status = EFI_NOT_FOUND;
494 	}
495 	return (status);
496 }
497 
498 /*
499  * Set up list of possible serial consoles.
500  * This function is run very early, so we do not expect to
501  * run out of memory, and on error, we can not print output.
502  *
503  * isaio protocols can include serial ports, parallel ports,
504  * keyboard, mouse. We walk protocol handles, create list of
505  * serial ports, then create console descriptors.
506  */
507 void
efi_isa_ini(void)508 efi_isa_ini(void)
509 {
510 	EFI_STATUS status;
511 	uint_t c, n;
512 	struct console **tmp;
513 	struct console *tty;
514 	struct serial *port;
515 
516 	status = efi_isa_probe_ports();
517 #ifdef MDE_CPU_X64
518 	if (status == EFI_NOT_FOUND) {
519 		/*
520 		 * We have no IsaIo serial ports. But, as this implementation
521 		 * is very similar to one used in BIOS comconsole driver,
522 		 * and in order to avoid using comconsole as last resort
523 		 * fallback on x86 platform, we implement fake IsaIo.
524 		 */
525 		if (STAILQ_EMPTY(&serials))
526 			status = efi_isa_create_io_ports();
527 	}
528 #endif
529 	if (EFI_ERROR(status))
530 		return;
531 
532 	n = 0;
533 	/* Count ports we have */
534 	STAILQ_FOREACH(port, &serials, next) {
535 		if (uuid_equal((uuid_t *)port->guid,
536 		    (uuid_t *)&gEfiIsaIoProtocolGuid, NULL) == 1)
537 			n++;
538 	}
539 
540 	if (n == 0)
541 		return;		/* no serial ports here */
542 
543 	c = cons_array_size();
544 	if (c == 0)
545 		n++;	/* For NULL pointer */
546 
547 	tmp = realloc(consoles, (c + n) * sizeof (*consoles));
548 	if (tmp == NULL) {
549 		return;
550 	}
551 	consoles = tmp;
552 	if (c > 0)
553 		c--;
554 
555 	STAILQ_FOREACH(port, &serials, next) {
556 		char id;
557 
558 		if (uuid_equal((uuid_t *)port->guid,
559 		    (uuid_t *)&gEfiIsaIoProtocolGuid, NULL) == 0) {
560 			continue;
561 		}
562 
563 		tty = calloc(1, sizeof (*tty));
564 		if (tty == NULL) {
565 			/* Out of memory?! */
566 			continue;
567 		}
568 		switch (port->ioaddr) {
569 		case 0:	/* bad ioaddr */
570 			continue;
571 		case COM1_IOADDR:
572 			id = 'a';
573 			break;
574 		case COM2_IOADDR:
575 			id = 'b';
576 			break;
577 		case COM3_IOADDR:
578 			id = 'c';
579 			break;
580 		case COM4_IOADDR:
581 			id = 'd';
582 			break;
583 		default:
584 			/*
585 			 * We should not see this happening, but assigning
586 			 * this id would let us help to identify unexpected
587 			 * configuration.
588 			 */
589 			id = '0';
590 		}
591 		/* Set up serial device descriptor */
592 		(void) asprintf(&tty->c_name, "tty%c", id);
593 		(void) asprintf(&tty->c_desc, "serial port %c", id);
594 		tty->c_flags = C_PRESENTIN | C_PRESENTOUT;
595 		tty->c_probe = efi_isa_probe;
596 		tty->c_init = efi_isa_init;
597 		tty->c_out = efi_isa_putchar;
598 		tty->c_in = efi_isa_getchar;
599 		tty->c_ready = efi_isa_ischar;
600 		tty->c_ioctl = efi_isa_ioctl;
601 		tty->c_devinfo = efi_isa_devinfo;
602 		tty->c_private = port;
603 		consoles[c++] = tty;
604 
605 		/* Reset terminal to initial normal settings with ESC [ 0 m */
606 		efi_isa_putchar(tty, 0x1b);
607 		efi_isa_putchar(tty, '[');
608 		efi_isa_putchar(tty, '0');
609 		efi_isa_putchar(tty, 'm');
610 		/* drain input from random data */
611 		while (efi_isa_getchar(tty) != -1)
612 			;
613 	}
614 	consoles[c] = NULL;
615 }
616 
617 static EFI_STATUS
efi_isa_getspeed(struct serial * sp)618 efi_isa_getspeed(struct serial *sp)
619 {
620 	EFI_STATUS status;
621 	uint_t  divisor;
622 	uchar_t dlbh;
623 	uchar_t dlbl;
624 	uchar_t cfcr;
625 	uchar_t c;
626 
627 	status = sp->io.isa->Io.Read(sp->io.isa,
628 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT, sp->ioaddr + com_cfcr, 1, &cfcr);
629 	if (EFI_ERROR(status))
630 		return (status);
631 	c = CFCR_DLAB | cfcr;
632 	status = sp->io.isa->Io.Write(sp->io.isa,
633 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT, sp->ioaddr + com_cfcr, 1, &c);
634 	if (EFI_ERROR(status))
635 		return (status);
636 
637 	status = sp->io.isa->Io.Read(sp->io.isa,
638 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT, sp->ioaddr + com_dlbl, 1, &dlbl);
639 	if (EFI_ERROR(status))
640 		return (status);
641 	status = sp->io.isa->Io.Read(sp->io.isa,
642 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT, sp->ioaddr + com_dlbh, 1, &dlbh);
643 	if (EFI_ERROR(status))
644 		return (status);
645 
646 	status = sp->io.isa->Io.Write(sp->io.isa,
647 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT, sp->ioaddr + com_cfcr, 1, &cfcr);
648 	if (EFI_ERROR(status))
649 		return (status);
650 
651 	divisor = dlbh << 8 | dlbl;
652 
653 	if (divisor == 0)
654 		return (EFI_DEVICE_ERROR);
655 
656 	sp->baudrate = COMC_DIV2BPS(divisor);
657 	return (EFI_SUCCESS);
658 }
659 
660 static void
efi_isa_probe(struct console * cp)661 efi_isa_probe(struct console *cp)
662 {
663 	struct serial *sp = cp->c_private;
664 	EFI_STATUS status;
665 	uint8_t lcr;
666 
667 	if (!efi_isa_port_is_present(sp)) {
668 		return;
669 	}
670 
671 	status = efi_isa_getspeed(sp);
672 	if (EFI_ERROR(status)) {
673 		/* Use fallback value. */
674 		sp->baudrate = COMSPEED;
675 	}
676 	status = sp->io.isa->Io.Read(sp->io.isa,
677 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT, sp->ioaddr + com_lcr,
678 	    1, &lcr);
679 	if (EFI_ERROR(status)) {
680 		return;
681 	}
682 
683 	sp->databits = (lcr & BITS8) == BITS8? 8:7;
684 	sp->stopbits = (lcr & STOP2) == STOP2? TwoStopBits:OneStopBit;
685 	if ((lcr & (PAREN|PAREVN)) == (PAREN|PAREVN))
686 		sp->parity = EvenParity;
687 	else if ((lcr & PAREN) == PAREN)
688 		sp->parity = OddParity;
689 	else
690 		sp->parity = NoParity;
691 
692 	/* check if we are listed in ConIn */
693 	efi_check_and_set_condev(sp, "ConIn");
694 	efi_isa_setup_env(cp);
695 	if (!efi_isa_setup(cp))
696 		printf("Failed to set up %s\n", cp->c_name);
697 }
698 
699 static int
efi_isa_init(struct console * cp,int arg __unused)700 efi_isa_init(struct console *cp, int arg __unused)
701 {
702 
703 	if (efi_isa_setup(cp))
704 		return (CMD_OK);
705 
706 	cp->c_flags = 0;
707 	return (CMD_ERROR);
708 }
709 
710 static void
efi_isa_putchar(struct console * cp,int c)711 efi_isa_putchar(struct console *cp, int c)
712 {
713 	int wait;
714 	EFI_STATUS status;
715 	UINTN bufsz = 1;
716 	char control, cb = c;
717 	struct serial *sp = cp->c_private;
718 
719 	for (wait = COMC_TXWAIT; wait > 0; wait--) {
720 		status = sp->io.isa->Io.Read(sp->io.isa,
721 		    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT, sp->ioaddr + com_lsr,
722 		    bufsz, &control);
723 		if (EFI_ERROR(status))
724 			continue;
725 
726 		if ((control & LSR_TXRDY) != LSR_TXRDY)
727 			continue;
728 
729 		status = sp->io.isa->Io.Write(sp->io.isa,
730 		    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT, sp->ioaddr + com_data,
731 		    bufsz, &cb);
732 		if (status != EFI_TIMEOUT)
733 			break;
734 	}
735 }
736 
737 static int
efi_isa_getchar(struct console * cp)738 efi_isa_getchar(struct console *cp)
739 {
740 	EFI_STATUS status;
741 	UINTN bufsz = 1;
742 	char c;
743 	struct serial *sp = cp->c_private;
744 
745 	/*
746 	 * if this device is also used as ConIn, some firmwares
747 	 * fail to return all input via SIO protocol.
748 	 */
749 	if (sp->is_efi_console) {
750 		return (efi_console.c_in(&efi_console));
751 	}
752 
753 	if (!efi_isa_ischar(cp))
754 		return (-1);
755 
756 	status = sp->io.isa->Io.Read(sp->io.isa,
757 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT,
758 	    sp->ioaddr + com_data, bufsz, &c);
759 	if (EFI_ERROR(status))
760 		return (-1);
761 
762 	return (c);
763 }
764 
765 static int
efi_isa_ischar(struct console * cp)766 efi_isa_ischar(struct console *cp)
767 {
768 	EFI_STATUS status;
769 	uint8_t control;
770 	struct serial *sp = cp->c_private;
771 
772 	/*
773 	 * if this device is also used as ConIn, some firmwares
774 	 * fail to return all input via SIO protocol.
775 	 */
776 	if (sp->is_efi_console) {
777 		return (efi_console.c_ready(&efi_console));
778 	}
779 
780 	status = sp->io.isa->Io.Read(sp->io.isa,
781 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT,
782 	    sp->ioaddr + com_lsr, 1, &control);
783 	if (EFI_ERROR(status))
784 		return (0);
785 
786 	return (control & LSR_RXRDY);
787 }
788 
789 static int
efi_isa_ioctl(struct console * cp __unused,int cmd __unused,void * data __unused)790 efi_isa_ioctl(struct console *cp __unused, int cmd __unused,
791     void *data __unused)
792 {
793 	return (ENOTTY);
794 }
795 
796 static void
efi_isa_devinfo(struct console * cp)797 efi_isa_devinfo(struct console *cp)
798 {
799 	struct serial *port = cp->c_private;
800 	EFI_DEVICE_PATH *dp;
801 	CHAR16 *text;
802 
803 	if (port->currdev != NULL) {
804 		dp = efi_lookup_devpath(port->currdev);
805 		if (dp == NULL)
806 			return;
807 
808 		text = efi_devpath_name(dp);
809 		if (text == NULL)
810 			return;
811 
812 		printf("\tISA IO device %S", text);
813 		efi_free_devpath_name(text);
814 		efi_close_devpath(port->currdev);
815 		return;
816 	}
817 
818 	if (port->ioaddr != 0) {
819 		printf("\tISA IO port %#x", port->ioaddr);
820 	}
821 }
822 
823 static char *
efi_isa_asprint_mode(struct serial * sp)824 efi_isa_asprint_mode(struct serial *sp)
825 {
826 	char par, *buf, *stop;
827 
828 	if (sp == NULL)
829 		return (NULL);
830 
831 	switch (sp->parity) {
832 	case NoParity:
833 		par = 'n';
834 		break;
835 	case EvenParity:
836 		par = 'e';
837 		break;
838 	case OddParity:
839 		par = 'o';
840 		break;
841 	case MarkParity:
842 		par = 'm';
843 		break;
844 	case SpaceParity:
845 		par = 's';
846 		break;
847 	default:
848 		par = 'n';
849 		break;
850 	}
851 
852 	switch (sp->stopbits) {
853 	case OneStopBit:
854 		stop = "1";
855 		break;
856 	case TwoStopBits:
857 		stop = "2";
858 		break;
859 	case OneFiveStopBits:
860 		stop = "1.5";
861 		break;
862 	default:
863 		stop = "1";
864 		break;
865 	}
866 
867 	(void) asprintf(&buf, "%ju,%d,%c,%s,-", sp->baudrate,
868 	    sp->databits, par, stop);
869 	return (buf);
870 }
871 
872 static int
efi_isa_parse_mode(struct serial * sp,const char * value)873 efi_isa_parse_mode(struct serial *sp, const char *value)
874 {
875 	unsigned long n;
876 	uint64_t baudrate;
877 	uint8_t databits;
878 	EFI_PARITY_TYPE parity;
879 	EFI_STOP_BITS_TYPE stopbits;
880 	char *ep;
881 
882 	if (value == NULL || *value == '\0')
883 		return (CMD_ERROR);
884 
885 	errno = 0;
886 	n = strtoul(value, &ep, 10);
887 	if (errno != 0 || *ep != ',')
888 		return (CMD_ERROR);
889 	baudrate = n;
890 
891 	ep++;
892 	n = strtoul(ep, &ep, 10);
893 	if (errno != 0 || *ep != ',')
894 		return (CMD_ERROR);
895 
896 	switch (n) {
897 	case 5:
898 		databits = 5;
899 		break;
900 	case 6:
901 		databits = 6;
902 		break;
903 	case 7:
904 		databits = 7;
905 		break;
906 	case 8:
907 		databits = 8;
908 		break;
909 	default:
910 		return (CMD_ERROR);
911 	}
912 
913 	ep++;
914 	switch (*ep++) {
915 	case 'n':
916 		parity = NoParity;
917 		break;
918 	case 'e':
919 		parity = EvenParity;
920 		break;
921 	case 'o':
922 		parity = OddParity;
923 		break;
924 	default:
925 		return (CMD_ERROR);
926 	}
927 
928 	if (*ep == ',')
929 		ep++;
930 	else
931 		return (CMD_ERROR);
932 
933 	switch (*ep++) {
934 	case '1':
935 		stopbits = OneStopBit;
936 		break;
937 	case '2':
938 		stopbits = TwoStopBits;
939 		break;
940 	default:
941 		return (CMD_ERROR);
942 	}
943 
944 	/* handshake is ignored, but we check syntax anyhow */
945 	if (*ep == ',')
946 		ep++;
947 	else
948 		return (CMD_ERROR);
949 
950 	switch (*ep++) {
951 	case '-':
952 	case 'h':
953 	case 's':
954 		break;
955 	default:
956 		return (CMD_ERROR);
957 	}
958 
959 	if (*ep != '\0')
960 		return (CMD_ERROR);
961 
962 	sp->baudrate = baudrate;
963 	sp->databits = databits;
964 	sp->parity = parity;
965 	sp->stopbits = stopbits;
966 	return (CMD_OK);
967 }
968 
969 /*
970  * CMD_ERROR will cause set/setenv/setprop command to fail,
971  * when used in loader scripts (forth), this will cause processing
972  * of boot scripts to fail, rendering bootloading impossible.
973  * To prevent such unfortunate situation, we return CMD_OK when
974  * there is no such port, or there is invalid value in mode line.
975  */
976 static int
efi_isa_mode_set(struct env_var * ev,int flags,const void * value)977 efi_isa_mode_set(struct env_var *ev, int flags, const void *value)
978 {
979 	struct console *cp;
980 	char name[15];
981 
982 	if (value == NULL)
983 		return (CMD_ERROR);
984 
985 	if ((cp = cons_get_console(ev->ev_name)) == NULL)
986 		return (CMD_OK);
987 
988 	/* Do not override serial setup if port is listed in ConIn */
989 	(void) snprintf(name, sizeof (name), "%s-spcr-mode", cp->c_name);
990 	if (getenv(name) == NULL) {
991 		if (efi_isa_parse_mode(cp->c_private, value) == CMD_ERROR) {
992 			printf("%s: invalid mode: %s\n", ev->ev_name,
993 			    (char *)value);
994 			return (CMD_OK);
995 		}
996 
997 		(void) efi_isa_setup(cp);
998 		(void) env_setenv(ev->ev_name, flags | EV_NOHOOK, value,
999 		    NULL, NULL);
1000 	}
1001 
1002 	return (CMD_OK);
1003 }
1004 
1005 /*
1006  * CMD_ERROR will cause set/setenv/setprop command to fail,
1007  * when used in loader scripts (forth), this will cause processing
1008  * of boot scripts to fail, rendering bootloading impossible.
1009  * To prevent such unfortunate situation, we return CMD_OK when
1010  * there is no such port or invalid value was used.
1011  */
1012 static int
efi_isa_cd_set(struct env_var * ev,int flags,const void * value)1013 efi_isa_cd_set(struct env_var *ev, int flags, const void *value)
1014 {
1015 	struct console *cp;
1016 	struct serial *sp;
1017 
1018 	if (value == NULL)
1019 		return (CMD_OK);
1020 
1021 	if ((cp = cons_get_console(ev->ev_name)) == NULL)
1022 		return (CMD_OK);
1023 
1024 	sp = cp->c_private;
1025 	if (strcmp(value, "true") == 0) {
1026 		sp->ignore_cd = 1;
1027 	} else if (strcmp(value, "false") == 0) {
1028 		sp->ignore_cd = 0;
1029 	} else {
1030 		printf("%s: invalid value: %s\n", ev->ev_name,
1031 		    (char *)value);
1032 		return (CMD_OK);
1033 	}
1034 
1035 	(void) efi_isa_setup(cp);
1036 
1037 	(void) env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
1038 
1039 	return (CMD_OK);
1040 }
1041 
1042 /*
1043  * CMD_ERROR will cause set/setenv/setprop command to fail,
1044  * when used in loader scripts (forth), this will cause processing
1045  * of boot scripts to fail, rendering bootloading impossible.
1046  * To prevent such unfortunate situation, we return CMD_OK when
1047  * there is no such port, or invalid value was used.
1048  */
1049 static int
efi_isa_rtsdtr_set(struct env_var * ev,int flags,const void * value)1050 efi_isa_rtsdtr_set(struct env_var *ev, int flags, const void *value)
1051 {
1052 	struct console *cp;
1053 	struct serial *sp;
1054 
1055 	if (value == NULL)
1056 		return (CMD_OK);
1057 
1058 	if ((cp = cons_get_console(ev->ev_name)) == NULL)
1059 		return (CMD_OK);
1060 
1061 	sp = cp->c_private;
1062 	if (strcmp(value, "true") == 0) {
1063 		sp->rtsdtr_off = 1;
1064 	} else if (strcmp(value, "false") == 0) {
1065 		sp->rtsdtr_off = 0;
1066 	} else {
1067 		printf("%s: invalid value: %s\n", ev->ev_name,
1068 		    (char *)value);
1069 		return (CMD_OK);
1070 	}
1071 
1072 	(void) efi_isa_setup(cp);
1073 
1074 	(void) env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
1075 
1076 	return (CMD_OK);
1077 }
1078 
1079 /*
1080  * In case of error, we also reset ACTIVE flags, so the console
1081  * framefork will try alternate consoles.
1082  */
1083 static bool
efi_isa_setup(struct console * cp)1084 efi_isa_setup(struct console *cp)
1085 {
1086 	EFI_STATUS status;
1087 	uint8_t data, lcr;
1088 	struct serial *sp = cp->c_private;
1089 	uint_t tries, try_count = 1000000;
1090 
1091 	if (sp->baudrate == 0)
1092 		return (false);
1093 
1094 	switch (sp->databits) {
1095 	case 8:
1096 		lcr = BITS8;
1097 		break;
1098 	case 7:
1099 		lcr = BITS7;
1100 		break;
1101 	case 6:
1102 		lcr = BITS6;
1103 		break;
1104 	case 5:
1105 		lcr = BITS5;
1106 		break;
1107 	default:
1108 		lcr = BITS8;
1109 		break;
1110 	}
1111 
1112 	switch (sp->parity) {
1113 	case NoParity:
1114 		break;
1115 	case OddParity:
1116 		lcr |= PAREN|PARODD;
1117 		break;
1118 	case EvenParity:
1119 		lcr |= PAREN|PAREVN;
1120 		break;
1121 	default:
1122 		break;
1123 	}
1124 
1125 	switch (sp->stopbits) {
1126 	case OneStopBit:
1127 		break;
1128 	case TwoStopBits:
1129 		lcr |= STOP2;
1130 		break;
1131 	default:
1132 		break;
1133 	}
1134 
1135 	data = CFCR_DLAB | lcr;
1136 	status = sp->io.isa->Io.Write(sp->io.isa,
1137 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT,
1138 	    sp->ioaddr + com_cfcr, 1, &data);
1139 	if (EFI_ERROR(status))
1140 		return (false);
1141 	data = COMC_BPS(sp->baudrate) & 0xff;
1142 	status = sp->io.isa->Io.Write(sp->io.isa,
1143 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT,
1144 	    sp->ioaddr + com_dlbl, 1, &data);
1145 	if (EFI_ERROR(status))
1146 		return (false);
1147 	data = COMC_BPS(sp->baudrate) >> 8;
1148 	status = sp->io.isa->Io.Write(sp->io.isa,
1149 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT,
1150 	    sp->ioaddr + com_dlbh, 1, &data);
1151 	if (EFI_ERROR(status))
1152 		return (false);
1153 	status = sp->io.isa->Io.Write(sp->io.isa,
1154 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT,
1155 	    sp->ioaddr + com_cfcr, 1, &lcr);
1156 	if (EFI_ERROR(status))
1157 		return (false);
1158 	data = sp->rtsdtr_off? ~(MCR_RTS | MCR_DTR) : MCR_RTS | MCR_DTR;
1159 	status = sp->io.isa->Io.Write(sp->io.isa,
1160 	    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT,
1161 	    sp->ioaddr + com_mcr, 1, &data);
1162 	if (EFI_ERROR(status))
1163 		return (false);
1164 
1165 	tries = 0;
1166 	do {
1167 		status = sp->io.isa->Io.Read(sp->io.isa,
1168 		    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT,
1169 		    sp->ioaddr + com_data, 1, &data);
1170 		status = sp->io.isa->Io.Read(sp->io.isa,
1171 		    EFI_ISA_ACPI_MEMORY_WIDTH_8_BIT,
1172 		    sp->ioaddr + com_lsr, 1, &data);
1173 	} while (data & LSR_RXRDY && ++tries < try_count);
1174 
1175 	if (tries == try_count)
1176 		return (false);
1177 
1178 	/* Mark this port usable. */
1179 	cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
1180 	return (true);
1181 }
1182