efi_status_t allocate_new_fdt_and_exit_boot()

in efi/libstub/fdt.c [239:351]


efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
					    unsigned long *new_fdt_addr,
					    u64 initrd_addr, u64 initrd_size,
					    char *cmdline_ptr,
					    unsigned long fdt_addr,
					    unsigned long fdt_size)
{
	unsigned long map_size, desc_size, buff_size;
	u32 desc_ver;
	unsigned long mmap_key;
	efi_memory_desc_t *memory_map, *runtime_map;
	efi_status_t status;
	int runtime_entry_count;
	struct efi_boot_memmap map;
	struct exit_boot_struct priv;

	map.map		= &runtime_map;
	map.map_size	= &map_size;
	map.desc_size	= &desc_size;
	map.desc_ver	= &desc_ver;
	map.key_ptr	= &mmap_key;
	map.buff_size	= &buff_size;

	/*
	 * 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(&map);
	if (status != EFI_SUCCESS) {
		efi_err("Unable to retrieve UEFI memory map.\n");
		return status;
	}

	efi_info("Exiting boot services...\n");

	map.map = &memory_map;
	status = efi_allocate_pages(MAX_FDT_SIZE, new_fdt_addr, ULONG_MAX);
	if (status != EFI_SUCCESS) {
		efi_err("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(&map);
	if (status != EFI_SUCCESS)
		goto fail_free_new_fdt;

	status = update_fdt((void *)fdt_addr, fdt_size,
			    (void *)*new_fdt_addr, MAX_FDT_SIZE, cmdline_ptr,
			    initrd_addr, initrd_size);

	if (status != EFI_SUCCESS) {
		efi_err("Unable to construct new device tree.\n");
		goto fail_free_new_fdt;
	}

	runtime_entry_count		= 0;
	priv.runtime_map		= runtime_map;
	priv.runtime_entry_count	= &runtime_entry_count;
	priv.new_fdt_addr		= (void *)*new_fdt_addr;

	status = efi_exit_boot_services(handle, &map, &priv, exit_boot_func);

	if (status == EFI_SUCCESS) {
		efi_set_virtual_address_map_t *svam;

		if (efi_novamap)
			return EFI_SUCCESS;

		/* Install the new virtual address map */
		svam = efi_system_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;
	}

	efi_err("Exit boot services failed.\n");

fail_free_new_fdt:
	efi_free(MAX_FDT_SIZE, *new_fdt_addr);

fail:
	efi_system_table->boottime->free_pool(runtime_map);

	return EFI_LOAD_ERROR;
}