1685d8164SArd Biesheuvel // SPDX-License-Identifier: GPL-2.0 2685d8164SArd Biesheuvel 3685d8164SArd Biesheuvel #include <linux/efi.h> 4685d8164SArd Biesheuvel #include <asm/efi.h> 5685d8164SArd Biesheuvel 6685d8164SArd Biesheuvel #include "efistub.h" 7685d8164SArd Biesheuvel 8685d8164SArd Biesheuvel /** 9685d8164SArd Biesheuvel * efi_low_alloc_above() - allocate pages at or above given address 10685d8164SArd Biesheuvel * @size: size of the memory area to allocate 11685d8164SArd Biesheuvel * @align: minimum alignment of the allocated memory area. It should 12685d8164SArd Biesheuvel * a power of two. 13685d8164SArd Biesheuvel * @addr: on exit the address of the allocated memory 14685d8164SArd Biesheuvel * @min: minimum address to used for the memory allocation 15685d8164SArd Biesheuvel * 16685d8164SArd Biesheuvel * Allocate at the lowest possible address that is not below @min as 17685d8164SArd Biesheuvel * EFI_LOADER_DATA. The allocated pages are aligned according to @align but at 18685d8164SArd Biesheuvel * least EFI_ALLOC_ALIGN. The first allocated page will not below the address 19685d8164SArd Biesheuvel * given by @min. 20685d8164SArd Biesheuvel * 21685d8164SArd Biesheuvel * Return: status code 22685d8164SArd Biesheuvel */ 231a895dbfSArd Biesheuvel efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, 24685d8164SArd Biesheuvel unsigned long *addr, unsigned long min) 25685d8164SArd Biesheuvel { 26*eab31265SArd Biesheuvel struct efi_boot_memmap *map; 27685d8164SArd Biesheuvel efi_status_t status; 28685d8164SArd Biesheuvel unsigned long nr_pages; 29685d8164SArd Biesheuvel int i; 30685d8164SArd Biesheuvel 31*eab31265SArd Biesheuvel status = efi_get_memory_map(&map); 32685d8164SArd Biesheuvel if (status != EFI_SUCCESS) 33685d8164SArd Biesheuvel goto fail; 34685d8164SArd Biesheuvel 35685d8164SArd Biesheuvel /* 36685d8164SArd Biesheuvel * Enforce minimum alignment that EFI or Linux requires when 37685d8164SArd Biesheuvel * requesting a specific address. We are doing page-based (or 38685d8164SArd Biesheuvel * larger) allocations, and both the address and size must meet 39685d8164SArd Biesheuvel * alignment constraints. 40685d8164SArd Biesheuvel */ 41685d8164SArd Biesheuvel if (align < EFI_ALLOC_ALIGN) 42685d8164SArd Biesheuvel align = EFI_ALLOC_ALIGN; 43685d8164SArd Biesheuvel 44685d8164SArd Biesheuvel size = round_up(size, EFI_ALLOC_ALIGN); 45685d8164SArd Biesheuvel nr_pages = size / EFI_PAGE_SIZE; 46*eab31265SArd Biesheuvel for (i = 0; i < map->map_size / map->desc_size; i++) { 47685d8164SArd Biesheuvel efi_memory_desc_t *desc; 48*eab31265SArd Biesheuvel unsigned long m = (unsigned long)map->map; 49685d8164SArd Biesheuvel u64 start, end; 50685d8164SArd Biesheuvel 51*eab31265SArd Biesheuvel desc = efi_early_memdesc_ptr(m, map->desc_size, i); 52685d8164SArd Biesheuvel 53685d8164SArd Biesheuvel if (desc->type != EFI_CONVENTIONAL_MEMORY) 54685d8164SArd Biesheuvel continue; 55685d8164SArd Biesheuvel 56685d8164SArd Biesheuvel if (efi_soft_reserve_enabled() && 57685d8164SArd Biesheuvel (desc->attribute & EFI_MEMORY_SP)) 58685d8164SArd Biesheuvel continue; 59685d8164SArd Biesheuvel 60685d8164SArd Biesheuvel if (desc->num_pages < nr_pages) 61685d8164SArd Biesheuvel continue; 62685d8164SArd Biesheuvel 63685d8164SArd Biesheuvel start = desc->phys_addr; 64685d8164SArd Biesheuvel end = start + desc->num_pages * EFI_PAGE_SIZE; 65685d8164SArd Biesheuvel 66685d8164SArd Biesheuvel if (start < min) 67685d8164SArd Biesheuvel start = min; 68685d8164SArd Biesheuvel 69685d8164SArd Biesheuvel start = round_up(start, align); 70685d8164SArd Biesheuvel if ((start + size) > end) 71685d8164SArd Biesheuvel continue; 72685d8164SArd Biesheuvel 73685d8164SArd Biesheuvel status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, 74685d8164SArd Biesheuvel EFI_LOADER_DATA, nr_pages, &start); 75685d8164SArd Biesheuvel if (status == EFI_SUCCESS) { 76685d8164SArd Biesheuvel *addr = start; 77685d8164SArd Biesheuvel break; 78685d8164SArd Biesheuvel } 79685d8164SArd Biesheuvel } 80685d8164SArd Biesheuvel 81*eab31265SArd Biesheuvel if (i == map->map_size / map->desc_size) 82685d8164SArd Biesheuvel status = EFI_NOT_FOUND; 83685d8164SArd Biesheuvel 84685d8164SArd Biesheuvel efi_bs_call(free_pool, map); 85685d8164SArd Biesheuvel fail: 86685d8164SArd Biesheuvel return status; 87685d8164SArd Biesheuvel } 88685d8164SArd Biesheuvel 89685d8164SArd Biesheuvel /** 90685d8164SArd Biesheuvel * efi_relocate_kernel() - copy memory area 91685d8164SArd Biesheuvel * @image_addr: pointer to address of memory area to copy 92685d8164SArd Biesheuvel * @image_size: size of memory area to copy 93685d8164SArd Biesheuvel * @alloc_size: minimum size of memory to allocate, must be greater or 94685d8164SArd Biesheuvel * equal to image_size 95685d8164SArd Biesheuvel * @preferred_addr: preferred target address 96685d8164SArd Biesheuvel * @alignment: minimum alignment of the allocated memory area. It 97685d8164SArd Biesheuvel * should be a power of two. 98685d8164SArd Biesheuvel * @min_addr: minimum target address 99685d8164SArd Biesheuvel * 100685d8164SArd Biesheuvel * Copy a memory area to a newly allocated memory area aligned according 101685d8164SArd Biesheuvel * to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address 102685d8164SArd Biesheuvel * is not available, the allocated address will not be below @min_addr. 103685d8164SArd Biesheuvel * On exit, @image_addr is updated to the target copy address that was used. 104685d8164SArd Biesheuvel * 105685d8164SArd Biesheuvel * This function is used to copy the Linux kernel verbatim. It does not apply 106685d8164SArd Biesheuvel * any relocation changes. 107685d8164SArd Biesheuvel * 108685d8164SArd Biesheuvel * Return: status code 109685d8164SArd Biesheuvel */ 110685d8164SArd Biesheuvel efi_status_t efi_relocate_kernel(unsigned long *image_addr, 111685d8164SArd Biesheuvel unsigned long image_size, 112685d8164SArd Biesheuvel unsigned long alloc_size, 113685d8164SArd Biesheuvel unsigned long preferred_addr, 114685d8164SArd Biesheuvel unsigned long alignment, 115685d8164SArd Biesheuvel unsigned long min_addr) 116685d8164SArd Biesheuvel { 117685d8164SArd Biesheuvel unsigned long cur_image_addr; 118685d8164SArd Biesheuvel unsigned long new_addr = 0; 119685d8164SArd Biesheuvel efi_status_t status; 120685d8164SArd Biesheuvel unsigned long nr_pages; 121685d8164SArd Biesheuvel efi_physical_addr_t efi_addr = preferred_addr; 122685d8164SArd Biesheuvel 123685d8164SArd Biesheuvel if (!image_addr || !image_size || !alloc_size) 124685d8164SArd Biesheuvel return EFI_INVALID_PARAMETER; 125685d8164SArd Biesheuvel if (alloc_size < image_size) 126685d8164SArd Biesheuvel return EFI_INVALID_PARAMETER; 127685d8164SArd Biesheuvel 128685d8164SArd Biesheuvel cur_image_addr = *image_addr; 129685d8164SArd Biesheuvel 130685d8164SArd Biesheuvel /* 131685d8164SArd Biesheuvel * The EFI firmware loader could have placed the kernel image 132685d8164SArd Biesheuvel * anywhere in memory, but the kernel has restrictions on the 133685d8164SArd Biesheuvel * max physical address it can run at. Some architectures 13440262299SJoe Perches * also have a preferred address, so first try to relocate 135685d8164SArd Biesheuvel * to the preferred address. If that fails, allocate as low 136685d8164SArd Biesheuvel * as possible while respecting the required alignment. 137685d8164SArd Biesheuvel */ 138685d8164SArd Biesheuvel nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; 139685d8164SArd Biesheuvel status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, 140685d8164SArd Biesheuvel EFI_LOADER_DATA, nr_pages, &efi_addr); 141685d8164SArd Biesheuvel new_addr = efi_addr; 142685d8164SArd Biesheuvel /* 143685d8164SArd Biesheuvel * If preferred address allocation failed allocate as low as 144685d8164SArd Biesheuvel * possible. 145685d8164SArd Biesheuvel */ 146685d8164SArd Biesheuvel if (status != EFI_SUCCESS) { 147685d8164SArd Biesheuvel status = efi_low_alloc_above(alloc_size, alignment, &new_addr, 148685d8164SArd Biesheuvel min_addr); 149685d8164SArd Biesheuvel } 150685d8164SArd Biesheuvel if (status != EFI_SUCCESS) { 151793473c2SArvind Sankar efi_err("Failed to allocate usable memory for kernel.\n"); 152685d8164SArd Biesheuvel return status; 153685d8164SArd Biesheuvel } 154685d8164SArd Biesheuvel 155685d8164SArd Biesheuvel /* 156685d8164SArd Biesheuvel * We know source/dest won't overlap since both memory ranges 157685d8164SArd Biesheuvel * have been allocated by UEFI, so we can safely use memcpy. 158685d8164SArd Biesheuvel */ 159685d8164SArd Biesheuvel memcpy((void *)new_addr, (void *)cur_image_addr, image_size); 160685d8164SArd Biesheuvel 161685d8164SArd Biesheuvel /* Return the new address of the relocated image. */ 162685d8164SArd Biesheuvel *image_addr = new_addr; 163685d8164SArd Biesheuvel 164685d8164SArd Biesheuvel return status; 165685d8164SArd Biesheuvel } 166