1f57db62cSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0 2f57db62cSArd Biesheuvel 3f57db62cSArd Biesheuvel #include <linux/efi.h> 4f57db62cSArd Biesheuvel #include <asm/efi.h> 5f57db62cSArd Biesheuvel 6f57db62cSArd Biesheuvel #include "efistub.h" 7f57db62cSArd Biesheuvel 8f57db62cSArd Biesheuvel #define EFI_MMAP_NR_SLACK_SLOTS 8 9f57db62cSArd Biesheuvel 10f57db62cSArd Biesheuvel static inline bool mmap_has_headroom(unsigned long buff_size, 11f57db62cSArd Biesheuvel unsigned long map_size, 12f57db62cSArd Biesheuvel unsigned long desc_size) 13f57db62cSArd Biesheuvel { 14f57db62cSArd Biesheuvel unsigned long slack = buff_size - map_size; 15f57db62cSArd Biesheuvel 16f57db62cSArd Biesheuvel return slack / desc_size >= EFI_MMAP_NR_SLACK_SLOTS; 17f57db62cSArd Biesheuvel } 18f57db62cSArd Biesheuvel 19f57db62cSArd Biesheuvel efi_status_t efi_get_memory_map(struct efi_boot_memmap *map) 20f57db62cSArd Biesheuvel { 21f57db62cSArd Biesheuvel efi_memory_desc_t *m = NULL; 22f57db62cSArd Biesheuvel efi_status_t status; 23f57db62cSArd Biesheuvel unsigned long key; 24f57db62cSArd Biesheuvel u32 desc_version; 25f57db62cSArd Biesheuvel 26f57db62cSArd Biesheuvel *map->desc_size = sizeof(*m); 27f57db62cSArd Biesheuvel *map->map_size = *map->desc_size * 32; 28f57db62cSArd Biesheuvel *map->buff_size = *map->map_size; 29f57db62cSArd Biesheuvel again: 30f57db62cSArd Biesheuvel status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, 31f57db62cSArd Biesheuvel *map->map_size, (void **)&m); 32f57db62cSArd Biesheuvel if (status != EFI_SUCCESS) 33f57db62cSArd Biesheuvel goto fail; 34f57db62cSArd Biesheuvel 35f57db62cSArd Biesheuvel *map->desc_size = 0; 36f57db62cSArd Biesheuvel key = 0; 37f57db62cSArd Biesheuvel status = efi_bs_call(get_memory_map, map->map_size, m, 38f57db62cSArd Biesheuvel &key, map->desc_size, &desc_version); 39f57db62cSArd Biesheuvel if (status == EFI_BUFFER_TOO_SMALL || 40f57db62cSArd Biesheuvel !mmap_has_headroom(*map->buff_size, *map->map_size, 41f57db62cSArd Biesheuvel *map->desc_size)) { 42f57db62cSArd Biesheuvel efi_bs_call(free_pool, m); 43f57db62cSArd Biesheuvel /* 44f57db62cSArd Biesheuvel * Make sure there is some entries of headroom so that the 45f57db62cSArd Biesheuvel * buffer can be reused for a new map after allocations are 46f57db62cSArd Biesheuvel * no longer permitted. Its unlikely that the map will grow to 47f57db62cSArd Biesheuvel * exceed this headroom once we are ready to trigger 48f57db62cSArd Biesheuvel * ExitBootServices() 49f57db62cSArd Biesheuvel */ 50f57db62cSArd Biesheuvel *map->map_size += *map->desc_size * EFI_MMAP_NR_SLACK_SLOTS; 51f57db62cSArd Biesheuvel *map->buff_size = *map->map_size; 52f57db62cSArd Biesheuvel goto again; 53f57db62cSArd Biesheuvel } 54f57db62cSArd Biesheuvel 55f57db62cSArd Biesheuvel if (status != EFI_SUCCESS) 56f57db62cSArd Biesheuvel efi_bs_call(free_pool, m); 57f57db62cSArd Biesheuvel 58f57db62cSArd Biesheuvel if (map->key_ptr && status == EFI_SUCCESS) 59f57db62cSArd Biesheuvel *map->key_ptr = key; 60f57db62cSArd Biesheuvel if (map->desc_ver && status == EFI_SUCCESS) 61f57db62cSArd Biesheuvel *map->desc_ver = desc_version; 62f57db62cSArd Biesheuvel 63f57db62cSArd Biesheuvel fail: 64f57db62cSArd Biesheuvel *map->map = m; 65f57db62cSArd Biesheuvel return status; 66f57db62cSArd Biesheuvel } 67f57db62cSArd Biesheuvel 68f57db62cSArd Biesheuvel /* 69f57db62cSArd Biesheuvel * Allocate at the highest possible address that is not above 'max'. 70f57db62cSArd Biesheuvel */ 71a7495c28SArd Biesheuvel efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, 72a7495c28SArd Biesheuvel unsigned long max) 73f57db62cSArd Biesheuvel { 74a7495c28SArd Biesheuvel efi_physical_addr_t alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1; 75a7495c28SArd Biesheuvel int slack = EFI_ALLOC_ALIGN / EFI_PAGE_SIZE - 1; 76f57db62cSArd Biesheuvel efi_status_t status; 77f57db62cSArd Biesheuvel 78f57db62cSArd Biesheuvel size = round_up(size, EFI_ALLOC_ALIGN); 79a7495c28SArd Biesheuvel status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, 80a7495c28SArd Biesheuvel EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack, 81a7495c28SArd Biesheuvel &alloc_addr); 82a7495c28SArd Biesheuvel if (status != EFI_SUCCESS) 83f57db62cSArd Biesheuvel return status; 84f57db62cSArd Biesheuvel 85a7495c28SArd Biesheuvel *addr = ALIGN((unsigned long)alloc_addr, EFI_ALLOC_ALIGN); 86a7495c28SArd Biesheuvel 87a7495c28SArd Biesheuvel if (slack > 0) { 88a7495c28SArd Biesheuvel int l = (alloc_addr % EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; 89a7495c28SArd Biesheuvel 90a7495c28SArd Biesheuvel if (l) { 91a7495c28SArd Biesheuvel efi_bs_call(free_pages, alloc_addr, slack - l + 1); 92a7495c28SArd Biesheuvel slack = l - 1; 93a7495c28SArd Biesheuvel } 94a7495c28SArd Biesheuvel if (slack) 95a7495c28SArd Biesheuvel efi_bs_call(free_pages, *addr + size, slack); 96a7495c28SArd Biesheuvel } 97a7495c28SArd Biesheuvel return EFI_SUCCESS; 98a7495c28SArd Biesheuvel } 99f57db62cSArd Biesheuvel /* 100f57db62cSArd Biesheuvel * Allocate at the lowest possible address that is not below 'min'. 101f57db62cSArd Biesheuvel */ 102f57db62cSArd Biesheuvel efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, 103f57db62cSArd Biesheuvel unsigned long *addr, unsigned long min) 104f57db62cSArd Biesheuvel { 105f57db62cSArd Biesheuvel unsigned long map_size, desc_size, buff_size; 106f57db62cSArd Biesheuvel efi_memory_desc_t *map; 107f57db62cSArd Biesheuvel efi_status_t status; 108f57db62cSArd Biesheuvel unsigned long nr_pages; 109f57db62cSArd Biesheuvel int i; 110f57db62cSArd Biesheuvel struct efi_boot_memmap boot_map; 111f57db62cSArd Biesheuvel 112f57db62cSArd Biesheuvel boot_map.map = ↦ 113f57db62cSArd Biesheuvel boot_map.map_size = &map_size; 114f57db62cSArd Biesheuvel boot_map.desc_size = &desc_size; 115f57db62cSArd Biesheuvel boot_map.desc_ver = NULL; 116f57db62cSArd Biesheuvel boot_map.key_ptr = NULL; 117f57db62cSArd Biesheuvel boot_map.buff_size = &buff_size; 118f57db62cSArd Biesheuvel 119f57db62cSArd Biesheuvel status = efi_get_memory_map(&boot_map); 120f57db62cSArd Biesheuvel if (status != EFI_SUCCESS) 121f57db62cSArd Biesheuvel goto fail; 122f57db62cSArd Biesheuvel 123f57db62cSArd Biesheuvel /* 124f57db62cSArd Biesheuvel * Enforce minimum alignment that EFI or Linux requires when 125f57db62cSArd Biesheuvel * requesting a specific address. We are doing page-based (or 126f57db62cSArd Biesheuvel * larger) allocations, and both the address and size must meet 127f57db62cSArd Biesheuvel * alignment constraints. 128f57db62cSArd Biesheuvel */ 129f57db62cSArd Biesheuvel if (align < EFI_ALLOC_ALIGN) 130f57db62cSArd Biesheuvel align = EFI_ALLOC_ALIGN; 131f57db62cSArd Biesheuvel 132f57db62cSArd Biesheuvel size = round_up(size, EFI_ALLOC_ALIGN); 133f57db62cSArd Biesheuvel nr_pages = size / EFI_PAGE_SIZE; 134f57db62cSArd Biesheuvel for (i = 0; i < map_size / desc_size; i++) { 135f57db62cSArd Biesheuvel efi_memory_desc_t *desc; 136f57db62cSArd Biesheuvel unsigned long m = (unsigned long)map; 137f57db62cSArd Biesheuvel u64 start, end; 138f57db62cSArd Biesheuvel 139f57db62cSArd Biesheuvel desc = efi_early_memdesc_ptr(m, desc_size, i); 140f57db62cSArd Biesheuvel 141f57db62cSArd Biesheuvel if (desc->type != EFI_CONVENTIONAL_MEMORY) 142f57db62cSArd Biesheuvel continue; 143f57db62cSArd Biesheuvel 144f57db62cSArd Biesheuvel if (efi_soft_reserve_enabled() && 145f57db62cSArd Biesheuvel (desc->attribute & EFI_MEMORY_SP)) 146f57db62cSArd Biesheuvel continue; 147f57db62cSArd Biesheuvel 148f57db62cSArd Biesheuvel if (desc->num_pages < nr_pages) 149f57db62cSArd Biesheuvel continue; 150f57db62cSArd Biesheuvel 151f57db62cSArd Biesheuvel start = desc->phys_addr; 152f57db62cSArd Biesheuvel end = start + desc->num_pages * EFI_PAGE_SIZE; 153f57db62cSArd Biesheuvel 154f57db62cSArd Biesheuvel if (start < min) 155f57db62cSArd Biesheuvel start = min; 156f57db62cSArd Biesheuvel 157f57db62cSArd Biesheuvel start = round_up(start, align); 158f57db62cSArd Biesheuvel if ((start + size) > end) 159f57db62cSArd Biesheuvel continue; 160f57db62cSArd Biesheuvel 161f57db62cSArd Biesheuvel status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, 162f57db62cSArd Biesheuvel EFI_LOADER_DATA, nr_pages, &start); 163f57db62cSArd Biesheuvel if (status == EFI_SUCCESS) { 164f57db62cSArd Biesheuvel *addr = start; 165f57db62cSArd Biesheuvel break; 166f57db62cSArd Biesheuvel } 167f57db62cSArd Biesheuvel } 168f57db62cSArd Biesheuvel 169f57db62cSArd Biesheuvel if (i == map_size / desc_size) 170f57db62cSArd Biesheuvel status = EFI_NOT_FOUND; 171f57db62cSArd Biesheuvel 172f57db62cSArd Biesheuvel efi_bs_call(free_pool, map); 173f57db62cSArd Biesheuvel fail: 174f57db62cSArd Biesheuvel return status; 175f57db62cSArd Biesheuvel } 176f57db62cSArd Biesheuvel 177f57db62cSArd Biesheuvel void efi_free(unsigned long size, unsigned long addr) 178f57db62cSArd Biesheuvel { 179f57db62cSArd Biesheuvel unsigned long nr_pages; 180f57db62cSArd Biesheuvel 181f57db62cSArd Biesheuvel if (!size) 182f57db62cSArd Biesheuvel return; 183f57db62cSArd Biesheuvel 184f57db62cSArd Biesheuvel nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; 185f57db62cSArd Biesheuvel efi_bs_call(free_pages, addr, nr_pages); 186f57db62cSArd Biesheuvel } 187f57db62cSArd Biesheuvel 188f57db62cSArd Biesheuvel /* 189f57db62cSArd Biesheuvel * Relocate a kernel image, either compressed or uncompressed. 190f57db62cSArd Biesheuvel * In the ARM64 case, all kernel images are currently 191f57db62cSArd Biesheuvel * uncompressed, and as such when we relocate it we need to 192f57db62cSArd Biesheuvel * allocate additional space for the BSS segment. Any low 193f57db62cSArd Biesheuvel * memory that this function should avoid needs to be 194f57db62cSArd Biesheuvel * unavailable in the EFI memory map, as if the preferred 195f57db62cSArd Biesheuvel * address is not available the lowest available address will 196f57db62cSArd Biesheuvel * be used. 197f57db62cSArd Biesheuvel */ 198f57db62cSArd Biesheuvel efi_status_t efi_relocate_kernel(unsigned long *image_addr, 199f57db62cSArd Biesheuvel unsigned long image_size, 200f57db62cSArd Biesheuvel unsigned long alloc_size, 201f57db62cSArd Biesheuvel unsigned long preferred_addr, 202f57db62cSArd Biesheuvel unsigned long alignment, 203f57db62cSArd Biesheuvel unsigned long min_addr) 204f57db62cSArd Biesheuvel { 205f57db62cSArd Biesheuvel unsigned long cur_image_addr; 206f57db62cSArd Biesheuvel unsigned long new_addr = 0; 207f57db62cSArd Biesheuvel efi_status_t status; 208f57db62cSArd Biesheuvel unsigned long nr_pages; 209f57db62cSArd Biesheuvel efi_physical_addr_t efi_addr = preferred_addr; 210f57db62cSArd Biesheuvel 211f57db62cSArd Biesheuvel if (!image_addr || !image_size || !alloc_size) 212f57db62cSArd Biesheuvel return EFI_INVALID_PARAMETER; 213f57db62cSArd Biesheuvel if (alloc_size < image_size) 214f57db62cSArd Biesheuvel return EFI_INVALID_PARAMETER; 215f57db62cSArd Biesheuvel 216f57db62cSArd Biesheuvel cur_image_addr = *image_addr; 217f57db62cSArd Biesheuvel 218f57db62cSArd Biesheuvel /* 219f57db62cSArd Biesheuvel * The EFI firmware loader could have placed the kernel image 220f57db62cSArd Biesheuvel * anywhere in memory, but the kernel has restrictions on the 221f57db62cSArd Biesheuvel * max physical address it can run at. Some architectures 222f57db62cSArd Biesheuvel * also have a prefered address, so first try to relocate 223f57db62cSArd Biesheuvel * to the preferred address. If that fails, allocate as low 224f57db62cSArd Biesheuvel * as possible while respecting the required alignment. 225f57db62cSArd Biesheuvel */ 226f57db62cSArd Biesheuvel nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; 227f57db62cSArd Biesheuvel status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, 228f57db62cSArd Biesheuvel EFI_LOADER_DATA, nr_pages, &efi_addr); 229f57db62cSArd Biesheuvel new_addr = efi_addr; 230f57db62cSArd Biesheuvel /* 231f57db62cSArd Biesheuvel * If preferred address allocation failed allocate as low as 232f57db62cSArd Biesheuvel * possible. 233f57db62cSArd Biesheuvel */ 234f57db62cSArd Biesheuvel if (status != EFI_SUCCESS) { 235f57db62cSArd Biesheuvel status = efi_low_alloc_above(alloc_size, alignment, &new_addr, 236f57db62cSArd Biesheuvel min_addr); 237f57db62cSArd Biesheuvel } 238f57db62cSArd Biesheuvel if (status != EFI_SUCCESS) { 239f57db62cSArd Biesheuvel pr_efi_err("Failed to allocate usable memory for kernel.\n"); 240f57db62cSArd Biesheuvel return status; 241f57db62cSArd Biesheuvel } 242f57db62cSArd Biesheuvel 243f57db62cSArd Biesheuvel /* 244f57db62cSArd Biesheuvel * We know source/dest won't overlap since both memory ranges 245f57db62cSArd Biesheuvel * have been allocated by UEFI, so we can safely use memcpy. 246f57db62cSArd Biesheuvel */ 247f57db62cSArd Biesheuvel memcpy((void *)new_addr, (void *)cur_image_addr, image_size); 248f57db62cSArd Biesheuvel 249f57db62cSArd Biesheuvel /* Return the new address of the relocated image. */ 250f57db62cSArd Biesheuvel *image_addr = new_addr; 251f57db62cSArd Biesheuvel 252f57db62cSArd Biesheuvel return status; 253f57db62cSArd Biesheuvel } 254