aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-06-05 16:15:32 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-05 16:15:32 -0400
commitc3c55a07203947f72afa50a3218460b27307c47d (patch)
treede3a2f8adbb3bea4bba1df0f709b0d6c1f4e87b7 /drivers/firmware
parent046f153343e33dcad1be7f6249ea6ff1c6fd9b58 (diff)
parent74bcc2499291d38b6253f9dbd6af33a195222208 (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/Kconfig7
-rw-r--r--drivers/firmware/efi/arm-stub.c278
-rw-r--r--drivers/firmware/efi/efi.c79
-rw-r--r--drivers/firmware/efi/fdt.c285
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
50config 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
50endmenu 57endmenu
51 58
52config UEFI_CPER 59config 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
15static 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
39static 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}
63static efi_status_t efi_file_close(void *handle)
64{
65 efi_file_handle_t *fh = handle;
66
67 return fh->close(handle);
68}
69
70static efi_status_t
71efi_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
79static efi_status_t
80efi_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
106grow:
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
132static 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 */
148static 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 */
161unsigned 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
270fail_free_cmdline:
271 efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr);
272
273fail_free_image:
274 efi_free(sys_table, image_size, *image_addr);
275 efi_free(sys_table, reserve_size, reserve_addr);
276fail:
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
25struct efi __read_mostly efi = { 27struct 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
334static __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
347struct param_info {
348 int verbose;
349 void *params;
350};
351
352static 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
390int __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
13static 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
157fdt_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
182efi_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
258fail_free_mmap:
259 sys_table->boottime->free_pool(memory_map);
260
261fail_free_new_fdt:
262 efi_free(sys_table, new_fdt_size, *new_fdt_addr);
263
264fail:
265 return EFI_LOAD_ERROR;
266}
267
268static 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}