From: Matt Tolentino 1. Adds CONFIG_EFI option to boot on EFI platforms, but also continue to boot on existing platforms depending on the boot parameters. 2. Kernels w/o CONFIG_EFI are smaller (nearly 8k, although I can probably move some more stuff around for greater savings). This was accomplished through the following changes: + Removed CONFIG_ACPI_EFI. I've substituted the efi_enabled flag which is set depending on CONFIG_EFI at build time, but also dynamically at runtime through inspection of the boot parameters. Note, I haven't run this change by Len or David for input from the ACPI or IA64 perspectives. However, this does allow for the behavior we need on x86. I need to do one more ia64 specific patch (something like add int efi_enabled = 1; and a CONFIG_EFI option to always be on in Kconfig) to ensure this doesn't break itanium builds, but I wanted to get your input first. + reorganized some of the functions in time.c. I removed the efi_get_time and efi_set_rtc_mmss and put them as inline into efi.c. I think the manner in which these are called makes more sense now and ensures that they aren't even compiled without CONFIG_EFI. I was able to boot both kernels (EFI and non-EFI) on several machines with regular BIOS. The ACPI tables were detected correctly, interrupts looked like they were routed correctly, etc., so give a try. arch/i386/Kconfig | 15 +++++ arch/i386/kernel/Makefile | 3 - arch/i386/kernel/acpi/boot.c | 10 ++- arch/i386/kernel/efi.c | 51 +++++++++++++++++++- arch/i386/kernel/setup.c | 10 +++ arch/i386/kernel/time.c | 79 ++++++------------------------- drivers/acpi/Kconfig | 12 ---- drivers/acpi/osl.c | 108 ++++++++++++++++++++----------------------- include/linux/efi.h | 7 ++ 9 files changed, 155 insertions(+), 140 deletions(-) diff -puN arch/i386/Kconfig~ia32-efi-config-option arch/i386/Kconfig --- 25/arch/i386/Kconfig~ia32-efi-config-option 2003-10-24 20:55:51.000000000 -0700 +++ 25-akpm/arch/i386/Kconfig 2003-10-24 20:55:51.000000000 -0700 @@ -784,6 +784,18 @@ config MTRR See for more information. +config EFI + bool "Boot from EFI support (EXPERIMENTAL)" + depends on ACPI + default n + ---help--- + This enables the the kernel to boot on EFI platforms using system configuration + passed to it from the firmware. This also enables the EFI runtime services to be + invoked. + + This option is only useful on systems that have EFI firmware. In addition, you + must use the latest ELILO loader available at ftp.hpl.hp.com/pub/linux-ia64/. + config HAVE_DEC_LOCK bool depends on (SMP || PREEMPT) && X86_CMPXCHG @@ -793,8 +805,7 @@ config HAVE_DEC_LOCK # Summit needs it only when NUMA is on config BOOT_IOREMAP bool - depends on X86_PC -# depends on (((X86_SUMMIT || X86_GENERICARCH) && NUMA)) || X86_GENERICARCH + depends on (((X86_SUMMIT || X86_GENERICARCH) && NUMA) || (X86 && EFI)) default y endmenu diff -puN arch/i386/kernel/acpi/boot.c~ia32-efi-config-option arch/i386/kernel/acpi/boot.c --- 25/arch/i386/kernel/acpi/boot.c~ia32-efi-config-option 2003-10-24 20:55:51.000000000 -0700 +++ 25-akpm/arch/i386/kernel/acpi/boot.c 2003-10-24 20:55:51.000000000 -0700 @@ -325,10 +325,12 @@ acpi_find_rsdp (void) { unsigned long rsdp_phys = 0; - if (efi.acpi20) - return __pa(efi.acpi20); - else if (efi.acpi) - return __pa(efi.acpi); + if (efi_enabled) { + if (efi.acpi20) + return __pa(efi.acpi20); + else if (efi.acpi) + return __pa(efi.acpi); + } /* * Scan memory looking for the RSDP signature. First search EBDA (low * memory) paragraphs and then search upper memory (E0000-FFFFF). diff -puN arch/i386/kernel/efi.c~ia32-efi-config-option arch/i386/kernel/efi.c --- 25/arch/i386/kernel/efi.c~ia32-efi-config-option 2003-10-24 20:55:51.000000000 -0700 +++ 25-akpm/arch/i386/kernel/efi.c 2003-10-24 20:55:51.000000000 -0700 @@ -174,12 +174,61 @@ phys_efi_get_time(efi_time_t *tm, efi_ti return status; } +int inline 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) + panic("Ooops, efitime: can't read time!\n"); + 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; + + if (status != EFI_SUCCESS) { + printk("Ooops: efitime: can't read time!\n"); + return -1; + } + return 0; +} +/* + * This should only be used during kernel init and before runtime + * services have been remapped, therefore, we'll need to call in physical + * mode. Note, this call isn't used later, so mark it __init. + */ +unsigned long inline __init efi_get_time(void) +{ + efi_status_t status; + efi_time_t eft; + efi_time_cap_t cap; + + status = phys_efi_get_time(&eft, &cap); + if (status != EFI_SUCCESS) + printk("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); +} void efi_gettimeofday(struct timespec *tv) { efi_time_t tm; + efi_status_t status; memset(tv, 0, sizeof(*tv)); - if ((*efi.get_time) (&tm, 0) != EFI_SUCCESS) + spin_lock(&efi_rt_lock); + status = (*efi.get_time) (&tm, 0); + spin_unlock(&efi_rt_lock); + if (status != EFI_SUCCESS) return; tv->tv_sec = mktime(tm.year, tm.month, tm.day, tm.hour, tm.minute, diff -puN arch/i386/kernel/Makefile~ia32-efi-config-option arch/i386/kernel/Makefile --- 25/arch/i386/kernel/Makefile~ia32-efi-config-option 2003-10-24 20:55:51.000000000 -0700 +++ 25-akpm/arch/i386/kernel/Makefile 2003-10-24 20:56:06.000000000 -0700 @@ -7,7 +7,7 @@ extra-y := head.o init_task.o vmlinux.ld obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \ pci-dma.o i386_ksyms.o i387.o dmi_scan.o bootflag.o \ - doublefault.o efi.o efi_stub.o + doublefault.o obj-y += cpu/ obj-y += timers/ @@ -31,6 +31,7 @@ obj-$(CONFIG_MODULES) += module.o obj-y += sysenter.o vsyscall.o obj-$(CONFIG_ACPI_SRAT) += srat.o obj-$(CONFIG_HPET_TIMER) += time_hpet.o +obj-$(CONFIG_EFI) += efi.o efi_stub.o EXTRA_AFLAGS := -traditional diff -puN arch/i386/kernel/setup.c~ia32-efi-config-option arch/i386/kernel/setup.c --- 25/arch/i386/kernel/setup.c~ia32-efi-config-option 2003-10-24 20:55:51.000000000 -0700 +++ 25-akpm/arch/i386/kernel/setup.c 2003-10-24 20:55:51.000000000 -0700 @@ -58,7 +58,9 @@ static inline char * __init machine_spec * Machine setup.. */ +#ifdef CONFIG_EFI int efi_enabled = 0; +#endif /* cpu data as detected by the assembly code in head.S */ struct cpuinfo_x86 new_cpu_data __initdata = { 0, 0, 0, 0, -1, 1, 0, 0, -1 }; @@ -1046,11 +1048,17 @@ void __init setup_arch(char **cmdline_p) pre_setup_arch_hook(); early_cpu_init(); - /* FIXME: This isn't an official loader_type right + /* + * FIXME: This isn't an official loader_type right * now but does currently work with elilo. + * If we were configured as an EFI kernel, check to make + * sure that we were loaded correctly from elilo and that + * the system table is valid. If not, then initialize normally. */ +#ifdef CONFIG_EFI if ((LOADER_TYPE == 0x50) && EFI_SYSTAB) efi_enabled = 1; +#endif ROOT_DEV = old_decode_dev(ORIG_ROOT_DEV); drive_info = DRIVE_INFO; diff -puN arch/i386/kernel/time.c~ia32-efi-config-option arch/i386/kernel/time.c --- 25/arch/i386/kernel/time.c~ia32-efi-config-option 2003-10-24 20:55:51.000000000 -0700 +++ 25-akpm/arch/i386/kernel/time.c 2003-10-24 20:55:51.000000000 -0700 @@ -170,43 +170,15 @@ static int set_rtc_mmss(unsigned long no /* gets recalled with irq locally disabled */ spin_lock(&rtc_lock); - retval = mach_set_rtc_mmss(nowtime); + if (efi_enabled) + retval = efi_set_rtc_mmss(nowtime); + else + retval = mach_set_rtc_mmss(nowtime); spin_unlock(&rtc_lock); return retval; } -static int efi_set_rtc_mmss(unsigned long nowtime) -{ - int real_seconds, real_minutes; - unsigned long flags; - efi_status_t status; - efi_time_t eft; - efi_time_cap_t cap; - - spin_lock_irqsave(&rtc_lock, flags); - - status = efi.get_time(&eft, &cap); - if (status != EFI_SUCCESS) - panic("Ooops, efitime: can't read time!\n"); - 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; - - status = efi.set_time(&eft); - if (status != EFI_SUCCESS) - panic("Ooops: efitime: can't read time!\n"); - - spin_unlock_irqrestore(&rtc_lock, flags); - return 0; -} - /* last time the cmos clock got updated */ static long last_rtc_update; @@ -259,7 +231,13 @@ static inline void do_timer_interrupt(in >= USEC_AFTER - ((unsigned) TICK_SIZE) / 2 && (xtime.tv_nsec / 1000) <= USEC_BEFORE + ((unsigned) TICK_SIZE) / 2) { - if ((efi_enabled && (!efi_set_rtc_mmss(xtime.tv_sec) )) || (set_rtc_mmss(xtime.tv_sec) == 0)) + /* horrible...FIXME */ + if (efi_enabled) { + if (efi_set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; + } else if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ @@ -313,7 +291,10 @@ unsigned long get_cmos_time(void) spin_lock(&rtc_lock); - retval = mach_get_cmos_time(); + if (efi_enabled) + retval = efi_get_time(); + else + retval = mach_get_cmos_time(); spin_unlock(&rtc_lock); @@ -324,26 +305,6 @@ static struct sysdev_class pit_sysclass set_kset_name("pit"), }; -/* - * This is called before the RT mappings are in place, so we - * need to be able to get the time in physical mode. - */ -unsigned long efi_get_time(void) -{ - efi_status_t status; - unsigned long flags; - efi_time_t eft; - efi_time_cap_t cap; - - spin_lock_irqsave(&rtc_lock, flags); - status = phys_efi_get_time(&eft, &cap); - if (status != EFI_SUCCESS) - printk("Oops: efitime: can't read time status: 0x%lx\n", status); - - spin_unlock_irqrestore(&rtc_lock, flags); - - return mktime(eft.year, eft.month, eft.day, eft.hour, eft.minute, eft.second); -} /* XXX this driverfs stuff should probably go elsewhere later -john */ static struct sys_device device_i8253 = { @@ -366,10 +327,7 @@ extern void (*late_time_init)(void); /* Duplicate of time_init() below, with hpet_enable part added */ void __init hpet_time_init(void) { - if (efi_enabled) - xtime.tv_sec = efi_get_time(); - else - xtime.tv_sec = get_cmos_time(); + xtime.tv_sec = get_cmos_time(); wall_to_monotonic.tv_sec = -xtime.tv_sec; xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); wall_to_monotonic.tv_nsec = -xtime.tv_nsec; @@ -395,10 +353,7 @@ void __init time_init(void) return; } #endif - if (efi_enabled) - xtime.tv_sec = efi_get_time(); - else - xtime.tv_sec = get_cmos_time(); + xtime.tv_sec = get_cmos_time(); wall_to_monotonic.tv_sec = -xtime.tv_sec; xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); wall_to_monotonic.tv_nsec = -xtime.tv_nsec; diff -puN drivers/acpi/Kconfig~ia32-efi-config-option drivers/acpi/Kconfig --- 25/drivers/acpi/Kconfig~ia32-efi-config-option 2003-10-24 20:55:51.000000000 -0700 +++ 25-akpm/drivers/acpi/Kconfig 2003-10-24 20:55:51.000000000 -0700 @@ -251,18 +251,6 @@ config ACPI_SYSTEM This driver will enable your system to shut down using ACPI, and dump your ACPI DSDT table using /proc/acpi/dsdt. -config ACPI_EFI - bool "Obtain RSDP from EFI Configuration Table" - depends on ACPI_INTERPRETER - depends on IA64 || X86 - default n - help - On EFI Systems the RSDP pointer is passed to the kernel via - the EFI Configuration Table. On Itanium systems this is - standard and required. For IA-32, systems that have - EFI firmware should leave this enabled. Platforms with - traditional legacy BIOS should disable this option. - config ACPI_RELAXED_AML bool "Relaxed AML" depends on ACPI_INTERPRETER diff -puN drivers/acpi/osl.c~ia32-efi-config-option drivers/acpi/osl.c --- 25/drivers/acpi/osl.c~ia32-efi-config-option 2003-10-24 20:55:51.000000000 -0700 +++ 25-akpm/drivers/acpi/osl.c 2003-10-24 20:55:51.000000000 -0700 @@ -41,9 +41,7 @@ #include #include -#ifdef CONFIG_ACPI_EFI #include -#endif #define _COMPONENT ACPI_OS_SERVICES @@ -139,22 +137,24 @@ acpi_os_free(void *ptr) acpi_status acpi_os_get_root_pointer(u32 flags, struct acpi_pointer *addr) { -#ifdef CONFIG_ACPI_EFI - addr->pointer_type = ACPI_PHYSICAL_POINTER; - if (efi.acpi20) - addr->pointer.physical = (acpi_physical_address) virt_to_phys(efi.acpi20); - else if (efi.acpi) - addr->pointer.physical = (acpi_physical_address) virt_to_phys(efi.acpi); - else { - printk(KERN_ERR PREFIX "System description tables not found\n"); - return AE_NOT_FOUND; - } -#else - if (ACPI_FAILURE(acpi_find_root_pointer(flags, addr))) { - printk(KERN_ERR PREFIX "System description tables not found\n"); - return AE_NOT_FOUND; + if (efi_enabled) { + addr->pointer_type = ACPI_PHYSICAL_POINTER; + if (efi.acpi20) + addr->pointer.physical = + (acpi_physical_address) virt_to_phys(efi.acpi20); + else if (efi.acpi) + addr->pointer.physical = + (acpi_physical_address) virt_to_phys(efi.acpi); + else { + printk(KERN_ERR PREFIX "System description tables not found\n"); + return AE_NOT_FOUND; + } + } else { + if (ACPI_FAILURE(acpi_find_root_pointer(flags, addr))) { + printk(KERN_ERR PREFIX "System description tables not found\n"); + return AE_NOT_FOUND; + } } -#endif /*CONFIG_ACPI_EFI*/ return AE_OK; } @@ -162,22 +162,22 @@ acpi_os_get_root_pointer(u32 flags, stru acpi_status acpi_os_map_memory(acpi_physical_address phys, acpi_size size, void **virt) { -#ifdef CONFIG_ACPI_EFI - if (EFI_MEMORY_WB & efi_mem_attributes(phys)) { - *virt = phys_to_virt(phys); + if (efi_enabled) { + if (EFI_MEMORY_WB & efi_mem_attributes(phys)) { + *virt = phys_to_virt(phys); + } else { + *virt = ioremap(phys, size); + } } else { - *virt = ioremap(phys, size); - } -#else - if (phys > ULONG_MAX) { - printk(KERN_ERR PREFIX "Cannot map memory that high\n"); - return AE_BAD_PARAMETER; + if (phys > ULONG_MAX) { + printk(KERN_ERR PREFIX "Cannot map memory that high\n"); + return AE_BAD_PARAMETER; + } + /* + * ioremap checks to ensure this is in reserved space + */ + *virt = ioremap((unsigned long) phys, size); } - /* - * ioremap checks to ensure this is in reserved space - */ - *virt = ioremap((unsigned long) phys, size); -#endif if (!*virt) return AE_NO_MEMORY; @@ -368,19 +368,17 @@ acpi_os_read_memory( { u32 dummy; void *virt_addr; - -#ifdef CONFIG_ACPI_EFI int iomem = 0; - if (EFI_MEMORY_WB & efi_mem_attributes(phys_addr)) { + if (efi_enabled) { + if (EFI_MEMORY_WB & efi_mem_attributes(phys_addr)) { + virt_addr = phys_to_virt(phys_addr); + } else { + iomem = 1; + virt_addr = ioremap(phys_addr, width); + } + } else virt_addr = phys_to_virt(phys_addr); - } else { - iomem = 1; - virt_addr = ioremap(phys_addr, width); - } -#else - virt_addr = phys_to_virt(phys_addr); -#endif if (!value) value = &dummy; @@ -398,10 +396,10 @@ acpi_os_read_memory( BUG(); } -#ifdef CONFIG_ACPI_EFI - if (iomem) - iounmap(virt_addr); -#endif + if (efi_enabled) { + if (iomem) + iounmap(virt_addr); + } return AE_OK; } @@ -413,19 +411,17 @@ acpi_os_write_memory( u32 width) { void *virt_addr; - -#ifdef CONFIG_ACPI_EFI int iomem = 0; - if (EFI_MEMORY_WB & efi_mem_attributes(phys_addr)) { + if (efi_enabled) { + if (EFI_MEMORY_WB & efi_mem_attributes(phys_addr)) { + virt_addr = phys_to_virt(phys_addr); + } else { + iomem = 1; + virt_addr = ioremap(phys_addr, width); + } + } else virt_addr = phys_to_virt(phys_addr); - } else { - iomem = 1; - virt_addr = ioremap(phys_addr, width); - } -#else - virt_addr = phys_to_virt(phys_addr); -#endif switch (width) { case 8: @@ -441,10 +437,8 @@ acpi_os_write_memory( BUG(); } -#ifdef CONFIG_ACPI_EFI if (iomem) iounmap(virt_addr); -#endif return AE_OK; } diff -puN include/linux/efi.h~ia32-efi-config-option include/linux/efi.h --- 25/include/linux/efi.h~ia32-efi-config-option 2003-10-24 20:55:51.000000000 -0700 +++ 25-akpm/include/linux/efi.h 2003-10-24 20:55:51.000000000 -0700 @@ -297,8 +297,15 @@ extern u64 efi_mem_attributes (unsigned extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource); extern efi_status_t phys_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc); +extern unsigned long inline __init efi_get_time(void); +extern int inline __init efi_set_rtc_mmss(unsigned long nowtime); extern struct efi_memory_map memmap; + +#ifdef CONFIG_EFI extern int efi_enabled; +#else +#define efi_enabled 0 +#endif /* * Variable Attributes _