efi/libstub: Add API function to allocate aligned memory
authorArd Biesheuvel <ardb@kernel.org>
Fri, 27 Mar 2020 15:09:40 +0000 (16:09 +0100)
committerArd Biesheuvel <ardb@kernel.org>
Fri, 24 Apr 2020 12:52:16 +0000 (14:52 +0200)
Break out the code to create an aligned page allocation from mem.c
and move it into a function efi_allocate_pages_aligned() in alignedmem.c.
Update efi_allocate_pages() to invoke it unless the minimum alignment
equals the EFI page size (4 KB), in which case the ordinary page
allocator is sufficient. This way, efi_allocate_pages_aligned() will
only be pulled into the build if it is actually being used (which will
be on arm64 only in the immediate future)

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
drivers/firmware/efi/libstub/Makefile
drivers/firmware/efi/libstub/alignedmem.c [new file with mode: 0644]
drivers/firmware/efi/libstub/efistub.h
drivers/firmware/efi/libstub/mem.c

index 75cb2c3..bb8af2b 100644 (file)
@@ -42,7 +42,8 @@ KCOV_INSTRUMENT                       := n
 
 lib-y                          := efi-stub-helper.o gop.o secureboot.o tpm.o \
                                   file.o mem.o random.o randomalloc.o pci.o \
-                                  skip_spaces.o lib-cmdline.o lib-ctype.o
+                                  skip_spaces.o lib-cmdline.o lib-ctype.o \
+                                  alignedmem.o
 
 # include the stub's generic dependencies from lib/ when building for ARM/arm64
 efi-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c
diff --git a/drivers/firmware/efi/libstub/alignedmem.c b/drivers/firmware/efi/libstub/alignedmem.c
new file mode 100644 (file)
index 0000000..cc89c4d
--- /dev/null
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/efi.h>
+#include <asm/efi.h>
+
+#include "efistub.h"
+
+/**
+ * efi_allocate_pages_aligned() - Allocate memory pages
+ * @size:      minimum number of bytes to allocate
+ * @addr:      On return the address of the first allocated page. The first
+ *             allocated page has alignment EFI_ALLOC_ALIGN which is an
+ *             architecture dependent multiple of the page size.
+ * @max:       the address that the last allocated memory page shall not
+ *             exceed
+ * @align:     minimum alignment of the base of the allocation
+ *
+ * Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according
+ * to @align, which should be >= EFI_ALLOC_ALIGN. The last allocated page will
+ * not exceed the address given by @max.
+ *
+ * Return:     status code
+ */
+efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr,
+                                       unsigned long max, unsigned long align)
+{
+       efi_physical_addr_t alloc_addr;
+       efi_status_t status;
+       int slack;
+
+       if (align < EFI_ALLOC_ALIGN)
+               align = EFI_ALLOC_ALIGN;
+
+       alloc_addr = ALIGN_DOWN(max + 1, align) - 1;
+       size = round_up(size, EFI_ALLOC_ALIGN);
+       slack = align / EFI_PAGE_SIZE - 1;
+
+       status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS,
+                            EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack,
+                            &alloc_addr);
+       if (status != EFI_SUCCESS)
+               return status;
+
+       *addr = ALIGN((unsigned long)alloc_addr, align);
+
+       if (slack > 0) {
+               int l = (alloc_addr % align) / EFI_PAGE_SIZE;
+
+               if (l) {
+                       efi_bs_call(free_pages, alloc_addr, slack - l + 1);
+                       slack = l - 1;
+               }
+               if (slack)
+                       efi_bs_call(free_pages, *addr + size, slack);
+       }
+       return EFI_SUCCESS;
+}
index 9af65be..baa0bc1 100644 (file)
@@ -657,6 +657,9 @@ efi_status_t efi_low_alloc(unsigned long size, unsigned long align,
 efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
                                unsigned long max);
 
+efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr,
+                                       unsigned long max, unsigned long align);
+
 efi_status_t efi_relocate_kernel(unsigned long *image_addr,
                                 unsigned long image_size,
                                 unsigned long alloc_size,
index 869a79c..0020b0f 100644 (file)
@@ -93,31 +93,24 @@ fail:
 efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
                                unsigned long max)
 {
-       efi_physical_addr_t alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1;
-       int slack = EFI_ALLOC_ALIGN / EFI_PAGE_SIZE - 1;
+       efi_physical_addr_t alloc_addr;
        efi_status_t status;
 
-       size = round_up(size, EFI_ALLOC_ALIGN);
+       if (EFI_ALLOC_ALIGN > EFI_PAGE_SIZE)
+               return efi_allocate_pages_aligned(size, addr, max,
+                                                 EFI_ALLOC_ALIGN);
+
+       alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1;
        status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS,
-                            EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack,
+                            EFI_LOADER_DATA, DIV_ROUND_UP(size, EFI_PAGE_SIZE),
                             &alloc_addr);
        if (status != EFI_SUCCESS)
                return status;
 
-       *addr = ALIGN((unsigned long)alloc_addr, EFI_ALLOC_ALIGN);
-
-       if (slack > 0) {
-               int l = (alloc_addr % EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
-
-               if (l) {
-                       efi_bs_call(free_pages, alloc_addr, slack - l + 1);
-                       slack = l - 1;
-               }
-               if (slack)
-                       efi_bs_call(free_pages, *addr + size, slack);
-       }
+       *addr = alloc_addr;
        return EFI_SUCCESS;
 }
+
 /**
  * efi_low_alloc_above() - allocate pages at or above given address
  * @size:      size of the memory area to allocate