diff options
author | Ard Biesheuvel <ardb@kernel.org> | 2020-03-27 18:09:40 +0300 |
---|---|---|
committer | Ard Biesheuvel <ardb@kernel.org> | 2020-04-24 15:52:16 +0300 |
commit | 43b1df0e013c25abb536699f46d0e9f291b586a0 (patch) | |
tree | cc2415ae3b79cb9494824ba9a5ce1e7a25e39d5c /drivers/firmware/efi/libstub/alignedmem.c | |
parent | 5d12da9dd6c155f7a09dfcaa2b9827764c939b2a (diff) | |
download | linux-43b1df0e013c25abb536699f46d0e9f291b586a0.tar.xz |
efi/libstub: Add API function to allocate aligned memory
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>
Diffstat (limited to 'drivers/firmware/efi/libstub/alignedmem.c')
-rw-r--r-- | drivers/firmware/efi/libstub/alignedmem.c | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/drivers/firmware/efi/libstub/alignedmem.c b/drivers/firmware/efi/libstub/alignedmem.c new file mode 100644 index 000000000000..cc89c4d6196f --- /dev/null +++ b/drivers/firmware/efi/libstub/alignedmem.c @@ -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; +} |