9771 loader.efi: 'handles' is used uninitialized in comconsole.c
[unleashed.git] / usr / src / boot / sys / boot / efi / loader / comconsole.c
blob57168cca17915e4501755a11ca9cd9a9b35ac5ac
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 handles = NULL;
128 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
129 if (status != EFI_BUFFER_TOO_SMALL)
130 return (status);
132 if ((handles = malloc(bufsz)) == NULL)
133 return (ENOMEM);
135 *nhandles = (int)(bufsz/sizeof (EFI_HANDLE));
137 * get handle array
139 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
140 if (EFI_ERROR(status)) {
141 free(handles);
142 *nhandles = 0;
143 } else
144 *handlep = handles;
145 return (status);
148 static void
149 comc_probe(struct console *cp)
151 EFI_STATUS status;
152 struct serial *port;
153 char name[20];
154 char value[20];
155 char *env;
156 EFI_HANDLE *handles = NULL; /* array of handles */
157 int nhandles = 0; /* number of handles in array */
159 /* are we already set up? */
160 if (cp->c_private != NULL)
161 return;
163 /* make sure the handles are available */
164 status = efi_serial_init(&handles, &nhandles);
166 cp->c_private = malloc(sizeof (struct serial));
167 port = cp->c_private;
168 port->baudrate = COMSPEED;
170 if (strcmp(cp->c_name, "ttya") == 0)
171 port->ioaddr = 0;
172 else if (strcmp(cp->c_name, "ttyb") == 0)
173 port->ioaddr = 1;
174 else if (strcmp(cp->c_name, "ttyc") == 0)
175 port->ioaddr = 2;
176 else if (strcmp(cp->c_name, "ttyd") == 0)
177 port->ioaddr = 3;
179 if (port->ioaddr >= nhandles)
180 port->ioaddr = -1; /* invalid port */
182 port->databits = 8; /* 8,n,1 */
183 port->parity = NoParity; /* 8,n,1 */
184 port->stopbits = OneStopBit; /* 8,n,1 */
185 port->ignore_cd = 1; /* ignore cd */
186 port->rtsdtr_off = 0; /* rts-dtr is on */
187 port->sio = NULL;
189 if (port->ioaddr != -1) {
190 status = BS->OpenProtocol(handles[port->ioaddr],
191 &serial, (void**)&port->sio, IH, NULL,
192 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
194 if (EFI_ERROR(status))
195 port->ioaddr = -1; /* invalid port */
197 if (handles != NULL)
198 free(handles);
200 snprintf(name, sizeof (name), "%s-mode", cp->c_name);
201 env = getenv(name);
203 if (env != NULL)
204 (void) comc_parse_mode(port, env);
206 env = comc_asprint_mode(port);
208 if (env != NULL) {
209 unsetenv(name);
210 env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset);
211 free(env);
214 snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name);
215 env = getenv(name);
216 if (env != NULL) {
217 if (strcmp(env, "true") == 0)
218 port->ignore_cd = 1;
219 else if (strcmp(env, "false") == 0)
220 port->ignore_cd = 0;
223 snprintf(value, sizeof (value), "%s",
224 port->ignore_cd? "true" : "false");
225 unsetenv(name);
226 env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset);
228 snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name);
229 env = getenv(name);
230 if (env != NULL) {
231 if (strcmp(env, "true") == 0)
232 port->rtsdtr_off = 1;
233 else if (strcmp(env, "false") == 0)
234 port->rtsdtr_off = 0;
237 snprintf(value, sizeof (value), "%s",
238 port->rtsdtr_off? "true" : "false");
239 unsetenv(name);
240 env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset);
241 comc_setup(cp);
244 static int
245 comc_init(struct console *cp, int arg __attribute((unused)))
248 comc_setup(cp);
250 if ((cp->c_flags & (C_PRESENTIN | C_PRESENTOUT)) ==
251 (C_PRESENTIN | C_PRESENTOUT))
252 return (CMD_OK);
253 return (CMD_ERROR);
256 static void
257 comc_putchar(struct console *cp, int c)
259 int wait;
260 EFI_STATUS status;
261 UINTN bufsz = 1;
262 char cb = c;
263 struct serial *sp = cp->c_private;
265 if (sp->sio == NULL)
266 return;
268 for (wait = COMC_TXWAIT; wait > 0; wait--) {
269 status = sp->sio->Write(sp->sio, &bufsz, &cb);
270 if (status != EFI_TIMEOUT)
271 break;
275 static int
276 comc_getchar(struct console *cp)
278 EFI_STATUS status;
279 UINTN bufsz = 1;
280 char c;
281 struct serial *sp = cp->c_private;
283 if (sp->sio == NULL || !comc_ischar(cp))
284 return (-1);
286 status = sp->sio->Read(sp->sio, &bufsz, &c);
287 if (EFI_ERROR(status) || bufsz == 0)
288 return (-1);
290 return (c);
293 static int
294 comc_ischar(struct console *cp)
296 EFI_STATUS status;
297 uint32_t control;
298 struct serial *sp = cp->c_private;
300 if (sp->sio == NULL)
301 return (0);
303 status = sp->sio->GetControl(sp->sio, &control);
304 if (EFI_ERROR(status))
305 return (0);
307 return (!(status & EFI_SERIAL_INPUT_BUFFER_EMPTY));
310 static char *
311 comc_asprint_mode(struct serial *sp)
313 char par = 'n', *buf;
314 int stop = 1;
316 if (sp == NULL)
317 return (NULL);
319 switch (sp->parity) {
320 case NoParity: par = 'n';
321 break;
322 case EvenParity: par = 'e';
323 break;
324 case OddParity: par = 'o';
325 break;
327 switch (sp->stopbits) {
328 case OneStopBit: stop = 1;
329 break;
330 case TwoStopBits: stop = 2;
331 break;
334 asprintf(&buf, "%ju,%d,%c,%d,-", sp->baudrate, sp->databits, par, stop);
335 return (buf);
338 static int
339 comc_parse_mode(struct serial *sp, const char *value)
341 unsigned long n;
342 uint64_t baudrate;
343 uint8_t databits = 8;
344 int parity = NoParity;
345 int stopbits = OneStopBit;
346 char *ep;
348 if (value == NULL || *value == '\0')
349 return (CMD_ERROR);
351 errno = 0;
352 n = strtoul(value, &ep, 10);
353 if (errno != 0 || *ep != ',')
354 return (CMD_ERROR);
355 baudrate = n;
357 ep++;
358 n = strtoul(ep, &ep, 10);
359 if (errno != 0 || *ep != ',')
360 return (CMD_ERROR);
362 switch (n) {
363 case 7: databits = 7;
364 break;
365 case 8: databits = 8;
366 break;
367 default:
368 return (CMD_ERROR);
371 ep++;
372 switch (*ep++) {
373 case 'n': parity = NoParity;
374 break;
375 case 'e': parity = EvenParity;
376 break;
377 case 'o': parity = OddParity;
378 break;
379 default:
380 return (CMD_ERROR);
383 if (*ep == ',')
384 ep++;
385 else
386 return (CMD_ERROR);
388 switch (*ep++) {
389 case '1': stopbits = OneStopBit;
390 break;
391 case '2': stopbits = TwoStopBits;
392 break;
393 default:
394 return (CMD_ERROR);
397 /* handshake is ignored, but we check syntax anyhow */
398 if (*ep == ',')
399 ep++;
400 else
401 return (CMD_ERROR);
403 switch (*ep++) {
404 case '-':
405 case 'h':
406 case 's':
407 break;
408 default:
409 return (CMD_ERROR);
412 if (*ep != '\0')
413 return (CMD_ERROR);
415 sp->baudrate = baudrate;
416 sp->databits = databits;
417 sp->parity = parity;
418 sp->stopbits = stopbits;
419 return (CMD_OK);
422 static struct console *
423 get_console(char *name)
425 struct console *cp = NULL;
427 switch (name[3]) {
428 case 'a': cp = &ttya;
429 break;
430 case 'b': cp = &ttyb;
431 break;
432 case 'c': cp = &ttyc;
433 break;
434 case 'd': cp = &ttyd;
435 break;
437 return (cp);
440 static int
441 comc_mode_set(struct env_var *ev, int flags, const void *value)
443 struct console *cp;
445 if (value == NULL)
446 return (CMD_ERROR);
448 if ((cp = get_console(ev->ev_name)) == NULL)
449 return (CMD_ERROR);
451 if (comc_parse_mode(cp->c_private, value) == CMD_ERROR)
452 return (CMD_ERROR);
454 comc_setup(cp);
455 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
457 return (CMD_OK);
460 static int
461 comc_cd_set(struct env_var *ev, int flags, const void *value)
463 struct console *cp;
464 struct serial *sp;
466 if (value == NULL)
467 return (CMD_ERROR);
469 if ((cp = get_console(ev->ev_name)) == NULL)
470 return (CMD_ERROR);
472 sp = cp->c_private;
473 if (strcmp(value, "true") == 0)
474 sp->ignore_cd = 1;
475 else if (strcmp(value, "false") == 0)
476 sp->ignore_cd = 0;
477 else
478 return (CMD_ERROR);
480 comc_setup(cp);
481 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
483 return (CMD_OK);
486 static int
487 comc_rtsdtr_set(struct env_var *ev, int flags, const void *value)
489 struct console *cp;
490 struct serial *sp;
492 if (value == NULL)
493 return (CMD_ERROR);
495 if ((cp = get_console(ev->ev_name)) == NULL)
496 return (CMD_ERROR);
498 sp = cp->c_private;
499 if (strcmp(value, "true") == 0)
500 sp->rtsdtr_off = 1;
501 else if (strcmp(value, "false") == 0)
502 sp->rtsdtr_off = 0;
503 else
504 return (CMD_ERROR);
506 comc_setup(cp);
507 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
509 return (CMD_OK);
512 static void
513 comc_setup(struct console *cp)
515 EFI_STATUS status;
516 UINT32 control;
517 struct serial *sp = cp->c_private;
519 if ((cp->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0)
520 return;
522 /* port is not usable */
523 if (sp->sio == NULL) {
524 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
525 return;
528 cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
529 status = sp->sio->Reset(sp->sio);
530 if (EFI_ERROR(status)) {
531 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
534 status = sp->sio->SetAttributes(sp->sio, sp->baudrate, 0, 0, sp->parity,
535 sp->databits, sp->stopbits);
536 if (EFI_ERROR(status)) {
537 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
540 if (sp->rtsdtr_off) {
541 status = sp->sio->GetControl(sp->sio, &control);
542 if (EFI_ERROR(status)) {
543 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
545 control &= ~(EFI_SERIAL_REQUEST_TO_SEND |
546 EFI_SERIAL_DATA_TERMINAL_READY);
547 status = sp->sio->SetControl(sp->sio, control);
548 if (EFI_ERROR(status)) {
549 cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);