diff options
Diffstat (limited to 'arch/x86/platform')
| -rw-r--r-- | arch/x86/platform/efi/Makefile | 1 | ||||
| -rw-r--r-- | arch/x86/platform/efi/efi-bgrt.c | 76 | ||||
| -rw-r--r-- | arch/x86/platform/efi/efi.c | 66 | 
3 files changed, 130 insertions, 13 deletions
diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile index 73b8be0f367..6db1cc4c753 100644 --- a/arch/x86/platform/efi/Makefile +++ b/arch/x86/platform/efi/Makefile @@ -1 +1,2 @@  obj-$(CONFIG_EFI) 		+= efi.o efi_$(BITS).o efi_stub_$(BITS).o +obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o diff --git a/arch/x86/platform/efi/efi-bgrt.c b/arch/x86/platform/efi/efi-bgrt.c new file mode 100644 index 00000000000..f6a0c1b8e51 --- /dev/null +++ b/arch/x86/platform/efi/efi-bgrt.c @@ -0,0 +1,76 @@ +/* + * Copyright 2012 Intel Corporation + * Author: Josh Triplett <josh@joshtriplett.org> + * + * Based on the bgrt driver: + * Copyright 2012 Red Hat, Inc <mjg@redhat.com> + * Author: Matthew Garrett + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/acpi.h> +#include <linux/efi.h> +#include <linux/efi-bgrt.h> + +struct acpi_table_bgrt *bgrt_tab; +void *bgrt_image; +size_t bgrt_image_size; + +struct bmp_header { +	u16 id; +	u32 size; +} __packed; + +void efi_bgrt_init(void) +{ +	acpi_status status; +	void __iomem *image; +	bool ioremapped = false; +	struct bmp_header bmp_header; + +	if (acpi_disabled) +		return; + +	status = acpi_get_table("BGRT", 0, +	                        (struct acpi_table_header **)&bgrt_tab); +	if (ACPI_FAILURE(status)) +		return; + +	if (bgrt_tab->version != 1) +		return; +	if (bgrt_tab->image_type != 0 || !bgrt_tab->image_address) +		return; + +	image = efi_lookup_mapped_addr(bgrt_tab->image_address); +	if (!image) { +		image = ioremap(bgrt_tab->image_address, sizeof(bmp_header)); +		ioremapped = true; +		if (!image) +			return; +	} + +	memcpy_fromio(&bmp_header, image, sizeof(bmp_header)); +	if (ioremapped) +		iounmap(image); +	bgrt_image_size = bmp_header.size; + +	bgrt_image = kmalloc(bgrt_image_size, GFP_KERNEL); +	if (!bgrt_image) +		return; + +	if (ioremapped) { +		image = ioremap(bgrt_tab->image_address, bmp_header.size); +		if (!image) { +			kfree(bgrt_image); +			bgrt_image = NULL; +			return; +		} +	} + +	memcpy_fromio(bgrt_image, image, bgrt_image_size); +	if (ioremapped) +		iounmap(image); +} diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 92660edaa1e..aded2a91162 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -31,6 +31,7 @@  #include <linux/kernel.h>  #include <linux/init.h>  #include <linux/efi.h> +#include <linux/efi-bgrt.h>  #include <linux/export.h>  #include <linux/bootmem.h>  #include <linux/memblock.h> @@ -419,10 +420,21 @@ void __init efi_reserve_boot_services(void)  	}  } -static void __init efi_free_boot_services(void) +static void __init efi_unmap_memmap(void) +{ +	if (memmap.map) { +		early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size); +		memmap.map = NULL; +	} +} + +void __init efi_free_boot_services(void)  {  	void *p; +	if (!efi_native) +		return; +  	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {  		efi_memory_desc_t *md = p;  		unsigned long long start = md->phys_addr; @@ -438,6 +450,8 @@ static void __init efi_free_boot_services(void)  		free_bootmem_late(start, size);  	} + +	efi_unmap_memmap();  }  static int __init efi_systab_init(void *phys) @@ -732,6 +746,11 @@ void __init efi_init(void)  #endif  } +void __init efi_late_init(void) +{ +	efi_bgrt_init(); +} +  void __init efi_set_executable(efi_memory_desc_t *md, bool executable)  {  	u64 addr, npages; @@ -764,6 +783,34 @@ static void __init runtime_code_page_mkexec(void)  }  /* + * We can't ioremap data in EFI boot services RAM, because we've already mapped + * it as RAM.  So, look it up in the existing EFI memory map instead.  Only + * callable after efi_enter_virtual_mode and before efi_free_boot_services. + */ +void __iomem *efi_lookup_mapped_addr(u64 phys_addr) +{ +	void *p; +	if (WARN_ON(!memmap.map)) +		return NULL; +	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { +		efi_memory_desc_t *md = p; +		u64 size = md->num_pages << EFI_PAGE_SHIFT; +		u64 end = md->phys_addr + size; +		if (!(md->attribute & EFI_MEMORY_RUNTIME) && +		    md->type != EFI_BOOT_SERVICES_CODE && +		    md->type != EFI_BOOT_SERVICES_DATA) +			continue; +		if (!md->virt_addr) +			continue; +		if (phys_addr >= md->phys_addr && phys_addr < end) { +			phys_addr += md->virt_addr - md->phys_addr; +			return (__force void __iomem *)(unsigned long)phys_addr; +		} +	} +	return NULL; +} + +/*   * This function will switch the EFI runtime services to virtual mode.   * Essentially, look through the EFI memmap and map every region that   * has the runtime attribute bit set in its memory descriptor and update @@ -787,8 +834,10 @@ void __init efi_enter_virtual_mode(void)  	 * non-native EFI  	 */ -	if (!efi_native) -		goto out; +	if (!efi_native) { +		efi_unmap_memmap(); +		return; +	}  	/* Merge contiguous regions of the same type and attribute */  	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { @@ -878,18 +927,12 @@ void __init efi_enter_virtual_mode(void)  	}  	/* -	 * Thankfully, it does seem that no runtime services other than -	 * SetVirtualAddressMap() will touch boot services code, so we can -	 * get rid of it all at this point -	 */ -	efi_free_boot_services(); - -	/*  	 * Now that EFI is in virtual mode, update the function  	 * pointers in the runtime service table to the new virtual addresses.  	 *  	 * Call EFI services through wrapper functions.  	 */ +	efi.runtime_version = efi_systab.fw_revision;  	efi.get_time = virt_efi_get_time;  	efi.set_time = virt_efi_set_time;  	efi.get_wakeup_time = virt_efi_get_wakeup_time; @@ -906,9 +949,6 @@ void __init efi_enter_virtual_mode(void)  	if (__supported_pte_mask & _PAGE_NX)  		runtime_code_page_mkexec(); -out: -	early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size); -	memmap.map = NULL;  	kfree(new_memmap);  }  |