cregit-Linux how code gets into the kernel

Release 4.14 arch/x86/platform/efi/efi.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Common EFI (Extensible Firmware Interface) support functions
 * Based on Extensible Firmware Interface Specification version 1.0
 *
 * Copyright (C) 1999 VA Linux Systems
 * Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
 * Copyright (C) 1999-2002 Hewlett-Packard Co.
 *      David Mosberger-Tang <davidm@hpl.hp.com>
 *      Stephane Eranian <eranian@hpl.hp.com>
 * Copyright (C) 2005-2008 Intel Co.
 *      Fenghua Yu <fenghua.yu@intel.com>
 *      Bibo Mao <bibo.mao@intel.com>
 *      Chandramouli Narayanan <mouli@linux.intel.com>
 *      Huang Ying <ying.huang@intel.com>
 * Copyright (C) 2013 SuSE Labs
 *      Borislav Petkov <bp@suse.de> - runtime services VA mapping
 *
 * Copied from efi_32.c to eliminate the duplicated code between EFI
 * 32/64 support code. --ying 2007-10-26
 *
 * All EFI Runtime Services are not implemented yet as EFI only
 * supports physical mode addressing on SoftSDV. This is to be fixed
 * in a future version.  --drummond 1999-07-20
 *
 * Implemented EFI runtime services and virtual mode calls.  --davidm
 *
 * Goutham Rao: <goutham.rao@intel.com>
 *      Skip non-WB memory and ignore empty memory ranges.
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/efi.h>
#include <linux/efi-bgrt.h>
#include <linux/export.h>
#include <linux/bootmem.h>
#include <linux/slab.h>
#include <linux/memblock.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/time.h>
#include <linux/io.h>
#include <linux/reboot.h>
#include <linux/bcd.h>

#include <asm/setup.h>
#include <asm/efi.h>
#include <asm/e820/api.h>
#include <asm/time.h>
#include <asm/set_memory.h>
#include <asm/tlbflush.h>
#include <asm/x86_init.h>
#include <asm/uv/uv.h>


static struct efi efi_phys __initdata;

static efi_system_table_t efi_systab __initdata;


static efi_config_table_type_t arch_tables[] __initdata = {
#ifdef CONFIG_X86_UV
	{UV_SYSTEM_TABLE_GUID, "UVsystab", &efi.uv_systab},
#endif
	{NULL_GUID, NULL, NULL},
};


u64 efi_setup;		
/* efi setup_data physical address */


static int add_efi_memmap __initdata;

static int __init setup_add_efi_memmap(char *arg) { add_efi_memmap = 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Jackson18100.00%1100.00%
Total18100.00%1100.00%

early_param("add_efi_memmap", setup_add_efi_memmap);
static efi_status_t __init phys_efi_set_virtual_address_map( unsigned long memory_map_size, unsigned long descriptor_size, u32 descriptor_version, efi_memory_desc_t *virtual_map) { efi_status_t status; unsigned long flags; pgd_t *save_pgd; save_pgd = efi_call_phys_prolog(); /* Disable interrupts around EFI calls: */ local_irq_save(flags); status = efi_call_phys(efi_phys.set_virtual_address_map, memory_map_size, descriptor_size, descriptor_version, virtual_map); local_irq_restore(flags); efi_call_phys_epilog(save_pgd); return status; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying4864.86%120.00%
Ingo Molnar2432.43%240.00%
Matt Fleming11.35%120.00%
Mathias Krause11.35%120.00%
Total74100.00%5100.00%


void __init efi_find_mirror(void) { efi_memory_desc_t *md; u64 mirror_size = 0, total_size = 0; for_each_efi_memory_desc(md) { unsigned long long start = md->phys_addr; unsigned long long size = md->num_pages << EFI_PAGE_SHIFT; total_size += size; if (md->attribute & EFI_MEMORY_MORE_RELIABLE) { memblock_mark_mirror(start, size); mirror_size += size; } } if (mirror_size) pr_info("Memory: %lldM/%lldM mirrored memory\n", mirror_size>>20, total_size>>20); }

Contributors

PersonTokensPropCommitsCommitProp
Tony Luck8393.26%150.00%
Matt Fleming66.74%150.00%
Total89100.00%2100.00%

/* * Tell the kernel about the EFI memory map. This might include * more than the max 128 entries that can fit in the e820 legacy * (zeropage) memory map. */
static void __init do_add_efi_memmap(void) { efi_memory_desc_t *md; for_each_efi_memory_desc(md) { unsigned long long start = md->phys_addr; unsigned long long size = md->num_pages << EFI_PAGE_SHIFT; int e820_type; switch (md->type) { case EFI_LOADER_CODE: case EFI_LOADER_DATA: case EFI_BOOT_SERVICES_CODE: case EFI_BOOT_SERVICES_DATA: case EFI_CONVENTIONAL_MEMORY: if (md->attribute & EFI_MEMORY_WB) e820_type = E820_TYPE_RAM; else e820_type = E820_TYPE_RESERVED; break; case EFI_ACPI_RECLAIM_MEMORY: e820_type = E820_TYPE_ACPI; break; case EFI_ACPI_MEMORY_NVS: e820_type = E820_TYPE_NVS; break; case EFI_UNUSABLE_MEMORY: e820_type = E820_TYPE_UNUSABLE; break; case EFI_PERSISTENT_MEMORY: e820_type = E820_TYPE_PMEM; break; default: /* * EFI_RESERVED_TYPE EFI_RUNTIME_SERVICES_CODE * EFI_RUNTIME_SERVICES_DATA EFI_MEMORY_MAPPED_IO * EFI_MEMORY_MAPPED_IO_PORT_SPACE EFI_PAL_CODE */ e820_type = E820_TYPE_RESERVED; break; } e820__range_add(start, size, e820_type); } e820__update_table(e820_table); }

Contributors

PersonTokensPropCommitsCommitProp
Paul Jackson6346.32%222.22%
Cliff Wickman5238.24%111.11%
Ingo Molnar107.35%444.44%
Dan J Williams64.41%111.11%
Matt Fleming53.68%111.11%
Total136100.00%9100.00%


int __init efi_memblock_x86_reserve_range(void) { struct efi_info *e = &boot_params.efi_info; struct efi_memory_map_data data; phys_addr_t pmap; int rv; if (efi_enabled(EFI_PARAVIRT)) return 0; #ifdef CONFIG_X86_32 /* Can't handle data above 4GB at this time */ if (e->efi_memmap_hi) { pr_err("Memory map is above 4GB, disabling EFI.\n"); return -EINVAL; } pmap = e->efi_memmap; #else pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32)); #endif data.phys_map = pmap; data.size = e->efi_memmap_size; data.desc_size = e->efi_memdesc_size; data.desc_version = e->efi_memdesc_version; rv = efi_memmap_init_early(&data); if (rv) return rv; if (add_efi_memmap) do_add_efi_memmap(); WARN(efi.memmap.desc_version != 1, "Unexpected EFI_MEMORY_DESCRIPTOR version %ld", efi.memmap.desc_version); memblock_reserve(pmap, efi.memmap.nr_map * efi.memmap.desc_size); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying4927.37%19.09%
Matt Fleming3821.23%218.18%
Borislav Petkov2815.64%19.09%
Olof Johansson2011.17%19.09%
Ard Biesheuvel2011.17%218.18%
Paul Jackson126.70%19.09%
Daniel Kiper105.59%19.09%
Tejun Heo10.56%19.09%
Yinghai Lu10.56%19.09%
Total179100.00%11100.00%

#define OVERFLOW_ADDR_SHIFT (64 - EFI_PAGE_SHIFT) #define OVERFLOW_ADDR_MASK (U64_MAX << OVERFLOW_ADDR_SHIFT) #define U64_HIGH_BIT (~(U64_MAX >> 1))
static bool __init efi_memmap_entry_valid(const efi_memory_desc_t *md, int i) { u64 end = (md->num_pages << EFI_PAGE_SHIFT) + md->phys_addr - 1; u64 end_hi = 0; char buf[64]; if (md->num_pages == 0) { end = 0; } else if (md->num_pages > EFI_PAGES_MAX || EFI_PAGES_MAX - md->num_pages < (md->phys_addr >> EFI_PAGE_SHIFT)) { end_hi = (md->num_pages & OVERFLOW_ADDR_MASK) >> OVERFLOW_ADDR_SHIFT; if ((md->phys_addr & U64_HIGH_BIT) && !(end & U64_HIGH_BIT)) end_hi += 1; } else { return true; } pr_warn_once(FW_BUG "Invalid EFI memory map entries:\n"); if (end_hi) { pr_warn("mem%02u: %s range=[0x%016llx-0x%llx%016llx] (invalid)\n", i, efi_md_typeattr_format(buf, sizeof(buf), md), md->phys_addr, end_hi, end); } else { pr_warn("mem%02u: %s range=[0x%016llx-0x%016llx] (invalid)\n", i, efi_md_typeattr_format(buf, sizeof(buf), md), md->phys_addr, end); } return false; }

Contributors

PersonTokensPropCommitsCommitProp
Peter Jones191100.00%1100.00%
Total191100.00%1100.00%


static void __init efi_clean_memmap(void) { efi_memory_desc_t *out = efi.memmap.map; const efi_memory_desc_t *in = out; const efi_memory_desc_t *end = efi.memmap.map_end; int i, n_removal; 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; } if (n_removal > 0) { u64 size = efi.memmap.nr_map - n_removal; pr_warn("Removing %d invalid memory map entries.\n", n_removal); efi_memmap_install(efi.memmap.phys_map, size); } }

Contributors

PersonTokensPropCommitsCommitProp
Peter Jones160100.00%1100.00%
Total160100.00%1100.00%


void __init efi_print_memmap(void) { efi_memory_desc_t *md; int i = 0; for_each_efi_memory_desc(md) { char buf[64]; pr_info("mem%02u: %s range=[0x%016llx-0x%016llx] (%lluMB)\n", i++, efi_md_typeattr_format(buf, sizeof(buf), md), md->phys_addr, md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1, (md->num_pages >> (20 - EFI_PAGE_SHIFT))); } }

Contributors

PersonTokensPropCommitsCommitProp
Olof Johansson2835.44%114.29%
Huang Ying2430.38%114.29%
Laszlo Ersek1518.99%114.29%
Matt Fleming78.86%114.29%
Robert Elliott33.80%114.29%
Taku Izumi11.27%114.29%
Borislav Petkov11.27%114.29%
Total79100.00%7100.00%


static int __init efi_systab_init(void *phys) { if (efi_enabled(EFI_64BIT)) { efi_system_table_64_t *systab64; struct efi_setup_data *data = NULL; u64 tmp = 0; if (efi_setup) { data = early_memremap(efi_setup, sizeof(*data)); if (!data) return -ENOMEM; } systab64 = early_memremap((unsigned long)phys, sizeof(*systab64)); if (systab64 == NULL) { pr_err("Couldn't map the system table!\n"); if (data) early_memunmap(data, sizeof(*data)); return -ENOMEM; } efi_systab.hdr = systab64->hdr; efi_systab.fw_vendor = data ? (unsigned long)data->fw_vendor : systab64->fw_vendor; tmp |= data ? data->fw_vendor : systab64->fw_vendor; efi_systab.fw_revision = systab64->fw_revision; efi_systab.con_in_handle = systab64->con_in_handle; tmp |= systab64->con_in_handle; efi_systab.con_in = systab64->con_in; tmp |= systab64->con_in; efi_systab.con_out_handle = systab64->con_out_handle; tmp |= systab64->con_out_handle; efi_systab.con_out = systab64->con_out; tmp |= systab64->con_out; efi_systab.stderr_handle = systab64->stderr_handle; tmp |= systab64->stderr_handle; efi_systab.stderr = systab64->stderr; tmp |= systab64->stderr; efi_systab.runtime = data ? (void *)(unsigned long)data->runtime : (void *)(unsigned long)systab64->runtime; tmp |= data ? data->runtime : systab64->runtime; efi_systab.boottime = (void *)(unsigned long)systab64->boottime; tmp |= systab64->boottime; efi_systab.nr_tables = systab64->nr_tables; efi_systab.tables = data ? (unsigned long)data->tables : systab64->tables; tmp |= data ? data->tables : systab64->tables; early_memunmap(systab64, sizeof(*systab64)); if (data) early_memunmap(data, sizeof(*data)); #ifdef CONFIG_X86_32 if (tmp >> 32) { pr_err("EFI data located above 4GB, disabling EFI.\n"); return -EINVAL; } #endif } else { efi_system_table_32_t *systab32; systab32 = early_memremap((unsigned long)phys, sizeof(*systab32)); if (systab32 == NULL) { pr_err("Couldn't map the system table!\n"); return -ENOMEM; } efi_systab.hdr = systab32->hdr; efi_systab.fw_vendor = systab32->fw_vendor; efi_systab.fw_revision = systab32->fw_revision; efi_systab.con_in_handle = systab32->con_in_handle; efi_systab.con_in = systab32->con_in; efi_systab.con_out_handle = systab32->con_out_handle; efi_systab.con_out = systab32->con_out; efi_systab.stderr_handle = systab32->stderr_handle; efi_systab.stderr = systab32->stderr; efi_systab.runtime = (void *)(unsigned long)systab32->runtime; efi_systab.boottime = (void *)(unsigned long)systab32->boottime; efi_systab.nr_tables = systab32->nr_tables; efi_systab.tables = systab32->tables; early_memunmap(systab32, sizeof(*systab32)); } efi.systab = &efi_systab; /* * Verify the EFI Table */ if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { pr_err("System table signature incorrect!\n"); return -EINVAL; } if ((efi.systab->hdr.revision >> 16) == 0) pr_err("Warning: System table version %d.%02d, expected 1.00 or greater!\n", efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Olof Johansson44669.69%444.44%
Dave Young11517.97%111.11%
Huang Ying6810.62%111.11%
Daniel Kiper60.94%111.11%
Matt Fleming40.62%111.11%
Joe Perches10.16%111.11%
Total640100.00%9100.00%


static int __init efi_runtime_init32(void) { efi_runtime_services_32_t *runtime; runtime = early_memremap((unsigned long)efi.systab->runtime, sizeof(efi_runtime_services_32_t)); if (!runtime) { pr_err("Could not map the runtime service table!\n"); return -ENOMEM; } /* * We will only need *early* access to the SetVirtualAddressMap * EFI runtime service. All other runtime services will be called * via the virtual mapping. */ efi_phys.set_virtual_address_map = (efi_set_virtual_address_map_t *) (unsigned long)runtime->set_virtual_address_map; early_memunmap(runtime, sizeof(efi_runtime_services_32_t)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Olof Johansson3746.84%550.00%
Huang Ying3037.97%110.00%
Matt Fleming810.13%110.00%
Daniel Kiper22.53%110.00%
Linus Torvalds11.27%110.00%
Mathias Krause11.27%110.00%
Total79100.00%10100.00%


static int __init efi_runtime_init64(void) { efi_runtime_services_64_t *runtime; runtime = early_memremap((unsigned long)efi.systab->runtime, sizeof(efi_runtime_services_64_t)); if (!runtime) { pr_err("Could not map the runtime service table!\n"); return -ENOMEM; } /* * We will only need *early* access to the SetVirtualAddressMap * EFI runtime service. All other runtime services will be called * via the virtual mapping. */ efi_phys.set_virtual_address_map = (efi_set_virtual_address_map_t *) (unsigned long)runtime->set_virtual_address_map; early_memunmap(runtime, sizeof(efi_runtime_services_64_t)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Matt Fleming5873.42%120.00%
Olof Johansson1012.66%120.00%
Huang Ying810.13%120.00%
Daniel Kiper22.53%120.00%
Mathias Krause11.27%120.00%
Total79100.00%5100.00%


static int __init efi_runtime_init(void) { int rv; /* * Check out the runtime services table. We need to map * the runtime services table so that we can grab the physical * address of several of the EFI runtime functions, needed to * set the firmware into virtual mode. * * When EFI_PARAVIRT is in force then we could not map runtime * service memory region because we do not have direct access to it. * However, runtime services are available through proxy functions * (e.g. in case of Xen dom0 EFI implementation they call special * hypercall which executes relevant EFI functions) and that is why * they are always enabled. */ if (!efi_enabled(EFI_PARAVIRT)) { if (efi_enabled(EFI_64BIT)) rv = efi_runtime_init64(); else rv = efi_runtime_init32(); if (rv) return rv; } set_bit(EFI_RUNTIME_SERVICES, &efi.flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Matt Fleming4675.41%240.00%
Daniel Kiper1118.03%120.00%
Olof Johansson46.56%240.00%
Total61100.00%5100.00%


void __init efi_init(void) { efi_char16_t *c16; char vendor[100] = "unknown"; int i = 0; void *tmp; #ifdef CONFIG_X86_32 if (boot_params.efi_info.efi_systab_hi || boot_params.efi_info.efi_memmap_hi) { pr_info("Table located above 4GB, disabling EFI.\n"); return; } efi_phys.systab = (efi_system_table_t *)boot_params.efi_info.efi_systab; #else efi_phys.systab = (efi_system_table_t *) (boot_params.efi_info.efi_systab | ((__u64)boot_params.efi_info.efi_systab_hi<<32)); #endif if (efi_systab_init(efi_phys.systab)) return; efi.config_table = (unsigned long)efi.systab->tables; efi.fw_vendor = (unsigned long)efi.systab->fw_vendor; efi.runtime = (unsigned long)efi.systab->runtime; /* * Show what we know for posterity */ c16 = tmp = early_memremap(efi.systab->fw_vendor, 2); if (c16) { for (i = 0; i < sizeof(vendor) - 1 && *c16; ++i) vendor[i] = *c16++; vendor[i] = '\0'; } else pr_err("Could not map the firmware vendor!\n"); early_memunmap(tmp, 2); pr_info("EFI v%u.%.02u by %s\n", efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, vendor); if (efi_reuse_config(efi.systab->tables, efi.systab->nr_tables)) return; if (efi_config_init(arch_tables)) return; /* * Note: We currently don't support runtime services on an EFI * that doesn't match the kernel 32/64-bit mode. */ if (!efi_runtime_supported()) pr_info("No EFI runtime due to 32/64-bit mismatch with kernel\n"); else { if (efi_runtime_disabled() || efi_runtime_init()) { efi_memmap_unmap(); return; } } efi_clean_memmap(); if (efi_enabled(EFI_DBG)) efi_print_memmap(); }

Contributors

PersonTokensPropCommitsCommitProp
Olof Johansson22269.38%525.00%
Dave Young6219.38%315.00%
Huang Ying92.81%15.00%
Matt Fleming92.81%420.00%
Borislav Petkov72.19%15.00%
Paul Jackson41.25%210.00%
Peter Jones30.94%15.00%
Daniel Kiper20.62%15.00%
Leif Lindholm10.31%15.00%
Taku Izumi10.31%15.00%
Total320100.00%20100.00%


void __init efi_set_executable(efi_memory_desc_t *md, bool executable) { u64 addr, npages; addr = md->virt_addr; npages = md->num_pages; memrange_efi_to_native(&addr, &npages); if (executable) set_memory_x(addr, npages); else set_memory_nx(addr, npages); }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Garrett58100.00%1100.00%
Total58100.00%1100.00%


void __init runtime_code_page_mkexec(void) { efi_memory_desc_t *md; /* Make EFI runtime service code area executable */ for_each_efi_memory_desc(md) { if (md->type != EFI_RUNTIME_SERVICES_CODE) continue; efi_set_executable(md, true); } }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying2571.43%350.00%
Matt Fleming411.43%116.67%
Arjan van de Ven38.57%116.67%
Matthew Garrett38.57%116.67%
Total35100.00%6100.00%


void __init efi_memory_uc(u64 addr, unsigned long size) { unsigned long page_shift = 1UL << EFI_PAGE_SHIFT; u64 npages; npages = round_up(size, page_shift) / page_shift; memrange_efi_to_native(&addr, &npages); set_memory_uc(addr, npages); }

Contributors

PersonTokensPropCommitsCommitProp
Matt Fleming5098.04%150.00%
Mathias Krause11.96%150.00%
Total51100.00%2100.00%


void __init old_map_region(efi_memory_desc_t *md) { u64 start_pfn, end_pfn, end; unsigned long size; void *va; start_pfn = PFN_DOWN(md->phys_addr); size = md->num_pages << PAGE_SHIFT; end = md->phys_addr + size; end_pfn = PFN_UP(end); if (pfn_range_is_mapped(start_pfn, end_pfn)) { 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); md->virt_addr = (u64) (unsigned long) va; if (!va) pr_err("ioremap of 0x%llX failed!\n", (unsigned long long)md->phys_addr); }

Contributors

PersonTokensPropCommitsCommitProp
Borislav Petkov154100.00%1100.00%
Total154100.00%1100.00%

/* Merge contiguous regions of the same type and attribute */
static void __init efi_merge_regions(void) { efi_memory_desc_t *md, *prev_md = NULL; for_each_efi_memory_desc(md) { u64 prev_size; if (!prev_md) { prev_md = md; continue; } if (prev_md->type != md->type || prev_md->attribute != md->attribute) { prev_md = md; continue; } prev_size = prev_md->num_pages << EFI_PAGE_SHIFT; if (md->phys_addr == (prev_md->phys_addr + prev_size)) { prev_md->num_pages += md->num_pages; md->type = EFI_RESERVED_TYPE; md->attribute = 0; continue; } prev_md = md; } }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Garrett9684.96%120.00%
Huang Ying108.85%120.00%
Matt Fleming43.54%120.00%
Dave Young21.77%120.00%
Josh Boyer10.88%120.00%
Total113100.00%5100.00%


static void __init get_systab_virt_addr(efi_memory_desc_t *md) { unsigned long size; u64 end, systab; size = md->num_pages << EFI_PAGE_SHIFT; end = md->phys_addr + size; 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; } }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying6777.01%360.00%
Dave Young1921.84%120.00%
Borislav Petkov11.15%120.00%
Total87100.00%5100.00%


static void *realloc_pages(void *old_memmap, int old_shift) { void *ret; ret = (void *)__get_free_pages(GFP_KERNEL, old_shift + 1); if (!ret) goto out; /* * A first-time allocation doesn't have anything to copy. */ if (!old_memmap) return ret; memcpy(ret, old_memmap, PAGE_SIZE << old_shift); out: free_pages((unsigned long)old_memmap, old_shift); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Borislav Petkov77100.00%1100.00%
Total77100.00%1100.00%

/* * 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(). */
static inline void *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; }

Contributors

PersonTokensPropCommitsCommitProp
Matt Fleming55100.00%2100.00%
Total55100.00%2100.00%

/* * 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. */
static void *efi_map_next_entry(void *entry) { if (!efi_enabled(EFI_OLD_MEMMAP) && 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; }

Contributors

PersonTokensPropCommitsCommitProp
Matt Fleming70100.00%2100.00%
Total70100.00%2100.00%


static bool should_map_region(efi_memory_desc_t *md) { /* * Runtime regions always require runtime mappings (obviously). */ if (md->attribute & EFI_MEMORY_RUNTIME) return true; /* * 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)) return false; /* * Map all of RAM so that we can access arguments in the 1:1 * mapping when making EFI runtime calls. */ if (IS_ENABLED(CONFIG_EFI_MIXED) && !efi_is_native()) { if (md->type == EFI_CONVENTIONAL_MEMORY || md->type == EFI_LOADER_DATA || md->type == EFI_LOADER_CODE) return true; } /* * 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) return true; return false; }

Contributors

PersonTokensPropCommitsCommitProp
Matt Fleming91100.00%1100.00%
Total91100.00%1100.00%

/* * Map the efi memory ranges of the runtime services and update new_mmap with * virtual addresses. */
static void * __init efi_map_regions(int *count, int *pg_shift) { void *p, *new_memmap = NULL; unsigned long left = 0; unsigned long 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); get_systab_virt_addr(md); if (left < desc_size) { new_memmap = realloc_pages(new_memmap, *pg_shift); if (!new_memmap) return NULL; left += PAGE_SIZE << *pg_shift; (*pg_shift)++; } memcpy(new_memmap + (*count * desc_size), md, desc_size); left -= desc_size; (*count)++; } return new_memmap; }

Contributors

PersonTokensPropCommitsCommitProp
Dave Young5434.84%112.50%
Borislav Petkov4830.97%225.00%
Matthew Garrett2616.77%112.50%
Matt Fleming2616.77%337.50%
Huang Ying10.65%112.50%
Total155100.00%8100.00%


static void __init kexec_enter_virtual_mode(void) { #ifdef CONFIG_KEXEC_CORE efi_memory_desc_t *md; unsigned int num_pages; efi.systab = NULL; /* * We don't do virtual mode, since we don't do runtime services, on * non-native EFI. With efi=old_map, we don't do runtime services in * kexec kernel because in the initial boot something else might * have been mapped at these virtual addresses. */ if (!efi_is_native() || efi_enabled(EFI_OLD_MEMMAP)) { 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 */ get_systab_virt_addr(md); } /* * 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; } BUG_ON(!efi.systab); num_pages = ALIGN(efi.memmap.nr_map * efi.memmap.desc_size, PAGE_SIZE); num_pages >>= PAGE_SHIFT; if (efi_setup_page_tables(efi.memmap.phys_map, num_pages)) { clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); return; } efi_sync_low_kernel_mappings(); /* * 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_native_runtime_setup(); efi.set_virtual_address_map = NULL; if (efi_enabled(EFI_OLD_MEMMAP) && (__supported_pte_mask & _PAGE_NX)) runtime_code_page_mkexec(); /* clean DUMMY object */ efi_delete_dummy_variable(); #endif }

Contributors

PersonTokensPropCommitsCommitProp
Matt Fleming12752.48%650.00%
Borislav Petkov9539.26%18.33%
Dave Young114.55%216.67%
Sai Praneeth62.48%18.33%
Saurabh Tangri20.83%18.33%
Ard Biesheuvel10.41%18.33%
Total242100.00%12100.00%

/* * 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 old method which used to update that memory descriptor with the * virtual address obtained from ioremap() is still supported when the * kernel is booted with efi=old_map on its command line. Same old * method enabled the runtime services to be called without having to * thunk back into physical mode for every invocation. * * 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(). */
static void __init __efi_enter_virtual_mode(void) { int count = 0, pg_shift = 0; void *new_memmap = NULL; efi_status_t status; unsigned long pa; efi.systab = NULL; if (efi_alloc_page_tables()) { pr_err("Failed to allocate EFI page tables\n"); clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); return; } efi_merge_regions(); new_memmap = efi_map_regions(&count, &pg_shift); if (!new_memmap) { pr_err("Error reallocating memory, EFI runtime non-functional!\n"); clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); return; } pa = __pa(new_memmap); /* * 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"); clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); return; } if (efi_enabled(EFI_DBG)) { pr_info("EFI runtime memory map:\n"); efi_print_memmap(); } BUG_ON(!efi.systab); if (efi_setup_page_tables(pa, 1 << pg_shift)) { clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); return; } efi_sync_low_kernel_mappings(); if (efi_is_native()) { status = phys_efi_set_virtual_address_map( efi.memmap.desc_size * count, efi.memmap.desc_size, efi.memmap.desc_version, (efi_memory_desc_t *)pa); } else { status = efi_thunk_set_virtual_address_map( efi_phys.set_virtual_address_map, efi.memmap.desc_size * count, efi.memmap.desc_size, efi.memmap.desc_version, (efi_memory_desc_t *)pa); } 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; if (efi_is_native()) efi_native_runtime_setup(); else efi_thunk_runtime_setup(); efi.set_virtual_address_map = NULL; /* * 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(); /* clean DUMMY object */ efi_delete_dummy_variable(); }

Contributors

PersonTokensPropCommitsCommitProp
Matt Fleming13240.49%522.73%
Dave Young8626.38%418.18%
Huang Ying5617.18%14.55%
Borislav Petkov319.51%418.18%
Matthew Garrett82.45%313.64%
Seiji Aguchi51.53%14.55%
Sai Praneeth41.23%14.55%
Saurabh Tangri20.61%14.55%
Ard Biesheuvel10.31%14.55%
Olof Johansson10.31%14.55%
Total326100.00%22100.00%


void __init efi_enter_virtual_mode(void) { if (efi_enabled(EFI_PARAVIRT)) return; if (efi_setup) kexec_enter_virtual_mode(); else __efi_enter_virtual_mode(); efi_dump_pagetable(); }

Contributors

PersonTokensPropCommitsCommitProp
Borislav Petkov1963.33%133.33%
Daniel Kiper826.67%133.33%
Sai Praneeth310.00%133.33%
Total30100.00%3100.00%


static int __init arch_parse_efi_cmdline(char *str) { if (!str) { pr_warn("need at least one option\n"); return -EINVAL; } if (parse_option_str(str, "old_map")) set_bit(EFI_OLD_MEMMAP, &efi.flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Borislav Petkov2857.14%120.00%
Ricardo Neri1632.65%120.00%
Matt Fleming36.12%120.00%
Dave Young24.08%240.00%
Total49100.00%5100.00%

early_param("efi", arch_parse_efi_cmdline);

Overall Contributors

PersonTokensPropCommitsCommitProp
Olof Johansson77520.09%66.12%
Matt Fleming74719.37%1717.35%
Borislav Petkov49812.91%99.18%
Huang Ying44911.64%55.10%
Peter Jones3669.49%11.02%
Dave Young3579.26%99.18%
Matthew Garrett1914.95%55.10%
Paul Jackson1082.80%33.06%
Tony Luck832.15%11.02%
Cliff Wickman521.35%11.02%
Daniel Kiper411.06%22.04%
Ingo Molnar370.96%77.14%
Leif Lindholm320.83%22.04%
Ard Biesheuvel220.57%33.06%
Ricardo Neri160.41%11.02%
Laszlo Ersek150.39%11.02%
Sai Praneeth130.34%33.06%
Dan J Williams60.16%11.02%
Mathias Krause60.16%33.06%
Seiji Aguchi50.13%11.02%
Yinghai Lu40.10%11.02%
Saurabh Tangri40.10%11.02%
Arjan van de Ven30.08%11.02%
Paul Gortmaker30.08%11.02%
Feng Tang30.08%11.02%
Robert Elliott30.08%11.02%
Josh Triplett30.08%11.02%
David Howells30.08%11.02%
Prarit Bhargava20.05%11.02%
Taku Izumi20.05%11.02%
Joe Perches20.05%11.02%
Harvey Harrison10.03%11.02%
Laura Abbott10.03%11.02%
Josh Boyer10.03%11.02%
Linus Torvalds10.03%11.02%
Greg Kroah-Hartman10.03%11.02%
Tejun Heo10.03%11.02%
Total3857100.00%98100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.