Commit 255e6140 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'efi-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull EFI fixes from Ingo Molnar:
 "A number of regression fixes:

   - Fix a boot hang on machines that have somewhat unusual memory map
     entries of phys_addr=0x0 num_pages=0, which broke due to a recent
     commit. This commit got cherry-picked from the v4.11 queue because
     the bug is affecting real machines.

   - Fix a boot hang also reported by KASAN, caused by incorrect init
     ordering introduced by a recent optimization.

   - Fix a recent robustification fix to allocate_new_fdt_and_exit_boot()
     that introduced an invalid assumption. Neither bugs were seen in
     the wild AFAIK"

* 'efi-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  efi/x86: Prune invalid memory map entries and fix boot regression
  x86/efi: Don't allocate memmap through memblock after mm_init()
  efi/libstub/arm*: Pass latest memory map to the kernel
parents f4d3935e 0100a3e6
...@@ -210,6 +210,70 @@ int __init efi_memblock_x86_reserve_range(void) ...@@ -210,6 +210,70 @@ int __init efi_memblock_x86_reserve_range(void)
return 0; return 0;
} }
#define OVERFLOW_ADDR_SHIFT (64 - EFI_PAGE_SHIFT)
#define OVERFLOW_ADDR_MASK (U64_MAX << OVERFLOW_ADDR_SHIFT)
#define U64_HIGH_BIT (~(U64_MAX >> 1))
static bool __init efi_memmap_entry_valid(const efi_memory_desc_t *md, int i)
{
u64 end = (md->num_pages << EFI_PAGE_SHIFT) + md->phys_addr - 1;
u64 end_hi = 0;
char buf[64];
if (md->num_pages == 0) {
end = 0;
} else if (md->num_pages > EFI_PAGES_MAX ||
EFI_PAGES_MAX - md->num_pages <
(md->phys_addr >> EFI_PAGE_SHIFT)) {
end_hi = (md->num_pages & OVERFLOW_ADDR_MASK)
>> OVERFLOW_ADDR_SHIFT;
if ((md->phys_addr & U64_HIGH_BIT) && !(end & U64_HIGH_BIT))
end_hi += 1;
} else {
return true;
}
pr_warn_once(FW_BUG "Invalid EFI memory map entries:\n");
if (end_hi) {
pr_warn("mem%02u: %s range=[0x%016llx-0x%llx%016llx] (invalid)\n",
i, efi_md_typeattr_format(buf, sizeof(buf), md),
md->phys_addr, end_hi, end);
} else {
pr_warn("mem%02u: %s range=[0x%016llx-0x%016llx] (invalid)\n",
i, efi_md_typeattr_format(buf, sizeof(buf), md),
md->phys_addr, end);
}
return false;
}
static void __init efi_clean_memmap(void)
{
efi_memory_desc_t *out = efi.memmap.map;
const efi_memory_desc_t *in = out;
const efi_memory_desc_t *end = efi.memmap.map_end;
int i, n_removal;
for (i = n_removal = 0; in < end; i++) {
if (efi_memmap_entry_valid(in, i)) {
if (out != in)
memcpy(out, in, efi.memmap.desc_size);
out = (void *)out + efi.memmap.desc_size;
} else {
n_removal++;
}
in = (void *)in + efi.memmap.desc_size;
}
if (n_removal > 0) {
u64 size = efi.memmap.nr_map - n_removal;
pr_warn("Removing %d invalid memory map entries.\n", n_removal);
efi_memmap_install(efi.memmap.phys_map, size);
}
}
void __init efi_print_memmap(void) void __init efi_print_memmap(void)
{ {
efi_memory_desc_t *md; efi_memory_desc_t *md;
...@@ -472,6 +536,8 @@ void __init efi_init(void) ...@@ -472,6 +536,8 @@ void __init efi_init(void)
} }
} }
efi_clean_memmap();
if (efi_enabled(EFI_DBG)) if (efi_enabled(EFI_DBG))
efi_print_memmap(); efi_print_memmap();
} }
......
...@@ -214,7 +214,7 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size) ...@@ -214,7 +214,7 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size)
new_size = efi.memmap.desc_size * num_entries; new_size = efi.memmap.desc_size * num_entries;
new_phys = memblock_alloc(new_size, 0); new_phys = efi_memmap_alloc(num_entries);
if (!new_phys) { if (!new_phys) {
pr_err("Could not allocate boot services memmap\n"); pr_err("Could not allocate boot services memmap\n");
return; return;
...@@ -355,7 +355,7 @@ void __init efi_free_boot_services(void) ...@@ -355,7 +355,7 @@ void __init efi_free_boot_services(void)
} }
new_size = efi.memmap.desc_size * num_entries; new_size = efi.memmap.desc_size * num_entries;
new_phys = memblock_alloc(new_size, 0); new_phys = efi_memmap_alloc(num_entries);
if (!new_phys) { if (!new_phys) {
pr_err("Failed to allocate new EFI memmap\n"); pr_err("Failed to allocate new EFI memmap\n");
return; return;
......
...@@ -71,8 +71,7 @@ void __init efi_fake_memmap(void) ...@@ -71,8 +71,7 @@ void __init efi_fake_memmap(void)
} }
/* allocate memory for new EFI memmap */ /* allocate memory for new EFI memmap */
new_memmap_phy = memblock_alloc(efi.memmap.desc_size * new_nr_map, new_memmap_phy = efi_memmap_alloc(new_nr_map);
PAGE_SIZE);
if (!new_memmap_phy) if (!new_memmap_phy)
return; return;
......
...@@ -39,14 +39,6 @@ efi_status_t efi_file_close(void *handle); ...@@ -39,14 +39,6 @@ efi_status_t efi_file_close(void *handle);
unsigned long 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 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,
u64 initrd_addr, u64 initrd_size,
efi_memory_desc_t *memory_map,
unsigned long map_size, unsigned long desc_size,
u32 desc_ver);
efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
void *handle, void *handle,
unsigned long *new_fdt_addr, unsigned long *new_fdt_addr,
......
...@@ -16,13 +16,10 @@ ...@@ -16,13 +16,10 @@
#include "efistub.h" #include "efistub.h"
efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
unsigned long orig_fdt_size, unsigned long orig_fdt_size,
void *fdt, int new_fdt_size, char *cmdline_ptr, void *fdt, int new_fdt_size, char *cmdline_ptr,
u64 initrd_addr, u64 initrd_size, u64 initrd_addr, u64 initrd_size)
efi_memory_desc_t *memory_map,
unsigned long map_size, unsigned long desc_size,
u32 desc_ver)
{ {
int node, num_rsv; int node, num_rsv;
int status; int status;
...@@ -101,25 +98,23 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, ...@@ -101,25 +98,23 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
if (status) if (status)
goto fdt_set_fail; goto fdt_set_fail;
fdt_val64 = cpu_to_fdt64((u64)(unsigned long)memory_map); fdt_val64 = U64_MAX; /* placeholder */
status = fdt_setprop(fdt, node, "linux,uefi-mmap-start", status = fdt_setprop(fdt, node, "linux,uefi-mmap-start",
&fdt_val64, sizeof(fdt_val64)); &fdt_val64, sizeof(fdt_val64));
if (status) if (status)
goto fdt_set_fail; goto fdt_set_fail;
fdt_val32 = cpu_to_fdt32(map_size); fdt_val32 = U32_MAX; /* placeholder */
status = fdt_setprop(fdt, node, "linux,uefi-mmap-size", status = fdt_setprop(fdt, node, "linux,uefi-mmap-size",
&fdt_val32, sizeof(fdt_val32)); &fdt_val32, sizeof(fdt_val32));
if (status) if (status)
goto fdt_set_fail; goto fdt_set_fail;
fdt_val32 = cpu_to_fdt32(desc_size);
status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size", status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size",
&fdt_val32, sizeof(fdt_val32)); &fdt_val32, sizeof(fdt_val32));
if (status) if (status)
goto fdt_set_fail; goto fdt_set_fail;
fdt_val32 = cpu_to_fdt32(desc_ver);
status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver", status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver",
&fdt_val32, sizeof(fdt_val32)); &fdt_val32, sizeof(fdt_val32));
if (status) if (status)
...@@ -148,6 +143,43 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, ...@@ -148,6 +143,43 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
return EFI_LOAD_ERROR; return EFI_LOAD_ERROR;
} }
static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map)
{
int node = fdt_path_offset(fdt, "/chosen");
u64 fdt_val64;
u32 fdt_val32;
int err;
if (node < 0)
return EFI_LOAD_ERROR;
fdt_val64 = cpu_to_fdt64((unsigned long)*map->map);
err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-start",
&fdt_val64, sizeof(fdt_val64));
if (err)
return EFI_LOAD_ERROR;
fdt_val32 = cpu_to_fdt32(*map->map_size);
err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-size",
&fdt_val32, sizeof(fdt_val32));
if (err)
return EFI_LOAD_ERROR;
fdt_val32 = cpu_to_fdt32(*map->desc_size);
err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-size",
&fdt_val32, sizeof(fdt_val32));
if (err)
return EFI_LOAD_ERROR;
fdt_val32 = cpu_to_fdt32(*map->desc_ver);
err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-ver",
&fdt_val32, sizeof(fdt_val32));
if (err)
return EFI_LOAD_ERROR;
return EFI_SUCCESS;
}
#ifndef EFI_FDT_ALIGN #ifndef EFI_FDT_ALIGN
#define EFI_FDT_ALIGN EFI_PAGE_SIZE #define EFI_FDT_ALIGN EFI_PAGE_SIZE
#endif #endif
...@@ -243,20 +275,10 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, ...@@ -243,20 +275,10 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
goto fail; goto fail;
} }
/*
* Now that we have done our final memory allocation (and free)
* we can get the memory map key needed for
* exit_boot_services().
*/
status = efi_get_memory_map(sys_table, &map);
if (status != EFI_SUCCESS)
goto fail_free_new_fdt;
status = update_fdt(sys_table, status = update_fdt(sys_table,
(void *)fdt_addr, fdt_size, (void *)fdt_addr, fdt_size,
(void *)*new_fdt_addr, new_fdt_size, (void *)*new_fdt_addr, new_fdt_size,
cmdline_ptr, initrd_addr, initrd_size, cmdline_ptr, initrd_addr, initrd_size);
memory_map, map_size, desc_size, desc_ver);
/* Succeeding the first time is the expected case. */ /* Succeeding the first time is the expected case. */
if (status == EFI_SUCCESS) if (status == EFI_SUCCESS)
...@@ -266,20 +288,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, ...@@ -266,20 +288,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
/* /*
* We need to allocate more space for the new * We need to allocate more space for the new
* device tree, so free existing buffer that is * device tree, so free existing buffer that is
* too small. Also free memory map, as we will need * too small.
* to get new one that reflects the free/alloc we do
* on the device tree buffer.
*/ */
efi_free(sys_table, new_fdt_size, *new_fdt_addr); efi_free(sys_table, new_fdt_size, *new_fdt_addr);
sys_table->boottime->free_pool(memory_map);
new_fdt_size += EFI_PAGE_SIZE; new_fdt_size += EFI_PAGE_SIZE;
} else { } else {
pr_efi_err(sys_table, "Unable to construct new device tree.\n"); pr_efi_err(sys_table, "Unable to construct new device tree.\n");
goto fail_free_mmap; goto fail_free_new_fdt;
} }
} }
sys_table->boottime->free_pool(memory_map);
priv.runtime_map = runtime_map; priv.runtime_map = runtime_map;
priv.runtime_entry_count = &runtime_entry_count; priv.runtime_entry_count = &runtime_entry_count;
status = efi_exit_boot_services(sys_table, handle, &map, &priv, status = efi_exit_boot_services(sys_table, handle, &map, &priv,
...@@ -288,6 +306,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, ...@@ -288,6 +306,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
if (status == EFI_SUCCESS) { if (status == EFI_SUCCESS) {
efi_set_virtual_address_map_t *svam; efi_set_virtual_address_map_t *svam;
status = update_fdt_memmap((void *)*new_fdt_addr, &map);
if (status != EFI_SUCCESS) {
/*
* The kernel won't get far without the memory map, but
* may still be able to print something meaningful so
* return success here.
*/
return EFI_SUCCESS;
}
/* Install the new virtual address map */ /* Install the new virtual address map */
svam = sys_table->runtime->set_virtual_address_map; svam = sys_table->runtime->set_virtual_address_map;
status = svam(runtime_entry_count * desc_size, desc_size, status = svam(runtime_entry_count * desc_size, desc_size,
...@@ -319,9 +347,6 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, ...@@ -319,9 +347,6 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
pr_efi_err(sys_table, "Exit boot services failed.\n"); pr_efi_err(sys_table, "Exit boot services failed.\n");
fail_free_mmap:
sys_table->boottime->free_pool(memory_map);
fail_free_new_fdt: fail_free_new_fdt:
efi_free(sys_table, new_fdt_size, *new_fdt_addr); efi_free(sys_table, new_fdt_size, *new_fdt_addr);
......
...@@ -9,6 +9,44 @@ ...@@ -9,6 +9,44 @@
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/io.h> #include <linux/io.h>
#include <asm/early_ioremap.h> #include <asm/early_ioremap.h>
#include <linux/memblock.h>
#include <linux/slab.h>
static phys_addr_t __init __efi_memmap_alloc_early(unsigned long size)
{
return memblock_alloc(size, 0);
}
static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size)
{
unsigned int order = get_order(size);
struct page *p = alloc_pages(GFP_KERNEL, order);
if (!p)
return 0;
return PFN_PHYS(page_to_pfn(p));
}
/**
* efi_memmap_alloc - Allocate memory for the EFI memory map
* @num_entries: Number of entries in the allocated map.
*
* Depending on whether mm_init() has already been invoked or not,
* either memblock or "normal" page allocation is used.
*
* Returns the physical address of the allocated memory map on
* success, zero on failure.
*/
phys_addr_t __init efi_memmap_alloc(unsigned int num_entries)
{
unsigned long size = num_entries * efi.memmap.desc_size;
if (slab_is_available())
return __efi_memmap_alloc_late(size);
return __efi_memmap_alloc_early(size);
}
/** /**
* __efi_memmap_init - Common code for mapping the EFI memory map * __efi_memmap_init - Common code for mapping the EFI memory map
......
...@@ -103,6 +103,7 @@ typedef struct { ...@@ -103,6 +103,7 @@ typedef struct {
#define EFI_PAGE_SHIFT 12 #define EFI_PAGE_SHIFT 12
#define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT) #define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT)
#define EFI_PAGES_MAX (U64_MAX >> EFI_PAGE_SHIFT)
typedef struct { typedef struct {
u32 type; u32 type;
...@@ -950,6 +951,7 @@ static inline efi_status_t efi_query_variable_store(u32 attributes, ...@@ -950,6 +951,7 @@ static inline efi_status_t efi_query_variable_store(u32 attributes,
#endif #endif
extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr);
extern phys_addr_t __init efi_memmap_alloc(unsigned int num_entries);
extern int __init efi_memmap_init_early(struct efi_memory_map_data *data); extern int __init efi_memmap_init_early(struct efi_memory_map_data *data);
extern int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size); extern int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size);
extern void __init efi_memmap_unmap(void); extern void __init efi_memmap_unmap(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment