static efi_status_t exit_boot_func(efi_system_table_t *sys_table_arg, struct efi_boot_memmap *map, void *priv) { struct exit_boot_struct *p = priv; /* * Update the memory map with virtual addresses. The function will also * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME * entries so that we can pass it straight to SetVirtualAddressMap() */ efi_get_virtmap(*map->map, *map->map_size, *map->desc_size, p->runtime_map, p->runtime_entry_count); return EFI_SUCCESS; }
efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, void *handle, unsigned long *new_fdt_addr, unsigned long max_addr, u64 initrd_addr, u64 initrd_size, char *cmdline_ptr, unsigned long fdt_addr, unsigned long fdt_size) { unsigned long map_size, desc_size; u32 desc_ver; unsigned long mmap_key; efi_memory_desc_t *memory_map, *runtime_map; unsigned long new_fdt_size; efi_status_t status; int runtime_entry_count = 0; /* * Get a copy of the current memory map that we will use to prepare * the input for SetVirtualAddressMap(). We don't have to worry about * subsequent allocations adding entries, since they could not affect * the number of EFI_MEMORY_RUNTIME regions. */ status = efi_get_memory_map(sys_table, &runtime_map, &map_size, &desc_size, &desc_ver, &mmap_key); if (status != EFI_SUCCESS) { pr_efi_err(sys_table, "Unable to retrieve UEFI memory map.\n"); return status; } pr_efi(sys_table, "Exiting boot services and installing virtual address map...\n"); /* * Estimate size of new FDT, and allocate memory for it. We * will allocate a bigger buffer if this ends up being too * small, so a rough guess is OK here. */ new_fdt_size = fdt_size + EFI_PAGE_SIZE; while (1) { status = efi_high_alloc(sys_table, new_fdt_size, EFI_FDT_ALIGN, new_fdt_addr, max_addr); if (status != EFI_SUCCESS) { pr_efi_err(sys_table, "Unable to allocate memory for new device tree.\n"); goto fail; } /* * Now that we have done our final memory allocation (and free) * we can get the memory map key needed for * exit_boot_services(). */ status = efi_get_memory_map(sys_table, &memory_map, &map_size, &desc_size, &desc_ver, &mmap_key); if (status != EFI_SUCCESS) goto fail_free_new_fdt; status = update_fdt(sys_table, (void *)fdt_addr, fdt_size, (void *)*new_fdt_addr, new_fdt_size, cmdline_ptr, initrd_addr, initrd_size, memory_map, map_size, desc_size, desc_ver); /* Succeeding the first time is the expected case. */ if (status == EFI_SUCCESS) break; if (status == EFI_BUFFER_TOO_SMALL) { /* * We need to allocate more space for the new * device tree, so free existing buffer that is * too small. Also free memory map, as we will need * to get new one that reflects the free/alloc we do * on the device tree buffer. */ efi_free(sys_table, new_fdt_size, *new_fdt_addr); sys_table->boottime->free_pool(memory_map); new_fdt_size += EFI_PAGE_SIZE; } else { pr_efi_err(sys_table, "Unable to constuct new device tree.\n"); goto fail_free_mmap; } } /* * Update the memory map with virtual addresses. The function will also * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME * entries so that we can pass it straight into SetVirtualAddressMap() */ efi_get_virtmap(memory_map, map_size, desc_size, runtime_map, &runtime_entry_count); /* Now we are ready to exit_boot_services.*/ status = sys_table->boottime->exit_boot_services(handle, mmap_key); if (status == EFI_SUCCESS) { efi_set_virtual_address_map_t *svam; /* Install the new virtual address map */ svam = sys_table->runtime->set_virtual_address_map; status = svam(runtime_entry_count * desc_size, desc_size, desc_ver, runtime_map); /* * We are beyond the point of no return here, so if the call to * SetVirtualAddressMap() failed, we need to signal that to the * incoming kernel but proceed normally otherwise. */ if (status != EFI_SUCCESS) { int l; /* * Set the virtual address field of all * EFI_MEMORY_RUNTIME entries to 0. This will signal * the incoming kernel that no virtual translation has * been installed. */ for (l = 0; l < map_size; l += desc_size) { efi_memory_desc_t *p = (void *)memory_map + l; if (p->attribute & EFI_MEMORY_RUNTIME) p->virt_addr = 0; } } return EFI_SUCCESS; } pr_efi_err(sys_table, "Exit boot services failed.\n"); fail_free_mmap: sys_table->boottime->free_pool(memory_map); fail_free_new_fdt: efi_free(sys_table, new_fdt_size, *new_fdt_addr); fail: sys_table->boottime->free_pool(runtime_map); return EFI_LOAD_ERROR; }