diff options
Diffstat (limited to 'drivers/firmware')
| -rw-r--r-- | drivers/firmware/efi/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/firmware/efi/efi.c | 73 | ||||
| -rw-r--r-- | drivers/firmware/efi/efivars.c | 6 | ||||
| -rw-r--r-- | drivers/firmware/efi/libstub/Makefile | 15 | ||||
| -rw-r--r-- | drivers/firmware/efi/libstub/arm-stub.c | 67 | ||||
| -rw-r--r-- | drivers/firmware/efi/libstub/efi-stub-helper.c | 43 | ||||
| -rw-r--r-- | drivers/firmware/efi/libstub/efistub.h | 8 | ||||
| -rw-r--r-- | drivers/firmware/efi/libstub/fdt.c | 62 | ||||
| -rw-r--r-- | drivers/firmware/efi/runtime-map.c | 2 | 
9 files changed, 221 insertions, 59 deletions
| diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index f712d47f30d8..8de4da5c9ab6 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -12,11 +12,11 @@ config EFI_VARS  	  Note that using this driver in concert with efibootmgr requires  	  at least test release version 0.5.0-test3 or later, which is -	  available from Matt Domsch's website located at: +	  available from:  	  <http://linux.dell.com/efibootmgr/testing/efibootmgr-0.5.0-test3.tar.gz>  	  Subsequent efibootmgr releases may be found at: -	  <http://linux.dell.com/efibootmgr> +	  <http://github.com/vathpela/efibootmgr>  config EFI_VARS_PSTORE  	tristate "Register efivars backend for pstore" diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 9035c1b74d58..3061bb8629dc 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -115,15 +115,24 @@ EFI_ATTR_SHOW(fw_vendor);  EFI_ATTR_SHOW(runtime);  EFI_ATTR_SHOW(config_table); +static ssize_t fw_platform_size_show(struct kobject *kobj, +				     struct kobj_attribute *attr, char *buf) +{ +	return sprintf(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32); +} +  static struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO(fw_vendor);  static struct kobj_attribute efi_attr_runtime = __ATTR_RO(runtime);  static struct kobj_attribute efi_attr_config_table = __ATTR_RO(config_table); +static struct kobj_attribute efi_attr_fw_platform_size = +	__ATTR_RO(fw_platform_size);  static struct attribute *efi_subsys_attrs[] = {  	&efi_attr_systab.attr,  	&efi_attr_fw_vendor.attr,  	&efi_attr_runtime.attr,  	&efi_attr_config_table.attr, +	&efi_attr_fw_platform_size.attr,  	NULL,  }; @@ -272,15 +281,10 @@ static __init int match_config_table(efi_guid_t *guid,  				     unsigned long table,  				     efi_config_table_type_t *table_types)  { -	u8 str[EFI_VARIABLE_GUID_LEN + 1];  	int i;  	if (table_types) { -		efi_guid_unparse(guid, str); -  		for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { -			efi_guid_unparse(&table_types[i].guid, str); -  			if (!efi_guidcmp(*guid, table_types[i].guid)) {  				*(table_types[i].ptr) = table;  				pr_cont(" %s=0x%lx ", @@ -293,29 +297,15 @@ static __init int match_config_table(efi_guid_t *guid,  	return 0;  } -int __init efi_config_init(efi_config_table_type_t *arch_tables) +int __init efi_config_parse_tables(void *config_tables, int count, int sz, +				   efi_config_table_type_t *arch_tables)  { -	void *config_tables, *tablep; -	int i, sz; - -	if (efi_enabled(EFI_64BIT)) -		sz = sizeof(efi_config_table_64_t); -	else -		sz = sizeof(efi_config_table_32_t); - -	/* -	 * Let's see what config tables the firmware passed to us. -	 */ -	config_tables = early_memremap(efi.systab->tables, -				       efi.systab->nr_tables * sz); -	if (config_tables == NULL) { -		pr_err("Could not map Configuration table!\n"); -		return -ENOMEM; -	} +	void *tablep; +	int i;  	tablep = config_tables;  	pr_info(""); -	for (i = 0; i < efi.systab->nr_tables; i++) { +	for (i = 0; i < count; i++) {  		efi_guid_t guid;  		unsigned long table; @@ -328,8 +318,6 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables)  			if (table64 >> 32) {  				pr_cont("\n");  				pr_err("Table located above 4GB, disabling EFI.\n"); -				early_memunmap(config_tables, -					       efi.systab->nr_tables * sz);  				return -EINVAL;  			}  #endif @@ -344,13 +332,37 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables)  		tablep += sz;  	}  	pr_cont("\n"); -	early_memunmap(config_tables, efi.systab->nr_tables * sz); -  	set_bit(EFI_CONFIG_TABLES, &efi.flags); -  	return 0;  } +int __init efi_config_init(efi_config_table_type_t *arch_tables) +{ +	void *config_tables; +	int sz, ret; + +	if (efi_enabled(EFI_64BIT)) +		sz = sizeof(efi_config_table_64_t); +	else +		sz = sizeof(efi_config_table_32_t); + +	/* +	 * Let's see what config tables the firmware passed to us. +	 */ +	config_tables = early_memremap(efi.systab->tables, +				       efi.systab->nr_tables * sz); +	if (config_tables == NULL) { +		pr_err("Could not map Configuration table!\n"); +		return -ENOMEM; +	} + +	ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, sz, +				      arch_tables); + +	early_memunmap(config_tables, efi.systab->nr_tables * sz); +	return ret; +} +  #ifdef CONFIG_EFI_VARS_MODULE  static int __init efi_load_efivars(void)  { @@ -403,8 +415,7 @@ static int __init fdt_find_uefi_params(unsigned long node, const char *uname,  	u64 val;  	int i, len; -	if (depth != 1 || -	    (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) +	if (depth != 1 || strcmp(uname, "chosen") != 0)  		return 0;  	for (i = 0; i < ARRAY_SIZE(dt_params); i++) { diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index f256ecd8a176..7b2e0496e0c0 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -39,7 +39,7 @@   *   fix locking per Peter Chubb's findings   *   *  25 Mar 2002 - Matt Domsch <[email protected]> - *   move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_unparse() + *   move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_to_str()   *   *  12 Feb 2002 - Matt Domsch <[email protected]>   *   use list_for_each_safe when deleting vars. @@ -128,7 +128,7 @@ efivar_guid_read(struct efivar_entry *entry, char *buf)  	if (!entry || !buf)  		return 0; -	efi_guid_unparse(&var->VendorGuid, str); +	efi_guid_to_str(&var->VendorGuid, str);  	str += strlen(str);  	str += sprintf(str, "\n"); @@ -569,7 +569,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)  	   private variables from another's.         */  	*(short_name + strlen(short_name)) = '-'; -	efi_guid_unparse(&new_var->var.VendorGuid, +	efi_guid_to_str(&new_var->var.VendorGuid,  			 short_name + strlen(short_name));  	new_var->kobj.kset = efivars_kset; diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index b14bc2b9fb4d..280bc0a63365 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -19,8 +19,23 @@ KBUILD_CFLAGS			:= $(cflags-y) \  				   $(call cc-option,-fno-stack-protector)  GCOV_PROFILE			:= n +KASAN_SANITIZE			:= n  lib-y				:= efi-stub-helper.o  lib-$(CONFIG_EFI_ARMSTUB)	+= arm-stub.o fdt.o  CFLAGS_fdt.o			+= -I$(srctree)/scripts/dtc/libfdt/ + +# +# arm64 puts the stub in the kernel proper, which will unnecessarily retain all +# code indefinitely unless it is annotated as __init/__initdata/__initconst etc. +# So let's apply the __init annotations at the section level, by prefixing +# the section names directly. This will ensure that even all the inline string +# literals are covered. +# +extra-$(CONFIG_ARM64)		:= $(lib-y) +lib-$(CONFIG_ARM64)		:= $(patsubst %.o,%.init.o,$(lib-y)) + +OBJCOPYFLAGS := --prefix-alloc-sections=.init +$(obj)/%.init.o: $(obj)/%.o FORCE +	$(call if_changed,objcopy) diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index eb48a1a1a576..dcae482a9a17 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -17,10 +17,10 @@  #include "efistub.h" -static int __init efi_secureboot_enabled(efi_system_table_t *sys_table_arg) +static int efi_secureboot_enabled(efi_system_table_t *sys_table_arg)  { -	static efi_guid_t const var_guid __initconst = EFI_GLOBAL_VARIABLE_GUID; -	static efi_char16_t const var_name[] __initconst = { +	static efi_guid_t const var_guid = EFI_GLOBAL_VARIABLE_GUID; +	static efi_char16_t const var_name[] = {  		'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0 };  	efi_get_variable_t *f_getvar = sys_table_arg->runtime->get_variable; @@ -164,7 +164,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,   * for both archictectures, with the arch-specific code provided in the   * handle_kernel_image() function.   */ -unsigned long __init efi_entry(void *handle, efi_system_table_t *sys_table, +unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,  			       unsigned long *image_addr)  {  	efi_loaded_image_t *image; @@ -295,3 +295,62 @@ fail_free_image:  fail:  	return EFI_ERROR;  } + +/* + * This is the base address at which to start allocating virtual memory ranges + * for UEFI Runtime Services. This is in the low TTBR0 range so that we can use + * any allocation we choose, and eliminate the risk of a conflict after kexec. + * The value chosen is the largest non-zero power of 2 suitable for this purpose + * both on 32-bit and 64-bit ARM CPUs, to maximize the likelihood that it can + * be mapped efficiently. + */ +#define EFI_RT_VIRTUAL_BASE	0x40000000 + +/* + * efi_get_virtmap() - create a virtual mapping for the EFI memory map + * + * This function populates the virt_addr fields of all memory region descriptors + * in @memory_map whose EFI_MEMORY_RUNTIME attribute is set. Those descriptors + * are also copied to @runtime_map, and their total count is returned in @count. + */ +void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, +		     unsigned long desc_size, efi_memory_desc_t *runtime_map, +		     int *count) +{ +	u64 efi_virt_base = EFI_RT_VIRTUAL_BASE; +	efi_memory_desc_t *out = runtime_map; +	int l; + +	for (l = 0; l < map_size; l += desc_size) { +		efi_memory_desc_t *in = (void *)memory_map + l; +		u64 paddr, size; + +		if (!(in->attribute & EFI_MEMORY_RUNTIME)) +			continue; + +		/* +		 * Make the mapping compatible with 64k pages: this allows +		 * a 4k page size kernel to kexec a 64k page size kernel and +		 * vice versa. +		 */ +		paddr = round_down(in->phys_addr, SZ_64K); +		size = round_up(in->num_pages * EFI_PAGE_SIZE + +				in->phys_addr - paddr, SZ_64K); + +		/* +		 * Avoid wasting memory on PTEs by choosing a virtual base that +		 * is compatible with section mappings if this region has the +		 * appropriate size and physical alignment. (Sections are 2 MB +		 * on 4k granule kernels) +		 */ +		if (IS_ALIGNED(in->phys_addr, SZ_2M) && size >= SZ_2M) +			efi_virt_base = round_up(efi_virt_base, SZ_2M); + +		in->virt_addr = efi_virt_base + in->phys_addr - paddr; +		efi_virt_base += size; + +		memcpy(out, in, desc_size); +		out = (void *)out + desc_size; +		++*count; +	} +} diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index a920fec8fe88..af5d63c7cc53 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -32,6 +32,15 @@  static unsigned long __chunk_size = EFI_READ_CHUNK_SIZE; +/* + * Allow the platform to override the allocation granularity: this allows + * systems that have the capability to run with a larger page size to deal + * with the allocations for initrd and fdt more efficiently. + */ +#ifndef EFI_ALLOC_ALIGN +#define EFI_ALLOC_ALIGN		EFI_PAGE_SIZE +#endif +  struct file_info {  	efi_file_handle_t *handle;  	u64 size; @@ -66,25 +75,29 @@ efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg,  	unsigned long key;  	u32 desc_version; -	*map_size = sizeof(*m) * 32; -again: +	*map_size = 0; +	*desc_size = 0; +	key = 0; +	status = efi_call_early(get_memory_map, map_size, NULL, +				&key, desc_size, &desc_version); +	if (status != EFI_BUFFER_TOO_SMALL) +		return EFI_LOAD_ERROR; +  	/*  	 * Add an additional efi_memory_desc_t because we're doing an  	 * allocation which may be in a new descriptor region.  	 */ -	*map_size += sizeof(*m); +	*map_size += *desc_size;  	status = efi_call_early(allocate_pool, EFI_LOADER_DATA,  				*map_size, (void **)&m);  	if (status != EFI_SUCCESS)  		goto fail; -	*desc_size = 0; -	key = 0;  	status = efi_call_early(get_memory_map, map_size, m,  				&key, desc_size, &desc_version);  	if (status == EFI_BUFFER_TOO_SMALL) {  		efi_call_early(free_pool, m); -		goto again; +		return EFI_LOAD_ERROR;  	}  	if (status != EFI_SUCCESS) @@ -101,7 +114,7 @@ fail:  } -unsigned long __init get_dram_base(efi_system_table_t *sys_table_arg) +unsigned long get_dram_base(efi_system_table_t *sys_table_arg)  {  	efi_status_t status;  	unsigned long map_size; @@ -150,10 +163,10 @@ efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,  	 * a specific address.  We are doing page-based allocations,  	 * so we must be aligned to a page.  	 */ -	if (align < EFI_PAGE_SIZE) -		align = EFI_PAGE_SIZE; +	if (align < EFI_ALLOC_ALIGN) +		align = EFI_ALLOC_ALIGN; -	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; +	nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;  again:  	for (i = 0; i < map_size / desc_size; i++) {  		efi_memory_desc_t *desc; @@ -235,10 +248,10 @@ efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,  	 * a specific address.  We are doing page-based allocations,  	 * so we must be aligned to a page.  	 */ -	if (align < EFI_PAGE_SIZE) -		align = EFI_PAGE_SIZE; +	if (align < EFI_ALLOC_ALIGN) +		align = EFI_ALLOC_ALIGN; -	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; +	nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;  	for (i = 0; i < map_size / desc_size; i++) {  		efi_memory_desc_t *desc;  		unsigned long m = (unsigned long)map; @@ -292,7 +305,7 @@ void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,  	if (!size)  		return; -	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; +	nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;  	efi_call_early(free_pages, addr, nr_pages);  } @@ -561,7 +574,7 @@ efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg,  	 * to the preferred address.  If that fails, allocate as low  	 * as possible while respecting the required alignment.  	 */ -	nr_pages = round_up(alloc_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; +	nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;  	status = efi_call_early(allocate_pages,  				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,  				nr_pages, &efi_addr); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 304ab295ca1a..47437b16b186 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -5,6 +5,10 @@  /* error code which can't be mistaken for valid address */  #define EFI_ERROR	(~0UL) +#undef memcpy +#undef memset +#undef memmove +  void efi_char16_printk(efi_system_table_t *, efi_char16_t *);  efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg, void *__image, @@ -39,4 +43,8 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,  void *get_fdt(efi_system_table_t *sys_table); +void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, +		     unsigned long desc_size, efi_memory_desc_t *runtime_map, +		     int *count); +  #endif diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index c846a9608cbd..91da56c4fd54 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -14,6 +14,8 @@  #include <linux/libfdt.h>  #include <asm/efi.h> +#include "efistub.h" +  efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,  			unsigned long orig_fdt_size,  			void *fdt, int new_fdt_size, char *cmdline_ptr, @@ -193,9 +195,26 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,  	unsigned long map_size, desc_size;  	u32 desc_ver;  	unsigned long mmap_key; -	efi_memory_desc_t *memory_map; +	efi_memory_desc_t *memory_map, *runtime_map;  	unsigned long new_fdt_size;  	efi_status_t status; +	int runtime_entry_count = 0; + +	/* +	 * Get a copy of the current memory map that we will use to prepare +	 * the input for SetVirtualAddressMap(). We don't have to worry about +	 * subsequent allocations adding entries, since they could not affect +	 * the number of EFI_MEMORY_RUNTIME regions. +	 */ +	status = efi_get_memory_map(sys_table, &runtime_map, &map_size, +				    &desc_size, &desc_ver, &mmap_key); +	if (status != EFI_SUCCESS) { +		pr_efi_err(sys_table, "Unable to retrieve UEFI memory map.\n"); +		return status; +	} + +	pr_efi(sys_table, +	       "Exiting boot services and installing virtual address map...\n");  	/*  	 * Estimate size of new FDT, and allocate memory for it. We @@ -248,12 +267,48 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,  		}  	} +	/* +	 * Update the memory map with virtual addresses. The function will also +	 * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME +	 * entries so that we can pass it straight into SetVirtualAddressMap() +	 */ +	efi_get_virtmap(memory_map, map_size, desc_size, runtime_map, +			&runtime_entry_count); +  	/* Now we are ready to exit_boot_services.*/  	status = sys_table->boottime->exit_boot_services(handle, mmap_key); +	if (status == EFI_SUCCESS) { +		efi_set_virtual_address_map_t *svam; -	if (status == EFI_SUCCESS) -		return status; +		/* Install the new virtual address map */ +		svam = sys_table->runtime->set_virtual_address_map; +		status = svam(runtime_entry_count * desc_size, desc_size, +			      desc_ver, runtime_map); + +		/* +		 * We are beyond the point of no return here, so if the call to +		 * SetVirtualAddressMap() failed, we need to signal that to the +		 * incoming kernel but proceed normally otherwise. +		 */ +		if (status != EFI_SUCCESS) { +			int l; + +			/* +			 * Set the virtual address field of all +			 * EFI_MEMORY_RUNTIME entries to 0. This will signal +			 * the incoming kernel that no virtual translation has +			 * been installed. +			 */ +			for (l = 0; l < map_size; l += desc_size) { +				efi_memory_desc_t *p = (void *)memory_map + l; + +				if (p->attribute & EFI_MEMORY_RUNTIME) +					p->virt_addr = 0; +			} +		} +		return EFI_SUCCESS; +	}  	pr_efi_err(sys_table, "Exit boot services failed.\n"); @@ -264,6 +319,7 @@ fail_free_new_fdt:  	efi_free(sys_table, new_fdt_size, *new_fdt_addr);  fail: +	sys_table->boottime->free_pool(runtime_map);  	return EFI_LOAD_ERROR;  } diff --git a/drivers/firmware/efi/runtime-map.c b/drivers/firmware/efi/runtime-map.c index 018c29a26615..87b8e3b900d2 100644 --- a/drivers/firmware/efi/runtime-map.c +++ b/drivers/firmware/efi/runtime-map.c @@ -191,7 +191,7 @@ int __init efi_runtime_map_init(struct kobject *efi_kobj)  	return 0;  out_add_entry: -	for (j = i - 1; j > 0; j--) { +	for (j = i - 1; j >= 0; j--) {  		entry = *(map_entries + j);  		kobject_put(&entry->kobj);  	} |