linux/drivers/firmware/efi/libstub/mem.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
   8static inline bool mmap_has_headroom(unsigned long buff_size,
   9                                     unsigned long map_size,
  10                                     unsigned long desc_size)
  11{
  12        unsigned long slack = buff_size - map_size;
  13
  14        return slack / desc_size >= EFI_MMAP_NR_SLACK_SLOTS;
  15}
  16
  17/**
  18 * efi_get_memory_map() - get memory map
  19 * @map:        on return pointer to memory map
  20 *
  21 * Retrieve the UEFI memory map. The allocated memory leaves room for
  22 * up to EFI_MMAP_NR_SLACK_SLOTS additional memory map entries.
  23 *
  24 * Return:      status code
  25 */
  26efi_status_t efi_get_memory_map(struct efi_boot_memmap *map)
  27{
  28        efi_memory_desc_t *m = NULL;
  29        efi_status_t status;
  30        unsigned long key;
  31        u32 desc_version;
  32
  33        *map->desc_size =       sizeof(*m);
  34        *map->map_size =        *map->desc_size * 32;
  35        *map->buff_size =       *map->map_size;
  36again:
  37        status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
  38                             *map->map_size, (void **)&m);
  39        if (status != EFI_SUCCESS)
  40                goto fail;
  41
  42        *map->desc_size = 0;
  43        key = 0;
  44        status = efi_bs_call(get_memory_map, map->map_size, m,
  45                             &key, map->desc_size, &desc_version);
  46        if (status == EFI_BUFFER_TOO_SMALL ||
  47            !mmap_has_headroom(*map->buff_size, *map->map_size,
  48                               *map->desc_size)) {
  49                efi_bs_call(free_pool, m);
  50                /*
  51                 * Make sure there is some entries of headroom so that the
  52                 * buffer can be reused for a new map after allocations are
  53                 * no longer permitted.  Its unlikely that the map will grow to
  54                 * exceed this headroom once we are ready to trigger
  55                 * ExitBootServices()
  56                 */
  57                *map->map_size += *map->desc_size * EFI_MMAP_NR_SLACK_SLOTS;
  58                *map->buff_size = *map->map_size;
  59                goto again;
  60        }
  61
  62        if (status == EFI_SUCCESS) {
  63                if (map->key_ptr)
  64                        *map->key_ptr = key;
  65                if (map->desc_ver)
  66                        *map->desc_ver = desc_version;
  67        } else {
  68                efi_bs_call(free_pool, m);
  69        }
  70
  71fail:
  72        *map->map = m;
  73        return status;
  74}
  75
  76/**
  77 * efi_allocate_pages() - Allocate memory pages
  78 * @size:       minimum number of bytes to allocate
  79 * @addr:       On return the address of the first allocated page. The first
  80 *              allocated page has alignment EFI_ALLOC_ALIGN which is an
  81 *              architecture dependent multiple of the page size.
  82 * @max:        the address that the last allocated memory page shall not
  83 *              exceed
  84 *
  85 * Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according
  86 * to EFI_ALLOC_ALIGN. The last allocated page will not exceed the address
  87 * given by @max.
  88 *
  89 * Return:      status code
  90 */
  91efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
  92                                unsigned long max)
  93{
  94        efi_physical_addr_t alloc_addr;
  95        efi_status_t status;
  96
  97        if (EFI_ALLOC_ALIGN > EFI_PAGE_SIZE)
  98                return efi_allocate_pages_aligned(size, addr, max,
  99                                                  EFI_ALLOC_ALIGN);
 100
 101        alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1;
 102        status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS,
 103                             EFI_LOADER_DATA, DIV_ROUND_UP(size, EFI_PAGE_SIZE),
 104                             &alloc_addr);
 105        if (status != EFI_SUCCESS)
 106                return status;
 107
 108        *addr = alloc_addr;
 109        return EFI_SUCCESS;
 110}
 111
 112/**
 113 * efi_free() - free memory pages
 114 * @size:       size of the memory area to free in bytes
 115 * @addr:       start of the memory area to free (must be EFI_PAGE_SIZE
 116 *              aligned)
 117 *
 118 * @size is rounded up to a multiple of EFI_ALLOC_ALIGN which is an
 119 * architecture specific multiple of EFI_PAGE_SIZE. So this function should
 120 * only be used to return pages allocated with efi_allocate_pages() or
 121 * efi_low_alloc_above().
 122 */
 123void efi_free(unsigned long size, unsigned long addr)
 124{
 125        unsigned long nr_pages;
 126
 127        if (!size)
 128                return;
 129
 130        nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
 131        efi_bs_call(free_pages, addr, nr_pages);
 132}
 133