1bf457786SArd Biesheuvel /* 2bf457786SArd Biesheuvel * Copyright (C) 2013, 2014 Linaro Ltd; <roy.franz@linaro.org> 3bf457786SArd Biesheuvel * 4bf457786SArd Biesheuvel * This file implements the EFI boot stub for the arm64 kernel. 5bf457786SArd Biesheuvel * Adapted from ARM version by Mark Salter <msalter@redhat.com> 6bf457786SArd Biesheuvel * 7bf457786SArd Biesheuvel * This program is free software; you can redistribute it and/or modify 8bf457786SArd Biesheuvel * it under the terms of the GNU General Public License version 2 as 9bf457786SArd Biesheuvel * published by the Free Software Foundation. 10bf457786SArd Biesheuvel * 11bf457786SArd Biesheuvel */ 12bf457786SArd Biesheuvel #include <linux/efi.h> 13bf457786SArd Biesheuvel #include <asm/efi.h> 14bf457786SArd Biesheuvel #include <asm/sections.h> 15bf457786SArd Biesheuvel 162b5fe07aSArd Biesheuvel #include "efistub.h" 172b5fe07aSArd Biesheuvel 182b5fe07aSArd Biesheuvel extern bool __nokaslr; 192b5fe07aSArd Biesheuvel 20bf457786SArd Biesheuvel efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg, 21bf457786SArd Biesheuvel unsigned long *image_addr, 22bf457786SArd Biesheuvel unsigned long *image_size, 23bf457786SArd Biesheuvel unsigned long *reserve_addr, 24bf457786SArd Biesheuvel unsigned long *reserve_size, 25bf457786SArd Biesheuvel unsigned long dram_base, 26bf457786SArd Biesheuvel efi_loaded_image_t *image) 27bf457786SArd Biesheuvel { 28bf457786SArd Biesheuvel efi_status_t status; 29bf457786SArd Biesheuvel unsigned long kernel_size, kernel_memsize = 0; 30bf457786SArd Biesheuvel void *old_image_addr = (void *)*image_addr; 312dc10ad8SLinus Torvalds unsigned long preferred_offset; 322b5fe07aSArd Biesheuvel u64 phys_seed = 0; 332b5fe07aSArd Biesheuvel 342b5fe07aSArd Biesheuvel if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { 352b5fe07aSArd Biesheuvel if (!__nokaslr) { 362b5fe07aSArd Biesheuvel status = efi_get_random_bytes(sys_table_arg, 372b5fe07aSArd Biesheuvel sizeof(phys_seed), 382b5fe07aSArd Biesheuvel (u8 *)&phys_seed); 392b5fe07aSArd Biesheuvel if (status == EFI_NOT_FOUND) { 402b5fe07aSArd Biesheuvel pr_efi(sys_table_arg, "EFI_RNG_PROTOCOL unavailable, no randomness supplied\n"); 412b5fe07aSArd Biesheuvel } else if (status != EFI_SUCCESS) { 422b5fe07aSArd Biesheuvel pr_efi_err(sys_table_arg, "efi_get_random_bytes() failed\n"); 432b5fe07aSArd Biesheuvel return status; 442b5fe07aSArd Biesheuvel } 452b5fe07aSArd Biesheuvel } else { 462b5fe07aSArd Biesheuvel pr_efi(sys_table_arg, "KASLR disabled on kernel command line\n"); 472b5fe07aSArd Biesheuvel } 482b5fe07aSArd Biesheuvel } 492dc10ad8SLinus Torvalds 502dc10ad8SLinus Torvalds /* 512dc10ad8SLinus Torvalds * The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond 522dc10ad8SLinus Torvalds * a 2 MB aligned base, which itself may be lower than dram_base, as 532dc10ad8SLinus Torvalds * long as the resulting offset equals or exceeds it. 542dc10ad8SLinus Torvalds */ 552b5fe07aSArd Biesheuvel preferred_offset = round_down(dram_base, MIN_KIMG_ALIGN) + TEXT_OFFSET; 562dc10ad8SLinus Torvalds if (preferred_offset < dram_base) 572b5fe07aSArd Biesheuvel preferred_offset += MIN_KIMG_ALIGN; 58bf457786SArd Biesheuvel 59bf457786SArd Biesheuvel kernel_size = _edata - _text; 60bf457786SArd Biesheuvel kernel_memsize = kernel_size + (_end - _edata); 61bf457786SArd Biesheuvel 622b5fe07aSArd Biesheuvel if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) { 63bf457786SArd Biesheuvel /* 642b5fe07aSArd Biesheuvel * If KASLR is enabled, and we have some randomness available, 652b5fe07aSArd Biesheuvel * locate the kernel at a randomized offset in physical memory. 662b5fe07aSArd Biesheuvel */ 672b5fe07aSArd Biesheuvel *reserve_size = kernel_memsize + TEXT_OFFSET; 682b5fe07aSArd Biesheuvel status = efi_random_alloc(sys_table_arg, *reserve_size, 692b5fe07aSArd Biesheuvel MIN_KIMG_ALIGN, reserve_addr, 702b5fe07aSArd Biesheuvel phys_seed); 712b5fe07aSArd Biesheuvel 722b5fe07aSArd Biesheuvel *image_addr = *reserve_addr + TEXT_OFFSET; 732b5fe07aSArd Biesheuvel } else { 742b5fe07aSArd Biesheuvel /* 752b5fe07aSArd Biesheuvel * Else, try a straight allocation at the preferred offset. 76bf457786SArd Biesheuvel * This will work around the issue where, if dram_base == 0x0, 77bf457786SArd Biesheuvel * efi_low_alloc() refuses to allocate at 0x0 (to prevent the 78bf457786SArd Biesheuvel * address of the allocation to be mistaken for a FAIL return 79bf457786SArd Biesheuvel * value or a NULL pointer). It will also ensure that, on 80bf457786SArd Biesheuvel * platforms where the [dram_base, dram_base + TEXT_OFFSET) 81bf457786SArd Biesheuvel * interval is partially occupied by the firmware (like on APM 82bf457786SArd Biesheuvel * Mustang), we can still place the kernel at the address 83bf457786SArd Biesheuvel * 'dram_base + TEXT_OFFSET'. 84bf457786SArd Biesheuvel */ 852b5fe07aSArd Biesheuvel if (*image_addr == preferred_offset) 862b5fe07aSArd Biesheuvel return EFI_SUCCESS; 872b5fe07aSArd Biesheuvel 882dc10ad8SLinus Torvalds *image_addr = *reserve_addr = preferred_offset; 892b5fe07aSArd Biesheuvel *reserve_size = round_up(kernel_memsize, EFI_ALLOC_ALIGN); 902b5fe07aSArd Biesheuvel 91bf457786SArd Biesheuvel status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS, 922b5fe07aSArd Biesheuvel EFI_LOADER_DATA, 932b5fe07aSArd Biesheuvel *reserve_size / EFI_PAGE_SIZE, 94bf457786SArd Biesheuvel (efi_physical_addr_t *)reserve_addr); 952b5fe07aSArd Biesheuvel } 962b5fe07aSArd Biesheuvel 97bf457786SArd Biesheuvel if (status != EFI_SUCCESS) { 982b5fe07aSArd Biesheuvel *reserve_size = kernel_memsize + TEXT_OFFSET; 992b5fe07aSArd Biesheuvel status = efi_low_alloc(sys_table_arg, *reserve_size, 1002b5fe07aSArd Biesheuvel MIN_KIMG_ALIGN, reserve_addr); 101bf457786SArd Biesheuvel 102bf457786SArd Biesheuvel if (status != EFI_SUCCESS) { 103bf457786SArd Biesheuvel pr_efi_err(sys_table_arg, "Failed to relocate kernel\n"); 1042b5fe07aSArd Biesheuvel *reserve_size = 0; 105bf457786SArd Biesheuvel return status; 106bf457786SArd Biesheuvel } 107bf457786SArd Biesheuvel *image_addr = *reserve_addr + TEXT_OFFSET; 108bf457786SArd Biesheuvel } 109bf457786SArd Biesheuvel memcpy((void *)*image_addr, old_image_addr, kernel_size); 110bf457786SArd Biesheuvel 111bf457786SArd Biesheuvel return EFI_SUCCESS; 112bf457786SArd Biesheuvel } 113