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 <stand.h>
27 #include <sys/errno.h>
28 #include <bootstrap.h>
29 #include <stdbool.h>
30
31 #include <efi.h>
32 #include <efilib.h>
33
34 static EFI_GUID serial = SERIAL_IO_PROTOCOL;
35
36 #define COMC_TXWAIT 0x40000 /* transmit timeout */
37
38 #define PNP0501 0x501 /* 16550A-compatible COM port */
39
40 struct serial {
41 uint64_t newbaudrate;
42 uint64_t baudrate;
43 uint32_t timeout;
44 uint32_t receivefifodepth;
45 uint32_t databits;
46 EFI_PARITY_TYPE parity;
47 EFI_STOP_BITS_TYPE stopbits;
48 int ioaddr; /* index in handles array */
49 EFI_HANDLE currdev; /* current serial device */
50 EFI_HANDLE condev; /* EFI Console device */
51 SERIAL_IO_INTERFACE *sio;
52 };
53
54 static void comc_probe(struct console *);
55 static int comc_init(int);
56 static void comc_putchar(int);
57 static int comc_getchar(void);
58 static int comc_ischar(void);
59 static bool comc_setup(void);
60 static int comc_parse_intval(const char *, unsigned *);
61 static int comc_port_set(struct env_var *, int, const void *);
62 static int comc_speed_set(struct env_var *, int, const void *);
63
64 static struct serial *comc_port;
65 extern struct console efi_console;
66
67 struct console eficom = {
68 .c_name = "eficom",
69 .c_desc = "serial port",
70 .c_flags = 0,
71 .c_probe = comc_probe,
72 .c_init = comc_init,
73 .c_out = comc_putchar,
74 .c_in = comc_getchar,
75 .c_ready = comc_ischar,
76 };
77
78 #if defined(__aarch64__) && __FreeBSD_version < 1500000
79 static void comc_probe_compat(struct console *);
80 struct console comconsole = {
81 .c_name = "comconsole",
82 .c_desc = "serial port",
83 .c_flags = 0,
84 .c_probe = comc_probe_compat,
85 .c_init = comc_init,
86 .c_out = comc_putchar,
87 .c_in = comc_getchar,
88 .c_ready = comc_ischar,
89 };
90 #endif
91
92 static EFI_STATUS
efi_serial_init(EFI_HANDLE ** handlep,int * nhandles)93 efi_serial_init(EFI_HANDLE **handlep, int *nhandles)
94 {
95 UINTN bufsz = 0;
96 EFI_STATUS status;
97 EFI_HANDLE *handles;
98
99 /*
100 * get buffer size
101 */
102 *nhandles = 0;
103 handles = NULL;
104 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
105 if (status != EFI_BUFFER_TOO_SMALL)
106 return (status);
107
108 if ((handles = malloc(bufsz)) == NULL)
109 return (ENOMEM);
110
111 *nhandles = (int)(bufsz / sizeof (EFI_HANDLE));
112 /*
113 * get handle array
114 */
115 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
116 if (EFI_ERROR(status)) {
117 free(handles);
118 *nhandles = 0;
119 } else
120 *handlep = handles;
121 return (status);
122 }
123
124 /*
125 * Find serial device number from device path.
126 * Return -1 if not found.
127 */
128 static int
efi_serial_get_index(EFI_DEVICE_PATH * devpath,int idx)129 efi_serial_get_index(EFI_DEVICE_PATH *devpath, int idx)
130 {
131 ACPI_HID_DEVICE_PATH *acpi;
132 CHAR16 *text;
133
134 while (!IsDevicePathEnd(devpath)) {
135 if (DevicePathType(devpath) == MESSAGING_DEVICE_PATH &&
136 DevicePathSubType(devpath) == MSG_UART_DP)
137 return (idx);
138
139 if (DevicePathType(devpath) == ACPI_DEVICE_PATH &&
140 (DevicePathSubType(devpath) == ACPI_DP ||
141 DevicePathSubType(devpath) == ACPI_EXTENDED_DP)) {
142
143 acpi = (ACPI_HID_DEVICE_PATH *)devpath;
144 if (acpi->HID == EISA_PNP_ID(PNP0501)) {
145 return (acpi->UID);
146 }
147 }
148
149 devpath = NextDevicePathNode(devpath);
150 }
151 return (-1);
152 }
153
154 /*
155 * The order of handles from LocateHandle() is not known, we need to
156 * iterate handles, pick device path for handle, and check the device
157 * number.
158 */
159 static EFI_HANDLE
efi_serial_get_handle(int port,EFI_HANDLE condev)160 efi_serial_get_handle(int port, EFI_HANDLE condev)
161 {
162 EFI_STATUS status;
163 EFI_HANDLE *handles, handle;
164 EFI_DEVICE_PATH *devpath;
165 int index, nhandles;
166
167 if (port == -1)
168 return (NULL);
169
170 handles = NULL;
171 nhandles = 0;
172 status = efi_serial_init(&handles, &nhandles);
173 if (EFI_ERROR(status))
174 return (NULL);
175
176 /*
177 * We have console handle, set ioaddr for it.
178 */
179 if (condev != NULL) {
180 for (index = 0; index < nhandles; index++) {
181 if (condev == handles[index]) {
182 devpath = efi_lookup_devpath(condev);
183 comc_port->ioaddr =
184 efi_serial_get_index(devpath, index);
185 efi_close_devpath(condev);
186 free(handles);
187 return (condev);
188 }
189 }
190 }
191
192 handle = NULL;
193 for (index = 0; handle == NULL && index < nhandles; index++) {
194 devpath = efi_lookup_devpath(handles[index]);
195 if (port == efi_serial_get_index(devpath, index))
196 handle = (handles[index]);
197 efi_close_devpath(handles[index]);
198 }
199
200 /*
201 * In case we did fail to identify the device by path, use port as
202 * array index. Note, we did check port == -1 above.
203 */
204 if (port < nhandles && handle == NULL)
205 handle = handles[port];
206
207 free(handles);
208 return (handle);
209 }
210
211 static EFI_HANDLE
comc_get_con_serial_handle(const char * name)212 comc_get_con_serial_handle(const char *name)
213 {
214 EFI_HANDLE handle;
215 EFI_DEVICE_PATH *node;
216 EFI_STATUS status;
217 char *buf, *ep;
218 size_t sz;
219
220 buf = NULL;
221 sz = 0;
222 status = efi_global_getenv(name, buf, &sz);
223 if (status == EFI_BUFFER_TOO_SMALL) {
224 buf = malloc(sz);
225 if (buf == NULL)
226 return (NULL);
227 status = efi_global_getenv(name, buf, &sz);
228 }
229 if (status != EFI_SUCCESS) {
230 free(buf);
231 return (NULL);
232 }
233
234 ep = buf + sz;
235 node = (EFI_DEVICE_PATH *)buf;
236 while ((char *)node < ep) {
237 status = BS->LocateDevicePath(&serial, &node, &handle);
238 if (status == EFI_SUCCESS) {
239 free(buf);
240 return (handle);
241 }
242
243 /* Sanity check the node before moving to the next node. */
244 if (DevicePathNodeLength(node) < sizeof(*node))
245 break;
246
247 /* Start of next device path in list. */
248 node = NextDevicePathNode(node);
249 }
250 free(buf);
251 return (NULL);
252 }
253
254 /*
255 * Called from cons_probe() to see if this device is available.
256 * Return immediately on x86, except for hyperv, since it interferes with
257 * common configurations otherwise (yes, this is just firewalling the bug).
258 */
259 static void
comc_probe(struct console * sc)260 comc_probe(struct console *sc)
261 {
262 EFI_STATUS status;
263 EFI_HANDLE handle;
264 char name[20];
265 char value[20];
266 unsigned val;
267 char *env, *buf, *ep;
268 size_t sz;
269
270 #ifdef __amd64__
271 /*
272 * This driver tickles issues on a number of different firmware loads.
273 * It is only required for HyperV, and is only known to work on HyperV,
274 * so only allow it on HyperV.
275 */
276 env = getenv("smbios.bios.version");
277 if (env == NULL || strncmp(env, "Hyper-V", 7) != 0) {
278 return;
279 }
280 #endif
281
282 if (comc_port == NULL) {
283 comc_port = calloc(1, sizeof (struct serial));
284 if (comc_port == NULL)
285 return;
286 }
287
288 /* Use defaults from firmware */
289 comc_port->databits = 8;
290 comc_port->parity = DefaultParity;
291 comc_port->stopbits = DefaultStopBits;
292
293 handle = NULL;
294 env = getenv("efi_com_port");
295 if (comc_parse_intval(env, &val) == CMD_OK) {
296 comc_port->ioaddr = val;
297 } else {
298 /*
299 * efi_com_port is not set, we need to select default.
300 * First, we consult ConOut variable to see if
301 * we have serial port redirection. If not, we just
302 * pick first device.
303 */
304 handle = comc_get_con_serial_handle("ConOut");
305 comc_port->condev = handle;
306 }
307
308 handle = efi_serial_get_handle(comc_port->ioaddr, handle);
309 if (handle != NULL) {
310 comc_port->currdev = handle;
311 status = BS->OpenProtocol(handle, &serial,
312 (void**)&comc_port->sio, IH, NULL,
313 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
314
315 if (EFI_ERROR(status)) {
316 comc_port->sio = NULL;
317 } else {
318 comc_port->newbaudrate =
319 comc_port->baudrate = comc_port->sio->Mode->BaudRate;
320 comc_port->timeout = comc_port->sio->Mode->Timeout;
321 comc_port->receivefifodepth =
322 comc_port->sio->Mode->ReceiveFifoDepth;
323 comc_port->databits = comc_port->sio->Mode->DataBits;
324 comc_port->parity = comc_port->sio->Mode->Parity;
325 comc_port->stopbits = comc_port->sio->Mode->StopBits;
326 }
327 }
328
329 /*
330 * If there's no sio, then the device isn't there, so just return since
331 * the present flags aren't yet set.
332 */
333 if (comc_port->sio == NULL) {
334 free(comc_port);
335 comc_port = NULL;
336 return;
337 }
338
339 if (env != NULL)
340 unsetenv("efi_com_port");
341 snprintf(value, sizeof (value), "%u", comc_port->ioaddr);
342 env_setenv("efi_com_port", EV_VOLATILE, value,
343 comc_port_set, env_nounset);
344
345 env = getenv("efi_com_speed");
346 if (env == NULL)
347 /* fallback to comconsole setting */
348 env = getenv("comconsole_speed");
349
350 if (comc_parse_intval(env, &val) == CMD_OK)
351 comc_port->newbaudrate = val;
352
353 if (env != NULL)
354 unsetenv("efi_com_speed");
355 snprintf(value, sizeof (value), "%ju", (uintmax_t)comc_port->baudrate);
356 env_setenv("efi_com_speed", EV_VOLATILE, value,
357 comc_speed_set, env_nounset);
358
359 if (comc_setup()) {
360 sc->c_flags = C_PRESENTIN | C_PRESENTOUT;
361 } else {
362 sc->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
363 free(comc_port);
364 comc_port = NULL;
365 }
366 }
367
368 #if defined(__aarch64__) && __FreeBSD_version < 1500000
369 static void
comc_probe_compat(struct console * sc)370 comc_probe_compat(struct console *sc)
371 {
372 comc_probe(&eficom);
373 if (eficom.c_flags & (C_PRESENTIN | C_PRESENTOUT)) {
374 printf("comconsole: comconsole device name is deprecated, switch to eficom\n");
375 }
376 /*
377 * Note: We leave the present bits unset in sc to avoid ghosting.
378 */
379 }
380 #endif
381
382 /*
383 * Called when the console is selected in cons_change. If we didn't detect the
384 * device, comc_port will be NULL, and comc_setup will fail. It may be called
385 * even when the device isn't present as a 'fallback' console or when listed
386 * specifically in console env, so we have to reset the c_flags in those case to
387 * say it's not present.
388 */
389 static int
comc_init(int arg __unused)390 comc_init(int arg __unused)
391 {
392 if (comc_setup())
393 return (0);
394
395 eficom.c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT);
396 return (1);
397 }
398
399 static void
comc_putchar(int c)400 comc_putchar(int c)
401 {
402 int wait;
403 EFI_STATUS status;
404 UINTN bufsz = 1;
405 char cb = c;
406
407 if (comc_port->sio == NULL)
408 return;
409
410 for (wait = COMC_TXWAIT; wait > 0; wait--) {
411 status = comc_port->sio->Write(comc_port->sio, &bufsz, &cb);
412 if (status != EFI_TIMEOUT)
413 break;
414 }
415 }
416
417 static int
comc_getchar(void)418 comc_getchar(void)
419 {
420 EFI_STATUS status;
421 UINTN bufsz = 1;
422 char c;
423
424
425 /*
426 * if this device is also used as ConIn, some firmwares
427 * fail to return all input via SIO protocol.
428 */
429 if (comc_port->currdev == comc_port->condev) {
430 if ((efi_console.c_flags & C_ACTIVEIN) == 0)
431 return (efi_console.c_in());
432 return (-1);
433 }
434
435 if (comc_port->sio == NULL)
436 return (-1);
437
438 status = comc_port->sio->Read(comc_port->sio, &bufsz, &c);
439 if (EFI_ERROR(status) || bufsz == 0)
440 return (-1);
441
442 return (c);
443 }
444
445 static int
comc_ischar(void)446 comc_ischar(void)
447 {
448 EFI_STATUS status;
449 uint32_t control;
450
451 /*
452 * if this device is also used as ConIn, some firmwares
453 * fail to return all input via SIO protocol.
454 */
455 if (comc_port->currdev == comc_port->condev) {
456 if ((efi_console.c_flags & C_ACTIVEIN) == 0)
457 return (efi_console.c_ready());
458 return (0);
459 }
460
461 if (comc_port->sio == NULL)
462 return (0);
463
464 status = comc_port->sio->GetControl(comc_port->sio, &control);
465 if (EFI_ERROR(status))
466 return (0);
467
468 return (!(control & EFI_SERIAL_INPUT_BUFFER_EMPTY));
469 }
470
471 static int
comc_parse_intval(const char * value,unsigned * valp)472 comc_parse_intval(const char *value, unsigned *valp)
473 {
474 unsigned n;
475 char *ep;
476
477 if (value == NULL || *value == '\0')
478 return (CMD_ERROR);
479
480 errno = 0;
481 n = strtoul(value, &ep, 10);
482 if (errno != 0 || *ep != '\0')
483 return (CMD_ERROR);
484 *valp = n;
485
486 return (CMD_OK);
487 }
488
489 static int
comc_port_set(struct env_var * ev,int flags,const void * value)490 comc_port_set(struct env_var *ev, int flags, const void *value)
491 {
492 unsigned port;
493 SERIAL_IO_INTERFACE *sio;
494 EFI_HANDLE handle;
495 EFI_STATUS status;
496
497 if (value == NULL || comc_port == NULL)
498 return (CMD_ERROR);
499
500 if (comc_parse_intval(value, &port) != CMD_OK)
501 return (CMD_ERROR);
502
503 handle = efi_serial_get_handle(port, NULL);
504 if (handle == NULL) {
505 printf("no handle\n");
506 return (CMD_ERROR);
507 }
508
509 status = BS->OpenProtocol(handle, &serial,
510 (void**)&sio, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
511
512 if (EFI_ERROR(status)) {
513 printf("OpenProtocol: %lu\n", EFI_ERROR_CODE(status));
514 return (CMD_ERROR);
515 }
516
517 comc_port->currdev = handle;
518 comc_port->ioaddr = port;
519 comc_port->sio = sio;
520
521 (void) comc_setup();
522
523 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
524 return (CMD_OK);
525 }
526
527 static int
comc_speed_set(struct env_var * ev,int flags,const void * value)528 comc_speed_set(struct env_var *ev, int flags, const void *value)
529 {
530 unsigned speed;
531
532 if (value == NULL || comc_port == NULL)
533 return (CMD_ERROR);
534
535 if (comc_parse_intval(value, &speed) != CMD_OK)
536 return (CMD_ERROR);
537
538 comc_port->newbaudrate = speed;
539 if (comc_setup())
540 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
541
542 return (CMD_OK);
543 }
544
545 /*
546 * In case of error, we also reset ACTIVE flags, so the console
547 * framefork will try alternate consoles.
548 */
549 static bool
comc_setup(void)550 comc_setup(void)
551 {
552 EFI_STATUS status;
553 char *ev;
554
555 /*
556 * If the device isn't active, or there's no port present.
557 */
558 if ((eficom.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0 || comc_port == NULL)
559 return (false);
560
561 if (comc_port->sio->Reset != NULL) {
562 status = comc_port->sio->Reset(comc_port->sio);
563 if (EFI_ERROR(status))
564 return (false);
565 }
566
567 /*
568 * Avoid setting the baud rate on Hyper-V. Also, only set the baud rate
569 * if the baud rate has changed from the default. And pass in '0' or
570 * DefaultFoo when we're not changing those values. Some EFI
571 * implementations get cranky when you set things to the values reported
572 * back even when they are unchanged.
573 */
574 if (comc_port->sio->SetAttributes != NULL &&
575 comc_port->newbaudrate != comc_port->baudrate) {
576 ev = getenv("smbios.bios.version");
577 if (ev != NULL && strncmp(ev, "Hyper-V", 7) != 0) {
578 status = comc_port->sio->SetAttributes(comc_port->sio,
579 comc_port->newbaudrate, 0, 0, DefaultParity, 0,
580 DefaultStopBits);
581 if (EFI_ERROR(status))
582 return (false);
583 comc_port->baudrate = comc_port->newbaudrate;
584 }
585 }
586
587 #ifdef EFI_FORCE_RTS
588 if (comc_port->sio->GetControl != NULL && comc_port->sio->SetControl != NULL) {
589 UINT32 control;
590
591 status = comc_port->sio->GetControl(comc_port->sio, &control);
592 if (EFI_ERROR(status))
593 return (false);
594 control |= EFI_SERIAL_REQUEST_TO_SEND;
595 (void) comc_port->sio->SetControl(comc_port->sio, control);
596 }
597 #endif
598 /* Mark this port usable. */
599 eficom.c_flags |= (C_PRESENTIN | C_PRESENTOUT);
600 return (true);
601 }
602