diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-05 16:15:32 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-05 16:15:32 -0400 |
commit | c3c55a07203947f72afa50a3218460b27307c47d (patch) | |
tree | de3a2f8adbb3bea4bba1df0f709b0d6c1f4e87b7 /drivers/firmware | |
parent | 046f153343e33dcad1be7f6249ea6ff1c6fd9b58 (diff) | |
parent | 74bcc2499291d38b6253f9dbd6af33a195222208 (diff) |
Merge branch 'arm64-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip into next
Pull ARM64 EFI update from Peter Anvin:
"By agreement with the ARM64 EFI maintainers, we have agreed to make
-tip the upstream for all EFI patches. That is why this patchset
comes from me :)
This patchset enables EFI stub support for ARM64, like we already have
on x86"
* 'arm64-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
arm64: efi: only attempt efi map setup if booting via EFI
efi/arm64: ignore dtb= when UEFI SecureBoot is enabled
doc: arm64: add description of EFI stub support
arm64: efi: add EFI stub
doc: arm: add UEFI support documentation
arm64: add EFI runtime services
efi: Add shared FDT related functions for ARM/ARM64
arm64: Add function to create identity mappings
efi: add helper function to get UEFI params from FDT
doc: efi-stub.txt updates for ARM
lib: add fdt_empty_tree.c
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/efi/Kconfig | 7 | ||||
-rw-r--r-- | drivers/firmware/efi/arm-stub.c | 278 | ||||
-rw-r--r-- | drivers/firmware/efi/efi.c | 79 | ||||
-rw-r--r-- | drivers/firmware/efi/fdt.c | 285 |
4 files changed, 649 insertions, 0 deletions
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 1e75f48b61f8..d420ae2d3413 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig | |||
@@ -47,6 +47,13 @@ config EFI_RUNTIME_MAP | |||
47 | 47 | ||
48 | See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map. | 48 | See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map. |
49 | 49 | ||
50 | config EFI_PARAMS_FROM_FDT | ||
51 | bool | ||
52 | help | ||
53 | Select this config option from the architecture Kconfig if | ||
54 | the EFI runtime support gets system table address, memory | ||
55 | map address, and other parameters from the device tree. | ||
56 | |||
50 | endmenu | 57 | endmenu |
51 | 58 | ||
52 | config UEFI_CPER | 59 | config UEFI_CPER |
diff --git a/drivers/firmware/efi/arm-stub.c b/drivers/firmware/efi/arm-stub.c new file mode 100644 index 000000000000..41114ce03b01 --- /dev/null +++ b/drivers/firmware/efi/arm-stub.c | |||
@@ -0,0 +1,278 @@ | |||
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 | |||
15 | static int __init efi_secureboot_enabled(efi_system_table_t *sys_table_arg) | ||
16 | { | ||
17 | static efi_guid_t const var_guid __initconst = EFI_GLOBAL_VARIABLE_GUID; | ||
18 | static efi_char16_t const var_name[] __initconst = { | ||
19 | 'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0 }; | ||
20 | |||
21 | efi_get_variable_t *f_getvar = sys_table_arg->runtime->get_variable; | ||
22 | unsigned long size = sizeof(u8); | ||
23 | efi_status_t status; | ||
24 | u8 val; | ||
25 | |||
26 | status = f_getvar((efi_char16_t *)var_name, (efi_guid_t *)&var_guid, | ||
27 | NULL, &size, &val); | ||
28 | |||
29 | switch (status) { | ||
30 | case EFI_SUCCESS: | ||
31 | return val; | ||
32 | case EFI_NOT_FOUND: | ||
33 | return 0; | ||
34 | default: | ||
35 | return 1; | ||
36 | } | ||
37 | } | ||
38 | |||
39 | static efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg, | ||
40 | void *__image, void **__fh) | ||
41 | { | ||
42 | efi_file_io_interface_t *io; | ||
43 | efi_loaded_image_t *image = __image; | ||
44 | efi_file_handle_t *fh; | ||
45 | efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; | ||
46 | efi_status_t status; | ||
47 | void *handle = (void *)(unsigned long)image->device_handle; | ||
48 | |||
49 | status = sys_table_arg->boottime->handle_protocol(handle, | ||
50 | &fs_proto, (void **)&io); | ||
51 | if (status != EFI_SUCCESS) { | ||
52 | efi_printk(sys_table_arg, "Failed to handle fs_proto\n"); | ||
53 | return status; | ||
54 | } | ||
55 | |||
56 | status = io->open_volume(io, &fh); | ||
57 | if (status != EFI_SUCCESS) | ||
58 | efi_printk(sys_table_arg, "Failed to open volume\n"); | ||
59 | |||
60 | *__fh = fh; | ||
61 | return status; | ||
62 | } | ||
63 | static efi_status_t efi_file_close(void *handle) | ||
64 | { | ||
65 | efi_file_handle_t *fh = handle; | ||
66 | |||
67 | return fh->close(handle); | ||
68 | } | ||
69 | |||
70 | static efi_status_t | ||
71 | efi_file_read(void *handle, unsigned long *size, void *addr) | ||
72 | { | ||
73 | efi_file_handle_t *fh = handle; | ||
74 | |||
75 | return fh->read(handle, size, addr); | ||
76 | } | ||
77 | |||
78 | |||
79 | static efi_status_t | ||
80 | efi_file_size(efi_system_table_t *sys_table_arg, void *__fh, | ||
81 | efi_char16_t *filename_16, void **handle, u64 *file_sz) | ||
82 | { | ||
83 | efi_file_handle_t *h, *fh = __fh; | ||
84 | efi_file_info_t *info; | ||
85 | efi_status_t status; | ||
86 | efi_guid_t info_guid = EFI_FILE_INFO_ID; | ||
87 | unsigned long info_sz; | ||
88 | |||
89 | status = fh->open(fh, &h, filename_16, EFI_FILE_MODE_READ, (u64)0); | ||
90 | if (status != EFI_SUCCESS) { | ||
91 | efi_printk(sys_table_arg, "Failed to open file: "); | ||
92 | efi_char16_printk(sys_table_arg, filename_16); | ||
93 | efi_printk(sys_table_arg, "\n"); | ||
94 | return status; | ||
95 | } | ||
96 | |||
97 | *handle = h; | ||
98 | |||
99 | info_sz = 0; | ||
100 | status = h->get_info(h, &info_guid, &info_sz, NULL); | ||
101 | if (status != EFI_BUFFER_TOO_SMALL) { | ||
102 | efi_printk(sys_table_arg, "Failed to get file info size\n"); | ||
103 | return status; | ||
104 | } | ||
105 | |||
106 | grow: | ||
107 | status = sys_table_arg->boottime->allocate_pool(EFI_LOADER_DATA, | ||
108 | info_sz, (void **)&info); | ||
109 | if (status != EFI_SUCCESS) { | ||
110 | efi_printk(sys_table_arg, "Failed to alloc mem for file info\n"); | ||
111 | return status; | ||
112 | } | ||
113 | |||
114 | status = h->get_info(h, &info_guid, &info_sz, | ||
115 | info); | ||
116 | if (status == EFI_BUFFER_TOO_SMALL) { | ||
117 | sys_table_arg->boottime->free_pool(info); | ||
118 | goto grow; | ||
119 | } | ||
120 | |||
121 | *file_sz = info->file_size; | ||
122 | sys_table_arg->boottime->free_pool(info); | ||
123 | |||
124 | if (status != EFI_SUCCESS) | ||
125 | efi_printk(sys_table_arg, "Failed to get initrd info\n"); | ||
126 | |||
127 | return status; | ||
128 | } | ||
129 | |||
130 | |||
131 | |||
132 | static void efi_char16_printk(efi_system_table_t *sys_table_arg, | ||
133 | efi_char16_t *str) | ||
134 | { | ||
135 | struct efi_simple_text_output_protocol *out; | ||
136 | |||
137 | out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out; | ||
138 | out->output_string(out, str); | ||
139 | } | ||
140 | |||
141 | |||
142 | /* | ||
143 | * This function handles the architcture specific differences between arm and | ||
144 | * arm64 regarding where the kernel image must be loaded and any memory that | ||
145 | * must be reserved. On failure it is required to free all | ||
146 | * all allocations it has made. | ||
147 | */ | ||
148 | static efi_status_t handle_kernel_image(efi_system_table_t *sys_table, | ||
149 | unsigned long *image_addr, | ||
150 | unsigned long *image_size, | ||
151 | unsigned long *reserve_addr, | ||
152 | unsigned long *reserve_size, | ||
153 | unsigned long dram_base, | ||
154 | efi_loaded_image_t *image); | ||
155 | /* | ||
156 | * EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint | ||
157 | * that is described in the PE/COFF header. Most of the code is the same | ||
158 | * for both archictectures, with the arch-specific code provided in the | ||
159 | * handle_kernel_image() function. | ||
160 | */ | ||
161 | unsigned long __init efi_entry(void *handle, efi_system_table_t *sys_table, | ||
162 | unsigned long *image_addr) | ||
163 | { | ||
164 | efi_loaded_image_t *image; | ||
165 | efi_status_t status; | ||
166 | unsigned long image_size = 0; | ||
167 | unsigned long dram_base; | ||
168 | /* addr/point and size pairs for memory management*/ | ||
169 | unsigned long initrd_addr; | ||
170 | u64 initrd_size = 0; | ||
171 | unsigned long fdt_addr = 0; /* Original DTB */ | ||
172 | u64 fdt_size = 0; /* We don't get size from configuration table */ | ||
173 | char *cmdline_ptr = NULL; | ||
174 | int cmdline_size = 0; | ||
175 | unsigned long new_fdt_addr; | ||
176 | efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; | ||
177 | unsigned long reserve_addr = 0; | ||
178 | unsigned long reserve_size = 0; | ||
179 | |||
180 | /* Check if we were booted by the EFI firmware */ | ||
181 | if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) | ||
182 | goto fail; | ||
183 | |||
184 | pr_efi(sys_table, "Booting Linux Kernel...\n"); | ||
185 | |||
186 | /* | ||
187 | * Get a handle to the loaded image protocol. This is used to get | ||
188 | * information about the running image, such as size and the command | ||
189 | * line. | ||
190 | */ | ||
191 | status = sys_table->boottime->handle_protocol(handle, | ||
192 | &loaded_image_proto, (void *)&image); | ||
193 | if (status != EFI_SUCCESS) { | ||
194 | pr_efi_err(sys_table, "Failed to get loaded image protocol\n"); | ||
195 | goto fail; | ||
196 | } | ||
197 | |||
198 | dram_base = get_dram_base(sys_table); | ||
199 | if (dram_base == EFI_ERROR) { | ||
200 | pr_efi_err(sys_table, "Failed to find DRAM base\n"); | ||
201 | goto fail; | ||
202 | } | ||
203 | status = handle_kernel_image(sys_table, image_addr, &image_size, | ||
204 | &reserve_addr, | ||
205 | &reserve_size, | ||
206 | dram_base, image); | ||
207 | if (status != EFI_SUCCESS) { | ||
208 | pr_efi_err(sys_table, "Failed to relocate kernel\n"); | ||
209 | goto fail; | ||
210 | } | ||
211 | |||
212 | /* | ||
213 | * Get the command line from EFI, using the LOADED_IMAGE | ||
214 | * protocol. We are going to copy the command line into the | ||
215 | * device tree, so this can be allocated anywhere. | ||
216 | */ | ||
217 | cmdline_ptr = efi_convert_cmdline(sys_table, image, &cmdline_size); | ||
218 | if (!cmdline_ptr) { | ||
219 | pr_efi_err(sys_table, "getting command line via LOADED_IMAGE_PROTOCOL\n"); | ||
220 | goto fail_free_image; | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * Unauthenticated device tree data is a security hazard, so | ||
225 | * ignore 'dtb=' unless UEFI Secure Boot is disabled. | ||
226 | */ | ||
227 | if (efi_secureboot_enabled(sys_table)) { | ||
228 | pr_efi(sys_table, "UEFI Secure Boot is enabled.\n"); | ||
229 | } else { | ||
230 | status = handle_cmdline_files(sys_table, image, cmdline_ptr, | ||
231 | "dtb=", | ||
232 | ~0UL, (unsigned long *)&fdt_addr, | ||
233 | (unsigned long *)&fdt_size); | ||
234 | |||
235 | if (status != EFI_SUCCESS) { | ||
236 | pr_efi_err(sys_table, "Failed to load device tree!\n"); | ||
237 | goto fail_free_cmdline; | ||
238 | } | ||
239 | } | ||
240 | if (!fdt_addr) | ||
241 | /* Look for a device tree configuration table entry. */ | ||
242 | fdt_addr = (uintptr_t)get_fdt(sys_table); | ||
243 | |||
244 | status = handle_cmdline_files(sys_table, image, cmdline_ptr, | ||
245 | "initrd=", dram_base + SZ_512M, | ||
246 | (unsigned long *)&initrd_addr, | ||
247 | (unsigned long *)&initrd_size); | ||
248 | if (status != EFI_SUCCESS) | ||
249 | pr_efi_err(sys_table, "Failed initrd from command line!\n"); | ||
250 | |||
251 | new_fdt_addr = fdt_addr; | ||
252 | status = allocate_new_fdt_and_exit_boot(sys_table, handle, | ||
253 | &new_fdt_addr, dram_base + MAX_FDT_OFFSET, | ||
254 | initrd_addr, initrd_size, cmdline_ptr, | ||
255 | fdt_addr, fdt_size); | ||
256 | |||
257 | /* | ||
258 | * If all went well, we need to return the FDT address to the | ||
259 | * calling function so it can be passed to kernel as part of | ||
260 | * the kernel boot protocol. | ||
261 | */ | ||
262 | if (status == EFI_SUCCESS) | ||
263 | return new_fdt_addr; | ||
264 | |||
265 | pr_efi_err(sys_table, "Failed to update FDT and exit boot services\n"); | ||
266 | |||
267 | efi_free(sys_table, initrd_size, initrd_addr); | ||
268 | efi_free(sys_table, fdt_size, fdt_addr); | ||
269 | |||
270 | fail_free_cmdline: | ||
271 | efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr); | ||
272 | |||
273 | fail_free_image: | ||
274 | efi_free(sys_table, image_size, *image_addr); | ||
275 | efi_free(sys_table, reserve_size, reserve_addr); | ||
276 | fail: | ||
277 | return EFI_ERROR; | ||
278 | } | ||
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index af20f1712337..cd36deb619fa 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c | |||
@@ -20,6 +20,8 @@ | |||
20 | #include <linux/init.h> | 20 | #include <linux/init.h> |
21 | #include <linux/device.h> | 21 | #include <linux/device.h> |
22 | #include <linux/efi.h> | 22 | #include <linux/efi.h> |
23 | #include <linux/of.h> | ||
24 | #include <linux/of_fdt.h> | ||
23 | #include <linux/io.h> | 25 | #include <linux/io.h> |
24 | 26 | ||
25 | struct efi __read_mostly efi = { | 27 | struct efi __read_mostly efi = { |
@@ -318,3 +320,80 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables) | |||
318 | 320 | ||
319 | return 0; | 321 | return 0; |
320 | } | 322 | } |
323 | |||
324 | #ifdef CONFIG_EFI_PARAMS_FROM_FDT | ||
325 | |||
326 | #define UEFI_PARAM(name, prop, field) \ | ||
327 | { \ | ||
328 | { name }, \ | ||
329 | { prop }, \ | ||
330 | offsetof(struct efi_fdt_params, field), \ | ||
331 | FIELD_SIZEOF(struct efi_fdt_params, field) \ | ||
332 | } | ||
333 | |||
334 | static __initdata struct { | ||
335 | const char name[32]; | ||
336 | const char propname[32]; | ||
337 | int offset; | ||
338 | int size; | ||
339 | } dt_params[] = { | ||
340 | UEFI_PARAM("System Table", "linux,uefi-system-table", system_table), | ||
341 | UEFI_PARAM("MemMap Address", "linux,uefi-mmap-start", mmap), | ||
342 | UEFI_PARAM("MemMap Size", "linux,uefi-mmap-size", mmap_size), | ||
343 | UEFI_PARAM("MemMap Desc. Size", "linux,uefi-mmap-desc-size", desc_size), | ||
344 | UEFI_PARAM("MemMap Desc. Version", "linux,uefi-mmap-desc-ver", desc_ver) | ||
345 | }; | ||
346 | |||
347 | struct param_info { | ||
348 | int verbose; | ||
349 | void *params; | ||
350 | }; | ||
351 | |||
352 | static int __init fdt_find_uefi_params(unsigned long node, const char *uname, | ||
353 | int depth, void *data) | ||
354 | { | ||
355 | struct param_info *info = data; | ||
356 | void *prop, *dest; | ||
357 | unsigned long len; | ||
358 | u64 val; | ||
359 | int i; | ||
360 | |||
361 | if (depth != 1 || | ||
362 | (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) | ||
363 | return 0; | ||
364 | |||
365 | pr_info("Getting parameters from FDT:\n"); | ||
366 | |||
367 | for (i = 0; i < ARRAY_SIZE(dt_params); i++) { | ||
368 | prop = of_get_flat_dt_prop(node, dt_params[i].propname, &len); | ||
369 | if (!prop) { | ||
370 | pr_err("Can't find %s in device tree!\n", | ||
371 | dt_params[i].name); | ||
372 | return 0; | ||
373 | } | ||
374 | dest = info->params + dt_params[i].offset; | ||
375 | |||
376 | val = of_read_number(prop, len / sizeof(u32)); | ||
377 | |||
378 | if (dt_params[i].size == sizeof(u32)) | ||
379 | *(u32 *)dest = val; | ||
380 | else | ||
381 | *(u64 *)dest = val; | ||
382 | |||
383 | if (info->verbose) | ||
384 | pr_info(" %s: 0x%0*llx\n", dt_params[i].name, | ||
385 | dt_params[i].size * 2, val); | ||
386 | } | ||
387 | return 1; | ||
388 | } | ||
389 | |||
390 | int __init efi_get_fdt_params(struct efi_fdt_params *params, int verbose) | ||
391 | { | ||
392 | struct param_info info; | ||
393 | |||
394 | info.verbose = verbose; | ||
395 | info.params = params; | ||
396 | |||
397 | return of_scan_flat_dt(fdt_find_uefi_params, &info); | ||
398 | } | ||
399 | #endif /* CONFIG_EFI_PARAMS_FROM_FDT */ | ||
diff --git a/drivers/firmware/efi/fdt.c b/drivers/firmware/efi/fdt.c new file mode 100644 index 000000000000..5c6a8e8a9580 --- /dev/null +++ b/drivers/firmware/efi/fdt.c | |||
@@ -0,0 +1,285 @@ | |||
1 | /* | ||
2 | * FDT related Helper functions used by the EFI stub on multiple | ||
3 | * architectures. This should be #included by the EFI stub | ||
4 | * implementation files. | ||
5 | * | ||
6 | * Copyright 2013 Linaro Limited; author Roy Franz | ||
7 | * | ||
8 | * This file is part of the Linux kernel, and is made available | ||
9 | * under the terms of the GNU General Public License version 2. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, | ||
14 | unsigned long orig_fdt_size, | ||
15 | void *fdt, int new_fdt_size, char *cmdline_ptr, | ||
16 | u64 initrd_addr, u64 initrd_size, | ||
17 | efi_memory_desc_t *memory_map, | ||
18 | unsigned long map_size, unsigned long desc_size, | ||
19 | u32 desc_ver) | ||
20 | { | ||
21 | int node, prev; | ||
22 | int status; | ||
23 | u32 fdt_val32; | ||
24 | u64 fdt_val64; | ||
25 | |||
26 | /* | ||
27 | * Copy definition of linux_banner here. Since this code is | ||
28 | * built as part of the decompressor for ARM v7, pulling | ||
29 | * in version.c where linux_banner is defined for the | ||
30 | * kernel brings other kernel dependencies with it. | ||
31 | */ | ||
32 | const char linux_banner[] = | ||
33 | "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@" | ||
34 | LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n"; | ||
35 | |||
36 | /* Do some checks on provided FDT, if it exists*/ | ||
37 | if (orig_fdt) { | ||
38 | if (fdt_check_header(orig_fdt)) { | ||
39 | pr_efi_err(sys_table, "Device Tree header not valid!\n"); | ||
40 | return EFI_LOAD_ERROR; | ||
41 | } | ||
42 | /* | ||
43 | * We don't get the size of the FDT if we get if from a | ||
44 | * configuration table. | ||
45 | */ | ||
46 | if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) { | ||
47 | pr_efi_err(sys_table, "Truncated device tree! foo!\n"); | ||
48 | return EFI_LOAD_ERROR; | ||
49 | } | ||
50 | } | ||
51 | |||
52 | if (orig_fdt) | ||
53 | status = fdt_open_into(orig_fdt, fdt, new_fdt_size); | ||
54 | else | ||
55 | status = fdt_create_empty_tree(fdt, new_fdt_size); | ||
56 | |||
57 | if (status != 0) | ||
58 | goto fdt_set_fail; | ||
59 | |||
60 | /* | ||
61 | * Delete any memory nodes present. We must delete nodes which | ||
62 | * early_init_dt_scan_memory may try to use. | ||
63 | */ | ||
64 | prev = 0; | ||
65 | for (;;) { | ||
66 | const char *type, *name; | ||
67 | int len; | ||
68 | |||
69 | node = fdt_next_node(fdt, prev, NULL); | ||
70 | if (node < 0) | ||
71 | break; | ||
72 | |||
73 | type = fdt_getprop(fdt, node, "device_type", &len); | ||
74 | if (type && strncmp(type, "memory", len) == 0) { | ||
75 | fdt_del_node(fdt, node); | ||
76 | continue; | ||
77 | } | ||
78 | |||
79 | prev = node; | ||
80 | } | ||
81 | |||
82 | node = fdt_subnode_offset(fdt, 0, "chosen"); | ||
83 | if (node < 0) { | ||
84 | node = fdt_add_subnode(fdt, 0, "chosen"); | ||
85 | if (node < 0) { | ||
86 | status = node; /* node is error code when negative */ | ||
87 | goto fdt_set_fail; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) { | ||
92 | status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr, | ||
93 | strlen(cmdline_ptr) + 1); | ||
94 | if (status) | ||
95 | goto fdt_set_fail; | ||
96 | } | ||
97 | |||
98 | /* Set initrd address/end in device tree, if present */ | ||
99 | if (initrd_size != 0) { | ||
100 | u64 initrd_image_end; | ||
101 | u64 initrd_image_start = cpu_to_fdt64(initrd_addr); | ||
102 | |||
103 | status = fdt_setprop(fdt, node, "linux,initrd-start", | ||
104 | &initrd_image_start, sizeof(u64)); | ||
105 | if (status) | ||
106 | goto fdt_set_fail; | ||
107 | initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size); | ||
108 | status = fdt_setprop(fdt, node, "linux,initrd-end", | ||
109 | &initrd_image_end, sizeof(u64)); | ||
110 | if (status) | ||
111 | goto fdt_set_fail; | ||
112 | } | ||
113 | |||
114 | /* Add FDT entries for EFI runtime services in chosen node. */ | ||
115 | node = fdt_subnode_offset(fdt, 0, "chosen"); | ||
116 | fdt_val64 = cpu_to_fdt64((u64)(unsigned long)sys_table); | ||
117 | status = fdt_setprop(fdt, node, "linux,uefi-system-table", | ||
118 | &fdt_val64, sizeof(fdt_val64)); | ||
119 | if (status) | ||
120 | goto fdt_set_fail; | ||
121 | |||
122 | fdt_val64 = cpu_to_fdt64((u64)(unsigned long)memory_map); | ||
123 | status = fdt_setprop(fdt, node, "linux,uefi-mmap-start", | ||
124 | &fdt_val64, sizeof(fdt_val64)); | ||
125 | if (status) | ||
126 | goto fdt_set_fail; | ||
127 | |||
128 | fdt_val32 = cpu_to_fdt32(map_size); | ||
129 | status = fdt_setprop(fdt, node, "linux,uefi-mmap-size", | ||
130 | &fdt_val32, sizeof(fdt_val32)); | ||
131 | if (status) | ||
132 | goto fdt_set_fail; | ||
133 | |||
134 | fdt_val32 = cpu_to_fdt32(desc_size); | ||
135 | status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size", | ||
136 | &fdt_val32, sizeof(fdt_val32)); | ||
137 | if (status) | ||
138 | goto fdt_set_fail; | ||
139 | |||
140 | fdt_val32 = cpu_to_fdt32(desc_ver); | ||
141 | status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver", | ||
142 | &fdt_val32, sizeof(fdt_val32)); | ||
143 | if (status) | ||
144 | goto fdt_set_fail; | ||
145 | |||
146 | /* | ||
147 | * Add kernel version banner so stub/kernel match can be | ||
148 | * verified. | ||
149 | */ | ||
150 | status = fdt_setprop_string(fdt, node, "linux,uefi-stub-kern-ver", | ||
151 | linux_banner); | ||
152 | if (status) | ||
153 | goto fdt_set_fail; | ||
154 | |||
155 | return EFI_SUCCESS; | ||
156 | |||
157 | fdt_set_fail: | ||
158 | if (status == -FDT_ERR_NOSPACE) | ||
159 | return EFI_BUFFER_TOO_SMALL; | ||
160 | |||
161 | return EFI_LOAD_ERROR; | ||
162 | } | ||
163 | |||
164 | #ifndef EFI_FDT_ALIGN | ||
165 | #define EFI_FDT_ALIGN EFI_PAGE_SIZE | ||
166 | #endif | ||
167 | |||
168 | /* | ||
169 | * Allocate memory for a new FDT, then add EFI, commandline, and | ||
170 | * initrd related fields to the FDT. This routine increases the | ||
171 | * FDT allocation size until the allocated memory is large | ||
172 | * enough. EFI allocations are in EFI_PAGE_SIZE granules, | ||
173 | * which are fixed at 4K bytes, so in most cases the first | ||
174 | * allocation should succeed. | ||
175 | * EFI boot services are exited at the end of this function. | ||
176 | * There must be no allocations between the get_memory_map() | ||
177 | * call and the exit_boot_services() call, so the exiting of | ||
178 | * boot services is very tightly tied to the creation of the FDT | ||
179 | * with the final memory map in it. | ||
180 | */ | ||
181 | |||
182 | efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, | ||
183 | void *handle, | ||
184 | unsigned long *new_fdt_addr, | ||
185 | unsigned long max_addr, | ||
186 | u64 initrd_addr, u64 initrd_size, | ||
187 | char *cmdline_ptr, | ||
188 | unsigned long fdt_addr, | ||
189 | unsigned long fdt_size) | ||
190 | { | ||
191 | unsigned long map_size, desc_size; | ||
192 | u32 desc_ver; | ||
193 | unsigned long mmap_key; | ||
194 | efi_memory_desc_t *memory_map; | ||
195 | unsigned long new_fdt_size; | ||
196 | efi_status_t status; | ||
197 | |||
198 | /* | ||
199 | * Estimate size of new FDT, and allocate memory for it. We | ||
200 | * will allocate a bigger buffer if this ends up being too | ||
201 | * small, so a rough guess is OK here. | ||
202 | */ | ||
203 | new_fdt_size = fdt_size + EFI_PAGE_SIZE; | ||
204 | while (1) { | ||
205 | status = efi_high_alloc(sys_table, new_fdt_size, EFI_FDT_ALIGN, | ||
206 | new_fdt_addr, max_addr); | ||
207 | if (status != EFI_SUCCESS) { | ||
208 | pr_efi_err(sys_table, "Unable to allocate memory for new device tree.\n"); | ||
209 | goto fail; | ||
210 | } | ||
211 | |||
212 | /* | ||
213 | * Now that we have done our final memory allocation (and free) | ||
214 | * we can get the memory map key needed for | ||
215 | * exit_boot_services(). | ||
216 | */ | ||
217 | status = efi_get_memory_map(sys_table, &memory_map, &map_size, | ||
218 | &desc_size, &desc_ver, &mmap_key); | ||
219 | if (status != EFI_SUCCESS) | ||
220 | goto fail_free_new_fdt; | ||
221 | |||
222 | status = update_fdt(sys_table, | ||
223 | (void *)fdt_addr, fdt_size, | ||
224 | (void *)*new_fdt_addr, new_fdt_size, | ||
225 | cmdline_ptr, initrd_addr, initrd_size, | ||
226 | memory_map, map_size, desc_size, desc_ver); | ||
227 | |||
228 | /* Succeeding the first time is the expected case. */ | ||
229 | if (status == EFI_SUCCESS) | ||
230 | break; | ||
231 | |||
232 | if (status == EFI_BUFFER_TOO_SMALL) { | ||
233 | /* | ||
234 | * We need to allocate more space for the new | ||
235 | * device tree, so free existing buffer that is | ||
236 | * too small. Also free memory map, as we will need | ||
237 | * to get new one that reflects the free/alloc we do | ||
238 | * on the device tree buffer. | ||
239 | */ | ||
240 | efi_free(sys_table, new_fdt_size, *new_fdt_addr); | ||
241 | sys_table->boottime->free_pool(memory_map); | ||
242 | new_fdt_size += EFI_PAGE_SIZE; | ||
243 | } else { | ||
244 | pr_efi_err(sys_table, "Unable to constuct new device tree.\n"); | ||
245 | goto fail_free_mmap; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | /* Now we are ready to exit_boot_services.*/ | ||
250 | status = sys_table->boottime->exit_boot_services(handle, mmap_key); | ||
251 | |||
252 | |||
253 | if (status == EFI_SUCCESS) | ||
254 | return status; | ||
255 | |||
256 | pr_efi_err(sys_table, "Exit boot services failed.\n"); | ||
257 | |||
258 | fail_free_mmap: | ||
259 | sys_table->boottime->free_pool(memory_map); | ||
260 | |||
261 | fail_free_new_fdt: | ||
262 | efi_free(sys_table, new_fdt_size, *new_fdt_addr); | ||
263 | |||
264 | fail: | ||
265 | return EFI_LOAD_ERROR; | ||
266 | } | ||
267 | |||
268 | static void *get_fdt(efi_system_table_t *sys_table) | ||
269 | { | ||
270 | efi_guid_t fdt_guid = DEVICE_TREE_GUID; | ||
271 | efi_config_table_t *tables; | ||
272 | void *fdt; | ||
273 | int i; | ||
274 | |||
275 | tables = (efi_config_table_t *) sys_table->tables; | ||
276 | fdt = NULL; | ||
277 | |||
278 | for (i = 0; i < sys_table->nr_tables; i++) | ||
279 | if (efi_guidcmp(tables[i].guid, fdt_guid) == 0) { | ||
280 | fdt = (void *) tables[i].table; | ||
281 | break; | ||
282 | } | ||
283 | |||
284 | return fdt; | ||
285 | } | ||