14febfb8dSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0
2bf457786SArd Biesheuvel /*
3bf457786SArd Biesheuvel  * Copyright (C) 2013, 2014 Linaro Ltd;  <roy.franz@linaro.org>
4bf457786SArd Biesheuvel  *
5bf457786SArd Biesheuvel  * This file implements the EFI boot stub for the arm64 kernel.
6bf457786SArd Biesheuvel  * Adapted from ARM version by Mark Salter <msalter@redhat.com>
7bf457786SArd Biesheuvel  */
80426a4e6SArd Biesheuvel 
90426a4e6SArd Biesheuvel 
10bf457786SArd Biesheuvel #include <linux/efi.h>
11bf457786SArd Biesheuvel #include <asm/efi.h>
12170976bcSMark Rutland #include <asm/memory.h>
136f05106eSArd Biesheuvel #include <asm/sections.h>
1442b55734SArd Biesheuvel #include <asm/sysreg.h>
15bf457786SArd Biesheuvel 
162b5fe07aSArd Biesheuvel #include "efistub.h"
172b5fe07aSArd Biesheuvel 
18cd33a5c1SArd Biesheuvel efi_status_t check_platform_features(void)
1942b55734SArd Biesheuvel {
2042b55734SArd Biesheuvel 	u64 tg;
2142b55734SArd Biesheuvel 
2242b55734SArd Biesheuvel 	/* UEFI mandates support for 4 KB granularity, no need to check */
2342b55734SArd Biesheuvel 	if (IS_ENABLED(CONFIG_ARM64_4K_PAGES))
2442b55734SArd Biesheuvel 		return EFI_SUCCESS;
2542b55734SArd Biesheuvel 
2642b55734SArd Biesheuvel 	tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_TGRAN_SHIFT) & 0xf;
27*26f55386SJames Morse 	if (tg < ID_AA64MMFR0_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_TGRAN_SUPPORTED_MAX) {
2842b55734SArd Biesheuvel 		if (IS_ENABLED(CONFIG_ARM64_64K_PAGES))
29793473c2SArvind Sankar 			efi_err("This 64 KB granular kernel is not supported by your CPU\n");
3042b55734SArd Biesheuvel 		else
31793473c2SArvind Sankar 			efi_err("This 16 KB granular kernel is not supported by your CPU\n");
3242b55734SArd Biesheuvel 		return EFI_UNSUPPORTED;
3342b55734SArd Biesheuvel 	}
3442b55734SArd Biesheuvel 	return EFI_SUCCESS;
3542b55734SArd Biesheuvel }
36bf457786SArd Biesheuvel 
3782046702SArd Biesheuvel /*
387c116db2SWill Deacon  * Although relocatable kernels can fix up the misalignment with respect to
397c116db2SWill Deacon  * MIN_KIMG_ALIGN, the resulting virtual text addresses are subtly out of
407c116db2SWill Deacon  * sync with those recorded in the vmlinux when kaslr is disabled but the
417c116db2SWill Deacon  * image required relocation anyway. Therefore retain 2M alignment unless
427c116db2SWill Deacon  * KASLR is in use.
4382046702SArd Biesheuvel  */
447c116db2SWill Deacon static u64 min_kimg_align(void)
457c116db2SWill Deacon {
467c116db2SWill Deacon 	return efi_nokaslr ? MIN_KIMG_ALIGN : EFI_KIMG_ALIGN;
477c116db2SWill Deacon }
4882046702SArd Biesheuvel 
49cd33a5c1SArd Biesheuvel efi_status_t handle_kernel_image(unsigned long *image_addr,
50bf457786SArd Biesheuvel 				 unsigned long *image_size,
51bf457786SArd Biesheuvel 				 unsigned long *reserve_addr,
52bf457786SArd Biesheuvel 				 unsigned long *reserve_size,
53bf457786SArd Biesheuvel 				 efi_loaded_image_t *image)
54bf457786SArd Biesheuvel {
55bf457786SArd Biesheuvel 	efi_status_t status;
56bf457786SArd Biesheuvel 	unsigned long kernel_size, kernel_memsize = 0;
575d12da9dSArd Biesheuvel 	u32 phys_seed = 0;
582b5fe07aSArd Biesheuvel 
592b5fe07aSArd Biesheuvel 	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
60980771f6SArd Biesheuvel 		if (!efi_nokaslr) {
61cd33a5c1SArd Biesheuvel 			status = efi_get_random_bytes(sizeof(phys_seed),
622b5fe07aSArd Biesheuvel 						      (u8 *)&phys_seed);
632b5fe07aSArd Biesheuvel 			if (status == EFI_NOT_FOUND) {
641c761ee9SMark Brown 				efi_info("EFI_RNG_PROTOCOL unavailable\n");
65d32de913SArd Biesheuvel 				efi_nokaslr = true;
662b5fe07aSArd Biesheuvel 			} else if (status != EFI_SUCCESS) {
671c761ee9SMark Brown 				efi_err("efi_get_random_bytes() failed (0x%lx)\n",
68d32de913SArd Biesheuvel 					status);
69d32de913SArd Biesheuvel 				efi_nokaslr = true;
702b5fe07aSArd Biesheuvel 			}
712b5fe07aSArd Biesheuvel 		} else {
72793473c2SArvind Sankar 			efi_info("KASLR disabled on kernel command line\n");
732b5fe07aSArd Biesheuvel 		}
742b5fe07aSArd Biesheuvel 	}
752dc10ad8SLinus Torvalds 
7682046702SArd Biesheuvel 	if (image->image_base != _text)
77793473c2SArvind Sankar 		efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n");
78bf457786SArd Biesheuvel 
79bf457786SArd Biesheuvel 	kernel_size = _edata - _text;
80bf457786SArd Biesheuvel 	kernel_memsize = kernel_size + (_end - _edata);
81120dc60dSArd Biesheuvel 	*reserve_size = kernel_memsize;
82bf457786SArd Biesheuvel 
832b5fe07aSArd Biesheuvel 	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) {
84bf457786SArd Biesheuvel 		/*
852b5fe07aSArd Biesheuvel 		 * If KASLR is enabled, and we have some randomness available,
862b5fe07aSArd Biesheuvel 		 * locate the kernel at a randomized offset in physical memory.
872b5fe07aSArd Biesheuvel 		 */
887c116db2SWill Deacon 		status = efi_random_alloc(*reserve_size, min_kimg_align(),
895d12da9dSArd Biesheuvel 					  reserve_addr, phys_seed);
902b5fe07aSArd Biesheuvel 	} else {
9182046702SArd Biesheuvel 		status = EFI_OUT_OF_RESOURCES;
922b5fe07aSArd Biesheuvel 	}
932b5fe07aSArd Biesheuvel 
94bf457786SArd Biesheuvel 	if (status != EFI_SUCCESS) {
95120dc60dSArd Biesheuvel 		if (IS_ALIGNED((u64)_text, min_kimg_align())) {
9682046702SArd Biesheuvel 			/*
9782046702SArd Biesheuvel 			 * Just execute from wherever we were loaded by the
9882046702SArd Biesheuvel 			 * UEFI PE/COFF loader if the alignment is suitable.
9982046702SArd Biesheuvel 			 */
10082046702SArd Biesheuvel 			*image_addr = (u64)_text;
10182046702SArd Biesheuvel 			*reserve_size = 0;
10282046702SArd Biesheuvel 			return EFI_SUCCESS;
10382046702SArd Biesheuvel 		}
10482046702SArd Biesheuvel 
105e71356feSArd Biesheuvel 		status = efi_allocate_pages_aligned(*reserve_size, reserve_addr,
1067c116db2SWill Deacon 						    ULONG_MAX, min_kimg_align());
107bf457786SArd Biesheuvel 
108bf457786SArd Biesheuvel 		if (status != EFI_SUCCESS) {
109793473c2SArvind Sankar 			efi_err("Failed to relocate kernel\n");
1102b5fe07aSArd Biesheuvel 			*reserve_size = 0;
111bf457786SArd Biesheuvel 			return status;
112bf457786SArd Biesheuvel 		}
113bf457786SArd Biesheuvel 	}
114c2136dceSArd Biesheuvel 
115120dc60dSArd Biesheuvel 	*image_addr = *reserve_addr;
116c2136dceSArd Biesheuvel 	memcpy((void *)*image_addr, _text, kernel_size);
117bf457786SArd Biesheuvel 
118bf457786SArd Biesheuvel 	return EFI_SUCCESS;
119bf457786SArd Biesheuvel }
120