diff options
Diffstat (limited to 'arch/x86/platform/efi/efi.c')
| -rw-r--r-- | arch/x86/platform/efi/efi.c | 78 | 
1 files changed, 57 insertions, 21 deletions
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 0fe27d7c625..b30aa26a8df 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -145,17 +145,6 @@ static void virt_efi_reset_system(int reset_type,  		       data_size, data);  } -static efi_status_t virt_efi_set_virtual_address_map( -	unsigned long memory_map_size, -	unsigned long descriptor_size, -	u32 descriptor_version, -	efi_memory_desc_t *virtual_map) -{ -	return efi_call_virt4(set_virtual_address_map, -			      memory_map_size, descriptor_size, -			      descriptor_version, virtual_map); -} -  static efi_status_t __init phys_efi_set_virtual_address_map(  	unsigned long memory_map_size,  	unsigned long descriptor_size, @@ -468,11 +457,25 @@ void __init efi_init(void)  #endif  } +void __init efi_set_executable(efi_memory_desc_t *md, bool executable) +{ +	u64 addr, npages; + +	addr = md->virt_addr; +	npages = md->num_pages; + +	memrange_efi_to_native(&addr, &npages); + +	if (executable) +		set_memory_x(addr, npages); +	else +		set_memory_nx(addr, npages); +} +  static void __init runtime_code_page_mkexec(void)  {  	efi_memory_desc_t *md;  	void *p; -	u64 addr, npages;  	/* Make EFI runtime service code area executable */  	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { @@ -481,10 +484,7 @@ static void __init runtime_code_page_mkexec(void)  		if (md->type != EFI_RUNTIME_SERVICES_CODE)  			continue; -		addr = md->virt_addr; -		npages = md->num_pages; -		memrange_efi_to_native(&addr, &npages); -		set_memory_x(addr, npages); +		efi_set_executable(md, true);  	}  } @@ -498,13 +498,42 @@ static void __init runtime_code_page_mkexec(void)   */  void __init efi_enter_virtual_mode(void)  { -	efi_memory_desc_t *md; +	efi_memory_desc_t *md, *prev_md = NULL;  	efi_status_t status;  	unsigned long size;  	u64 end, systab, addr, npages, end_pfn; -	void *p, *va; +	void *p, *va, *new_memmap = NULL; +	int count = 0;  	efi.systab = NULL; + +	/* Merge contiguous regions of the same type and attribute */ +	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { +		u64 prev_size; +		md = p; + +		if (!prev_md) { +			prev_md = md; +			continue; +		} + +		if (prev_md->type != md->type || +		    prev_md->attribute != md->attribute) { +			prev_md = md; +			continue; +		} + +		prev_size = prev_md->num_pages << EFI_PAGE_SHIFT; + +		if (md->phys_addr == (prev_md->phys_addr + prev_size)) { +			prev_md->num_pages += md->num_pages; +			md->type = EFI_RESERVED_TYPE; +			md->attribute = 0; +			continue; +		} +		prev_md = md; +	} +  	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {  		md = p;  		if (!(md->attribute & EFI_MEMORY_RUNTIME)) @@ -541,15 +570,21 @@ void __init efi_enter_virtual_mode(void)  			systab += md->virt_addr - md->phys_addr;  			efi.systab = (efi_system_table_t *) (unsigned long) systab;  		} +		new_memmap = krealloc(new_memmap, +				      (count + 1) * memmap.desc_size, +				      GFP_KERNEL); +		memcpy(new_memmap + (count * memmap.desc_size), md, +		       memmap.desc_size); +		count++;  	}  	BUG_ON(!efi.systab);  	status = phys_efi_set_virtual_address_map( -		memmap.desc_size * memmap.nr_map, +		memmap.desc_size * count,  		memmap.desc_size,  		memmap.desc_version, -		memmap.phys_map); +		(efi_memory_desc_t *)__pa(new_memmap));  	if (status != EFI_SUCCESS) {  		printk(KERN_ALERT "Unable to switch EFI into virtual mode " @@ -572,11 +607,12 @@ void __init efi_enter_virtual_mode(void)  	efi.set_variable = virt_efi_set_variable;  	efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count;  	efi.reset_system = virt_efi_reset_system; -	efi.set_virtual_address_map = virt_efi_set_virtual_address_map; +	efi.set_virtual_address_map = NULL;  	if (__supported_pte_mask & _PAGE_NX)  		runtime_code_page_mkexec();  	early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size);  	memmap.map = NULL; +	kfree(new_memmap);  }  /*  |