From: Brian Maly <bmaly@redhat.com> Date: Thu, 20 Dec 2007 14:10:45 -0500 Subject: [x86_64] UEFI code support Message-id: 476ABE35.8070406@redhat.com O-Subject: Re: [RHEL5 patch] UEFI support for x86_64 Bugzilla: 253295 RH-Acked-by: Prarit Bhargava <prarit@redhat.com> RH-Acked-by: Jeff Garzik <jgarzik@redhat.com> RH-Acked-by: Jon Masters <jcm@redhat.com> Brian Maly wrote: > Don Zickus wrote: >> On Wed, Dec 19, 2007 at 09:23:02AM -0500, Brian Maly wrote: >> >>> resolves BZ 253295 >>> >>> >>> This is a preliminary patch to add x86_64 UEFI support to the RHEL5 >>> kenel. It is preliminary state is because we dont yet have the hardware >>> with UEFI enabled firmware which is needed for testing. There is some >>> likelyhood that some additional bits of code may be required in addition >>> to this patch being that this patch is a backport from a 2.6.21 patch. >>> If this is the case I will followup with additional patches as required. >>> Basic testing on non-UEFI hardware revealed no regressions. >>> In the interim we will need to depend on vendor testing of UEFI until >>> some enabled hardware arrives. >>> >>> There are some CONFIG options that are required: >>> CONFIG_EFI=y >>> CONFIG_FB_EFI=y >>> CONFIG_EFI_VARS=y >>> CONFIG_EFI_PARTITION=y >>> >>> Don, would you prefer a patch for the config files? >>> >> diff --git a/Documentation/i386/zero-page.txt b/Documentation/i386/zero-page.txt index c04a421..53d650e 100644 --- a/Documentation/i386/zero-page.txt +++ b/Documentation/i386/zero-page.txt @@ -31,11 +31,11 @@ Offset Type Description 0xb0 - 0x13f Free. Add more parameters here if you really need them. 0x140- 0x1be EDID_INFO Video mode setup -0x1c4 unsigned long EFI system table pointer -0x1c8 unsigned long EFI memory descriptor size -0x1cc unsigned long EFI memory descriptor version +0x1c4 unsigned long EFI system table pointer* +0x1c8 unsigned long EFI memory descriptor size* +0x1cc unsigned long EFI memory descriptor version* 0x1d0 unsigned long EFI memory descriptor map pointer -0x1d4 unsigned long EFI memory descriptor map size +0x1d4 unsigned long EFI memory descriptor map size* 0x1e0 unsigned long ALT_MEM_K, alternative mem check, in Kb 0x1e8 char number of entries in E820MAP (below) 0x1e9 unsigned char number of entries in EDDBUF (below) @@ -86,3 +86,13 @@ Offset Type Description 0x2d0 - 0xd00 E820MAP 0xd00 - 0xeff EDDBUF (edd.S) for disk signature read sector 0xd00 - 0xeeb EDDBUF (edd.S) for edd data + +Changes for x86_64 implementation: +--------------------------------- +For alignment purposes, the following parameters are rearranged. + +0x1b8 unsigned long EFI system table pointer +0x1c0 unsigned long EFI Loader signature +0x1c4 unsigned long EFI memory descriptor size +0x1c8 unsigned long EFI memory descriptor version +0x1cc unsigned long EFI memory descriptor map size diff --git a/Documentation/x86_64/uefi.txt b/Documentation/x86_64/uefi.txt new file mode 100644 index 0000000..a4816af --- /dev/null +++ b/Documentation/x86_64/uefi.txt @@ -0,0 +1,42 @@ +General note on [U]EFI x86_64 support +------------------------------------- + +This provides documentation on [U]EFI support for x86_64 architecture. +The nomenclature EFI and UEFI are used intechangeably in this document. + +Although the tools below are _not_ needed for building the kernel, +the needed bootloader support and associated tools for x86_64 platforms +with EFI firmware and specifications are listed below. + +1. UEFI specification: http://www.uefi.org + +2. Booting EFI64 enabled kernel requires boot loader support. +Patches to elilo and gnu-efi library with x86_64 support and documentation +have been submitted to respective project maintainers. + elilo: http://sourceforge.net/projects/elilo + gnu-efi library: http://sourceforge.net/projects/gnu-efi/ + gnu-efi-3.0d release now supports [U]EFI x86_64. + +3. The tool to convert ELF to PE-COFF image: + binutils-2.17.50.0.14 supports Intel64 EFI. + see http://www.kernel.org/pub/linux/devel/binutils/ + [ elilo/gnu-efi with x86_64 support need this binutils support ] + +4. x86_64 platform with EFI/UEFI firmware. + +Mechanics: +--------- +- Apply the EFI64 kernel patches and build with the following configuration. + CONFIG_EFI=y + EFI_FB=y + CONFIG_FRAMEBUFFER_CONSOLE=y + CONFIG_EFI_VARS=y + +- Create a VFAT partition on the disk +- Copy the following to the VFAT partition: + elilo bootloader with x86_64 support and elilo configuration file + efi64 kernel image and initrd. Instructions on building elilo + and its dependencies can be found in the elilo sourceforge project. +- Boot to EFI shell and invoke elilo choosing efi64 kernel image +- On UEFI2.0 firmware systems, pass vga=normal for boot messages to show up + console. You can pass along the 'resume' boot option to test suspend/resume. diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index fe3be72..8aec979 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -218,6 +218,19 @@ config X86_HT depends on SMP && !MK8 && !X86_64_XEN default y +config EFI + bool "Boot from EFI support (EXPERIMENTAL)" + ---help--- + This enables the the kernel to boot on EFI platforms using + system configuration information passed to it from the firmware. + This also 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 diff --git a/arch/x86_64/kernel/Makefile b/arch/x86_64/kernel/Makefile index f6b0cf5..d5eeace 100644 --- a/arch/x86_64/kernel/Makefile +++ b/arch/x86_64/kernel/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_X86_PM_TIMER) += pmtimer.o obj-$(CONFIG_X86_VSMP) += vsmp.o obj-$(CONFIG_K8_NB) += k8.o obj-$(CONFIG_AUDIT) += audit.o +obj-$(CONFIG_EFI) += efi.o efi_callwrap.o obj-$(CONFIG_MODULES) += module.o diff --git a/arch/x86_64/kernel/aperture.c b/arch/x86_64/kernel/aperture.c index d8a434c..d99c749 100644 --- a/arch/x86_64/kernel/aperture.c +++ b/arch/x86_64/kernel/aperture.c @@ -82,6 +82,10 @@ static int __init aperture_valid(u64 aper_base, u32 aper_size) printk("Aperture pointing to e820 RAM. Ignoring.\n"); return 0; } + if (e820_any_mapped(aper_base, aper_base + aper_size, E820_RUNTIME_CODE)) { + printk("Aperture pointing to runtime code. Ignoring.\n"); + return 0; + } return 1; } diff --git a/arch/x86_64/kernel/e820.c b/arch/x86_64/kernel/e820.c index 52b313c..54a777d 100644 --- a/arch/x86_64/kernel/e820.c +++ b/arch/x86_64/kernel/e820.c @@ -17,6 +17,7 @@ #include <linux/kexec.h> #include <linux/module.h> #include <linux/mm.h> +#include <linux/efi.h> #include <asm/pgtable.h> #include <asm/page.h> @@ -278,6 +279,7 @@ void __init e820_reserve_resources(void) case E820_RAM: res->name = "System RAM"; break; case E820_ACPI: res->name = "ACPI Tables"; break; case E820_NVS: res->name = "ACPI Non-volatile Storage"; break; + case E820_RUNTIME_CODE: res->name = "EFI runtime code"; break; default: res->name = "reserved"; } res->start = e820.map[i].addr; @@ -384,6 +386,9 @@ void __init e820_print_map(char *who) case E820_NVS: printk("(ACPI NVS)\n"); break; + case E820_RUNTIME_CODE: + printk("(runtime code)\n"); + break; default: printk("type %u\n", e820.map[i].type); break; } @@ -624,6 +629,8 @@ void __init setup_memory_region(void) * Otherwise fake a memory map; one section from 0k->640k, * the next section from 1mb->appropriate_mem_k */ + if (efi_enabled) + efi_init(); sanitize_e820_map(E820_MAP, &E820_MAP_NR); if (copy_e820_map(E820_MAP, E820_MAP_NR) < 0) { unsigned long mem_size; diff --git a/arch/x86_64/kernel/efi.c b/arch/x86_64/kernel/efi.c new file mode 100644 index 0000000..6f9c4df --- /dev/null +++ b/arch/x86_64/kernel/efi.c @@ -0,0 +1,531 @@ +/* + * Extensible Firmware Interface + * + * 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> + * + * 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. NDIS wrapper code from + * sourceforge project is adapted here for UEFI wrapper call. + * - mouli 06/14/2007. + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/types.h> +#include <linux/time.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 <asm/setup.h> +#include <asm/bootsetup.h> +#include <asm/io.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-x86_64/eficallwrap.h> + +#define EFI_DEBUG 0 +#define PFX "EFI: " + +struct efi efi; +EXPORT_SYMBOL(efi); + +extern void (*machine_emergency_restart_func)(void); + +struct efi efi_phys __initdata; +struct efi_memory_map memmap; +static efi_system_table_t efi_systab __initdata; + +static unsigned long efi_rt_eflags; +/* efi_rt_lock protects efi physical mode call */ +static spinlock_t efi_rt_lock = SPIN_LOCK_UNLOCKED; +static pgd_t save_pgd; + +static efi_status_t _efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) +{ + return LIN2WIN2((void*)efi.systab->runtime->get_time, + (u64)tm, + (u64)tc); +} + +static efi_status_t _efi_set_time(efi_time_t *tm) +{ + return LIN2WIN1((void*)efi.systab->runtime->set_time, + (u64)tm); +} + +static efi_status_t _efi_get_wakeup_time( + efi_bool_t *enabled, efi_bool_t *pending, efi_time_t *tm) +{ + return LIN2WIN3((void*)efi.systab->runtime->get_wakeup_time, + (u64)enabled, + (u64)pending, + (u64)tm); +} + +static efi_status_t _efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) +{ + return LIN2WIN2((void*)efi.systab->runtime->set_wakeup_time, + (u64)enabled, + (u64)tm); +} + +static efi_status_t _efi_get_variable( + efi_char16_t *name, efi_guid_t *vendor, u32 *attr, + unsigned long *data_size, void *data) +{ + return LIN2WIN5((void*)efi.systab->runtime->get_variable, + (u64)name, + (u64)vendor, + (u64)attr, + (u64)data_size, + (u64)data); +} + +static efi_status_t _efi_get_next_variable( + unsigned long *name_size, efi_char16_t *name, efi_guid_t *vendor) +{ + return LIN2WIN3((void*)efi.systab->runtime->get_next_variable, + (u64)name_size, + (u64)name, + (u64)vendor); +} + +static efi_status_t _efi_set_variable( + efi_char16_t *name, efi_guid_t *vendor, + u64 attr, u64 data_size, void *data) +{ + return LIN2WIN5((void*)efi.systab->runtime->set_variable, + (u64)name, + (u64)vendor, + (u64)attr, + (u64)data_size, + (u64)data); +} + +static efi_status_t _efi_get_next_high_mono_count(u32 *count) +{ + return LIN2WIN1((void*)efi.systab->runtime->get_next_high_mono_count, + (u64)count); +} + +static efi_status_t _efi_reset_system( + int reset_type, efi_status_t status, + unsigned long data_size, efi_char16_t *data) +{ + return LIN2WIN4((void*)efi.systab->runtime->reset_system, + (u64)reset_type, + (u64)status, + (u64)data_size, + (u64)data); +} + +static efi_status_t _efi_set_virtual_address_map( + unsigned long memory_map_size, + unsigned long descriptor_size, + u32 descriptor_version, efi_memory_desc_t *virtual_map) +{ + return LIN2WIN4((void*)efi.systab->runtime->set_virtual_address_map, + (u64)memory_map_size, + (u64)descriptor_size, + (u64)descriptor_version, + (u64)virtual_map); +} + +static void __init efi_call_phys_prelog(void) __acquires(efi_rt_lock) +{ + unsigned long vaddress; + + spin_lock(&efi_rt_lock); + local_irq_save(efi_rt_eflags); + + 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(); +} + +static void __init efi_call_phys_epilog(void) __releases(efi_rt_lock) +{ + /* + * After the lock is released, the original page table is restored. + */ + set_pgd(pgd_offset_k(0x0UL), save_pgd); + global_flush_tlb(); + local_irq_restore(efi_rt_eflags); + spin_unlock(&efi_rt_lock); +} + +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; + + efi_call_phys_prelog(); + status = LIN2WIN4(efi_phys.set_virtual_address_map, + (u64)memory_map_size, + (u64)descriptor_size, + (u64)descriptor_version, + (u64)virtual_map); + efi_call_phys_epilog(); + return status; +} + +static efi_status_t __init phys_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) +{ + + efi_status_t status; + + efi_call_phys_prelog(); + status = LIN2WIN2(efi_phys.get_time, + (u64)tm, + (u64)tc); + efi_call_phys_epilog(); + return status; +} + +inline int efi_set_rtc_mmss(unsigned long nowtime) +{ + int real_seconds, real_minutes; + efi_status_t status; + efi_time_t eft; + efi_time_cap_t cap; + + spin_lock(&efi_rt_lock); + status = efi.get_time(&eft, &cap); + spin_unlock(&efi_rt_lock); + if (status != EFI_SUCCESS) { + printk(KERN_ERR PFX "Oops: efitime: can't read time!\n"); + return -1; + } + + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - eft.minute) + 15)/30) & 1) + real_minutes += 30; + real_minutes %= 60; + eft.minute = real_minutes; + eft.second = real_seconds; + + spin_lock(&efi_rt_lock); + status = efi.set_time(&eft); + spin_unlock(&efi_rt_lock); + if (status != EFI_SUCCESS) { + printk(KERN_ERR PFX "Oops: efitime: can't write time!\n"); + return -1; + } + return 0; +} +/* + * This is used during kernel init before runtime + * services have been remapped and also during suspend, therefore, + * we'll need to call both in physical and virtual modes. + */ +inline unsigned long efi_get_time(void) +{ + efi_status_t status; + efi_time_t eft; + efi_time_cap_t cap; + + if (efi.get_time) { + /* if we are in virtual mode use remapped function */ + status = efi.get_time(&eft, &cap); + } else { + /* we are in physical mode */ + status = phys_efi_get_time(&eft, &cap); + } + if (status != EFI_SUCCESS) + printk(KERN_ERR PFX "Oops: efitime: can't read time status: 0x%lx\n",status); + + return mktime(eft.year, eft.month, eft.day, eft.hour, + eft.minute, eft.second); +} + +/* + * We need to map the EFI memory map again after paging_init(). + */ +void __init efi_map_memmap(void) +{ + int i; + + memmap.map = __va((unsigned long)memmap.phys_map); + memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size); + + /* Make EFI runtime code area executable */ + for (i = 0; i < e820.nr_map; i++) { + switch (e820.map[i].type) { + case E820_RUNTIME_CODE: + init_memory_mapping( + e820.map[i].addr, + e820.map[i].addr + e820.map[i].size); + break; + default: + break; + } + } +} + +/* Override function for emergency restart on EFI enabled systems */ +static void efi_emergency_restart(void) +{ + /* If EFI enabled, reset system through EFI protocol. */ + efi.reset_system(EFI_RESET_WARM, EFI_SUCCESS, 0, NULL); + return; +} + +void __init efi_init(void) +{ + efi_config_table_t *config_tables; + efi_runtime_services_t *runtime; + efi_char16_t *c16; + char vendor[100] = "unknown"; + int i = 0; + + memset(&efi, 0, sizeof(efi) ); + memset(&efi_phys, 0, sizeof(efi_phys)); + + efi_phys.systab = (efi_system_table_t *)EFI_SYSTAB; + memmap.phys_map = (void*)EFI_MEMMAP; + memmap.nr_map = EFI_MEMMAP_SIZE/EFI_MEMDESC_SIZE; + memmap.desc_version = EFI_MEMDESC_VERSION; + memmap.desc_size = EFI_MEMDESC_SIZE; + + efi.systab = (efi_system_table_t *) early_ioremap( + (unsigned long)efi_phys.systab, + sizeof(efi_system_table_t)); + memcpy(&efi_systab, efi.systab, sizeof(efi_system_table_t)); + efi.systab = &efi_systab; + /* + * Verify the EFI Table + */ + if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) + printk(KERN_ERR PFX "Woah! EFI system table signature incorrect\n"); + if ((efi.systab->hdr.revision ^ EFI_SYSTEM_TABLE_REVISION) >> 16 != 0) + printk(KERN_ERR PFX + "Warning: EFI system table major version mismatch: " + "got %d.%02d, expected %d.%02d\n", + efi.systab->hdr.revision >> 16, + efi.systab->hdr.revision & 0xffff, + EFI_SYSTEM_TABLE_REVISION >> 16, + EFI_SYSTEM_TABLE_REVISION & 0xffff); + /* + * Grab some details from the system table + */ + config_tables = (efi_config_table_t *)efi.systab->tables; + runtime = efi.systab->runtime; + + /* + * Show what we know for posterity + */ + c16 = (efi_char16_t *) early_ioremap(efi.systab->fw_vendor, 2); + if (!probe_kernel_address(c16, i)) { + for (i = 0; i < sizeof(vendor) && *c16; ++i) + vendor[i] = *c16++; + vendor[i] = '\0'; + } + + printk(KERN_INFO PFX "EFI v%u.%.02u by %s \n", + efi.systab->hdr.revision >> 16, + efi.systab->hdr.revision & 0xffff, vendor); + + /* + * Let's see what config tables the firmware passed to us. + */ + config_tables = (efi_config_table_t *)early_ioremap( efi.systab->tables, + efi.systab->nr_tables * sizeof(efi_config_table_t)); + if (config_tables == NULL) + printk(KERN_ERR PFX "Could not map EFI Configuration Table!\n"); + + for (i = 0; i < efi.systab->nr_tables; i++) { + if (efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID) == 0) { + efi.mps = config_tables[i].table; + printk(KERN_INFO " MPS=0x%lx ", config_tables[i].table); + } else + if (efi_guidcmp(config_tables[i].guid, ACPI_20_TABLE_GUID) == 0) { + efi.acpi20 = config_tables[i].table; + printk(KERN_INFO " ACPI 2.0=0x%lx ", config_tables[i].table); + } else + if (efi_guidcmp(config_tables[i].guid, ACPI_TABLE_GUID) == 0) { + efi.acpi = config_tables[i].table; + printk(KERN_INFO " ACPI=0x%lx ", config_tables[i].table); + } else + if (efi_guidcmp(config_tables[i].guid, SMBIOS_TABLE_GUID) == 0) { + efi.smbios = config_tables[i].table; + printk(KERN_INFO " SMBIOS=0x%lx ", config_tables[i].table); + } else + if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) { + efi.hcdp = config_tables[i].table; + printk(KERN_INFO " HCDP=0x%lx ", config_tables[i].table); + } else + if (efi_guidcmp(config_tables[i].guid, UGA_IO_PROTOCOL_GUID) == 0) { + efi.uga = config_tables[i].table; + printk(KERN_INFO " UGA=0x%lx ", config_tables[i].table); + } + } + printk(KERN_INFO "\n"); + + /* + * 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. + */ + runtime = (efi_runtime_services_t *) early_ioremap((unsigned long) + efi.systab->runtime, + sizeof(efi_runtime_services_t)); + if (runtime != NULL) { + /* + * We will only need *early* access to the following + * two EFI runtime services before set_virtual_address_map + * is invoked. + */ + efi_phys.get_time = (efi_get_time_t *) runtime->get_time; + efi_phys.set_virtual_address_map = + (efi_set_virtual_address_map_t *)runtime->set_virtual_address_map; + } else + printk(KERN_ERR PFX "Could not map the runtime service table!\n"); + /* Map the EFI memory map for use until paging_init() */ + memmap.map = (efi_memory_desc_t *) early_ioremap( + (unsigned long) EFI_MEMMAP, + EFI_MEMMAP_SIZE); + if (memmap.map == NULL) + printk(KERN_ERR PFX "Could not map the EFI memory map!\n"); + if (EFI_MEMDESC_SIZE != sizeof(efi_memory_desc_t)) { + printk(KERN_WARNING PFX "Kernel-defined memdesc" + "doesn't match the one from EFI!\n"); + } + memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size); + /* Override the emergency restart function */ + machine_emergency_restart_func = efi_emergency_restart; +} + +/* + * This function will switch the EFI runtime services to virtual mode. + * Essentially, look through the EFI memmap and map every region that + * has the runtime attribute bit set in its memory descriptor and update + * that memory descriptor with the virtual address obtained from ioremap(). + * This enables the runtime services to be called without having to + * thunk back into physical mode for every invocation. + */ +void __init efi_enter_virtual_mode(void) +{ + efi_memory_desc_t *md; + efi_status_t status; + unsigned long end; + void *p; + + efi.systab = NULL; + for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { + md = p; + if (!(md->attribute & EFI_MEMORY_RUNTIME)) + continue; + if (md->attribute & EFI_MEMORY_WB) + md->virt_addr = (unsigned long)__va(md->phys_addr); + else if (md->attribute & (EFI_MEMORY_UC | EFI_MEMORY_WC)) + md->virt_addr = (unsigned long)ioremap(md->phys_addr, + md->num_pages << EFI_PAGE_SHIFT); + end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT); + if ((md->phys_addr <= (unsigned long)efi_phys.systab) && + ((unsigned long)efi_phys.systab < end)) + efi.systab = (efi_system_table_t *) + (md->virt_addr - md->phys_addr + + (unsigned long)efi_phys.systab); + } + + if (!efi.systab) + BUG(); + + status = phys_efi_set_virtual_address_map( + memmap.desc_size * memmap.nr_map, + memmap.desc_size, + memmap.desc_version, + memmap.phys_map); + + if (status != EFI_SUCCESS) { + printk (KERN_ALERT "You are screwed! " + "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.get_time = (efi_get_time_t *)_efi_get_time; + efi.set_time = (efi_set_time_t *)_efi_set_time; + efi.get_wakeup_time = (efi_get_wakeup_time_t *)_efi_get_wakeup_time; + efi.set_wakeup_time = (efi_set_wakeup_time_t *)_efi_set_wakeup_time; + efi.get_variable = (efi_get_variable_t *)_efi_get_variable; + efi.get_next_variable = (efi_get_next_variable_t *)_efi_get_next_variable; + efi.set_variable = (efi_set_variable_t *)_efi_set_variable; + efi.get_next_high_mono_count = (efi_get_next_high_mono_count_t *) + _efi_get_next_high_mono_count; + efi.reset_system = (efi_reset_system_t *)_efi_reset_system; + efi.set_virtual_address_map = (efi_set_virtual_address_map_t *) + _efi_set_virtual_address_map; +} + +/* + * Convenience functions to obtain memory types and attributes + */ +u32 efi_mem_type(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->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; +} diff --git a/arch/x86_64/kernel/efi_callwrap.c b/arch/x86_64/kernel/efi_callwrap.c new file mode 100644 index 0000000..b0635cd --- /dev/null +++ b/arch/x86_64/kernel/efi_callwrap.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2006 Giridhar Pemmasani + * Copyright (C) 2007-2010 Intel Corp + * Contributed by Chandramouli Narayanan<mouli@linux.intel.com> + * Adapted NDIS wrapper macros from http://ndiswrapper.sourceforge.net + * for EFI x86_64 linux support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/types.h> +#include <linux/time.h> +#include <linux/spinlock.h> +#include <linux/bootmem.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/efi.h> + +#define alloc_win_stack_frame(argc) \ + "subq $" #argc "*8, %%rsp\n\t" +#define free_win_stack_frame(argc) \ + "addq $" #argc "*8, %%rsp\n\t" + +/* m is index of Windows arg required, n is total number of args to + * function Windows arg 1 should be at 0(%rsp), arg 2 at 8(%rsp) and + * so on, after stack frame is allocated, which starts at -n*8(%rsp) + * when stack frame is allocated. 4 > m >= n. +*/ + +#define lin2win_win_arg(m,n) "(" #m "-1-" #n ")*8(%%rsp)" + +/* volatile args for Windows function must be in clobber / output list */ + +efi_status_t LIN2WIN0(void *func) +{ + u64 ret, dummy; + register u64 r8 __asm__("r8"); + register u64 r9 __asm__("r9"); + register u64 r10 __asm__("r10"); + register u64 r11 __asm__("r11"); + __asm__ __volatile__( + alloc_win_stack_frame(4) + "call *%[fptr]\n\t" + free_win_stack_frame(4) + : "=a" (ret), "=c" (dummy), "=d" (dummy), + "=r" (r8), "=r" (r9), "=r" (r10), "=r" (r11) + : [fptr] "r" (func)); + return ret; +} + +efi_status_t LIN2WIN1(void *func, u64 arg1) +{ + u64 ret, dummy; + register u64 r8 __asm__("r8"); + register u64 r9 __asm__("r9"); + register u64 r10 __asm__("r10"); + register u64 r11 __asm__("r11"); + __asm__ __volatile__( + alloc_win_stack_frame(4) + "call *%[fptr]\n\t" + free_win_stack_frame(4) + : "=a" (ret), "=c" (dummy), "=d" (dummy), + "=r" (r8), "=r" (r9), "=r" (r10), "=r" (r11) + : "c" (arg1), + [fptr] "r" (func)); + return ret; +} + +efi_status_t LIN2WIN2(void *func, u64 arg1, u64 arg2) +{ + u64 ret, dummy; + register u64 r8 __asm__("r8"); + register u64 r9 __asm__("r9"); + register u64 r10 __asm__("r10"); + register u64 r11 __asm__("r11"); + __asm__ __volatile__( + alloc_win_stack_frame(4) + "call *%[fptr]\n\t" + free_win_stack_frame(4) + : "=a" (ret), "=c" (dummy), "=d" (dummy), + "=r" (r8), "=r" (r9), "=r" (r10), "=r" (r11) + : "c" (arg1), "d" (arg2), + [fptr] "r" (func)); + return ret; +} + +efi_status_t LIN2WIN3( + void *func, + u64 arg1, + u64 arg2, + u64 arg3) +{ + u64 ret, dummy; + register u64 r8 __asm__("r8") = (u64)arg3; + register u64 r9 __asm__("r9"); + register u64 r10 __asm__("r10"); + register u64 r11 __asm__("r11"); + __asm__ __volatile__( + alloc_win_stack_frame(4) + "call *%[fptr]\n\t" + free_win_stack_frame(4) + : "=a" (ret), "=c" (dummy), "=d" (dummy), + "=r" (r8), "=r" (r9), "=r" (r10), "=r" (r11) + : "c" (arg1), "d" (arg2), "r" (r8), + [fptr] "r" (func)); + return ret; +} + +efi_status_t LIN2WIN4( + void *func, + u64 arg1, + u64 arg2, + u64 arg3, + u64 arg4) +{ + u64 ret, dummy; + register u64 r8 __asm__("r8") = (u64)arg3; + register u64 r9 __asm__("r9") = (u64)arg4; + register u64 r10 __asm__("r10"); + register u64 r11 __asm__("r11"); + __asm__ __volatile__( + alloc_win_stack_frame(4) + "call *%[fptr]\n\t" + free_win_stack_frame(4) + : "=a" (ret), "=c" (dummy), "=d" (dummy), + "=r" (r8), "=r" (r9), "=r" (r10), "=r" (r11) + : "c" (arg1), "d" (arg2), "r" (r8), "r" (r9), + [fptr] "r" (func)); + return ret; +} + +efi_status_t LIN2WIN5( + void *func, + u64 arg1, + u64 arg2, + u64 arg3, + u64 arg4, + u64 arg5) +{ + u64 ret, dummy; + register u64 r8 __asm__("r8") = (u64)arg3; + register u64 r9 __asm__("r9") = (u64)arg4; + register u64 r10 __asm__("r10"); + register u64 r11 __asm__("r11"); + __asm__ __volatile__( + "mov %[rarg5], " lin2win_win_arg(5,6) "\n\t" + alloc_win_stack_frame(6) + "call *%[fptr]\n\t" + free_win_stack_frame(6) + : "=a" (ret), "=c" (dummy), "=d" (dummy), + "=r" (r8), "=r" (r9), "=r" (r10), "=r" (r11) + : "c" (arg1), "d" (arg2), "r" (r8), "r" (r9), + [rarg5] "r" ((unsigned long long)arg5), + [fptr] "r" (func)); + return ret; +} + +efi_status_t LIN2WIN6( + void *func, + u64 arg1, + u64 arg2, + u64 arg3, + u64 arg4, + u64 arg5, + u64 arg6) +{ + u64 ret, dummy; + register u64 r8 __asm__("r8") = (u64)arg3; + register u64 r9 __asm__("r9") = (u64)arg4; + register u64 r10 __asm__("r10"); + register u64 r11 __asm__("r11"); + __asm__ __volatile__( + "movq %[rarg5], " lin2win_win_arg(5,6) "\n\t" + "movq %[rarg6], " lin2win_win_arg(6,6) "\n\t" + alloc_win_stack_frame(6) + "call *%[fptr]\n\t" + free_win_stack_frame(6) + : "=a" (ret), "=c" (dummy), "=d" (dummy), + "=r" (r8), "=r" (r9), "=r" (r10), "=r" (r11) + : "c" (arg1), "d" (arg2), "r" (r8), "r" (r9), + [rarg5] "r" ((u64)arg5), [rarg6] "r" ((u64)arg6), + [fptr] "r" (func)); + return ret; +} +EXPORT_SYMBOL_GPL(LIN2WIN0); +EXPORT_SYMBOL_GPL(LIN2WIN1); +EXPORT_SYMBOL_GPL(LIN2WIN2); +EXPORT_SYMBOL_GPL(LIN2WIN3); +EXPORT_SYMBOL_GPL(LIN2WIN4); +EXPORT_SYMBOL_GPL(LIN2WIN5); +EXPORT_SYMBOL_GPL(LIN2WIN6); diff --git a/arch/x86_64/kernel/reboot.c b/arch/x86_64/kernel/reboot.c index 6f56f2f..3bdfe22 100644 --- a/arch/x86_64/kernel/reboot.c +++ b/arch/x86_64/kernel/reboot.c @@ -7,6 +7,7 @@ #include <linux/ctype.h> #include <linux/string.h> #include <linux/pm.h> +#include <linux/efi.h> #include <asm/io.h> #include <asm/kdebug.h> #include <asm/delay.h> @@ -23,6 +24,14 @@ void (*pm_power_off)(void); EXPORT_SYMBOL(pm_power_off); +/* machine_emergency_restart_func is set to the restart implementation here. + * The variable provides a way for overriding the implementation. + * For example, EFI initialization could set up an emergency restart + * function hiding its implementation from here. + */ +void (*machine_emergency_restart_func)(void) = machine_emergency_restart; +EXPORT_SYMBOL(machine_emergency_restart_func); + static long no_idt[3]; static enum { BOOT_TRIPLE = 't', @@ -151,7 +160,7 @@ void machine_restart(char * __unused) if (!reboot_force) { machine_shutdown(); } - machine_emergency_restart(); + machine_emergency_restart_func(); } void machine_halt(void) diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c index e18e3af..4f11f98 100644 --- a/arch/x86_64/kernel/setup.c +++ b/arch/x86_64/kernel/setup.c @@ -45,6 +45,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> @@ -70,6 +71,10 @@ * Machine setup.. */ +#ifdef CONFIG_EFI +int efi_enabled = 0; +EXPORT_SYMBOL(efi_enabled); +#endif struct cpuinfo_x86 boot_cpu_data __read_mostly; EXPORT_SYMBOL(boot_cpu_data); @@ -519,6 +524,10 @@ void __init setup_arch(char **cmdline_p) rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); #endif +#ifdef CONFIG_EFI + if (!strncmp(EFI_LOADER_SIG, "EFIL", 4)) + efi_enabled = 1; +#endif setup_memory_region(); copy_edd(); @@ -553,6 +562,8 @@ void __init setup_arch(char **cmdline_p) discover_ebda(); init_memory_mapping(0, (end_pfn_map << PAGE_SHIFT)); + if (efi_enabled) + efi_map_memmap(); dmi_scan_machine(); @@ -695,7 +706,8 @@ void __init setup_arch(char **cmdline_p) #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 diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index e2c3a21..23af13e 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c @@ -33,6 +33,7 @@ #include <linux/notifier.h> #include <linux/cpu.h> #include <linux/kallsyms.h> +#include <linux/efi.h> #include <linux/acpi.h> #ifdef CONFIG_ACPI #include <acpi/achware.h> /* for PM timer frequency */ @@ -233,6 +234,11 @@ static void set_rtc_mmss(unsigned long nowtime) */ spin_lock(&rtc_lock); + if (efi_enabled) { + efi_set_rtc_mmss(nowtime); + spin_unlock(&rtc_lock); + return; + } /* * Tell the clock it's being set and stop it. @@ -532,10 +538,15 @@ unsigned long long sched_clock(void) static unsigned long get_cmos_time(void) { unsigned int year, mon, day, hour, min, sec; - unsigned long flags; + unsigned long flags, retval; unsigned extyear = 0; spin_lock_irqsave(&rtc_lock, flags); + if (efi_enabled) { + retval = efi_get_time(); + spin_unlock_irqrestore(&rtc_lock, flags); + return retval; + } do { sec = CMOS_READ(RTC_SECONDS); diff --git a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c index 92fdae9..16d58db 100644 --- a/arch/x86_64/mm/init.c +++ b/arch/x86_64/mm/init.c @@ -52,6 +52,8 @@ struct dma_mapping_ops* dma_ops; EXPORT_SYMBOL(dma_ops); static unsigned long dma_reserve __initdata; +/* Flag indicating EFI runtime executable code area */ +static int efi_runtime_code_area = 0; DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); @@ -96,6 +98,7 @@ void show_mem(void) } int after_bootmem; +EXPORT_SYMBOL(after_bootmem); static __init void *spp_getpage(void) { @@ -235,11 +238,16 @@ phys_pmd_init(pmd_t *pmd, unsigned long address, unsigned long end) unsigned long entry; if (address >= end) { - for (; i < PTRS_PER_PMD; i++, pmd++) - set_pmd(pmd, __pmd(0)); + if (!after_bootmem && !efi_runtime_code_area) + for (; i < PTRS_PER_PMD; i++, pmd++) + set_pmd(pmd, __pmd(0)); break; } entry = _PAGE_NX|_PAGE_PSE|_KERNPG_TABLE|_PAGE_GLOBAL|address; + if (efi_runtime_code_area) { + entry = pmd_val(*pmd); + entry &= ~_PAGE_NX; + } else entry = _PAGE_NX|_PAGE_PSE|_KERNPG_TABLE|_PAGE_GLOBAL|address; entry &= __supported_pte_mask; set_pmd(pmd, __pmd(entry)); } diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index c46e372..720b317 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -552,6 +552,17 @@ config FB_VESA You will get a boot time penguin logo at no additional cost. Please read <file:Documentation/fb/vesafb.txt>. If unsure, say Y. +config FB_EFI + bool "EFI-based Framebuffer Support" + depends on (FB = y) && X86 && EFI + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the EFI frame buffer device driver. If the firmware on + your platform is UEFI2.0, select Y to add support for + Graphics Output Protocol for early console messages to appear. + config FB_IMAC bool "Intel-based Macintosh Framebuffer Support" depends on (FB = y) && X86 && EFI diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 481c6c9..c261bde 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -102,6 +102,7 @@ obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/ # Platform or fallback drivers go here obj-$(CONFIG_FB_VESA) += vesafb.o obj-$(CONFIG_FB_IMAC) += imacfb.o +obj-$(CONFIG_FB_EFI) += efifb.o obj-$(CONFIG_FB_VGA16) += vga16fb.o vgastate.o obj-$(CONFIG_FB_OF) += offb.o diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c new file mode 100644 index 0000000..f555546 --- /dev/null +++ b/drivers/video/efifb.c @@ -0,0 +1,249 @@ +/* + * framebuffer driver for Intel Based Mac's + * + * (c) 2006 Edgar Hucek <gimli@dark-green.com> + * Original efi driver written by Gerd Knorr <kraxel@goldbach.in-berlin.de> + * + */ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fb.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/screen_info.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/dmi.h> +#include <linux/efi.h> + +#include <asm/io.h> + +#include <video/vga.h> + +static struct fb_var_screeninfo efifb_defined __initdata = { + .activate = FB_ACTIVATE_NOW, + .height = -1, + .width = -1, + .right_margin = 32, + .upper_margin = 16, + .lower_margin = 4, + .vsync_len = 4, + .vmode = FB_VMODE_NONINTERLACED, +}; + +static struct fb_fix_screeninfo efifb_fix __initdata = { + .id = "EFI VGA", + .type = FB_TYPE_PACKED_PIXELS, + .accel = FB_ACCEL_NONE, + .visual = FB_VISUAL_TRUECOLOR, +}; + +static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + /* + * Set a single color register. The values supplied are + * already rounded down to the hardware's capabilities + * (according to the entries in the `var' structure). Return + * != 0 for invalid regno. + */ + + if (regno >= info->cmap.len) + return 1; + + if (regno < 16) { + red >>= 8; + green >>= 8; + blue >>= 8; + ((u32 *)(info->pseudo_palette))[regno] = + (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset); + } + return 0; +} + +static struct fb_ops efifb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = efifb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static int __init efifb_probe(struct platform_device *dev) +{ + struct fb_info *info; + int err; + unsigned int size_vmode; + unsigned int size_remap; + unsigned int size_total; + + efifb_fix.smem_start = screen_info.lfb_base; + efifb_defined.bits_per_pixel = screen_info.lfb_depth; + efifb_defined.xres = screen_info.lfb_width; + efifb_defined.yres = screen_info.lfb_height; + efifb_fix.line_length = screen_info.lfb_linelength; + + /* size_vmode -- that is the amount of memory needed for the + * used video mode, i.e. the minimum amount of + * memory we need. */ + size_vmode = efifb_defined.yres * efifb_fix.line_length; + + /* size_total -- all video memory we have. Used for + * entries, ressource allocation and bounds + * checking. */ + //size_total = screen_info.lfb_size * 65536; + size_total = screen_info.lfb_size; + if (size_total < size_vmode) + size_total = size_vmode; + + /* size_remap -- the amount of video memory we are going to + * use for efifb. With modern cards it is no + * option to simply use size_total as that + * wastes plenty of kernel address space. */ + size_remap = size_vmode * 2; + if (size_remap < size_vmode) + size_remap = size_vmode; + if (size_remap > size_total) + size_remap = size_total; + efifb_fix.smem_len = size_remap; + +#ifndef __i386__ + //screen_info.imacpm_seg = 0; +#endif + + if (!request_mem_region(efifb_fix.smem_start, size_total, "efifb")) { + printk(KERN_WARNING + "efifb: cannot reserve video memory at 0x%lx\n", + efifb_fix.smem_start); + /* We cannot make this fatal. Sometimes this comes from magic + spaces our resource handlers simply don't know about */ + } + + info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev); + if (!info) { + err = -ENOMEM; + goto err_release_mem; + } + info->pseudo_palette = info->par; + info->par = NULL; + + info->screen_base = ioremap(efifb_fix.smem_start, efifb_fix.smem_len); + if (!info->screen_base) { + printk(KERN_ERR "efifb: abort, cannot ioremap video memory " + "0x%x @ 0x%lx\n", + efifb_fix.smem_len, efifb_fix.smem_start); + err = -EIO; + goto err_unmap; + } + + printk(KERN_INFO "efifb: framebuffer at 0x%lx, mapped to 0x%p, " + "using %dk, total %dk\n", + efifb_fix.smem_start, info->screen_base, + size_remap/1024, size_total/1024); + printk(KERN_INFO "efifb: mode is %dx%dx%d, linelength=%d, pages=%d\n", + efifb_defined.xres, efifb_defined.yres, + efifb_defined.bits_per_pixel, efifb_fix.line_length, + screen_info.pages); + + efifb_defined.xres_virtual = efifb_defined.xres; + efifb_defined.yres_virtual = efifb_fix.smem_len / + efifb_fix.line_length; + printk(KERN_INFO "efifb: scrolling: redraw\n"); + efifb_defined.yres_virtual = efifb_defined.yres; + + /* some dummy values for timing to make fbset happy */ + efifb_defined.pixclock = 10000000 / efifb_defined.xres * + 1000 / efifb_defined.yres; + efifb_defined.left_margin = (efifb_defined.xres / 8) & 0xf8; + efifb_defined.hsync_len = (efifb_defined.xres / 8) & 0xf8; + + efifb_defined.red.offset = screen_info.red_pos; + efifb_defined.red.length = screen_info.red_size; + efifb_defined.green.offset = screen_info.green_pos; + efifb_defined.green.length = screen_info.green_size; + efifb_defined.blue.offset = screen_info.blue_pos; + efifb_defined.blue.length = screen_info.blue_size; + efifb_defined.transp.offset = screen_info.rsvd_pos; + efifb_defined.transp.length = screen_info.rsvd_size; + + printk(KERN_INFO "efifb: %s: " + "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n", + "Truecolor", + screen_info.rsvd_size, + screen_info.red_size, + screen_info.green_size, + screen_info.blue_size, + screen_info.rsvd_pos, + screen_info.red_pos, + screen_info.green_pos, + screen_info.blue_pos); + + efifb_fix.ypanstep = 0; + efifb_fix.ywrapstep = 0; + + info->fbops = &efifb_ops; + info->var = efifb_defined; + info->fix = efifb_fix; + info->flags = FBINFO_FLAG_DEFAULT; + + if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { + err = -ENOMEM; + goto err_unmap; + } + if (register_framebuffer(info)<0) { + err = -EINVAL; + goto err_fb_dealoc; + } + printk(KERN_INFO "fb%d: %s frame buffer device\n", + info->node, info->fix.id); + return 0; + +err_fb_dealoc: + fb_dealloc_cmap(&info->cmap); +err_unmap: + iounmap(info->screen_base); + framebuffer_release(info); +err_release_mem: + release_mem_region(efifb_fix.smem_start, size_total); + return err; +} + +static struct platform_driver efifb_driver = { + .probe = efifb_probe, + .driver = { + .name = "efifb", + }, +}; + +static struct platform_device efifb_device = { + .name = "efifb", +}; + +static int __init efifb_init(void) +{ + int ret; + + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) + return -ENODEV; + + + ret = platform_driver_register(&efifb_driver); + + if (!ret) { + ret = platform_device_register(&efifb_device); + if (ret) + platform_driver_unregister(&efifb_driver); + } + return ret; +} +module_init(efifb_init); + +MODULE_LICENSE("GPL"); diff --git a/include/asm-x86_64/bootsetup.h b/include/asm-x86_64/bootsetup.h index b829f7b..743c11d 100644 --- a/include/asm-x86_64/bootsetup.h +++ b/include/asm-x86_64/bootsetup.h @@ -17,6 +17,12 @@ extern char x86_boot_params[BOOT_PARAM_SIZE]; #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40)) #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80)) #define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0)) +#define EFI_SYSTAB (*((unsigned long *)(PARAM+0x1b8))) +#define EFI_LOADER_SIG ((unsigned char *)(PARAM+0x1c0)) +#define EFI_MEMDESC_SIZE (*((unsigned int *) (PARAM+0x1c4))) +#define EFI_MEMDESC_VERSION (*((unsigned int *) (PARAM+0x1c8))) +#define EFI_MEMMAP_SIZE (*((unsigned int *) (PARAM+0x1cc))) +#define EFI_MEMMAP (*((unsigned long *)(PARAM+0x1d0))) #define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2)) #define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8)) #define SAVED_VIDEO_MODE (*(unsigned short *) (PARAM+0x1FA)) diff --git a/include/asm-x86_64/e820.h b/include/asm-x86_64/e820.h index eaf2103..25b8d2e 100644 --- a/include/asm-x86_64/e820.h +++ b/include/asm-x86_64/e820.h @@ -21,6 +21,7 @@ #define E820_RESERVED 2 #define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */ #define E820_NVS 4 +#define E820_RUNTIME_CODE 5 /* efi runtime code */ #define HIGH_MEMORY (1024*1024) diff --git a/include/asm-x86_64/eficallwrap.h b/include/asm-x86_64/eficallwrap.h new file mode 100644 index 0000000..7147d72 --- /dev/null +++ b/include/asm-x86_64/eficallwrap.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2006 Giridhar Pemmasani + * Copyright (C) 2007-2010 Intel Corp + * Contributed by Chandramouli Narayanan<mouli@linux.intel.com> + * Adapted NDIS wrapper macros from http://ndiswrapper.sourceforge.net + * for EFI x86_64 linux support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +extern efi_status_t LIN2WIN0(void *fp); +extern efi_status_t LIN2WIN1(void *fp, u64 arg1); +extern efi_status_t LIN2WIN2(void *fp, u64 arg1, u64 arg2); +extern efi_status_t LIN2WIN3(void *fp, u64 arg1, u64 arg2, u64 arg3); +extern efi_status_t LIN2WIN4(void *fp, u64 arg1, u64 arg2, u64 arg3, u64 arg4); +extern efi_status_t LIN2WIN5( + void *fp, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5); +extern efi_status_t LIN2WIN6( + void *fp, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5, u64 arg6); diff --git a/include/linux/efi.h b/include/linux/efi.h index 59abb68..6977fa2 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -213,6 +213,7 @@ typedef struct { } efi_config_table_t; #define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL) +#define EFI_SYSTEM_TABLE_REVISION ((1 << 16) | 00) typedef struct { efi_table_hdr_t hdr; diff --git a/include/linux/screen_info.h b/include/linux/screen_info.h index b02308e..a72009f 100644 --- a/include/linux/screen_info.h +++ b/include/linux/screen_info.h @@ -63,6 +63,7 @@ extern struct screen_info screen_info; #define VIDEO_TYPE_EGAC 0x21 /* EGA in Color Mode */ #define VIDEO_TYPE_VGAC 0x22 /* VGA+ in Color Mode */ #define VIDEO_TYPE_VLFB 0x23 /* VESA VGA in graphic mode */ +#define VIDEO_TYPE_EFI 0x24 /* EFI graphic mode */ #define VIDEO_TYPE_PICA_S3 0x30 /* ACER PICA-61 local S3 video */ #define VIDEO_TYPE_MIPS_G364 0x31 /* MIPS Magnum 4000 G364 video */ diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 391e7ed..975c963 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -1,8 +1,43 @@ #ifndef __LINUX_UACCESS_H__ #define __LINUX_UACCESS_H__ +#include <linux/preempt.h> #include <asm/uaccess.h> +/* + * These routines enable/disable the pagefault handler in that + * it will not take any locks and go straight to the fixup table. + * + * They have great resemblance to the preempt_disable/enable calls + * and in fact they are identical; this is because currently there is + * no other way to make the pagefault handlers do this. So we do + * disable preemption but we don't necessarily care about that. + */ +static inline void pagefault_disable(void) +{ + inc_preempt_count(); + /* + * make sure to have issued the store before a pagefault + * can hit. + */ + barrier(); +} + +static inline void pagefault_enable(void) +{ + /* + * make sure to issue those last loads/stores before enabling + * the pagefault handler again. + */ + barrier(); + dec_preempt_count(); + /* + * make sure we do.. + */ + barrier(); + preempt_check_resched(); +} + #ifndef ARCH_HAS_NOCACHE_UACCESS static inline unsigned long __copy_from_user_inatomic_nocache(void *to, @@ -19,4 +54,34 @@ static inline unsigned long __copy_from_user_nocache(void *to, #endif /* ARCH_HAS_NOCACHE_UACCESS */ +/** + * probe_kernel_address(): safely attempt to read from a location + * @addr: address to read from - its type is type typeof(retval)* + * @retval: read into this variable + * + * Safely read from address @addr into variable @revtal. If a kernel fault + * happens, handle that and return -EFAULT. + * We ensure that the __get_user() is executed in atomic context so that + * do_page_fault() doesn't attempt to take mmap_sem. This makes + * probe_kernel_address() suitable for use within regions where the caller + * already holds mmap_sem, or other locks which nest inside mmap_sem. + * This must be a macro because __get_user() needs to know the types of the + * args. + * + * We don't include enough header files to be able to do the set_fs(). We + * require that the probe_kernel_address() caller will do that. + */ +#define probe_kernel_address(addr, retval) \ + ({ \ + long ret; \ + mm_segment_t old_fs = get_fs(); \ + \ + set_fs(KERNEL_DS); \ + pagefault_disable(); \ + ret = __get_user(retval, (__force typeof(retval) __user *)(addr)); \ + pagefault_enable(); \ + set_fs(old_fs); \ + ret; \ + }) + #endif /* __LINUX_UACCESS_H__ */