1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2013, 2014 Linaro Ltd; <roy.franz@linaro.org> 4 * 5 * This file implements the EFI boot stub for the arm64 kernel. 6 * Adapted from ARM version by Mark Salter <msalter@redhat.com> 7 */ 8 9 10 #include <linux/efi.h> 11 #include <asm/efi.h> 12 #include <asm/memory.h> 13 #include <asm/sections.h> 14 #include <asm/sysreg.h> 15 16 #include "efistub.h" 17 18 efi_status_t check_platform_features(void) 19 { 20 u64 tg; 21 22 /* UEFI mandates support for 4 KB granularity, no need to check */ 23 if (IS_ENABLED(CONFIG_ARM64_4K_PAGES)) 24 return EFI_SUCCESS; 25 26 tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_TGRAN_SHIFT) & 0xf; 27 if (tg != ID_AA64MMFR0_TGRAN_SUPPORTED) { 28 if (IS_ENABLED(CONFIG_ARM64_64K_PAGES)) 29 pr_efi_err("This 64 KB granular kernel is not supported by your CPU\n"); 30 else 31 pr_efi_err("This 16 KB granular kernel is not supported by your CPU\n"); 32 return EFI_UNSUPPORTED; 33 } 34 return EFI_SUCCESS; 35 } 36 37 efi_status_t handle_kernel_image(unsigned long *image_addr, 38 unsigned long *image_size, 39 unsigned long *reserve_addr, 40 unsigned long *reserve_size, 41 unsigned long dram_base, 42 efi_loaded_image_t *image) 43 { 44 efi_status_t status; 45 unsigned long kernel_size, kernel_memsize = 0; 46 unsigned long preferred_offset; 47 u64 phys_seed = 0; 48 49 if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { 50 if (!nokaslr()) { 51 status = efi_get_random_bytes(sizeof(phys_seed), 52 (u8 *)&phys_seed); 53 if (status == EFI_NOT_FOUND) { 54 pr_efi("EFI_RNG_PROTOCOL unavailable, no randomness supplied\n"); 55 } else if (status != EFI_SUCCESS) { 56 pr_efi_err("efi_get_random_bytes() failed\n"); 57 return status; 58 } 59 } else { 60 pr_efi("KASLR disabled on kernel command line\n"); 61 } 62 } 63 64 /* 65 * The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond 66 * a 2 MB aligned base, which itself may be lower than dram_base, as 67 * long as the resulting offset equals or exceeds it. 68 */ 69 preferred_offset = round_down(dram_base, MIN_KIMG_ALIGN) + TEXT_OFFSET; 70 if (preferred_offset < dram_base) 71 preferred_offset += MIN_KIMG_ALIGN; 72 73 kernel_size = _edata - _text; 74 kernel_memsize = kernel_size + (_end - _edata); 75 76 if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) { 77 /* 78 * Produce a displacement in the interval [0, MIN_KIMG_ALIGN) 79 * that doesn't violate this kernel's de-facto alignment 80 * constraints. 81 */ 82 u32 mask = (MIN_KIMG_ALIGN - 1) & ~(EFI_KIMG_ALIGN - 1); 83 u32 offset = (phys_seed >> 32) & mask; 84 85 /* 86 * With CONFIG_RANDOMIZE_TEXT_OFFSET=y, TEXT_OFFSET may not 87 * be a multiple of EFI_KIMG_ALIGN, and we must ensure that 88 * we preserve the misalignment of 'offset' relative to 89 * EFI_KIMG_ALIGN so that statically allocated objects whose 90 * alignment exceeds PAGE_SIZE appear correctly aligned in 91 * memory. 92 */ 93 offset |= TEXT_OFFSET % EFI_KIMG_ALIGN; 94 95 /* 96 * If KASLR is enabled, and we have some randomness available, 97 * locate the kernel at a randomized offset in physical memory. 98 */ 99 *reserve_size = kernel_memsize + offset; 100 status = efi_random_alloc(*reserve_size, 101 MIN_KIMG_ALIGN, reserve_addr, 102 (u32)phys_seed); 103 104 *image_addr = *reserve_addr + offset; 105 } else { 106 /* 107 * Else, try a straight allocation at the preferred offset. 108 * This will work around the issue where, if dram_base == 0x0, 109 * efi_low_alloc() refuses to allocate at 0x0 (to prevent the 110 * address of the allocation to be mistaken for a FAIL return 111 * value or a NULL pointer). It will also ensure that, on 112 * platforms where the [dram_base, dram_base + TEXT_OFFSET) 113 * interval is partially occupied by the firmware (like on APM 114 * Mustang), we can still place the kernel at the address 115 * 'dram_base + TEXT_OFFSET'. 116 */ 117 *image_addr = (unsigned long)_text; 118 if (*image_addr == preferred_offset) 119 return EFI_SUCCESS; 120 121 *image_addr = *reserve_addr = preferred_offset; 122 *reserve_size = round_up(kernel_memsize, EFI_ALLOC_ALIGN); 123 124 status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, 125 EFI_LOADER_DATA, 126 *reserve_size / EFI_PAGE_SIZE, 127 (efi_physical_addr_t *)reserve_addr); 128 } 129 130 if (status != EFI_SUCCESS) { 131 *reserve_size = kernel_memsize + TEXT_OFFSET; 132 status = efi_low_alloc(*reserve_size, 133 MIN_KIMG_ALIGN, reserve_addr); 134 135 if (status != EFI_SUCCESS) { 136 pr_efi_err("Failed to relocate kernel\n"); 137 *reserve_size = 0; 138 return status; 139 } 140 *image_addr = *reserve_addr + TEXT_OFFSET; 141 } 142 143 if (image->image_base != _text) 144 pr_efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n"); 145 146 memcpy((void *)*image_addr, _text, kernel_size); 147 148 return EFI_SUCCESS; 149 } 150