aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware/efi/libstub/fdt.c
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2014-07-02 08:54:43 -0400
committerMatt Fleming <matt.fleming@intel.com>2014-07-18 16:22:19 -0400
commitf4f75ad5741fe0331bbe1f5c42b906cda299f26b (patch)
tree0f06f0b7ecc94bc930a952dd79763adc94b34fda /drivers/firmware/efi/libstub/fdt.c
parentbd669475d14e3279a7f96ed917a82df5da6ad52d (diff)
efi: efistub: Convert into static library
This patch changes both x86 and arm64 efistub implementations from #including shared .c files under drivers/firmware/efi to building shared code as a static library. The x86 code uses a stub built into the boot executable which uncompresses the kernel at boot time. In this case, the library is linked into the decompressor. In the arm64 case, the stub is part of the kernel proper so the library is linked into the kernel proper as well. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'drivers/firmware/efi/libstub/fdt.c')
-rw-r--r--drivers/firmware/efi/libstub/fdt.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c
new file mode 100644
index 000000000000..86d2934840e2
--- /dev/null
+++ b/drivers/firmware/efi/libstub/fdt.c
@@ -0,0 +1,279 @@
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#include <linux/efi.h>
14#include <linux/libfdt.h>
15#include <asm/efi.h>
16
17efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
18 unsigned long orig_fdt_size,
19 void *fdt, int new_fdt_size, char *cmdline_ptr,
20 u64 initrd_addr, u64 initrd_size,
21 efi_memory_desc_t *memory_map,
22 unsigned long map_size, unsigned long desc_size,
23 u32 desc_ver)
24{
25 int node, prev;
26 int status;
27 u32 fdt_val32;
28 u64 fdt_val64;
29
30 /* Do some checks on provided FDT, if it exists*/
31 if (orig_fdt) {
32 if (fdt_check_header(orig_fdt)) {
33 pr_efi_err(sys_table, "Device Tree header not valid!\n");
34 return EFI_LOAD_ERROR;
35 }
36 /*
37 * We don't get the size of the FDT if we get if from a
38 * configuration table.
39 */
40 if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) {
41 pr_efi_err(sys_table, "Truncated device tree! foo!\n");
42 return EFI_LOAD_ERROR;
43 }
44 }
45
46 if (orig_fdt)
47 status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
48 else
49 status = fdt_create_empty_tree(fdt, new_fdt_size);
50
51 if (status != 0)
52 goto fdt_set_fail;
53
54 /*
55 * Delete any memory nodes present. We must delete nodes which
56 * early_init_dt_scan_memory may try to use.
57 */
58 prev = 0;
59 for (;;) {
60 const char *type, *name;
61 int len;
62
63 node = fdt_next_node(fdt, prev, NULL);
64 if (node < 0)
65 break;
66
67 type = fdt_getprop(fdt, node, "device_type", &len);
68 if (type && strncmp(type, "memory", len) == 0) {
69 fdt_del_node(fdt, node);
70 continue;
71 }
72
73 prev = node;
74 }
75
76 node = fdt_subnode_offset(fdt, 0, "chosen");
77 if (node < 0) {
78 node = fdt_add_subnode(fdt, 0, "chosen");
79 if (node < 0) {
80 status = node; /* node is error code when negative */
81 goto fdt_set_fail;
82 }
83 }
84
85 if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
86 status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
87 strlen(cmdline_ptr) + 1);
88 if (status)
89 goto fdt_set_fail;
90 }
91
92 /* Set initrd address/end in device tree, if present */
93 if (initrd_size != 0) {
94 u64 initrd_image_end;
95 u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
96
97 status = fdt_setprop(fdt, node, "linux,initrd-start",
98 &initrd_image_start, sizeof(u64));
99 if (status)
100 goto fdt_set_fail;
101 initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
102 status = fdt_setprop(fdt, node, "linux,initrd-end",
103 &initrd_image_end, sizeof(u64));
104 if (status)
105 goto fdt_set_fail;
106 }
107
108 /* Add FDT entries for EFI runtime services in chosen node. */
109 node = fdt_subnode_offset(fdt, 0, "chosen");
110 fdt_val64 = cpu_to_fdt64((u64)(unsigned long)sys_table);
111 status = fdt_setprop(fdt, node, "linux,uefi-system-table",
112 &fdt_val64, sizeof(fdt_val64));
113 if (status)
114 goto fdt_set_fail;
115
116 fdt_val64 = cpu_to_fdt64((u64)(unsigned long)memory_map);
117 status = fdt_setprop(fdt, node, "linux,uefi-mmap-start",
118 &fdt_val64, sizeof(fdt_val64));
119 if (status)
120 goto fdt_set_fail;
121
122 fdt_val32 = cpu_to_fdt32(map_size);
123 status = fdt_setprop(fdt, node, "linux,uefi-mmap-size",
124 &fdt_val32, sizeof(fdt_val32));
125 if (status)
126 goto fdt_set_fail;
127
128 fdt_val32 = cpu_to_fdt32(desc_size);
129 status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size",
130 &fdt_val32, sizeof(fdt_val32));
131 if (status)
132 goto fdt_set_fail;
133
134 fdt_val32 = cpu_to_fdt32(desc_ver);
135 status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver",
136 &fdt_val32, sizeof(fdt_val32));
137 if (status)
138 goto fdt_set_fail;
139
140 /*
141 * Add kernel version banner so stub/kernel match can be
142 * verified.
143 */
144 status = fdt_setprop_string(fdt, node, "linux,uefi-stub-kern-ver",
145 linux_banner);
146 if (status)
147 goto fdt_set_fail;
148
149 return EFI_SUCCESS;
150
151fdt_set_fail:
152 if (status == -FDT_ERR_NOSPACE)
153 return EFI_BUFFER_TOO_SMALL;
154
155 return EFI_LOAD_ERROR;
156}
157
158#ifndef EFI_FDT_ALIGN
159#define EFI_FDT_ALIGN EFI_PAGE_SIZE
160#endif
161
162/*
163 * Allocate memory for a new FDT, then add EFI, commandline, and
164 * initrd related fields to the FDT. This routine increases the
165 * FDT allocation size until the allocated memory is large
166 * enough. EFI allocations are in EFI_PAGE_SIZE granules,
167 * which are fixed at 4K bytes, so in most cases the first
168 * allocation should succeed.
169 * EFI boot services are exited at the end of this function.
170 * There must be no allocations between the get_memory_map()
171 * call and the exit_boot_services() call, so the exiting of
172 * boot services is very tightly tied to the creation of the FDT
173 * with the final memory map in it.
174 */
175
176efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
177 void *handle,
178 unsigned long *new_fdt_addr,
179 unsigned long max_addr,
180 u64 initrd_addr, u64 initrd_size,
181 char *cmdline_ptr,
182 unsigned long fdt_addr,
183 unsigned long fdt_size)
184{
185 unsigned long map_size, desc_size;
186 u32 desc_ver;
187 unsigned long mmap_key;
188 efi_memory_desc_t *memory_map;
189 unsigned long new_fdt_size;
190 efi_status_t status;
191
192 /*
193 * Estimate size of new FDT, and allocate memory for it. We
194 * will allocate a bigger buffer if this ends up being too
195 * small, so a rough guess is OK here.
196 */
197 new_fdt_size = fdt_size + EFI_PAGE_SIZE;
198 while (1) {
199 status = efi_high_alloc(sys_table, new_fdt_size, EFI_FDT_ALIGN,
200 new_fdt_addr, max_addr);
201 if (status != EFI_SUCCESS) {
202 pr_efi_err(sys_table, "Unable to allocate memory for new device tree.\n");
203 goto fail;
204 }
205
206 /*
207 * Now that we have done our final memory allocation (and free)
208 * we can get the memory map key needed for
209 * exit_boot_services().
210 */
211 status = efi_get_memory_map(sys_table, &memory_map, &map_size,
212 &desc_size, &desc_ver, &mmap_key);
213 if (status != EFI_SUCCESS)
214 goto fail_free_new_fdt;
215
216 status = update_fdt(sys_table,
217 (void *)fdt_addr, fdt_size,
218 (void *)*new_fdt_addr, new_fdt_size,
219 cmdline_ptr, initrd_addr, initrd_size,
220 memory_map, map_size, desc_size, desc_ver);
221
222 /* Succeeding the first time is the expected case. */
223 if (status == EFI_SUCCESS)
224 break;
225
226 if (status == EFI_BUFFER_TOO_SMALL) {
227 /*
228 * We need to allocate more space for the new
229 * device tree, so free existing buffer that is
230 * too small. Also free memory map, as we will need
231 * to get new one that reflects the free/alloc we do
232 * on the device tree buffer.
233 */
234 efi_free(sys_table, new_fdt_size, *new_fdt_addr);
235 sys_table->boottime->free_pool(memory_map);
236 new_fdt_size += EFI_PAGE_SIZE;
237 } else {
238 pr_efi_err(sys_table, "Unable to constuct new device tree.\n");
239 goto fail_free_mmap;
240 }
241 }
242
243 /* Now we are ready to exit_boot_services.*/
244 status = sys_table->boottime->exit_boot_services(handle, mmap_key);
245
246
247 if (status == EFI_SUCCESS)
248 return status;
249
250 pr_efi_err(sys_table, "Exit boot services failed.\n");
251
252fail_free_mmap:
253 sys_table->boottime->free_pool(memory_map);
254
255fail_free_new_fdt:
256 efi_free(sys_table, new_fdt_size, *new_fdt_addr);
257
258fail:
259 return EFI_LOAD_ERROR;
260}
261
262void *get_fdt(efi_system_table_t *sys_table)
263{
264 efi_guid_t fdt_guid = DEVICE_TREE_GUID;
265 efi_config_table_t *tables;
266 void *fdt;
267 int i;
268
269 tables = (efi_config_table_t *) sys_table->tables;
270 fdt = NULL;
271
272 for (i = 0; i < sys_table->nr_tables; i++)
273 if (efi_guidcmp(tables[i].guid, fdt_guid) == 0) {
274 fdt = (void *) tables[i].table;
275 break;
276 }
277
278 return fdt;
279}