Re: [PATCH -mm 1/4 -v6] x86_64 EFI runtime service support: EFI basic runtime service support

2007-11-27 Thread Huang, Ying
On Tue, 2007-11-27 at 02:02 -0800, Andrew Morton wrote:
> > +
> > +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);
> 
> It's peculiar to have a spinlock in __initdata.  Often there just isn't any
> code path by which multiple threads/CPUs can access the same data that
> early in boot.

Yes. This spinlock is used only before efi_enter_virtual_mode, which is
far before smp_init, so this spinlock is unnecessary, I will remove it.

> > +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(_lock);
> > +   local_irq_save(efi_flags);
> 
> I think we discussed this before, but I forget the result.  It really
> should be described better in the comments here, because this code leaps out
> and shouts "wrong".
> 
> a) Why not use spin_lock_irqsave()?
> 
> b) If this is an open-coded spin_lock_irqsave() then it gets the two
> operations in the wrong order and is hence deadlockable.
> 
> c) it isn't obvious to the reader that this locking is even needed in
> initial bootup.
> 
> Now I _think_ all these issuses were addressed in discussion.  But unless
> the code comment knocks them all on the head (it doesn't) then it will all
> come up again.

Because the efi_lock will removed, so this will be no longer a problem.

> > +   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(_lock);
> > +}
> > +
> >
> > ...
> >
> > +void __init runtime_code_page_mkexec(void)
> > +{
> > +   efi_memory_desc_t *md;
> 
> I thought we were going to use `struct efi_memory_desc'?

There is even no struct efi_memory_desc definition in
include/linux/efi.h. I can fix all such coding style problem across all
platforms if desired in another patchset.

> > +#include 
> > +#include 
> > +#include 
> > +
> > +#define EFI_DEBUG  0
> 
> I suspect you really want to turn on debug mode during initial public
> testing.  Verify that it generates sufficient information for you to be
> able to fix problems if/when people report them.

OK, I will do it.

> > +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;
> > +   void *tmp;
> > +
> > +   memset(, 0, sizeof(efi));
> > +   memset(_phys, 0, sizeof(efi_phys));
> 
> These were already zeroed by the compiler (I have a feeling I said that a
> couple of months back)

I will fix it.

> > +#ifdef CONFIG_X86_32
> 
> Strictly this isn't needed until [patch 4/4] but that's a very minor point.
> 
> > +   efi_phys.systab = (efi_system_table_t *)boot_params.efi_info.efi_systab;
> > +   memmap.phys_map = (void *)boot_params.efi_info.efi_memmap;
> > +#else
> > +   efi_phys.systab = (efi_system_table_t *)
> > +   (boot_params.efi_info.efi_systab |
> > +((__u64)boot_params.efi_info.efi_systab_hi<<32));
> > +   memmap.phys_map = (void *)
> > +   (boot_params.efi_info.efi_memmap |
> > +((__u64)boot_params.efi_info.efi_memmap_hi<<32));
> > +#endif
> > +   memmap.nr_map = boot_params.efi_info.efi_memmap_size /
> > +   boot_params.efi_info.efi_memdesc_size;
> > +   memmap.desc_version = boot_params.efi_info.efi_memdesc_version;
> > +   memmap.desc_size = boot_params.efi_info.efi_memdesc_size;
> > +
> > +   efi.systab = efi_early_ioremap((unsigned long)efi_phys.systab,
> > +  sizeof(efi_system_table_t));
> > +   if (efi.systab == NULL)
> > +   printk(KERN_ERR "Woah! Couldn't map the EFI systema table.\n");
> 
> s/systema/system/.
> 
> I'd be inclined to s/Woah! //, too.  Sorry, I'm boring.

I will fix it.

> > +   memcpy(_systab, efi.systab, sizeof(efi_system_table_t));
> > +   efi_early_iounmap(efi.systab, sizeof(efi_system_table_t));
> > +   efi.systab = _systab;
> > +
> > +   /*
> > +* Verify the EFI Table
> > +*/
> > +   if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
> > +   printk(KERN_ERR "Woah! EFI system table "
> > +  "signature incorrect\n");
> > +   if ((efi.systab->hdr.revision >> 16) == 0)
> > +   printk(KERN_ERR "Warning: EFI system table version "
> > +  

Re: [PATCH -mm 1/4 -v6] x86_64 EFI runtime service support: EFI basic runtime service support

2007-11-27 Thread Andrew Morton
On Mon, 26 Nov 2007 16:23:41 +0800 "Huang, Ying" <[EMAIL PROTECTED]> wrote:

> 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 ( 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.
> 
> ...
>
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +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);

It's peculiar to have a spinlock in __initdata.  Often there just isn't any
code path by which multiple threads/CPUs can access the same data that
early in boot.

> +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;
> +
> + 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(_lock);
> + local_irq_save(efi_flags);

I think we discussed this before, but I forget the result.  It really
should be described better in the comments here, because this code leaps out
and shouts "wrong".

a) Why not use spin_lock_irqsave()?

b) If this is an open-coded spin_lock_irqsave() then it gets the two
operations in the wrong order and is hence deadlockable.

c) it isn't obvious to the reader that this locking is even needed in
initial bootup.

Now I _think_ all these issuses were addressed in discussion.  But unless
the code comment knocks them all on the head (it doesn't) then it will all
come up again.

> + 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(_lock);
> +}
> +
>
> ...
>
> +void __init runtime_code_page_mkexec(void)
> +{
> + efi_memory_desc_t *md;

I thought we were going to use `struct efi_memory_desc'?

> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include 
> +#include 
> +#include 
> +
> +#define EFI_DEBUG0

I suspect you really want to turn on debug mode during initial public
testing.  Verify that it generates sufficient information for you to be
able to fix problems if/when people report them.

> +int efi_enabled;
> +EXPORT_SYMBOL(efi_enabled);

Re: [PATCH -mm 1/4 -v6] x86_64 EFI runtime service support: EFI basic runtime service support

2007-11-27 Thread Andrew Morton
On Mon, 26 Nov 2007 16:23:41 +0800 Huang, Ying [EMAIL PROTECTED] wrote:

 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_callx (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.
 
 ...

 +
 +#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;
 +/* efi_lock protects efi physical mode call */
 +static __initdata DEFINE_SPINLOCK(efi_lock);

It's peculiar to have a spinlock in __initdata.  Often there just isn't any
code path by which multiple threads/CPUs can access the same data that
early in boot.

 +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;
 +
 + 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);

I think we discussed this before, but I forget the result.  It really
should be described better in the comments here, because this code leaps out
and shouts wrong.

a) Why not use spin_lock_irqsave()?

b) If this is an open-coded spin_lock_irqsave() then it gets the two
operations in the wrong order and is hence deadlockable.

c) it isn't obvious to the reader that this locking is even needed in
initial bootup.

Now I _think_ all these issuses were addressed in discussion.  But unless
the code comment knocks them all on the head (it doesn't) then it will all
come up again.

 + 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);
 +}
 +

 ...

 +void __init runtime_code_page_mkexec(void)
 +{
 + efi_memory_desc_t *md;

I thought we were going to use `struct efi_memory_desc'?

 +#include linux/kernel.h
 +#include linux/init.h
 +#include linux/efi.h
 +#include linux/bootmem.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/time.h
 +
 

Re: [PATCH -mm 1/4 -v6] x86_64 EFI runtime service support: EFI basic runtime service support

2007-11-27 Thread Huang, Ying
On Tue, 2007-11-27 at 02:02 -0800, Andrew Morton wrote:
  +
  +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);
 
 It's peculiar to have a spinlock in __initdata.  Often there just isn't any
 code path by which multiple threads/CPUs can access the same data that
 early in boot.

Yes. This spinlock is used only before efi_enter_virtual_mode, which is
far before smp_init, so this spinlock is unnecessary, I will remove it.

  +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);
 
 I think we discussed this before, but I forget the result.  It really
 should be described better in the comments here, because this code leaps out
 and shouts wrong.
 
 a) Why not use spin_lock_irqsave()?
 
 b) If this is an open-coded spin_lock_irqsave() then it gets the two
 operations in the wrong order and is hence deadlockable.
 
 c) it isn't obvious to the reader that this locking is even needed in
 initial bootup.
 
 Now I _think_ all these issuses were addressed in discussion.  But unless
 the code comment knocks them all on the head (it doesn't) then it will all
 come up again.

Because the efi_lock will removed, so this will be no longer a problem.

  +   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);
  +}
  +
 
  ...
 
  +void __init runtime_code_page_mkexec(void)
  +{
  +   efi_memory_desc_t *md;
 
 I thought we were going to use `struct efi_memory_desc'?

There is even no struct efi_memory_desc definition in
include/linux/efi.h. I can fix all such coding style problem across all
platforms if desired in another patchset.

  +#include asm/setup.h
  +#include asm/efi.h
  +#include asm/time.h
  +
  +#define EFI_DEBUG  0
 
 I suspect you really want to turn on debug mode during initial public
 testing.  Verify that it generates sufficient information for you to be
 able to fix problems if/when people report them.

OK, I will do it.

  +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;
  +   void *tmp;
  +
  +   memset(efi, 0, sizeof(efi));
  +   memset(efi_phys, 0, sizeof(efi_phys));
 
 These were already zeroed by the compiler (I have a feeling I said that a
 couple of months back)

I will fix it.

  +#ifdef CONFIG_X86_32
 
 Strictly this isn't needed until [patch 4/4] but that's a very minor point.
 
  +   efi_phys.systab = (efi_system_table_t *)boot_params.efi_info.efi_systab;
  +   memmap.phys_map = (void *)boot_params.efi_info.efi_memmap;
  +#else
  +   efi_phys.systab = (efi_system_table_t *)
  +   (boot_params.efi_info.efi_systab |
  +((__u64)boot_params.efi_info.efi_systab_hi32));
  +   memmap.phys_map = (void *)
  +   (boot_params.efi_info.efi_memmap |
  +((__u64)boot_params.efi_info.efi_memmap_hi32));
  +#endif
  +   memmap.nr_map = boot_params.efi_info.efi_memmap_size /
  +   boot_params.efi_info.efi_memdesc_size;
  +   memmap.desc_version = boot_params.efi_info.efi_memdesc_version;
  +   memmap.desc_size = boot_params.efi_info.efi_memdesc_size;
  +
  +   efi.systab = efi_early_ioremap((unsigned long)efi_phys.systab,
  +  sizeof(efi_system_table_t));
  +   if (efi.systab == NULL)
  +   printk(KERN_ERR Woah! Couldn't map the EFI systema table.\n);
 
 s/systema/system/.
 
 I'd be inclined to s/Woah! //, too.  Sorry, I'm boring.

I will fix it.

  +   memcpy(efi_systab, efi.systab, sizeof(efi_system_table_t));
  +   efi_early_iounmap(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 Woah! EFI system table 
  +  signature incorrect\n);
  +   if ((efi.systab-hdr.revision  16) == 0)
  +   printk(KERN_ERR Warning: EFI system table version 
  +  %d.%02d, expected 1.00 or greater\n,
  +  efi.systab-hdr.revision  16,
  +  efi.systab-hdr.revision  0x);
  +
  +   /*
  +* Show what we know for 

[PATCH -mm 1/4 -v6] x86_64 EFI runtime service support: EFI basic runtime service support

2007-11-26 Thread Huang, Ying
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 ( 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: Chandramouli Narayanan <[EMAIL PROTECTED]>
Signed-off-by: Huang Ying <[EMAIL PROTECTED]>

---
 arch/x86/Kconfig  |2 
 arch/x86/kernel/Makefile_64   |1 
 arch/x86/kernel/efi.c |  484 ++
 arch/x86/kernel/efi_64.c  |  171 ++
 arch/x86/kernel/efi_stub_64.S |   68 +
 arch/x86/kernel/setup_64.c|   17 +
 include/asm-x86/bootparam.h   |5 
 include/asm-x86/efi.h |   70 ++
 include/asm-x86/fixmap_64.h   |3 
 9 files changed, 817 insertions(+), 4 deletions(-)

--- /dev/null
+++ b/arch/x86/kernel/efi_64.c
@@ -0,0 +1,171 @@
+/*
+ * 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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+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 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(_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(_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 

[PATCH -mm 1/4 -v6] x86_64 EFI runtime service support: EFI basic runtime service support

2007-11-26 Thread Huang, Ying
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_callx (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: Chandramouli Narayanan [EMAIL PROTECTED]
Signed-off-by: Huang Ying [EMAIL PROTECTED]

---
 arch/x86/Kconfig  |2 
 arch/x86/kernel/Makefile_64   |1 
 arch/x86/kernel/efi.c |  484 ++
 arch/x86/kernel/efi_64.c  |  171 ++
 arch/x86/kernel/efi_stub_64.S |   68 +
 arch/x86/kernel/setup_64.c|   17 +
 include/asm-x86/bootparam.h   |5 
 include/asm-x86/efi.h |   70 ++
 include/asm-x86/fixmap_64.h   |3 
 9 files changed, 817 insertions(+), 4 deletions(-)

--- /dev/null
+++ b/arch/x86/kernel/efi_64.c
@@ -0,0 +1,171 @@
+/*
+ * 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
+
+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 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