9652 loader.efi: use explicit lea in multiboot_tramp.S
[unleashed.git] / usr / src / boot / sys / boot / efi / loader / comconsole.c
blob539637b8332adabb720b75aa9213115ead75dfac
1 /*
2 * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
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.
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.
26 #include <sys/cdefs.h>
28 #include <stand.h>
29 #include <sys/errno.h>
30 #include <bootstrap.h>
32 #include <efi.h>
33 #include <efilib.h>
35 #include "loader_efi.h"
37 static EFI_GUID serial = SERIAL_IO_PROTOCOL;
39 #define COMC_TXWAIT 0x40000 /* transmit timeout */
41 #ifndef COMSPEED
42 #define COMSPEED 9600
43 #endif
45 struct serial {
46 uint64_t baudrate;
47 uint8_t databits;
48 EFI_PARITY_TYPE parity;
49 EFI_STOP_BITS_TYPE stopbits;
50 uint8_t ignore_cd; /* boolean */
51 uint8_t rtsdtr_off; /* boolean */
52 int ioaddr; /* index in handles array */
53 SERIAL_IO_INTERFACE *sio;
56 static void comc_probe(struct console *);
57 static int comc_init(struct console *, int);
58 static void comc_putchar(struct console *, int);
59 static int comc_getchar(struct console *);
60 static int comc_ischar(struct console *);
61 static void comc_setup(struct console *);
62 static char *comc_asprint_mode(struct serial *);
63 static int comc_parse_mode(struct serial *, const char *);
64 static int comc_mode_set(struct env_var *, int, const void *);
65 static int comc_cd_set(struct env_var *, int, const void *);
66 static int comc_rtsdtr_set(struct env_var *, int, const void *);
68 struct console ttya = {
69 .c_name = "ttya",
70 .c_desc = "serial port a",
71 .c_flags = 0,
72 .c_probe = comc_probe,
73 .c_init = comc_init,
74 .c_out = comc_putchar,
75 .c_in = comc_getchar,
76 .c_ready = comc_ischar,
77 .c_private = NULL
80 struct console ttyb = {
81 .c_name = "ttyb",
82 .c_desc = "serial port b",
83 .c_flags = 0,
84 .c_probe = comc_probe,
85 .c_init = comc_init,
86 .c_out = comc_putchar,
87 .c_in = comc_getchar,
88 .c_ready = comc_ischar,
89 .c_private = NULL
92 struct console ttyc = {
93 .c_name = "ttyc",
94 .c_desc = "serial port c",
95 .c_flags = 0,
96 .c_probe = comc_probe,
97 .c_init = comc_init,
98 .c_out = comc_putchar,
99 .c_in = comc_getchar,
100 .c_ready = comc_ischar,
101 .c_private = NULL
104 struct console ttyd = {
105 .c_name = "ttyd",
106 .c_desc = "serial port d",
107 .c_flags = 0,
108 .c_probe = comc_probe,
109 .c_init = comc_init,
110 .c_out = comc_putchar,
111 .c_in = comc_getchar,
112 .c_ready = comc_ischar,
113 .c_private = NULL
116 EFI_STATUS
117 efi_serial_init(EFI_HANDLE **handlep, int *nhandles)
119 UINTN bufsz = 0;
120 EFI_STATUS status;
121 EFI_HANDLE *handles;
124 * get buffer size
126 *nhandles = 0;
127 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
128 if (status != EFI_BUFFER_TOO_SMALL)
129 return (status);
131 if ((handles = malloc(bufsz)) == NULL)
132 return (ENOMEM);
134 *nhandles = (int)(bufsz/sizeof (EFI_HANDLE));
136 * get handle array
138 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
139 if (EFI_ERROR(status)) {
140 free(handles);
141 *nhandles = 0;
142 } else
143 *handlep = handles;
144 return (status);
147 static void
148 comc_probe(struct console *cp)
150 EFI_STATUS status;
151 struct serial *port;
152 char name[20];
153 char value[20];
154 char *cons, *env;
155 EFI_HANDLE *handles = NULL; /* array of handles */
156 int nhandles = 0; /* number of handles in array */
158 /* are we already set up? */
159 if (cp->c_private != NULL)
160 return;
162 /* make sure the handles are available */
163 status = efi_serial_init(&handles, &nhandles);
165 cp->c_private = malloc(sizeof (struct serial));
166 port = cp->c_private;
167 port->baudrate = COMSPEED;
169 if (strcmp(cp->c_name, "ttya") == 0)
170 port->ioaddr = 0;
171 else if (strcmp(cp->c_name, "ttyb") == 0)
172 port->ioaddr = 1;
173 else if (strcmp(cp->c_name, "ttyc") == 0)
174 port->ioaddr = 2;
175 else if (strcmp(cp->c_name, "ttyd") == 0)
176 port->ioaddr = 3;
178 if (port->ioaddr >= nhandles)
179 port->ioaddr = -1; /* invalid port */
181 port->databits = 8; /* 8,n,1 */
182 port->parity = NoParity; /* 8,n,1 */
183 port->stopbits = OneStopBit; /* 8,n,1 */
184 port->ignore_cd = 1; /* ignore cd */
185 port->rtsdtr_off = 0; /* rts-dtr is on */
186 port->sio = NULL;
188 if (port->ioaddr != -1) {
189 status = BS->OpenProtocol(handles[port->ioaddr],
190 &serial, (void**)&port->sio, IH, NULL,
191 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
193 if (EFI_ERROR(status))
194 port->ioaddr = -1; /* invalid port */
196 if (handles != NULL)
197 free(handles);
199 snprintf(name, sizeof (name), "%s-mode", cp->c_name);
200 env = getenv(name);
202 if (env != NULL)
203 (void) comc_parse_mode(port, env);
205 env = comc_asprint_mode(port);
207 if (env != NULL) {
208 unsetenv(name);
209 env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset);
210 free(env);
213 snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name);
214 env = getenv(name);
215 if (env != NULL) {
216 if (strcmp(env, "true") == 0)
217 port->ignore_cd = 1;
218 else if (strcmp(env, "false") == 0)
219 port->ignore_cd = 0;
222 snprintf(value, sizeof (value), "%s",
223 port->ignore_cd? "true" : "false");
224 unsetenv(name);
225 env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset);
227 snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name);
228 env = getenv(name);
229 if (env != NULL) {
230 if (strcmp(env, "true") == 0)
231 port->rtsdtr_off = 1;
232 else if (strcmp(env, "false") == 0)
233 port->rtsdtr_off = 0;
236 snprintf(value, sizeof (value), "%s",
237 port->rtsdtr_off? "true" : "false");
238 unsetenv(name);
239 env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset);
240 comc_setup(cp);
243 static int
244 comc_init(struct console *cp, int arg __attribute((unused)))
247 comc_setup(cp);
249 if ((cp->c_flags & (C_PRESENTIN | C_PRESENTOUT)) ==
250 (C_PRESENTIN | C_PRESENTOUT))
251 return (CMD_OK);
252 return (CMD_ERROR);
255 static void
256 comc_putchar(struct console *cp, int c)
258 int wait;
259 EFI_STATUS status;
260 UINTN bufsz = 1;
261 char cb = c;
262 struct serial *sp = cp->c_private;
264 if (sp->sio == NULL)
265 return;
267 for (wait = COMC_TXWAIT; wait > 0; wait--) {
268 status = sp->sio->Write(sp->sio, &bufsz, &cb);
269 if (status != EFI_TIMEOUT)
270 break;
274 static int
275 comc_getchar(struct console *cp)
277 EFI_STATUS status;
278 UINTN bufsz = 1;
279 char c;
280 struct serial *sp = cp->c_private;
282 if (sp->sio == NULL || !comc_ischar(cp))
283 return (-1);
285 status = sp->sio->Read(sp->sio, &bufsz, &c);
286 if (EFI_ERROR(status) || bufsz == 0)
287 return (-1);
289 return (c);
292 static int
293 comc_ischar(struct console *cp)
295 EFI_STATUS status;
296 uint32_t control;
297 struct serial *sp = cp->c_private;
299 if (sp->sio == NULL)
300 return (0);
302 status = sp->sio->GetControl(sp->sio, &control);
303 if (EFI_ERROR(status))
304 return (0);
306 return (!(status & EFI_SERIAL_INPUT_BUFFER_EMPTY));
309 static char *
310 comc_asprint_mode(struct serial *sp)
312 char par = 'n', *buf;
313 int stop = 1;
315 if (sp == NULL)
316 return (NULL);
318 switch (sp->parity) {
319 case NoParity: par = 'n';
320 break;
321 case EvenParity: par = 'e';
322 break;
323 case OddParity: par = 'o';
324 break;
326 switch (sp->stopbits) {
327 case OneStopBit: stop = 1;
328 break;
329 case TwoStopBits: stop = 2;
330 break;
333 asprintf(&buf, "%ju,%d,%c,%d,-", sp->baudrate, sp->databits, par, stop);
334 return (buf);
337 static int
338 comc_parse_mode(struct serial *sp, const char *value)
340 unsigned long n;
341 uint64_t baudrate;
342 uint8_t databits = 8;
343 int parity = NoParity;
344 int stopbits = OneStopBit;
345 char *ep;
347 if (value == NULL || *value == '\0')
348 return (CMD_ERROR);
350 errno = 0;
351 n = strtoul(value, &ep, 10);
352 if (errno != 0 || *ep != ',')
353 return (CMD_ERROR);
354 baudrate = n;
356 ep++;
357 n = strtoul(ep, &ep, 10);
358 if (errno != 0 || *ep != ',')
359 return (CMD_ERROR);
361 switch (n) {
362 case 7: databits = 7;
363 break;
364 case 8: databits = 8;
365 break;
366 default:
367 return (CMD_ERROR);
370 ep++;
371 switch (*ep++) {
372 case 'n': parity = NoParity;
373 break;
374 case 'e': parity = EvenParity;
375 break;
376 case 'o': parity = OddParity;
377 break;
378 default:
379 return (CMD_ERROR);
382 if (*ep == ',')
383 ep++;
384 else
385 return (CMD_ERROR);
387 switch (*ep++) {
388 case '1': stopbits = OneStopBit;
389 break;
390 case '2': stopbits = TwoStopBits;
391 break;
392 default:
393 return (CMD_ERROR);
396 /* handshake is ignored, but we check syntax anyhow */
397 if (*ep == ',')
398 ep++;
399 else
400 return (CMD_ERROR);
402 switch (*ep++) {
403 case '-':
404 case 'h':
405 case 's':
406 break;
407 default:
408 return (CMD_ERROR);
411 if (*ep != '\0')
412 return (CMD_ERROR);
414 sp->baudrate = baudrate;
415 sp->databits = databits;
416 sp->parity = parity;
417 sp->stopbits = stopbits;
418 return (CMD_OK);
421 static struct console *
422 get_console(char *name)
424 struct console *cp = NULL;
426 switch (name[3]) {
427 case 'a': cp = &ttya;
428 break;
429 case 'b': cp = &ttyb;
430 break;
431 case 'c': cp = &ttyc;
432 break;
433 case 'd': cp = &ttyd;
434 break;
436 return (cp);
439 static int
440 comc_mode_set(struct env_var *ev, int flags, const void *value)
442 struct console *cp;
444 if (value == NULL)
445 return (CMD_ERROR);
447 if ((cp = get_console(ev->ev_name)) == NULL)
448 return (CMD_ERROR);
450 if (comc_parse_mode(cp->c_private, value) == CMD_ERROR)
451 return (CMD_ERROR);
453 comc_setup(cp);
454 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
456 return (CMD_OK);
459 static int
460 comc_cd_set(struct env_var *ev, int flags, const void *value)
462 struct console *cp;
463 struct serial *sp;
465 if (value == NULL)
466 return (CMD_ERROR);
468 if ((cp = get_console(ev->ev_name)) == NULL)
469 return (CMD_ERROR);
471 sp = cp->c_private;
472 if (strcmp(value, "true") == 0)
473 sp->ignore_cd = 1;
474 else if (strcmp(value, "false") == 0)
475 sp->ignore_cd = 0;
476 else
477 return (CMD_ERROR);
479 comc_setup(cp);
480 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
482 return (CMD_OK);
485 static int
486 comc_rtsdtr_set(struct env_var *ev, int flags, const void *value)
488 struct console *cp;
489 struct serial *sp;
491 if (value == NULL)
492 return (CMD_ERROR);
494 if ((cp = get_console(ev->ev_name)) == NULL)
495 return (CMD_ERROR);
497 sp = cp->c_private;
498 if (strcmp(value, "true") == 0)
499 sp->rtsdtr_off = 1;
500 else if (strcmp(value, "false") == 0)
501 sp->rtsdtr_off = 0;
502 else
503 return (CMD_ERROR);
505 comc_setup(cp);
506 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
508 return (CMD_OK);
511 static void
512 comc_setup(struct console *cp)
514 EFI_STATUS status;
515 UINT32 control;
516 struct serial *sp = cp->c_private;
518 if ((cp->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0)
519 return;
521 /* port is not usable */
522 if (sp->sio == NULL) {
523 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
524 return;
527 cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
528 status = sp->sio->Reset(sp->sio);
529 if (EFI_ERROR(status)) {
530 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
533 status = sp->sio->SetAttributes(sp->sio, sp->baudrate, 0, 0, sp->parity,
534 sp->databits, sp->stopbits);
535 if (EFI_ERROR(status)) {
536 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
539 if (sp->rtsdtr_off) {
540 status = sp->sio->GetControl(sp->sio, &control);
541 if (EFI_ERROR(status)) {
542 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
544 control &= ~(EFI_SERIAL_REQUEST_TO_SEND |
545 EFI_SERIAL_DATA_TERMINAL_READY);
546 status = sp->sio->SetControl(sp->sio, control);
547 if (EFI_ERROR(status)) {
548 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);