0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/efi.h>
0011 #include <linux/libfdt.h>
0012 #include <asm/efi.h>
0013
0014 #include "efistub.h"
0015
0016 #define EFI_DT_ADDR_CELLS_DEFAULT 2
0017 #define EFI_DT_SIZE_CELLS_DEFAULT 2
0018
0019 static void fdt_update_cell_size(void *fdt)
0020 {
0021 int offset;
0022
0023 offset = fdt_path_offset(fdt, "/");
0024
0025
0026 fdt_setprop_u32(fdt, offset, "#address-cells", EFI_DT_ADDR_CELLS_DEFAULT);
0027 fdt_setprop_u32(fdt, offset, "#size-cells", EFI_DT_SIZE_CELLS_DEFAULT);
0028 }
0029
0030 static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
0031 void *fdt, int new_fdt_size, char *cmdline_ptr,
0032 u64 initrd_addr, u64 initrd_size)
0033 {
0034 int node, num_rsv;
0035 int status;
0036 u32 fdt_val32;
0037 u64 fdt_val64;
0038
0039
0040 if (orig_fdt) {
0041 if (fdt_check_header(orig_fdt)) {
0042 efi_err("Device Tree header not valid!\n");
0043 return EFI_LOAD_ERROR;
0044 }
0045
0046
0047
0048
0049 if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) {
0050 efi_err("Truncated device tree! foo!\n");
0051 return EFI_LOAD_ERROR;
0052 }
0053 }
0054
0055 if (orig_fdt) {
0056 status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
0057 } else {
0058 status = fdt_create_empty_tree(fdt, new_fdt_size);
0059 if (status == 0) {
0060
0061
0062
0063
0064 fdt_update_cell_size(fdt);
0065 }
0066 }
0067
0068 if (status != 0)
0069 goto fdt_set_fail;
0070
0071
0072
0073
0074
0075 num_rsv = fdt_num_mem_rsv(fdt);
0076 while (num_rsv-- > 0)
0077 fdt_del_mem_rsv(fdt, num_rsv);
0078
0079 node = fdt_subnode_offset(fdt, 0, "chosen");
0080 if (node < 0) {
0081 node = fdt_add_subnode(fdt, 0, "chosen");
0082 if (node < 0) {
0083
0084 status = node;
0085 goto fdt_set_fail;
0086 }
0087 }
0088
0089 if (cmdline_ptr != NULL && strlen(cmdline_ptr) > 0) {
0090 status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
0091 strlen(cmdline_ptr) + 1);
0092 if (status)
0093 goto fdt_set_fail;
0094 }
0095
0096
0097 if (initrd_size != 0) {
0098 u64 initrd_image_end;
0099 u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
0100
0101 status = fdt_setprop_var(fdt, node, "linux,initrd-start", initrd_image_start);
0102 if (status)
0103 goto fdt_set_fail;
0104
0105 initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
0106 status = fdt_setprop_var(fdt, node, "linux,initrd-end", initrd_image_end);
0107 if (status)
0108 goto fdt_set_fail;
0109 }
0110
0111
0112 node = fdt_subnode_offset(fdt, 0, "chosen");
0113 fdt_val64 = cpu_to_fdt64((u64)(unsigned long)efi_system_table);
0114
0115 status = fdt_setprop_var(fdt, node, "linux,uefi-system-table", fdt_val64);
0116 if (status)
0117 goto fdt_set_fail;
0118
0119 fdt_val64 = U64_MAX;
0120
0121 status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-start", fdt_val64);
0122 if (status)
0123 goto fdt_set_fail;
0124
0125 fdt_val32 = U32_MAX;
0126
0127 status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-size", fdt_val32);
0128 if (status)
0129 goto fdt_set_fail;
0130
0131 status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-desc-size", fdt_val32);
0132 if (status)
0133 goto fdt_set_fail;
0134
0135 status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-desc-ver", fdt_val32);
0136 if (status)
0137 goto fdt_set_fail;
0138
0139 if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && !efi_nokaslr) {
0140 efi_status_t efi_status;
0141
0142 efi_status = efi_get_random_bytes(sizeof(fdt_val64),
0143 (u8 *)&fdt_val64);
0144 if (efi_status == EFI_SUCCESS) {
0145 status = fdt_setprop_var(fdt, node, "kaslr-seed", fdt_val64);
0146 if (status)
0147 goto fdt_set_fail;
0148 }
0149 }
0150
0151
0152 fdt_pack(fdt);
0153
0154 return EFI_SUCCESS;
0155
0156 fdt_set_fail:
0157 if (status == -FDT_ERR_NOSPACE)
0158 return EFI_BUFFER_TOO_SMALL;
0159
0160 return EFI_LOAD_ERROR;
0161 }
0162
0163 static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map)
0164 {
0165 int node = fdt_path_offset(fdt, "/chosen");
0166 u64 fdt_val64;
0167 u32 fdt_val32;
0168 int err;
0169
0170 if (node < 0)
0171 return EFI_LOAD_ERROR;
0172
0173 fdt_val64 = cpu_to_fdt64((unsigned long)*map->map);
0174
0175 err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-start", fdt_val64);
0176 if (err)
0177 return EFI_LOAD_ERROR;
0178
0179 fdt_val32 = cpu_to_fdt32(*map->map_size);
0180
0181 err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-size", fdt_val32);
0182 if (err)
0183 return EFI_LOAD_ERROR;
0184
0185 fdt_val32 = cpu_to_fdt32(*map->desc_size);
0186
0187 err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-desc-size", fdt_val32);
0188 if (err)
0189 return EFI_LOAD_ERROR;
0190
0191 fdt_val32 = cpu_to_fdt32(*map->desc_ver);
0192
0193 err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-desc-ver", fdt_val32);
0194 if (err)
0195 return EFI_LOAD_ERROR;
0196
0197 return EFI_SUCCESS;
0198 }
0199
0200 struct exit_boot_struct {
0201 efi_memory_desc_t *runtime_map;
0202 int *runtime_entry_count;
0203 void *new_fdt_addr;
0204 };
0205
0206 static efi_status_t exit_boot_func(struct efi_boot_memmap *map,
0207 void *priv)
0208 {
0209 struct exit_boot_struct *p = priv;
0210
0211
0212
0213
0214
0215 efi_get_virtmap(*map->map, *map->map_size, *map->desc_size,
0216 p->runtime_map, p->runtime_entry_count);
0217
0218 return update_fdt_memmap(p->new_fdt_addr, map);
0219 }
0220
0221 #ifndef MAX_FDT_SIZE
0222 # define MAX_FDT_SIZE SZ_2M
0223 #endif
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239 efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
0240 unsigned long *new_fdt_addr,
0241 u64 initrd_addr, u64 initrd_size,
0242 char *cmdline_ptr,
0243 unsigned long fdt_addr,
0244 unsigned long fdt_size)
0245 {
0246 unsigned long map_size, desc_size, buff_size;
0247 u32 desc_ver;
0248 unsigned long mmap_key;
0249 efi_memory_desc_t *memory_map, *runtime_map;
0250 efi_status_t status;
0251 int runtime_entry_count;
0252 struct efi_boot_memmap map;
0253 struct exit_boot_struct priv;
0254
0255 map.map = &runtime_map;
0256 map.map_size = &map_size;
0257 map.desc_size = &desc_size;
0258 map.desc_ver = &desc_ver;
0259 map.key_ptr = &mmap_key;
0260 map.buff_size = &buff_size;
0261
0262
0263
0264
0265
0266
0267
0268 status = efi_get_memory_map(&map);
0269 if (status != EFI_SUCCESS) {
0270 efi_err("Unable to retrieve UEFI memory map.\n");
0271 return status;
0272 }
0273
0274 efi_info("Exiting boot services...\n");
0275
0276 map.map = &memory_map;
0277 status = efi_allocate_pages(MAX_FDT_SIZE, new_fdt_addr, ULONG_MAX);
0278 if (status != EFI_SUCCESS) {
0279 efi_err("Unable to allocate memory for new device tree.\n");
0280 goto fail;
0281 }
0282
0283
0284
0285
0286
0287 status = efi_get_memory_map(&map);
0288 if (status != EFI_SUCCESS)
0289 goto fail_free_new_fdt;
0290
0291 status = update_fdt((void *)fdt_addr, fdt_size,
0292 (void *)*new_fdt_addr, MAX_FDT_SIZE, cmdline_ptr,
0293 initrd_addr, initrd_size);
0294
0295 if (status != EFI_SUCCESS) {
0296 efi_err("Unable to construct new device tree.\n");
0297 goto fail_free_new_fdt;
0298 }
0299
0300 runtime_entry_count = 0;
0301 priv.runtime_map = runtime_map;
0302 priv.runtime_entry_count = &runtime_entry_count;
0303 priv.new_fdt_addr = (void *)*new_fdt_addr;
0304
0305 status = efi_exit_boot_services(handle, &map, &priv, exit_boot_func);
0306
0307 if (status == EFI_SUCCESS) {
0308 efi_set_virtual_address_map_t *svam;
0309
0310 if (efi_novamap)
0311 return EFI_SUCCESS;
0312
0313
0314 svam = efi_system_table->runtime->set_virtual_address_map;
0315 status = svam(runtime_entry_count * desc_size, desc_size,
0316 desc_ver, runtime_map);
0317
0318
0319
0320
0321
0322
0323 if (status != EFI_SUCCESS) {
0324 int l;
0325
0326
0327
0328
0329
0330
0331
0332 for (l = 0; l < map_size; l += desc_size) {
0333 efi_memory_desc_t *p = (void *)memory_map + l;
0334
0335 if (p->attribute & EFI_MEMORY_RUNTIME)
0336 p->virt_addr = 0;
0337 }
0338 }
0339 return EFI_SUCCESS;
0340 }
0341
0342 efi_err("Exit boot services failed.\n");
0343
0344 fail_free_new_fdt:
0345 efi_free(MAX_FDT_SIZE, *new_fdt_addr);
0346
0347 fail:
0348 efi_system_table->boottime->free_pool(runtime_map);
0349
0350 return EFI_LOAD_ERROR;
0351 }
0352
0353 void *get_fdt(unsigned long *fdt_size)
0354 {
0355 void *fdt;
0356
0357 fdt = get_efi_config_table(DEVICE_TREE_GUID);
0358
0359 if (!fdt)
0360 return NULL;
0361
0362 if (fdt_check_header(fdt) != 0) {
0363 efi_err("Invalid header detected on UEFI supplied FDT, ignoring ...\n");
0364 return NULL;
0365 }
0366 *fdt_size = fdt_totalsize(fdt);
0367 return fdt;
0368 }