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