On 20 June 2018 at 05:03, Sai Praneeth Prakhya <sai.praneeth.prakhya@xxxxxxxxx> wrote: > From: Sai Praneeth <sai.praneeth.prakhya@xxxxxxxxx> > > efi_memmap_alloc(), as the name suggests, allocates memory for a new efi > memory map. It's referenced from couple of places, namely, > efi_arch_mem_reserve() and efi_free_boot_services(). These callers, > after allocating memory, remap it for further use. As usual, a routine > check is performed to confirm successful remap. If the remap fails, > ideally, the allocated memory should be freed but presently we just > return without freeing it up. Hence, fix this bug by introducing > efi_memmap_free() which frees memory allocated by efi_memmap_alloc(). > > As efi_memmap_alloc() allocates memory depending on whether mm_init() > has already been invoked or not, introduce a new argument called "late" > that lets us know which type of allocation was performed by > efi_memmap_alloc(). Later, this is used by efi_memmap_free() to invoke > the appropriate method to free the allocated memory. The other main > purpose "late" argument serves is to make sure that efi_memmap_alloc() > and efi_memmap_free() are always binded properly i.e. there could be a > scenario in which efi_memmap_alloc() is used before slab_is_available() > and efi_memmap_free() could be used after slab_is_available(). Without > "late", this could break because allocation would have been done using > memblock_alloc() while freeing will be done using __free_pages(). > > Since these API's could easily be misused make it explicit, so that the > caller has to pass "late" argument to efi_memmap_alloc() and later use > the same for efi_memmap_free(). > > Also, efi_fake_memmap() references efi_memmap_alloc() but it frees > memory correctly using memblock_free(), but replace it with > efi_memmap_free() to maintain consistency, as in, allocate memory with > efi_memmap_alloc() and free memory with efi_memmap_free(). > > It's a fact that memremap() and early_memremap() might never fail and > this code might never get a chance to run but to maintain good kernel > programming semantics, we might need this patch. > > Signed-off-by: Sai Praneeth Prakhya <sai.praneeth.prakhya@xxxxxxxxx> Thanks Sai. I have queued this for -next > Cc: Lee Chun-Yi <jlee@xxxxxxxx> > Cc: Borislav Petkov <bp@xxxxxxxxx> > Cc: Tony Luck <tony.luck@xxxxxxxxx> > Cc: Dave Hansen <dave.hansen@xxxxxxxxx> > Cc: Bhupesh Sharma <bhsharma@xxxxxxxxxx> > Cc: Ricardo Neri <ricardo.neri@xxxxxxxxx> > Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx> > Cc: Ravi Shankar <ravi.v.shankar@xxxxxxxxx> > Cc: Matt Fleming <matt@xxxxxxxxxxxxxxxxxxx> > Cc: Ard Biesheuvel <ard.biesheuvel@xxxxxxxxxx> > --- > > Changes from V2 to V3: > ---------------------- > 1. Add a new argument "late" to efi_memmap_alloc(), so that > efi_memmap_alloc() could communicate the type of allocation performed. > 2. Re-introduce efi_memmap_free() (from V1) but with an extra argument > "late", to know the type of allocation performed by efi_memmap_alloc(). > > Changes from V1 to V2: > ---------------------- > 1. Fix the bug of freeing memory map that was just installed by correctly > calling free_pages(). > 2. Call memblock_free() and __free_pages() directly from the appropriate > places instead of efi_memmap_free(). > > Note: Patch based on Linus's mainline tree V4.18-rc1 > > arch/x86/platform/efi/quirks.c | 16 ++++++++++++---- > drivers/firmware/efi/fake_mem.c | 5 +++-- > drivers/firmware/efi/memmap.c | 38 ++++++++++++++++++++++++++++++++++++-- > include/linux/efi.h | 3 ++- > 4 files changed, 53 insertions(+), 9 deletions(-) > > diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c > index 36c1f8b9f7e0..ef5698a3af7a 100644 > --- a/arch/x86/platform/efi/quirks.c > +++ b/arch/x86/platform/efi/quirks.c > @@ -248,6 +248,7 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size) > efi_memory_desc_t md; > int num_entries; > void *new; > + bool late; > > if (efi_mem_desc_lookup(addr, &md)) { > pr_err("Failed to lookup EFI memory descriptor for %pa\n", &addr); > @@ -276,7 +277,7 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size) > > new_size = efi.memmap.desc_size * num_entries; > > - new_phys = efi_memmap_alloc(num_entries); > + new_phys = efi_memmap_alloc(num_entries, &late); > if (!new_phys) { > pr_err("Could not allocate boot services memmap\n"); > return; > @@ -285,6 +286,7 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size) > new = early_memremap(new_phys, new_size); > if (!new) { > pr_err("Failed to map new boot services memmap\n"); > + efi_memmap_free(new_phys, num_entries, late); > return; > } > > @@ -375,6 +377,7 @@ void __init efi_free_boot_services(void) > efi_memory_desc_t *md; > int num_entries = 0; > void *new, *new_md; > + bool late; > > for_each_efi_memory_desc(md) { > unsigned long long start = md->phys_addr; > @@ -420,7 +423,7 @@ void __init efi_free_boot_services(void) > return; > > new_size = efi.memmap.desc_size * num_entries; > - new_phys = efi_memmap_alloc(num_entries); > + new_phys = efi_memmap_alloc(num_entries, &late); > if (!new_phys) { > pr_err("Failed to allocate new EFI memmap\n"); > return; > @@ -429,7 +432,7 @@ void __init efi_free_boot_services(void) > new = memremap(new_phys, new_size, MEMREMAP_WB); > if (!new) { > pr_err("Failed to map new EFI memmap\n"); > - return; > + goto free_mem; > } > > /* > @@ -452,8 +455,13 @@ void __init efi_free_boot_services(void) > > if (efi_memmap_install(new_phys, num_entries)) { > pr_err("Could not install new EFI memmap\n"); > - return; > + goto free_mem; > } > + > + return; > + > +free_mem: > + efi_memmap_free(new_phys, num_entries, late); > } > > /* > diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c > index 6c7d60c239b5..52cb2b7fc2f7 100644 > --- a/drivers/firmware/efi/fake_mem.c > +++ b/drivers/firmware/efi/fake_mem.c > @@ -57,6 +57,7 @@ void __init efi_fake_memmap(void) > phys_addr_t new_memmap_phy; > void *new_memmap; > int i; > + bool late; > > if (!nr_fake_mem) > return; > @@ -71,7 +72,7 @@ void __init efi_fake_memmap(void) > } > > /* allocate memory for new EFI memmap */ > - new_memmap_phy = efi_memmap_alloc(new_nr_map); > + new_memmap_phy = efi_memmap_alloc(new_nr_map, &late); > if (!new_memmap_phy) > return; > > @@ -79,7 +80,7 @@ void __init efi_fake_memmap(void) > new_memmap = early_memremap(new_memmap_phy, > efi.memmap.desc_size * new_nr_map); > if (!new_memmap) { > - memblock_free(new_memmap_phy, efi.memmap.desc_size * new_nr_map); > + efi_memmap_free(new_memmap_phy, new_nr_map, late); > return; > } > > diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c > index 5fc70520e04c..678e85704054 100644 > --- a/drivers/firmware/efi/memmap.c > +++ b/drivers/firmware/efi/memmap.c > @@ -32,6 +32,7 @@ static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size) > /** > * efi_memmap_alloc - Allocate memory for the EFI memory map > * @num_entries: Number of entries in the allocated map. > + * @late: Type of allocation performed (late or early?). > * > * Depending on whether mm_init() has already been invoked or not, > * either memblock or "normal" page allocation is used. > @@ -39,17 +40,50 @@ static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size) > * 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) > +phys_addr_t __init efi_memmap_alloc(unsigned int num_entries, bool *late) > { > unsigned long size = num_entries * efi.memmap.desc_size; > > - if (slab_is_available()) > + if (!late) > + return 0; > + > + *late = slab_is_available(); > + > + if (*late) > return __efi_memmap_alloc_late(size); > > return __efi_memmap_alloc_early(size); > } > > /** > + * efi_memmap_free - Free memory allocated by efi_memmap_alloc() > + * @mem: Physical address allocated by efi_memmap_alloc(). > + * @num_entries: Number of entries in the allocated map. > + * @late: What type of allocation did efi_memmap_alloc() perform? > + * > + * Use this function _only_ in conjunction with efi_memmap_alloc(). > + * efi_memmap_alloc() allocates memory depending on whether mm_init() > + * has already been invoked or not. It uses either memblock or "normal" > + * page allocation. Since the allocation is done in two different ways, > + * similarly, we free it in two different ways. > + * > + */ > +void __init efi_memmap_free(phys_addr_t mem, unsigned int num_entries, bool late) > +{ > + unsigned long size = num_entries * efi.memmap.desc_size; > + unsigned int order = get_order(size); > + phys_addr_t end = mem + size - 1; > + > + if (late) { > + __free_pages(pfn_to_page(PHYS_PFN(mem)), order); > + return; > + } > + > + if (memblock_free(mem, size)) > + pr_err("Failed to free mem from %pa to %pa\n", &mem, &end); > +} > + > +/** > * __efi_memmap_init - Common code for mapping the EFI memory map > * @data: EFI memory map data > * @late: Use early or late mapping function? > diff --git a/include/linux/efi.h b/include/linux/efi.h > index 56add823f190..f7f98d9a76f2 100644 > --- a/include/linux/efi.h > +++ b/include/linux/efi.h > @@ -1007,7 +1007,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 phys_addr_t __init efi_memmap_alloc(unsigned int num_entries, bool *late); > 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); > @@ -1016,6 +1016,7 @@ extern int __init efi_memmap_split_count(efi_memory_desc_t *md, > struct range *range); > extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap, > void *buf, struct efi_mem_range *mem); > +void __init efi_memmap_free(phys_addr_t mem, unsigned int num_entries, bool late); > > extern int efi_config_init(efi_config_table_type_t *arch_tables); > #ifdef CONFIG_EFI_ESRT > -- > 2.7.4 > -- To unsubscribe from this list: send the line "unsubscribe linux-efi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html