summaryrefslogtreecommitdiff
path: root/drivers/firmware/efi/efi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firmware/efi/efi.c')
-rw-r--r--drivers/firmware/efi/efi.c76
1 files changed, 59 insertions, 17 deletions
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index a2b0cbc8741c..abeff7dc0b58 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -187,8 +187,27 @@ static const struct attribute_group efi_subsys_attr_group = {
static struct efivars generic_efivars;
static struct efivar_operations generic_ops;
+static bool generic_ops_supported(void)
+{
+ unsigned long name_size;
+ efi_status_t status;
+ efi_char16_t name;
+ efi_guid_t guid;
+
+ name_size = sizeof(name);
+
+ status = efi.get_next_variable(&name_size, &name, &guid);
+ if (status == EFI_UNSUPPORTED)
+ return false;
+
+ return true;
+}
+
static int generic_ops_register(void)
{
+ if (!generic_ops_supported())
+ return 0;
+
generic_ops.get_variable = efi.get_variable;
generic_ops.get_next_variable = efi.get_next_variable;
generic_ops.query_variable_store = efi_query_variable_store;
@@ -197,11 +216,14 @@ static int generic_ops_register(void)
generic_ops.set_variable = efi.set_variable;
generic_ops.set_variable_nonblocking = efi.set_variable_nonblocking;
}
- return efivars_register(&generic_efivars, &generic_ops, efi_kobj);
+ return efivars_register(&generic_efivars, &generic_ops);
}
static void generic_ops_unregister(void)
{
+ if (!generic_ops.get_variable)
+ return;
+
efivars_unregister(&generic_efivars);
}
@@ -481,7 +503,7 @@ void __init efi_find_mirror(void)
* and if so, populate the supplied memory descriptor with the appropriate
* data.
*/
-int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
+int __efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
{
efi_memory_desc_t *md;
@@ -499,6 +521,12 @@ int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
u64 size;
u64 end;
+ /* skip bogus entries (including empty ones) */
+ if ((md->phys_addr & (EFI_PAGE_SIZE - 1)) ||
+ (md->num_pages <= 0) ||
+ (md->num_pages > (U64_MAX - md->phys_addr) >> EFI_PAGE_SHIFT))
+ continue;
+
size = md->num_pages << EFI_PAGE_SHIFT;
end = md->phys_addr + size;
if (phys_addr >= md->phys_addr && phys_addr < end) {
@@ -509,6 +537,9 @@ int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
return -ENOENT;
}
+extern int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
+ __weak __alias(__efi_mem_desc_lookup);
+
/*
* Calculate the highest address of an efi memory descriptor.
*/
@@ -535,6 +566,10 @@ void __init __weak efi_arch_mem_reserve(phys_addr_t addr, u64 size) {}
*/
void __init efi_mem_reserve(phys_addr_t addr, u64 size)
{
+ /* efi_mem_reserve() does not work under Xen */
+ if (WARN_ON_ONCE(efi_enabled(EFI_PARAVIRT)))
+ return;
+
if (!memblock_is_region_reserved(addr, size))
memblock_reserve(addr, size);
@@ -583,13 +618,20 @@ static __init int match_config_table(const efi_guid_t *guid,
int i;
for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) {
- if (!efi_guidcmp(*guid, table_types[i].guid)) {
- *(table_types[i].ptr) = table;
+ if (efi_guidcmp(*guid, table_types[i].guid))
+ continue;
+
+ if (!efi_config_table_is_usable(guid, table)) {
if (table_types[i].name[0])
- pr_cont("%s=0x%lx ",
+ pr_cont("(%s=0x%lx unusable) ",
table_types[i].name, table);
return 1;
}
+
+ *(table_types[i].ptr) = table;
+ if (table_types[i].name[0])
+ pr_cont("%s=0x%lx ", table_types[i].name, table);
+ return 1;
}
return 0;
@@ -720,20 +762,13 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables,
return 0;
}
-int __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr,
- int min_major_version)
+int __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr)
{
if (systab_hdr->signature != EFI_SYSTEM_TABLE_SIGNATURE) {
pr_err("System table signature incorrect!\n");
return -EINVAL;
}
- if ((systab_hdr->revision >> 16) < min_major_version)
- pr_err("Warning: System table version %d.%02d, expected %d.00 or greater!\n",
- systab_hdr->revision >> 16,
- systab_hdr->revision & 0xffff,
- min_major_version);
-
return 0;
}
@@ -764,6 +799,7 @@ void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr,
char vendor[100] = "unknown";
const efi_char16_t *c16;
size_t i;
+ u16 rev;
c16 = map_fw_vendor(fw_vendor, sizeof(vendor) * sizeof(efi_char16_t));
if (c16) {
@@ -774,10 +810,14 @@ void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr,
unmap_fw_vendor(c16, sizeof(vendor) * sizeof(efi_char16_t));
}
- pr_info("EFI v%u.%.02u by %s\n",
- systab_hdr->revision >> 16,
- systab_hdr->revision & 0xffff,
- vendor);
+ rev = (u16)systab_hdr->revision;
+ pr_info("EFI v%u.%u", systab_hdr->revision >> 16, rev / 10);
+
+ rev %= 10;
+ if (rev)
+ pr_cont(".%u", rev);
+
+ pr_cont(" by %s\n", vendor);
if (IS_ENABLED(CONFIG_X86_64) &&
systab_hdr->revision > EFI_1_10_SYSTEM_TABLE_REVISION &&
@@ -1007,6 +1047,8 @@ int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size)
/* first try to find a slot in an existing linked list entry */
for (prsv = efi_memreserve_root->next; prsv; ) {
rsv = memremap(prsv, sizeof(*rsv), MEMREMAP_WB);
+ if (!rsv)
+ return -ENOMEM;
index = atomic_fetch_add_unless(&rsv->count, 1, rsv->size);
if (index < rsv->size) {
rsv->entry[index].base = addr;