Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 21 Feb 2019 02:28:04 +0000 (UTC)
From:      Kyle Evans <kevans@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r344403 - in stable/11/stand/efi: include loader
Message-ID:  <201902210228.x1L2S4dn075574@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kevans
Date: Thu Feb 21 02:28:04 2019
New Revision: 344403
URL: https://svnweb.freebsd.org/changeset/base/344403

Log:
  MFC r336264, r336270-r336272, r336464, r336627: UEFI Console Improvements
  
  r336264:
  Define ADR subtype of ACPI type for a device path.
  
  r336270:
  uefi stand: Guess the console better
  
  For server machines, ComOut is set to the set of devices that the efi
  console suppots. Parse it to see if we have serial, video or both.
  Make that take precidence over the command line args. boot1.efi parses
  them, but loader.efi doesn't. It's not clear where to read boot.conf
  from, so we don't do that. The command line args can still be set via
  efibootmgr, which is more inline with the UEFI boot manager to replace
  that. These args are typically used only to set serial vs video and
  the com speed line. We can infer that from ComOut, so do so.
  Remember the com speed and hw.uart.console to match.
  
  r336271:
  Add reporting of whether or not a keyboard is detected. In addition,
  note that r336270's commit message was slightly incorrect. It changed
  the default setting of the console to honor the ConOut
  variable. Overrides via the command line are still possible, and we
  use the devices in ConOut to set the proper console. If, for example,
  serial cosnole is specified, we'll set console to "efi" if ConOut has
  a serial port list and to either "efi comconsole" or "comconsole efi"
  if not depending on whether -D or -D -h was specified.
  
  r336272:
  Minor adjustments:
  
  o Fix the parsing of the device path. a last minute change terminated
    it too soon.
  o Kill setting LINES. We don't need to do it, and even if we did hard
    coding it to 24 is wrong.
  o Now that the console is working again for the loader, adjust the
    printfs to be more in line with other platforms.
  
  r336464:
  If the console is already set, don't override it.
  
  If console=X is specified on the command line, it's effectively
  overridden by the current code. It shouldn't do that.
  
  r336627:
  Fix the attempt to see if we're overriding the console in the command
  line args. I had thought console would be NULL, but it's efi. Set it
  to efi (as a clue) before we initialize the console, then test it to
  see if it changed on the command line to do the automatic
  override. This gets my serial console back.
  
  RelNotes: yes

Modified:
  stable/11/stand/efi/include/efidevp.h
  stable/11/stand/efi/loader/bootinfo.c
  stable/11/stand/efi/loader/main.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/stand/efi/include/efidevp.h
==============================================================================
--- stable/11/stand/efi/include/efidevp.h	Thu Feb 21 01:30:37 2019	(r344402)
+++ stable/11/stand/efi/include/efidevp.h	Thu Feb 21 02:28:04 2019	(r344403)
@@ -141,6 +141,9 @@ typedef struct _ACPI_EXTENDED_HID_DEVICE_PATH {
   UINT32                          CID;
 } ACPI_EXTENDED_HID_DEVICE_PATH;
 
+#define ACPI_ADR_DP                         0x03
+/* ACPI_ADR_DEVICE_PATH not defined */
+
 //
 // EISA ID Macro
 // EISA ID Definition 32-bits

Modified: stable/11/stand/efi/loader/bootinfo.c
==============================================================================
--- stable/11/stand/efi/loader/bootinfo.c	Thu Feb 21 01:30:37 2019	(r344402)
+++ stable/11/stand/efi/loader/bootinfo.c	Thu Feb 21 02:28:04 2019	(r344403)
@@ -67,10 +67,11 @@ extern EFI_SYSTEM_TABLE	*ST;
 static int
 bi_getboothowto(char *kargs)
 {
-	const char *sw;
+	const char *sw, *tmp;
 	char *opts;
 	char *console;
-	int howto;
+	int howto, speed, port;
+	char buf[50];
 
 	howto = boot_parse_cmdline(kargs);
 	howto |= boot_env_to_howto();
@@ -81,6 +82,35 @@ bi_getboothowto(char *kargs)
 			howto |= RB_SERIAL;
 		if (strcmp(console, "nullconsole") == 0)
 			howto |= RB_MUTE;
+		if (strcmp(console, "efi") == 0) {
+			/*
+			 * If we found a com port and com speed, we need to tell
+			 * the kernel where the serial port is, and how
+			 * fast. Ideally, we'd get the port from ACPI, but that
+			 * isn't running in the loader. Do the next best thing
+			 * by allowing it to be set by a loader.conf variable,
+			 * either a EFI specific one, or the compatible
+			 * comconsole_port if not. PCI support is needed, but
+			 * for that we'd ideally refactor the
+			 * libi386/comconsole.c code to have identical behavior.
+			 */
+			tmp = getenv("efi_com_speed");
+			if (tmp != NULL) {
+				speed = strtol(tmp, NULL, 0);
+				tmp = getenv("efi_com_port");
+				if (tmp == NULL)
+					tmp = getenv("comconsole_port");
+				/* XXX fallback to EFI variable set in rc.d? */
+				if (tmp != NULL)
+					port = strtol(tmp, NULL, 0);
+				else
+					port = 0x3f8;
+				snprintf(buf, sizeof(buf), "io:%d,br:%d", port,
+				    speed);
+				env_setenv("hw.uart.console", EV_VOLATILE, buf,
+				    NULL, NULL);
+			}
+		}
 	}
 
 	return (howto);

Modified: stable/11/stand/efi/loader/main.c
==============================================================================
--- stable/11/stand/efi/loader/main.c	Thu Feb 21 01:30:37 2019	(r344402)
+++ stable/11/stand/efi/loader/main.c	Thu Feb 21 02:28:04 2019	(r344403)
@@ -316,11 +316,6 @@ find_currdev(EFI_LOADED_IMAGE *img)
 		if (dp->pd_parent != NULL) {
 			dp = dp->pd_parent;
 			STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
-				text = efi_devpath_name(pp->pd_devpath);
-				if (text != NULL) {
-					printf("And now the part: %S\n", text);
-					efi_free_devpath_name(text);
-				}
 				/*
 				 * Roll up the ZFS special case
 				 * for those partitions that have
@@ -400,8 +395,8 @@ interactive_interrupt(const char *msg)
 	return (false);
 }
 
-int
-parse_args(int argc, CHAR16 *argv[], bool has_kbd)
+static int
+parse_args(int argc, CHAR16 *argv[])
 {
 	int i, j, howto;
 	bool vargood;
@@ -429,12 +424,97 @@ parse_args(int argc, CHAR16 *argv[], bool has_kbd)
 	return (howto);
 }
 
+/*
+ * Parse ConOut (the list of consoles active) and see if we can find a
+ * serial port and/or a video port. It would be nice to also walk the
+ * ACPI name space to map the UID for the serial port to a port. The
+ * latter is especially hard.
+ */
+static int
+parse_uefi_con_out(void)
+{
+	int how, rv;
+	int vid_seen = 0, com_seen = 0, seen = 0;
+	size_t sz;
+	char buf[4096], *ep;
+	EFI_DEVICE_PATH *node;
+	ACPI_HID_DEVICE_PATH  *acpi;
+	UART_DEVICE_PATH  *uart;
+	bool pci_pending;
 
+	how = 0;
+	sz = sizeof(buf);
+	rv = efi_global_getenv("ConOut", buf, &sz);
+	if (rv != EFI_SUCCESS)
+		goto out;
+	ep = buf + sz;
+	node = (EFI_DEVICE_PATH *)buf;
+	while ((char *)node < ep) {
+		pci_pending = false;
+		if (DevicePathType(node) == ACPI_DEVICE_PATH &&
+		    DevicePathSubType(node) == ACPI_DP) {
+			/* Check for Serial node */
+			acpi = (void *)node;
+			if (EISA_ID_TO_NUM(acpi->HID) == 0x501)
+				com_seen = ++seen;
+		} else if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
+		    DevicePathSubType(node) == MSG_UART_DP) {
+			char bd[16];
+
+			uart = (void *)node;
+			snprintf(bd, sizeof(bd), "%d", uart->BaudRate);
+			setenv("efi_com_speed", bd, 1);
+		} else if (DevicePathType(node) == ACPI_DEVICE_PATH &&
+		    DevicePathSubType(node) == ACPI_ADR_DP) {
+			/* Check for AcpiAdr() Node for video */
+			vid_seen = ++seen;
+		} else if (DevicePathType(node) == HARDWARE_DEVICE_PATH &&
+		    DevicePathSubType(node) == HW_PCI_DP) {
+			/*
+			 * Note, vmware fusion has a funky console device
+			 *	PciRoot(0x0)/Pci(0xf,0x0)
+			 * which we can only detect at the end since we also
+			 * have to cope with:
+			 *	PciRoot(0x0)/Pci(0x1f,0x0)/Serial(0x1)
+			 * so only match it if it's last.
+			 */
+			pci_pending = true;
+		}
+		node = NextDevicePathNode(node); /* Skip the end node */
+	}
+	if (pci_pending && vid_seen == 0)
+		vid_seen = ++seen;
+
+	/*
+	 * Truth table for RB_MULTIPLE | RB_SERIAL
+	 * Value		Result
+	 * 0			Use only video console
+	 * RB_SERIAL		Use only serial console
+	 * RB_MULTIPLE		Use both video and serial console
+	 *			(but video is primary so gets rc messages)
+	 * both			Use both video and serial console
+	 *			(but serial is primary so gets rc messages)
+	 *
+	 * Try to honor this as best we can. If only one of serial / video
+	 * found, then use that. Otherwise, use the first one we found.
+	 * This also implies if we found nothing, default to video.
+	 */
+	how = 0;
+	if (vid_seen && com_seen) {
+		how |= RB_MULTIPLE;
+		if (com_seen < vid_seen)
+			how |= RB_SERIAL;
+	} else if (com_seen)
+		how |= RB_SERIAL;
+out:
+	return (how);
+}
+
 EFI_STATUS
 main(int argc, CHAR16 *argv[])
 {
 	EFI_GUID *guid;
-	int howto, i;
+	int howto, i, uhowto;
 	UINTN k;
 	bool has_kbd;
 	char *s;
@@ -474,6 +554,7 @@ main(int argc, CHAR16 *argv[])
 	 * eg. the boot device, which we can't do yet.  We can use
 	 * printf() etc. once this is done.
 	 */
+	setenv("console", "efi", 1);
 	cons_probe();
 
 	/*
@@ -481,23 +562,61 @@ main(int argc, CHAR16 *argv[])
 	 */
 	bcache_init(32768, 512);
 
-	howto = parse_args(argc, argv, has_kbd);
+	howto = parse_args(argc, argv);
+	if (!has_kbd && (howto & RB_PROBE))
+		howto |= RB_SERIAL | RB_MULTIPLE;
+	howto &= ~RB_PROBE;
+	uhowto = parse_uefi_con_out();
 
-	boot_howto_to_env(howto);
-
 	/*
-	 * XXX we need fallback to this stuff after looking at the ConIn, ConOut and ConErr variables
+	 * We now have two notions of console. howto should be viewed as
+	 * overrides. If console is already set, don't set it again.
 	 */
-	if (howto & RB_MULTIPLE) {
-		if (howto & RB_SERIAL)
-			setenv("console", "comconsole efi" , 1);
-		else
-			setenv("console", "efi comconsole" , 1);
-	} else if (howto & RB_SERIAL) {
-		setenv("console", "comconsole" , 1);
-	} else
-		setenv("console", "efi", 1);
-
+#define	VIDEO_ONLY	0
+#define	SERIAL_ONLY	RB_SERIAL
+#define	VID_SER_BOTH	RB_MULTIPLE
+#define	SER_VID_BOTH	(RB_SERIAL | RB_MULTIPLE)
+#define	CON_MASK	(RB_SERIAL | RB_MULTIPLE)
+	if (strcmp(getenv("console"), "efi") == 0) {
+		if ((howto & CON_MASK) == 0) {
+			/* No override, uhowto is controlling and efi cons is perfect */
+			howto = howto | (uhowto & CON_MASK);
+			setenv("console", "efi", 1);
+		} else if ((howto & CON_MASK) == (uhowto & CON_MASK)) {
+			/* override matches what UEFI told us, efi console is perfect */
+			setenv("console", "efi", 1);
+		} else if ((uhowto & (CON_MASK)) != 0) {
+			/*
+			 * We detected a serial console on ConOut. All possible
+			 * overrides include serial. We can't really override what efi
+			 * gives us, so we use it knowing it's the best choice.
+			 */
+			setenv("console", "efi", 1);
+		} else {
+			/*
+			 * We detected some kind of serial in the override, but ConOut
+			 * has no serial, so we have to sort out which case it really is.
+			 */
+			switch (howto & CON_MASK) {
+			case SERIAL_ONLY:
+				setenv("console", "comconsole", 1);
+				break;
+			case VID_SER_BOTH:
+				setenv("console", "efi comconsole", 1);
+				break;
+			case SER_VID_BOTH:
+				setenv("console", "comconsole efi", 1);
+				break;
+				/* case VIDEO_ONLY can't happen -- it's the first if above */
+			}
+		}
+	}
+	/*
+	 * howto is set now how we want to export the flags to the kernel, so
+	 * set the env based on it.
+	 */
+	boot_howto_to_env(howto);
+	
 	if (efi_copy_init()) {
 		printf("failed to allocate staging area\n");
 		return (EFI_BUFFER_TOO_SMALL);
@@ -517,18 +636,17 @@ main(int argc, CHAR16 *argv[])
 	} else
 		printf("efipart_inithandles failed %d, expect failures", i);
 
-	printf("Command line arguments:");
+	printf("%s\n", bootprog_info);
+	printf("   Command line arguments:");
 	for (i = 0; i < argc; i++)
 		printf(" %S", argv[i]);
 	printf("\n");
 
-	printf("Image base: 0x%lx\n", (u_long)img->ImageBase);
-	printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16,
+	printf("   EFI version: %d.%02d\n", ST->Hdr.Revision >> 16,
 	    ST->Hdr.Revision & 0xffff);
-	printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor,
+	printf("   EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor,
 	    ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
 
-	printf("\n%s", bootprog_info);
 
 	/* Determine the devpath of our image so we can prefer it. */
 	text = efi_devpath_name(img->FilePath);
@@ -584,7 +702,6 @@ main(int argc, CHAR16 *argv[])
 			return (EFI_NOT_FOUND);
 
 	efi_init_environment();
-	setenv("LINES", "24", 1);	/* optional */
 
 #if !defined(__arm__)
 	for (k = 0; k < ST->NumberOfTableEntries; k++) {



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