Skip to content
Snippets Groups Projects
efi.c 29.7 KiB
Newer Older
			md->type = EFI_RESERVED_TYPE;
			md->attribute = 0;
			continue;
		}
		prev_md = md;
	}

	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
		md = p;
		if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
		    md->type != EFI_BOOT_SERVICES_CODE &&
		    md->type != EFI_BOOT_SERVICES_DATA)
			continue;
Huang, Ying's avatar
Huang, Ying committed

		size = md->num_pages << EFI_PAGE_SHIFT;
		end = md->phys_addr + size;

		start_pfn = PFN_DOWN(md->phys_addr);
		if (pfn_range_is_mapped(start_pfn, end_pfn)) {
Huang, Ying's avatar
Huang, Ying committed
			va = __va(md->phys_addr);

			if (!(md->attribute & EFI_MEMORY_WB))
				efi_memory_uc((u64)(unsigned long)va, size);
		} else
			va = efi_ioremap(md->phys_addr, size,
					 md->type, md->attribute);
Huang, Ying's avatar
Huang, Ying committed

		md->virt_addr = (u64) (unsigned long) va;

		if (!va) {
			pr_err("ioremap of 0x%llX failed!\n",
			       (unsigned long long)md->phys_addr);
Huang, Ying's avatar
Huang, Ying committed
			continue;
		}

		systab = (u64) (unsigned long) efi_phys.systab;
		if (md->phys_addr <= systab && systab < end) {
			systab += md->virt_addr - md->phys_addr;
			efi.systab = (efi_system_table_t *) (unsigned long) systab;
		}
		new_memmap = krealloc(new_memmap,
				      (count + 1) * memmap.desc_size,
				      GFP_KERNEL);
		memcpy(new_memmap + (count * memmap.desc_size), md,
		       memmap.desc_size);
		count++;
	}

	BUG_ON(!efi.systab);

	status = phys_efi_set_virtual_address_map(
		memmap.desc_size * count,
		memmap.desc_size,
		memmap.desc_version,
		(efi_memory_desc_t *)__pa(new_memmap));

	if (status != EFI_SUCCESS) {
		pr_alert("Unable to switch EFI into virtual mode "
			 "(status=%lx)!\n", status);
		panic("EFI call to SetVirtualAddressMap() failed!");
	}

	/*
	 * Now that EFI is in virtual mode, update the function
	 * pointers in the runtime service table to the new virtual addresses.
	 *
	 * Call EFI services through wrapper functions.
	 */
	efi.runtime_version = efi_systab.hdr.revision;
	efi.get_time = virt_efi_get_time;
	efi.set_time = virt_efi_set_time;
	efi.get_wakeup_time = virt_efi_get_wakeup_time;
	efi.set_wakeup_time = virt_efi_set_wakeup_time;
	efi.get_variable = virt_efi_get_variable;
	efi.get_next_variable = virt_efi_get_next_variable;
	efi.set_variable = virt_efi_set_variable;
	efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count;
	efi.reset_system = virt_efi_reset_system;
	efi.set_virtual_address_map = NULL;
	efi.query_variable_info = virt_efi_query_variable_info;
	efi.update_capsule = virt_efi_update_capsule;
	efi.query_capsule_caps = virt_efi_query_capsule_caps;
	if (__supported_pte_mask & _PAGE_NX)
		runtime_code_page_mkexec();
}

/*
 * Convenience functions to obtain memory types and attributes
 */
u32 efi_mem_type(unsigned long phys_addr)
{
	efi_memory_desc_t *md;
	void *p;

	if (!efi_enabled(EFI_MEMMAP))
		return 0;

	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
		md = p;
		if ((md->phys_addr <= phys_addr) &&
		    (phys_addr < (md->phys_addr +
				  (md->num_pages << EFI_PAGE_SHIFT))))
			return md->type;
	}
	return 0;
}

u64 efi_mem_attributes(unsigned long phys_addr)
{
	efi_memory_desc_t *md;
	void *p;

	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
		md = p;
		if ((md->phys_addr <= phys_addr) &&
		    (phys_addr < (md->phys_addr +
				  (md->num_pages << EFI_PAGE_SHIFT))))
			return md->attribute;
	}
	return 0;
}

/*
 * Some firmware has serious problems when using more than 50% of the EFI
 * variable store, i.e. it triggers bugs that can brick machines. Ensure that
 * we never use more than this safe limit.
 *
 * Return EFI_SUCCESS if it is safe to write 'size' bytes to the variable
 * store.
 */
efi_status_t efi_query_variable_store(u32 attributes, unsigned long size)
{
	efi_status_t status;
	u64 storage_size, remaining_size, max_size;

	status = efi.query_variable_info(attributes, &storage_size,
					 &remaining_size, &max_size);
	if (status != EFI_SUCCESS)
		return status;

	if (!max_size && remaining_size > size)
		printk_once(KERN_ERR FW_BUG "Broken EFI implementation"
			    " is returning MaxVariableSize=0\n");
	/*
	 * Some firmware implementations refuse to boot if there's insufficient
	 * space in the variable store. We account for that by refusing the
	 * write if permitting it would reduce the available space to under
	 * 50%. However, some firmware won't reclaim variable space until
	 * after the used (not merely the actively used) space drops below
	 * a threshold. We can approximate that case with the value calculated
	 * above. If both the firmware and our calculations indicate that the
	 * available space would drop below 50%, refuse the write.
	 */

	if (!storage_size || size > remaining_size ||
	    (max_size && size > max_size))
		return EFI_OUT_OF_RESOURCES;

	if (!efi_no_storage_paranoia &&
	    ((active_size + size + VAR_METADATA_SIZE > storage_size / 2) &&
	     (remaining_size - size < storage_size / 2)))
		return EFI_OUT_OF_RESOURCES;

	return EFI_SUCCESS;
}
EXPORT_SYMBOL_GPL(efi_query_variable_store);