Commit fc9f248d authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'efi-fixes-for-v7.0-2' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi

Pull EFI fix from Ard Biesheuvel:
 "Fix for the x86 EFI workaround keeping boot services code and data
  regions reserved until after SetVirtualAddressMap() completes:
  deferred struct page initialization may result in some of this memory
  being lost permanently"

* tag 'efi-fixes-for-v7.0-2' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi:
  x86/efi: defer freeing of boot services memory
parents 014441d1 a4b0bf6a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -138,7 +138,7 @@ extern void __init efi_apply_memmap_quirks(void);
extern int __init efi_reuse_config(u64 tables, int nr_tables);
extern void efi_delete_dummy_variable(void);
extern void efi_crash_gracefully_on_page_fault(unsigned long phys_addr);
extern void efi_free_boot_services(void);
extern void efi_unmap_boot_services(void);

void arch_efi_call_virt_setup(void);
void arch_efi_call_virt_teardown(void);
+1 −1
Original line number Diff line number Diff line
@@ -836,7 +836,7 @@ static void __init __efi_enter_virtual_mode(void)
	}

	efi_check_for_embedded_firmwares();
	efi_free_boot_services();
	efi_unmap_boot_services();

	if (!efi_is_mixed())
		efi_native_runtime_setup();
+52 −3
Original line number Diff line number Diff line
@@ -341,7 +341,7 @@ void __init efi_reserve_boot_services(void)

		/*
		 * Because the following memblock_reserve() is paired
		 * with memblock_free_late() for this region in
		 * with free_reserved_area() for this region in
		 * efi_free_boot_services(), we must be extremely
		 * careful not to reserve, and subsequently free,
		 * critical regions of memory (like the kernel image) or
@@ -404,17 +404,33 @@ static void __init efi_unmap_pages(efi_memory_desc_t *md)
		pr_err("Failed to unmap VA mapping for 0x%llx\n", va);
}

void __init efi_free_boot_services(void)
struct efi_freeable_range {
	u64 start;
	u64 end;
};

static struct efi_freeable_range *ranges_to_free;

void __init efi_unmap_boot_services(void)
{
	struct efi_memory_map_data data = { 0 };
	efi_memory_desc_t *md;
	int num_entries = 0;
	int idx = 0;
	size_t sz;
	void *new, *new_md;

	/* Keep all regions for /sys/kernel/debug/efi */
	if (efi_enabled(EFI_DBG))
		return;

	sz = sizeof(*ranges_to_free) * efi.memmap.nr_map + 1;
	ranges_to_free = kzalloc(sz, GFP_KERNEL);
	if (!ranges_to_free) {
		pr_err("Failed to allocate storage for freeable EFI regions\n");
		return;
	}

	for_each_efi_memory_desc(md) {
		unsigned long long start = md->phys_addr;
		unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
@@ -471,7 +487,15 @@ void __init efi_free_boot_services(void)
			start = SZ_1M;
		}

		memblock_free_late(start, size);
		/*
		 * With CONFIG_DEFERRED_STRUCT_PAGE_INIT parts of the memory
		 * map are still not initialized and we can't reliably free
		 * memory here.
		 * Queue the ranges to free at a later point.
		 */
		ranges_to_free[idx].start = start;
		ranges_to_free[idx].end = start + size;
		idx++;
	}

	if (!num_entries)
@@ -512,6 +536,31 @@ void __init efi_free_boot_services(void)
	}
}

static int __init efi_free_boot_services(void)
{
	struct efi_freeable_range *range = ranges_to_free;
	unsigned long freed = 0;

	if (!ranges_to_free)
		return 0;

	while (range->start) {
		void *start = phys_to_virt(range->start);
		void *end = phys_to_virt(range->end);

		free_reserved_area(start, end, -1, NULL);
		freed += (end - start);
		range++;
	}
	kfree(ranges_to_free);

	if (freed)
		pr_info("Freeing EFI boot services memory: %ldK\n", freed / SZ_1K);

	return 0;
}
arch_initcall(efi_free_boot_services);

/*
 * A number of config table entries get remapped to virtual addresses
 * after entering EFI virtual mode. However, the kexec kernel requires
+1 −1
Original line number Diff line number Diff line
@@ -85,7 +85,7 @@ static struct kobject *mokvar_kobj;
 * as an alternative to ordinary EFI variables, due to platform-dependent
 * limitations. The memory occupied by this table is marked as reserved.
 *
 * This routine must be called before efi_free_boot_services() in order
 * This routine must be called before efi_unmap_boot_services() in order
 * to guarantee that it can mark the table as reserved.
 *
 * Implicit inputs: