EFI fixes
Linus,
Please pull the latest efi-urgent-for-linus git tree from:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git efi-urgent-for-linus
# HEAD: 0100a3e67a9cef64d72cd3a1da86f3ddbee50363 efi/x86: Prune invalid memory map entries and fix boot regression
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.
Thanks,
Ingo
------------------>
Ard Biesheuvel (1):
efi/libstub/arm*: Pass latest memory map to the kernel
Nicolai Stange (1):
x86/efi: Don't allocate memmap through memblock after mm_init()
Peter Jones (1):
efi/x86: Prune invalid memory map entries and fix boot regression
arch/x86/platform/efi/efi.c | 66 ++++++++++++++++++++++++++ arch/x86/platform/efi/quirks.c | 4 +- drivers/firmware/efi/fake_mem.c | 3 +- drivers/firmware/efi/libstub/efistub.h | 8 ---- drivers/firmware/efi/libstub/fdt.c | 87 ++++++++++++++++++++++------------ drivers/firmware/efi/memmap.c | 38 +++++++++++++++ include/linux/efi.h | 2 + 7 files changed, 165 insertions(+), 43 deletions(-)
arch/x86/platform/efi/efi.c +66 -0
@@ -210,6 +210,70 @@ int __init efi_memblock_x86_reserve_range(void)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 ) {efi_memory_desc_t *md;
@@ -472,6 +536,8 @@ void __init efi_init(void) } } +efi_clean_memmap (); +if (efi_enabled (EFI_DBG ))efi_print_memmap (); }
arch/x86/platform/efi/quirks.c +2 -2
@@ -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_phys =memblock_alloc (new_size,0 ); + new_phys =efi_memmap_alloc (num_entries);if (!new_phys) {pr_err ("Could not allocate boot services memmap\n" );return ;
@@ -355,7 +355,7 @@ void __init efi_free_boot_services(void) } 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) {pr_err ("Failed to allocate new EFI memmap\n" );return ;
drivers/firmware/efi/fake_mem.c +1 -2
@@ -71,8 +71,7 @@ void __init efi_fake_memmap(void) }/* allocate memory for new EFI memmap */ - new_memmap_phy =memblock_alloc (efi.memmap.desc_size * new_nr_map, -PAGE_SIZE ); + new_memmap_phy =efi_memmap_alloc (new_nr_map);if (!new_memmap_phy)return ;
drivers/firmware/efi/libstub/efistub.h +0 -8
@@ -39,14 +39,6 @@ efi_status_t efi_file_close(void *handle);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, void *handle, unsigned long *new_fdt_addr,
drivers/firmware/efi/libstub/fdt.c +56 -31
155 lines not on this page · raw message
drivers/firmware/efi/memmap.c +38 -0
@@ -9,6 +9,44 @@#include <linux/efi.h> #include <linux/io.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
include/linux/efi.h +2 -0
@@ -103,6 +103,7 @@ typedef struct {#define EFI_PAGE_SHIFT 12#define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT) +#define EFI_PAGES_MAX (U64_MAX >> EFI_PAGE_SHIFT) typedef struct { u32 type;
@@ -950,6 +951,7 @@ static inline efi_status_t efi_query_variable_store(u32 attributes,#endif 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_late (phys_addr_t addr,unsigned long size);extern void __init efi_memmap_unmap (void );