/* * Tell the kernel about the EFI memory map. This might include * more than the max 128 entries that can fit in the passed in e820 * legacy (zeropage) memory map, but the kernel's e820 table can hold * E820_MAX_ENTRIES.
*/
/* * Given add_efi_memmap defaults to 0 and there is no alternative * e820 mechanism for soft-reserved memory, import the full EFI memory * map if soft reservations are present and enabled. Otherwise, the * mechanism to disable the kernel's consideration of EFI_MEMORY_SP is * the efi=nosoftreserve option.
*/ staticbool do_efi_soft_reserve(void)
{
efi_memory_desc_t *md;
for (i = n_removal = 0; in < end; i++) { if (efi_memmap_entry_valid(in, i)) { if (out != in)
memcpy(out, in, efi.memmap.desc_size);
out = (void *)out + efi.memmap.desc_size;
} else {
n_removal++;
}
in = (void *)in + efi.memmap.desc_size;
}
/* * Firmware can use EfiMemoryMappedIO to request that MMIO regions be * mapped by the OS so they can be accessed by EFI runtime services, but * should have no other significance to the OS (UEFI r2.10, sec 7.2). * However, most bootloaders and EFI stubs convert EfiMemoryMappedIO * regions to E820_TYPE_RESERVED entries, which prevent Linux from * allocating space from them (see remove_e820_regions()). * * Some platforms use EfiMemoryMappedIO entries for PCI MMCONFIG space and * PCI host bridge windows, which means Linux can't allocate BAR space for * hot-added devices. * * Remove large EfiMemoryMappedIO regions from the E820 map to avoid this * problem. * * Retain small EfiMemoryMappedIO regions because on some platforms, these * describe non-window space that's included in host bridge _CRS. If we * assign that space to PCI devices, they don't work.
*/ staticvoid __init efi_remove_e820_mmio(void)
{
efi_memory_desc_t *md;
u64 size, start, end; int i = 0;
for_each_efi_memory_desc(md) { if (md->type == EFI_MEMORY_MAPPED_IO) {
size = md->num_pages << EFI_PAGE_SHIFT;
start = md->phys_addr;
end = start + size - 1; if (size >= 256*1024) {
pr_info("Remove mem%02u: MMIO range=[0x%08llx-0x%08llx] (%lluMB) from e820 map\n",
i, start, end, size >> 20);
e820__range_remove(start, size,
E820_TYPE_RESERVED, 1);
} else {
pr_info("Not removing mem%02u: MMIO range=[0x%08llx-0x%08llx] (%lluKB) from e820 map\n",
i, start, end, size >> 10);
}
}
i++;
}
}
void __init efi_print_memmap(void)
{
efi_memory_desc_t *md; int i = 0;
/* * Iterate the EFI memory map in reverse order because the regions * will be mapped top-down. The end result is the same as if we had * mapped things forward, but doesn't require us to change the * existing implementation of efi_map_region().
*/ staticinlinevoid *efi_map_next_entry_reverse(void *entry)
{ /* Initial call */ if (!entry) return efi.memmap.map_end - efi.memmap.desc_size;
entry -= efi.memmap.desc_size; if (entry < efi.memmap.map) return NULL;
return entry;
}
/* * efi_map_next_entry - Return the next EFI memory map descriptor * @entry: Previous EFI memory map descriptor * * This is a helper function to iterate over the EFI memory map, which * we do in different orders depending on the current configuration. * * To begin traversing the memory map @entry must be %NULL. * * Returns %NULL when we reach the end of the memory map.
*/ staticvoid *efi_map_next_entry(void *entry)
{ if (efi_enabled(EFI_64BIT)) { /* * Starting in UEFI v2.5 the EFI_PROPERTIES_TABLE * config table feature requires us to map all entries * in the same order as they appear in the EFI memory * map. That is to say, entry N must have a lower * virtual address than entry N+1. This is because the * firmware toolchain leaves relative references in * the code/data sections, which are split and become * separate EFI memory regions. Mapping things * out-of-order leads to the firmware accessing * unmapped addresses. * * Since we need to map things this way whether or not * the kernel actually makes use of * EFI_PROPERTIES_TABLE, let's just switch to this * scheme by default for 64-bit.
*/ return efi_map_next_entry_reverse(entry);
}
/* Initial call */ if (!entry) return efi.memmap.map;
entry += efi.memmap.desc_size; if (entry >= efi.memmap.map_end) return NULL;
return entry;
}
staticbool should_map_region(efi_memory_desc_t *md)
{ /* * Runtime regions always require runtime mappings (obviously).
*/ if (md->attribute & EFI_MEMORY_RUNTIME) returntrue;
/* * 32-bit EFI doesn't suffer from the bug that requires us to * reserve boot services regions, and mixed mode support * doesn't exist for 32-bit kernels.
*/ if (IS_ENABLED(CONFIG_X86_32)) returnfalse;
/* * EFI specific purpose memory may be reserved by default * depending on kernel config and boot options.
*/ if (md->type == EFI_CONVENTIONAL_MEMORY &&
efi_soft_reserve_enabled() &&
(md->attribute & EFI_MEMORY_SP)) returnfalse;
/* * Map all of RAM so that we can access arguments in the 1:1 * mapping when making EFI runtime calls.
*/ if (efi_is_mixed()) { if (md->type == EFI_CONVENTIONAL_MEMORY ||
md->type == EFI_LOADER_DATA ||
md->type == EFI_LOADER_CODE) returntrue;
}
/* * Map boot services regions as a workaround for buggy * firmware that accesses them even when they shouldn't. * * See efi_{reserve,free}_boot_services().
*/ if (md->type == EFI_BOOT_SERVICES_CODE ||
md->type == EFI_BOOT_SERVICES_DATA) returntrue;
returnfalse;
}
/* * Map the efi memory ranges of the runtime services and update new_mmap with * virtual addresses.
*/ staticvoid * __init efi_map_regions(int *count, int *pg_shift)
{ void *p, *new_memmap = NULL; unsignedlong left = 0; unsignedlong desc_size;
efi_memory_desc_t *md;
desc_size = efi.memmap.desc_size;
p = NULL; while ((p = efi_map_next_entry(p))) {
md = p;
if (!should_map_region(md)) continue;
efi_map_region(md);
if (left < desc_size) {
new_memmap = realloc_pages(new_memmap, *pg_shift); if (!new_memmap) return NULL;
/* * We don't do virtual mode, since we don't do runtime services, on * non-native EFI.
*/ if (efi_is_mixed()) {
efi_memmap_unmap();
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); return;
}
if (efi_alloc_page_tables()) {
pr_err("Failed to allocate EFI page tables\n");
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); return;
}
/* * Map efi regions which were passed via setup_data. The virt_addr is a * fixed addr which was used in first kernel of a kexec boot.
*/
for_each_efi_memory_desc(md)
efi_map_region_fixed(md); /* FIXME: add error handling */
/* * Unregister the early EFI memmap from efi_init() and install * the new EFI memory map.
*/
efi_memmap_unmap();
if (efi_memmap_init_late(efi.memmap.phys_map,
efi.memmap.desc_size * efi.memmap.nr_map)) {
pr_err("Failed to remap late EFI memory map\n");
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); return;
}
/* * This function will switch the EFI runtime services to virtual mode. * Essentially, we look through the EFI memmap and map every region that * has the runtime attribute bit set in its memory descriptor into the * efi_pgd page table. * * The new method does a pagetable switch in a preemption-safe manner * so that we're in a different address space when calling a runtime * function. For function arguments passing we do copy the PUDs of the * kernel page table into efi_pgd prior to each call. * * Specially for kexec boot, efi runtime maps in previous kernel should * be passed in via setup_data. In that case runtime ranges will be mapped * to the same virtual addresses as the first kernel, see * kexec_enter_virtual_mode().
*/ staticvoid __init __efi_enter_virtual_mode(void)
{ int count = 0, pg_shift = 0; void *new_memmap = NULL;
efi_status_t status; unsignedlong pa;
if (efi_alloc_page_tables()) {
pr_err("Failed to allocate EFI page tables\n"); goto err;
}
/* * Unregister the early EFI memmap from efi_init() and install * the new EFI memory map that we are about to pass to the * firmware via SetVirtualAddressMap().
*/
efi_memmap_unmap();
if (efi_memmap_init_late(pa, efi.memmap.desc_size * count)) {
pr_err("Failed to remap late EFI memory map\n"); goto err;
}
if (efi_enabled(EFI_DBG)) {
pr_info("EFI runtime memory map:\n");
efi_print_memmap();
}
if (efi_setup_page_tables(pa, 1 << pg_shift)) goto err;
efi_sync_low_kernel_mappings();
status = efi_set_virtual_address_map(efi.memmap.desc_size * count,
efi.memmap.desc_size,
efi.memmap.desc_version,
(efi_memory_desc_t *)pa,
efi_systab_phys); if (status != EFI_SUCCESS) {
pr_err("Unable to switch EFI into virtual mode (status=%lx)!\n",
status); goto err;
}
if (!efi_is_mixed())
efi_native_runtime_setup(); else
efi_thunk_runtime_setup();
/* * Apply more restrictive page table mapping attributes now that * SVAM() has been called and the firmware has performed all * necessary relocation fixups for the new virtual addresses.
*/
efi_runtime_update_mappings();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.