linux/drivers/firmware/efi/libstub/alignedmem.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3#include <linux/efi.h>
   4#include <asm/efi.h>
   5
   6#include "efistub.h"
   7
   8/**
   9 * efi_allocate_pages_aligned() - Allocate memory pages
  10 * @size:       minimum number of bytes to allocate
  11 * @addr:       On return the address of the first allocated page. The first
  12 *              allocated page has alignment EFI_ALLOC_ALIGN which is an
  13 *              architecture dependent multiple of the page size.
  14 * @max:        the address that the last allocated memory page shall not
  15 *              exceed
  16 * @align:      minimum alignment of the base of the allocation
  17 *
  18 * Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according
  19 * to @align, which should be >= EFI_ALLOC_ALIGN. The last allocated page will
  20 * not exceed the address given by @max.
  21 *
  22 * Return:      status code
  23 */
  24efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr,
  25                                        unsigned long max, unsigned long align)
  26{
  27        efi_physical_addr_t alloc_addr;
  28        efi_status_t status;
  29        int slack;
  30
  31        if (align < EFI_ALLOC_ALIGN)
  32                align = EFI_ALLOC_ALIGN;
  33
  34        alloc_addr = ALIGN_DOWN(max + 1, align) - 1;
  35        size = round_up(size, EFI_ALLOC_ALIGN);
  36        slack = align / EFI_PAGE_SIZE - 1;
  37
  38        status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS,
  39                             EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack,
  40                             &alloc_addr);
  41        if (status != EFI_SUCCESS)
  42                return status;
  43
  44        *addr = ALIGN((unsigned long)alloc_addr, align);
  45
  46        if (slack > 0) {
  47                int l = (alloc_addr & (align - 1)) / EFI_PAGE_SIZE;
  48
  49                if (l) {
  50                        efi_bs_call(free_pages, alloc_addr, slack - l + 1);
  51                        slack = l - 1;
  52                }
  53                if (slack)
  54                        efi_bs_call(free_pages, *addr + size, slack);
  55        }
  56        return EFI_SUCCESS;
  57}
  58