diff options
Diffstat (limited to 'drivers/firmware/efi/efi.c')
| -rw-r--r-- | drivers/firmware/efi/efi.c | 140 | 
1 files changed, 140 insertions, 0 deletions
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 5145fa344ad5..2e2fbdec0845 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -13,11 +13,27 @@   * This file is released under the GPLv2.   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/kobject.h>  #include <linux/module.h>  #include <linux/init.h>  #include <linux/device.h>  #include <linux/efi.h> +#include <linux/io.h> + +struct efi __read_mostly efi = { +	.mps        = EFI_INVALID_TABLE_ADDR, +	.acpi       = EFI_INVALID_TABLE_ADDR, +	.acpi20     = EFI_INVALID_TABLE_ADDR, +	.smbios     = EFI_INVALID_TABLE_ADDR, +	.sal_systab = EFI_INVALID_TABLE_ADDR, +	.boot_info  = EFI_INVALID_TABLE_ADDR, +	.hcdp       = EFI_INVALID_TABLE_ADDR, +	.uga        = EFI_INVALID_TABLE_ADDR, +	.uv_systab  = EFI_INVALID_TABLE_ADDR, +}; +EXPORT_SYMBOL(efi);  static struct kobject *efi_kobj;  static struct kobject *efivars_kobj; @@ -132,3 +148,127 @@ err_put:  }  subsys_initcall(efisubsys_init); + + +/* + * 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) +{ +	struct efi_memory_map *map; +	void *p; +	map = efi.memmap; +	if (!map) +		return NULL; +	if (WARN_ON(!map->map)) +		return NULL; +	for (p = map->map; p < map->map_end; p += map->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; +} + +static __initdata efi_config_table_type_t common_tables[] = { +	{ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, +	{ACPI_TABLE_GUID, "ACPI", &efi.acpi}, +	{HCDP_TABLE_GUID, "HCDP", &efi.hcdp}, +	{MPS_TABLE_GUID, "MPS", &efi.mps}, +	{SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab}, +	{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, +	{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, +	{NULL_GUID, NULL, 0}, +}; + +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 ", +					table_types[i].name, table); +				return 1; +			} +		} +	} + +	return 0; +} + +int __init efi_config_init(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; +	} + +	tablep = config_tables; +	pr_info(""); +	for (i = 0; i < efi.systab->nr_tables; i++) { +		efi_guid_t guid; +		unsigned long table; + +		if (efi_enabled(EFI_64BIT)) { +			u64 table64; +			guid = ((efi_config_table_64_t *)tablep)->guid; +			table64 = ((efi_config_table_64_t *)tablep)->table; +			table = table64; +#ifndef CONFIG_64BIT +			if (table64 >> 32) { +				pr_cont("\n"); +				pr_err("Table located above 4GB, disabling EFI.\n"); +				early_iounmap(config_tables, +					       efi.systab->nr_tables * sz); +				return -EINVAL; +			} +#endif +		} else { +			guid = ((efi_config_table_32_t *)tablep)->guid; +			table = ((efi_config_table_32_t *)tablep)->table; +		} + +		if (!match_config_table(&guid, table, common_tables)) +			match_config_table(&guid, table, arch_tables); + +		tablep += sz; +	} +	pr_cont("\n"); +	early_iounmap(config_tables, efi.systab->nr_tables * sz); +	return 0; +}  | 
