summaryrefslogtreecommitdiff
path: root/sys/arch/amd64/stand/efiboot/efiboot.c
diff options
context:
space:
mode:
authorJonathan Gray <jsg@cvs.openbsd.org>2019-03-15 06:53:38 +0000
committerJonathan Gray <jsg@cvs.openbsd.org>2019-03-15 06:53:38 +0000
commitace2a0ead8129749e6be405252844ba3fcaf56b3 (patch)
tree02c4bade3e71b2aa62021c5814d48a726b3ac335 /sys/arch/amd64/stand/efiboot/efiboot.c
parent878756b65c9378b63e072bc26197cbf636759ef8 (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.c113
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
***********************************************************************/