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