All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/5] [RESEND] x86: efi: cleanups and basic 32/64-bit support
@ 2012-02-08  0:25 Olof Johansson
  2012-02-08  0:25 ` [PATCH 1/5] x86: efi: refactor efi_init() a bit Olof Johansson
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Olof Johansson @ 2012-02-08  0:25 UTC (permalink / raw)
  To: x86, hpa, mingo, tglx; +Cc: linux-kernel, matt.fleming, mjg

This series allows basic booting of a 32-bit kernel on 64-bit EFI and vice
versa.  It's needed by Chrome OS, and we've been carrying a nasty hack
to do it that I've cleaned up and made sure it works in both directions.

Tested on Chrome OS for 64-bit EFI 32-bit kernel. Tested with an old
MacBook for 32-bit EFI, 64-bit kernel.

Note that this is required, but not sufficient, for full platform support for
EFI in a mixed environment. There is no handling of runtime services, and no
thunking for going in and out of firmware in a different mode.


Resend of the last posted version. Acked by Matt, and Matthew seems to be OK
with it as well (see http://marc.info/?l=linux-kernel&m=132578786105542).

Please consider for 3.4 merge window. Thanks!


-Olof


Changelog is:                                                                                                               
v4:
* Removed bogus memdesc warning printout
* Fixed up printk formatting, removing redundant EFI output
* Some of the earlier cleanup was accidentally reverted by this patch, fixed.                                                                      
* Reworded some messages to not have to line wrap printk strings  

v3:                                                                                                                                                
* Reorganized to a series of patches to make it easier to review, and                                                                              
  do some of the cleanups I had left out before.                                                                                                   

v2:                                                                                                                                                
* Added graceful error handling for 32-bit kernel that gets passed                                                                                 
EFI data above 4GB.                                                                                                                              
* Removed some warnings that were missed in first version.                                                                                         




^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 1/5] x86: efi: refactor efi_init() a bit
  2012-02-08  0:25 [PATCH v4 0/5] [RESEND] x86: efi: cleanups and basic 32/64-bit support Olof Johansson
@ 2012-02-08  0:25 ` Olof Johansson
  2012-02-08  0:25 ` [PATCH 2/5] x86: efi: convert printk to pr_*() Olof Johansson
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 14+ messages in thread
From: Olof Johansson @ 2012-02-08  0:25 UTC (permalink / raw)
  To: x86, hpa, mingo, tglx; +Cc: linux-kernel, matt.fleming, mjg, Olof Johansson

Break out some of the init steps into helper functions.

Only change to execution flow is the removal of the warning when the
kernel memdesc structure differ in size from what firmware specifies
since it's a bogus warning (it's a valid difference per spec).

v4:
* Removed memdesc warning as per above

Signed-off-by: Olof Johansson <olof@lixom.net>
Acked-by: Matt Fleming <matt.fleming@intel.com>
---
 arch/x86/platform/efi/efi.c |   89 ++++++++++++++++++++++++++-----------------
 1 files changed, 54 insertions(+), 35 deletions(-)

diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 264cc6e..fec2b2e 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -436,23 +436,8 @@ static void __init efi_free_boot_services(void)
 	}
 }
 
-void __init efi_init(void)
+static void __init efi_systab_init(void *phys)
 {
-	efi_config_table_t *config_tables;
-	efi_runtime_services_t *runtime;
-	efi_char16_t *c16;
-	char vendor[100] = "unknown";
-	int i = 0;
-	void *tmp;
-
-#ifdef CONFIG_X86_32
-	efi_phys.systab = (efi_system_table_t *)boot_params.efi_info.efi_systab;
-#else
-	efi_phys.systab = (efi_system_table_t *)
-		(boot_params.efi_info.efi_systab |
-		 ((__u64)boot_params.efi_info.efi_systab_hi<<32));
-#endif
-
 	efi.systab = early_ioremap((unsigned long)efi_phys.systab,
 				   sizeof(efi_system_table_t));
 	if (efi.systab == NULL)
@@ -471,22 +456,12 @@ void __init efi_init(void)
 		       "%d.%02d, expected 1.00 or greater!\n",
 		       efi.systab->hdr.revision >> 16,
 		       efi.systab->hdr.revision & 0xffff);
+}
 
-	/*
-	 * Show what we know for posterity
-	 */
-	c16 = tmp = early_ioremap(efi.systab->fw_vendor, 2);
-	if (c16) {
-		for (i = 0; i < sizeof(vendor) - 1 && *c16; ++i)
-			vendor[i] = *c16++;
-		vendor[i] = '\0';
-	} else
-		printk(KERN_ERR PFX "Could not map the firmware vendor!\n");
-	early_iounmap(tmp, 2);
-
-	printk(KERN_INFO "EFI v%u.%.02u by %s\n",
-	       efi.systab->hdr.revision >> 16,
-	       efi.systab->hdr.revision & 0xffff, vendor);
+static void __init efi_config_init(u64 tables, int nr_tables)
+{
+	efi_config_table_t *config_tables;
+	int i;
 
 	/*
 	 * Let's see what config tables the firmware passed to us.
@@ -533,6 +508,11 @@ void __init efi_init(void)
 	printk("\n");
 	early_iounmap(config_tables,
 			  efi.systab->nr_tables * sizeof(efi_config_table_t));
+}
+
+static void __init efi_runtime_init(void)
+{
+	efi_runtime_services_t *runtime;
 
 	/*
 	 * Check out the runtime services table. We need to map
@@ -561,7 +541,10 @@ void __init efi_init(void)
 		printk(KERN_ERR "Could not map the EFI runtime service "
 		       "table!\n");
 	early_iounmap(runtime, sizeof(efi_runtime_services_t));
+}
 
+static void __init efi_memmap_init(void)
+{
 	/* Map the EFI memory map */
 	memmap.map = early_ioremap((unsigned long)memmap.phys_map,
 				   memmap.nr_map * memmap.desc_size);
@@ -569,12 +552,48 @@ void __init efi_init(void)
 		printk(KERN_ERR "Could not map the EFI memory map!\n");
 	memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size);
 
-	if (memmap.desc_size != sizeof(efi_memory_desc_t))
-		printk(KERN_WARNING
-		  "Kernel-defined memdesc doesn't match the one from EFI!\n");
-
 	if (add_efi_memmap)
 		do_add_efi_memmap();
+}
+
+void __init efi_init(void)
+{
+	efi_char16_t *c16;
+	char vendor[100] = "unknown";
+	int i = 0;
+	void *tmp;
+
+#ifdef CONFIG_X86_32
+	efi_phys.systab = (efi_system_table_t *)boot_params.efi_info.efi_systab;
+#else
+	efi_phys.systab = (efi_system_table_t *)
+		(boot_params.efi_info.efi_systab |
+		 ((__u64)boot_params.efi_info.efi_systab_hi<<32));
+#endif
+
+	efi_systab_init(efi_phys.systab);
+
+	/*
+	 * Show what we know for posterity
+	 */
+	c16 = tmp = early_ioremap(efi.systab->fw_vendor, 2);
+	if (c16) {
+		for (i = 0; i < sizeof(vendor) - 1 && *c16; ++i)
+			vendor[i] = *c16++;
+		vendor[i] = '\0';
+	} else
+		printk(KERN_ERR PFX "Could not map the firmware vendor!\n");
+	early_iounmap(tmp, 2);
+
+	printk(KERN_INFO "EFI v%u.%.02u by %s\n",
+	       efi.systab->hdr.revision >> 16,
+	       efi.systab->hdr.revision & 0xffff, vendor);
+
+	efi_config_init(efi.systab->tables, efi.systab->nr_tables);
+
+	efi_runtime_init();
+
+	efi_memmap_init();
 
 #ifdef CONFIG_X86_32
 	x86_platform.get_wallclock = efi_get_time;
-- 
1.7.9.111.gf3fb0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 2/5] x86: efi: convert printk to pr_*()
  2012-02-08  0:25 [PATCH v4 0/5] [RESEND] x86: efi: cleanups and basic 32/64-bit support Olof Johansson
  2012-02-08  0:25 ` [PATCH 1/5] x86: efi: refactor efi_init() a bit Olof Johansson
@ 2012-02-08  0:25 ` Olof Johansson
  2012-02-08  0:25 ` [PATCH 3/5] x86: efi: cleanup config table walking Olof Johansson
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 14+ messages in thread
From: Olof Johansson @ 2012-02-08  0:25 UTC (permalink / raw)
  To: x86, hpa, mingo, tglx
  Cc: linux-kernel, matt.fleming, mjg, Olof Johansson, Joe Perches

Alright, I guess I'll go through and convert them, even though
there's no net gain to speak of.

v4:
* Switched to pr_fmt and removed some redundant use of "EFI" in
  messages.

Signed-off-by: Olof Johansson <olof@lixom.net>
Cc: Joe Perches <joe@perches.com>
---
 arch/x86/platform/efi/efi.c |   58 +++++++++++++++++++++---------------------
 1 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index fec2b2e..777de17 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -26,6 +26,8 @@
  *	Skip non-WB memory and ignore empty memory ranges.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/efi.h>
@@ -47,7 +49,6 @@
 #include <asm/x86_init.h>
 
 #define EFI_DEBUG	1
-#define PFX 		"EFI: "
 
 int efi_enabled;
 EXPORT_SYMBOL(efi_enabled);
@@ -254,7 +255,7 @@ int efi_set_rtc_mmss(unsigned long nowtime)
 
 	status = efi.get_time(&eft, &cap);
 	if (status != EFI_SUCCESS) {
-		printk(KERN_ERR "Oops: efitime: can't read time!\n");
+		pr_err("Oops: efitime: can't read time!\n");
 		return -1;
 	}
 
@@ -268,7 +269,7 @@ int efi_set_rtc_mmss(unsigned long nowtime)
 
 	status = efi.set_time(&eft);
 	if (status != EFI_SUCCESS) {
-		printk(KERN_ERR "Oops: efitime: can't write time!\n");
+		pr_err("Oops: efitime: can't write time!\n");
 		return -1;
 	}
 	return 0;
@@ -282,7 +283,7 @@ unsigned long efi_get_time(void)
 
 	status = efi.get_time(&eft, &cap);
 	if (status != EFI_SUCCESS)
-		printk(KERN_ERR "Oops: efitime: can't read time!\n");
+		pr_err("Oops: efitime: can't read time!\n");
 
 	return mktime(eft.year, eft.month, eft.day, eft.hour,
 		      eft.minute, eft.second);
@@ -374,7 +375,7 @@ static void __init print_efi_memmap(void)
 	     p < memmap.map_end;
 	     p += memmap.desc_size, i++) {
 		md = p;
-		printk(KERN_INFO PFX "mem%02u: type=%u, attr=0x%llx, "
+		pr_info("mem%02u: type=%u, attr=0x%llx, "
 			"range=[0x%016llx-0x%016llx) (%lluMB)\n",
 			i, md->type, md->attribute, md->phys_addr,
 			md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT),
@@ -407,7 +408,7 @@ void __init efi_reserve_boot_services(void)
 			memblock_is_region_reserved(start, size)) {
 			/* Could not reserve, skip it */
 			md->num_pages = 0;
-			memblock_dbg(PFX "Could not reserve boot range "
+			memblock_dbg("Could not reserve boot range "
 					"[0x%010llx-0x%010llx]\n",
 						start, start+size-1);
 		} else
@@ -441,7 +442,7 @@ static void __init efi_systab_init(void *phys)
 	efi.systab = early_ioremap((unsigned long)efi_phys.systab,
 				   sizeof(efi_system_table_t));
 	if (efi.systab == NULL)
-		printk(KERN_ERR "Couldn't map the EFI system table!\n");
+		pr_err("Couldn't map the system table!\n");
 	memcpy(&efi_systab, efi.systab, sizeof(efi_system_table_t));
 	early_iounmap(efi.systab, sizeof(efi_system_table_t));
 	efi.systab = &efi_systab;
@@ -450,9 +451,9 @@ static void __init efi_systab_init(void *phys)
 	 * Verify the EFI Table
 	 */
 	if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
-		printk(KERN_ERR "EFI system table signature incorrect!\n");
+		pr_err("System table signature incorrect!\n");
 	if ((efi.systab->hdr.revision >> 16) == 0)
-		printk(KERN_ERR "Warning: EFI system table version "
+		pr_err("Warning: System table version "
 		       "%d.%02d, expected 1.00 or greater!\n",
 		       efi.systab->hdr.revision >> 16,
 		       efi.systab->hdr.revision & 0xffff);
@@ -470,42 +471,42 @@ static void __init efi_config_init(u64 tables, int nr_tables)
 		efi.systab->tables,
 		efi.systab->nr_tables * sizeof(efi_config_table_t));
 	if (config_tables == NULL)
-		printk(KERN_ERR "Could not map EFI Configuration Table!\n");
+		pr_err("Could not map Configuration table!\n");
 
-	printk(KERN_INFO);
+	pr_info("");
 	for (i = 0; i < efi.systab->nr_tables; i++) {
 		if (!efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID)) {
 			efi.mps = config_tables[i].table;
-			printk(" MPS=0x%lx ", config_tables[i].table);
+			pr_cont(" MPS=0x%lx ", config_tables[i].table);
 		} else if (!efi_guidcmp(config_tables[i].guid,
 					ACPI_20_TABLE_GUID)) {
 			efi.acpi20 = config_tables[i].table;
-			printk(" ACPI 2.0=0x%lx ", config_tables[i].table);
+			pr_cont(" ACPI 2.0=0x%lx ", config_tables[i].table);
 		} else if (!efi_guidcmp(config_tables[i].guid,
 					ACPI_TABLE_GUID)) {
 			efi.acpi = config_tables[i].table;
-			printk(" ACPI=0x%lx ", config_tables[i].table);
+			pr_cont(" ACPI=0x%lx ", config_tables[i].table);
 		} else if (!efi_guidcmp(config_tables[i].guid,
 					SMBIOS_TABLE_GUID)) {
 			efi.smbios = config_tables[i].table;
-			printk(" SMBIOS=0x%lx ", config_tables[i].table);
+			pr_cont(" SMBIOS=0x%lx ", config_tables[i].table);
 #ifdef CONFIG_X86_UV
 		} else if (!efi_guidcmp(config_tables[i].guid,
 					UV_SYSTEM_TABLE_GUID)) {
 			efi.uv_systab = config_tables[i].table;
-			printk(" UVsystab=0x%lx ", config_tables[i].table);
+			pr_cont(" UVsystab=0x%lx ", config_tables[i].table);
 #endif
 		} else if (!efi_guidcmp(config_tables[i].guid,
 					HCDP_TABLE_GUID)) {
 			efi.hcdp = config_tables[i].table;
-			printk(" HCDP=0x%lx ", config_tables[i].table);
+			pr_cont(" HCDP=0x%lx ", config_tables[i].table);
 		} else if (!efi_guidcmp(config_tables[i].guid,
 					UGA_IO_PROTOCOL_GUID)) {
 			efi.uga = config_tables[i].table;
-			printk(" UGA=0x%lx ", config_tables[i].table);
+			pr_cont(" UGA=0x%lx ", config_tables[i].table);
 		}
 	}
-	printk("\n");
+	pr_cont("\n");
 	early_iounmap(config_tables,
 			  efi.systab->nr_tables * sizeof(efi_config_table_t));
 }
@@ -538,8 +539,7 @@ static void __init efi_runtime_init(void)
 		 */
 		efi.get_time = phys_efi_get_time;
 	} else
-		printk(KERN_ERR "Could not map the EFI runtime service "
-		       "table!\n");
+		pr_err("Could not map the runtime service table!\n");
 	early_iounmap(runtime, sizeof(efi_runtime_services_t));
 }
 
@@ -549,7 +549,7 @@ static void __init efi_memmap_init(void)
 	memmap.map = early_ioremap((unsigned long)memmap.phys_map,
 				   memmap.nr_map * memmap.desc_size);
 	if (memmap.map == NULL)
-		printk(KERN_ERR "Could not map the EFI memory map!\n");
+		pr_err("Could not map the memory map!\n");
 	memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size);
 
 	if (add_efi_memmap)
@@ -582,12 +582,12 @@ void __init efi_init(void)
 			vendor[i] = *c16++;
 		vendor[i] = '\0';
 	} else
-		printk(KERN_ERR PFX "Could not map the firmware vendor!\n");
+		pr_err("Could not map the firmware vendor!\n");
 	early_iounmap(tmp, 2);
 
-	printk(KERN_INFO "EFI v%u.%.02u by %s\n",
-	       efi.systab->hdr.revision >> 16,
-	       efi.systab->hdr.revision & 0xffff, vendor);
+	pr_info("EFI v%u.%.02u by %s\n",
+		efi.systab->hdr.revision >> 16,
+		efi.systab->hdr.revision & 0xffff, vendor);
 
 	efi_config_init(efi.systab->tables, efi.systab->nr_tables);
 
@@ -714,7 +714,7 @@ void __init efi_enter_virtual_mode(void)
 		md->virt_addr = (u64) (unsigned long) va;
 
 		if (!va) {
-			printk(KERN_ERR PFX "ioremap of 0x%llX failed!\n",
+			pr_err("ioremap of 0x%llX failed!\n",
 			       (unsigned long long)md->phys_addr);
 			continue;
 		}
@@ -741,8 +741,8 @@ void __init efi_enter_virtual_mode(void)
 		(efi_memory_desc_t *)__pa(new_memmap));
 
 	if (status != EFI_SUCCESS) {
-		printk(KERN_ALERT "Unable to switch EFI into virtual mode "
-		       "(status=%lx)!\n", status);
+		pr_alert("Unable to switch EFI into virtual mode "
+			 "(status=%lx)!\n", status);
 		panic("EFI call to SetVirtualAddressMap() failed!");
 	}
 
-- 
1.7.9.111.gf3fb0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 3/5] x86: efi: cleanup config table walking
  2012-02-08  0:25 [PATCH v4 0/5] [RESEND] x86: efi: cleanups and basic 32/64-bit support Olof Johansson
  2012-02-08  0:25 ` [PATCH 1/5] x86: efi: refactor efi_init() a bit Olof Johansson
  2012-02-08  0:25 ` [PATCH 2/5] x86: efi: convert printk to pr_*() Olof Johansson
@ 2012-02-08  0:25 ` Olof Johansson
  2012-02-08  0:25 ` [PATCH 4/5] x86: efi: add basic error handling Olof Johansson
  2012-02-08  0:25 ` [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel Olof Johansson
  4 siblings, 0 replies; 14+ messages in thread
From: Olof Johansson @ 2012-02-08  0:25 UTC (permalink / raw)
  To: x86, hpa, mingo, tglx; +Cc: linux-kernel, matt.fleming, mjg, Olof Johansson

Trivial cleanup, move guid and table pointers to local copies to
make the code cleaner.

Signed-off-by: Olof Johansson <olof@lixom.net>
Acked-by: Matt Fleming <matt.fleming@intel.com>
---
 arch/x86/platform/efi/efi.c |   61 +++++++++++++++++++-----------------------
 1 files changed, 28 insertions(+), 33 deletions(-)

diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 777de17..05e543d 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -462,53 +462,48 @@ static void __init efi_systab_init(void *phys)
 static void __init efi_config_init(u64 tables, int nr_tables)
 {
 	efi_config_table_t *config_tables;
-	int i;
+	int i, sz = sizeof(efi_config_table_t);
 
 	/*
 	 * Let's see what config tables the firmware passed to us.
 	 */
-	config_tables = early_ioremap(
-		efi.systab->tables,
-		efi.systab->nr_tables * sizeof(efi_config_table_t));
+	config_tables = early_ioremap(efi.systab->tables,
+				      efi.systab->nr_tables * sz);
 	if (config_tables == NULL)
 		pr_err("Could not map Configuration table!\n");
 
 	pr_info("");
 	for (i = 0; i < efi.systab->nr_tables; i++) {
-		if (!efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID)) {
-			efi.mps = config_tables[i].table;
-			pr_cont(" MPS=0x%lx ", config_tables[i].table);
-		} else if (!efi_guidcmp(config_tables[i].guid,
-					ACPI_20_TABLE_GUID)) {
-			efi.acpi20 = config_tables[i].table;
-			pr_cont(" ACPI 2.0=0x%lx ", config_tables[i].table);
-		} else if (!efi_guidcmp(config_tables[i].guid,
-					ACPI_TABLE_GUID)) {
-			efi.acpi = config_tables[i].table;
-			pr_cont(" ACPI=0x%lx ", config_tables[i].table);
-		} else if (!efi_guidcmp(config_tables[i].guid,
-					SMBIOS_TABLE_GUID)) {
-			efi.smbios = config_tables[i].table;
-			pr_cont(" SMBIOS=0x%lx ", config_tables[i].table);
+		efi_guid_t guid = config_tables[i].guid;
+		unsigned long table = config_tables[i].table;
+
+		if (!efi_guidcmp(guid, MPS_TABLE_GUID)) {
+			efi.mps = table;
+			pr_cont(" MPS=0x%lx ", table);
+		} else if (!efi_guidcmp(guid, ACPI_20_TABLE_GUID)) {
+			efi.acpi20 = table;
+			pr_cont(" ACPI 2.0=0x%lx ", table);
+		} else if (!efi_guidcmp(guid, ACPI_TABLE_GUID)) {
+			efi.acpi = table;
+			pr_cont(" ACPI=0x%lx ", table);
+		} else if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID)) {
+			efi.smbios = table;
+			pr_cont(" SMBIOS=0x%lx ", table);
 #ifdef CONFIG_X86_UV
-		} else if (!efi_guidcmp(config_tables[i].guid,
-					UV_SYSTEM_TABLE_GUID)) {
-			efi.uv_systab = config_tables[i].table;
-			pr_cont(" UVsystab=0x%lx ", config_tables[i].table);
+		} else if (!efi_guidcmp(guid, UV_SYSTEM_TABLE_GUID)) {
+			efi.uv_systab = table;
+			pr_cont(" UVsystab=0x%lx ", table);
 #endif
-		} else if (!efi_guidcmp(config_tables[i].guid,
-					HCDP_TABLE_GUID)) {
-			efi.hcdp = config_tables[i].table;
-			pr_cont(" HCDP=0x%lx ", config_tables[i].table);
-		} else if (!efi_guidcmp(config_tables[i].guid,
-					UGA_IO_PROTOCOL_GUID)) {
-			efi.uga = config_tables[i].table;
-			pr_cont(" UGA=0x%lx ", config_tables[i].table);
+		} else if (!efi_guidcmp(guid, HCDP_TABLE_GUID)) {
+			efi.hcdp = table;
+			pr_cont(" HCDP=0x%lx ", table);
+		} else if (!efi_guidcmp(guid, UGA_IO_PROTOCOL_GUID)) {
+			efi.uga = table;
+			pr_cont(" UGA=0x%lx ", table);
 		}
 	}
 	pr_cont("\n");
-	early_iounmap(config_tables,
-			  efi.systab->nr_tables * sizeof(efi_config_table_t));
+	early_iounmap(config_tables, efi.systab->nr_tables * sz);
 }
 
 static void __init efi_runtime_init(void)
-- 
1.7.9.111.gf3fb0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 4/5] x86: efi: add basic error handling
  2012-02-08  0:25 [PATCH v4 0/5] [RESEND] x86: efi: cleanups and basic 32/64-bit support Olof Johansson
                   ` (2 preceding siblings ...)
  2012-02-08  0:25 ` [PATCH 3/5] x86: efi: cleanup config table walking Olof Johansson
@ 2012-02-08  0:25 ` Olof Johansson
  2012-02-08  0:25 ` [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel Olof Johansson
  4 siblings, 0 replies; 14+ messages in thread
From: Olof Johansson @ 2012-02-08  0:25 UTC (permalink / raw)
  To: x86, hpa, mingo, tglx; +Cc: linux-kernel, matt.fleming, mjg, Olof Johansson

It's not perfect, but way better than before. Mark efi_enabled as false in
case of error and at least stop dereferencing pointers that are known to
be invalid.

The only significant missing piece is the lack of undoing the
memblock_reserve of the memory that efi marks as in use. On the other
hand, it's not a large amount of memory, and leaving it unavailable for
system use should be the safer choice anyway.

Signed-off-by: Olof Johansson <olof@lixom.net>
Acked-by: Matt Fleming <matt.fleming@intel.com>
---
 arch/x86/platform/efi/efi.c |   85 +++++++++++++++++++++++++++++--------------
 1 files changed, 57 insertions(+), 28 deletions(-)

diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 05e543d..c1111f7 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -437,12 +437,14 @@ static void __init efi_free_boot_services(void)
 	}
 }
 
-static void __init efi_systab_init(void *phys)
+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)
+	if (efi.systab == NULL) {
 		pr_err("Couldn't map the system table!\n");
+		return -ENOMEM;
+	}
 	memcpy(&efi_systab, efi.systab, sizeof(efi_system_table_t));
 	early_iounmap(efi.systab, sizeof(efi_system_table_t));
 	efi.systab = &efi_systab;
@@ -450,16 +452,20 @@ static void __init efi_systab_init(void *phys)
 	/*
 	 * Verify the EFI Table
 	 */
-	if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+	if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
 		pr_err("System table signature incorrect!\n");
+		return -EINVAL;
+	}
 	if ((efi.systab->hdr.revision >> 16) == 0)
 		pr_err("Warning: System table version "
 		       "%d.%02d, expected 1.00 or greater!\n",
 		       efi.systab->hdr.revision >> 16,
 		       efi.systab->hdr.revision & 0xffff);
+
+	return 0;
 }
 
-static void __init efi_config_init(u64 tables, int nr_tables)
+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);
@@ -469,8 +475,10 @@ static void __init efi_config_init(u64 tables, int nr_tables)
 	 */
 	config_tables = early_ioremap(efi.systab->tables,
 				      efi.systab->nr_tables * sz);
-	if (config_tables == NULL)
+	if (config_tables == NULL) {
 		pr_err("Could not map Configuration table!\n");
+		return -ENOMEM;
+	}
 
 	pr_info("");
 	for (i = 0; i < efi.systab->nr_tables; i++) {
@@ -504,9 +512,11 @@ static void __init efi_config_init(u64 tables, int nr_tables)
 	}
 	pr_cont("\n");
 	early_iounmap(config_tables, efi.systab->nr_tables * sz);
+
+	return 0;
 }
 
-static void __init efi_runtime_init(void)
+static int __init efi_runtime_init(void)
 {
 	efi_runtime_services_t *runtime;
 
@@ -518,37 +528,44 @@ static void __init efi_runtime_init(void)
 	 */
 	runtime = 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;
-		/*
-		 * Make efi_get_time can be called before entering
-		 * virtual mode.
-		 */
-		efi.get_time = phys_efi_get_time;
-	} else
+	if (!runtime) {
 		pr_err("Could not map the runtime service table!\n");
+		return -ENOMEM;
+	}
+	/*
+	 * 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;
+	/*
+	 * Make efi_get_time can be called before entering
+	 * virtual mode.
+	 */
+	efi.get_time = phys_efi_get_time;
 	early_iounmap(runtime, sizeof(efi_runtime_services_t));
+
+	return 0;
 }
 
-static void __init efi_memmap_init(void)
+static int __init efi_memmap_init(void)
 {
 	/* Map the EFI memory map */
 	memmap.map = early_ioremap((unsigned long)memmap.phys_map,
 				   memmap.nr_map * memmap.desc_size);
-	if (memmap.map == NULL)
+	if (memmap.map == NULL) {
 		pr_err("Could not map the memory map!\n");
+		return -ENOMEM;
+	}
 	memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size);
 
 	if (add_efi_memmap)
 		do_add_efi_memmap();
+
+	return 0;
 }
 
 void __init efi_init(void)
@@ -566,7 +583,10 @@ void __init efi_init(void)
 		 ((__u64)boot_params.efi_info.efi_systab_hi<<32));
 #endif
 
-	efi_systab_init(efi_phys.systab);
+	if (efi_systab_init(efi_phys.systab)) {
+		efi_enabled = 0;
+		return;
+	}
 
 	/*
 	 * Show what we know for posterity
@@ -584,11 +604,20 @@ void __init efi_init(void)
 		efi.systab->hdr.revision >> 16,
 		efi.systab->hdr.revision & 0xffff, vendor);
 
-	efi_config_init(efi.systab->tables, efi.systab->nr_tables);
+	if (efi_config_init(efi.systab->tables, efi.systab->nr_tables)) {
+		efi_enabled = 0;
+		return;
+	}
 
-	efi_runtime_init();
+	if (efi_runtime_init()) {
+		efi_enabled = 0;
+		return;
+	}
 
-	efi_memmap_init();
+	if (efi_memmap_init()) {
+		efi_enabled = 0;
+		return;
+	}
 
 #ifdef CONFIG_X86_32
 	x86_platform.get_wallclock = efi_get_time;
-- 
1.7.9.111.gf3fb0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel
  2012-02-08  0:25 [PATCH v4 0/5] [RESEND] x86: efi: cleanups and basic 32/64-bit support Olof Johansson
                   ` (3 preceding siblings ...)
  2012-02-08  0:25 ` [PATCH 4/5] x86: efi: add basic error handling Olof Johansson
@ 2012-02-08  0:25 ` Olof Johansson
  2012-02-08 18:31   ` Matt Fleming
  4 siblings, 1 reply; 14+ messages in thread
From: Olof Johansson @ 2012-02-08  0:25 UTC (permalink / raw)
  To: x86, hpa, mingo, tglx; +Cc: linux-kernel, matt.fleming, mjg, Olof Johansson

Traditionally the kernel has refused to setup EFI at all if there's been
a mismatch in 32/64-bit mode between EFI and the kernel.

On some platforms that boot natively through EFI (Chrome OS being one),
we still need to get at least some of the static data such as memory
configuration out of EFI. Runtime services aren't as critical, and
it's a significant amount of work to implement switching between the
operating modes to call between kernel and firmware for thise cases. So
I'm ignoring it for now.

v4:
* Some of the earlier cleanup was accidentally reverted by this patch, fixed.
* Reworded some messages to not have to line wrap printk strings

v3:
* Reorganized to a series of patches to make it easier to review, and
  do some of the cleanups I had left out before.

v2:
* Added graceful error handling for 32-bit kernel that gets passed
  EFI data above 4GB.
* Removed some warnings that were missed in first version.

Signed-off-by: Olof Johansson <olof@lixom.net>
---
 arch/x86/include/asm/efi.h  |    2 +-
 arch/x86/kernel/setup.c     |   10 ++-
 arch/x86/platform/efi/efi.c |  164 +++++++++++++++++++++++++++++++++++++------
 include/linux/efi.h         |   45 ++++++++++++
 4 files changed, 195 insertions(+), 26 deletions(-)

diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index 26d8c18..4103e4d 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -90,7 +90,7 @@ extern u64 efi_call6(void *fp, u64 arg1, u64 arg2, u64 arg3,
 
 extern int add_efi_memmap;
 extern void efi_set_executable(efi_memory_desc_t *md, bool executable);
-extern void efi_memblock_x86_reserve_range(void);
+extern int efi_memblock_x86_reserve_range(void);
 extern void efi_call_phys_prelog(void);
 extern void efi_call_phys_epilog(void);
 
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index e22bb08..a7ce9b5 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -751,10 +751,16 @@ void __init setup_arch(char **cmdline_p)
 #endif
 #ifdef CONFIG_EFI
 	if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
-		     EFI_LOADER_SIGNATURE, 4)) {
+		     "EL32", 4)) {
 		efi_enabled = 1;
-		efi_memblock_x86_reserve_range();
+		efi_64bit = false;
+	} else if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
+		     "EL64", 4)) {
+		efi_enabled = 1;
+		efi_64bit = true;
 	}
+	if (efi_enabled && efi_memblock_x86_reserve_range())
+		efi_enabled = 0;
 #endif
 
 	x86_init.oem.arch_setup();
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index c1111f7..14449b3 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -68,6 +68,9 @@ EXPORT_SYMBOL(efi);
 
 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;
 
@@ -346,11 +349,16 @@ static void __init do_add_efi_memmap(void)
 	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 |
@@ -362,6 +370,8 @@ void __init efi_memblock_x86_reserve_range(void)
 	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
@@ -439,14 +449,75 @@ static void __init efi_free_boot_services(void)
 
 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.\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;
 
 	/*
@@ -467,24 +538,47 @@ static int __init efi_systab_init(void *phys)
 
 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.\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);
@@ -509,10 +603,10 @@ static int __init efi_config_init(u64 tables, int nr_tables)
 			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;
 }
 
@@ -576,11 +670,19 @@ void __init efi_init(void)
 	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.\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)) {
@@ -609,19 +711,26 @@ void __init efi_init(void)
 		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 && efi_runtime_init()) {
 		efi_enabled = 0;
 		return;
-	}
+	} else
+		pr_info("No EFI runtime due to 32/64b mismatch with kernel\n");
 
 	if (efi_memmap_init()) {
 		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
@@ -679,6 +788,13 @@ void __init efi_enter_virtual_mode(void)
 
 	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;
@@ -798,6 +914,8 @@ void __init efi_enter_virtual_mode(void)
 	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);
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 37c3007..17385ba 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -315,6 +315,16 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
 
 typedef struct {
 	efi_guid_t guid;
+	u64 table;
+} _efi_config_table_64_t;
+
+typedef struct {
+	efi_guid_t guid;
+	u32 table;
+} _efi_config_table_32_t;
+
+typedef struct {
+	efi_guid_t guid;
 	unsigned long table;
 } efi_config_table_t;
 
@@ -329,6 +339,40 @@ typedef struct {
 
 typedef struct {
 	efi_table_hdr_t hdr;
+	u64 fw_vendor;	/* physical addr of CHAR16 vendor string */
+	u32 fw_revision;
+	u32 __pad1;
+	u64 con_in_handle;
+	u64 con_in;
+	u64 con_out_handle;
+	u64 con_out;
+	u64 stderr_handle;
+	u64 stderr;
+	u64 runtime;
+	u64 boottime;
+	u32 nr_tables;
+	u32 __pad2;
+	u64 tables;
+} _efi_system_table_64_t;
+
+typedef struct {
+	efi_table_hdr_t hdr;
+	u32 fw_vendor;	/* physical addr of CHAR16 vendor string */
+	u32 fw_revision;
+	u32 con_in_handle;
+	u32 con_in;
+	u32 con_out_handle;
+	u32 con_out;
+	u32 stderr_handle;
+	u32 stderr;
+	u32 runtime;
+	u32 boottime;
+	u32 nr_tables;
+	u32 tables;
+} _efi_system_table_32_t;
+
+typedef struct {
+	efi_table_hdr_t hdr;
 	unsigned long fw_vendor;	/* physical addr of CHAR16 vendor string */
 	u32 fw_revision;
 	unsigned long con_in_handle;
@@ -497,6 +541,7 @@ extern int __init efi_setup_pcdp_console(char *);
 #ifdef CONFIG_EFI
 # ifdef CONFIG_X86
    extern int efi_enabled;
+   extern bool efi_64bit;
 # else
 #  define efi_enabled 1
 # endif
-- 
1.7.9.111.gf3fb0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel
  2012-02-08  0:25 ` [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel Olof Johansson
@ 2012-02-08 18:31   ` Matt Fleming
  2012-02-08 18:54     ` Olof Johansson
  0 siblings, 1 reply; 14+ messages in thread
From: Matt Fleming @ 2012-02-08 18:31 UTC (permalink / raw)
  To: Olof Johansson; +Cc: x86, hpa, mingo, tglx, linux-kernel, mjg

On Tue, 2012-02-07 at 16:25 -0800, Olof Johansson wrote: 
> Traditionally the kernel has refused to setup EFI at all if there's been
> a mismatch in 32/64-bit mode between EFI and the kernel.
> 
> On some platforms that boot natively through EFI (Chrome OS being one),
> we still need to get at least some of the static data such as memory
> configuration out of EFI. Runtime services aren't as critical, and
> it's a significant amount of work to implement switching between the
> operating modes to call between kernel and firmware for thise cases. So
> I'm ignoring it for now.

Presumably with these patches in mainline you can get rid of the hacks
that ChromeOS currently uses?

> v4:
> * Some of the earlier cleanup was accidentally reverted by this patch, fixed.
> * Reworded some messages to not have to line wrap printk strings
> 
> v3:
> * Reorganized to a series of patches to make it easier to review, and
>   do some of the cleanups I had left out before.
> 
> v2:
> * Added graceful error handling for 32-bit kernel that gets passed
>   EFI data above 4GB.
> * Removed some warnings that were missed in first version.
> 
> Signed-off-by: Olof Johansson <olof@lixom.net>
> ---
>  arch/x86/include/asm/efi.h  |    2 +-
>  arch/x86/kernel/setup.c     |   10 ++-
>  arch/x86/platform/efi/efi.c |  164 +++++++++++++++++++++++++++++++++++++------
>  include/linux/efi.h         |   45 ++++++++++++
>  4 files changed, 195 insertions(+), 26 deletions(-)
> 

[...]

> +
> +		early_iounmap(systab64, sizeof(*systab64));
> +#ifdef CONFIG_X86_32
> +		if (tmp >> 32) {
> +			pr_err("EFI data located above 4GB, disabling.\n");
> +			return -EINVAL;
> +		}
> +#endif

You should really say "disabling EFI" here.

[...]

> +#ifdef CONFIG_X86_32
> +			if (table64 >> 32) {
> +				pr_cont("\n");
> +				pr_err("Table located above 4GB, disabling.\n");
> +				early_iounmap(config_tables,
> +					      efi.systab->nr_tables * sz);
> +				return -EINVAL;
> +			}
> +#endif

Likewise here, mention that you're disabling EFI.

[...]

> @@ -576,11 +670,19 @@ void __init efi_init(void)
>  	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.\n");
> +		efi_enabled = 0;
> +		return;
> +	}
>  	efi_phys.systab = (efi_system_table_t *)boot_params.efi_info.efi_systab;
> +	efi_native = !efi_64bit;
>  #else

... and here

> 	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)) {
> @@ -609,19 +711,26 @@ void __init efi_init(void)
>  		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 && efi_runtime_init()) {
>  		efi_enabled = 0;
>  		return;
> -	}
> +	} else
> +		pr_info("No EFI runtime due to 32/64b mismatch with kernel\n");

Hmm... this isn't right. efi_runtime_init() returns 0 on success, so
you're printing this warning in the native EFI success path. Also,
please spell out "32/64-bit". 

> @@ -679,6 +788,13 @@ void __init efi_enter_virtual_mode(void)
>  
>  	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;
> +

	/*
	 * Multi-line comments should look like this.
	 */

> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 37c3007..17385ba 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -315,6 +315,16 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
>  
>  typedef struct {
>  	efi_guid_t guid;
> +	u64 table;
> +} _efi_config_table_64_t;
> +
> +typedef struct {
> +	efi_guid_t guid;
> +	u32 table;
> +} _efi_config_table_32_t;
> +
> +typedef struct {
> +	efi_guid_t guid;
>  	unsigned long table;
>  } efi_config_table_t;

There's no need for the underscore prefix on these names.
efi_config_table_64_t, etc, is fine. Normally you should try to avoid
adding new typedefs, but I think these make sense because they're an
extension of existing typedefs.

Actually, thinking about it some more, it would make more sense to just
delete efi_config_table_t and efi_system_table_t and make everyone
explicitly pick a size, not least because it will make people adding new
code think long and hard about the mixed mode case.

> @@ -329,6 +339,40 @@ typedef struct {
>  
>  typedef struct {
>  	efi_table_hdr_t hdr;
> +	u64 fw_vendor;	/* physical addr of CHAR16 vendor string */
> +	u32 fw_revision;
> +	u32 __pad1;
> +	u64 con_in_handle;
> +	u64 con_in;
> +	u64 con_out_handle;
> +	u64 con_out;
> +	u64 stderr_handle;
> +	u64 stderr;
> +	u64 runtime;
> +	u64 boottime;
> +	u32 nr_tables;
> +	u32 __pad2;
> +	u64 tables;
> +} _efi_system_table_64_t;

No underscore please.

> +typedef struct {
> +	efi_table_hdr_t hdr;
> +	u32 fw_vendor;	/* physical addr of CHAR16 vendor string */
> +	u32 fw_revision;
> +	u32 con_in_handle;
> +	u32 con_in;
> +	u32 con_out_handle;
> +	u32 con_out;
> +	u32 stderr_handle;
> +	u32 stderr;
> +	u32 runtime;
> +	u32 boottime;
> +	u32 nr_tables;
> +	u32 tables;
> +} _efi_system_table_32_t;

Same here.



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel
  2012-02-08 18:31   ` Matt Fleming
@ 2012-02-08 18:54     ` Olof Johansson
  0 siblings, 0 replies; 14+ messages in thread
From: Olof Johansson @ 2012-02-08 18:54 UTC (permalink / raw)
  To: Matt Fleming; +Cc: x86, hpa, mingo, tglx, linux-kernel, mjg

On Wed, Feb 08, 2012 at 06:31:54PM +0000, Matt Fleming wrote:
> On Tue, 2012-02-07 at 16:25 -0800, Olof Johansson wrote: 
> > Traditionally the kernel has refused to setup EFI at all if there's been
> > a mismatch in 32/64-bit mode between EFI and the kernel.
> > 
> > On some platforms that boot natively through EFI (Chrome OS being one),
> > we still need to get at least some of the static data such as memory
> > configuration out of EFI. Runtime services aren't as critical, and
> > it's a significant amount of work to implement switching between the
> > operating modes to call between kernel and firmware for thise cases. So
> > I'm ignoring it for now.
> 
> Presumably with these patches in mainline you can get rid of the hacks
> that ChromeOS currently uses?

Yes, that's my personal objective for doing it. The main benefit of that is that it
makes it significantly easier for anyone working on the Chrome OS kernel to
contribute and test upstream if we can boot an unmodified mainline kernel on
our machines.

> > +
> > +		early_iounmap(systab64, sizeof(*systab64));
> > +#ifdef CONFIG_X86_32
> > +		if (tmp >> 32) {
> > +			pr_err("EFI data located above 4GB, disabling.\n");
> > +			return -EINVAL;
> > +		}
> > +#endif
> 
> You should really say "disabling EFI" here.

Sure, I'll change that.

> > +#ifdef CONFIG_X86_32
> > +			if (table64 >> 32) {
> > +				pr_cont("\n");
> > +				pr_err("Table located above 4GB, disabling.\n");
> > +				early_iounmap(config_tables,
> > +					      efi.systab->nr_tables * sz);
> > +				return -EINVAL;
> > +			}
> > +#endif
> 
> Likewise here, mention that you're disabling EFI.

No problem.

> [...]
> 
> > @@ -576,11 +670,19 @@ void __init efi_init(void)
> >  	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.\n");
> > +		efi_enabled = 0;
> > +		return;
> > +	}
> >  	efi_phys.systab = (efi_system_table_t *)boot_params.efi_info.efi_systab;
> > +	efi_native = !efi_64bit;
> >  #else
> 
> ... and here

Yup.

> 
> > 	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)) {
> > @@ -609,19 +711,26 @@ void __init efi_init(void)
> >  		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 && efi_runtime_init()) {
> >  		efi_enabled = 0;
> >  		return;
> > -	}
> > +	} else
> > +		pr_info("No EFI runtime due to 32/64b mismatch with kernel\n");
> 
> Hmm... this isn't right. efi_runtime_init() returns 0 on success, so
> you're printing this warning in the native EFI success path. Also,
> please spell out "32/64-bit". 

Uh, yeah, that's obviously broken, I'll fix. And I'll expand the wording.

> >  
> > +	/* We don't do virtual mode, since we don't do runtime services, on
> > +	 * non-native EFI
> > +	 */
> > +
> > +	if (!efi_native)
> > +		goto out;
> > +
> 
> 	/*
> 	 * Multi-line comments should look like this.
> 	 */

D'oh, of course.

> > +} _efi_config_table_32_t;
> > +
> > +typedef struct {
> > +	efi_guid_t guid;
> >  	unsigned long table;
> >  } efi_config_table_t;
> 
> There's no need for the underscore prefix on these names.
> efi_config_table_64_t, etc, is fine. Normally you should try to avoid
> adding new typedefs, but I think these make sense because they're an
> extension of existing typedefs.

I just went with the existing convention in the file and added it as
a typedef.  The underbar is there, just as in many other cases around
the kernel, to indicate that it's an internal-only version that's not
supposed to be used unless you know what you're doing. But sure, I can
take them off if it makes you happy. ;)

> Actually, thinking about it some more, it would make more sense to just
> delete efi_config_table_t and efi_system_table_t and make everyone
> explicitly pick a size, not least because it will make people adding new
> code think long and hard about the mixed mode case.

I tried that approach and the end result was messier than this is, since
there's no way to know which data type you need until runtime. The
number of people adding code to this module is limited, I'm not so
worried about that.

So, I would prefer keeping the current implementation with the above (and
below) comments fixed.

> > +} _efi_system_table_64_t;
> 
> No underscore please.
> 
[...]
> > +} _efi_system_table_32_t;
> 
> Same here.

Sure.


-Olof

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel
  2012-01-05 21:02 ` [PATCH v4 0/5] x86: efi: cleanups and basic 32/64 bit support Olof Johansson
@ 2012-01-05 21:02   ` Olof Johansson
  0 siblings, 0 replies; 14+ messages in thread
From: Olof Johansson @ 2012-01-05 21:02 UTC (permalink / raw)
  To: Ingo Molnar, Matthew Garrett
  Cc: H. Peter Anvin, Thomas Gleixner, x86, Matt Fleming, linux-kernel,
	Olof Johansson

Traditionally the kernel has refused to setup EFI at all if there's been
a mismatch in 32/64-bit mode between EFI and the kernel.

On some platforms that boot natively through EFI (Chrome OS being one),
we still need to get at least some of the static data such as memory
configuration out of EFI. Runtime services aren't as critical, and
it's a significant amount of work to implement switching between the
operating modes to call between kernel and firmware for thise cases. So
I'm ignoring it for now.

v4:
* Some of the earlier cleanup was accidentally reverted by this patch, fixed.
* Reworded some messages to not have to line wrap printk strings

v3:
* Reorganized to a series of patches to make it easier to review, and
  do some of the cleanups I had left out before.

v2:
* Added graceful error handling for 32-bit kernel that gets passed
  EFI data above 4GB.
* Removed some warnings that were missed in first version.

Signed-off-by: Olof Johansson <olof@lixom.net>
---
 arch/x86/include/asm/efi.h  |    2 +-
 arch/x86/kernel/setup.c     |   10 ++-
 arch/x86/platform/efi/efi.c |  164 +++++++++++++++++++++++++++++++++++++------
 include/linux/efi.h         |   45 ++++++++++++
 4 files changed, 195 insertions(+), 26 deletions(-)

diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index 844f735..c9dcc18 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -95,7 +95,7 @@ extern void __iomem *efi_ioremap(unsigned long addr, unsigned long size,
 
 extern int add_efi_memmap;
 extern void efi_set_executable(efi_memory_desc_t *md, bool executable);
-extern void efi_memblock_x86_reserve_range(void);
+extern int efi_memblock_x86_reserve_range(void);
 extern void efi_call_phys_prelog(void);
 extern void efi_call_phys_epilog(void);
 
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index d7d5099..8863888 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -749,10 +749,16 @@ void __init setup_arch(char **cmdline_p)
 #endif
 #ifdef CONFIG_EFI
 	if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
-		     EFI_LOADER_SIGNATURE, 4)) {
+		     "EL32", 4)) {
 		efi_enabled = 1;
-		efi_memblock_x86_reserve_range();
+		efi_64bit = false;
+	} else if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
+		     "EL64", 4)) {
+		efi_enabled = 1;
+		efi_64bit = true;
 	}
+	if (efi_enabled && efi_memblock_x86_reserve_range())
+		efi_enabled = 0;
 #endif
 
 	x86_init.oem.arch_setup();
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 5a053e7..e41a320 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -68,6 +68,9 @@ EXPORT_SYMBOL(efi);
 
 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;
 
@@ -339,11 +342,16 @@ static void __init do_add_efi_memmap(void)
 	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 |
@@ -355,6 +363,8 @@ void __init efi_memblock_x86_reserve_range(void)
 	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
@@ -432,14 +442,75 @@ static void __init efi_free_boot_services(void)
 
 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.\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;
 
 	/*
@@ -460,24 +531,47 @@ static int __init efi_systab_init(void *phys)
 
 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.\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);
@@ -502,10 +596,10 @@ static int __init efi_config_init(u64 tables, int nr_tables)
 			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;
 }
 
@@ -569,11 +663,19 @@ void __init efi_init(void)
 	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.\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)) {
@@ -602,19 +704,26 @@ void __init efi_init(void)
 		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 && efi_runtime_init()) {
 		efi_enabled = 0;
 		return;
-	}
+	} else
+		pr_info("No EFI runtime due to 32/64b mismatch with kernel\n");
 
 	if (efi_memmap_init()) {
 		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
@@ -672,6 +781,13 @@ void __init efi_enter_virtual_mode(void)
 
 	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;
@@ -787,6 +903,8 @@ void __init efi_enter_virtual_mode(void)
 	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);
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 37c3007..17385ba 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -315,6 +315,16 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
 
 typedef struct {
 	efi_guid_t guid;
+	u64 table;
+} _efi_config_table_64_t;
+
+typedef struct {
+	efi_guid_t guid;
+	u32 table;
+} _efi_config_table_32_t;
+
+typedef struct {
+	efi_guid_t guid;
 	unsigned long table;
 } efi_config_table_t;
 
@@ -329,6 +339,40 @@ typedef struct {
 
 typedef struct {
 	efi_table_hdr_t hdr;
+	u64 fw_vendor;	/* physical addr of CHAR16 vendor string */
+	u32 fw_revision;
+	u32 __pad1;
+	u64 con_in_handle;
+	u64 con_in;
+	u64 con_out_handle;
+	u64 con_out;
+	u64 stderr_handle;
+	u64 stderr;
+	u64 runtime;
+	u64 boottime;
+	u32 nr_tables;
+	u32 __pad2;
+	u64 tables;
+} _efi_system_table_64_t;
+
+typedef struct {
+	efi_table_hdr_t hdr;
+	u32 fw_vendor;	/* physical addr of CHAR16 vendor string */
+	u32 fw_revision;
+	u32 con_in_handle;
+	u32 con_in;
+	u32 con_out_handle;
+	u32 con_out;
+	u32 stderr_handle;
+	u32 stderr;
+	u32 runtime;
+	u32 boottime;
+	u32 nr_tables;
+	u32 tables;
+} _efi_system_table_32_t;
+
+typedef struct {
+	efi_table_hdr_t hdr;
 	unsigned long fw_vendor;	/* physical addr of CHAR16 vendor string */
 	u32 fw_revision;
 	unsigned long con_in_handle;
@@ -497,6 +541,7 @@ extern int __init efi_setup_pcdp_console(char *);
 #ifdef CONFIG_EFI
 # ifdef CONFIG_X86
    extern int efi_enabled;
+   extern bool efi_64bit;
 # else
 #  define efi_enabled 1
 # endif
-- 
1.7.8.GIT


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel
  2012-01-05 18:21   ` Matthew Garrett
@ 2012-01-05 20:59     ` Olof Johansson
  0 siblings, 0 replies; 14+ messages in thread
From: Olof Johansson @ 2012-01-05 20:59 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: H. Peter Anvin, linux-kernel, Ingo Molnar, Thomas Gleixner, x86,
	Matt Fleming

On Thu, Jan 5, 2012 at 10:21 AM, Matthew Garrett <mjg59@srcf.ucam.org> wrote:
> This seems like a reasonable step, but outside some special cases it's
> not going to be terribly useful - general purpose EFI machines aren't
> going to boot unless you can set the boot variable, which requires
> runtime services. ChromeOS machines presumably have their own way of
> handling this so I've no objection to adding it if it helps them, but
> just wanted to make sure people knew that it's something that we'd still
> need to fix up if we want this to work elsewhere.

Thanks, good point. And yes, on ChromeOS there is a wrapper around the
kernel that provides the bootargs and a few other things, so we get by
with this limited EFI support there.

Given that the feature has been missing for years now, and it's a
nontrivial amount of work to get the mode switching implemented right
and debugged, I'm not inclined to go the whole way. This is a good
start, and if someone needs the rest of the functionality they can
build on top of this.


-Olof

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel
  2012-01-03 17:11 ` [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel Olof Johansson
  2012-01-05 11:21   ` Matt Fleming
@ 2012-01-05 18:21   ` Matthew Garrett
  2012-01-05 20:59     ` Olof Johansson
  1 sibling, 1 reply; 14+ messages in thread
From: Matthew Garrett @ 2012-01-05 18:21 UTC (permalink / raw)
  To: Olof Johansson
  Cc: H. Peter Anvin, linux-kernel, Ingo Molnar, Thomas Gleixner, x86,
	Matt Fleming

This seems like a reasonable step, but outside some special cases it's 
not going to be terribly useful - general purpose EFI machines aren't 
going to boot unless you can set the boot variable, which requires 
runtime services. ChromeOS machines presumably have their own way of 
handling this so I've no objection to adding it if it helps them, but 
just wanted to make sure people knew that it's something that we'd still 
need to fix up if we want this to work elsewhere.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel
  2012-01-05 11:21   ` Matt Fleming
@ 2012-01-05 17:53     ` Olof Johansson
  0 siblings, 0 replies; 14+ messages in thread
From: Olof Johansson @ 2012-01-05 17:53 UTC (permalink / raw)
  To: Matt Fleming
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86

On Thu, Jan 5, 2012 at 3:21 AM, Matt Fleming <matt.fleming@intel.com> wrote:

> This needs resubmitting as you're undoing the changes you've made in
> PATCH 2/5, i.e. stuff like this,

Yes, I completely messed up here, pretty sad. Fixed up in the series
I'll repost shortly.


-Olof

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel
  2012-01-03 17:11 ` [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel Olof Johansson
@ 2012-01-05 11:21   ` Matt Fleming
  2012-01-05 17:53     ` Olof Johansson
  2012-01-05 18:21   ` Matthew Garrett
  1 sibling, 1 reply; 14+ messages in thread
From: Matt Fleming @ 2012-01-05 11:21 UTC (permalink / raw)
  To: Olof Johansson
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86

On Tue, 2012-01-03 at 09:11 -0800, Olof Johansson wrote:
> Traditionally the kernel has refused to setup EFI at all if there's been
> a mismatch in 32/64-bit mode between EFI and the kernel.
> 
> On some platforms that boot natively through EFI (Chrome OS being one),
> we still need to get at least some of the static data such as memory
> configuration out of EFI. Runtime services aren't as critical, and
> it's a significant amount of work to implement switching between the
> operating modes to call between kernel and firmware for thise cases. So
> I'm ignoring it for now.
> 
> v3:
> * Reorganized to a series of patches to make it easier to review, and
>   do some of the cleanups I had left out before.
> 
> v2:
> * Added graceful error handling for 32-bit kernel that gets passed
>   EFI data above 4GB.
> * Removed some warnings that were missed in first version.
> 
> Signed-off-by: Olof Johansson <olof@lixom.net>
> ---
>  arch/x86/include/asm/efi.h  |    2 +-
>  arch/x86/kernel/setup.c     |   10 ++-
>  arch/x86/platform/efi/efi.c |  234 ++++++++++++++++++++++++++++++++-----------
>  include/linux/efi.h         |   45 ++++++++
>  4 files changed, 228 insertions(+), 63 deletions(-)

This needs resubmitting as you're undoing the changes you've made in
PATCH 2/5, i.e. stuff like this,

> @@ -254,7 +257,7 @@ int efi_set_rtc_mmss(unsigned long nowtime)
>  
>  	status = efi.get_time(&eft, &cap);
>  	if (status != EFI_SUCCESS) {
> -		pr_err("Oops: efitime: can't read time!\n");
> +		printk(KERN_ERR "Oops: efitime: can't read time!\n");
>  		return -1;
>  	}



^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel
  2012-01-03 17:11 [PATCH v3 0/5] x86: efi: cleanups and basic 32/64 bit support Olof Johansson
@ 2012-01-03 17:11 ` Olof Johansson
  2012-01-05 11:21   ` Matt Fleming
  2012-01-05 18:21   ` Matthew Garrett
  2012-01-05 21:02 ` [PATCH v4 0/5] x86: efi: cleanups and basic 32/64 bit support Olof Johansson
  1 sibling, 2 replies; 14+ messages in thread
From: Olof Johansson @ 2012-01-03 17:11 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett
  Cc: linux-kernel, Ingo Molnar, Thomas Gleixner, x86, Matt Fleming,
	Olof Johansson

Traditionally the kernel has refused to setup EFI at all if there's been
a mismatch in 32/64-bit mode between EFI and the kernel.

On some platforms that boot natively through EFI (Chrome OS being one),
we still need to get at least some of the static data such as memory
configuration out of EFI. Runtime services aren't as critical, and
it's a significant amount of work to implement switching between the
operating modes to call between kernel and firmware for thise cases. So
I'm ignoring it for now.

v3:
* Reorganized to a series of patches to make it easier to review, and
  do some of the cleanups I had left out before.

v2:
* Added graceful error handling for 32-bit kernel that gets passed
  EFI data above 4GB.
* Removed some warnings that were missed in first version.

Signed-off-by: Olof Johansson <olof@lixom.net>
---
 arch/x86/include/asm/efi.h  |    2 +-
 arch/x86/kernel/setup.c     |   10 ++-
 arch/x86/platform/efi/efi.c |  234 ++++++++++++++++++++++++++++++++-----------
 include/linux/efi.h         |   45 ++++++++
 4 files changed, 228 insertions(+), 63 deletions(-)

diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index 844f735..c9dcc18 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -95,7 +95,7 @@ extern void __iomem *efi_ioremap(unsigned long addr, unsigned long size,
 
 extern int add_efi_memmap;
 extern void efi_set_executable(efi_memory_desc_t *md, bool executable);
-extern void efi_memblock_x86_reserve_range(void);
+extern int efi_memblock_x86_reserve_range(void);
 extern void efi_call_phys_prelog(void);
 extern void efi_call_phys_epilog(void);
 
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index d7d5099..8863888 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -749,10 +749,16 @@ void __init setup_arch(char **cmdline_p)
 #endif
 #ifdef CONFIG_EFI
 	if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
-		     EFI_LOADER_SIGNATURE, 4)) {
+		     "EL32", 4)) {
 		efi_enabled = 1;
-		efi_memblock_x86_reserve_range();
+		efi_64bit = false;
+	} else if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
+		     "EL64", 4)) {
+		efi_enabled = 1;
+		efi_64bit = true;
 	}
+	if (efi_enabled && efi_memblock_x86_reserve_range())
+		efi_enabled = 0;
 #endif
 
 	x86_init.oem.arch_setup();
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index e5ec213..6b53e32 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -67,6 +67,9 @@ EXPORT_SYMBOL(efi);
 
 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;
 
@@ -254,7 +257,7 @@ int efi_set_rtc_mmss(unsigned long nowtime)
 
 	status = efi.get_time(&eft, &cap);
 	if (status != EFI_SUCCESS) {
-		pr_err("Oops: efitime: can't read time!\n");
+		printk(KERN_ERR "Oops: efitime: can't read time!\n");
 		return -1;
 	}
 
@@ -268,7 +271,7 @@ int efi_set_rtc_mmss(unsigned long nowtime)
 
 	status = efi.set_time(&eft);
 	if (status != EFI_SUCCESS) {
-		pr_err("Oops: efitime: can't write time!\n");
+		printk(KERN_ERR "Oops: efitime: can't write time!\n");
 		return -1;
 	}
 	return 0;
@@ -282,7 +285,7 @@ unsigned long efi_get_time(void)
 
 	status = efi.get_time(&eft, &cap);
 	if (status != EFI_SUCCESS)
-		pr_err("Oops: efitime: can't read time!\n");
+		printk(KERN_ERR "Oops: efitime: can't read time!\n");
 
 	return mktime(eft.year, eft.month, eft.day, eft.hour,
 		      eft.minute, eft.second);
@@ -338,11 +341,14 @@ static void __init do_add_efi_memmap(void)
 	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)
+		return -EINVAL;
 	pmap = boot_params.efi_info.efi_memmap;
 #else
 	pmap = (boot_params.efi_info.efi_memmap |
@@ -354,6 +360,8 @@ void __init efi_memblock_x86_reserve_range(void)
 	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
@@ -367,7 +375,7 @@ static void __init print_efi_memmap(void)
 	     p < memmap.map_end;
 	     p += memmap.desc_size, i++) {
 		md = p;
-		pr_info(PFX "mem%02u: type=%u, attr=0x%llx, "
+		printk(KERN_INFO PFX "mem%02u: type=%u, attr=0x%llx, "
 			"range=[0x%016llx-0x%016llx) (%lluMB)\n",
 			i, md->type, md->attribute, md->phys_addr,
 			md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT),
@@ -431,25 +439,84 @@ static void __init efi_free_boot_services(void)
 
 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 EFI 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) {
+			printk(KERN_ERR "Couldn't map the EFI 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) {
+			printk(KERN_ERR "EFI data located above 4GB, disabling.\n");
+			return -EINVAL;
+		}
+#endif
+	} else {
+		_efi_system_table_32_t *systab32;
+
+		systab32 = early_ioremap((unsigned long)phys,
+					 sizeof(*systab32));
+		if (systab32 == NULL) {
+			printk(KERN_ERR "Couldn't map the EFI 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;
 
 	/*
 	 * Verify the EFI Table
 	 */
-	if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
-		pr_err("EFI system table signature incorrect!\n");
-		return -EINVAL;
-	}
+	if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+		printk(KERN_ERR "EFI system table signature incorrect!\n");
 	if ((efi.systab->hdr.revision >> 16) == 0)
-		pr_err("Warning: EFI system table version "
+		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 & 0xffff);
@@ -459,52 +526,75 @@ static int __init efi_systab_init(void *phys)
 
 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 *mapped_table, *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);
-	if (config_tables == NULL) {
-		pr_err("Could not map EFI Configuration Table!\n");
+	mapped_table = early_ioremap(tables, nr_tables * sz);
+	if (mapped_table == NULL) {
+		printk(KERN_ERR "Could not map EFI Configuration Table!\n");
 		return -ENOMEM;
 	}
 
-	pr_info("");
+	tablep = mapped_table;
+	printk(KERN_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) {
+				printk(KERN_ERR "EFI table located above "
+						"4GB, disabling.\n");
+				early_iounmap(mapped_table,
+					      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);
+			printk(" MPS=0x%lx ", table);
 		} else if (!efi_guidcmp(guid, ACPI_20_TABLE_GUID)) {
 			efi.acpi20 = table;
-			pr_cont(" ACPI 2.0=0x%lx ", table);
+			printk(" ACPI 2.0=0x%lx ", table);
 		} else if (!efi_guidcmp(guid, ACPI_TABLE_GUID)) {
 			efi.acpi = table;
-			pr_cont(" ACPI=0x%lx ", table);
+			printk(" ACPI=0x%lx ", table);
 		} else if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID)) {
 			efi.smbios = table;
-			pr_cont(" SMBIOS=0x%lx ", table);
+			printk(" SMBIOS=0x%lx ", table);
 #ifdef CONFIG_X86_UV
 		} else if (!efi_guidcmp(guid, UV_SYSTEM_TABLE_GUID)) {
 			efi.uv_systab = table;
-			pr_cont(" UVsystab=0x%lx ", table);
+			printk(" UVsystab=0x%lx ", table);
 #endif
 		} else if (!efi_guidcmp(guid, HCDP_TABLE_GUID)) {
 			efi.hcdp = table;
-			pr_cont(" HCDP=0x%lx ", table);
+			printk(" HCDP=0x%lx ", table);
 		} else if (!efi_guidcmp(guid, UGA_IO_PROTOCOL_GUID)) {
 			efi.uga = table;
-			pr_cont(" UGA=0x%lx ", table);
+			printk(" UGA=0x%lx ", table);
 		}
+		tablep += sz;
 	}
-	pr_cont("\n");
-	early_iounmap(config_tables, efi.systab->nr_tables * sz);
-
+	printk("\n");
+	early_iounmap(mapped_table, efi.systab->nr_tables * sz);
 	return 0;
 }
 
@@ -512,17 +602,11 @@ static int __init efi_runtime_init(void)
 {
 	efi_runtime_services_t *runtime;
 
-	/*
-	 * 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 = early_ioremap((unsigned long)efi.systab->runtime,
 				sizeof(efi_runtime_services_t));
-
-	if (!runtime) {
-		pr_err("Could not map the EFI runtime service table!\n");
+	if (runtime == NULL) {
+		printk(KERN_ERR "Could not map the EFI runtime service "
+		       "table!\n");
 		return -ENOMEM;
 	}
 	/*
@@ -550,7 +634,7 @@ static int __init efi_memmap_init(void)
 	memmap.map = early_ioremap((unsigned long)memmap.phys_map,
 				   memmap.nr_map * memmap.desc_size);
 	if (memmap.map == NULL) {
-		pr_err("Could not map the EFI memory map!\n");
+		pr_warn("Could not map the EFI memory map!\n");
 		return -ENOMEM;
 	}
 	memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size);
@@ -573,11 +657,19 @@ void __init efi_init(void)
 	void *tmp;
 
 #ifdef CONFIG_X86_32
+	if (boot_params.efi_info.efi_systab_hi ||
+	    boot_params.efi_info.efi_memmap_hi) {
+		printk(KERN_INFO "EFI tables located above 4GB, disabling.\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)) {
@@ -594,31 +686,44 @@ void __init efi_init(void)
 			vendor[i] = *c16++;
 		vendor[i] = '\0';
 	} else
-		pr_err(PFX "Could not map the firmware vendor!\n");
+		printk(KERN_ERR PFX "Could not map the firmware vendor!\n");
 	early_iounmap(tmp, 2);
 
-	pr_info("EFI v%u.%.02u by %s\n",
-		efi.systab->hdr.revision >> 16,
-		efi.systab->hdr.revision & 0xffff, vendor);
+	printk(KERN_INFO "EFI v%u.%.02u by %s\n",
+	       efi.systab->hdr.revision >> 16,
+	       efi.systab->hdr.revision & 0xffff, vendor);
 
 	if (efi_config_init(efi.systab->tables, efi.systab->nr_tables)) {
 		efi_enabled = 0;
 		return;
 	}
 
-	if (efi_runtime_init()) {
+	/*
+	 * 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.
+	 *
+	 * Note: We currently don't support runtime services on an EFI
+	 * that doesn't match the kernel 32/64-bit mode.
+	 */
+
+	if (efi_native && efi_runtime_init()) {
 		efi_enabled = 0;
 		return;
-	}
+	} else
+		printk(KERN_INFO "Not initializing EFI runtime due to "
+				 "32/64 bit mismatch with kernel\n");
 
 	if (efi_memmap_init()) {
 		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
@@ -676,6 +781,13 @@ void __init efi_enter_virtual_mode(void)
 
 	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;
@@ -724,7 +836,7 @@ void __init efi_enter_virtual_mode(void)
 		md->virt_addr = (u64) (unsigned long) va;
 
 		if (!va) {
-			pr_err(PFX "ioremap of 0x%llX failed!\n",
+			printk(KERN_ERR PFX "ioremap of 0x%llX failed!\n",
 			       (unsigned long long)md->phys_addr);
 			continue;
 		}
@@ -758,7 +870,7 @@ void __init efi_enter_virtual_mode(void)
 		(efi_memory_desc_t *)__pa(new_memmap));
 
 	if (status != EFI_SUCCESS) {
-		pr_alert("Unable to switch EFI into virtual mode "
+		printk(KERN_ALERT "Unable to switch EFI into virtual mode "
 		       "(status=%lx)!\n", status);
 		panic("EFI call to SetVirtualAddressMap() failed!");
 	}
@@ -791,6 +903,8 @@ void __init efi_enter_virtual_mode(void)
 	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);
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 37c3007..17385ba 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -315,6 +315,16 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
 
 typedef struct {
 	efi_guid_t guid;
+	u64 table;
+} _efi_config_table_64_t;
+
+typedef struct {
+	efi_guid_t guid;
+	u32 table;
+} _efi_config_table_32_t;
+
+typedef struct {
+	efi_guid_t guid;
 	unsigned long table;
 } efi_config_table_t;
 
@@ -329,6 +339,40 @@ typedef struct {
 
 typedef struct {
 	efi_table_hdr_t hdr;
+	u64 fw_vendor;	/* physical addr of CHAR16 vendor string */
+	u32 fw_revision;
+	u32 __pad1;
+	u64 con_in_handle;
+	u64 con_in;
+	u64 con_out_handle;
+	u64 con_out;
+	u64 stderr_handle;
+	u64 stderr;
+	u64 runtime;
+	u64 boottime;
+	u32 nr_tables;
+	u32 __pad2;
+	u64 tables;
+} _efi_system_table_64_t;
+
+typedef struct {
+	efi_table_hdr_t hdr;
+	u32 fw_vendor;	/* physical addr of CHAR16 vendor string */
+	u32 fw_revision;
+	u32 con_in_handle;
+	u32 con_in;
+	u32 con_out_handle;
+	u32 con_out;
+	u32 stderr_handle;
+	u32 stderr;
+	u32 runtime;
+	u32 boottime;
+	u32 nr_tables;
+	u32 tables;
+} _efi_system_table_32_t;
+
+typedef struct {
+	efi_table_hdr_t hdr;
 	unsigned long fw_vendor;	/* physical addr of CHAR16 vendor string */
 	u32 fw_revision;
 	unsigned long con_in_handle;
@@ -497,6 +541,7 @@ extern int __init efi_setup_pcdp_console(char *);
 #ifdef CONFIG_EFI
 # ifdef CONFIG_X86
    extern int efi_enabled;
+   extern bool efi_64bit;
 # else
 #  define efi_enabled 1
 # endif
-- 
1.7.8.GIT


^ permalink raw reply related	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2012-02-08 18:54 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-08  0:25 [PATCH v4 0/5] [RESEND] x86: efi: cleanups and basic 32/64-bit support Olof Johansson
2012-02-08  0:25 ` [PATCH 1/5] x86: efi: refactor efi_init() a bit Olof Johansson
2012-02-08  0:25 ` [PATCH 2/5] x86: efi: convert printk to pr_*() Olof Johansson
2012-02-08  0:25 ` [PATCH 3/5] x86: efi: cleanup config table walking Olof Johansson
2012-02-08  0:25 ` [PATCH 4/5] x86: efi: add basic error handling Olof Johansson
2012-02-08  0:25 ` [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel Olof Johansson
2012-02-08 18:31   ` Matt Fleming
2012-02-08 18:54     ` Olof Johansson
  -- strict thread matches above, loose matches on Subject: below --
2012-01-03 17:11 [PATCH v3 0/5] x86: efi: cleanups and basic 32/64 bit support Olof Johansson
2012-01-03 17:11 ` [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel Olof Johansson
2012-01-05 11:21   ` Matt Fleming
2012-01-05 17:53     ` Olof Johansson
2012-01-05 18:21   ` Matthew Garrett
2012-01-05 20:59     ` Olof Johansson
2012-01-05 21:02 ` [PATCH v4 0/5] x86: efi: cleanups and basic 32/64 bit support Olof Johansson
2012-01-05 21:02   ` [PATCH 5/5] x86: efi: allow basic init with mixed 32/64-bit efi/kernel Olof Johansson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.