Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

efi: Add shared FDT related functions for ARM/ARM64

Both ARM and ARM64 stubs will update the device tree that they pass to
the kernel. In both cases they primarily need to add the same UEFI
related information, so the function can be shared. Create a new FDT
related file for this to avoid use of architecture #ifdefs in
efi-stub-helper.c.

Signed-off-by: Roy Franz <roy.franz@linaro.org>
[ Fixed memory node deletion code. ]
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Leif Lindholm <leif.lindholm@linaro.org>
Acked-by: Grant Likely <grant.likely@linaro.org>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>

authored by

Roy Franz and committed by
Matt Fleming
263b4a30 d7ecbddf

+288
+285
drivers/firmware/efi/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 + 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 + }
+3
include/linux/efi.h
··· 575 575 #define EFI_FILE_SYSTEM_GUID \ 576 576 EFI_GUID( 0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) 577 577 578 + #define DEVICE_TREE_GUID \ 579 + EFI_GUID( 0xb1b621d5, 0xf19c, 0x41a5, 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 ) 580 + 578 581 typedef struct { 579 582 efi_guid_t guid; 580 583 u64 table;