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