Commit 5b83683f authored by Huang, Ying's avatar Huang, Ying Committed by Ingo Molnar

x86: EFI 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.
Signed-off-by: default avatarChandramouli Narayanan <mouli@linux.intel.com>
Signed-off-by: default avatarHuang Ying <ying.huang@intel.com>
Cc: Andi Kleen <ak@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 8c8b8859
...@@ -959,7 +959,7 @@ config MTRR ...@@ -959,7 +959,7 @@ config MTRR
config EFI config EFI
def_bool n def_bool n
prompt "Boot from EFI support" prompt "Boot from EFI support"
depends on X86_32 && ACPI depends on ACPI
---help--- ---help---
This enables the kernel to boot on EFI platforms using This enables the kernel to boot on EFI platforms using
system configuration information passed to it from the firmware. system configuration information passed to it from the firmware.
......
...@@ -39,6 +39,7 @@ obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64.o ...@@ -39,6 +39,7 @@ obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64.o
obj-$(CONFIG_X86_VSMP) += vsmp_64.o obj-$(CONFIG_X86_VSMP) += vsmp_64.o
obj-$(CONFIG_K8_NB) += k8.o obj-$(CONFIG_K8_NB) += k8.o
obj-$(CONFIG_AUDIT) += audit_64.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_MODULES) += module_64.o
obj-$(CONFIG_PCI) += early-quirks.o obj-$(CONFIG_PCI) += early-quirks.o
......
This diff is collapsed.
/*
* x86_64 specific EFI support functions
* Based on Extensible Firmware Interface Specification version 1.0
*
* 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>
*
* 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>
static pgd_t save_pgd __initdata;
static unsigned long efi_flags __initdata;
static int __init setup_noefi(char *arg)
{
efi_enabled = 0;
return 0;
}
early_param("noefi", setup_noefi);
static void __init early_mapping_set_exec(unsigned long start,
unsigned long end,
int executable)
{
pte_t *kpte;
int level;
while (start < end) {
kpte = lookup_address((unsigned long)__va(start), &level);
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)
{
unsigned long vaddress;
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));
__flush_tlb_all();
}
void __init efi_call_phys_epilog(void)
{
/*
* 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);
__flush_tlb_all();
local_irq_restore(efi_flags);
}
/*
* 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);
}
__flush_tlb_all();
}
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));
}
/*
* Function calling ABI conversion from Linux to EFI for x86_64
*
* Copyright (C) 2007 Intel Corp
* Bibo Mao <bibo.mao@intel.com>
* Huang Ying <ying.huang@intel.com>
*/
#include <linux/linkage.h>
#define SAVE_XMM \
mov %rsp, %rax; \
subq $0x70, %rsp; \
and $~0xf, %rsp; \
mov %rax, (%rsp); \
mov %cr0, %rax; \
clts; \
mov %rax, 0x8(%rsp); \
movaps %xmm0, 0x60(%rsp); \
movaps %xmm1, 0x50(%rsp); \
movaps %xmm2, 0x40(%rsp); \
movaps %xmm3, 0x30(%rsp); \
movaps %xmm4, 0x20(%rsp); \
movaps %xmm5, 0x10(%rsp)
#define RESTORE_XMM \
movaps 0x60(%rsp), %xmm0; \
movaps 0x50(%rsp), %xmm1; \
movaps 0x40(%rsp), %xmm2; \
movaps 0x30(%rsp), %xmm3; \
movaps 0x20(%rsp), %xmm4; \
movaps 0x10(%rsp), %xmm5; \
mov 0x8(%rsp), %rsi; \
mov %rsi, %cr0; \
mov (%rsp), %rsp
ENTRY(efi_call0)
SAVE_XMM
subq $32, %rsp
call *%rdi
addq $32, %rsp
RESTORE_XMM
ret
ENTRY(efi_call1)
SAVE_XMM
subq $32, %rsp
mov %rsi, %rcx
call *%rdi
addq $32, %rsp
RESTORE_XMM
ret
ENTRY(efi_call2)
SAVE_XMM
subq $32, %rsp
mov %rsi, %rcx
call *%rdi
addq $32, %rsp
RESTORE_XMM
ret
ENTRY(efi_call3)
SAVE_XMM
subq $32, %rsp
mov %rcx, %r8
mov %rsi, %rcx
call *%rdi
addq $32, %rsp
RESTORE_XMM
ret
ENTRY(efi_call4)
SAVE_XMM
subq $32, %rsp
mov %r8, %r9
mov %rcx, %r8
mov %rsi, %rcx
call *%rdi
addq $32, %rsp
RESTORE_XMM
ret
ENTRY(efi_call5)
SAVE_XMM
subq $48, %rsp
mov %r9, 32(%rsp)
mov %r8, %r9
mov %rcx, %r8
mov %rsi, %rcx
call *%rdi
addq $48, %rsp
RESTORE_XMM
ret
ENTRY(efi_call6)
SAVE_XMM
mov (%rsp), %rax
mov 8(%rax), %rax
subq $48, %rsp
mov %r9, 32(%rsp)
mov %rax, 40(%rsp)
mov %r8, %r9
mov %rcx, %r8
mov %rsi, %rcx
call *%rdi
addq $48, %rsp
RESTORE_XMM
ret
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/crash_dump.h> #include <linux/crash_dump.h>
#include <linux/root_dev.h> #include <linux/root_dev.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/efi.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/edd.h> #include <linux/edd.h>
...@@ -299,6 +300,11 @@ void __init setup_arch(char **cmdline_p) ...@@ -299,6 +300,11 @@ void __init setup_arch(char **cmdline_p)
rd_prompt = ((boot_params.hdr.ram_size & RAMDISK_PROMPT_FLAG) != 0); rd_prompt = ((boot_params.hdr.ram_size & RAMDISK_PROMPT_FLAG) != 0);
rd_doload = ((boot_params.hdr.ram_size & RAMDISK_LOAD_FLAG) != 0); rd_doload = ((boot_params.hdr.ram_size & RAMDISK_LOAD_FLAG) != 0);
#endif #endif
#ifdef CONFIG_EFI
if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
"EL64", 4))
efi_enabled = 1;
#endif
ARCH_SETUP ARCH_SETUP
...@@ -341,6 +347,8 @@ void __init setup_arch(char **cmdline_p) ...@@ -341,6 +347,8 @@ void __init setup_arch(char **cmdline_p)
discover_ebda(); discover_ebda();
init_memory_mapping(0, (end_pfn_map << PAGE_SHIFT)); init_memory_mapping(0, (end_pfn_map << PAGE_SHIFT));
if (efi_enabled)
efi_init();
dmi_scan_machine(); dmi_scan_machine();
...@@ -414,6 +422,12 @@ void __init setup_arch(char **cmdline_p) ...@@ -414,6 +422,12 @@ void __init setup_arch(char **cmdline_p)
*/ */
acpi_reserve_bootmem(); acpi_reserve_bootmem();
#endif #endif
if (efi_enabled) {
efi_map_memmap();
efi_reserve_bootmem();
}
/* /*
* Find and reserve possible boot-time SMP configuration: * Find and reserve possible boot-time SMP configuration:
*/ */
...@@ -479,6 +493,7 @@ void __init setup_arch(char **cmdline_p) ...@@ -479,6 +493,7 @@ void __init setup_arch(char **cmdline_p)
#ifdef CONFIG_VT #ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE) #if defined(CONFIG_VGA_CONSOLE)
if (!efi_enabled || (efi_mem_type(0xa0000) != EFI_CONVENTIONAL_MEMORY))
conswitchp = &vga_con; conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE) #elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con; conswitchp = &dummy_con;
......
...@@ -54,13 +54,14 @@ struct sys_desc_table { ...@@ -54,13 +54,14 @@ struct sys_desc_table {
}; };
struct efi_info { struct efi_info {
__u32 _pad1; __u32 efi_loader_signature;
__u32 efi_systab; __u32 efi_systab;
__u32 efi_memdesc_size; __u32 efi_memdesc_size;
__u32 efi_memdesc_version; __u32 efi_memdesc_version;
__u32 efi_memmap; __u32 efi_memmap;
__u32 efi_memmap_size; __u32 efi_memmap_size;
__u32 _pad2[2]; __u32 efi_systab_hi;
__u32 efi_memmap_hi;
}; };
/* The so-called "zeropage" */ /* The so-called "zeropage" */
......
#ifndef _ASM_X86_EFI_H
#define _ASM_X86_EFI_H
#ifdef CONFIG_X86_32
#else /* !CONFIG_X86_32 */
#define MAX_EFI_IO_PAGES 100
extern u64 efi_call0(void *fp);
extern u64 efi_call1(void *fp, u64 arg1);
extern u64 efi_call2(void *fp, u64 arg1, u64 arg2);
extern u64 efi_call3(void *fp, u64 arg1, u64 arg2, u64 arg3);
extern u64 efi_call4(void *fp, u64 arg1, u64 arg2, u64 arg3, u64 arg4);
extern u64 efi_call5(void *fp, u64 arg1, u64 arg2, u64 arg3,
u64 arg4, u64 arg5);
extern u64 efi_call6(void *fp, u64 arg1, u64 arg2, u64 arg3,
u64 arg4, u64 arg5, u64 arg6);
#define efi_call_phys0(f) \
efi_call0((void *)(f))
#define efi_call_phys1(f, a1) \
efi_call1((void *)(f), (u64)(a1))
#define efi_call_phys2(f, a1, a2) \
efi_call2((void *)(f), (u64)(a1), (u64)(a2))
#define efi_call_phys3(f, a1, a2, a3) \
efi_call3((void *)(f), (u64)(a1), (u64)(a2), (u64)(a3))
#define efi_call_phys4(f, a1, a2, a3, a4) \
efi_call4((void *)(f), (u64)(a1), (u64)(a2), (u64)(a3), \
(u64)(a4))
#define efi_call_phys5(f, a1, a2, a3, a4, a5) \
efi_call5((void *)(f), (u64)(a1), (u64)(a2), (u64)(a3), \
(u64)(a4), (u64)(a5))
#define efi_call_phys6(f, a1, a2, a3, a4, a5, a6) \
efi_call6((void *)(f), (u64)(a1), (u64)(a2), (u64)(a3), \
(u64)(a4), (u64)(a5), (u64)(a6))
#define efi_call_virt0(f) \
efi_call0((void *)(efi.systab->runtime->f))
#define efi_call_virt1(f, a1) \
efi_call1((void *)(efi.systab->runtime->f), (u64)(a1))
#define efi_call_virt2(f, a1, a2) \
efi_call2((void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2))
#define efi_call_virt3(f, a1, a2, a3) \
efi_call3((void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \
(u64)(a3))
#define efi_call_virt4(f, a1, a2, a3, a4) \
efi_call4((void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \
(u64)(a3), (u64)(a4))
#define efi_call_virt5(f, a1, a2, a3, a4, a5) \
efi_call5((void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \
(u64)(a3), (u64)(a4), (u64)(a5))
#define efi_call_virt6(f, a1, a2, a3, a4, a5, a6) \
efi_call6((void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \
(u64)(a3), (u64)(a4), (u64)(a5), (u64)(a6))
#define efi_early_ioremap(addr, size) early_ioremap(addr, size)
#define efi_early_iounmap(vaddr, size) early_iounmap(vaddr, size)
extern void *efi_ioremap(unsigned long offset, unsigned long size);
extern int efi_time;
#endif /* CONFIG_X86_32 */
extern void efi_reserve_bootmem(void);
extern void efi_call_phys_prelog(void);
extern void efi_call_phys_epilog(void);
extern void runtime_code_page_mkexec(void);
#endif
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <asm/apicdef.h> #include <asm/apicdef.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/vsyscall.h> #include <asm/vsyscall.h>
#include <asm/efi.h>
/* /*
* Here we define all the compile-time 'special' virtual * Here we define all the compile-time 'special' virtual
...@@ -41,6 +42,8 @@ enum fixed_addresses { ...@@ -41,6 +42,8 @@ enum fixed_addresses {
FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */ FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */
FIX_IO_APIC_BASE_0, FIX_IO_APIC_BASE_0,
FIX_IO_APIC_BASE_END = FIX_IO_APIC_BASE_0 + MAX_IO_APICS-1, 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 __end_of_fixed_addresses
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment