• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013, 2014 Linaro Ltd;  <roy.franz@linaro.org>
3  *
4  * This file implements the EFI boot stub for the arm64 kernel.
5  * 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  */
12 
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 
21 #include <linux/efi.h>
22 #include <asm/efi.h>
23 
24 #include "efistub.h"
25 
handle_kernel_image(efi_system_table_t * sys_table_arg,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)26 efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg,
27 					unsigned long *image_addr,
28 					unsigned long *image_size,
29 					unsigned long *reserve_addr,
30 					unsigned long *reserve_size,
31 					unsigned long dram_base,
32 					efi_loaded_image_t *image)
33 {
34 	efi_status_t status;
35 	unsigned long kernel_size, kernel_memsize = 0;
36 	void *old_image_addr = (void *)*image_addr;
37 	unsigned long preferred_offset;
38 	u64 phys_seed = 0;
39 
40 	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
41 		if (!nokaslr()) {
42 			status = efi_get_random_bytes(sys_table_arg,
43 						      sizeof(phys_seed),
44 						      (u8 *)&phys_seed);
45 			if (status == EFI_NOT_FOUND) {
46 				pr_efi(sys_table_arg, "EFI_RNG_PROTOCOL unavailable, no randomness supplied\n");
47 			} else if (status != EFI_SUCCESS) {
48 				pr_efi_err(sys_table_arg, "efi_get_random_bytes() failed\n");
49 				return status;
50 			}
51 		} else {
52 			pr_efi(sys_table_arg, "KASLR disabled on kernel command line\n");
53 		}
54 	}
55 
56 	/*
57 	 * The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond
58 	 * a 2 MB aligned base, which itself may be lower than dram_base, as
59 	 * long as the resulting offset equals or exceeds it.
60 	 */
61 	preferred_offset = round_down(dram_base, MIN_KIMG_ALIGN) + TEXT_OFFSET;
62 	if (preferred_offset < dram_base)
63 		preferred_offset += MIN_KIMG_ALIGN;
64 
65 	kernel_size = _edata - _text;
66 	kernel_memsize = kernel_size + (_end - _edata);
67 
68 	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) {
69 		/*
70 		 * If KASLR is enabled, and we have some randomness available,
71 		 * locate the kernel at a randomized offset in physical memory.
72 		 */
73 		*reserve_size = kernel_memsize + TEXT_OFFSET;
74 		status = efi_random_alloc(sys_table_arg, *reserve_size,
75 					  MIN_KIMG_ALIGN, reserve_addr,
76 					  phys_seed);
77 
78 		*image_addr = *reserve_addr + TEXT_OFFSET;
79 	} else {
80 		/*
81 		 * Else, try a straight allocation at the preferred offset.
82 		 * This will work around the issue where, if dram_base == 0x0,
83 		 * efi_low_alloc() refuses to allocate at 0x0 (to prevent the
84 		 * address of the allocation to be mistaken for a FAIL return
85 		 * value or a NULL pointer). It will also ensure that, on
86 		 * platforms where the [dram_base, dram_base + TEXT_OFFSET)
87 		 * interval is partially occupied by the firmware (like on APM
88 		 * Mustang), we can still place the kernel at the address
89 		 * 'dram_base + TEXT_OFFSET'.
90 		 */
91 		if (*image_addr == preferred_offset)
92 			return EFI_SUCCESS;
93 
94 		*image_addr = *reserve_addr = preferred_offset;
95 		*reserve_size = round_up(kernel_memsize, EFI_ALLOC_ALIGN);
96 
97 		status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS,
98 					EFI_LOADER_DATA,
99 					*reserve_size / EFI_PAGE_SIZE,
100 					(efi_physical_addr_t *)reserve_addr);
101 	}
102 
103 	if (status != EFI_SUCCESS) {
104 		*reserve_size = kernel_memsize + TEXT_OFFSET;
105 		status = efi_low_alloc(sys_table_arg, *reserve_size,
106 				       MIN_KIMG_ALIGN, reserve_addr);
107 
108 		if (status != EFI_SUCCESS) {
109 			pr_efi_err(sys_table_arg, "Failed to relocate kernel\n");
110 			*reserve_size = 0;
111 			return status;
112 		}
113 		*image_addr = *reserve_addr + TEXT_OFFSET;
114 	}
115 	memcpy((void *)*image_addr, old_image_addr, kernel_size);
116 
117 	return EFI_SUCCESS;
118 }
119