Example #1
0
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, buff_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;
	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(sys_table, &map);
	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");

	map.map = &memory_map;
	/*
	 * 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, &map);
		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 construct new device tree.\n");
			goto fail_free_mmap;
		}
	}

	sys_table->boottime->free_pool(memory_map);
	priv.runtime_map = runtime_map;
	priv.runtime_entry_count = &runtime_entry_count;
	status = efi_exit_boot_services(sys_table, handle, &map, &priv,
					exit_boot_func);

	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;
}
Example #2
0
/** Get platform-specific Multiboot information.
 * @param loader        Loader internal data. */
void multiboot_platform_load(multiboot_loader_t *loader)
{
	void *efi_mmap __cleanup_free;
	efi_uintn_t efi_entries, desc_size;
	efi_uint32_t desc_version;
	multiboot_mmap_entry_t *mmap __cleanup_free = NULL;
	size_t offset;
	void *dest;

	/* Multiboot requires an E820-style memory map. Exit boot services mode to
	 * get the final memory map and then we convert it into a E820 format. */
	efi_exit_boot_services(&efi_mmap, &efi_entries, &desc_size, &desc_version);

	offset = 0;
	for (efi_uintn_t i = 0; i < efi_entries; i++) {
		efi_memory_descriptor_t *desc = efi_mmap + (i * desc_size);
		uint32_t type;

		switch (desc->type) {
		case EFI_LOADER_CODE:
		case EFI_LOADER_DATA:
		case EFI_BOOT_SERVICES_CODE:
		case EFI_BOOT_SERVICES_DATA:
		case EFI_CONVENTIONAL_MEMORY:
			type = MULTIBOOT_MMAP_FREE;
			break;
		case EFI_UNUSABLE_MEMORY:
			type = MULTIBOOT_MMAP_BAD;
			break;
		case EFI_ACPI_RECLAIM_MEMORY:
			type = MULTIBOOT_MMAP_ACPI_RECLAIM;
			break;
		case EFI_ACPI_MEMORY_NVS:
			type = MULTIBOOT_MMAP_ACPI_NVS;
			break;
		default:
			type = MULTIBOOT_MMAP_RESERVED;
			break;
		}

		/* Coalesce adjacent entries of same type. */
		if (mmap && mmap[offset].type == type && desc->physical_start == mmap[offset].addr + mmap[offset].len) {
			mmap[offset].len += desc->num_pages * EFI_PAGE_SIZE;
		} else {
			if (mmap)
				offset++;

			mmap = realloc(mmap, sizeof(*mmap) * (offset + 1));
			mmap[offset].size = sizeof(*mmap) - 4;
			mmap[offset].addr = desc->physical_start;
			mmap[offset].len = desc->num_pages * EFI_PAGE_SIZE;
			mmap[offset].type = type;
		}
	}

	/* Copy the final memory map into the info area. */
	loader->info->flags |= MULTIBOOT_INFO_MEMORY | MULTIBOOT_INFO_MEM_MAP;
	loader->info->mmap_length = sizeof(*mmap) * (offset + 1);
	dest = multiboot_alloc_info(loader, loader->info->mmap_length, &loader->info->mmap_addr);
	memcpy(dest, mmap, loader->info->mmap_length);

	/* Get upper/lower memory information. */
	for (size_t i = 0; i <= offset; i++) {
		if (mmap[i].type == MULTIBOOT_MMAP_FREE) {
			if (mmap[i].addr <= 0x100000 && mmap[i].addr + mmap[i].len > 0x100000) {
				loader->info->mem_upper = (mmap[i].addr + mmap[i].len - 0x100000) / 1024;
			} else if (mmap[i].addr == 0) {
				loader->info->mem_lower = min(mmap[i].len, 0x100000) / 1024;
			}
		}
	}

  /* Pass video mode information if required. */
  if (loader->mode) {
      vbe_info_t *control;
      vbe_mode_info_t *mode;

      loader->info->flags |= MULTIBOOT_INFO_VIDEO_INFO;

      /* Try to fudge together something that looks vaguely VBE-ish... */
      control = multiboot_alloc_info(loader, sizeof(*control), &loader->info->vbe_control_info);
      memcpy(control->vbe_signature, "VESA", sizeof(control->vbe_signature));
      control->vbe_version_major = 2;
      control->vbe_version_minor = 0;
      control->video_mode_ptr = loader->mode->mem_phys;
      control->total_memory = loader->mode->mem_size;

      mode = multiboot_alloc_info(loader, sizeof(*mode), &loader->info->vbe_mode_info);
      mode->mode_attributes = 0x9b;
      mode->bytes_per_scan_line = loader->mode->pitch;
      mode->x_resolution = loader->mode->width;
      mode->y_resolution = loader->mode->height;
      mode->bits_per_pixel = loader->mode->bpp;
      mode->memory_model = VBE_MEMORY_MODEL_DIRECT_COLOUR;
      mode->reserved1 = 1;
      mode->red_mask_size = loader->mode->red_size;
      mode->red_field_position = loader->mode->red_pos;
      mode->green_mask_size = loader->mode->green_size;
      mode->green_field_position = loader->mode->green_pos;
      mode->blue_mask_size = loader->mode->blue_size;
      mode->blue_field_position = loader->mode->blue_pos;
      mode->phys_base_ptr = loader->mode->mem_phys;

      loader->info->vbe_mode = 0x100 | VBE_MODE_LFB;
  }
}