efi: Fix efi_memmap_alloc() leaks
authorDan Williams <dan.j.williams@intel.com>
Mon, 13 Jan 2020 17:22:44 +0000 (18:22 +0100)
committerIngo Molnar <mingo@kernel.org>
Mon, 20 Jan 2020 07:14:29 +0000 (08:14 +0100)
With efi_fake_memmap() and efi_arch_mem_reserve() the efi table may be
updated and replaced multiple times. When that happens a previous
dynamically allocated efi memory map can be garbage collected. Use the
new EFI_MEMMAP_{SLAB,MEMBLOCK} flags to detect when a dynamically
allocated memory map is being replaced.

Debug statements in efi_memmap_free() reveal:

  efi: __efi_memmap_free:37: phys: 0x23ffdd580 size: 2688 flags: 0x2
  efi: __efi_memmap_free:37: phys: 0x9db00 size: 2640 flags: 0x2
  efi: __efi_memmap_free:37: phys: 0x9e580 size: 2640 flags: 0x2

...a savings of 7968 bytes on a qemu boot with 2 entries specified to
efi_fake_mem=.

[ ardb: added a comment to clarify that efi_memmap_free() does nothing when
        called from efi_clean_memmap(), i.e., with data->flags == 0x0 ]

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Link: https://lore.kernel.org/r/20200113172245.27925-13-ardb@kernel.org
drivers/firmware/efi/memmap.c

index 04dfa56..5016721 100644 (file)
@@ -29,6 +29,28 @@ static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size)
        return PFN_PHYS(page_to_pfn(p));
 }
 
+static void __init __efi_memmap_free(u64 phys, unsigned long size, unsigned long flags)
+{
+       if (flags & EFI_MEMMAP_MEMBLOCK) {
+               if (slab_is_available())
+                       memblock_free_late(phys, size);
+               else
+                       memblock_free(phys, size);
+       } else if (flags & EFI_MEMMAP_SLAB) {
+               struct page *p = pfn_to_page(PHYS_PFN(phys));
+               unsigned int order = get_order(size);
+
+               free_pages((unsigned long) page_address(p), order);
+       }
+}
+
+static void __init efi_memmap_free(void)
+{
+       __efi_memmap_free(efi.memmap.phys_map,
+                       efi.memmap.desc_size * efi.memmap.nr_map,
+                       efi.memmap.flags);
+}
+
 /**
  * efi_memmap_alloc - Allocate memory for the EFI memory map
  * @num_entries: Number of entries in the allocated map.
@@ -100,6 +122,9 @@ static int __init __efi_memmap_init(struct efi_memory_map_data *data)
                return -ENOMEM;
        }
 
+       /* NOP if data->flags & (EFI_MEMMAP_MEMBLOCK | EFI_MEMMAP_SLAB) == 0 */
+       efi_memmap_free();
+
        map.phys_map = data->phys_map;
        map.nr_map = data->size / data->desc_size;
        map.map_end = map.map + data->size;