diff options
author | Jonathan Gray <jsg@cvs.openbsd.org> | 2019-03-15 06:53:38 +0000 |
---|---|---|
committer | Jonathan Gray <jsg@cvs.openbsd.org> | 2019-03-15 06:53:38 +0000 |
commit | ace2a0ead8129749e6be405252844ba3fcaf56b3 (patch) | |
tree | 02c4bade3e71b2aa62021c5814d48a726b3ac335 /sys/arch/amd64/stand/efiboot/efiboot.c | |
parent | 878756b65c9378b63e072bc26197cbf636759ef8 (diff) |
Many machines with serial ports ship with uefi firmware which does not
have the serial io protocol. Fallback to direct inb/outb hardware access
in this case using code derived from arch/amd64/stand/libsa/bioscons.c
ok kettenis@
Diffstat (limited to 'sys/arch/amd64/stand/efiboot/efiboot.c')
-rw-r--r-- | sys/arch/amd64/stand/efiboot/efiboot.c | 113 |
1 files changed, 107 insertions, 6 deletions
diff --git a/sys/arch/amd64/stand/efiboot/efiboot.c b/sys/arch/amd64/stand/efiboot/efiboot.c index 060e8886afc..83ede4f7ebe 100644 --- a/sys/arch/amd64/stand/efiboot/efiboot.c +++ b/sys/arch/amd64/stand/efiboot/efiboot.c @@ -1,4 +1,4 @@ -/* $OpenBSD: efiboot.c,v 1.32 2018/11/20 03:10:47 yasuoka Exp $ */ +/* $OpenBSD: efiboot.c,v 1.33 2019/03/15 06:53:37 jsg Exp $ */ /* * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> @@ -20,9 +20,11 @@ #include <sys/queue.h> #include <dev/cons.h> #include <dev/isa/isareg.h> +#include <dev/ic/comreg.h> #include <sys/disklabel.h> #include <cmd.h> #include <stand/boot/bootarg.h> +#include <machine/pio.h> #include "libsa.h" #include "disk.h" @@ -533,6 +535,87 @@ int com_addr = -1; int com_speed = -1; static SERIAL_IO_INTERFACE *serios[4]; +const int comports[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; + +/* call with sp == 0 to query the current speed */ +int +pio_comspeed(dev_t dev, int sp) +{ + int port = (com_addr == -1) ? comports[minor(dev)] : com_addr; + int i, newsp; + int err; + + if (sp <= 0) + return com_speed; + /* valid baud rate? */ + if (115200 < sp || sp < 75) + return -1; + + /* + * Accepted speeds: + * 75 150 300 600 1200 2400 4800 9600 19200 38400 76800 and + * 14400 28800 57600 115200 + */ + for (i = sp; i != 75 && i != 14400; i >>= 1) + if (i & 1) + return -1; + +/* ripped screaming from dev/ic/com.c */ +#define divrnd(n, q) (((n)*2/(q)+1)/2) /* divide and round off */ + newsp = divrnd((COM_FREQ / 16), sp); + if (newsp <= 0) + return -1; + err = divrnd((COM_FREQ / 16) * 1000, sp * newsp) - 1000; + if (err < 0) + err = -err; + if (err > COM_TOLERANCE) + return -1; +#undef divrnd + + if (com_speed != -1 && cn_tab && cn_tab->cn_dev == dev && + com_speed != sp) { + printf("com%d: changing speed to %d baud in 5 seconds, " + "change your terminal to match!\n\a", + minor(dev), sp); + sleep(5); + } + + outb(port + com_cfcr, LCR_DLAB); + outb(port + com_dlbl, newsp); + outb(port + com_dlbh, newsp>>8); + outb(port + com_cfcr, LCR_8BITS); + if (com_speed != -1) + printf("\ncom%d: %d baud\n", minor(dev), sp); + + newsp = com_speed; + com_speed = sp; + return newsp; +} + +int +pio_com_getc(dev_t dev) +{ + int port = (com_addr == -1) ? comports[minor(dev & 0x7f)] : com_addr; + + if (dev & 0x80) + return (inb(port + com_lsr) & LSR_RXRDY); + + while ((inb(port + com_lsr) & LSR_RXRDY) == 0) + ; + + return (inb(port + com_data) & 0xff); +} + +void +pio_com_putc(dev_t dev, int c) +{ + int port = (com_addr == -1) ? comports[minor(dev)] : com_addr; + + while ((inb(port + com_lsr) & LSR_TXRDY) == 0) + ; + + outb(port + com_data, c); +} void efi_com_probe(struct consdev *cn) @@ -545,6 +628,9 @@ efi_com_probe(struct consdev *cn) UINTN sz; int i, uid = -1; + cn->cn_pri = CN_LOWPRI; + cn->cn_dev = makedev(8, 0); + sz = 0; status = EFI_CALL(BS->LocateHandle, ByProtocol, &serio_guid, 0, &sz, 0); if (status == EFI_BUFFER_TOO_SMALL) { @@ -594,8 +680,6 @@ efi_com_probe(struct consdev *cn) if (serios[i] != NULL) printf(" com%d", i); } - cn->cn_pri = CN_LOWPRI; - cn->cn_dev = makedev(8, 0); } int @@ -615,7 +699,7 @@ comspeed(dev_t dev, int sp) return com_speed; if (!efi_valid_com(dev)) - return (-1); + return pio_comspeed(dev, sp); if (serio->Mode->BaudRate != sp) { status = EFI_CALL(serio->SetAttributes, serio, @@ -659,7 +743,7 @@ efi_com_getc(dev_t dev) static u_char lastchar = 0; if (!efi_valid_com(dev & 0x7f)) - return (0) ; + return pio_com_getc(dev); serio = serios[minor(dev & 0x7f)]; if (lastchar != 0) { @@ -693,8 +777,10 @@ efi_com_putc(dev_t dev, int c) UINTN sz = 1; u_char buf; - if (!efi_valid_com(dev)) + if (!efi_valid_com(dev)) { + pio_com_putc(dev, c); return; + } serio = serios[minor(dev)]; buf = c; EFI_CALL(serio->Write, serio, &sz, &buf); @@ -856,6 +942,21 @@ getsecs(void) return (r); } +u_int +sleep(u_int i) +{ + time_t t; + + /* + * Loop for the requested number of seconds, polling, + * so that it may handle interrupts. + */ + for (t = getsecs() + i; getsecs() < t; cnischar()) + ; + + return 0; +} + /*********************************************************************** * Commands ***********************************************************************/ |