Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 4 Apr 2018 13:58:18 +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: r332028 - in stable/11: stand/efi/loader sys/amd64/amd64 sys/dev/efidev
Message-ID:  <201804041358.w34DwI6j013403@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kevans
Date: Wed Apr  4 13:58:18 2018
New Revision: 332028
URL: https://svnweb.freebsd.org/changeset/base/332028

Log:
  MFC r330868, r331241, r331361, r331365: EFIRT Fixes
  
  r330868:
  EFIRT: SetVirtualAddressMap with 1:1 mapping after exiting boot services
  
  This fixes a problem encountered on the Lenovo Thinkpad X220/Yoga 11e where
  runtime services would try to inexplicably jump to other parts of memory
  where it shouldn't be when attempting to enumerate EFI vars, causing a
  panic.
  
  The virtual mapping is enabled by default and can be disabled by setting
  efi_disable_vmap in loader.conf(5).
  
  r331241:
  Check if the gettime runtime service is valid.
  
  The U-Boot efi runtime service expects us to set the address map before
  calling any runtime services. It will then remap a few functions to their
  runtime version. One of these is the gettime function. If we call into
  this without having set a runtime map we get a page fault.
  
  Add a check to see if this is valid in efi_init() so we don't try to use
  the possibly invalid pointer.
  
  r331361:
  Enter into the EFI environment before dereferencing the runtime services
  pointer. This may be within the EFI address space and not the FreeBSD
  kernel address space.
  
  r331365:
  Re-work efidev ordering to fix efirt preloaded by loader on amd64
  
  On amd64, efi_enter calls fpu_kern_enter(). This may not be called until
  fpuinitstate has been invoked, resulting in a kernel panic with
  efirt_load="YES" in loader.conf(5).
  
  Move fpuinitstate a little earlier in SI_SUB_DRIVERS so that we can squeeze
  efirt between it and efirtc at SI_SUB_DRIVERS, SI_ORDER_ANY. efidev must be
  after efirt and doesn't really need to be at SI_SUB_DEVFS, so drop it at
  SI_SUB_DRIVER, SI_ORDER_ANY.
  
  The not immediately obvious dependency of fpuinitstate by efirt has been
  noted in both places.

Modified:
  stable/11/stand/efi/loader/bootinfo.c
  stable/11/sys/amd64/amd64/efirt_machdep.c
  stable/11/sys/amd64/amd64/fpu.c
  stable/11/sys/dev/efidev/efidev.c
  stable/11/sys/dev/efidev/efirt.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/stand/efi/loader/bootinfo.c
==============================================================================
--- stable/11/stand/efi/loader/bootinfo.c	Wed Apr  4 13:54:51 2018	(r332027)
+++ stable/11/stand/efi/loader/bootinfo.c	Wed Apr  4 13:58:18 2018	(r332028)
@@ -236,17 +236,48 @@ bi_copymodules(vm_offset_t addr)
 	return(addr);
 }
 
+static EFI_STATUS
+efi_do_vmap(EFI_MEMORY_DESCRIPTOR *mm, UINTN sz, UINTN mmsz, UINT32 mmver)
+{
+	EFI_MEMORY_DESCRIPTOR *desc, *viter, *vmap;
+	EFI_STATUS ret;
+	int curr, ndesc, nset;
+
+	nset = 0;
+	desc = mm;
+	ndesc = sz / mmsz;
+	vmap = malloc(sz);
+	if (vmap == NULL)
+		/* This isn't really an EFI error case, but pretend it is */
+		return (EFI_OUT_OF_RESOURCES);
+	viter = vmap;
+	for (curr = 0; curr < ndesc;
+	    curr++, desc = NextMemoryDescriptor(desc, mmsz)) {
+		if ((desc->Attribute & EFI_MEMORY_RUNTIME) != 0) {
+			++nset;
+			desc->VirtualStart = desc->PhysicalStart;
+			*viter = *desc;
+			viter = NextMemoryDescriptor(viter, mmsz);
+		}
+	}
+	ret = RS->SetVirtualAddressMap(nset * mmsz, mmsz, mmver, vmap);
+	free(vmap);
+	return (ret);
+}
+
 static int
 bi_load_efi_data(struct preloaded_file *kfp)
 {
 	EFI_MEMORY_DESCRIPTOR *mm;
 	EFI_PHYSICAL_ADDRESS addr;
 	EFI_STATUS status;
+	const char *efi_novmap;
 	size_t efisz;
 	UINTN efi_mapkey;
 	UINTN mmsz, pages, retry, sz;
 	UINT32 mmver;
 	struct efi_map_header *efihdr;
+	bool do_vmap;
 
 #if defined(__amd64__) || defined(__aarch64__)
 	struct efi_fb efifb;
@@ -266,6 +297,11 @@ bi_load_efi_data(struct preloaded_file *kfp)
 	}
 #endif
 
+	do_vmap = true;
+	efi_novmap = getenv("efi_disable_vmap");
+	if (efi_novmap != NULL)
+		do_vmap = strcasecmp(efi_novmap, "YES") != 0;
+
 	efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
 
 	/*
@@ -321,6 +357,13 @@ bi_load_efi_data(struct preloaded_file *kfp)
 		}
 		status = BS->ExitBootServices(IH, efi_mapkey);
 		if (EFI_ERROR(status) == 0) {
+			/*
+			 * This may be disabled by setting efi_disable_vmap in
+			 * loader.conf(5). By default we will setup the virtual
+			 * map entries.
+			 */
+			if (do_vmap)
+				efi_do_vmap(mm, sz, mmsz, mmver);
 			efihdr->memory_size = sz;
 			efihdr->descriptor_size = mmsz;
 			efihdr->descriptor_version = mmver;

Modified: stable/11/sys/amd64/amd64/efirt_machdep.c
==============================================================================
--- stable/11/sys/amd64/amd64/efirt_machdep.c	Wed Apr  4 13:54:51 2018	(r332027)
+++ stable/11/sys/amd64/amd64/efirt_machdep.c	Wed Apr  4 13:58:18 2018	(r332028)
@@ -166,7 +166,7 @@ efi_create_1t1_map(struct efi_md *map, int ndesc, int 
 	    descsz)) {
 		if ((p->md_attr & EFI_MD_ATTR_RT) == 0)
 			continue;
-		if (p->md_virt != NULL) {
+		if (p->md_virt != NULL && (uint64_t)p->md_virt != p->md_phys) {
 			if (bootverbose)
 				printf("EFI Runtime entry %d is mapped\n", i);
 			goto fail;

Modified: stable/11/sys/amd64/amd64/fpu.c
==============================================================================
--- stable/11/sys/amd64/amd64/fpu.c	Wed Apr  4 13:54:51 2018	(r332027)
+++ stable/11/sys/amd64/amd64/fpu.c	Wed Apr  4 13:58:18 2018	(r332028)
@@ -357,7 +357,8 @@ fpuinitstate(void *arg __unused)
 	start_emulating();
 	intr_restore(saveintr);
 }
-SYSINIT(fpuinitstate, SI_SUB_DRIVERS, SI_ORDER_ANY, fpuinitstate, NULL);
+/* EFIRT needs this to be initialized before we can enter our EFI environment */
+SYSINIT(fpuinitstate, SI_SUB_DRIVERS, SI_ORDER_FIRST, fpuinitstate, NULL);
 
 /*
  * Free coprocessor (if we have it).

Modified: stable/11/sys/dev/efidev/efidev.c
==============================================================================
--- stable/11/sys/dev/efidev/efidev.c	Wed Apr  4 13:54:51 2018	(r332027)
+++ stable/11/sys/dev/efidev/efidev.c	Wed Apr  4 13:58:18 2018	(r332028)
@@ -216,6 +216,6 @@ static moduledata_t efidev_moddata = {
 	.priv = NULL,
 };
 
-DECLARE_MODULE(efidev, efidev_moddata, SI_SUB_DEVFS, SI_ORDER_ANY);
+DECLARE_MODULE(efidev, efidev_moddata, SI_SUB_DRIVERS, SI_ORDER_ANY);
 MODULE_VERSION(efidev, 1);
 MODULE_DEPEND(efidev, efirt, 1, 1, 1);

Modified: stable/11/sys/dev/efidev/efirt.c
==============================================================================
--- stable/11/sys/dev/efidev/efirt.c	Wed Apr  4 13:54:51 2018	(r332027)
+++ stable/11/sys/dev/efidev/efirt.c	Wed Apr  4 13:58:18 2018	(r332028)
@@ -88,6 +88,9 @@ static int efi_status2err[25] = {
 	EPROTO		/* EFI_PROTOCOL_ERROR */
 };
 
+static int efi_enter(void);
+static void efi_leave(void);
+
 static int
 efi_status_to_errno(efi_status status)
 {
@@ -99,6 +102,25 @@ efi_status_to_errno(efi_status status)
 
 static struct mtx efi_lock;
 
+static bool
+efi_is_in_map(struct efi_md *map, int ndesc, int descsz, vm_offset_t addr)
+{
+	struct efi_md *p;
+	int i;
+
+	for (i = 0, p = map; i < ndesc; i++, p = efi_next_descriptor(p,
+	    descsz)) {
+		if ((p->md_attr & EFI_MD_ATTR_RT) == 0)
+			continue;
+
+		if (addr >= (uintptr_t)p->md_virt &&
+		    addr < (uintptr_t)p->md_virt + p->md_pages * PAGE_SIZE)
+			return (true);
+	}
+
+	return (false);
+}
+
 static int
 efi_init(void)
 {
@@ -159,6 +181,30 @@ efi_init(void)
 		return (ENXIO);
 	}
 
+	/*
+	 * Some UEFI implementations have multiple implementations of the
+	 * RS->GetTime function. They switch from one we can only use early
+	 * in the boot process to one valid as a RunTime service only when we
+	 * call RS->SetVirtualAddressMap. As this is not always the case, e.g.
+	 * with an old loader.efi, check if the RS->GetTime function is within
+	 * the EFI map, and fail to attach if not.
+	 *
+	 * We need to enter into the EFI environment as efi_runtime may point
+	 * to an EFI address.
+	 */
+	efi_enter();
+	if (!efi_is_in_map(map, efihdr->memory_size / efihdr->descriptor_size,
+	    efihdr->descriptor_size, (vm_offset_t)efi_runtime->rt_gettime)) {
+		efi_leave();
+		if (bootverbose)
+			printf(
+			 "EFI runtime services table has an invalid pointer\n");
+		efi_runtime = NULL;
+		efi_destroy_1t1_map();
+		return (ENXIO);
+	}
+	efi_leave();
+
 	return (0);
 }
 
@@ -405,5 +451,6 @@ static moduledata_t efirt_moddata = {
 	.evhand = efirt_modevents,
 	.priv = NULL,
 };
-DECLARE_MODULE(efirt, efirt_moddata, SI_SUB_VM_CONF, SI_ORDER_ANY);
+/* After fpuinitstate, before efidev */
+DECLARE_MODULE(efirt, efirt_moddata, SI_SUB_DRIVERS, SI_ORDER_SECOND);
 MODULE_VERSION(efirt, 1);



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