]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/firmware/efi/memmap.c
efi: Fix handling of multiple efi_fake_mem= entries
[linux.git] / drivers / firmware / efi / memmap.c
index 38b686c67b177da4875b9174c0f50ca165c6b2d4..2ff1883dc788d2954761e894e6edfd267a576535 100644 (file)
@@ -29,9 +29,32 @@ static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size)
        return PFN_PHYS(page_to_pfn(p));
 }
 
+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.
+ * @data: efi memmap installation parameters
  *
  * Depending on whether mm_init() has already been invoked or not,
  * either memblock or "normal" page allocation is used.
@@ -39,34 +62,47 @@ 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)
+int __init efi_memmap_alloc(unsigned int num_entries,
+               struct efi_memory_map_data *data)
 {
-       unsigned long size = num_entries * efi.memmap.desc_size;
-
-       if (slab_is_available())
-               return __efi_memmap_alloc_late(size);
+       /* Expect allocation parameters are zero initialized */
+       WARN_ON(data->phys_map || data->size);
+
+       data->size = num_entries * efi.memmap.desc_size;
+       data->desc_version = efi.memmap.desc_version;
+       data->desc_size = efi.memmap.desc_size;
+       data->flags &= ~(EFI_MEMMAP_SLAB | EFI_MEMMAP_MEMBLOCK);
+       data->flags |= efi.memmap.flags & EFI_MEMMAP_LATE;
+
+       if (slab_is_available()) {
+               data->flags |= EFI_MEMMAP_SLAB;
+               data->phys_map = __efi_memmap_alloc_late(data->size);
+       } else {
+               data->flags |= EFI_MEMMAP_MEMBLOCK;
+               data->phys_map = __efi_memmap_alloc_early(data->size);
+       }
 
-       return __efi_memmap_alloc_early(size);
+       if (!data->phys_map)
+               return -ENOMEM;
+       return 0;
 }
 
 /**
  * __efi_memmap_init - Common code for mapping the EFI memory map
  * @data: EFI memory map data
- * @late: Use early or late mapping function?
  *
  * This function takes care of figuring out which function to use to
  * map the EFI memory map in efi.memmap based on how far into the boot
  * we are.
  *
- * During bootup @late should be %false since we only have access to
- * the early_memremap*() functions as the vmalloc space isn't setup.
- * Once the kernel is fully booted we can fallback to the more robust
- * memremap*() API.
+ * During bootup EFI_MEMMAP_LATE in data->flags should be clear since we
+ * only have access to the early_memremap*() functions as the vmalloc
+ * space isn't setup.  Once the kernel is fully booted we can fallback
+ * to the more robust memremap*() API.
  *
  * Returns zero on success, a negative error code on failure.
  */
-static int __init
-__efi_memmap_init(struct efi_memory_map_data *data, bool late)
+static int __init __efi_memmap_init(struct efi_memory_map_data *data)
 {
        struct efi_memory_map map;
        phys_addr_t phys_map;
@@ -76,7 +112,7 @@ __efi_memmap_init(struct efi_memory_map_data *data, bool late)
 
        phys_map = data->phys_map;
 
-       if (late)
+       if (data->flags & EFI_MEMMAP_LATE)
                map.map = memremap(phys_map, data->size, MEMREMAP_WB);
        else
                map.map = early_memremap(phys_map, data->size);
@@ -86,13 +122,16 @@ __efi_memmap_init(struct efi_memory_map_data *data, bool late)
                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;
 
        map.desc_version = data->desc_version;
        map.desc_size = data->desc_size;
-       map.late = late;
+       map.flags = data->flags;
 
        set_bit(EFI_MEMMAP, &efi.flags);
 
@@ -111,9 +150,10 @@ __efi_memmap_init(struct efi_memory_map_data *data, bool late)
 int __init efi_memmap_init_early(struct efi_memory_map_data *data)
 {
        /* Cannot go backwards */
-       WARN_ON(efi.memmap.late);
+       WARN_ON(efi.memmap.flags & EFI_MEMMAP_LATE);
 
-       return __efi_memmap_init(data, false);
+       data->flags = 0;
+       return __efi_memmap_init(data);
 }
 
 void __init efi_memmap_unmap(void)
@@ -121,7 +161,7 @@ void __init efi_memmap_unmap(void)
        if (!efi_enabled(EFI_MEMMAP))
                return;
 
-       if (!efi.memmap.late) {
+       if (!(efi.memmap.flags & EFI_MEMMAP_LATE)) {
                unsigned long size;
 
                size = efi.memmap.desc_size * efi.memmap.nr_map;
@@ -162,13 +202,14 @@ int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size)
        struct efi_memory_map_data data = {
                .phys_map = addr,
                .size = size,
+               .flags = EFI_MEMMAP_LATE,
        };
 
        /* Did we forget to unmap the early EFI memmap? */
        WARN_ON(efi.memmap.map);
 
        /* Were we already called? */
-       WARN_ON(efi.memmap.late);
+       WARN_ON(efi.memmap.flags & EFI_MEMMAP_LATE);
 
        /*
         * It makes no sense to allow callers to register different
@@ -178,13 +219,12 @@ int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size)
        data.desc_version = efi.memmap.desc_version;
        data.desc_size = efi.memmap.desc_size;
 
-       return __efi_memmap_init(&data, true);
+       return __efi_memmap_init(&data);
 }
 
 /**
  * efi_memmap_install - Install a new EFI memory map in efi.memmap
- * @addr: Physical address of the memory map
- * @nr_map: Number of entries in the memory map
+ * @ctx: map allocation parameters (address, size, flags)
  *
  * Unlike efi_memmap_init_*(), this function does not allow the caller
  * to switch from early to late mappings. It simply uses the existing
@@ -192,18 +232,11 @@ int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size)
  *
  * Returns zero on success, a negative error code on failure.
  */
-int __init efi_memmap_install(phys_addr_t addr, unsigned int nr_map)
+int __init efi_memmap_install(struct efi_memory_map_data *data)
 {
-       struct efi_memory_map_data data;
-
        efi_memmap_unmap();
 
-       data.phys_map = addr;
-       data.size = efi.memmap.desc_size * nr_map;
-       data.desc_version = efi.memmap.desc_version;
-       data.desc_size = efi.memmap.desc_size;
-
-       return __efi_memmap_init(&data, efi.memmap.late);
+       return __efi_memmap_init(data);
 }
 
 /**