Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 14 Mar 2020 06:36:03 +0000 (UTC)
From:      Toomas Soome <tsoome@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r358989 - in head/stand/efi: libefi loader loader/arch/arm loader/arch/arm64
Message-ID:  <202003140636.02E6a3NS020671@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: tsoome
Date: Sat Mar 14 06:36:03 2020
New Revision: 358989
URL: https://svnweb.freebsd.org/changeset/base/358989

Log:
  loader: add comconsole implementation on top of SIO protocol
  
  Provide comconsole on top of SIO for arm platforms (x86 does use bios version).

Added:
  head/stand/efi/loader/efiserialio.c   (contents, props changed)
Modified:
  head/stand/efi/libefi/efi_console.c
  head/stand/efi/loader/arch/arm/Makefile.inc
  head/stand/efi/loader/arch/arm64/Makefile.inc
  head/stand/efi/loader/conf.c
  head/stand/efi/loader/main.c

Modified: head/stand/efi/libefi/efi_console.c
==============================================================================
--- head/stand/efi/libefi/efi_console.c	Sat Mar 14 05:57:22 2020	(r358988)
+++ head/stand/efi/libefi/efi_console.c	Sat Mar 14 06:36:03 2020	(r358989)
@@ -377,9 +377,22 @@ efi_cons_respond(void *s __unused, const void *buf __u
 {
 }
 
+/*
+ * Set up conin/conout/coninex to make sure we have input ready.
+ */
 static void
 efi_cons_probe(struct console *cp)
 {
+	EFI_STATUS status;
+
+	conout = ST->ConOut;
+	conin = ST->ConIn;
+
+	status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid,
+	    (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+	if (status != EFI_SUCCESS)
+		coninex = NULL;
+
 	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
 }
 
@@ -889,15 +902,7 @@ efi_cons_init(int arg)
 	if (conin != NULL)
 		return (0);
 
-	conout = ST->ConOut;
-	conin = ST->ConIn;
-
 	conout->EnableCursor(conout, TRUE);
-	status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid,
-	    (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
-	if (status != EFI_SUCCESS)
-		coninex = NULL;
-
 	if (efi_cons_update_mode())
 		return (0);
 

Modified: head/stand/efi/loader/arch/arm/Makefile.inc
==============================================================================
--- head/stand/efi/loader/arch/arm/Makefile.inc	Sat Mar 14 05:57:22 2020	(r358988)
+++ head/stand/efi/loader/arch/arm/Makefile.inc	Sat Mar 14 06:36:03 2020	(r358989)
@@ -1,6 +1,7 @@
 # $FreeBSD$
 
 SRCS+=	exec.c \
+	efiserialio.c \
 	start.S
 
 HAVE_FDT=yes

Modified: head/stand/efi/loader/arch/arm64/Makefile.inc
==============================================================================
--- head/stand/efi/loader/arch/arm64/Makefile.inc	Sat Mar 14 05:57:22 2020	(r358988)
+++ head/stand/efi/loader/arch/arm64/Makefile.inc	Sat Mar 14 06:36:03 2020	(r358989)
@@ -3,6 +3,7 @@
 HAVE_FDT=yes
 
 SRCS+=	exec.c \
+	efiserialio.c \
 	start.S
 
 .PATH:	${BOOTSRC}/arm64/libarm64

Modified: head/stand/efi/loader/conf.c
==============================================================================
--- head/stand/efi/loader/conf.c	Sat Mar 14 05:57:22 2020	(r358988)
+++ head/stand/efi/loader/conf.c	Sat Mar 14 06:36:03 2020	(r358989)
@@ -73,16 +73,16 @@ struct netif_driver *netif_drivers[] = {
 };
 
 extern struct console efi_console;
-#if defined(__amd64__) || defined(__i386__)
 extern struct console comconsole;
+#if defined(__amd64__) || defined(__i386__)
 extern struct console nullconsole;
 extern struct console spinconsole;
 #endif
 
 struct console *consoles[] = {
 	&efi_console,
-#if defined(__amd64__) || defined(__i386__)
 	&comconsole,
+#if defined(__amd64__) || defined(__i386__)
 	&nullconsole,
 	&spinconsole,
 #endif

Added: head/stand/efi/loader/efiserialio.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/stand/efi/loader/efiserialio.c	Sat Mar 14 06:36:03 2020	(r358989)
@@ -0,0 +1,518 @@
+/*-
+ * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/errno.h>
+#include <bootstrap.h>
+#include <stdbool.h>
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "loader_efi.h"
+
+static EFI_GUID serial = SERIAL_IO_PROTOCOL;
+
+#define	COMC_TXWAIT	0x40000		/* transmit timeout */
+
+#ifndef	COMSPEED
+#define	COMSPEED	9600
+#endif
+
+#define	PNP0501		0x501		/* 16550A-compatible COM port */
+
+struct serial {
+	uint64_t	baudrate;
+	uint8_t		databits;
+	EFI_PARITY_TYPE	parity;
+	EFI_STOP_BITS_TYPE stopbits;
+	uint8_t		ignore_cd;	/* boolean */
+	uint8_t		rtsdtr_off;	/* boolean */
+	int		ioaddr;		/* index in handles array */
+	EFI_HANDLE	currdev;	/* current serial device */
+	EFI_HANDLE	condev;		/* EFI Console device */
+	SERIAL_IO_INTERFACE *sio;
+};
+
+static void	comc_probe(struct console *);
+static int	comc_init(int);
+static void	comc_putchar(int);
+static int	comc_getchar(void);
+static int	comc_ischar(void);
+static bool	comc_setup(void);
+static int	comc_parse_intval(const char *, unsigned *);
+static int	comc_port_set(struct env_var *, int, const void *);
+static int	comc_speed_set(struct env_var *, int, const void *);
+
+static struct serial	*comc_port;
+extern struct console efi_console;
+
+struct console comconsole = {
+	.c_name = "comconsole",
+	.c_desc = "serial port",
+	.c_flags = 0,
+	.c_probe = comc_probe,
+	.c_init = comc_init,
+	.c_out = comc_putchar,
+	.c_in = comc_getchar,
+	.c_ready = comc_ischar,
+};
+
+static EFI_STATUS
+efi_serial_init(EFI_HANDLE **handlep, int *nhandles)
+{
+	UINTN bufsz = 0;
+	EFI_STATUS status;
+	EFI_HANDLE *handles;
+
+	/*
+	 * get buffer size
+	 */
+	*nhandles = 0;
+	handles = NULL;
+	status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
+	if (status != EFI_BUFFER_TOO_SMALL)
+		return (status);
+
+	if ((handles = malloc(bufsz)) == NULL)
+		return (ENOMEM);
+
+	*nhandles = (int)(bufsz / sizeof (EFI_HANDLE));
+	/*
+	 * get handle array
+	 */
+	status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
+	if (EFI_ERROR(status)) {
+		free(handles);
+		*nhandles = 0;
+	} else
+		*handlep = handles;
+	return (status);
+}
+
+/*
+ * Find serial device number from device path.
+ * Return -1 if not found.
+ */
+static int
+efi_serial_get_index(EFI_DEVICE_PATH *devpath, int idx)
+{
+	ACPI_HID_DEVICE_PATH  *acpi;
+	CHAR16 *text;
+
+	while (!IsDevicePathEnd(devpath)) {
+		if (DevicePathType(devpath) == MESSAGING_DEVICE_PATH &&
+		    DevicePathSubType(devpath) == MSG_UART_DP)
+			return (idx);
+
+		if (DevicePathType(devpath) == ACPI_DEVICE_PATH &&
+		    (DevicePathSubType(devpath) == ACPI_DP ||
+		    DevicePathSubType(devpath) == ACPI_EXTENDED_DP)) {
+
+			acpi = (ACPI_HID_DEVICE_PATH *)devpath;
+			if (acpi->HID == EISA_PNP_ID(PNP0501)) {
+				return (acpi->UID);
+			}
+		}
+
+		devpath = NextDevicePathNode(devpath);
+	}
+	return (-1);
+}
+
+/*
+ * The order of handles from LocateHandle() is not known, we need to
+ * iterate handles, pick device path for handle, and check the device
+ * number.
+ */
+static EFI_HANDLE
+efi_serial_get_handle(int port, EFI_HANDLE condev)
+{
+	EFI_STATUS status;
+	EFI_HANDLE *handles, handle;
+	EFI_DEVICE_PATH *devpath;
+	int index, nhandles;
+
+	if (port == -1)
+		return (NULL);
+
+	handles = NULL;
+	nhandles = 0;
+	status = efi_serial_init(&handles, &nhandles);
+	if (EFI_ERROR(status))
+		return (NULL);
+
+	/*
+	 * We have console handle, set ioaddr for it.
+	 */
+	if (condev != NULL) {
+		for (index = 0; index < nhandles; index++) {
+			if (condev == handles[index]) {
+				devpath = efi_lookup_devpath(condev);
+				comc_port->ioaddr =
+				    efi_serial_get_index(devpath, index);
+				efi_close_devpath(condev);
+				free(handles);
+				return (condev);
+			}
+		}
+	}
+
+	handle = NULL;
+	for (index = 0; handle == NULL && index < nhandles; index++) {
+		devpath = efi_lookup_devpath(handles[index]);
+		if (port == efi_serial_get_index(devpath, index))
+			handle = (handles[index]);
+		efi_close_devpath(handles[index]);
+	}
+
+	/*
+	 * In case we did fail to identify the device by path, use port as
+	 * array index. Note, we did check port == -1 above.
+	 */
+	if (port < nhandles && handle == NULL)
+		handle = handles[port];
+
+	free(handles);
+	return (handle);
+}
+
+static EFI_HANDLE
+comc_get_con_serial_handle(const char *name)
+{
+	EFI_HANDLE handle;
+	EFI_DEVICE_PATH *node;
+	EFI_STATUS status;
+	char *buf, *ep;
+	size_t sz;
+
+	buf = NULL;
+	sz = 0;
+	status = efi_global_getenv(name, buf, &sz);
+	if (status == EFI_BUFFER_TOO_SMALL) {
+		buf = malloc(sz);
+		if (buf != NULL)
+			status = efi_global_getenv(name, buf, &sz);
+	}
+	if (status != EFI_SUCCESS) {
+		free(buf);
+		return (NULL);
+	}
+
+	ep = buf + sz;
+	node = (EFI_DEVICE_PATH *)buf;
+	while ((char *)node < ep) {
+		status = BS->LocateDevicePath(&serial, &node, &handle);
+		if (status == EFI_SUCCESS) {
+			free(buf);
+			return (handle);
+		}
+		if (IsDevicePathEndType(node) &&
+		    DevicePathSubType(node) ==
+		    END_INSTANCE_DEVICE_PATH_SUBTYPE) {
+			/*
+			 * Start of next device path in list.
+			 */
+			node = NextDevicePathNode(node);
+			continue;
+		}
+		if (IsDevicePathEnd(node))
+			break;
+	}
+	free(buf);
+	return (NULL);
+}
+
+static void
+comc_probe(struct console *sc)
+{
+	EFI_STATUS status;
+	EFI_HANDLE handle;
+	char name[20];
+	char value[20];
+	unsigned val;
+	char *env, *buf, *ep;
+	size_t sz;
+
+	if (comc_port == NULL) {
+		comc_port = malloc(sizeof (struct serial));
+		if (comc_port == NULL)
+			return;
+	}
+	comc_port->baudrate = COMSPEED;
+	comc_port->ioaddr = 0;			/* default port */
+	comc_port->databits = 8;		/* 8,n,1 */
+	comc_port->parity = NoParity;		/* 8,n,1 */
+	comc_port->stopbits = OneStopBit;	/* 8,n,1 */
+	comc_port->ignore_cd = 1;		/* ignore cd */
+	comc_port->rtsdtr_off = 0;		/* rts-dtr is on */
+	comc_port->sio = NULL;
+
+	handle = NULL;
+	env = getenv("efi_com_port");
+	if (comc_parse_intval(env, &val) == CMD_OK) {
+		comc_port->ioaddr = val;
+	} else {
+		/*
+		 * efi_com_port is not set, we need to select default.
+		 * First, we consult ConOut variable to see if
+		 * we have serial port redirection. If not, we just
+		 * pick first device.
+		 */
+		handle = comc_get_con_serial_handle("ConOut");
+		comc_port->condev = handle;
+	}
+
+	handle = efi_serial_get_handle(comc_port->ioaddr, handle);
+	if (handle != NULL) {
+		comc_port->currdev = handle;
+		status = BS->OpenProtocol(handle, &serial,
+		    (void**)&comc_port->sio, IH, NULL,
+		    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+		if (EFI_ERROR(status))
+			comc_port->sio = NULL;
+	}
+
+	if (env != NULL) 
+		unsetenv("efi_com_port");
+	snprintf(value, sizeof (value), "%u", comc_port->ioaddr);
+	env_setenv("efi_com_port", EV_VOLATILE, value,
+	    comc_port_set, env_nounset);
+
+	env = getenv("efi_com_speed");
+	if (comc_parse_intval(env, &val) == CMD_OK)
+		comc_port->baudrate = val;
+
+	if (env != NULL)
+		unsetenv("efi_com_speed");
+	snprintf(value, sizeof (value), "%ju", (uintmax_t)comc_port->baudrate);
+	env_setenv("efi_com_speed", EV_VOLATILE, value,
+	    comc_speed_set, env_nounset);
+
+	comconsole.c_flags = 0;
+	if (comc_setup())
+		sc->c_flags = C_PRESENTIN | C_PRESENTOUT;
+}
+
+static int
+comc_init(int arg __unused)
+{
+
+	if (comc_setup())
+		return (CMD_OK);
+
+	comconsole.c_flags = 0;
+	return (CMD_ERROR);
+}
+
+static void
+comc_putchar(int c)
+{
+	int wait;
+	EFI_STATUS status;
+	UINTN bufsz = 1;
+	char cb = c;
+
+	if (comc_port->sio == NULL)
+		return;
+
+	for (wait = COMC_TXWAIT; wait > 0; wait--) {
+		status = comc_port->sio->Write(comc_port->sio, &bufsz, &cb);
+		if (status != EFI_TIMEOUT)
+			break;
+	}
+}
+
+static int
+comc_getchar(void)
+{
+	EFI_STATUS status;
+	UINTN bufsz = 1;
+	char c;
+
+
+	/*
+	 * if this device is also used as ConIn, some firmwares
+	 * fail to return all input via SIO protocol.
+	 */
+	if (comc_port->currdev == comc_port->condev) {
+		if ((efi_console.c_flags & C_ACTIVEIN) == 0)
+			return (efi_console.c_in());
+		return (-1);
+	}
+
+	if (comc_port->sio == NULL)
+		return (-1);
+
+	status = comc_port->sio->Read(comc_port->sio, &bufsz, &c);
+	if (EFI_ERROR(status) || bufsz == 0)
+		return (-1);
+
+	return (c);
+}
+
+static int
+comc_ischar(void)
+{
+	EFI_STATUS status;
+	uint32_t control;
+
+	/*
+	 * if this device is also used as ConIn, some firmwares
+	 * fail to return all input via SIO protocol.
+	 */
+	if (comc_port->currdev == comc_port->condev) {
+		if ((efi_console.c_flags & C_ACTIVEIN) == 0)
+			return (efi_console.c_ready());
+		return (0);
+	}
+
+	if (comc_port->sio == NULL)
+		return (0);
+
+	status = comc_port->sio->GetControl(comc_port->sio, &control);
+	if (EFI_ERROR(status))
+		return (0);
+
+	return (!(control & EFI_SERIAL_INPUT_BUFFER_EMPTY));
+}
+
+static int
+comc_parse_intval(const char *value, unsigned *valp)
+{
+	unsigned n;
+	char *ep;
+
+	if (value == NULL || *value == '\0')
+		return (CMD_ERROR);
+
+	errno = 0;
+	n = strtoul(value, &ep, 10);
+	if (errno != 0 || *ep != '\0')
+		return (CMD_ERROR);
+	*valp = n;
+
+	return (CMD_OK);
+}
+
+static int
+comc_port_set(struct env_var *ev, int flags, const void *value)
+{
+	unsigned port;
+	SERIAL_IO_INTERFACE *sio;
+	EFI_HANDLE handle;
+	EFI_STATUS status;
+
+	if (value == NULL)
+		return (CMD_ERROR);
+
+	if (comc_parse_intval(value, &port) != CMD_OK) 
+		return (CMD_ERROR);
+
+	handle = efi_serial_get_handle(port, NULL);
+	if (handle == NULL) {
+		printf("no handle\n");
+		return (CMD_ERROR);
+	}
+
+	status = BS->OpenProtocol(handle, &serial,
+	    (void**)&sio, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+	if (EFI_ERROR(status)) {
+		printf("OpenProtocol: %lu\n", EFI_ERROR_CODE(status));
+		return (CMD_ERROR);
+	}
+
+	comc_port->currdev = handle;
+	comc_port->ioaddr = port;
+	comc_port->sio = sio;
+	
+	(void) comc_setup();
+
+	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+	return (CMD_OK);
+}
+
+static int
+comc_speed_set(struct env_var *ev, int flags, const void *value)
+{
+	unsigned speed;
+
+	if (value == NULL)
+		return (CMD_ERROR);
+
+	if (comc_parse_intval(value, &speed) != CMD_OK) 
+		return (CMD_ERROR);
+
+	comc_port->baudrate = speed;
+	(void) comc_setup();
+
+	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+
+	return (CMD_OK);
+}
+
+/*
+ * In case of error, we also reset ACTIVE flags, so the console
+ * framefork will try alternate consoles.
+ */
+static bool
+comc_setup(void)
+{
+	EFI_STATUS status;
+	UINT32 control;
+
+	/* port is not usable */
+	if (comc_port->sio == NULL)
+		return (false);
+
+	status = comc_port->sio->Reset(comc_port->sio);
+	if (EFI_ERROR(status))
+		return (false);
+
+	status = comc_port->sio->SetAttributes(comc_port->sio,
+	    comc_port->baudrate, 0, 0, comc_port->parity,
+	    comc_port->databits, comc_port->stopbits);
+	if (EFI_ERROR(status))
+		return (false);
+
+	status = comc_port->sio->GetControl(comc_port->sio, &control);
+	if (EFI_ERROR(status))
+		return (false);
+	if (comc_port->rtsdtr_off) {
+		control &= ~(EFI_SERIAL_REQUEST_TO_SEND |
+		    EFI_SERIAL_DATA_TERMINAL_READY);
+	} else {
+		control |= EFI_SERIAL_REQUEST_TO_SEND;
+	}
+	(void) comc_port->sio->SetControl(comc_port->sio, control);
+	/* Mark this port usable. */
+	comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+	return (true);
+}

Modified: head/stand/efi/loader/main.c
==============================================================================
--- head/stand/efi/loader/main.c	Sat Mar 14 05:57:22 2020	(r358988)
+++ head/stand/efi/loader/main.c	Sat Mar 14 06:36:03 2020	(r358989)
@@ -722,7 +722,8 @@ parse_uefi_con_out(void)
 	while ((char *)node < ep) {
 		pci_pending = false;
 		if (DevicePathType(node) == ACPI_DEVICE_PATH &&
-		    DevicePathSubType(node) == ACPI_DP) {
+		    (DevicePathSubType(node) == ACPI_DP ||
+		    DevicePathSubType(node) == ACPI_EXTENDED_DP)) {
 			/* Check for Serial node */
 			acpi = (void *)node;
 			if (EISA_ID_TO_NUM(acpi->HID) == 0x501) {
@@ -731,7 +732,7 @@ parse_uefi_con_out(void)
 			}
 		} else if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
 		    DevicePathSubType(node) == MSG_UART_DP) {
-
+			com_seen = ++seen;
 			uart = (void *)node;
 			setenv_int("efi_com_speed", uart->BaudRate);
 		} else if (DevicePathType(node) == ACPI_DEVICE_PATH &&
@@ -897,6 +898,11 @@ main(int argc, CHAR16 *argv[])
 	 * changes to take effect, regardless of where they come from.
 	 */
 	setenv("console", "efi", 1);
+	uhowto = parse_uefi_con_out();
+#if defined(__aarch64__) || defined(__arm__)
+	if ((uhowto & RB_SERIAL) != 0)
+		setenv("console", "comconsole", 1);
+#endif
 	cons_probe();
 
 	/* Init the time source */
@@ -930,7 +936,6 @@ main(int argc, CHAR16 *argv[])
 	if (!has_kbd && (howto & RB_PROBE))
 		howto |= RB_SERIAL | RB_MULTIPLE;
 	howto &= ~RB_PROBE;
-	uhowto = parse_uefi_con_out();
 
 	/*
 	 * Read additional environment variables from the boot device's



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202003140636.02E6a3NS020671>