ASoC: rt5677: Add ACPI support
[cascardo/linux.git] / drivers / firmware / efi / libstub / fdt.c
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
17 #include "efistub.h"
18
19 efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
20                         unsigned long orig_fdt_size,
21                         void *fdt, int new_fdt_size, char *cmdline_ptr,
22                         u64 initrd_addr, u64 initrd_size,
23                         efi_memory_desc_t *memory_map,
24                         unsigned long map_size, unsigned long desc_size,
25                         u32 desc_ver)
26 {
27         int node, num_rsv;
28         int status;
29         u32 fdt_val32;
30         u64 fdt_val64;
31
32         /* Do some checks on provided FDT, if it exists*/
33         if (orig_fdt) {
34                 if (fdt_check_header(orig_fdt)) {
35                         pr_efi_err(sys_table, "Device Tree header not valid!\n");
36                         return EFI_LOAD_ERROR;
37                 }
38                 /*
39                  * We don't get the size of the FDT if we get if from a
40                  * configuration table.
41                  */
42                 if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) {
43                         pr_efi_err(sys_table, "Truncated device tree! foo!\n");
44                         return EFI_LOAD_ERROR;
45                 }
46         }
47
48         if (orig_fdt)
49                 status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
50         else
51                 status = fdt_create_empty_tree(fdt, new_fdt_size);
52
53         if (status != 0)
54                 goto fdt_set_fail;
55
56         /*
57          * Delete all memory reserve map entries. When booting via UEFI,
58          * kernel will use the UEFI memory map to find reserved regions.
59          */
60         num_rsv = fdt_num_mem_rsv(fdt);
61         while (num_rsv-- > 0)
62                 fdt_del_mem_rsv(fdt, num_rsv);
63
64         node = fdt_subnode_offset(fdt, 0, "chosen");
65         if (node < 0) {
66                 node = fdt_add_subnode(fdt, 0, "chosen");
67                 if (node < 0) {
68                         status = node; /* node is error code when negative */
69                         goto fdt_set_fail;
70                 }
71         }
72
73         if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
74                 status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
75                                      strlen(cmdline_ptr) + 1);
76                 if (status)
77                         goto fdt_set_fail;
78         }
79
80         /* Set initrd address/end in device tree, if present */
81         if (initrd_size != 0) {
82                 u64 initrd_image_end;
83                 u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
84
85                 status = fdt_setprop(fdt, node, "linux,initrd-start",
86                                      &initrd_image_start, sizeof(u64));
87                 if (status)
88                         goto fdt_set_fail;
89                 initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
90                 status = fdt_setprop(fdt, node, "linux,initrd-end",
91                                      &initrd_image_end, sizeof(u64));
92                 if (status)
93                         goto fdt_set_fail;
94         }
95
96         /* Add FDT entries for EFI runtime services in chosen node. */
97         node = fdt_subnode_offset(fdt, 0, "chosen");
98         fdt_val64 = cpu_to_fdt64((u64)(unsigned long)sys_table);
99         status = fdt_setprop(fdt, node, "linux,uefi-system-table",
100                              &fdt_val64, sizeof(fdt_val64));
101         if (status)
102                 goto fdt_set_fail;
103
104         fdt_val64 = cpu_to_fdt64((u64)(unsigned long)memory_map);
105         status = fdt_setprop(fdt, node, "linux,uefi-mmap-start",
106                              &fdt_val64,  sizeof(fdt_val64));
107         if (status)
108                 goto fdt_set_fail;
109
110         fdt_val32 = cpu_to_fdt32(map_size);
111         status = fdt_setprop(fdt, node, "linux,uefi-mmap-size",
112                              &fdt_val32,  sizeof(fdt_val32));
113         if (status)
114                 goto fdt_set_fail;
115
116         fdt_val32 = cpu_to_fdt32(desc_size);
117         status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size",
118                              &fdt_val32, sizeof(fdt_val32));
119         if (status)
120                 goto fdt_set_fail;
121
122         fdt_val32 = cpu_to_fdt32(desc_ver);
123         status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver",
124                              &fdt_val32, sizeof(fdt_val32));
125         if (status)
126                 goto fdt_set_fail;
127
128         if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
129                 efi_status_t efi_status;
130
131                 efi_status = efi_get_random_bytes(sys_table, sizeof(fdt_val64),
132                                                   (u8 *)&fdt_val64);
133                 if (efi_status == EFI_SUCCESS) {
134                         status = fdt_setprop(fdt, node, "kaslr-seed",
135                                              &fdt_val64, sizeof(fdt_val64));
136                         if (status)
137                                 goto fdt_set_fail;
138                 } else if (efi_status != EFI_NOT_FOUND) {
139                         return efi_status;
140                 }
141         }
142         return EFI_SUCCESS;
143
144 fdt_set_fail:
145         if (status == -FDT_ERR_NOSPACE)
146                 return EFI_BUFFER_TOO_SMALL;
147
148         return EFI_LOAD_ERROR;
149 }
150
151 #ifndef EFI_FDT_ALIGN
152 #define EFI_FDT_ALIGN EFI_PAGE_SIZE
153 #endif
154
155 /*
156  * Allocate memory for a new FDT, then add EFI, commandline, and
157  * initrd related fields to the FDT.  This routine increases the
158  * FDT allocation size until the allocated memory is large
159  * enough.  EFI allocations are in EFI_PAGE_SIZE granules,
160  * which are fixed at 4K bytes, so in most cases the first
161  * allocation should succeed.
162  * EFI boot services are exited at the end of this function.
163  * There must be no allocations between the get_memory_map()
164  * call and the exit_boot_services() call, so the exiting of
165  * boot services is very tightly tied to the creation of the FDT
166  * with the final memory map in it.
167  */
168
169 efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
170                                             void *handle,
171                                             unsigned long *new_fdt_addr,
172                                             unsigned long max_addr,
173                                             u64 initrd_addr, u64 initrd_size,
174                                             char *cmdline_ptr,
175                                             unsigned long fdt_addr,
176                                             unsigned long fdt_size)
177 {
178         unsigned long map_size, desc_size;
179         u32 desc_ver;
180         unsigned long mmap_key;
181         efi_memory_desc_t *memory_map, *runtime_map;
182         unsigned long new_fdt_size;
183         efi_status_t status;
184         int runtime_entry_count = 0;
185
186         /*
187          * Get a copy of the current memory map that we will use to prepare
188          * the input for SetVirtualAddressMap(). We don't have to worry about
189          * subsequent allocations adding entries, since they could not affect
190          * the number of EFI_MEMORY_RUNTIME regions.
191          */
192         status = efi_get_memory_map(sys_table, &runtime_map, &map_size,
193                                     &desc_size, &desc_ver, &mmap_key);
194         if (status != EFI_SUCCESS) {
195                 pr_efi_err(sys_table, "Unable to retrieve UEFI memory map.\n");
196                 return status;
197         }
198
199         pr_efi(sys_table,
200                "Exiting boot services and installing virtual address map...\n");
201
202         /*
203          * Estimate size of new FDT, and allocate memory for it. We
204          * will allocate a bigger buffer if this ends up being too
205          * small, so a rough guess is OK here.
206          */
207         new_fdt_size = fdt_size + EFI_PAGE_SIZE;
208         while (1) {
209                 status = efi_high_alloc(sys_table, new_fdt_size, EFI_FDT_ALIGN,
210                                         new_fdt_addr, max_addr);
211                 if (status != EFI_SUCCESS) {
212                         pr_efi_err(sys_table, "Unable to allocate memory for new device tree.\n");
213                         goto fail;
214                 }
215
216                 /*
217                  * Now that we have done our final memory allocation (and free)
218                  * we can get the memory map key  needed for
219                  * exit_boot_services().
220                  */
221                 status = efi_get_memory_map(sys_table, &memory_map, &map_size,
222                                             &desc_size, &desc_ver, &mmap_key);
223                 if (status != EFI_SUCCESS)
224                         goto fail_free_new_fdt;
225
226                 status = update_fdt(sys_table,
227                                     (void *)fdt_addr, fdt_size,
228                                     (void *)*new_fdt_addr, new_fdt_size,
229                                     cmdline_ptr, initrd_addr, initrd_size,
230                                     memory_map, map_size, desc_size, desc_ver);
231
232                 /* Succeeding the first time is the expected case. */
233                 if (status == EFI_SUCCESS)
234                         break;
235
236                 if (status == EFI_BUFFER_TOO_SMALL) {
237                         /*
238                          * We need to allocate more space for the new
239                          * device tree, so free existing buffer that is
240                          * too small.  Also free memory map, as we will need
241                          * to get new one that reflects the free/alloc we do
242                          * on the device tree buffer.
243                          */
244                         efi_free(sys_table, new_fdt_size, *new_fdt_addr);
245                         sys_table->boottime->free_pool(memory_map);
246                         new_fdt_size += EFI_PAGE_SIZE;
247                 } else {
248                         pr_efi_err(sys_table, "Unable to construct new device tree.\n");
249                         goto fail_free_mmap;
250                 }
251         }
252
253         /*
254          * Update the memory map with virtual addresses. The function will also
255          * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME
256          * entries so that we can pass it straight into SetVirtualAddressMap()
257          */
258         efi_get_virtmap(memory_map, map_size, desc_size, runtime_map,
259                         &runtime_entry_count);
260
261         /* Now we are ready to exit_boot_services.*/
262         status = sys_table->boottime->exit_boot_services(handle, mmap_key);
263
264         if (status == EFI_SUCCESS) {
265                 efi_set_virtual_address_map_t *svam;
266
267                 /* Install the new virtual address map */
268                 svam = sys_table->runtime->set_virtual_address_map;
269                 status = svam(runtime_entry_count * desc_size, desc_size,
270                               desc_ver, runtime_map);
271
272                 /*
273                  * We are beyond the point of no return here, so if the call to
274                  * SetVirtualAddressMap() failed, we need to signal that to the
275                  * incoming kernel but proceed normally otherwise.
276                  */
277                 if (status != EFI_SUCCESS) {
278                         int l;
279
280                         /*
281                          * Set the virtual address field of all
282                          * EFI_MEMORY_RUNTIME entries to 0. This will signal
283                          * the incoming kernel that no virtual translation has
284                          * been installed.
285                          */
286                         for (l = 0; l < map_size; l += desc_size) {
287                                 efi_memory_desc_t *p = (void *)memory_map + l;
288
289                                 if (p->attribute & EFI_MEMORY_RUNTIME)
290                                         p->virt_addr = 0;
291                         }
292                 }
293                 return EFI_SUCCESS;
294         }
295
296         pr_efi_err(sys_table, "Exit boot services failed.\n");
297
298 fail_free_mmap:
299         sys_table->boottime->free_pool(memory_map);
300
301 fail_free_new_fdt:
302         efi_free(sys_table, new_fdt_size, *new_fdt_addr);
303
304 fail:
305         sys_table->boottime->free_pool(runtime_map);
306         return EFI_LOAD_ERROR;
307 }
308
309 void *get_fdt(efi_system_table_t *sys_table, unsigned long *fdt_size)
310 {
311         efi_guid_t fdt_guid = DEVICE_TREE_GUID;
312         efi_config_table_t *tables;
313         void *fdt;
314         int i;
315
316         tables = (efi_config_table_t *) sys_table->tables;
317         fdt = NULL;
318
319         for (i = 0; i < sys_table->nr_tables; i++)
320                 if (efi_guidcmp(tables[i].guid, fdt_guid) == 0) {
321                         fdt = (void *) tables[i].table;
322                         if (fdt_check_header(fdt) != 0) {
323                                 pr_efi_err(sys_table, "Invalid header detected on UEFI supplied FDT, ignoring ...\n");
324                                 return NULL;
325                         }
326                         *fdt_size = fdt_totalsize(fdt);
327                         break;
328          }
329
330         return fdt;
331 }