Commit | Line | Data |
---|---|---|
3c7f2550 MS |
1 | /* |
2 | * EFI stub implementation that is shared by arm and arm64 architectures. | |
3 | * This should be #included by the EFI stub implementation files. | |
4 | * | |
5 | * Copyright (C) 2013,2014 Linaro Limited | |
6 | * Roy Franz <roy.franz@linaro.org | |
7 | * Copyright (C) 2013 Red Hat, Inc. | |
8 | * Mark Salter <msalter@redhat.com> | |
9 | * | |
10 | * This file is part of the Linux kernel, and is made available under the | |
11 | * terms of the GNU General Public License version 2. | |
12 | * | |
13 | */ | |
14 | ||
bd669475 AB |
15 | #include <linux/efi.h> |
16 | #include <asm/efi.h> | |
17 | ||
18 | #include "efistub.h" | |
19 | ||
345c736e AB |
20 | static int __init efi_secureboot_enabled(efi_system_table_t *sys_table_arg) |
21 | { | |
22 | static efi_guid_t const var_guid __initconst = EFI_GLOBAL_VARIABLE_GUID; | |
23 | static efi_char16_t const var_name[] __initconst = { | |
24 | 'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0 }; | |
25 | ||
26 | efi_get_variable_t *f_getvar = sys_table_arg->runtime->get_variable; | |
27 | unsigned long size = sizeof(u8); | |
28 | efi_status_t status; | |
29 | u8 val; | |
30 | ||
31 | status = f_getvar((efi_char16_t *)var_name, (efi_guid_t *)&var_guid, | |
32 | NULL, &size, &val); | |
33 | ||
34 | switch (status) { | |
35 | case EFI_SUCCESS: | |
36 | return val; | |
37 | case EFI_NOT_FOUND: | |
38 | return 0; | |
39 | default: | |
40 | return 1; | |
41 | } | |
42 | } | |
43 | ||
bd669475 AB |
44 | efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg, |
45 | void *__image, void **__fh) | |
3c7f2550 MS |
46 | { |
47 | efi_file_io_interface_t *io; | |
48 | efi_loaded_image_t *image = __image; | |
49 | efi_file_handle_t *fh; | |
50 | efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; | |
51 | efi_status_t status; | |
52 | void *handle = (void *)(unsigned long)image->device_handle; | |
53 | ||
54 | status = sys_table_arg->boottime->handle_protocol(handle, | |
55 | &fs_proto, (void **)&io); | |
56 | if (status != EFI_SUCCESS) { | |
57 | efi_printk(sys_table_arg, "Failed to handle fs_proto\n"); | |
58 | return status; | |
59 | } | |
60 | ||
61 | status = io->open_volume(io, &fh); | |
62 | if (status != EFI_SUCCESS) | |
63 | efi_printk(sys_table_arg, "Failed to open volume\n"); | |
64 | ||
65 | *__fh = fh; | |
66 | return status; | |
67 | } | |
bd669475 AB |
68 | |
69 | efi_status_t efi_file_close(void *handle) | |
3c7f2550 MS |
70 | { |
71 | efi_file_handle_t *fh = handle; | |
72 | ||
73 | return fh->close(handle); | |
74 | } | |
75 | ||
bd669475 | 76 | efi_status_t |
3c7f2550 MS |
77 | efi_file_read(void *handle, unsigned long *size, void *addr) |
78 | { | |
79 | efi_file_handle_t *fh = handle; | |
80 | ||
81 | return fh->read(handle, size, addr); | |
82 | } | |
83 | ||
84 | ||
bd669475 | 85 | efi_status_t |
3c7f2550 MS |
86 | efi_file_size(efi_system_table_t *sys_table_arg, void *__fh, |
87 | efi_char16_t *filename_16, void **handle, u64 *file_sz) | |
88 | { | |
89 | efi_file_handle_t *h, *fh = __fh; | |
90 | efi_file_info_t *info; | |
91 | efi_status_t status; | |
92 | efi_guid_t info_guid = EFI_FILE_INFO_ID; | |
93 | unsigned long info_sz; | |
94 | ||
95 | status = fh->open(fh, &h, filename_16, EFI_FILE_MODE_READ, (u64)0); | |
96 | if (status != EFI_SUCCESS) { | |
97 | efi_printk(sys_table_arg, "Failed to open file: "); | |
98 | efi_char16_printk(sys_table_arg, filename_16); | |
99 | efi_printk(sys_table_arg, "\n"); | |
100 | return status; | |
101 | } | |
102 | ||
103 | *handle = h; | |
104 | ||
105 | info_sz = 0; | |
106 | status = h->get_info(h, &info_guid, &info_sz, NULL); | |
107 | if (status != EFI_BUFFER_TOO_SMALL) { | |
108 | efi_printk(sys_table_arg, "Failed to get file info size\n"); | |
109 | return status; | |
110 | } | |
111 | ||
112 | grow: | |
113 | status = sys_table_arg->boottime->allocate_pool(EFI_LOADER_DATA, | |
114 | info_sz, (void **)&info); | |
115 | if (status != EFI_SUCCESS) { | |
116 | efi_printk(sys_table_arg, "Failed to alloc mem for file info\n"); | |
117 | return status; | |
118 | } | |
119 | ||
120 | status = h->get_info(h, &info_guid, &info_sz, | |
121 | info); | |
122 | if (status == EFI_BUFFER_TOO_SMALL) { | |
123 | sys_table_arg->boottime->free_pool(info); | |
124 | goto grow; | |
125 | } | |
126 | ||
127 | *file_sz = info->file_size; | |
128 | sys_table_arg->boottime->free_pool(info); | |
129 | ||
130 | if (status != EFI_SUCCESS) | |
131 | efi_printk(sys_table_arg, "Failed to get initrd info\n"); | |
132 | ||
133 | return status; | |
134 | } | |
135 | ||
136 | ||
137 | ||
bd669475 | 138 | void efi_char16_printk(efi_system_table_t *sys_table_arg, |
3c7f2550 MS |
139 | efi_char16_t *str) |
140 | { | |
141 | struct efi_simple_text_output_protocol *out; | |
142 | ||
143 | out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out; | |
144 | out->output_string(out, str); | |
145 | } | |
146 | ||
147 | ||
148 | /* | |
149 | * This function handles the architcture specific differences between arm and | |
150 | * arm64 regarding where the kernel image must be loaded and any memory that | |
151 | * must be reserved. On failure it is required to free all | |
152 | * all allocations it has made. | |
153 | */ | |
bd669475 AB |
154 | efi_status_t handle_kernel_image(efi_system_table_t *sys_table, |
155 | unsigned long *image_addr, | |
156 | unsigned long *image_size, | |
157 | unsigned long *reserve_addr, | |
158 | unsigned long *reserve_size, | |
159 | unsigned long dram_base, | |
160 | efi_loaded_image_t *image); | |
3c7f2550 MS |
161 | /* |
162 | * EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint | |
163 | * that is described in the PE/COFF header. Most of the code is the same | |
164 | * for both archictectures, with the arch-specific code provided in the | |
165 | * handle_kernel_image() function. | |
166 | */ | |
167 | unsigned long __init efi_entry(void *handle, efi_system_table_t *sys_table, | |
168 | unsigned long *image_addr) | |
169 | { | |
170 | efi_loaded_image_t *image; | |
171 | efi_status_t status; | |
172 | unsigned long image_size = 0; | |
173 | unsigned long dram_base; | |
174 | /* addr/point and size pairs for memory management*/ | |
175 | unsigned long initrd_addr; | |
176 | u64 initrd_size = 0; | |
345c736e | 177 | unsigned long fdt_addr = 0; /* Original DTB */ |
3c7f2550 MS |
178 | u64 fdt_size = 0; /* We don't get size from configuration table */ |
179 | char *cmdline_ptr = NULL; | |
180 | int cmdline_size = 0; | |
181 | unsigned long new_fdt_addr; | |
182 | efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; | |
183 | unsigned long reserve_addr = 0; | |
184 | unsigned long reserve_size = 0; | |
185 | ||
186 | /* Check if we were booted by the EFI firmware */ | |
187 | if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) | |
188 | goto fail; | |
189 | ||
190 | pr_efi(sys_table, "Booting Linux Kernel...\n"); | |
191 | ||
192 | /* | |
193 | * Get a handle to the loaded image protocol. This is used to get | |
194 | * information about the running image, such as size and the command | |
195 | * line. | |
196 | */ | |
197 | status = sys_table->boottime->handle_protocol(handle, | |
198 | &loaded_image_proto, (void *)&image); | |
199 | if (status != EFI_SUCCESS) { | |
200 | pr_efi_err(sys_table, "Failed to get loaded image protocol\n"); | |
201 | goto fail; | |
202 | } | |
203 | ||
204 | dram_base = get_dram_base(sys_table); | |
205 | if (dram_base == EFI_ERROR) { | |
206 | pr_efi_err(sys_table, "Failed to find DRAM base\n"); | |
207 | goto fail; | |
208 | } | |
209 | status = handle_kernel_image(sys_table, image_addr, &image_size, | |
210 | &reserve_addr, | |
211 | &reserve_size, | |
212 | dram_base, image); | |
213 | if (status != EFI_SUCCESS) { | |
214 | pr_efi_err(sys_table, "Failed to relocate kernel\n"); | |
215 | goto fail; | |
216 | } | |
217 | ||
218 | /* | |
219 | * Get the command line from EFI, using the LOADED_IMAGE | |
220 | * protocol. We are going to copy the command line into the | |
221 | * device tree, so this can be allocated anywhere. | |
222 | */ | |
223 | cmdline_ptr = efi_convert_cmdline(sys_table, image, &cmdline_size); | |
224 | if (!cmdline_ptr) { | |
225 | pr_efi_err(sys_table, "getting command line via LOADED_IMAGE_PROTOCOL\n"); | |
226 | goto fail_free_image; | |
227 | } | |
228 | ||
345c736e AB |
229 | /* |
230 | * Unauthenticated device tree data is a security hazard, so | |
231 | * ignore 'dtb=' unless UEFI Secure Boot is disabled. | |
232 | */ | |
233 | if (efi_secureboot_enabled(sys_table)) { | |
234 | pr_efi(sys_table, "UEFI Secure Boot is enabled.\n"); | |
235 | } else { | |
3c7f2550 MS |
236 | status = handle_cmdline_files(sys_table, image, cmdline_ptr, |
237 | "dtb=", | |
238 | ~0UL, (unsigned long *)&fdt_addr, | |
239 | (unsigned long *)&fdt_size); | |
240 | ||
241 | if (status != EFI_SUCCESS) { | |
242 | pr_efi_err(sys_table, "Failed to load device tree!\n"); | |
243 | goto fail_free_cmdline; | |
244 | } | |
245 | } | |
345c736e AB |
246 | if (!fdt_addr) |
247 | /* Look for a device tree configuration table entry. */ | |
248 | fdt_addr = (uintptr_t)get_fdt(sys_table); | |
3c7f2550 MS |
249 | |
250 | status = handle_cmdline_files(sys_table, image, cmdline_ptr, | |
251 | "initrd=", dram_base + SZ_512M, | |
252 | (unsigned long *)&initrd_addr, | |
253 | (unsigned long *)&initrd_size); | |
254 | if (status != EFI_SUCCESS) | |
255 | pr_efi_err(sys_table, "Failed initrd from command line!\n"); | |
256 | ||
257 | new_fdt_addr = fdt_addr; | |
258 | status = allocate_new_fdt_and_exit_boot(sys_table, handle, | |
259 | &new_fdt_addr, dram_base + MAX_FDT_OFFSET, | |
260 | initrd_addr, initrd_size, cmdline_ptr, | |
261 | fdt_addr, fdt_size); | |
262 | ||
263 | /* | |
264 | * If all went well, we need to return the FDT address to the | |
265 | * calling function so it can be passed to kernel as part of | |
266 | * the kernel boot protocol. | |
267 | */ | |
268 | if (status == EFI_SUCCESS) | |
269 | return new_fdt_addr; | |
270 | ||
271 | pr_efi_err(sys_table, "Failed to update FDT and exit boot services\n"); | |
272 | ||
273 | efi_free(sys_table, initrd_size, initrd_addr); | |
274 | efi_free(sys_table, fdt_size, fdt_addr); | |
275 | ||
276 | fail_free_cmdline: | |
277 | efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr); | |
278 | ||
279 | fail_free_image: | |
280 | efi_free(sys_table, image_size, *image_addr); | |
281 | efi_free(sys_table, reserve_size, reserve_addr); | |
282 | fail: | |
283 | return EFI_ERROR; | |
284 | } |