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.
6 * Copyright 2013 Linaro Limited; author Roy Franz
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.
13 #include <linux/efi.h>
14 #include <linux/libfdt.h>
17 efi_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
,
30 /* Do some checks on provided FDT, if it exists*/
32 if (fdt_check_header(orig_fdt
)) {
33 pr_efi_err(sys_table
, "Device Tree header not valid!\n");
34 return EFI_LOAD_ERROR
;
37 * We don't get the size of the FDT if we get if from a
38 * configuration table.
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
;
47 status
= fdt_open_into(orig_fdt
, fdt
, new_fdt_size
);
49 status
= fdt_create_empty_tree(fdt
, new_fdt_size
);
55 * Delete any memory nodes present. We must delete nodes which
56 * early_init_dt_scan_memory may try to use.
60 const char *type
, *name
;
63 node
= fdt_next_node(fdt
, prev
, NULL
);
67 type
= fdt_getprop(fdt
, node
, "device_type", &len
);
68 if (type
&& strncmp(type
, "memory", len
) == 0) {
69 fdt_del_node(fdt
, node
);
76 node
= fdt_subnode_offset(fdt
, 0, "chosen");
78 node
= fdt_add_subnode(fdt
, 0, "chosen");
80 status
= node
; /* node is error code when negative */
85 if ((cmdline_ptr
!= NULL
) && (strlen(cmdline_ptr
) > 0)) {
86 status
= fdt_setprop(fdt
, node
, "bootargs", cmdline_ptr
,
87 strlen(cmdline_ptr
) + 1);
92 /* Set initrd address/end in device tree, if present */
93 if (initrd_size
!= 0) {
95 u64 initrd_image_start
= cpu_to_fdt64(initrd_addr
);
97 status
= fdt_setprop(fdt
, node
, "linux,initrd-start",
98 &initrd_image_start
, sizeof(u64
));
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
));
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
));
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
));
122 fdt_val32
= cpu_to_fdt32(map_size
);
123 status
= fdt_setprop(fdt
, node
, "linux,uefi-mmap-size",
124 &fdt_val32
, sizeof(fdt_val32
));
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
));
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
));
141 * Add kernel version banner so stub/kernel match can be
144 status
= fdt_setprop_string(fdt
, node
, "linux,uefi-stub-kern-ver",
152 if (status
== -FDT_ERR_NOSPACE
)
153 return EFI_BUFFER_TOO_SMALL
;
155 return EFI_LOAD_ERROR
;
158 #ifndef EFI_FDT_ALIGN
159 #define EFI_FDT_ALIGN EFI_PAGE_SIZE
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.
176 efi_status_t
allocate_new_fdt_and_exit_boot(efi_system_table_t
*sys_table
,
178 unsigned long *new_fdt_addr
,
179 unsigned long max_addr
,
180 u64 initrd_addr
, u64 initrd_size
,
182 unsigned long fdt_addr
,
183 unsigned long fdt_size
)
185 unsigned long map_size
, desc_size
;
187 unsigned long mmap_key
;
188 efi_memory_desc_t
*memory_map
;
189 unsigned long new_fdt_size
;
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.
197 new_fdt_size
= fdt_size
+ EFI_PAGE_SIZE
;
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");
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().
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
;
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
);
222 /* Succeeding the first time is the expected case. */
223 if (status
== EFI_SUCCESS
)
226 if (status
== EFI_BUFFER_TOO_SMALL
) {
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.
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
;
238 pr_efi_err(sys_table
, "Unable to constuct new device tree.\n");
243 /* Now we are ready to exit_boot_services.*/
244 status
= sys_table
->boottime
->exit_boot_services(handle
, mmap_key
);
247 if (status
== EFI_SUCCESS
)
250 pr_efi_err(sys_table
, "Exit boot services failed.\n");
253 sys_table
->boottime
->free_pool(memory_map
);
256 efi_free(sys_table
, new_fdt_size
, *new_fdt_addr
);
259 return EFI_LOAD_ERROR
;
262 void *get_fdt(efi_system_table_t
*sys_table
)
264 efi_guid_t fdt_guid
= DEVICE_TREE_GUID
;
265 efi_config_table_t
*tables
;
269 tables
= (efi_config_table_t
*) sys_table
->tables
;
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
;
This page took 0.078114 seconds and 5 git commands to generate.