struct efi_memory_map memmap;
 
+bool efi_64bit;
+static bool efi_native;
+
 static struct efi efi_phys __initdata;
 static efi_system_table_t efi_systab __initdata;
 
        sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map);
 }
 
-void __init efi_memblock_x86_reserve_range(void)
+int __init efi_memblock_x86_reserve_range(void)
 {
        unsigned long pmap;
 
 #ifdef CONFIG_X86_32
+       /* Can't handle data above 4GB at this time */
+       if (boot_params.efi_info.efi_memmap_hi) {
+               pr_err("Memory map is above 4GB, disabling EFI.\n");
+               return -EINVAL;
+       }
        pmap = boot_params.efi_info.efi_memmap;
 #else
        pmap = (boot_params.efi_info.efi_memmap |
        memmap.desc_version = boot_params.efi_info.efi_memdesc_version;
        memmap.desc_size = boot_params.efi_info.efi_memdesc_size;
        memblock_reserve(pmap, memmap.nr_map * memmap.desc_size);
+
+       return 0;
 }
 
 #if EFI_DEBUG
 
 static int __init efi_systab_init(void *phys)
 {
-       efi.systab = early_ioremap((unsigned long)efi_phys.systab,
-                                  sizeof(efi_system_table_t));
-       if (efi.systab == NULL) {
-               pr_err("Couldn't map the system table!\n");
-               return -ENOMEM;
+       if (efi_64bit) {
+               efi_system_table_64_t *systab64;
+               u64 tmp = 0;
+
+               systab64 = early_ioremap((unsigned long)phys,
+                                        sizeof(*systab64));
+               if (systab64 == NULL) {
+                       pr_err("Couldn't map the system table!\n");
+                       return -ENOMEM;
+               }
+
+               efi_systab.hdr = systab64->hdr;
+               efi_systab.fw_vendor = systab64->fw_vendor;
+               tmp |= systab64->fw_vendor;
+               efi_systab.fw_revision = systab64->fw_revision;
+               efi_systab.con_in_handle = systab64->con_in_handle;
+               tmp |= systab64->con_in_handle;
+               efi_systab.con_in = systab64->con_in;
+               tmp |= systab64->con_in;
+               efi_systab.con_out_handle = systab64->con_out_handle;
+               tmp |= systab64->con_out_handle;
+               efi_systab.con_out = systab64->con_out;
+               tmp |= systab64->con_out;
+               efi_systab.stderr_handle = systab64->stderr_handle;
+               tmp |= systab64->stderr_handle;
+               efi_systab.stderr = systab64->stderr;
+               tmp |= systab64->stderr;
+               efi_systab.runtime = (void *)(unsigned long)systab64->runtime;
+               tmp |= systab64->runtime;
+               efi_systab.boottime = (void *)(unsigned long)systab64->boottime;
+               tmp |= systab64->boottime;
+               efi_systab.nr_tables = systab64->nr_tables;
+               efi_systab.tables = systab64->tables;
+               tmp |= systab64->tables;
+
+               early_iounmap(systab64, sizeof(*systab64));
+#ifdef CONFIG_X86_32
+               if (tmp >> 32) {
+                       pr_err("EFI data located above 4GB, disabling EFI.\n");
+                       return -EINVAL;
+               }
+#endif
+       } else {
+               efi_system_table_32_t *systab32;
+
+               systab32 = early_ioremap((unsigned long)phys,
+                                        sizeof(*systab32));
+               if (systab32 == NULL) {
+                       pr_err("Couldn't map the system table!\n");
+                       return -ENOMEM;
+               }
+
+               efi_systab.hdr = systab32->hdr;
+               efi_systab.fw_vendor = systab32->fw_vendor;
+               efi_systab.fw_revision = systab32->fw_revision;
+               efi_systab.con_in_handle = systab32->con_in_handle;
+               efi_systab.con_in = systab32->con_in;
+               efi_systab.con_out_handle = systab32->con_out_handle;
+               efi_systab.con_out = systab32->con_out;
+               efi_systab.stderr_handle = systab32->stderr_handle;
+               efi_systab.stderr = systab32->stderr;
+               efi_systab.runtime = (void *)(unsigned long)systab32->runtime;
+               efi_systab.boottime = (void *)(unsigned long)systab32->boottime;
+               efi_systab.nr_tables = systab32->nr_tables;
+               efi_systab.tables = systab32->tables;
+
+               early_iounmap(systab32, sizeof(*systab32));
        }
-       memcpy(&efi_systab, efi.systab, sizeof(efi_system_table_t));
-       early_iounmap(efi.systab, sizeof(efi_system_table_t));
+
        efi.systab = &efi_systab;
 
        /*
 
 static int __init efi_config_init(u64 tables, int nr_tables)
 {
-       efi_config_table_t *config_tables;
-       int i, sz = sizeof(efi_config_table_t);
+       void *config_tables, *tablep;
+       int i, sz;
+
+       if (efi_64bit)
+               sz = sizeof(efi_config_table_64_t);
+       else
+               sz = sizeof(efi_config_table_32_t);
 
        /*
         * Let's see what config tables the firmware passed to us.
         */
-       config_tables = early_ioremap(efi.systab->tables,
-                                     efi.systab->nr_tables * sz);
+       config_tables = early_ioremap(tables, nr_tables * sz);
        if (config_tables == NULL) {
                pr_err("Could not map Configuration table!\n");
                return -ENOMEM;
        }
 
+       tablep = config_tables;
        pr_info("");
        for (i = 0; i < efi.systab->nr_tables; i++) {
-               efi_guid_t guid = config_tables[i].guid;
-               unsigned long table = config_tables[i].table;
-
+               efi_guid_t guid;
+               unsigned long table;
+
+               if (efi_64bit) {
+                       u64 table64;
+                       guid = ((efi_config_table_64_t *)tablep)->guid;
+                       table64 = ((efi_config_table_64_t *)tablep)->table;
+                       table = table64;
+#ifdef CONFIG_X86_32
+                       if (table64 >> 32) {
+                               pr_cont("\n");
+                               pr_err("Table located above 4GB, disabling EFI.\n");
+                               early_iounmap(config_tables,
+                                             efi.systab->nr_tables * sz);
+                               return -EINVAL;
+                       }
+#endif
+               } else {
+                       guid = ((efi_config_table_32_t *)tablep)->guid;
+                       table = ((efi_config_table_32_t *)tablep)->table;
+               }
                if (!efi_guidcmp(guid, MPS_TABLE_GUID)) {
                        efi.mps = table;
                        pr_cont(" MPS=0x%lx ", table);
                        efi.uga = table;
                        pr_cont(" UGA=0x%lx ", table);
                }
+               tablep += sz;
        }
        pr_cont("\n");
        early_iounmap(config_tables, efi.systab->nr_tables * sz);
-
        return 0;
 }
 
        void *tmp;
 
 #ifdef CONFIG_X86_32
+       if (boot_params.efi_info.efi_systab_hi ||
+           boot_params.efi_info.efi_memmap_hi) {
+               pr_info("Table located above 4GB, disabling EFI.\n");
+               efi_enabled = 0;
+               return;
+       }
        efi_phys.systab = (efi_system_table_t *)boot_params.efi_info.efi_systab;
+       efi_native = !efi_64bit;
 #else
        efi_phys.systab = (efi_system_table_t *)
-               (boot_params.efi_info.efi_systab |
-                ((__u64)boot_params.efi_info.efi_systab_hi<<32));
+                         (boot_params.efi_info.efi_systab |
+                         ((__u64)boot_params.efi_info.efi_systab_hi<<32));
+       efi_native = efi_64bit;
 #endif
 
        if (efi_systab_init(efi_phys.systab)) {
                return;
        }
 
-       if (efi_runtime_init()) {
+       /*
+        * Note: We currently don't support runtime services on an EFI
+        * that doesn't match the kernel 32/64-bit mode.
+        */
+
+       if (!efi_native)
+               pr_info("No EFI runtime due to 32/64-bit mismatch with kernel\n");
+       else if (efi_runtime_init()) {
                efi_enabled = 0;
                return;
        }
                efi_enabled = 0;
                return;
        }
-
 #ifdef CONFIG_X86_32
-       x86_platform.get_wallclock = efi_get_time;
-       x86_platform.set_wallclock = efi_set_rtc_mmss;
+       if (efi_native) {
+               x86_platform.get_wallclock = efi_get_time;
+               x86_platform.set_wallclock = efi_set_rtc_mmss;
+       }
 #endif
 
 #if EFI_DEBUG
 
        efi.systab = NULL;
 
+       /*
+        * We don't do virtual mode, since we don't do runtime services, on
+        * non-native EFI
+        */
+
+       if (!efi_native)
+               goto out;
+
        /* Merge contiguous regions of the same type and attribute */
        for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
                u64 prev_size;
        efi.query_capsule_caps = virt_efi_query_capsule_caps;
        if (__supported_pte_mask & _PAGE_NX)
                runtime_code_page_mkexec();
+
+out:
        early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size);
        memmap.map = NULL;
        kfree(new_memmap);