Ard Biesheuvel | 4febfb8 | 2019-02-02 10:41:15 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
Mark Salter | 3c7f255 | 2014-04-15 22:47:52 -0400 | [diff] [blame] | 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> |
Mark Salter | 3c7f255 | 2014-04-15 22:47:52 -0400 | [diff] [blame] | 7 | */ |
Ard Biesheuvel | 0426a4e | 2017-08-18 20:49:36 +0100 | [diff] [blame] | 8 | |
Ard Biesheuvel | 0426a4e | 2017-08-18 20:49:36 +0100 | [diff] [blame] | 9 | |
Mark Salter | 3c7f255 | 2014-04-15 22:47:52 -0400 | [diff] [blame] | 10 | #include <linux/efi.h> |
Ard Biesheuvel | a13b007 | 2014-07-02 14:54:41 +0200 | [diff] [blame] | 11 | #include <asm/efi.h> |
Mark Rutland | 170976b | 2017-07-14 15:54:36 +0100 | [diff] [blame] | 12 | #include <asm/memory.h> |
Ard Biesheuvel | 6f05106e | 2020-02-10 17:02:32 +0100 | [diff] [blame] | 13 | #include <asm/sections.h> |
Ard Biesheuvel | 42b5573 | 2016-02-17 12:36:02 +0000 | [diff] [blame] | 14 | #include <asm/sysreg.h> |
Mark Salter | 3c7f255 | 2014-04-15 22:47:52 -0400 | [diff] [blame] | 15 | |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 16 | #include "efistub.h" |
| 17 | |
Ard Biesheuvel | cd33a5c | 2019-12-24 16:10:19 +0100 | [diff] [blame] | 18 | efi_status_t check_platform_features(void) |
Ard Biesheuvel | 42b5573 | 2016-02-17 12:36:02 +0000 | [diff] [blame] | 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; |
James Morse | 26f5538 | 2021-03-10 11:23:10 +0530 | [diff] [blame] | 27 | if (tg < ID_AA64MMFR0_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_TGRAN_SUPPORTED_MAX) { |
Ard Biesheuvel | 42b5573 | 2016-02-17 12:36:02 +0000 | [diff] [blame] | 28 | if (IS_ENABLED(CONFIG_ARM64_64K_PAGES)) |
Arvind Sankar | 793473c | 2020-04-30 14:28:35 -0400 | [diff] [blame] | 29 | efi_err("This 64 KB granular kernel is not supported by your CPU\n"); |
Ard Biesheuvel | 42b5573 | 2016-02-17 12:36:02 +0000 | [diff] [blame] | 30 | else |
Arvind Sankar | 793473c | 2020-04-30 14:28:35 -0400 | [diff] [blame] | 31 | efi_err("This 16 KB granular kernel is not supported by your CPU\n"); |
Ard Biesheuvel | 42b5573 | 2016-02-17 12:36:02 +0000 | [diff] [blame] | 32 | return EFI_UNSUPPORTED; |
| 33 | } |
| 34 | return EFI_SUCCESS; |
| 35 | } |
Mark Salter | 3c7f255 | 2014-04-15 22:47:52 -0400 | [diff] [blame] | 36 | |
Ard Biesheuvel | 8204670 | 2020-03-27 17:23:52 +0100 | [diff] [blame] | 37 | /* |
Ard Biesheuvel | 5b94046 | 2021-07-26 11:38:41 +0200 | [diff] [blame] | 38 | * Distro versions of GRUB may ignore the BSS allocation entirely (i.e., fail |
| 39 | * to provide space, and fail to zero it). Check for this condition by double |
| 40 | * checking that the first and the last byte of the image are covered by the |
| 41 | * same EFI memory map entry. |
| 42 | */ |
| 43 | static bool check_image_region(u64 base, u64 size) |
| 44 | { |
| 45 | unsigned long map_size, desc_size, buff_size; |
| 46 | efi_memory_desc_t *memory_map; |
| 47 | struct efi_boot_memmap map; |
| 48 | efi_status_t status; |
| 49 | bool ret = false; |
| 50 | int map_offset; |
| 51 | |
| 52 | map.map = &memory_map; |
| 53 | map.map_size = &map_size; |
| 54 | map.desc_size = &desc_size; |
| 55 | map.desc_ver = NULL; |
| 56 | map.key_ptr = NULL; |
| 57 | map.buff_size = &buff_size; |
| 58 | |
| 59 | status = efi_get_memory_map(&map); |
| 60 | if (status != EFI_SUCCESS) |
| 61 | return false; |
| 62 | |
| 63 | for (map_offset = 0; map_offset < map_size; map_offset += desc_size) { |
| 64 | efi_memory_desc_t *md = (void *)memory_map + map_offset; |
| 65 | u64 end = md->phys_addr + md->num_pages * EFI_PAGE_SIZE; |
| 66 | |
| 67 | /* |
| 68 | * Find the region that covers base, and return whether |
| 69 | * it covers base+size bytes. |
| 70 | */ |
| 71 | if (base >= md->phys_addr && base < end) { |
| 72 | ret = (base + size) <= end; |
| 73 | break; |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | efi_bs_call(free_pool, memory_map); |
| 78 | |
| 79 | return ret; |
| 80 | } |
| 81 | |
Ard Biesheuvel | cd33a5c | 2019-12-24 16:10:19 +0100 | [diff] [blame] | 82 | efi_status_t handle_kernel_image(unsigned long *image_addr, |
Ard Biesheuvel | dae31fd | 2016-02-17 12:35:57 +0000 | [diff] [blame] | 83 | unsigned long *image_size, |
| 84 | unsigned long *reserve_addr, |
| 85 | unsigned long *reserve_size, |
Ard Biesheuvel | dae31fd | 2016-02-17 12:35:57 +0000 | [diff] [blame] | 86 | efi_loaded_image_t *image) |
Mark Salter | 3c7f255 | 2014-04-15 22:47:52 -0400 | [diff] [blame] | 87 | { |
| 88 | efi_status_t status; |
| 89 | unsigned long kernel_size, kernel_memsize = 0; |
Ard Biesheuvel | 5d12da9 | 2020-04-13 15:36:37 +0200 | [diff] [blame] | 90 | u32 phys_seed = 0; |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 91 | |
Ard Biesheuvel | 3a26242 | 2021-07-22 12:10:31 +0200 | [diff] [blame] | 92 | /* |
| 93 | * Although relocatable kernels can fix up the misalignment with |
| 94 | * respect to MIN_KIMG_ALIGN, the resulting virtual text addresses are |
| 95 | * subtly out of sync with those recorded in the vmlinux when kaslr is |
| 96 | * disabled but the image required relocation anyway. Therefore retain |
| 97 | * 2M alignment if KASLR was explicitly disabled, even if it was not |
| 98 | * going to be activated to begin with. |
| 99 | */ |
| 100 | u64 min_kimg_align = efi_nokaslr ? MIN_KIMG_ALIGN : EFI_KIMG_ALIGN; |
| 101 | |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 102 | if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { |
Ard Biesheuvel | 980771f | 2020-04-16 18:45:24 +0200 | [diff] [blame] | 103 | if (!efi_nokaslr) { |
Ard Biesheuvel | cd33a5c | 2019-12-24 16:10:19 +0100 | [diff] [blame] | 104 | status = efi_get_random_bytes(sizeof(phys_seed), |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 105 | (u8 *)&phys_seed); |
| 106 | if (status == EFI_NOT_FOUND) { |
Mark Brown | 1c761ee | 2021-01-20 16:38:10 +0000 | [diff] [blame] | 107 | efi_info("EFI_RNG_PROTOCOL unavailable\n"); |
Ard Biesheuvel | d32de91 | 2020-09-26 10:52:42 +0200 | [diff] [blame] | 108 | efi_nokaslr = true; |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 109 | } else if (status != EFI_SUCCESS) { |
Mark Brown | 1c761ee | 2021-01-20 16:38:10 +0000 | [diff] [blame] | 110 | efi_err("efi_get_random_bytes() failed (0x%lx)\n", |
Ard Biesheuvel | d32de91 | 2020-09-26 10:52:42 +0200 | [diff] [blame] | 111 | status); |
| 112 | efi_nokaslr = true; |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 113 | } |
| 114 | } else { |
Arvind Sankar | 793473c | 2020-04-30 14:28:35 -0400 | [diff] [blame] | 115 | efi_info("KASLR disabled on kernel command line\n"); |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 116 | } |
| 117 | } |
Ard Biesheuvel | 73effcc | 2015-10-29 15:07:25 +0100 | [diff] [blame] | 118 | |
Ard Biesheuvel | 8204670 | 2020-03-27 17:23:52 +0100 | [diff] [blame] | 119 | if (image->image_base != _text) |
Arvind Sankar | 793473c | 2020-04-30 14:28:35 -0400 | [diff] [blame] | 120 | efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n"); |
Mark Salter | 3c7f255 | 2014-04-15 22:47:52 -0400 | [diff] [blame] | 121 | |
Ard Biesheuvel | c32ac11 | 2021-07-26 16:31:44 +0200 | [diff] [blame] | 122 | if (!IS_ALIGNED((u64)_text, EFI_KIMG_ALIGN)) |
| 123 | efi_err("FIRMWARE BUG: kernel image not aligned on %ldk boundary\n", |
| 124 | EFI_KIMG_ALIGN >> 10); |
| 125 | |
Mark Salter | 3c7f255 | 2014-04-15 22:47:52 -0400 | [diff] [blame] | 126 | kernel_size = _edata - _text; |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 127 | kernel_memsize = kernel_size + (_end - _edata); |
Ard Biesheuvel | 120dc60 | 2020-08-25 15:54:40 +0200 | [diff] [blame] | 128 | *reserve_size = kernel_memsize; |
Ard Biesheuvel | e38457c | 2015-07-24 12:38:27 +0100 | [diff] [blame] | 129 | |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 130 | if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) { |
Ard Biesheuvel | e38457c | 2015-07-24 12:38:27 +0100 | [diff] [blame] | 131 | /* |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 132 | * If KASLR is enabled, and we have some randomness available, |
| 133 | * locate the kernel at a randomized offset in physical memory. |
| 134 | */ |
Ard Biesheuvel | 3a26242 | 2021-07-22 12:10:31 +0200 | [diff] [blame] | 135 | status = efi_random_alloc(*reserve_size, min_kimg_align, |
Ard Biesheuvel | 5d12da9 | 2020-04-13 15:36:37 +0200 | [diff] [blame] | 136 | reserve_addr, phys_seed); |
Ard Biesheuvel | ff80ef5 | 2021-07-26 16:24:01 +0200 | [diff] [blame] | 137 | if (status != EFI_SUCCESS) |
| 138 | efi_warn("efi_random_alloc() failed: 0x%lx\n", status); |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 139 | } else { |
Ard Biesheuvel | 8204670 | 2020-03-27 17:23:52 +0100 | [diff] [blame] | 140 | status = EFI_OUT_OF_RESOURCES; |
Mark Salter | 3c7f255 | 2014-04-15 22:47:52 -0400 | [diff] [blame] | 141 | } |
| 142 | |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 143 | if (status != EFI_SUCCESS) { |
Ard Biesheuvel | 5b94046 | 2021-07-26 11:38:41 +0200 | [diff] [blame] | 144 | if (!check_image_region((u64)_text, kernel_memsize)) { |
| 145 | efi_err("FIRMWARE BUG: Image BSS overlaps adjacent EFI memory region\n"); |
Ard Biesheuvel | 3a26242 | 2021-07-22 12:10:31 +0200 | [diff] [blame] | 146 | } else if (IS_ALIGNED((u64)_text, min_kimg_align)) { |
Ard Biesheuvel | 8204670 | 2020-03-27 17:23:52 +0100 | [diff] [blame] | 147 | /* |
| 148 | * Just execute from wherever we were loaded by the |
| 149 | * UEFI PE/COFF loader if the alignment is suitable. |
| 150 | */ |
| 151 | *image_addr = (u64)_text; |
| 152 | *reserve_size = 0; |
| 153 | return EFI_SUCCESS; |
| 154 | } |
| 155 | |
Ard Biesheuvel | e71356f | 2020-03-31 10:59:39 +0200 | [diff] [blame] | 156 | status = efi_allocate_pages_aligned(*reserve_size, reserve_addr, |
Ard Biesheuvel | 3a26242 | 2021-07-22 12:10:31 +0200 | [diff] [blame] | 157 | ULONG_MAX, min_kimg_align); |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 158 | |
| 159 | if (status != EFI_SUCCESS) { |
Arvind Sankar | 793473c | 2020-04-30 14:28:35 -0400 | [diff] [blame] | 160 | efi_err("Failed to relocate kernel\n"); |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 161 | *reserve_size = 0; |
| 162 | return status; |
| 163 | } |
Ard Biesheuvel | 2b5fe07 | 2016-01-26 14:48:29 +0100 | [diff] [blame] | 164 | } |
Ard Biesheuvel | c2136dce | 2020-03-29 10:05:43 +0200 | [diff] [blame] | 165 | |
Ard Biesheuvel | 120dc60 | 2020-08-25 15:54:40 +0200 | [diff] [blame] | 166 | *image_addr = *reserve_addr; |
Ard Biesheuvel | c2136dce | 2020-03-29 10:05:43 +0200 | [diff] [blame] | 167 | memcpy((void *)*image_addr, _text, kernel_size); |
Mark Salter | 3c7f255 | 2014-04-15 22:47:52 -0400 | [diff] [blame] | 168 | |
| 169 | return EFI_SUCCESS; |
| 170 | } |