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