x86_64 EFI runtime service support: EFI basic runtime service support
PATCH 1/4 x86_64 EFI runtime service support: EFI basic runtime service support
This patch adds basic runtime services support for EFI x86_64 system. The main file of the patch is the addition of efi_64.c for x86_64. This file is modeled after the EFI IA32 avatar. EFI runtime services initialization are implemented in efi_64.c. Some x86_64 specifics are worth noting here. On x86_64, parameters passed to EFI firmware services need to follow the EFI calling convention. For this purpose, a set of functions named efi_call<x> (<x> is the number of parameters) are implemented. EFI function calls are wrapped before calling the firmware service. The duplicated code between efi_32.c and efi_64.c is placed in efi.c to remove them from efi_32.c.
---
arch/x86/kernel/Makefile_64 | 1 arch/x86/kernel/efi.c | 483 ++++++++++++++++++++++++++++++++++++++++++ arch/x86/kernel/efi_64.c | 181 +++++++++++++++ arch/x86/kernel/efi_stub_64.S | 68 +++++ arch/x86/kernel/setup_64.c | 17 + arch/x86_64/Kconfig | 11 include/asm-x86/bootparam.h | 5 include/asm-x86/efi.h | 70 ++++++ include/asm-x86/fixmap_64.h | 3 9 files changed, 836 insertions(+), 3 deletions(-)
arch/x86/kernel/efi_64.c new file +181 -0
@@ -0,0 +1,181 @@ +/* +* x86_64 specific EFI support functions +* Based on Extensible Firmware Interface Specification version 1.0 +* +* Copyright (C) 2005-2008 Intel Co. +* Fenghua Yu <[email protected]> +* Bibo Mao <[email protected]> +* Chandramouli Narayanan <[email protected]> +* Huang Ying <[email protected]> +* +* Code to convert EFI to E820 map has been implemented in elilo bootloader +* based on a EFI patch by Edgar Hucek. Based on the E820 map, the page table +* is setup appropriately for EFI runtime code. +* - mouli 06/14/2007. +* +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/bootmem.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/efi.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <linux/reboot.h> + +#include <asm/setup.h> +#include <asm/page.h> +#include <asm/e820.h> +#include <asm/pgtable.h> +#include <asm/tlbflush.h> +#include <asm/cacheflush.h> +#include <asm/proto.h> +#include <asm/efi.h> + +int efi_time__initdata ; + +static pgd_t save_pgd__initdata ; +static unsigned long efi_flags__initdata ; +/* efi_lock protects efi physical mode call */ +static __initdata DEFINE_SPINLOCK (efi_lock); + +static int __init setup_noefi (char *arg) +{ + efi_enabled =0 ; +return 0 ; +} +early_param ("noefi" , setup_noefi); + +static int __init setup_efi_time (char *arg) +{ +if (arg && !strcmp ("on" , arg)) + efi_time =1 ; +return 0 ; +} +early_param ("efi_time" , setup_efi_time); + +static void __init early_mapping_set_exec (unsigned long start, +unsigned long end, +int executable) +{ +pte_t *kpte; + +while (start < end) { + kpte =lookup_address ((unsigned long )__va (start)); +BUG_ON (!kpte); +if (executable) +set_pte (kpte,pte_mkexec (*kpte)); +else +set_pte (kpte,__pte ((pte_val (*kpte) | _PAGE_NX) & \ + __supported_pte_mask)); +if (pte_huge (*kpte)) + start = (start +PMD_SIZE ) &PMD_MASK ; +else + start = (start +PAGE_SIZE ) &PAGE_MASK ; + } +} + +static void __init early_runtime_code_mapping_set_exec (int executable) +{ +efi_memory_desc_t *md; +void *p; + +/* Make EFI runtime service code area executable */ +for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { + md = p; +if (md->type ==EFI_RUNTIME_SERVICES_CODE ) { +unsigned long end; + end = md->phys_addr + (md->num_pages <<PAGE_SHIFT ); +early_mapping_set_exec (md->phys_addr, end, executable); + } + } +} + +void __init efi_call_phys_prelog (void )__acquires (efi_lock) +{ +unsigned long vaddress; + +/* +* Lock sequence is different from normal case because +* efi_flags is global +*/ +spin_lock (&efi_lock); +local_irq_save (efi_flags); +early_runtime_code_mapping_set_exec (1 ); + vaddress = (unsigned long )__va (0x0UL ); +pgd_val (save_pgd) =pgd_val (*pgd_offset_k (0x0UL )); +set_pgd (pgd_offset_k (0x0UL ), *pgd_offset_k (vaddress)); +global_flush_tlb (); +} + +void __init efi_call_phys_epilog (void )__releases (efi_lock) +{ +/* +* After the lock is released, the original page table is restored. +*/ +set_pgd (pgd_offset_k (0x0UL ), save_pgd); +early_runtime_code_mapping_set_exec (0 ); +global_flush_tlb (); +local_irq_restore (efi_flags); +spin_unlock (&efi_lock); +} + +/* +* We need to map the EFI memory map again after init_memory_mapping(). +*/ +void __init efi_map_memmap (void ) +{ + memmap.map =__va (memmap.phys_map); + memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size); +} + +void __init efi_reserve_bootmem (void ) +{ +reserve_bootmem_generic ((unsigned long )memmap.phys_map, + memmap.nr_map * memmap.desc_size); +} + +void __init runtime_code_page_mkexec (void ) +{ +efi_memory_desc_t *md; +void *p; + +/* Make EFI runtime service code area executable */ +for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { + md = p; +if (md->type ==EFI_RUNTIME_SERVICES_CODE ) +change_page_attr_addr (md->virt_addr, + md->num_pages, +PAGE_KERNEL_EXEC ); + } +global_flush_tlb (); +} + +void __iomem *__init efi_ioremap (unsigned long offset, +unsigned long size) +{ +static unsigned pages_mapped; +unsigned long last_addr; +unsigned i, pages; + + last_addr = offset + size -1 ; + offset &=PAGE_MASK ; + pages = (PAGE_ALIGN (last_addr) - offset) >>PAGE_SHIFT ; +if (pages_mapped + pages >MAX_EFI_IO_PAGES ) +return NULL ; + +for (i =0 ; i < pages; i++) { +set_fixmap_nocache (FIX_EFI_IO_MAP_FIRST_PAGE - pages_mapped, + offset); + offset +=PAGE_SIZE ; + pages_mapped++; + } + + return (void __iomem *)__fix_to_virt(FIX_EFI_IO_MAP_FIRST_PAGE - \ + (pages_mapped - pages)); +}
arch/x86/kernel/efi_stub_64.S new file +68 -0
68 lines not on this page · raw message
arch/x86/kernel/setup_64.c +16 -1
@@ -39,6 +39,7 @@#include <linux/dmi.h> #include <linux/dma-mapping.h> #include <linux/ctype.h> +#include <linux/efi.h> #include <asm/mtrr.h> #include <asm/uaccess.h>
@@ -269,6 +270,11 @@ rd_prompt = ((boot_params.hdr.ram_size &RAMDISK_PROMPT_FLAG ) !=0 ); rd_doload = ((boot_params.hdr.ram_size &RAMDISK_LOAD_FLAG ) !=0 );#endif +#ifdef CONFIG_EFI +if (!strncmp ((char *)&boot_params.efi_info.efi_loader_signature, +"EL64" ,4 )) + efi_enabled =1 ; +#endif setup_memory_region ();copy_edd ();
@@ -308,6 +314,8 @@discover_ebda ();init_memory_mapping (0 , (end_pfn_map <<PAGE_SHIFT )); +if (efi_enabled) +efi_init ();dmi_scan_machine ();
@@ -379,6 +387,12 @@*/ acpi_reserve_bootmem ();#endif + +if (efi_enabled) { +efi_map_memmap (); +efi_reserve_bootmem (); + } +/* * Find and reserve possible boot-time SMP configuration: */
@@ -448,7 +462,8 @@#ifdef CONFIG_VT #if defined(CONFIG_VGA_CONSOLE ) - conswitchp = &vga_con; +if (!efi_enabled || (efi_mem_type (0xa0000 ) !=EFI_CONVENTIONAL_MEMORY )) + conswitchp = &vga_con;#elif defined(CONFIG_DUMMY_CONSOLE ) conswitchp = &dummy_con;#endif
include/asm-x86/fixmap_64.h +3 -0
@@ -15,6 +15,7 @@#include <asm/apicdef.h> #include <asm/page.h> #include <asm/vsyscall.h> +#include <asm/efi.h> /* * Here we define all the compile-time 'special' virtual
@@ -41,6 +42,8 @@FIX_APIC_BASE ,/* local (CPU) APIC) -- required for SMP or not */ FIX_IO_APIC_BASE_0 ,FIX_IO_APIC_BASE_END =FIX_IO_APIC_BASE_0 +MAX_IO_APICS -1 , +FIX_EFI_IO_MAP_LAST_PAGE , +FIX_EFI_IO_MAP_FIRST_PAGE =FIX_EFI_IO_MAP_LAST_PAGE +MAX_EFI_IO_PAGES -1 , __end_of_fixed_addresses };
arch/x86_64/Kconfig +11 -0
@@ -269,6 +269,17 @@ depends on SMP && !MK8 default y +config EFI + bool "EFI runtime service support (EXPERIMENTAL)" + ---help--- + This enables the kernel to use any EFI runtime services that are + available (such as the EFI variable services). + This option is only useful on systems that have EFI firmware + and will result in a kernel image that is ~8k larger. However, + even with this option, the resultant kernel should continue to + boot on existing non-EFI platforms. For more information on + how to set up [U]EFI64 system, see Documentation/x86_64/uefi.txt. + config MATH_EMULATION bool
arch/x86/kernel/Makefile_64 +1 -0
@@ -33,6 +33,7 @@ obj-$(CONFIG_X86_VSMP) += vsmp_64.o obj-$(CONFIG_K8_NB) += k8.o obj-$(CONFIG_AUDIT) += audit_64.o +obj-$(CONFIG_EFI) += efi.o efi_64.o efi_stub_64.o obj-$(CONFIG_MODULES) += module_64.o obj-$(CONFIG_PCI) += early-quirks.o
include/asm-x86/bootparam.h +3 -2
@@ -54,13 +54,14 @@ };struct efi_info { -__u32 _pad1; +__u32 efi_loader_signature;__u32 efi_systab;__u32 efi_memdesc_size;__u32 efi_memdesc_version;__u32 efi_memmap;__u32 efi_memmap_size; -__u32 _pad2[2 ]; +__u32 efi_systab_hi; +__u32 efi_memmap_hi; };/* The so-called "zeropage" */
arch/x86/kernel/efi.c new file +483 -0
483 lines not on this page · raw message
include/asm-x86/efi.h new file +70 -0
70 lines not on this page · raw message