linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 00/10] x86 EFI boot stub
@ 2011-10-17 10:40 Matt Fleming
  2011-10-17 10:40 ` [PATCH 01/10] x86: Add missing bzImage fields to struct setup_header Matt Fleming
                   ` (17 more replies)
  0 siblings, 18 replies; 39+ messages in thread
From: Matt Fleming @ 2011-10-17 10:40 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett
  Cc: linux-kernel, Ingo Molnar, Thomas Gleixner, x86, Matt Fleming

From: Matt Fleming <matt.fleming@intel.com>

This series adds support for an EFI boot stub, similar to the existing
BIOS boot stub. The result is that you can boot a bzImage on an EFI
machine without the use of a boot loader by making the bzImage appear
to the EFI firmware to be an EFI application. Also, a single bzImage
can be booted on either a BIOS or EFI machine.

Using the EFI boot stub has the advantage that the kernel is
responsible for configuring the machine to the point where we can
fully boot the kernel, so we are no longer at the mercy of the boot
loader.

Changes in v5:

 - load_options_size is UTF-16, which needs dividing by 2 to convert
   to the corresponding ASCII size. Otherwise we could potentially
   read more than load_options_size because we're comparing UTF-16 and
   ASCII (reported by Maarten Lankhorst).

Changes in v4:

 - Don't read more than image->load_options_size when reading
   command-line args.

Changes in v3:

 - Fix compiler warnings when compiling CONFIG_EFI_STUB=n
 - Fix GOP search as suggested by Matthew Garrett.
 - Fix initrd memory allocation.
 - Fix various bugs pointed out by Maarten Lankhorst.

Changes in v2:

  - Added PATCH 01/10 because we now need to access hdr.pref_address
    and hdr.init_size when relocating the bzImage

  - Added relocation support to the stub because the firmware might
    load the bzImage at a very high physical address

  - Fixed the alignment of some u64 fields on x86

  - Now we dynamically write some fields in the bzImage EFI header at
    arch/x86/boot/tools/build time.

  - Added UGA support

  - Replaced some magic numbers with constants

Matt Fleming (10):
  x86: Add missing bzImage fields to struct setup_header
  x86, efi: Make efi_call_phys_prelog() CONFIG_RELOCATABLE-aware
  x86: Don't use magic strings for EFI loader signature
  efi.h: Add struct definition for boot time services
  efi.h: Add efi_image_loaded_t
  efi.h: Add allocation types for boottime->allocate_pages()
  efi.h: Add graphics protocol guids
  efi.h: Add boottime->locate_handle search types
  efi: Add EFI file I/O data types
  x86, efi: EFI boot stub support

 arch/x86/Kconfig                       |    7 +
 arch/x86/boot/compressed/Makefile      |   10 +-
 arch/x86/boot/compressed/eboot.c       |  975 ++++++++++++++++++++++++++++++++
 arch/x86/boot/compressed/efi_stub_32.S |   87 +++
 arch/x86/boot/compressed/efi_stub_64.S |    1 +
 arch/x86/boot/compressed/head_32.S     |   22 +
 arch/x86/boot/compressed/head_64.S     |   20 +
 arch/x86/boot/compressed/string.c      |    9 +
 arch/x86/boot/header.S                 |  158 +++++
 arch/x86/boot/string.c                 |   35 ++
 arch/x86/boot/tools/build.c            |   39 ++
 arch/x86/include/asm/bootparam.h       |    2 +
 arch/x86/include/asm/efi.h             |    4 +
 arch/x86/kernel/asm-offsets.c          |    2 +
 arch/x86/kernel/setup.c                |    7 +-
 arch/x86/platform/efi/efi_32.c         |   22 +-
 include/linux/efi.h                    |  136 +++++-
 17 files changed, 1519 insertions(+), 17 deletions(-)
 create mode 100644 arch/x86/boot/compressed/eboot.c
 create mode 100644 arch/x86/boot/compressed/efi_stub_32.S
 create mode 100644 arch/x86/boot/compressed/efi_stub_64.S

-- 
1.7.4.4


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

* [PATCH 01/10] x86: Add missing bzImage fields to struct setup_header
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
@ 2011-10-17 10:40 ` Matt Fleming
  2011-10-17 10:40 ` [PATCH 02/10] x86, efi: Make efi_call_phys_prelog() CONFIG_RELOCATABLE-aware Matt Fleming
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: Matt Fleming @ 2011-10-17 10:40 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett
  Cc: linux-kernel, Ingo Molnar, Thomas Gleixner, x86, Matt Fleming,
	Ingo Molnar, H. Peter Anvin

From: Matt Fleming <matt.fleming@intel.com>

commit 37ba7ab5e33c ("x86, boot: make kernel_alignment adjustable; new
bzImage fields") introduced some new fields into the bzImage header
but struct setup_header was not updated accordingly. Add the missing
'pref_address' and 'init_size' fields.

Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
---
 arch/x86/include/asm/bootparam.h |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/arch/x86/include/asm/bootparam.h b/arch/x86/include/asm/bootparam.h
index e020d88..2f90c51 100644
--- a/arch/x86/include/asm/bootparam.h
+++ b/arch/x86/include/asm/bootparam.h
@@ -64,6 +64,8 @@ struct setup_header {
 	__u32	payload_offset;
 	__u32	payload_length;
 	__u64	setup_data;
+	__u64	pref_address;
+	__u32	init_size;
 } __attribute__((packed));
 
 struct sys_desc_table {
-- 
1.7.4.4


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

* [PATCH 02/10] x86, efi: Make efi_call_phys_prelog() CONFIG_RELOCATABLE-aware
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
  2011-10-17 10:40 ` [PATCH 01/10] x86: Add missing bzImage fields to struct setup_header Matt Fleming
@ 2011-10-17 10:40 ` Matt Fleming
  2011-10-17 10:40 ` [PATCH 03/10] x86: Don't use magic strings for EFI loader signature Matt Fleming
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: Matt Fleming @ 2011-10-17 10:40 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett
  Cc: linux-kernel, Ingo Molnar, Thomas Gleixner, x86, Matt Fleming,
	H. Peter Anvin

From: Matt Fleming <matt.fleming@intel.com>

efi_call_phys_prelog() assumes that the kernel was loaded at a
physical address within the first 8MB of ram, usually
0x1000000. However, this isn't the case with a CONFIG_RELOCATABLE=y
kernel which could have been loaded anywhere in the physical address
space.

Replace the hardcoded pgd_index(0) and pgd_index(PAGE_OFFSET) with the
runtime addresses of the kernel in the physical and virtual space,
respectively.

Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
---
 arch/x86/platform/efi/efi_32.c |   22 +++++++++++++---------
 1 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/arch/x86/platform/efi/efi_32.c b/arch/x86/platform/efi/efi_32.c
index 5cab48e..1156e9a 100644
--- a/arch/x86/platform/efi/efi_32.c
+++ b/arch/x86/platform/efi/efi_32.c
@@ -44,8 +44,12 @@ void efi_call_phys_prelog(void)
 {
 	unsigned long cr4;
 	unsigned long temp;
+	unsigned long phys_addr, virt_addr;
 	struct desc_ptr gdt_descr;
 
+	virt_addr = (unsigned long)_text;
+	phys_addr = virt_addr - PAGE_OFFSET;
+
 	local_irq_save(efi_rt_eflags);
 
 	/*
@@ -57,18 +61,18 @@ void efi_call_phys_prelog(void)
 
 	if (cr4 & X86_CR4_PAE) {
 		efi_bak_pg_dir_pointer[0].pgd =
-		    swapper_pg_dir[pgd_index(0)].pgd;
-		swapper_pg_dir[0].pgd =
-		    swapper_pg_dir[pgd_index(PAGE_OFFSET)].pgd;
+		    swapper_pg_dir[pgd_index(phys_addr)].pgd;
+		swapper_pg_dir[pgd_index(phys_addr)].pgd =
+		    swapper_pg_dir[pgd_index(virt_addr)].pgd;
 	} else {
 		efi_bak_pg_dir_pointer[0].pgd =
-		    swapper_pg_dir[pgd_index(0)].pgd;
+		    swapper_pg_dir[pgd_index(phys_addr)].pgd;
 		efi_bak_pg_dir_pointer[1].pgd =
-		    swapper_pg_dir[pgd_index(0x400000)].pgd;
-		swapper_pg_dir[pgd_index(0)].pgd =
-		    swapper_pg_dir[pgd_index(PAGE_OFFSET)].pgd;
-		temp = PAGE_OFFSET + 0x400000;
-		swapper_pg_dir[pgd_index(0x400000)].pgd =
+		    swapper_pg_dir[pgd_index(phys_addr + 0x400000)].pgd;
+		swapper_pg_dir[pgd_index(phys_addr)].pgd =
+		    swapper_pg_dir[pgd_index(virt_addr)].pgd;
+		temp = virt_addr + 0x400000;
+		swapper_pg_dir[pgd_index(phys_addr + 0x400000)].pgd =
 		    swapper_pg_dir[pgd_index(temp)].pgd;
 	}
 
-- 
1.7.4.4


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

* [PATCH 03/10] x86: Don't use magic strings for EFI loader signature
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
  2011-10-17 10:40 ` [PATCH 01/10] x86: Add missing bzImage fields to struct setup_header Matt Fleming
  2011-10-17 10:40 ` [PATCH 02/10] x86, efi: Make efi_call_phys_prelog() CONFIG_RELOCATABLE-aware Matt Fleming
@ 2011-10-17 10:40 ` Matt Fleming
  2011-10-17 10:40 ` [PATCH 04/10] efi.h: Add struct definition for boot time services Matt Fleming
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: Matt Fleming @ 2011-10-17 10:40 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett
  Cc: linux-kernel, Ingo Molnar, Thomas Gleixner, x86, Matt Fleming

From: Matt Fleming <matt.fleming@linux.intel.com>

Introduce a symbol, EFI_LOADER_SIGNATURE instead of using the magic
strings, which also helps to reduce the amount of ifdeffery.

Cc: Matthew Garrett <mjg@redhat.com>
Cc: "H. Peter Anvin" <hpa@linux.intel.com>
Signed-off-by: Matt Fleming <matt.fleming@linux.intel.com>
---
 arch/x86/include/asm/efi.h |    4 ++++
 arch/x86/kernel/setup.c    |    7 +------
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index 7093e4a..844f735 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -3,6 +3,8 @@
 
 #ifdef CONFIG_X86_32
 
+#define EFI_LOADER_SIGNATURE	"EL32"
+
 extern unsigned long asmlinkage efi_call_phys(void *, ...);
 
 #define efi_call_phys0(f)		efi_call_phys(f)
@@ -37,6 +39,8 @@ extern unsigned long asmlinkage efi_call_phys(void *, ...);
 
 #else /* !CONFIG_X86_32 */
 
+#define EFI_LOADER_SIGNATURE	"EL64"
+
 extern u64 efi_call0(void *fp);
 extern u64 efi_call1(void *fp, u64 arg1);
 extern u64 efi_call2(void *fp, u64 arg1, u64 arg2);
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index afaf384..eca164b 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -750,12 +750,7 @@ void __init setup_arch(char **cmdline_p)
 #endif
 #ifdef CONFIG_EFI
 	if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
-#ifdef CONFIG_X86_32
-		     "EL32",
-#else
-		     "EL64",
-#endif
-	 4)) {
+		     EFI_LOADER_SIGNATURE, 4)) {
 		efi_enabled = 1;
 		efi_memblock_x86_reserve_range();
 	}
-- 
1.7.4.4


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

* [PATCH 04/10] efi.h: Add struct definition for boot time services
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (2 preceding siblings ...)
  2011-10-17 10:40 ` [PATCH 03/10] x86: Don't use magic strings for EFI loader signature Matt Fleming
@ 2011-10-17 10:40 ` Matt Fleming
  2011-10-17 10:40 ` [PATCH v2 05/10] efi.h: Add efi_image_loaded_t Matt Fleming
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: Matt Fleming @ 2011-10-17 10:40 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett
  Cc: linux-kernel, Ingo Molnar, Thomas Gleixner, x86, Matt Fleming

From: Matt Fleming <matt.fleming@linux.intel.com>

With the forthcoming efi stub code we're gonna need to access boot
time services so let's define a struct so we can access the functions.

Cc: Matthew Garrett <mjg@redhat.com>
Cc: "H. Peter Anvin" <hpa@linux.intel.com>
Signed-off-by: Matt Fleming <matt.fleming@linux.intel.com>
---
 include/linux/efi.h |   53 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 52 insertions(+), 1 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index 2362a0b..9547597 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -139,6 +139,57 @@ typedef struct {
 } efi_time_cap_t;
 
 /*
+ * EFI Boot Services table
+ */
+typedef struct {
+	efi_table_hdr_t hdr;
+	void *raise_tpl;
+	void *restore_tpl;
+	void *allocate_pages;
+	void *free_pages;
+	void *get_memory_map;
+	void *allocate_pool;
+	void *free_pool;
+	void *create_event;
+	void *set_timer;
+	void *wait_for_event;
+	void *signal_event;
+	void *close_event;
+	void *check_event;
+	void *install_protocol_interface;
+	void *reinstall_protocol_interface;
+	void *uninstall_protocol_interface;
+	void *handle_protocol;
+	void *__reserved;
+	void *register_protocol_notify;
+	void *locate_handle;
+	void *locate_device_path;
+	void *install_configuration_table;
+	void *load_image;
+	void *start_image;
+	void *exit;
+	void *unload_image;
+	void *exit_boot_services;
+	void *get_next_monotonic_count;
+	void *stall;
+	void *set_watchdog_timer;
+	void *connect_controller;
+	void *disconnect_controller;
+	void *open_protocol;
+	void *close_protocol;
+	void *open_protocol_information;
+	void *protocols_per_handle;
+	void *locate_handle_buffer;
+	void *locate_protocol;
+	void *install_multiple_protocol_interfaces;
+	void *uninstall_multiple_protocol_interfaces;
+	void *calculate_crc32;
+	void *copy_mem;
+	void *set_mem;
+	void *create_event_ex;
+} efi_boot_services_t;
+
+/*
  * Types and defines for EFI ResetSystem
  */
 #define EFI_RESET_COLD 0
@@ -261,7 +312,7 @@ typedef struct {
 	unsigned long stderr_handle;
 	unsigned long stderr;
 	efi_runtime_services_t *runtime;
-	unsigned long boottime;
+	efi_boot_services_t *boottime;
 	unsigned long nr_tables;
 	unsigned long tables;
 } efi_system_table_t;
-- 
1.7.4.4


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

* [PATCH v2 05/10] efi.h: Add efi_image_loaded_t
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (3 preceding siblings ...)
  2011-10-17 10:40 ` [PATCH 04/10] efi.h: Add struct definition for boot time services Matt Fleming
@ 2011-10-17 10:40 ` Matt Fleming
  2011-10-17 10:40 ` [PATCH 06/10] efi.h: Add allocation types for boottime->allocate_pages() Matt Fleming
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: Matt Fleming @ 2011-10-17 10:40 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett
  Cc: linux-kernel, Ingo Molnar, Thomas Gleixner, x86, Matt Fleming

From: Matt Fleming <matt.fleming@linux.intel.com>

Add the EFI loaded image structure and protocol guid which are
required by the x86 EFI boot stub. The EFI boot stub uses the
structure to figure out where it was loaded in memory and to pass
command line arguments to the kernel.

Cc: Matthew Garrett <mjg@redhat.com>
Cc: "H. Peter Anvin" <hpa@linux.intel.com>
Signed-off-by: Matt Fleming <matt.fleming@linux.intel.com>
---

v2: use __aligned_u64 because x86 only aligns 64-bit data types on a
   4-byte boundary unless instructed otherwise.

 include/linux/efi.h |   19 +++++++++++++++++++
 1 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index 9547597..e35005f 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -287,6 +287,9 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
 #define LINUX_EFI_CRASH_GUID \
     EFI_GUID(  0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
 
+#define LOADED_IMAGE_PROTOCOL_GUID \
+    EFI_GUID(  0x5b1b31a1, 0x9562, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
+
 typedef struct {
 	efi_guid_t guid;
 	unsigned long table;
@@ -326,6 +329,22 @@ struct efi_memory_map {
 	unsigned long desc_size;
 };
 
+typedef struct {
+	u32 revision;
+	void *parent_handle;
+	efi_system_table_t *system_table;
+	void *device_handle;
+	void *file_path;
+	void *reserved;
+	u32 load_options_size;
+	void *load_options;
+	void *image_base;
+	__aligned_u64 image_size;
+	unsigned int image_code_type;
+	unsigned int image_data_type;
+	unsigned long unload;
+} efi_loaded_image_t;
+
 #define EFI_INVALID_TABLE_ADDR		(~0UL)
 
 /*
-- 
1.7.4.4


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

* [PATCH 06/10] efi.h: Add allocation types for boottime->allocate_pages()
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (4 preceding siblings ...)
  2011-10-17 10:40 ` [PATCH v2 05/10] efi.h: Add efi_image_loaded_t Matt Fleming
@ 2011-10-17 10:40 ` Matt Fleming
  2011-10-17 10:40 ` [PATCH v2 07/10] efi.h: Add graphics protocol guids Matt Fleming
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: Matt Fleming @ 2011-10-17 10:40 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett
  Cc: linux-kernel, Ingo Molnar, Thomas Gleixner, x86, Matt Fleming

From: Matt Fleming <matt.fleming@linux.intel.com>

Add the allocation types detailed in section 6.2 - "AllocatePages()"
of the UEFI 2.3 specification. These definitions will be used by the
x86 EFI boot stub which needs to allocate memory during boot.

Cc: Matthew Garrett <mjg@redhat.com>
Cc: "H. Peter Anvin" <hpa@linux.intel.com>
Signed-off-by: Matt Fleming <matt.fleming@linux.intel.com>
---
 include/linux/efi.h |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index e35005f..378f2cd 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -109,6 +109,14 @@ typedef struct {
 	u32 imagesize;
 } efi_capsule_header_t;
 
+/*
+ * Allocation types for calls to boottime->allocate_pages.
+ */
+#define EFI_ALLOCATE_ANY_PAGES		0
+#define EFI_ALLOCATE_MAX_ADDRESS	1
+#define EFI_ALLOCATE_ADDRESS		2
+#define EFI_MAX_ALLOCATE_TYPE		3
+
 typedef int (*efi_freemem_callback_t) (u64 start, u64 end, void *arg);
 
 /*
-- 
1.7.4.4


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

* [PATCH v2 07/10] efi.h: Add graphics protocol guids
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (5 preceding siblings ...)
  2011-10-17 10:40 ` [PATCH 06/10] efi.h: Add allocation types for boottime->allocate_pages() Matt Fleming
@ 2011-10-17 10:40 ` Matt Fleming
  2011-10-17 10:40 ` [PATCH 08/10] efi.h: Add boottime->locate_handle search types Matt Fleming
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: Matt Fleming @ 2011-10-17 10:40 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett
  Cc: linux-kernel, Ingo Molnar, Thomas Gleixner, x86, Matt Fleming

From: Matt Fleming <matt.fleming@linux.intel.com>

The x86 EFI boot stub uses the Graphics Output Protocol and Universal
Graphics Adapter (UGA) protocol guids when initialising graphics
during boot.

Cc: Matthew Garrett <mjg@redhat.com>
Cc: "H. Peter Anvin" <hpa@linux.intel.com>
Signed-off-by: Matt Fleming <matt.fleming@linux.intel.com>
---

v2: Added UGA and PCI I/O guids.

 include/linux/efi.h |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index 378f2cd..e46d771 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -298,6 +298,15 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
 #define LOADED_IMAGE_PROTOCOL_GUID \
     EFI_GUID(  0x5b1b31a1, 0x9562, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
 
+#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
+    EFI_GUID(  0x9042a9de, 0x23dc, 0x4a38, 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a )
+
+#define EFI_UGA_PROTOCOL_GUID \
+    EFI_GUID(  0x982c298b, 0xf4fa, 0x41cb, 0xb8, 0x38, 0x77, 0xaa, 0x68, 0x8f, 0xb8, 0x39 )
+
+#define EFI_PCI_IO_PROTOCOL_GUID \
+    EFI_GUID(  0x4cf5b200, 0x68b8, 0x4ca5, 0x9e, 0xec, 0xb2, 0x3e, 0x3f, 0x50, 0x2, 0x9a )
+
 typedef struct {
 	efi_guid_t guid;
 	unsigned long table;
-- 
1.7.4.4


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

* [PATCH 08/10] efi.h: Add boottime->locate_handle search types
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (6 preceding siblings ...)
  2011-10-17 10:40 ` [PATCH v2 07/10] efi.h: Add graphics protocol guids Matt Fleming
@ 2011-10-17 10:40 ` Matt Fleming
  2011-10-17 10:40 ` [PATCH v2 09/10] efi: Add EFI file I/O data types Matt Fleming
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: Matt Fleming @ 2011-10-17 10:40 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett
  Cc: linux-kernel, Ingo Molnar, Thomas Gleixner, x86, Matt Fleming

From: Matt Fleming <matt.fleming@linux.intel.com>

The x86 EFI boot stub needs to locate handles for various protocols.

Cc: Matthew Garrett <mjg@redhat.com>
Cc: "H. Peter Anvin" <hpa@linux.intel.com>
Signed-off-by: Matt Fleming <matt.fleming@linux.intel.com>
---
 include/linux/efi.h |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index e46d771..d407c88 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -472,6 +472,13 @@ extern int __init efi_setup_pcdp_console(char *);
 #define EFI_VARIABLE_RUNTIME_ACCESS     0x0000000000000004
 
 /*
+ * The type of search to perform when calling boottime->locate_handle
+ */
+#define EFI_LOCATE_ALL_HANDLES			0
+#define EFI_LOCATE_BY_REGISTER_NOTIFY		1
+#define EFI_LOCATE_BY_PROTOCOL			2
+
+/*
  * EFI Device Path information
  */
 #define EFI_DEV_HW			0x01
-- 
1.7.4.4


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

* [PATCH v2 09/10] efi: Add EFI file I/O data types
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (7 preceding siblings ...)
  2011-10-17 10:40 ` [PATCH 08/10] efi.h: Add boottime->locate_handle search types Matt Fleming
@ 2011-10-17 10:40 ` Matt Fleming
  2011-10-17 10:40 ` [PATCH v5 10/10] x86, efi: EFI boot stub support Matt Fleming
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: Matt Fleming @ 2011-10-17 10:40 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett
  Cc: linux-kernel, Ingo Molnar, Thomas Gleixner, x86, Matt Fleming

From: Matt Fleming <matt.fleming@intel.com>

The x86 EFI stub needs to access files, for example when loading
initrd's. Add the required data types.

Cc: Matthew Garrett <mjg@redhat.com>
Cc: "H. Peter Anvin" <hpa@linux.intel.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
---

v2: use __aligned_u64 because x86 only aligns 64-bit data types on a
   4-byte boundary unless instructed otherwise.

 include/linux/efi.h |   40 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 40 insertions(+), 0 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index d407c88..37c3007 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -307,6 +307,12 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
 #define EFI_PCI_IO_PROTOCOL_GUID \
     EFI_GUID(  0x4cf5b200, 0x68b8, 0x4ca5, 0x9e, 0xec, 0xb2, 0x3e, 0x3f, 0x50, 0x2, 0x9a )
 
+#define EFI_FILE_INFO_ID \
+    EFI_GUID(  0x9576e92, 0x6d3f, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
+
+#define EFI_FILE_SYSTEM_GUID \
+    EFI_GUID(  0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
+
 typedef struct {
 	efi_guid_t guid;
 	unsigned long table;
@@ -362,6 +368,40 @@ typedef struct {
 	unsigned long unload;
 } efi_loaded_image_t;
 
+typedef struct {
+	u64 revision;
+	void *open_volume;
+} efi_file_io_interface_t;
+
+typedef struct {
+	u64 size;
+	u64 file_size;
+	u64 phys_size;
+	efi_time_t create_time;
+	efi_time_t last_access_time;
+	efi_time_t modification_time;
+	__aligned_u64 attribute;
+	efi_char16_t filename[1];
+} efi_file_info_t;
+
+typedef struct {
+	u64 revision;
+	void *open;
+	void *close;
+	void *delete;
+	void *read;
+	void *write;
+	void *get_position;
+	void *set_position;
+	void *get_info;
+	void *set_info;
+	void *flush;
+} efi_file_handle_t;
+
+#define EFI_FILE_MODE_READ	0x0000000000000001
+#define EFI_FILE_MODE_WRITE	0x0000000000000002
+#define EFI_FILE_MODE_CREATE	0x8000000000000000
+
 #define EFI_INVALID_TABLE_ADDR		(~0UL)
 
 /*
-- 
1.7.4.4


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

* [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (8 preceding siblings ...)
  2011-10-17 10:40 ` [PATCH v2 09/10] efi: Add EFI file I/O data types Matt Fleming
@ 2011-10-17 10:40 ` Matt Fleming
  2011-10-17 16:39   ` Maarten Lankhorst
  2011-10-17 20:06   ` Maarten Lankhorst
  2011-12-10  2:37 ` [tip:x86/efi] x86: Add missing bzImage fields to struct setup_header tip-bot for Matt Fleming
                   ` (7 subsequent siblings)
  17 siblings, 2 replies; 39+ messages in thread
From: Matt Fleming @ 2011-10-17 10:40 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett
  Cc: linux-kernel, Ingo Molnar, Thomas Gleixner, x86, Matt Fleming,
	Mike Waychison, Andi Kleen, Maarten Lankhorst

From: Matt Fleming <matt.fleming@intel.com>

There is currently a large divide between kernel development and the
development of EFI boot loaders. The idea behind this patch is to give
the kernel developers full control over the EFI boot process. As
H. Peter Anvin put it,

"The 'kernel carries its own stub' approach been very successful in
dealing with BIOS, and would make a lot of sense to me for EFI as
well."

This patch introduces an EFI boot stub that allows an x86 bzImage to
be loaded and executed by EFI firmware. The bzImage appears to the
firmware as an EFI application. Luckily there are enough free bits
within the bzImage header so that it can masquerade as an EFI
application, thereby coercing the EFI firmware into loading it and
jumping to its entry point. The beauty of this masquerading approach
is that both BIOS and EFI boot loaders can still load and run the same
bzImage, thereby allowing a single kernel image to work in any boot
environment.

The EFI boot stub supports multiple initrds, but they must exist on
the same partition as the bzImage. Command-line arguments for the
kernel can be appended after the bzImage name when run from the EFI
shell, e.g.

Shell> bzImage console=ttyS0 root=/dev/sdb initrd=initrd.img

Cc: H. Peter Anvin <hpa@linux.intel.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Mike Waychison <mikew@google.com>
Cc: Matthew Garrett <mjg@redhat.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Tested-by: Henrik Rydberg <rydberg@euromail.se>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
---

v5:

 - load_options_size is UTF-16, which needs dividing by 2 to convert
   to the corresponding ASCII size (spotted by Maarten).

v4:

 - Don't read more than image->load_options_size

v3:

 - Fix following warnings when compiling CONFIG_EFI_STUB=n

   arch/x86/boot/tools/build.c: In function ‘main’:
   arch/x86/boot/tools/build.c:138:24: warning: unused variable ‘pe_header’
   arch/x86/boot/tools/build.c:138:15: warning: unused variable ‘file_sz’

 - As reported by Matthew Garrett, some Apple machines have GOPs that
   don't have hardware attached. We need to weed these out by
   searching for ones that handle the PCIIO protocol.

 - Don't allocate memory if no initrds are on cmdline
 - Don't trust image->load_options_size

Maarten Lankhorst noted:
 - Don't strip first argument when booted from efibootmgr
 - Don't allocate too much memory for cmdline
 - Don't update cmdline_size, the kernel considers it read-only
 - Don't accept '\n' for initrd names

v2:

 - File alignment was too large, was 8192 should be 512. Reported by
   Maarten Lankhorst on LKML.
 - Added UGA support for graphics
 - Use VIDEO_TYPE_EFI instead of hard-coded number.
 - Move linelength assignment until after we've assigned depth
 - Dynamically fill out AddressOfEntryPoint in tools/build.c
 - Don't use magic number for GDT/TSS stuff. Requested by Andi Kleen
 - The bzImage may need to be relocated as it may have been loaded at
   a high address address by the firmware. This was required to get my
   macbook booting because the firmware loaded it at 0x7cxxxxxx, which
   triggers this error in decompress_kernel(),

	if (heap > ((-__PAGE_OFFSET-(128<<20)-1) & 0x7fffffff))
		error("Destination address too large");

 arch/x86/Kconfig                       |    7 +
 arch/x86/boot/compressed/Makefile      |   10 +-
 arch/x86/boot/compressed/eboot.c       |  975 ++++++++++++++++++++++++++++++++
 arch/x86/boot/compressed/efi_stub_32.S |   87 +++
 arch/x86/boot/compressed/efi_stub_64.S |    1 +
 arch/x86/boot/compressed/head_32.S     |   22 +
 arch/x86/boot/compressed/head_64.S     |   20 +
 arch/x86/boot/compressed/string.c      |    9 +
 arch/x86/boot/header.S                 |  158 +++++
 arch/x86/boot/string.c                 |   35 ++
 arch/x86/boot/tools/build.c            |   39 ++
 arch/x86/kernel/asm-offsets.c          |    2 +
 12 files changed, 1364 insertions(+), 1 deletions(-)
 create mode 100644 arch/x86/boot/compressed/eboot.c
 create mode 100644 arch/x86/boot/compressed/efi_stub_32.S
 create mode 100644 arch/x86/boot/compressed/efi_stub_64.S

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 6a47bb2..d40c876 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1466,6 +1466,13 @@ config EFI
 	  resultant kernel should continue to boot on existing non-EFI
 	  platforms.
 
+config EFI_STUB
+       bool "EFI stub support"
+       depends on EFI
+       ---help---
+          This kernel feature allows a bzImage to be loaded directly
+	  by EFI firmware without the use of a bootloader.
+
 config SECCOMP
 	def_bool y
 	prompt "Enable seccomp to safely compute untrusted bytecode"
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 09664ef..b123b9a 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -23,7 +23,15 @@ LDFLAGS_vmlinux := -T
 
 hostprogs-y	:= mkpiggy
 
-$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o $(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o $(obj)/piggy.o FORCE
+VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \
+	$(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \
+	$(obj)/piggy.o
+
+ifeq ($(CONFIG_EFI_STUB), y)
+	VMLINUX_OBJS += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o
+endif
+
+$(obj)/vmlinux: $(VMLINUX_OBJS) FORCE
 	$(call if_changed,ld)
 	@:
 
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
new file mode 100644
index 0000000..f84e389
--- /dev/null
+++ b/arch/x86/boot/compressed/eboot.c
@@ -0,0 +1,975 @@
+/* -----------------------------------------------------------------------
+ *
+ *   Copyright 2011 Intel Corporation; author Matt Fleming
+ *
+ *   This file is part of the Linux kernel, and is made available under
+ *   the terms of the GNU General Public License version 2.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <linux/efi.h>
+#include <asm/efi.h>
+#include <asm/setup.h>
+#include <asm/desc.h>
+
+#define SEG_TYPE_DATA		(0 << 3)
+#define SEG_TYPE_READ_WRITE	(1 << 1)
+#define SEG_TYPE_CODE		(1 << 3)
+#define SEG_TYPE_EXEC_READ	(1 << 1)
+#define SEG_TYPE_TSS		((1 << 3) | (1 << 0))
+#define SEG_OP_SIZE_32BIT	(1 << 0)
+#define SEG_GRANULARITY_4KB	(1 << 0)
+
+#define DESC_TYPE_CODE_DATA	(1 << 0)
+
+#define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
+
+#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR		0
+#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR		1
+#define PIXEL_BIT_MASK					2
+#define PIXEL_BLT_ONLY					3
+#define PIXEL_FORMAT_MAX				4
+
+typedef struct {
+	u32 red_mask;
+	u32 green_mask;
+	u32 blue_mask;
+	u32 reserved_mask;
+} efi_pixel_bitmask_t;
+
+typedef struct {
+	u32 version;
+	u32 horizontal_resolution;
+	u32 vertical_resolution;
+	int pixel_format;
+	efi_pixel_bitmask_t pixel_information;
+	u32 pixels_per_scan_line;
+} __attribute__((packed)) efi_graphics_output_mode_information_t;
+
+typedef struct {
+	u32 max_mode;
+	u32 mode;
+	unsigned long info;
+	unsigned long size_of_info;
+	u64 frame_buffer_base;
+	unsigned long frame_buffer_size;
+} __attribute__((packed)) efi_graphics_output_protocol_mode_t;
+
+typedef struct {
+	void *query_mode;
+	unsigned long set_mode;
+	unsigned long blt;
+	efi_graphics_output_protocol_mode_t *mode;
+} efi_graphics_output_protocol_t;
+
+typedef struct {
+	void *get_mode;
+	void *set_mode;
+	void *blt;
+} efi_uga_draw_protocol_t;
+
+static efi_system_table_t *sys_table;
+
+static efi_status_t low_alloc(unsigned long size, unsigned long align,
+			      unsigned long *addr)
+{
+	unsigned long map_size, key, desc_size;
+	efi_memory_desc_t *map;
+	efi_status_t status;
+	unsigned long nr_pages;
+	u32 desc_version;
+	int i;
+
+	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+
+	map_size = sizeof(*map) * 32;
+
+again:
+	/*
+	 * Add an additional efi_memory_desc_t because we're doing an
+	 * allocation which may be in a new descriptor region.
+	 */
+	map_size += sizeof(*map);
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, map_size, (void **)&map);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	status = efi_call_phys5(sys_table->boottime->get_memory_map, &map_size,
+				map, &key, &desc_size, &desc_version);
+	if (status == EFI_BUFFER_TOO_SMALL) {
+		efi_call_phys1(sys_table->boottime->free_pool, map);
+		goto again;
+	}
+
+	if (status != EFI_SUCCESS)
+		goto free_pool;
+
+	for (i = 0; i < map_size / desc_size; i++) {
+		efi_memory_desc_t *desc;
+		u64 start, end;
+
+		desc = (efi_memory_desc_t *)((unsigned long)map + (i * desc_size));
+
+		if (desc->type != EFI_CONVENTIONAL_MEMORY)
+			continue;
+
+		if (desc->num_pages < nr_pages)
+			continue;
+
+		start = desc->phys_addr;
+		end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
+
+		/*
+		 * Don't allocate at 0x0. It will confuse code that
+		 * checks pointers against NULL. Skip the first 8
+		 * bytes so we start at a nice even number.
+		 */
+		if (start == 0x0) {
+			start += 8;
+
+			/* Check for tiny memory regions */
+			if (start >= end)
+				continue;
+		}
+
+		start = round_up(start, align);
+		if ((start + size) > end)
+			continue;
+
+		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+					nr_pages, &start);
+		if (status == EFI_SUCCESS) {
+			*addr = start;
+			break;
+		}
+	}
+
+	if (i == map_size / desc_size)
+		status = EFI_NOT_FOUND;
+
+free_pool:
+	efi_call_phys1(sys_table->boottime->free_pool, map);
+fail:
+	return status;
+}
+
+static void low_free(unsigned long size, unsigned long addr)
+{
+	unsigned long nr_pages;
+
+	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+	efi_call_phys2(sys_table->boottime->free_pages, addr, size);
+}
+
+static void find_bits(unsigned long mask, u8 *pos, u8 *size)
+{
+        u8 first, len;
+
+	first = 0;
+	len = 0;
+
+	if (mask) {
+		while (!(mask & 0x1)) {
+			mask = mask >> 1;
+			first++;
+		}
+
+		while (mask & 0x1) {
+			mask = mask >> 1;
+			len++;
+		}
+	}
+
+        *pos = first;
+        *size = len;
+}
+
+/*
+ * See if we have Graphics Output Protocol
+ */
+static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
+			      unsigned long size)
+{
+	efi_graphics_output_protocol_t *gop, *first_gop;
+	efi_pixel_bitmask_t pixel_info;
+	unsigned long nr_gops;
+	efi_status_t status;
+	void **gop_handle;
+	u16 width, height;
+	u32 fb_base, fb_size;
+	u32 pixels_per_scan_line;
+	int pixel_format;
+	int i;
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, size, &gop_handle);
+	if (status != EFI_SUCCESS)
+		return status;
+
+	status = efi_call_phys5(sys_table->boottime->locate_handle,
+				EFI_LOCATE_BY_PROTOCOL, proto,
+				NULL, &size, gop_handle);
+	if (status != EFI_SUCCESS)
+		goto free_handle;
+
+	first_gop = NULL;
+
+	nr_gops = size / sizeof(void *);
+	for (i = 0; i < nr_gops; i++) {
+		efi_graphics_output_mode_information_t *info;
+		efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID;
+		void *pciio;
+		void *h = gop_handle[i];
+
+		status = efi_call_phys3(sys_table->boottime->handle_protocol,
+					h, proto, &gop);
+		if (status != EFI_SUCCESS)
+			continue;
+
+		efi_call_phys3(sys_table->boottime->handle_protocol,
+			       h, &pciio_proto, &pciio);
+
+		status = efi_call_phys4(gop->query_mode, gop,
+					gop->mode->mode, &size, &info);
+		if (status == EFI_SUCCESS && (!first_gop || pciio)) {
+			/*
+			 * Apple provide GOPs that are not backed by
+			 * real hardware (they're used to handle
+			 * multiple displays). The workaround is to
+			 * search for a GOP implementing the PCIIO
+			 * protocol, and if one isn't found, to just
+			 * fallback to the first GOP.
+			 */
+			width = info->horizontal_resolution;
+			height = info->vertical_resolution;
+			fb_base = gop->mode->frame_buffer_base;
+			fb_size = gop->mode->frame_buffer_size;
+			pixel_format = info->pixel_format;
+			pixel_info = info->pixel_information;
+			pixels_per_scan_line = info->pixels_per_scan_line;
+
+			/*
+			 * Once we've found a GOP supporting PCIIO,
+			 * don't bother looking any further.
+			 */
+			if (pciio)
+				break;
+
+			first_gop = gop;
+		}
+	}
+
+	/* Did we find any GOPs? */
+	if (!first_gop)
+		goto free_handle;
+
+	/* EFI framebuffer */
+	si->orig_video_isVGA = VIDEO_TYPE_EFI;
+
+	si->lfb_width = width;
+	si->lfb_height = height;
+	si->lfb_base = fb_base;
+	si->lfb_size = fb_size;
+	si->pages = 1;
+
+	if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
+		si->lfb_depth = 32;
+		si->lfb_linelength = pixels_per_scan_line * 4;
+		si->red_size = 8;
+		si->red_pos = 0;
+		si->green_size = 8;
+		si->green_pos = 8;
+		si->blue_size = 8;
+		si->blue_pos = 16;
+		si->rsvd_size = 8;
+		si->rsvd_pos = 24;
+	} else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
+		si->lfb_depth = 32;
+		si->lfb_linelength = pixels_per_scan_line * 4;
+		si->red_size = 8;
+		si->red_pos = 16;
+		si->green_size = 8;
+		si->green_pos = 8;
+		si->blue_size = 8;
+		si->blue_pos = 0;
+		si->rsvd_size = 8;
+		si->rsvd_pos = 24;
+	} else if (pixel_format == PIXEL_BIT_MASK) {
+		find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
+		find_bits(pixel_info.green_mask, &si->green_pos,
+			  &si->green_size);
+		find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
+		find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
+			  &si->rsvd_size);
+		si->lfb_depth = si->red_size + si->green_size +
+			si->blue_size + si->rsvd_size;
+		si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
+	} else {
+		si->lfb_depth = 4;
+		si->lfb_linelength = si->lfb_width / 2;
+		si->red_size = 0;
+		si->red_pos = 0;
+		si->green_size = 0;
+		si->green_pos = 0;
+		si->blue_size = 0;
+		si->blue_pos = 0;
+		si->rsvd_size = 0;
+		si->rsvd_pos = 0;
+	}
+
+free_handle:
+	efi_call_phys1(sys_table->boottime->free_pool, gop_handle);
+	return status;
+}
+
+/*
+ * See if we have Universal Graphics Adapter (UGA) protocol
+ */
+static efi_status_t setup_uga(struct screen_info *si, efi_guid_t *uga_proto,
+			      unsigned long size)
+{
+	efi_uga_draw_protocol_t *uga, *first_uga;
+	unsigned long nr_ugas;
+	efi_status_t status;
+	u32 width, height;
+	void **uga_handle = NULL;
+	int i;
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, size, &uga_handle);
+	if (status != EFI_SUCCESS)
+		return status;
+
+	status = efi_call_phys5(sys_table->boottime->locate_handle,
+				EFI_LOCATE_BY_PROTOCOL, uga_proto,
+				NULL, &size, uga_handle);
+	if (status != EFI_SUCCESS)
+		goto free_handle;
+
+	first_uga = NULL;
+
+	nr_ugas = size / sizeof(void *);
+	for (i = 0; i < nr_ugas; i++) {
+		efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID;
+		void *handle = uga_handle[i];
+		u32 w, h, depth, refresh;
+		void *pciio;
+
+		status = efi_call_phys3(sys_table->boottime->handle_protocol,
+					handle, uga_proto, &uga);
+		if (status != EFI_SUCCESS)
+			continue;
+
+		efi_call_phys3(sys_table->boottime->handle_protocol,
+			       handle, &pciio_proto, &pciio);
+
+		status = efi_call_phys5(uga->get_mode, uga, &w, &h,
+					&depth, &refresh);
+		if (status == EFI_SUCCESS && (!first_uga || pciio)) {
+			width = w;
+			height = h;
+
+			/*
+			 * Once we've found a UGA supporting PCIIO,
+			 * don't bother looking any further.
+			 */
+			if (pciio)
+				break;
+
+			first_uga = uga;
+		}
+	}
+
+	if (!first_uga)
+		goto free_handle;
+
+	/* EFI framebuffer */
+	si->orig_video_isVGA = VIDEO_TYPE_EFI;
+
+	si->lfb_depth = 32;
+	si->lfb_width = width;
+	si->lfb_height = height;
+
+	si->red_size = 8;
+	si->red_pos = 16;
+	si->green_size = 8;
+	si->green_pos = 8;
+	si->blue_size = 8;
+	si->blue_pos = 0;
+	si->rsvd_size = 8;
+	si->rsvd_pos = 24;
+
+
+free_handle:
+	efi_call_phys1(sys_table->boottime->free_pool, uga_handle);
+	return status;
+}
+
+void setup_graphics(struct boot_params *boot_params)
+{
+	efi_guid_t graphics_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+	struct screen_info *si;
+	efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID;
+	efi_status_t status;
+	unsigned long size;
+	void **gop_handle = NULL;
+	void **uga_handle = NULL;
+
+	si = &boot_params->screen_info;
+	memset(si, 0, sizeof(*si));
+
+	size = 0;
+	status = efi_call_phys5(sys_table->boottime->locate_handle,
+				EFI_LOCATE_BY_PROTOCOL, &graphics_proto,
+				NULL, &size, gop_handle);
+	if (status == EFI_BUFFER_TOO_SMALL)
+		status = setup_gop(si, &graphics_proto, size);
+
+	if (status != EFI_SUCCESS) {
+		size = 0;
+		status = efi_call_phys5(sys_table->boottime->locate_handle,
+					EFI_LOCATE_BY_PROTOCOL, &uga_proto,
+					NULL, &size, uga_handle);
+		if (status == EFI_BUFFER_TOO_SMALL)
+			setup_uga(si, &uga_proto, size);
+	}
+}
+
+struct initrd {
+	efi_file_handle_t *handle;
+	u64 size;
+};
+
+/*
+ * Check the cmdline for a LILO-style initrd= arguments.
+ *
+ * We only support loading an initrd from the same filesystem as the
+ * kernel image.
+ */
+static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
+				    struct setup_header *hdr)
+{
+	struct initrd *initrds;
+	unsigned long initrd_addr;
+	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
+	u64 initrd_total;
+	efi_file_io_interface_t *io;
+	efi_file_handle_t *fh;
+	efi_status_t status;
+	int nr_initrds;
+	char *str;
+	int i, j, k;
+
+	initrd_addr = 0;
+	initrd_total = 0;
+
+	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+
+	j = 0;			/* See close_handles */
+
+	if (!str || !*str)
+		return EFI_SUCCESS;
+
+	for (nr_initrds = 0; *str; nr_initrds++) {
+		str = strstr(str, "initrd=");
+		if (!str)
+			break;
+
+		str += 7;
+
+		/* Skip any leading slashes */
+		while (*str == '/' || *str == '\\')
+			str++;
+
+		while (*str && *str != ' ' && *str != '\n')
+			str++;
+	}
+
+	if (!nr_initrds)
+		return EFI_SUCCESS;
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA,
+				nr_initrds * sizeof(*initrds),
+				&initrds);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+	for (i = 0; i < nr_initrds; i++) {
+		struct initrd *initrd;
+		efi_file_handle_t *h;
+		efi_file_info_t *info;
+		efi_char16_t filename[256];
+		unsigned long info_sz;
+		efi_guid_t info_guid = EFI_FILE_INFO_ID;
+		efi_char16_t *p;
+		u64 file_sz;
+
+		str = strstr(str, "initrd=");
+		if (!str)
+			break;
+
+		str += 7;
+
+		initrd = &initrds[i];
+		p = filename;
+
+		/* Skip any leading slashes */
+		while (*str == '/' || *str == '\\')
+			str++;
+
+		while (*str && *str != ' ' && *str != '\n') {
+			if (p >= filename + sizeof(filename))
+				break;
+
+			*p++ = *str++;
+		}
+
+		*p = '\0';
+
+		/* Only open the volume once. */
+		if (!i) {
+			efi_boot_services_t *boottime;
+
+			boottime = sys_table->boottime;
+
+			status = efi_call_phys3(boottime->handle_protocol,
+					image->device_handle, &fs_proto, &io);
+			if (status != EFI_SUCCESS)
+				goto free_initrds;
+
+			status = efi_call_phys2(io->open_volume, io, &fh);
+			if (status != EFI_SUCCESS)
+				goto free_initrds;
+		}
+
+		status = efi_call_phys5(fh->open, fh, &h, filename,
+					EFI_FILE_MODE_READ, (u64)0);
+		if (status != EFI_SUCCESS)
+			goto close_handles;
+
+		initrd->handle = h;
+
+		info_sz = 0;
+		status = efi_call_phys4(h->get_info, h, &info_guid,
+					&info_sz, NULL);
+		if (status != EFI_BUFFER_TOO_SMALL)
+			goto close_handles;
+
+grow:
+		status = efi_call_phys3(sys_table->boottime->allocate_pool,
+					EFI_LOADER_DATA, info_sz, &info);
+		if (status != EFI_SUCCESS)
+			goto close_handles;
+
+		status = efi_call_phys4(h->get_info, h, &info_guid,
+					&info_sz, info);
+		if (status == EFI_BUFFER_TOO_SMALL) {
+			efi_call_phys1(sys_table->boottime->free_pool, info);
+			goto grow;
+		}
+
+		file_sz = info->file_size;
+		efi_call_phys1(sys_table->boottime->free_pool, info);
+
+		if (status != EFI_SUCCESS)
+			goto close_handles;
+
+		initrd->size = file_sz;
+		initrd_total += file_sz;
+	}
+
+	if (initrd_total) {
+		unsigned long addr;
+
+		/*
+		 * Multiple initrd's need to be at consecutive
+		 * addresses in memory, so allocate enough memory for
+		 * all the initrd's.
+		 */
+		status = low_alloc(initrd_total, 0x1000, &initrd_addr);
+		if (status != EFI_SUCCESS)
+			goto close_handles;
+
+		/* We've run out of free low memory. */
+		if (initrd_addr > hdr->initrd_addr_max) {
+			status = EFI_INVALID_PARAMETER;
+			goto free_initrd_total;
+		}
+
+		addr = initrd_addr;
+		for (j = 0; j < nr_initrds; j++) {
+			u64 size;
+
+			size = initrds[j].size;
+			status = efi_call_phys3(fh->read, initrds[j].handle,
+						&size, addr);
+			if (status != EFI_SUCCESS)
+				goto free_initrd_total;
+
+			efi_call_phys1(fh->close, initrds[j].handle);
+
+			addr += size;
+		}
+
+	}
+
+	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+
+	hdr->ramdisk_image = initrd_addr;
+	hdr->ramdisk_size = initrd_total;
+
+	return status;
+
+free_initrd_total:
+	low_free(initrd_total, initrd_addr);
+
+close_handles:
+	for (k = j; k < nr_initrds; k++)
+		efi_call_phys1(fh->close, initrds[k].handle);
+free_initrds:
+	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+fail:
+	hdr->ramdisk_image = 0;
+	hdr->ramdisk_size = 0;
+
+	return status;
+}
+
+/*
+ * Because the x86 boot code expects to be passed a boot_params we
+ * need to create one ourselves (usually the bootloader would create
+ * one for us).
+ */
+static efi_status_t make_boot_params(struct boot_params *boot_params,
+				     efi_loaded_image_t *image,
+				     void *handle)
+{
+	struct efi_info *efi = &boot_params->efi_info;
+	struct apm_bios_info *bi = &boot_params->apm_bios_info;
+	struct sys_desc_table *sdt = &boot_params->sys_desc_table;
+	struct e820entry *e820_map = &boot_params->e820_map[0];
+	struct e820entry *prev = NULL;
+	struct setup_header *hdr = &boot_params->hdr;
+	unsigned long size, key, desc_size, _size;
+	efi_memory_desc_t *mem_map;
+	void *options = image->load_options;
+	u32 load_options_size = image->load_options_size / 2; /* ASCII */
+	int options_size = 0;
+	efi_status_t status;
+	__u32 desc_version;
+	unsigned long cmdline;
+	u8 nr_entries;
+	u16 *s2;
+	u8 *s1;
+	int i;
+
+	hdr->type_of_loader = 0x21;
+
+	/* Convert unicode cmdline to ascii */
+	cmdline = 0;
+	s2 = (u16 *)options;
+
+	if (s2) {
+		while (*s2 && *s2 != '\n' && options_size < load_options_size) {
+			s2++;
+			options_size++;
+		}
+
+		if (options_size) {
+			if (options_size > hdr->cmdline_size)
+				options_size = hdr->cmdline_size;
+
+			options_size++;	/* NUL termination */
+
+			status = low_alloc(options_size, 1, &cmdline);
+			if (status != EFI_SUCCESS)
+				goto fail;
+
+			s1 = (u8 *)(unsigned long)cmdline;
+			s2 = (u16 *)options;
+
+			for (i = 0; i < options_size - 1; i++)
+				*s1++ = *s2++;
+
+			*s1 = '\0';
+		}
+	}
+
+	hdr->cmd_line_ptr = cmdline;
+
+	hdr->ramdisk_image = 0;
+	hdr->ramdisk_size = 0;
+
+	status = handle_ramdisks(image, hdr);
+	if (status != EFI_SUCCESS)
+		goto free_cmdline;
+
+	setup_graphics(boot_params);
+
+	/* Clear APM BIOS info */
+	memset(bi, 0, sizeof(*bi));
+
+	memset(sdt, 0, sizeof(*sdt));
+
+	memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
+
+	size = sizeof(*mem_map) * 32;
+
+again:
+	size += sizeof(*mem_map);
+	_size = size;
+	status = low_alloc(size, 1, (unsigned long *)&mem_map);
+	if (status != EFI_SUCCESS)
+		goto free_cmdline;
+
+	status = efi_call_phys5(sys_table->boottime->get_memory_map, &size,
+				mem_map, &key, &desc_size, &desc_version);
+	if (status == EFI_BUFFER_TOO_SMALL) {
+		low_free(_size, (unsigned long)mem_map);
+		goto again;
+	}
+
+	if (status != EFI_SUCCESS)
+		goto free_mem_map;
+
+	efi->efi_systab = (unsigned long)sys_table;
+	efi->efi_memdesc_size = desc_size;
+	efi->efi_memdesc_version = desc_version;
+	efi->efi_memmap = (unsigned long)mem_map;
+	efi->efi_memmap_size = size;
+
+#ifdef CONFIG_X86_64
+	efi->efi_systab_hi = (unsigned long)sys_table >> 32;
+	efi->efi_memmap_hi = (unsigned long)mem_map >> 32;
+#endif
+
+	/* Might as well exit boot services now */
+	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
+				handle, key);
+	if (status != EFI_SUCCESS)
+		goto free_mem_map;
+
+	/* Historic? */
+	boot_params->alt_mem_k = 32 * 1024;
+
+	/*
+	 * Convert the EFI memory map to E820.
+	 */
+	nr_entries = 0;
+	for (i = 0; i < size / desc_size; i++) {
+		efi_memory_desc_t *d;
+		unsigned int e820_type = 0;
+
+		d = (efi_memory_desc_t *)((unsigned long)mem_map + (i * desc_size));
+		switch(d->type) {
+		case EFI_RESERVED_TYPE:
+		case EFI_RUNTIME_SERVICES_CODE:
+		case EFI_RUNTIME_SERVICES_DATA:
+		case EFI_MEMORY_MAPPED_IO:
+		case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
+		case EFI_PAL_CODE:
+			e820_type = E820_RESERVED;
+			break;
+
+		case EFI_UNUSABLE_MEMORY:
+			e820_type = E820_UNUSABLE;
+			break;
+
+		case EFI_ACPI_RECLAIM_MEMORY:
+			e820_type = E820_ACPI;
+			break;
+
+		case EFI_LOADER_CODE:
+		case EFI_LOADER_DATA:
+		case EFI_BOOT_SERVICES_CODE:
+		case EFI_BOOT_SERVICES_DATA:
+		case EFI_CONVENTIONAL_MEMORY:
+			e820_type = E820_RAM;
+			break;
+
+		case EFI_ACPI_MEMORY_NVS:
+			e820_type = E820_NVS;
+			break;
+
+		default:
+			continue;
+		}
+
+		/* Merge adjacent mappings */
+		if (prev && prev->type == e820_type &&
+		    (prev->addr + prev->size) == d->phys_addr)
+			prev->size += d->num_pages << 12;
+		else {
+			e820_map->addr = d->phys_addr;
+			e820_map->size = d->num_pages << 12;
+			e820_map->type = e820_type;
+			prev = e820_map++;
+			nr_entries++;
+		}
+	}
+
+	boot_params->e820_entries = nr_entries;
+
+	return EFI_SUCCESS;
+
+free_mem_map:
+	low_free(_size, (unsigned long)mem_map);
+free_cmdline:
+	if (options_size)
+		low_free(options_size, hdr->cmd_line_ptr);
+fail:
+	return status;
+}
+
+/*
+ * On success we return a pointer to a boot_params structure, and NULL
+ * on failure.
+ */
+struct boot_params *efi_main(void *handle, efi_system_table_t *_table)
+{
+	struct boot_params *boot_params;
+	unsigned long start, nr_pages;
+	struct desc_ptr *gdt, *idt;
+	efi_loaded_image_t *image;
+	struct setup_header *hdr;
+	efi_status_t status;
+	efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
+	struct desc_struct *desc;
+
+	sys_table = _table;
+
+	/* Check if we were booted by the EFI firmware */
+	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+		goto fail;
+
+	status = efi_call_phys3(sys_table->boottime->handle_protocol,
+				handle, &proto, (void *)&image);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	status = low_alloc(0x4000, 1, (unsigned long *)&boot_params);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	memset(boot_params, 0x0, 0x4000);
+
+	/* Copy first two sectors to boot_params */
+	memcpy(boot_params, image->image_base, 1024);
+
+	hdr = &boot_params->hdr;
+
+	/*
+	 * The EFI firmware loader could have placed the kernel image
+	 * anywhere in memory, but the kernel has various restrictions
+	 * on the max physical address it can run at. Attempt to move
+	 * the kernel to boot_params.pref_address, or as low as
+	 * possible.
+	 */
+	start = hdr->pref_address;
+	nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+
+	status = efi_call_phys4(sys_table->boottime->allocate_pages,
+				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+				nr_pages, &start);
+	if (status != EFI_SUCCESS) {
+		status = low_alloc(hdr->init_size, hdr->kernel_alignment,
+				   &start);
+		if (status != EFI_SUCCESS)
+			goto fail;
+	}
+
+	hdr->code32_start = (__u32)start;
+	hdr->pref_address = (__u64)(unsigned long)image->image_base;
+
+	memcpy((void *)start, image->image_base, image->image_size);
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, sizeof(*gdt),
+				(void **)&gdt);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	gdt->size = 0x800;
+	status = low_alloc(gdt->size, 8, (unsigned long *)&gdt->address);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, sizeof(*idt),
+				(void **)&idt);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	idt->size = 0;
+	idt->address = 0;
+
+	status = make_boot_params(boot_params, image, handle);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	memset((char *)gdt->address, 0x0, gdt->size);
+	desc = (struct desc_struct *)gdt->address;
+
+	/* The first GDT is a dummy and the second is unused. */
+	desc += 2;
+
+	desc->limit0 = 0xffff;
+	desc->base0 = 0x0000;
+	desc->base1 = 0x0000;
+	desc->type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ;
+	desc->s = DESC_TYPE_CODE_DATA;
+	desc->dpl = 0;
+	desc->p = 1;
+	desc->limit = 0xf;
+	desc->avl = 0;
+	desc->l = 0;
+	desc->d = SEG_OP_SIZE_32BIT;
+	desc->g = SEG_GRANULARITY_4KB;
+	desc->base2 = 0x00;
+
+	desc++;
+	desc->limit0 = 0xffff;
+	desc->base0 = 0x0000;
+	desc->base1 = 0x0000;
+	desc->type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE;
+	desc->s = DESC_TYPE_CODE_DATA;
+	desc->dpl = 0;
+	desc->p = 1;
+	desc->limit = 0xf;
+	desc->avl = 0;
+	desc->l = 0;
+	desc->d = SEG_OP_SIZE_32BIT;
+	desc->g = SEG_GRANULARITY_4KB;
+	desc->base2 = 0x00;
+
+#ifdef CONFIG_X86_64
+	/* Task segment value */
+	desc++;
+	desc->limit0 = 0x0000;
+	desc->base0 = 0x0000;
+	desc->base1 = 0x0000;
+	desc->type = SEG_TYPE_TSS;
+	desc->s = 0;
+	desc->dpl = 0;
+	desc->p = 1;
+	desc->limit = 0x0;
+	desc->avl = 0;
+	desc->l = 0;
+	desc->d = 0;
+	desc->g = SEG_GRANULARITY_4KB;
+	desc->base2 = 0x00;
+#endif /* CONFIG_X86_64 */
+
+	asm volatile ("lidt %0" :: "m" (*idt));
+	asm volatile ("lgdt %0" :: "m" (*gdt));
+
+	asm volatile("cli");
+
+	return boot_params;
+fail:
+	return NULL;
+}
diff --git a/arch/x86/boot/compressed/efi_stub_32.S b/arch/x86/boot/compressed/efi_stub_32.S
new file mode 100644
index 0000000..5047cd9
--- /dev/null
+++ b/arch/x86/boot/compressed/efi_stub_32.S
@@ -0,0 +1,87 @@
+/*
+ * EFI call stub for IA32.
+ *
+ * This stub allows us to make EFI calls in physical mode with interrupts
+ * turned off. Note that this implementation is different from the one in
+ * arch/x86/platform/efi/efi_stub_32.S because we're _already_ in physical
+ * mode at this point.
+ */
+
+#include <linux/linkage.h>
+#include <asm/page_types.h>
+
+/*
+ * efi_call_phys(void *, ...) is a function with variable parameters.
+ * All the callers of this function assure that all the parameters are 4-bytes.
+ */
+
+/*
+ * In gcc calling convention, EBX, ESP, EBP, ESI and EDI are all callee save.
+ * So we'd better save all of them at the beginning of this function and restore
+ * at the end no matter how many we use, because we can not assure EFI runtime
+ * service functions will comply with gcc calling convention, too.
+ */
+
+.text
+ENTRY(efi_call_phys)
+	/*
+	 * 0. The function can only be called in Linux kernel. So CS has been
+	 * set to 0x0010, DS and SS have been set to 0x0018. In EFI, I found
+	 * the values of these registers are the same. And, the corresponding
+	 * GDT entries are identical. So I will do nothing about segment reg
+	 * and GDT, but change GDT base register in prelog and epilog.
+	 */
+
+	/*
+	 * 1. Because we haven't been relocated by this point we need to
+	 * use relative addressing.
+	 */
+	call	1f
+1:	popl	%edx
+	subl	$1b, %edx
+
+	/*
+	 * 2. Now on the top of stack is the return
+	 * address in the caller of efi_call_phys(), then parameter 1,
+	 * parameter 2, ..., param n. To make things easy, we save the return
+	 * address of efi_call_phys in a global variable.
+	 */
+	popl	%ecx
+	movl	%ecx, saved_return_addr(%edx)
+	/* get the function pointer into ECX*/
+	popl	%ecx
+	movl	%ecx, efi_rt_function_ptr(%edx)
+
+	/*
+	 * 3. Call the physical function.
+	 */
+	call	*%ecx
+
+	/*
+	 * 4. Balance the stack. And because EAX contain the return value,
+	 * we'd better not clobber it. We need to calculate our address
+	 * again because %ecx and %edx are not preserved across EFI function
+	 * calls.
+	 */
+	call	1f
+1:	popl	%edx
+	subl	$1b, %edx
+
+	movl	efi_rt_function_ptr(%edx), %ecx
+	pushl	%ecx
+
+	/*
+	 * 10. Push the saved return address onto the stack and return.
+	 */
+	movl	saved_return_addr(%edx), %ecx
+	pushl	%ecx
+	ret
+ENDPROC(efi_call_phys)
+.previous
+
+.data
+saved_return_addr:
+	.long 0
+efi_rt_function_ptr:
+	.long 0
+
diff --git a/arch/x86/boot/compressed/efi_stub_64.S b/arch/x86/boot/compressed/efi_stub_64.S
new file mode 100644
index 0000000..cedc60d
--- /dev/null
+++ b/arch/x86/boot/compressed/efi_stub_64.S
@@ -0,0 +1 @@
+#include "../../platform/efi/efi_stub_64.S"
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 67a655a..a055993 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -32,6 +32,28 @@
 
 	__HEAD
 ENTRY(startup_32)
+#ifdef CONFIG_EFI_STUB
+	/*
+	 * We don't need the return address, so set up the stack so
+	 * efi_main() can find its arugments.
+	 */
+	add	$0x4, %esp
+
+	call	efi_main
+	cmpl	$0, %eax
+	je	preferred_addr
+	movl	%eax, %esi
+	call	1f
+1:
+	popl	%eax
+	subl	$1b, %eax
+	subl	BP_pref_address(%esi), %eax
+	add	BP_code32_start(%esi), %eax
+	leal	preferred_addr(%eax), %eax
+	jmp	*%eax
+
+preferred_addr:
+#endif
 	cld
 	/*
 	 * Test KEEP_SEGMENTS flag to see if the bootloader is asking
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 35af09d..558d76c 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -199,6 +199,26 @@ ENTRY(startup_64)
 	 * an identity mapped page table being provied that maps our
 	 * entire text+data+bss and hopefully all of memory.
 	 */
+#ifdef CONFIG_EFI_STUB
+	pushq	%rsi
+	mov	%rcx, %rdi
+	mov	%rdx, %rsi
+	call	efi_main
+	popq	%rsi
+	cmpq	$0,%rax
+	je	preferred_addr
+	movq	%rax,%rsi
+	call	1f
+1:
+	popq	%rax
+	subq	$1b, %rax
+	subq	BP_pref_address(%rsi), %rax
+	add	BP_code32_start(%esi), %eax
+	leaq	preferred_addr(%rax), %rax
+	jmp	*%rax
+
+preferred_addr:
+#endif
 
 	/* Setup data segments. */
 	xorl	%eax, %eax
diff --git a/arch/x86/boot/compressed/string.c b/arch/x86/boot/compressed/string.c
index 19b3e69..ffb9c5c 100644
--- a/arch/x86/boot/compressed/string.c
+++ b/arch/x86/boot/compressed/string.c
@@ -1,2 +1,11 @@
 #include "misc.h"
+
+int memcmp(const void *s1, const void *s2, size_t len)
+{
+	u8 diff;
+	asm("repe; cmpsb; setnz %0"
+	    : "=qm" (diff), "+D" (s1), "+S" (s2), "+c" (len));
+	return diff;
+}
+
 #include "../string.c"
diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S
index 93e689f..c4756f6 100644
--- a/arch/x86/boot/header.S
+++ b/arch/x86/boot/header.S
@@ -45,6 +45,11 @@ SYSSEG		= 0x1000		/* historical load address >> 4 */
 
 	.global bootsect_start
 bootsect_start:
+#ifdef CONFIG_EFI_STUB
+	# "MZ", MS-DOS header
+	.byte 0x4d
+	.byte 0x5a
+#endif
 
 	# Normalize the start address
 	ljmp	$BOOTSEG, $start2
@@ -79,6 +84,14 @@ bs_die:
 	# invoke the BIOS reset code...
 	ljmp	$0xf000,$0xfff0
 
+#ifdef CONFIG_EFI_STUB
+	.org	0x3c
+	#
+	# Offset to the PE header.
+	#
+	.long	pe_header
+#endif /* CONFIG_EFI_STUB */
+
 	.section ".bsdata", "a"
 bugger_off_msg:
 	.ascii	"Direct booting from floppy is no longer supported.\r\n"
@@ -87,6 +100,141 @@ bugger_off_msg:
 	.ascii	"Remove disk and press any key to reboot . . .\r\n"
 	.byte	0
 
+#ifdef CONFIG_EFI_STUB
+pe_header:
+	.ascii	"PE"
+	.word 	0
+
+coff_header:
+#ifdef CONFIG_X86_32
+	.word	0x14c				# i386
+#else
+	.word	0x8664				# x86-64
+#endif
+	.word	2				# nr_sections
+	.long	0 				# TimeDateStamp
+	.long	0				# PointerToSymbolTable
+	.long	1				# NumberOfSymbols
+	.word	section_table - optional_header	# SizeOfOptionalHeader
+#ifdef CONFIG_X86_32
+	.word	0x306				# Characteristics.
+						# IMAGE_FILE_32BIT_MACHINE |
+						# IMAGE_FILE_DEBUG_STRIPPED |
+						# IMAGE_FILE_EXECUTABLE_IMAGE |
+						# IMAGE_FILE_LINE_NUMS_STRIPPED
+#else
+	.word	0x206				# Characteristics
+						# IMAGE_FILE_DEBUG_STRIPPED |
+						# IMAGE_FILE_EXECUTABLE_IMAGE |
+						# IMAGE_FILE_LINE_NUMS_STRIPPED
+#endif
+
+optional_header:
+#ifdef CONFIG_X86_32
+	.word	0x10b				# PE32 format
+#else
+	.word	0x20b 				# PE32+ format
+#endif
+	.byte	0x02				# MajorLinkerVersion
+	.byte	0x14				# MinorLinkerVersion
+
+	# Filled in by build.c
+	.long	0				# SizeOfCode
+
+	.long	0				# SizeOfInitializedData
+	.long	0				# SizeOfUninitializedData
+
+	# Filled in by build.c
+	.long	0x0000				# AddressOfEntryPoint
+
+	.long	0x0000				# BaseOfCode
+#ifdef CONFIG_X86_32
+	.long	0				# data
+#endif
+
+extra_header_fields:
+#ifdef CONFIG_X86_32
+	.long	0				# ImageBase
+#else
+	.quad	0				# ImageBase
+#endif
+	.long	0x1000				# SectionAlignment
+	.long	0x200				# FileAlignment
+	.word	0				# MajorOperatingSystemVersion
+	.word	0				# MinorOperatingSystemVersion
+	.word	0				# MajorImageVersion
+	.word	0				# MinorImageVersion
+	.word	0				# MajorSubsystemVersion
+	.word	0				# MinorSubsystemVersion
+	.long	0				# Win32VersionValue
+
+	#
+	# The size of the bzImage is written in tools/build.c
+	#
+	.long	0				# SizeOfImage
+
+	.long	0x200				# SizeOfHeaders
+	.long	0				# CheckSum
+	.word	0xa				# Subsystem (EFI application)
+	.word	0				# DllCharacteristics
+#ifdef CONFIG_X86_32
+	.long	0				# SizeOfStackReserve
+	.long	0				# SizeOfStackCommit
+	.long	0				# SizeOfHeapReserve
+	.long	0				# SizeOfHeapCommit
+#else
+	.quad	0				# SizeOfStackReserve
+	.quad	0				# SizeOfStackCommit
+	.quad	0				# SizeOfHeapReserve
+	.quad	0				# SizeOfHeapCommit
+#endif
+	.long	0				# LoaderFlags
+	.long	0x1				# NumberOfRvaAndSizes
+
+	.quad	0				# ExportTable
+	.quad	0				# ImportTable
+	.quad	0				# ResourceTable
+	.quad	0				# ExceptionTable
+	.quad	0				# CertificationTable
+	.quad	0				# BaseRelocationTable
+
+	# Section table
+section_table:
+	.ascii	".text"
+	.byte	0
+	.byte	0
+	.byte	0
+	.long	0
+	.long	0x0				# startup_{32,64}
+	.long	0				# Size of initialized data
+						# on disk
+	.long	0x0				# startup_{32,64}
+	.long	0				# PointerToRelocations
+	.long	0				# PointerToLineNumbers
+	.word	0				# NumberOfRelocations
+	.word	0				# NumberOfLineNumbers
+	.long	0x60500020			# Characteristics (section flags)
+
+	#
+	# The EFI application loader requires a relocation section
+	# because EFI applications are relocatable and not having
+	# this section seems to confuse it. But since we don't need
+	# the loader to fixup any relocs for us just fill it with a
+	# single dummy reloc.
+	#
+	.ascii	".reloc"
+	.byte	0
+	.byte	0
+	.long	reloc_end - reloc_start
+	.long	reloc_start
+	.long	reloc_end - reloc_start		# SizeOfRawData
+	.long	reloc_start			# PointerToRawData
+	.long	0				# PointerToRelocations
+	.long	0				# PointerToLineNumbers
+	.word	0				# NumberOfRelocations
+	.word	0				# NumberOfLineNumbers
+	.long	0x42100040			# Characteristics (section flags)
+#endif /* CONFIG_EFI_STUB */
 
 	# Kernel attributes; used by setup.  This is part 1 of the
 	# header, from the old boot sector.
@@ -318,3 +466,13 @@ die:
 setup_corrupt:
 	.byte	7
 	.string	"No setup signature found...\n"
+
+	.data
+dummy:	.long	0
+
+	.section .reloc
+reloc_start:
+	.long	dummy - reloc_start
+	.long	10
+	.word	0
+reloc_end:
diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c
index 3cbc405..574dedf 100644
--- a/arch/x86/boot/string.c
+++ b/arch/x86/boot/string.c
@@ -111,3 +111,38 @@ unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int bas
 
 	return result;
 }
+
+/**
+ * strlen - Find the length of a string
+ * @s: The string to be sized
+ */
+size_t strlen(const char *s)
+{
+	const char *sc;
+
+	for (sc = s; *sc != '\0'; ++sc)
+		/* nothing */;
+	return sc - s;
+}
+
+/**
+ * strstr - Find the first substring in a %NUL terminated string
+ * @s1: The string to be searched
+ * @s2: The string to search for
+ */
+char *strstr(const char *s1, const char *s2)
+{
+	size_t l1, l2;
+
+	l2 = strlen(s2);
+	if (!l2)
+		return (char *)s1;
+	l1 = strlen(s1);
+	while (l1 >= l2) {
+		l1--;
+		if (!memcmp(s1, s2, l2))
+			return (char *)s1;
+		s1++;
+	}
+	return NULL;
+}
diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c
index fdc60a0..4e9bd6b 100644
--- a/arch/x86/boot/tools/build.c
+++ b/arch/x86/boot/tools/build.c
@@ -135,6 +135,9 @@ static void usage(void)
 
 int main(int argc, char ** argv)
 {
+#ifdef CONFIG_EFI_STUB
+	unsigned int file_sz, pe_header;
+#endif
 	unsigned int i, sz, setup_sectors;
 	int c;
 	u32 sys_size;
@@ -194,6 +197,42 @@ int main(int argc, char ** argv)
 	buf[0x1f6] = sys_size >> 16;
 	buf[0x1f7] = sys_size >> 24;
 
+#ifdef CONFIG_EFI_STUB
+	file_sz = sz + i + ((sys_size * 16) - sz);
+
+	pe_header = *(unsigned int *)&buf[0x3c];
+
+	/* Size of code */
+	*(unsigned int *)&buf[pe_header + 0x1c] = file_sz;
+
+	/* Size of image */
+	*(unsigned int *)&buf[pe_header + 0x50] = file_sz;
+
+#ifdef CONFIG_X86_32
+	/* Address of entry point */
+	*(unsigned int *)&buf[pe_header + 0x28] = i;
+
+	/* .text size */
+	*(unsigned int *)&buf[pe_header + 0xb0] = file_sz;
+
+	/* .text size of initialised data */
+	*(unsigned int *)&buf[pe_header + 0xb8] = file_sz;
+#else
+	/*
+	 * Address of entry point. startup_32 is at the beginning and
+	 * the 64-bit entry point (startup_64) is always 512 bytes
+	 * after.
+	 */
+	*(unsigned int *)&buf[pe_header + 0x28] = i + 512;
+
+	/* .text size */
+	*(unsigned int *)&buf[pe_header + 0xc0] = file_sz;
+
+	/* .text size of initialised data */
+	*(unsigned int *)&buf[pe_header + 0xc8] = file_sz;
+#endif /* CONFIG_X86_32 */
+#endif /* CONFIG_EFI_STUB */
+
 	crc = partial_crc32(buf, i, crc);
 	if (fwrite(buf, 1, i, stdout) != i)
 		die("Writing setup failed");
diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c
index 4f13faf..68de2dc 100644
--- a/arch/x86/kernel/asm-offsets.c
+++ b/arch/x86/kernel/asm-offsets.c
@@ -67,4 +67,6 @@ void common(void) {
 	OFFSET(BP_hardware_subarch, boot_params, hdr.hardware_subarch);
 	OFFSET(BP_version, boot_params, hdr.version);
 	OFFSET(BP_kernel_alignment, boot_params, hdr.kernel_alignment);
+	OFFSET(BP_pref_address, boot_params, hdr.pref_address);
+	OFFSET(BP_code32_start, boot_params, hdr.code32_start);
 }
-- 
1.7.4.4


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

* Re: [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-10-17 10:40 ` [PATCH v5 10/10] x86, efi: EFI boot stub support Matt Fleming
@ 2011-10-17 16:39   ` Maarten Lankhorst
  2011-10-17 18:28     ` Matt Fleming
  2011-10-18  6:09     ` Ingo Molnar
  2011-10-17 20:06   ` Maarten Lankhorst
  1 sibling, 2 replies; 39+ messages in thread
From: Maarten Lankhorst @ 2011-10-17 16:39 UTC (permalink / raw)
  To: Matt Fleming
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Matt Fleming, Mike Waychison, Andi Kleen

Hey Matt,

On 10/17/2011 12:40 PM, Matt Fleming wrote:
> From: Matt Fleming <matt.fleming@intel.com>
>
> There is currently a large divide between kernel development and the
> development of EFI boot loaders. The idea behind this patch is to give
> the kernel developers full control over the EFI boot process. As
> H. Peter Anvin put it,
>
> "The 'kernel carries its own stub' approach been very successful in
> dealing with BIOS, and would make a lot of sense to me for EFI as
> well."
>
> This patch introduces an EFI boot stub that allows an x86 bzImage to
> be loaded and executed by EFI firmware. The bzImage appears to the
> firmware as an EFI application. Luckily there are enough free bits
> within the bzImage header so that it can masquerade as an EFI
> application, thereby coercing the EFI firmware into loading it and
> jumping to its entry point. The beauty of this masquerading approach
> is that both BIOS and EFI boot loaders can still load and run the same
> bzImage, thereby allowing a single kernel image to work in any boot
> environment.
>
> The EFI boot stub supports multiple initrds, but they must exist on
> the same partition as the bzImage. Command-line arguments for the
> kernel can be appended after the bzImage name when run from the EFI
> shell, e.g.
>
> Shell> bzImage console=ttyS0 root=/dev/sdb initrd=initrd.img
>
> Cc: H. Peter Anvin <hpa@linux.intel.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Ingo Molnar <mingo@elte.hu>
> Cc: Mike Waychison <mikew@google.com>
> Cc: Matthew Garrett <mjg@redhat.com>
> Cc: Andi Kleen <andi@firstfloor.org>
> Cc: Maarten Lankhorst <m.b.lankhorst@gmail.com>
> Tested-by: Henrik Rydberg <rydberg@euromail.se>
> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
> ---
>
> v5:
>
>  - load_options_size is UTF-16, which needs dividing by 2 to convert
>    to the corresponding ASCII size (spotted by Maarten).
>
> v4:
>
>  - Don't read more than image->load_options_size
>
> v3:
>
>  - Fix following warnings when compiling CONFIG_EFI_STUB=n
>
>    arch/x86/boot/tools/build.c: In function ‘main’:
>    arch/x86/boot/tools/build.c:138:24: warning: unused variable ‘pe_header’
>    arch/x86/boot/tools/build.c:138:15: warning: unused variable ‘file_sz’
>
>  - As reported by Matthew Garrett, some Apple machines have GOPs that
>    don't have hardware attached. We need to weed these out by
>    searching for ones that handle the PCIIO protocol.
>
>  - Don't allocate memory if no initrds are on cmdline
>  - Don't trust image->load_options_size
>
> Maarten Lankhorst noted:
>  - Don't strip first argument when booted from efibootmgr
>  - Don't allocate too much memory for cmdline
>  - Don't update cmdline_size, the kernel considers it read-only
>  - Don't accept '\n' for initrd names
>
> v2:
>
>  - File alignment was too large, was 8192 should be 512. Reported by
>    Maarten Lankhorst on LKML.
>  - Added UGA support for graphics
>  - Use VIDEO_TYPE_EFI instead of hard-coded number.
>  - Move linelength assignment until after we've assigned depth
>  - Dynamically fill out AddressOfEntryPoint in tools/build.c
>  - Don't use magic number for GDT/TSS stuff. Requested by Andi Kleen
>  - The bzImage may need to be relocated as it may have been loaded at
>    a high address address by the firmware. This was required to get my
>    macbook booting because the firmware loaded it at 0x7cxxxxxx, which
>    triggers this error in decompress_kernel(),
>
> 	if (heap > ((-__PAGE_OFFSET-(128<<20)-1) & 0x7fffffff))
> 		error("Destination address too large");
>
>  arch/x86/Kconfig                       |    7 +
>  arch/x86/boot/compressed/Makefile      |   10 +-
>  arch/x86/boot/compressed/eboot.c       |  975 ++++++++++++++++++++++++++++++++
>  arch/x86/boot/compressed/efi_stub_32.S |   87 +++
>  arch/x86/boot/compressed/efi_stub_64.S |    1 +
>  arch/x86/boot/compressed/head_32.S     |   22 +
>  arch/x86/boot/compressed/head_64.S     |   20 +
>  arch/x86/boot/compressed/string.c      |    9 +
>  arch/x86/boot/header.S                 |  158 +++++
>  arch/x86/boot/string.c                 |   35 ++
>  arch/x86/boot/tools/build.c            |   39 ++
>  arch/x86/kernel/asm-offsets.c          |    2 +
>  12 files changed, 1364 insertions(+), 1 deletions(-)
>  create mode 100644 arch/x86/boot/compressed/eboot.c
>  create mode 100644 arch/x86/boot/compressed/efi_stub_32.S
>  create mode 100644 arch/x86/boot/compressed/efi_stub_64.S
>
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 6a47bb2..d40c876 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -1466,6 +1466,13 @@ config EFI
>  	  resultant kernel should continue to boot on existing non-EFI
>  	  platforms.
>  
> +config EFI_STUB
> +       bool "EFI stub support"
> +       depends on EFI
> +       ---help---
> +          This kernel feature allows a bzImage to be loaded directly
> +	  by EFI firmware without the use of a bootloader.
> +
>  config SECCOMP
>  	def_bool y
>  	prompt "Enable seccomp to safely compute untrusted bytecode"
> diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
> index 09664ef..b123b9a 100644
> --- a/arch/x86/boot/compressed/Makefile
> +++ b/arch/x86/boot/compressed/Makefile
> @@ -23,7 +23,15 @@ LDFLAGS_vmlinux := -T
>  
>  hostprogs-y	:= mkpiggy
>  
> -$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o $(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o $(obj)/piggy.o FORCE
> +VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \
> +	$(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \
> +	$(obj)/piggy.o
> +
> +ifeq ($(CONFIG_EFI_STUB), y)
> +	VMLINUX_OBJS += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o
> +endif
> +
> +$(obj)/vmlinux: $(VMLINUX_OBJS) FORCE
>  	$(call if_changed,ld)
>  	@:
>  
> diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
> new file mode 100644
> index 0000000..f84e389
> --- /dev/null
> +++ b/arch/x86/boot/compressed/eboot.c
> @@ -0,0 +1,975 @@
> +/* -----------------------------------------------------------------------
> + *
> + *   Copyright 2011 Intel Corporation; author Matt Fleming
> + *
> + *   This file is part of the Linux kernel, and is made available under
> + *   the terms of the GNU General Public License version 2.
> + *
> + * ----------------------------------------------------------------------- */
> +
> +#include <linux/efi.h>
> +#include <asm/efi.h>
> +#include <asm/setup.h>
> +#include <asm/desc.h>
> +
> +#define SEG_TYPE_DATA		(0 << 3)
> +#define SEG_TYPE_READ_WRITE	(1 << 1)
> +#define SEG_TYPE_CODE		(1 << 3)
> +#define SEG_TYPE_EXEC_READ	(1 << 1)
> +#define SEG_TYPE_TSS		((1 << 3) | (1 << 0))
> +#define SEG_OP_SIZE_32BIT	(1 << 0)
> +#define SEG_GRANULARITY_4KB	(1 << 0)
> +
> +#define DESC_TYPE_CODE_DATA	(1 << 0)
> +
> +#define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
> +
> +#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR		0
> +#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR		1
> +#define PIXEL_BIT_MASK					2
> +#define PIXEL_BLT_ONLY					3
> +#define PIXEL_FORMAT_MAX				4
> +
> +typedef struct {
> +	u32 red_mask;
> +	u32 green_mask;
> +	u32 blue_mask;
> +	u32 reserved_mask;
> +} efi_pixel_bitmask_t;
> +
> +typedef struct {
> +	u32 version;
> +	u32 horizontal_resolution;
> +	u32 vertical_resolution;
> +	int pixel_format;
> +	efi_pixel_bitmask_t pixel_information;
> +	u32 pixels_per_scan_line;
> +} __attribute__((packed)) efi_graphics_output_mode_information_t;
> +
> +typedef struct {
> +	u32 max_mode;
> +	u32 mode;
> +	unsigned long info;
> +	unsigned long size_of_info;
> +	u64 frame_buffer_base;
> +	unsigned long frame_buffer_size;
> +} __attribute__((packed)) efi_graphics_output_protocol_mode_t;
> +
> +typedef struct {
> +	void *query_mode;
> +	unsigned long set_mode;
> +	unsigned long blt;
> +	efi_graphics_output_protocol_mode_t *mode;
> +} efi_graphics_output_protocol_t;
> +
> +typedef struct {
> +	void *get_mode;
> +	void *set_mode;
> +	void *blt;
> +} efi_uga_draw_protocol_t;
> +
> +static efi_system_table_t *sys_table;
> +
> +static efi_status_t low_alloc(unsigned long size, unsigned long align,
> +			      unsigned long *addr)
> +{
> +	unsigned long map_size, key, desc_size;
> +	efi_memory_desc_t *map;
> +	efi_status_t status;
> +	unsigned long nr_pages;
> +	u32 desc_version;
> +	int i;
> +
> +	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> +
> +	map_size = sizeof(*map) * 32;
> +
> +again:
> +	/*
> +	 * Add an additional efi_memory_desc_t because we're doing an
> +	 * allocation which may be in a new descriptor region.
> +	 */
> +	map_size += sizeof(*map);
> +	status = efi_call_phys3(sys_table->boottime->allocate_pool,
> +				EFI_LOADER_DATA, map_size, (void **)&map);
> +	if (status != EFI_SUCCESS)
> +		goto fail;
> +
> +	status = efi_call_phys5(sys_table->boottime->get_memory_map, &map_size,
> +				map, &key, &desc_size, &desc_version);
> +	if (status == EFI_BUFFER_TOO_SMALL) {
> +		efi_call_phys1(sys_table->boottime->free_pool, map);
> +		goto again;
> +	}
> +
> +	if (status != EFI_SUCCESS)
> +		goto free_pool;
> +
> +	for (i = 0; i < map_size / desc_size; i++) {
> +		efi_memory_desc_t *desc;
> +		u64 start, end;
> +
> +		desc = (efi_memory_desc_t *)((unsigned long)map + (i * desc_size));
> +
> +		if (desc->type != EFI_CONVENTIONAL_MEMORY)
> +			continue;
> +
> +		if (desc->num_pages < nr_pages)
> +			continue;
> +
> +		start = desc->phys_addr;
> +		end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
> +
> +		/*
> +		 * Don't allocate at 0x0. It will confuse code that
> +		 * checks pointers against NULL. Skip the first 8
> +		 * bytes so we start at a nice even number.
> +		 */
> +		if (start == 0x0) {
> +			start += 8;
> +
> +			/* Check for tiny memory regions */
> +			if (start >= end)
> +				continue;
> +		}
> +
> +		start = round_up(start, align);
> +		if ((start + size) > end)
> +			continue;
> +
> +		status = efi_call_phys4(sys_table->boottime->allocate_pages,
> +					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
> +					nr_pages, &start);
> +		if (status == EFI_SUCCESS) {
> +			*addr = start;
> +			break;
> +		}
> +	}
> +
> +	if (i == map_size / desc_size)
> +		status = EFI_NOT_FOUND;
> +
> +free_pool:
> +	efi_call_phys1(sys_table->boottime->free_pool, map);
> +fail:
> +	return status;
> +}
> +
> +static void low_free(unsigned long size, unsigned long addr)
> +{
> +	unsigned long nr_pages;
> +
> +	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> +	efi_call_phys2(sys_table->boottime->free_pages, addr, size);
> +}
> +
> +static void find_bits(unsigned long mask, u8 *pos, u8 *size)
> +{
> +        u8 first, len;
> +
> +	first = 0;
> +	len = 0;
> +
> +	if (mask) {
> +		while (!(mask & 0x1)) {
> +			mask = mask >> 1;
> +			first++;
> +		}
> +
> +		while (mask & 0x1) {
> +			mask = mask >> 1;
> +			len++;
> +		}
> +	}
> +
> +        *pos = first;
> +        *size = len;
> +}
> +
> +/*
> + * See if we have Graphics Output Protocol
> + */
> +static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
> +			      unsigned long size)
> +{
> +	efi_graphics_output_protocol_t *gop, *first_gop;
> +	efi_pixel_bitmask_t pixel_info;
> +	unsigned long nr_gops;
> +	efi_status_t status;
> +	void **gop_handle;
> +	u16 width, height;
> +	u32 fb_base, fb_size;
> +	u32 pixels_per_scan_line;
> +	int pixel_format;
> +	int i;
> +
> +	status = efi_call_phys3(sys_table->boottime->allocate_pool,
> +				EFI_LOADER_DATA, size, &gop_handle);
> +	if (status != EFI_SUCCESS)
> +		return status;
> +
> +	status = efi_call_phys5(sys_table->boottime->locate_handle,
> +				EFI_LOCATE_BY_PROTOCOL, proto,
> +				NULL, &size, gop_handle);
> +	if (status != EFI_SUCCESS)
> +		goto free_handle;
> +
> +	first_gop = NULL;
> +
> +	nr_gops = size / sizeof(void *);
> +	for (i = 0; i < nr_gops; i++) {
> +		efi_graphics_output_mode_information_t *info;
> +		efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID;
> +		void *pciio;
> +		void *h = gop_handle[i];
> +
> +		status = efi_call_phys3(sys_table->boottime->handle_protocol,
> +					h, proto, &gop);
> +		if (status != EFI_SUCCESS)
> +			continue;
> +
> +		efi_call_phys3(sys_table->boottime->handle_protocol,
> +			       h, &pciio_proto, &pciio);
> +
> +		status = efi_call_phys4(gop->query_mode, gop,
> +					gop->mode->mode, &size, &info);
> +		if (status == EFI_SUCCESS && (!first_gop || pciio)) {
> +			/*
> +			 * Apple provide GOPs that are not backed by
> +			 * real hardware (they're used to handle
> +			 * multiple displays). The workaround is to
> +			 * search for a GOP implementing the PCIIO
> +			 * protocol, and if one isn't found, to just
> +			 * fallback to the first GOP.
> +			 */
> +			width = info->horizontal_resolution;
> +			height = info->vertical_resolution;
> +			fb_base = gop->mode->frame_buffer_base;
> +			fb_size = gop->mode->frame_buffer_size;
> +			pixel_format = info->pixel_format;
> +			pixel_info = info->pixel_information;
> +			pixels_per_scan_line = info->pixels_per_scan_line;
> +
> +			/*
> +			 * Once we've found a GOP supporting PCIIO,
> +			 * don't bother looking any further.
> +			 */
> +			if (pciio)
> +				break;
> +
> +			first_gop = gop;
> +		}
> +	}
> +
> +	/* Did we find any GOPs? */
> +	if (!first_gop)
> +		goto free_handle;
> +
> +	/* EFI framebuffer */
> +	si->orig_video_isVGA = VIDEO_TYPE_EFI;
> +
> +	si->lfb_width = width;
> +	si->lfb_height = height;
> +	si->lfb_base = fb_base;
> +	si->lfb_size = fb_size;
> +	si->pages = 1;
> +
> +	if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
> +		si->lfb_depth = 32;
> +		si->lfb_linelength = pixels_per_scan_line * 4;
> +		si->red_size = 8;
> +		si->red_pos = 0;
> +		si->green_size = 8;
> +		si->green_pos = 8;
> +		si->blue_size = 8;
> +		si->blue_pos = 16;
> +		si->rsvd_size = 8;
> +		si->rsvd_pos = 24;
> +	} else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
> +		si->lfb_depth = 32;
> +		si->lfb_linelength = pixels_per_scan_line * 4;
> +		si->red_size = 8;
> +		si->red_pos = 16;
> +		si->green_size = 8;
> +		si->green_pos = 8;
> +		si->blue_size = 8;
> +		si->blue_pos = 0;
> +		si->rsvd_size = 8;
> +		si->rsvd_pos = 24;
> +	} else if (pixel_format == PIXEL_BIT_MASK) {
> +		find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
> +		find_bits(pixel_info.green_mask, &si->green_pos,
> +			  &si->green_size);
> +		find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
> +		find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
> +			  &si->rsvd_size);
> +		si->lfb_depth = si->red_size + si->green_size +
> +			si->blue_size + si->rsvd_size;
> +		si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
> +	} else {
> +		si->lfb_depth = 4;
> +		si->lfb_linelength = si->lfb_width / 2;
> +		si->red_size = 0;
> +		si->red_pos = 0;
> +		si->green_size = 0;
> +		si->green_pos = 0;
> +		si->blue_size = 0;
> +		si->blue_pos = 0;
> +		si->rsvd_size = 0;
> +		si->rsvd_pos = 0;
> +	}
> +
> +free_handle:
> +	efi_call_phys1(sys_table->boottime->free_pool, gop_handle);
> +	return status;
> +}
> +
> +/*
> + * See if we have Universal Graphics Adapter (UGA) protocol
> + */
> +static efi_status_t setup_uga(struct screen_info *si, efi_guid_t *uga_proto,
> +			      unsigned long size)
> +{
> +	efi_uga_draw_protocol_t *uga, *first_uga;
> +	unsigned long nr_ugas;
> +	efi_status_t status;
> +	u32 width, height;
> +	void **uga_handle = NULL;
> +	int i;
> +
> +	status = efi_call_phys3(sys_table->boottime->allocate_pool,
> +				EFI_LOADER_DATA, size, &uga_handle);
> +	if (status != EFI_SUCCESS)
> +		return status;
> +
> +	status = efi_call_phys5(sys_table->boottime->locate_handle,
> +				EFI_LOCATE_BY_PROTOCOL, uga_proto,
> +				NULL, &size, uga_handle);
> +	if (status != EFI_SUCCESS)
> +		goto free_handle;
> +
> +	first_uga = NULL;
> +
> +	nr_ugas = size / sizeof(void *);
> +	for (i = 0; i < nr_ugas; i++) {
> +		efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID;
> +		void *handle = uga_handle[i];
> +		u32 w, h, depth, refresh;
> +		void *pciio;
> +
> +		status = efi_call_phys3(sys_table->boottime->handle_protocol,
> +					handle, uga_proto, &uga);
> +		if (status != EFI_SUCCESS)
> +			continue;
> +
> +		efi_call_phys3(sys_table->boottime->handle_protocol,
> +			       handle, &pciio_proto, &pciio);
> +
> +		status = efi_call_phys5(uga->get_mode, uga, &w, &h,
> +					&depth, &refresh);
> +		if (status == EFI_SUCCESS && (!first_uga || pciio)) {
> +			width = w;
> +			height = h;
> +
> +			/*
> +			 * Once we've found a UGA supporting PCIIO,
> +			 * don't bother looking any further.
> +			 */
> +			if (pciio)
> +				break;
> +
> +			first_uga = uga;
> +		}
> +	}
> +
> +	if (!first_uga)
> +		goto free_handle;
> +
> +	/* EFI framebuffer */
> +	si->orig_video_isVGA = VIDEO_TYPE_EFI;
> +
> +	si->lfb_depth = 32;
> +	si->lfb_width = width;
> +	si->lfb_height = height;
> +
> +	si->red_size = 8;
> +	si->red_pos = 16;
> +	si->green_size = 8;
> +	si->green_pos = 8;
> +	si->blue_size = 8;
> +	si->blue_pos = 0;
> +	si->rsvd_size = 8;
> +	si->rsvd_pos = 24;
> +
> +
> +free_handle:
> +	efi_call_phys1(sys_table->boottime->free_pool, uga_handle);
> +	return status;
> +}
> +
> +void setup_graphics(struct boot_params *boot_params)
> +{
> +	efi_guid_t graphics_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
> +	struct screen_info *si;
> +	efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID;
> +	efi_status_t status;
> +	unsigned long size;
> +	void **gop_handle = NULL;
> +	void **uga_handle = NULL;
> +
> +	si = &boot_params->screen_info;
> +	memset(si, 0, sizeof(*si));
> +
> +	size = 0;
> +	status = efi_call_phys5(sys_table->boottime->locate_handle,
> +				EFI_LOCATE_BY_PROTOCOL, &graphics_proto,
> +				NULL, &size, gop_handle);
> +	if (status == EFI_BUFFER_TOO_SMALL)
> +		status = setup_gop(si, &graphics_proto, size);
> +
> +	if (status != EFI_SUCCESS) {
> +		size = 0;
> +		status = efi_call_phys5(sys_table->boottime->locate_handle,
> +					EFI_LOCATE_BY_PROTOCOL, &uga_proto,
> +					NULL, &size, uga_handle);
> +		if (status == EFI_BUFFER_TOO_SMALL)
> +			setup_uga(si, &uga_proto, size);
> +	}
> +}
> +
> +struct initrd {
> +	efi_file_handle_t *handle;
> +	u64 size;
> +};
> +
> +/*
> + * Check the cmdline for a LILO-style initrd= arguments.
> + *
> + * We only support loading an initrd from the same filesystem as the
> + * kernel image.
> + */
> +static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
> +				    struct setup_header *hdr)
> +{
> +	struct initrd *initrds;
> +	unsigned long initrd_addr;
> +	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
> +	u64 initrd_total;
> +	efi_file_io_interface_t *io;
> +	efi_file_handle_t *fh;
> +	efi_status_t status;
> +	int nr_initrds;
> +	char *str;
> +	int i, j, k;
> +
> +	initrd_addr = 0;
> +	initrd_total = 0;
> +
> +	str = (char *)(unsigned long)hdr->cmd_line_ptr;
> +
> +	j = 0;			/* See close_handles */
> +
> +	if (!str || !*str)
> +		return EFI_SUCCESS;
> +
> +	for (nr_initrds = 0; *str; nr_initrds++) {
> +		str = strstr(str, "initrd=");
> +		if (!str)
> +			break;
> +
> +		str += 7;
> +
> +		/* Skip any leading slashes */
> +		while (*str == '/' || *str == '\\')
> +			str++;
> +
> +		while (*str && *str != ' ' && *str != '\n')
> +			str++;
> +	}
> +
> +	if (!nr_initrds)
> +		return EFI_SUCCESS;
> +
> +	status = efi_call_phys3(sys_table->boottime->allocate_pool,
> +				EFI_LOADER_DATA,
> +				nr_initrds * sizeof(*initrds),
> +				&initrds);
> +	if (status != EFI_SUCCESS)
> +		goto fail;
> +
> +	str = (char *)(unsigned long)hdr->cmd_line_ptr;
> +	for (i = 0; i < nr_initrds; i++) {
> +		struct initrd *initrd;
> +		efi_file_handle_t *h;
> +		efi_file_info_t *info;
> +		efi_char16_t filename[256];
> +		unsigned long info_sz;
> +		efi_guid_t info_guid = EFI_FILE_INFO_ID;
> +		efi_char16_t *p;
> +		u64 file_sz;
> +
> +		str = strstr(str, "initrd=");
> +		if (!str)
> +			break;
> +
> +		str += 7;
> +
> +		initrd = &initrds[i];
> +		p = filename;
> +
> +		/* Skip any leading slashes */
> +		while (*str == '/' || *str == '\\')
> +			str++;
> +
> +		while (*str && *str != ' ' && *str != '\n') {
> +			if (p >= filename + sizeof(filename))
> +				break;
> +
> +			*p++ = *str++;
> +		}
> +
> +		*p = '\0';
> +
> +		/* Only open the volume once. */
> +		if (!i) {
> +			efi_boot_services_t *boottime;
> +
> +			boottime = sys_table->boottime;
> +
> +			status = efi_call_phys3(boottime->handle_protocol,
> +					image->device_handle, &fs_proto, &io);
> +			if (status != EFI_SUCCESS)
> +				goto free_initrds;
> +
> +			status = efi_call_phys2(io->open_volume, io, &fh);
> +			if (status != EFI_SUCCESS)
> +				goto free_initrds;
> +		}
> +
> +		status = efi_call_phys5(fh->open, fh, &h, filename,
> +					EFI_FILE_MODE_READ, (u64)0);
> +		if (status != EFI_SUCCESS)
> +			goto close_handles;
> +
> +		initrd->handle = h;
> +
> +		info_sz = 0;
> +		status = efi_call_phys4(h->get_info, h, &info_guid,
> +					&info_sz, NULL);
> +		if (status != EFI_BUFFER_TOO_SMALL)
> +			goto close_handles;
> +
> +grow:
> +		status = efi_call_phys3(sys_table->boottime->allocate_pool,
> +					EFI_LOADER_DATA, info_sz, &info);
> +		if (status != EFI_SUCCESS)
> +			goto close_handles;
> +
> +		status = efi_call_phys4(h->get_info, h, &info_guid,
> +					&info_sz, info);
> +		if (status == EFI_BUFFER_TOO_SMALL) {
> +			efi_call_phys1(sys_table->boottime->free_pool, info);
> +			goto grow;
> +		}
> +
> +		file_sz = info->file_size;
> +		efi_call_phys1(sys_table->boottime->free_pool, info);
> +
> +		if (status != EFI_SUCCESS)
> +			goto close_handles;
> +
> +		initrd->size = file_sz;
> +		initrd_total += file_sz;
> +	}
> +
> +	if (initrd_total) {
> +		unsigned long addr;
> +
> +		/*
> +		 * Multiple initrd's need to be at consecutive
> +		 * addresses in memory, so allocate enough memory for
> +		 * all the initrd's.
> +		 */
> +		status = low_alloc(initrd_total, 0x1000, &initrd_addr);
> +		if (status != EFI_SUCCESS)
> +			goto close_handles;
> +
> +		/* We've run out of free low memory. */
> +		if (initrd_addr > hdr->initrd_addr_max) {
> +			status = EFI_INVALID_PARAMETER;
> +			goto free_initrd_total;
> +		}
> +
> +		addr = initrd_addr;
> +		for (j = 0; j < nr_initrds; j++) {
> +			u64 size;
> +
> +			size = initrds[j].size;
> +			status = efi_call_phys3(fh->read, initrds[j].handle,
> +						&size, addr);
> +			if (status != EFI_SUCCESS)
> +				goto free_initrd_total;
> +
> +			efi_call_phys1(fh->close, initrds[j].handle);
> +
> +			addr += size;
> +		}
> +
> +	}
> +
> +	efi_call_phys1(sys_table->boottime->free_pool, initrds);
> +
> +	hdr->ramdisk_image = initrd_addr;
> +	hdr->ramdisk_size = initrd_total;
> +
> +	return status;
> +
> +free_initrd_total:
> +	low_free(initrd_total, initrd_addr);
> +
> +close_handles:
> +	for (k = j; k < nr_initrds; k++)
> +		efi_call_phys1(fh->close, initrds[k].handle);
> +free_initrds:
> +	efi_call_phys1(sys_table->boottime->free_pool, initrds);
> +fail:
> +	hdr->ramdisk_image = 0;
> +	hdr->ramdisk_size = 0;
> +
> +	return status;
> +}
> +
> +/*
> + * Because the x86 boot code expects to be passed a boot_params we
> + * need to create one ourselves (usually the bootloader would create
> + * one for us).
> + */
> +static efi_status_t make_boot_params(struct boot_params *boot_params,
> +				     efi_loaded_image_t *image,
> +				     void *handle)
> +{
> +	struct efi_info *efi = &boot_params->efi_info;
> +	struct apm_bios_info *bi = &boot_params->apm_bios_info;
> +	struct sys_desc_table *sdt = &boot_params->sys_desc_table;
> +	struct e820entry *e820_map = &boot_params->e820_map[0];
> +	struct e820entry *prev = NULL;
> +	struct setup_header *hdr = &boot_params->hdr;
> +	unsigned long size, key, desc_size, _size;
> +	efi_memory_desc_t *mem_map;
> +	void *options = image->load_options;
> +	u32 load_options_size = image->load_options_size / 2; /* ASCII */
> +	int options_size = 0;
> +	efi_status_t status;
> +	__u32 desc_version;
> +	unsigned long cmdline;
> +	u8 nr_entries;
> +	u16 *s2;
> +	u8 *s1;
> +	int i;
> +
> +	hdr->type_of_loader = 0x21;
> +
> +	/* Convert unicode cmdline to ascii */
> +	cmdline = 0;
> +	s2 = (u16 *)options;
> +
> +	if (s2) {
> +		while (*s2 && *s2 != '\n' && options_size < load_options_size) {
> +			s2++;
> +			options_size++;
> +		}
> +
> +		if (options_size) {
> +			if (options_size > hdr->cmdline_size)
> +				options_size = hdr->cmdline_size;
> +
> +			options_size++;	/* NUL termination */
> +
> +			status = low_alloc(options_size, 1, &cmdline);
> +			if (status != EFI_SUCCESS)
> +				goto fail;
Shouldn't this be options_size + 1?
> +
> +			s1 = (u8 *)(unsigned long)cmdline;
> +			s2 = (u16 *)options;
> +
> +			for (i = 0; i < options_size - 1; i++)
> +				*s1++ = *s2++;
> +
> +			*s1 = '\0';
For this null character..
> +		}
> +	}
> +
> +	hdr->cmd_line_ptr = cmdline;
> +
> +	hdr->ramdisk_image = 0;
> +	hdr->ramdisk_size = 0;
> +
> +	status = handle_ramdisks(image, hdr);
> +	if (status != EFI_SUCCESS)
> +		goto free_cmdline;
> +
> +	setup_graphics(boot_params);
> +
> +	/* Clear APM BIOS info */
> +	memset(bi, 0, sizeof(*bi));
> +
> +	memset(sdt, 0, sizeof(*sdt));
> +
> +	memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
> +
> +	size = sizeof(*mem_map) * 32;
> +
> +again:
> +	size += sizeof(*mem_map);
> +	_size = size;
> +	status = low_alloc(size, 1, (unsigned long *)&mem_map);
> +	if (status != EFI_SUCCESS)
> +		goto free_cmdline;
> +
> +	status = efi_call_phys5(sys_table->boottime->get_memory_map, &size,
> +				mem_map, &key, &desc_size, &desc_version);
> +	if (status == EFI_BUFFER_TOO_SMALL) {
> +		low_free(_size, (unsigned long)mem_map);
> +		goto again;
> +	}
> +
> +	if (status != EFI_SUCCESS)
> +		goto free_mem_map;
> +
> +	efi->efi_systab = (unsigned long)sys_table;
> +	efi->efi_memdesc_size = desc_size;
> +	efi->efi_memdesc_version = desc_version;
> +	efi->efi_memmap = (unsigned long)mem_map;
> +	efi->efi_memmap_size = size;
> +
> +#ifdef CONFIG_X86_64
> +	efi->efi_systab_hi = (unsigned long)sys_table >> 32;
> +	efi->efi_memmap_hi = (unsigned long)mem_map >> 32;
> +#endif
> +
> +	/* Might as well exit boot services now */
> +	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
> +				handle, key);
> +	if (status != EFI_SUCCESS)
> +		goto free_mem_map;
> +
> +	/* Historic? */
> +	boot_params->alt_mem_k = 32 * 1024;
> +
> +	/*
> +	 * Convert the EFI memory map to E820.
> +	 */
> +	nr_entries = 0;
> +	for (i = 0; i < size / desc_size; i++) {
> +		efi_memory_desc_t *d;
> +		unsigned int e820_type = 0;
> +
> +		d = (efi_memory_desc_t *)((unsigned long)mem_map + (i * desc_size));
> +		switch(d->type) {
> +		case EFI_RESERVED_TYPE:
> +		case EFI_RUNTIME_SERVICES_CODE:
> +		case EFI_RUNTIME_SERVICES_DATA:
> +		case EFI_MEMORY_MAPPED_IO:
> +		case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
> +		case EFI_PAL_CODE:
> +			e820_type = E820_RESERVED;
> +			break;
> +
> +		case EFI_UNUSABLE_MEMORY:
> +			e820_type = E820_UNUSABLE;
> +			break;
> +
> +		case EFI_ACPI_RECLAIM_MEMORY:
> +			e820_type = E820_ACPI;
> +			break;
> +
> +		case EFI_LOADER_CODE:
> +		case EFI_LOADER_DATA:
> +		case EFI_BOOT_SERVICES_CODE:
> +		case EFI_BOOT_SERVICES_DATA:
> +		case EFI_CONVENTIONAL_MEMORY:
> +			e820_type = E820_RAM;
> +			break;
> +
> +		case EFI_ACPI_MEMORY_NVS:
> +			e820_type = E820_NVS;
> +			break;
> +
> +		default:
> +			continue;
> +		}
> +
> +		/* Merge adjacent mappings */
> +		if (prev && prev->type == e820_type &&
> +		    (prev->addr + prev->size) == d->phys_addr)
> +			prev->size += d->num_pages << 12;
> +		else {
> +			e820_map->addr = d->phys_addr;
> +			e820_map->size = d->num_pages << 12;
> +			e820_map->type = e820_type;
> +			prev = e820_map++;
> +			nr_entries++;
> +		}
> +	}
> +
> +	boot_params->e820_entries = nr_entries;
> +
> +	return EFI_SUCCESS;
> +
> +free_mem_map:
> +	low_free(_size, (unsigned long)mem_map);
> +free_cmdline:
> +	if (options_size)
> +		low_free(options_size, hdr->cmd_line_ptr);
> +fail:
> +	return status;
> +}
> +
> +/*
> + * On success we return a pointer to a boot_params structure, and NULL
> + * on failure.
> + */
> +struct boot_params *efi_main(void *handle, efi_system_table_t *_table)
> +{
> +	struct boot_params *boot_params;
> +	unsigned long start, nr_pages;
> +	struct desc_ptr *gdt, *idt;
> +	efi_loaded_image_t *image;
> +	struct setup_header *hdr;
> +	efi_status_t status;
> +	efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
> +	struct desc_struct *desc;
> +
> +	sys_table = _table;
> +
> +	/* Check if we were booted by the EFI firmware */
> +	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
> +		goto fail;
> +
> +	status = efi_call_phys3(sys_table->boottime->handle_protocol,
> +				handle, &proto, (void *)&image);
> +	if (status != EFI_SUCCESS)
> +		goto fail;
> +
> +	status = low_alloc(0x4000, 1, (unsigned long *)&boot_params);
> +	if (status != EFI_SUCCESS)
> +		goto fail;
> +
> +	memset(boot_params, 0x0, 0x4000);
> +
> +	/* Copy first two sectors to boot_params */
> +	memcpy(boot_params, image->image_base, 1024);
> +
> +	hdr = &boot_params->hdr;
> +
> +	/*
> +	 * The EFI firmware loader could have placed the kernel image
> +	 * anywhere in memory, but the kernel has various restrictions
> +	 * on the max physical address it can run at. Attempt to move
> +	 * the kernel to boot_params.pref_address, or as low as
> +	 * possible.
> +	 */
> +	start = hdr->pref_address;
> +	nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
> +
> +	status = efi_call_phys4(sys_table->boottime->allocate_pages,
> +				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
> +				nr_pages, &start);
> +	if (status != EFI_SUCCESS) {
> +		status = low_alloc(hdr->init_size, hdr->kernel_alignment,
> +				   &start);
> +		if (status != EFI_SUCCESS)
> +			goto fail;
> +	}
> +
> +	hdr->code32_start = (__u32)start;
> +	hdr->pref_address = (__u64)(unsigned long)image->image_base;
> +
> +	memcpy((void *)start, image->image_base, image->image_size);
> +
> +	status = efi_call_phys3(sys_table->boottime->allocate_pool,
> +				EFI_LOADER_DATA, sizeof(*gdt),
> +				(void **)&gdt);
> +	if (status != EFI_SUCCESS)
> +		goto fail;
> +
> +	gdt->size = 0x800;
> +	status = low_alloc(gdt->size, 8, (unsigned long *)&gdt->address);
> +	if (status != EFI_SUCCESS)
> +		goto fail;
> +
> +	status = efi_call_phys3(sys_table->boottime->allocate_pool,
> +				EFI_LOADER_DATA, sizeof(*idt),
> +				(void **)&idt);
> +	if (status != EFI_SUCCESS)
> +		goto fail;
> +
> +	idt->size = 0;
> +	idt->address = 0;
> +
> +	status = make_boot_params(boot_params, image, handle);
> +	if (status != EFI_SUCCESS)
> +		goto fail;
> +
> +	memset((char *)gdt->address, 0x0, gdt->size);
> +	desc = (struct desc_struct *)gdt->address;
> +
> +	/* The first GDT is a dummy and the second is unused. */
> +	desc += 2;
> +
> +	desc->limit0 = 0xffff;
> +	desc->base0 = 0x0000;
> +	desc->base1 = 0x0000;
> +	desc->type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ;
> +	desc->s = DESC_TYPE_CODE_DATA;
> +	desc->dpl = 0;
> +	desc->p = 1;
> +	desc->limit = 0xf;
> +	desc->avl = 0;
> +	desc->l = 0;
> +	desc->d = SEG_OP_SIZE_32BIT;
> +	desc->g = SEG_GRANULARITY_4KB;
> +	desc->base2 = 0x00;
> +
> +	desc++;
> +	desc->limit0 = 0xffff;
> +	desc->base0 = 0x0000;
> +	desc->base1 = 0x0000;
> +	desc->type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE;
> +	desc->s = DESC_TYPE_CODE_DATA;
> +	desc->dpl = 0;
> +	desc->p = 1;
> +	desc->limit = 0xf;
> +	desc->avl = 0;
> +	desc->l = 0;
> +	desc->d = SEG_OP_SIZE_32BIT;
> +	desc->g = SEG_GRANULARITY_4KB;
> +	desc->base2 = 0x00;
> +
> +#ifdef CONFIG_X86_64
> +	/* Task segment value */
> +	desc++;
> +	desc->limit0 = 0x0000;
> +	desc->base0 = 0x0000;
> +	desc->base1 = 0x0000;
> +	desc->type = SEG_TYPE_TSS;
> +	desc->s = 0;
> +	desc->dpl = 0;
> +	desc->p = 1;
> +	desc->limit = 0x0;
> +	desc->avl = 0;
> +	desc->l = 0;
> +	desc->d = 0;
> +	desc->g = SEG_GRANULARITY_4KB;
> +	desc->base2 = 0x00;
> +#endif /* CONFIG_X86_64 */
> +
> +	asm volatile ("lidt %0" :: "m" (*idt));
> +	asm volatile ("lgdt %0" :: "m" (*gdt));
> +
> +	asm volatile("cli");
> +
> +	return boot_params;
> +fail:
> +	return NULL;
> +}
> diff --git a/arch/x86/boot/compressed/efi_stub_32.S b/arch/x86/boot/compressed/efi_stub_32.S
> new file mode 100644
> index 0000000..5047cd9
> --- /dev/null
> +++ b/arch/x86/boot/compressed/efi_stub_32.S
> @@ -0,0 +1,87 @@
> +/*
> + * EFI call stub for IA32.
> + *
> + * This stub allows us to make EFI calls in physical mode with interrupts
> + * turned off. Note that this implementation is different from the one in
> + * arch/x86/platform/efi/efi_stub_32.S because we're _already_ in physical
> + * mode at this point.
> + */
> +
> +#include <linux/linkage.h>
> +#include <asm/page_types.h>
> +
> +/*
> + * efi_call_phys(void *, ...) is a function with variable parameters.
> + * All the callers of this function assure that all the parameters are 4-bytes.
> + */
> +
> +/*
> + * In gcc calling convention, EBX, ESP, EBP, ESI and EDI are all callee save.
> + * So we'd better save all of them at the beginning of this function and restore
> + * at the end no matter how many we use, because we can not assure EFI runtime
> + * service functions will comply with gcc calling convention, too.
> + */
> +
> +.text
> +ENTRY(efi_call_phys)
> +	/*
> +	 * 0. The function can only be called in Linux kernel. So CS has been
> +	 * set to 0x0010, DS and SS have been set to 0x0018. In EFI, I found
> +	 * the values of these registers are the same. And, the corresponding
> +	 * GDT entries are identical. So I will do nothing about segment reg
> +	 * and GDT, but change GDT base register in prelog and epilog.
> +	 */
> +
> +	/*
> +	 * 1. Because we haven't been relocated by this point we need to
> +	 * use relative addressing.
> +	 */
> +	call	1f
> +1:	popl	%edx
> +	subl	$1b, %edx
> +
> +	/*
> +	 * 2. Now on the top of stack is the return
> +	 * address in the caller of efi_call_phys(), then parameter 1,
> +	 * parameter 2, ..., param n. To make things easy, we save the return
> +	 * address of efi_call_phys in a global variable.
> +	 */
> +	popl	%ecx
> +	movl	%ecx, saved_return_addr(%edx)
> +	/* get the function pointer into ECX*/
> +	popl	%ecx
> +	movl	%ecx, efi_rt_function_ptr(%edx)
> +
> +	/*
> +	 * 3. Call the physical function.
> +	 */
> +	call	*%ecx
> +
> +	/*
> +	 * 4. Balance the stack. And because EAX contain the return value,
> +	 * we'd better not clobber it. We need to calculate our address
> +	 * again because %ecx and %edx are not preserved across EFI function
> +	 * calls.
> +	 */
> +	call	1f
> +1:	popl	%edx
> +	subl	$1b, %edx
> +
> +	movl	efi_rt_function_ptr(%edx), %ecx
> +	pushl	%ecx
> +
> +	/*
> +	 * 10. Push the saved return address onto the stack and return.
> +	 */
> +	movl	saved_return_addr(%edx), %ecx
> +	pushl	%ecx
> +	ret
> +ENDPROC(efi_call_phys)
> +.previous
> +
> +.data
> +saved_return_addr:
> +	.long 0
> +efi_rt_function_ptr:
> +	.long 0
> +
> diff --git a/arch/x86/boot/compressed/efi_stub_64.S b/arch/x86/boot/compressed/efi_stub_64.S
> new file mode 100644
> index 0000000..cedc60d
> --- /dev/null
> +++ b/arch/x86/boot/compressed/efi_stub_64.S
> @@ -0,0 +1 @@
> +#include "../../platform/efi/efi_stub_64.S"
> diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
> index 67a655a..a055993 100644
> --- a/arch/x86/boot/compressed/head_32.S
> +++ b/arch/x86/boot/compressed/head_32.S
> @@ -32,6 +32,28 @@
>  
>  	__HEAD
>  ENTRY(startup_32)
> +#ifdef CONFIG_EFI_STUB
> +	/*
> +	 * We don't need the return address, so set up the stack so
> +	 * efi_main() can find its arugments.
> +	 */
> +	add	$0x4, %esp
> +
> +	call	efi_main
> +	cmpl	$0, %eax
> +	je	preferred_addr
> +	movl	%eax, %esi
> +	call	1f
> +1:
> +	popl	%eax
> +	subl	$1b, %eax
> +	subl	BP_pref_address(%esi), %eax
> +	add	BP_code32_start(%esi), %eax
> +	leal	preferred_addr(%eax), %eax
> +	jmp	*%eax
> +
> +preferred_addr:
> +#endif
>  	cld
>  	/*
>  	 * Test KEEP_SEGMENTS flag to see if the bootloader is asking
> diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
> index 35af09d..558d76c 100644
> --- a/arch/x86/boot/compressed/head_64.S
> +++ b/arch/x86/boot/compressed/head_64.S
> @@ -199,6 +199,26 @@ ENTRY(startup_64)
>  	 * an identity mapped page table being provied that maps our
>  	 * entire text+data+bss and hopefully all of memory.
>  	 */
> +#ifdef CONFIG_EFI_STUB
> +	pushq	%rsi
> +	mov	%rcx, %rdi
> +	mov	%rdx, %rsi
> +	call	efi_main
> +	popq	%rsi
> +	cmpq	$0,%rax
> +	je	preferred_addr
> +	movq	%rax,%rsi
> +	call	1f
> +1:
> +	popq	%rax
> +	subq	$1b, %rax
> +	subq	BP_pref_address(%rsi), %rax
> +	add	BP_code32_start(%esi), %eax
> +	leaq	preferred_addr(%rax), %rax
> +	jmp	*%rax
> +
> +preferred_addr:
> +#endif
>  
>  	/* Setup data segments. */
>  	xorl	%eax, %eax
> diff --git a/arch/x86/boot/compressed/string.c b/arch/x86/boot/compressed/string.c
> index 19b3e69..ffb9c5c 100644
> --- a/arch/x86/boot/compressed/string.c
> +++ b/arch/x86/boot/compressed/string.c
> @@ -1,2 +1,11 @@
>  #include "misc.h"
> +
> +int memcmp(const void *s1, const void *s2, size_t len)
> +{
> +	u8 diff;
> +	asm("repe; cmpsb; setnz %0"
> +	    : "=qm" (diff), "+D" (s1), "+S" (s2), "+c" (len));
> +	return diff;
> +}
> +
>  #include "../string.c"
> diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S
> index 93e689f..c4756f6 100644
> --- a/arch/x86/boot/header.S
> +++ b/arch/x86/boot/header.S
> @@ -45,6 +45,11 @@ SYSSEG		= 0x1000		/* historical load address >> 4 */
>  
>  	.global bootsect_start
>  bootsect_start:
> +#ifdef CONFIG_EFI_STUB
> +	# "MZ", MS-DOS header
> +	.byte 0x4d
> +	.byte 0x5a
> +#endif
>  
>  	# Normalize the start address
>  	ljmp	$BOOTSEG, $start2
> @@ -79,6 +84,14 @@ bs_die:
>  	# invoke the BIOS reset code...
>  	ljmp	$0xf000,$0xfff0
>  
> +#ifdef CONFIG_EFI_STUB
> +	.org	0x3c
> +	#
> +	# Offset to the PE header.
> +	#
> +	.long	pe_header
> +#endif /* CONFIG_EFI_STUB */
> +
>  	.section ".bsdata", "a"
>  bugger_off_msg:
>  	.ascii	"Direct booting from floppy is no longer supported.\r\n"
> @@ -87,6 +100,141 @@ bugger_off_msg:
>  	.ascii	"Remove disk and press any key to reboot . . .\r\n"
>  	.byte	0
>  
> +#ifdef CONFIG_EFI_STUB
> +pe_header:
> +	.ascii	"PE"
> +	.word 	0
> +
> +coff_header:
> +#ifdef CONFIG_X86_32
> +	.word	0x14c				# i386
> +#else
> +	.word	0x8664				# x86-64
> +#endif
> +	.word	2				# nr_sections
> +	.long	0 				# TimeDateStamp
> +	.long	0				# PointerToSymbolTable
> +	.long	1				# NumberOfSymbols
> +	.word	section_table - optional_header	# SizeOfOptionalHeader
> +#ifdef CONFIG_X86_32
> +	.word	0x306				# Characteristics.
> +						# IMAGE_FILE_32BIT_MACHINE |
> +						# IMAGE_FILE_DEBUG_STRIPPED |
> +						# IMAGE_FILE_EXECUTABLE_IMAGE |
> +						# IMAGE_FILE_LINE_NUMS_STRIPPED
> +#else
> +	.word	0x206				# Characteristics
> +						# IMAGE_FILE_DEBUG_STRIPPED |
> +						# IMAGE_FILE_EXECUTABLE_IMAGE |
> +						# IMAGE_FILE_LINE_NUMS_STRIPPED
> +#endif
> +
> +optional_header:
> +#ifdef CONFIG_X86_32
> +	.word	0x10b				# PE32 format
> +#else
> +	.word	0x20b 				# PE32+ format
> +#endif
> +	.byte	0x02				# MajorLinkerVersion
> +	.byte	0x14				# MinorLinkerVersion
> +
> +	# Filled in by build.c
> +	.long	0				# SizeOfCode
> +
> +	.long	0				# SizeOfInitializedData
> +	.long	0				# SizeOfUninitializedData
> +
> +	# Filled in by build.c
> +	.long	0x0000				# AddressOfEntryPoint
> +
> +	.long	0x0000				# BaseOfCode
> +#ifdef CONFIG_X86_32
> +	.long	0				# data
> +#endif
> +
> +extra_header_fields:
> +#ifdef CONFIG_X86_32
> +	.long	0				# ImageBase
> +#else
> +	.quad	0				# ImageBase
> +#endif
> +	.long	0x1000				# SectionAlignment
> +	.long	0x200				# FileAlignment
> +	.word	0				# MajorOperatingSystemVersion
> +	.word	0				# MinorOperatingSystemVersion
> +	.word	0				# MajorImageVersion
> +	.word	0				# MinorImageVersion
> +	.word	0				# MajorSubsystemVersion
> +	.word	0				# MinorSubsystemVersion
> +	.long	0				# Win32VersionValue
> +
> +	#
> +	# The size of the bzImage is written in tools/build.c
> +	#
> +	.long	0				# SizeOfImage
> +
> +	.long	0x200				# SizeOfHeaders
> +	.long	0				# CheckSum
> +	.word	0xa				# Subsystem (EFI application)
> +	.word	0				# DllCharacteristics
> +#ifdef CONFIG_X86_32
> +	.long	0				# SizeOfStackReserve
> +	.long	0				# SizeOfStackCommit
> +	.long	0				# SizeOfHeapReserve
> +	.long	0				# SizeOfHeapCommit
> +#else
> +	.quad	0				# SizeOfStackReserve
> +	.quad	0				# SizeOfStackCommit
> +	.quad	0				# SizeOfHeapReserve
> +	.quad	0				# SizeOfHeapCommit
> +#endif
> +	.long	0				# LoaderFlags
> +	.long	0x1				# NumberOfRvaAndSizes
> +
> +	.quad	0				# ExportTable
> +	.quad	0				# ImportTable
> +	.quad	0				# ResourceTable
> +	.quad	0				# ExceptionTable
> +	.quad	0				# CertificationTable
> +	.quad	0				# BaseRelocationTable
> +
> +	# Section table
> +section_table:
> +	.ascii	".text"
> +	.byte	0
> +	.byte	0
> +	.byte	0
> +	.long	0
> +	.long	0x0				# startup_{32,64}
> +	.long	0				# Size of initialized data
> +						# on disk
> +	.long	0x0				# startup_{32,64}
> +	.long	0				# PointerToRelocations
> +	.long	0				# PointerToLineNumbers
> +	.word	0				# NumberOfRelocations
> +	.word	0				# NumberOfLineNumbers
> +	.long	0x60500020			# Characteristics (section flags)
> +
> +	#
> +	# The EFI application loader requires a relocation section
> +	# because EFI applications are relocatable and not having
> +	# this section seems to confuse it. But since we don't need
> +	# the loader to fixup any relocs for us just fill it with a
> +	# single dummy reloc.
> +	#
> +	.ascii	".reloc"
> +	.byte	0
> +	.byte	0
> +	.long	reloc_end - reloc_start
> +	.long	reloc_start
> +	.long	reloc_end - reloc_start		# SizeOfRawData
> +	.long	reloc_start			# PointerToRawData
> +	.long	0				# PointerToRelocations
> +	.long	0				# PointerToLineNumbers
> +	.word	0				# NumberOfRelocations
> +	.word	0				# NumberOfLineNumbers
> +	.long	0x42100040			# Characteristics (section flags)
> +#endif /* CONFIG_EFI_STUB */
>  
>  	# Kernel attributes; used by setup.  This is part 1 of the
>  	# header, from the old boot sector.
> @@ -318,3 +466,13 @@ die:
>  setup_corrupt:
>  	.byte	7
>  	.string	"No setup signature found...\n"
> +
> +	.data
> +dummy:	.long	0
> +
> +	.section .reloc
> +reloc_start:
> +	.long	dummy - reloc_start
> +	.long	10
> +	.word	0
> +reloc_end:
> diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c
> index 3cbc405..574dedf 100644
> --- a/arch/x86/boot/string.c
> +++ b/arch/x86/boot/string.c
> @@ -111,3 +111,38 @@ unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int bas
>  
>  	return result;
>  }
> +
> +/**
> + * strlen - Find the length of a string
> + * @s: The string to be sized
> + */
> +size_t strlen(const char *s)
> +{
> +	const char *sc;
> +
> +	for (sc = s; *sc != '\0'; ++sc)
> +		/* nothing */;
> +	return sc - s;
> +}
> +
> +/**
> + * strstr - Find the first substring in a %NUL terminated string
> + * @s1: The string to be searched
> + * @s2: The string to search for
> + */
> +char *strstr(const char *s1, const char *s2)
> +{
> +	size_t l1, l2;
> +
> +	l2 = strlen(s2);
> +	if (!l2)
> +		return (char *)s1;
> +	l1 = strlen(s1);
> +	while (l1 >= l2) {
> +		l1--;
> +		if (!memcmp(s1, s2, l2))
> +			return (char *)s1;
> +		s1++;
> +	}
> +	return NULL;
> +}
> diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c
> index fdc60a0..4e9bd6b 100644
> --- a/arch/x86/boot/tools/build.c
> +++ b/arch/x86/boot/tools/build.c
> @@ -135,6 +135,9 @@ static void usage(void)
>  
>  int main(int argc, char ** argv)
>  {
> +#ifdef CONFIG_EFI_STUB
> +	unsigned int file_sz, pe_header;
> +#endif
>  	unsigned int i, sz, setup_sectors;
>  	int c;
>  	u32 sys_size;
> @@ -194,6 +197,42 @@ int main(int argc, char ** argv)
>  	buf[0x1f6] = sys_size >> 16;
>  	buf[0x1f7] = sys_size >> 24;
>  
> +#ifdef CONFIG_EFI_STUB
> +	file_sz = sz + i + ((sys_size * 16) - sz);
> +
> +	pe_header = *(unsigned int *)&buf[0x3c];
> +
> +	/* Size of code */
> +	*(unsigned int *)&buf[pe_header + 0x1c] = file_sz;
> +
> +	/* Size of image */
> +	*(unsigned int *)&buf[pe_header + 0x50] = file_sz;
> +
> +#ifdef CONFIG_X86_32
> +	/* Address of entry point */
> +	*(unsigned int *)&buf[pe_header + 0x28] = i;
> +
> +	/* .text size */
> +	*(unsigned int *)&buf[pe_header + 0xb0] = file_sz;
> +
> +	/* .text size of initialised data */
> +	*(unsigned int *)&buf[pe_header + 0xb8] = file_sz;
> +#else
> +	/*
> +	 * Address of entry point. startup_32 is at the beginning and
> +	 * the 64-bit entry point (startup_64) is always 512 bytes
> +	 * after.
> +	 */
> +	*(unsigned int *)&buf[pe_header + 0x28] = i + 512;
> +
> +	/* .text size */
> +	*(unsigned int *)&buf[pe_header + 0xc0] = file_sz;
> +
> +	/* .text size of initialised data */
> +	*(unsigned int *)&buf[pe_header + 0xc8] = file_sz;
> +#endif /* CONFIG_X86_32 */
> +#endif /* CONFIG_EFI_STUB */
> +
>  	crc = partial_crc32(buf, i, crc);
>  	if (fwrite(buf, 1, i, stdout) != i)
>  		die("Writing setup failed");
> diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c
> index 4f13faf..68de2dc 100644
> --- a/arch/x86/kernel/asm-offsets.c
> +++ b/arch/x86/kernel/asm-offsets.c
> @@ -67,4 +67,6 @@ void common(void) {
>  	OFFSET(BP_hardware_subarch, boot_params, hdr.hardware_subarch);
>  	OFFSET(BP_version, boot_params, hdr.version);
>  	OFFSET(BP_kernel_alignment, boot_params, hdr.kernel_alignment);
> +	OFFSET(BP_pref_address, boot_params, hdr.pref_address);
> +	OFFSET(BP_code32_start, boot_params, hdr.code32_start);
>  }


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

* Re: [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-10-17 16:39   ` Maarten Lankhorst
@ 2011-10-17 18:28     ` Matt Fleming
  2011-10-18  6:09     ` Ingo Molnar
  1 sibling, 0 replies; 39+ messages in thread
From: Matt Fleming @ 2011-10-17 18:28 UTC (permalink / raw)
  To: Maarten Lankhorst
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Mike Waychison, Andi Kleen

On Mon, 2011-10-17 at 18:39 +0200, Maarten Lankhorst wrote:
> > +		if (options_size) {
> > +			if (options_size > hdr->cmdline_size)
> > +				options_size = hdr->cmdline_size;
> > +
> > +			options_size++;	/* NUL termination */
> > +
> > +			status = low_alloc(options_size, 1, &cmdline);
> > +			if (status != EFI_SUCCESS)
> > +				goto fail;
> Shouldn't this be options_size + 1?

Nope. Notice the options_size++ above the allocation.

> > +
> > +			s1 = (u8 *)(unsigned long)cmdline;
> > +			s2 = (u16 *)options;
> > +
> > +			for (i = 0; i < options_size - 1; i++)
> > +				*s1++ = *s2++;

And the options_size - 1 here.

> > +
> > +			*s1 = '\0';
> For this null character..

So this should all work OK.

-- 
Matt Fleming, Intel Open Source Technology Center


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

* Re: [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-10-17 10:40 ` [PATCH v5 10/10] x86, efi: EFI boot stub support Matt Fleming
  2011-10-17 16:39   ` Maarten Lankhorst
@ 2011-10-17 20:06   ` Maarten Lankhorst
  2011-11-15 18:51     ` Matt Fleming
  1 sibling, 1 reply; 39+ messages in thread
From: Maarten Lankhorst @ 2011-10-17 20:06 UTC (permalink / raw)
  To: Matt Fleming
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Matt Fleming, Mike Waychison, Andi Kleen

Hey Matt,

On 10/17/2011 12:40 PM, Matt Fleming wrote:
> From: Matt Fleming <matt.fleming@intel.com>
>
> There is currently a large divide between kernel development and the
> development of EFI boot loaders. The idea behind this patch is to give
> the kernel developers full control over the EFI boot process. As
> H. Peter Anvin put it,
>
> "The 'kernel carries its own stub' approach been very successful in
> dealing with BIOS, and would make a lot of sense to me for EFI as
> well."
>
> This patch introduces an EFI boot stub that allows an x86 bzImage to
> be loaded and executed by EFI firmware. The bzImage appears to the
> firmware as an EFI application. Luckily there are enough free bits
> within the bzImage header so that it can masquerade as an EFI
> application, thereby coercing the EFI firmware into loading it and
> jumping to its entry point. The beauty of this masquerading approach
> is that both BIOS and EFI boot loaders can still load and run the same
> bzImage, thereby allowing a single kernel image to work in any boot
> environment.
>
> The EFI boot stub supports multiple initrds, but they must exist on
> the same partition as the bzImage. Command-line arguments for the
> kernel can be appended after the bzImage name when run from the EFI
> shell, e.g.
>
> Shell> bzImage console=ttyS0 root=/dev/sdb initrd=initrd.img
Seems initrd is failing for me. Is there any limitation on the size of the initrd? The default one fedora generates for my kernel is 46mb. It loads the kernel, but it reboots before it gets to userspace. Loading the kernel and initrd with grub2-efi works. EFI booting without initrd works too.

Cheers,
Maarten

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

* Re: [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-10-17 16:39   ` Maarten Lankhorst
  2011-10-17 18:28     ` Matt Fleming
@ 2011-10-18  6:09     ` Ingo Molnar
  1 sibling, 0 replies; 39+ messages in thread
From: Ingo Molnar @ 2011-10-18  6:09 UTC (permalink / raw)
  To: Maarten Lankhorst
  Cc: Matt Fleming, H. Peter Anvin, Matthew Garrett, linux-kernel,
	Thomas Gleixner, x86, Matt Fleming, Mike Waychison, Andi Kleen


* Maarten Lankhorst <m.b.lankhorst@gmail.com> wrote:

[ a 833 lines of TOTALLY POINTLESS QUOTE ]

> > +			status = low_alloc(options_size, 1, &cmdline);
> > +			if (status != EFI_SUCCESS)
> > +				goto fail;
> Shouldn't this be options_size + 1?
> > +
> > +			s1 = (u8 *)(unsigned long)cmdline;
> > +			s2 = (u16 *)options;
> > +
> > +			for (i = 0; i < options_size - 1; i++)
> > +				*s1++ = *s2++;
> > +
> > +			*s1 = '\0';
> For this null character..
> > +		}

[ another 700+ lines of TOTALLY POINTLESS QUOTE ]

It took me 3 attempts to find your comments in the 1500 lines of 
email.

Please format your emails! If you quote on 20 lines of patch then you 
absolutely HAVE TO cut out the remaining 1500 lines you did not 
comment on ...

Thanks,

	Ingo

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

* Re: [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-10-17 20:06   ` Maarten Lankhorst
@ 2011-11-15 18:51     ` Matt Fleming
  2011-11-23  0:44       ` Maarten Lankhorst
  0 siblings, 1 reply; 39+ messages in thread
From: Matt Fleming @ 2011-11-15 18:51 UTC (permalink / raw)
  To: Maarten Lankhorst
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Mike Waychison, Andi Kleen

On Mon, 2011-10-17 at 22:06 +0200, Maarten Lankhorst wrote:
> Seems initrd is failing for me. Is there any limitation on the size of
> the initrd? The default one fedora generates for my kernel is 46mb. It
> loads the kernel, but it reboots before it gets to userspace. Loading
> the kernel and initrd with grub2-efi works. EFI booting without initrd
> works too.

Sorry it took me so long to reply to this! Could you try version 6 of
this patch?

>From 5f1a572e59915daaa263093f0a2f180c88fa8504 Mon Sep 17 00:00:00 2001
From: Matt Fleming <matt.fleming@intel.com>
Date: Wed, 20 Jul 2011 09:24:53 +0100
Subject: [PATCH v6] x86, efi: EFI boot stub support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

v6:

 - Try to allocate initrd memory just below hdr->inird_addr_max.

v5:

 - load_options_size is UTF-16, which needs dividing by 2 to convert
   to the corresponding ASCII size.

v4:

 - Don't read more than image->load_options_size

v3:

 - Fix following warnings when compiling CONFIG_EFI_STUB=n

   arch/x86/boot/tools/build.c: In function ‘main’:
   arch/x86/boot/tools/build.c:138:24: warning: unused variable ‘pe_header’
   arch/x86/boot/tools/build.c:138:15: warning: unused variable ‘file_sz’

 - As reported by Matthew Garrett, some Apple machines have GOPs that
   don't have hardware attached. We need to weed these out by
   searching for ones that handle the PCIIO protocol.

 - Don't allocate memory if no initrds are on cmdline
 - Don't trust image->load_options_size

Maarten Lankhorst noted:
 - Don't strip first argument when booted from efibootmgr
 - Don't allocate too much memory for cmdline
 - Don't update cmdline_size, the kernel considers it read-only
 - Don't accept '\n' for initrd names

v2:

 - File alignment was too large, was 8192 should be 512. Reported by
   Maarten Lankhorst on LKML.
 - Added UGA support for graphics
 - Use VIDEO_TYPE_EFI instead of hard-coded number.
 - Move linelength assignment until after we've assigned depth
 - Dynamically fill out AddressOfEntryPoint in tools/build.c
 - Don't use magic number for GDT/TSS stuff. Requested by Andi Kleen
 - The bzImage may need to be relocated as it may have been loaded at
   a high address address by the firmware. This was required to get my
   macbook booting because the firmware loaded it at 0x7cxxxxxx, which
   triggers this error in decompress_kernel(),

	if (heap > ((-__PAGE_OFFSET-(128<<20)-1) & 0x7fffffff))
		error("Destination address too large");

There is currently a large divide between kernel development and the
development of EFI boot loaders. The idea behind this patch is to give
the kernel developers full control over the EFI boot process. As
H. Peter Anvin put it,

"The 'kernel carries its own stub' approach been very successful in
dealing with BIOS, and would make a lot of sense to me for EFI as
well."

This patch introduces an EFI boot stub that allows an x86 bzImage to
be loaded and executed by EFI firmware. The bzImage appears to the
firmware as an EFI application. Luckily there are enough free bits
within the bzImage header so that it can masquerade as an EFI
application, thereby coercing the EFI firmware into loading it and
jumping to its entry point. The beauty of this masquerading approach
is that both BIOS and EFI boot loaders can still load and run the same
bzImage, thereby allowing a single kernel image to work in any boot
environment.

The EFI boot stub supports multiple initrds, but they must exist on
the same partition as the bzImage. Command-line arguments for the
kernel can be appended after the bzImage name when run from the EFI
shell, e.g.

Shell> bzImage console=ttyS0 root=/dev/sdb initrd=initrd.img

Cc: H. Peter Anvin <hpa@linux.intel.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Mike Waychison <mikew@google.com>
Cc: Matthew Garrett <mjg@redhat.com>
Tested-by: Henrik Rydberg <rydberg@euromail.se>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
---
 arch/x86/Kconfig                       |    7 +
 arch/x86/boot/compressed/Makefile      |   10 +-
 arch/x86/boot/compressed/eboot.c       | 1065 ++++++++++++++++++++++++++++++++
 arch/x86/boot/compressed/efi_stub_32.S |   87 +++
 arch/x86/boot/compressed/efi_stub_64.S |    1 +
 arch/x86/boot/compressed/head_32.S     |   22 +
 arch/x86/boot/compressed/head_64.S     |   20 +
 arch/x86/boot/compressed/string.c      |    9 +
 arch/x86/boot/header.S                 |  158 +++++
 arch/x86/boot/string.c                 |   35 +
 arch/x86/boot/tools/build.c            |   39 ++
 arch/x86/kernel/asm-offsets.c          |    2 +
 12 files changed, 1454 insertions(+), 1 deletions(-)
 create mode 100644 arch/x86/boot/compressed/eboot.c
 create mode 100644 arch/x86/boot/compressed/efi_stub_32.S
 create mode 100644 arch/x86/boot/compressed/efi_stub_64.S

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index cb9a104..05335ae 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1474,6 +1474,13 @@ config EFI
 	  resultant kernel should continue to boot on existing non-EFI
 	  platforms.
 
+config EFI_STUB
+       bool "EFI stub support"
+       depends on EFI
+       ---help---
+          This kernel feature allows a bzImage to be loaded directly
+	  by EFI firmware without the use of a bootloader.
+
 config SECCOMP
 	def_bool y
 	prompt "Enable seccomp to safely compute untrusted bytecode"
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 09664ef..b123b9a 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -23,7 +23,15 @@ LDFLAGS_vmlinux := -T
 
 hostprogs-y	:= mkpiggy
 
-$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o $(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o $(obj)/piggy.o FORCE
+VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \
+	$(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \
+	$(obj)/piggy.o
+
+ifeq ($(CONFIG_EFI_STUB), y)
+	VMLINUX_OBJS += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o
+endif
+
+$(obj)/vmlinux: $(VMLINUX_OBJS) FORCE
 	$(call if_changed,ld)
 	@:
 
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
new file mode 100644
index 0000000..8627a56
--- /dev/null
+++ b/arch/x86/boot/compressed/eboot.c
@@ -0,0 +1,1065 @@
+/* -----------------------------------------------------------------------
+ *
+ *   Copyright 2011 Intel Corporation; author Matt Fleming
+ *
+ *   This file is part of the Linux kernel, and is made available under
+ *   the terms of the GNU General Public License version 2.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <linux/efi.h>
+#include <asm/efi.h>
+#include <asm/setup.h>
+#include <asm/desc.h>
+
+#define SEG_TYPE_DATA		(0 << 3)
+#define SEG_TYPE_READ_WRITE	(1 << 1)
+#define SEG_TYPE_CODE		(1 << 3)
+#define SEG_TYPE_EXEC_READ	(1 << 1)
+#define SEG_TYPE_TSS		((1 << 3) | (1 << 0))
+#define SEG_OP_SIZE_32BIT	(1 << 0)
+#define SEG_GRANULARITY_4KB	(1 << 0)
+
+#define DESC_TYPE_CODE_DATA	(1 << 0)
+
+#define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
+
+#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR		0
+#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR		1
+#define PIXEL_BIT_MASK					2
+#define PIXEL_BLT_ONLY					3
+#define PIXEL_FORMAT_MAX				4
+
+typedef struct {
+	u32 red_mask;
+	u32 green_mask;
+	u32 blue_mask;
+	u32 reserved_mask;
+} efi_pixel_bitmask_t;
+
+typedef struct {
+	u32 version;
+	u32 horizontal_resolution;
+	u32 vertical_resolution;
+	int pixel_format;
+	efi_pixel_bitmask_t pixel_information;
+	u32 pixels_per_scan_line;
+} __attribute__((packed)) efi_graphics_output_mode_information_t;
+
+typedef struct {
+	u32 max_mode;
+	u32 mode;
+	unsigned long info;
+	unsigned long size_of_info;
+	u64 frame_buffer_base;
+	unsigned long frame_buffer_size;
+} __attribute__((packed)) efi_graphics_output_protocol_mode_t;
+
+typedef struct {
+	void *query_mode;
+	unsigned long set_mode;
+	unsigned long blt;
+	efi_graphics_output_protocol_mode_t *mode;
+} efi_graphics_output_protocol_t;
+
+typedef struct {
+	void *get_mode;
+	void *set_mode;
+	void *blt;
+} efi_uga_draw_protocol_t;
+
+static efi_system_table_t *sys_table;
+
+static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size,
+			      unsigned long *desc_size)
+{
+	efi_memory_desc_t *m = NULL;
+	efi_status_t status;
+	unsigned long key;
+	u32 desc_version;
+
+	*map_size = sizeof(*m) * 32;
+again:
+	/*
+	 * Add an additional efi_memory_desc_t because we're doing an
+	 * allocation which may be in a new descriptor region.
+	 */
+	*map_size += sizeof(*m);
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, *map_size, (void **)&m);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size,
+				m, &key, desc_size, &desc_version);
+	if (status == EFI_BUFFER_TOO_SMALL) {
+		efi_call_phys1(sys_table->boottime->free_pool, m);
+		goto again;
+	}
+
+	if (status != EFI_SUCCESS)
+		efi_call_phys1(sys_table->boottime->free_pool, m);
+
+fail:
+	*map = m;
+	return status;
+}
+
+/*
+ * Allocate at the highest possible address that is not above 'max'.
+ */
+static efi_status_t high_alloc(unsigned long size, unsigned long align,
+			      unsigned long *addr, unsigned long max)
+{
+	unsigned long map_size, desc_size;
+	efi_memory_desc_t *map;
+	efi_status_t status;
+	unsigned long nr_pages;
+	u64 max_addr = 0;
+	int i;
+
+	status = __get_map(&map, &map_size, &desc_size);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+again:
+	for (i = 0; i < map_size / desc_size; i++) {
+		efi_memory_desc_t *desc;
+		u64 start, end;
+
+		desc = (efi_memory_desc_t *)((unsigned long)map + (i * desc_size));
+		if (desc->type != EFI_CONVENTIONAL_MEMORY)
+			continue;
+
+		if (desc->num_pages < nr_pages)
+			continue;
+
+		start = desc->phys_addr;
+		end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
+
+		if ((start + size) > end || (start + size) > max)
+			continue;
+
+		if (end - size > max)
+			end = max;
+
+		if (round_down(end - size, align) < start)
+			continue;
+
+		start = round_down(end - size, align);
+
+		/*
+		 * Don't allocate at 0x0. It will confuse code that
+		 * checks pointers against NULL.
+		 */
+		if (start == 0x0)
+			continue;
+
+		if (start > max_addr)
+			max_addr = start;
+	}
+
+	if (!max_addr)
+		status = EFI_NOT_FOUND;
+	else {
+		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+					nr_pages, &max_addr);
+		if (status != EFI_SUCCESS) {
+			max = max_addr;
+			max_addr = 0;
+			goto again;
+		}
+
+		*addr = max_addr;
+	}
+
+free_pool:
+	efi_call_phys1(sys_table->boottime->free_pool, map);
+
+fail:
+	return status;
+}
+
+/*
+ * Allocate at the lowest possible address.
+ */
+static efi_status_t low_alloc(unsigned long size, unsigned long align,
+			      unsigned long *addr)
+{
+	unsigned long map_size, desc_size;
+	efi_memory_desc_t *map;
+	efi_status_t status;
+	unsigned long nr_pages;
+	int i;
+
+	status = __get_map(&map, &map_size, &desc_size);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+	for (i = 0; i < map_size / desc_size; i++) {
+		efi_memory_desc_t *desc;
+		u64 start, end;
+
+		desc = (efi_memory_desc_t *)((unsigned long)map + (i * desc_size));
+
+		if (desc->type != EFI_CONVENTIONAL_MEMORY)
+			continue;
+
+		if (desc->num_pages < nr_pages)
+			continue;
+
+		start = desc->phys_addr;
+		end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
+
+		/*
+		 * Don't allocate at 0x0. It will confuse code that
+		 * checks pointers against NULL. Skip the first 8
+		 * bytes so we start at a nice even number.
+		 */
+		if (start == 0x0)
+			start += 8;
+
+		start = round_up(start, align);
+		if ((start + size) > end)
+			continue;
+
+		status = efi_call_phys4(sys_table->boottime->allocate_pages,
+					EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+					nr_pages, &start);
+		if (status == EFI_SUCCESS) {
+			*addr = start;
+			break;
+		}
+	}
+
+	if (i == map_size / desc_size)
+		status = EFI_NOT_FOUND;
+
+free_pool:
+	efi_call_phys1(sys_table->boottime->free_pool, map);
+fail:
+	return status;
+}
+
+static void low_free(unsigned long size, unsigned long addr)
+{
+	unsigned long nr_pages;
+
+	nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+	efi_call_phys2(sys_table->boottime->free_pages, addr, size);
+}
+
+static void find_bits(unsigned long mask, u8 *pos, u8 *size)
+{
+        u8 first, len;
+
+	first = 0;
+	len = 0;
+
+	if (mask) {
+		while (!(mask & 0x1)) {
+			mask = mask >> 1;
+			first++;
+		}
+
+		while (mask & 0x1) {
+			mask = mask >> 1;
+			len++;
+		}
+	}
+
+        *pos = first;
+        *size = len;
+}
+
+/*
+ * See if we have Graphics Output Protocol
+ */
+static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
+			      unsigned long size)
+{
+	efi_graphics_output_protocol_t *gop, *first_gop;
+	efi_pixel_bitmask_t pixel_info;
+	unsigned long nr_gops;
+	efi_status_t status;
+	void **gop_handle;
+	u16 width, height;
+	u32 fb_base, fb_size;
+	u32 pixels_per_scan_line;
+	int pixel_format;
+	int i;
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, size, &gop_handle);
+	if (status != EFI_SUCCESS)
+		return status;
+
+	status = efi_call_phys5(sys_table->boottime->locate_handle,
+				EFI_LOCATE_BY_PROTOCOL, proto,
+				NULL, &size, gop_handle);
+	if (status != EFI_SUCCESS)
+		goto free_handle;
+
+	first_gop = NULL;
+
+	nr_gops = size / sizeof(void *);
+	for (i = 0; i < nr_gops; i++) {
+		efi_graphics_output_mode_information_t *info;
+		efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID;
+		void *pciio;
+		void *h = gop_handle[i];
+
+		status = efi_call_phys3(sys_table->boottime->handle_protocol,
+					h, proto, &gop);
+		if (status != EFI_SUCCESS)
+			continue;
+
+		efi_call_phys3(sys_table->boottime->handle_protocol,
+			       h, &pciio_proto, &pciio);
+
+		status = efi_call_phys4(gop->query_mode, gop,
+					gop->mode->mode, &size, &info);
+		if (status == EFI_SUCCESS && (!first_gop || pciio)) {
+			/*
+			 * Apple provide GOPs that are not backed by
+			 * real hardware (they're used to handle
+			 * multiple displays). The workaround is to
+			 * search for a GOP implementing the PCIIO
+			 * protocol, and if one isn't found, to just
+			 * fallback to the first GOP.
+			 */
+			width = info->horizontal_resolution;
+			height = info->vertical_resolution;
+			fb_base = gop->mode->frame_buffer_base;
+			fb_size = gop->mode->frame_buffer_size;
+			pixel_format = info->pixel_format;
+			pixel_info = info->pixel_information;
+			pixels_per_scan_line = info->pixels_per_scan_line;
+
+			/*
+			 * Once we've found a GOP supporting PCIIO,
+			 * don't bother looking any further.
+			 */
+			if (pciio)
+				break;
+
+			first_gop = gop;
+		}
+	}
+
+	/* Did we find any GOPs? */
+	if (!first_gop)
+		goto free_handle;
+
+	/* EFI framebuffer */
+	si->orig_video_isVGA = VIDEO_TYPE_EFI;
+
+	si->lfb_width = width;
+	si->lfb_height = height;
+	si->lfb_base = fb_base;
+	si->lfb_size = fb_size;
+	si->pages = 1;
+
+	if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
+		si->lfb_depth = 32;
+		si->lfb_linelength = pixels_per_scan_line * 4;
+		si->red_size = 8;
+		si->red_pos = 0;
+		si->green_size = 8;
+		si->green_pos = 8;
+		si->blue_size = 8;
+		si->blue_pos = 16;
+		si->rsvd_size = 8;
+		si->rsvd_pos = 24;
+	} else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
+		si->lfb_depth = 32;
+		si->lfb_linelength = pixels_per_scan_line * 4;
+		si->red_size = 8;
+		si->red_pos = 16;
+		si->green_size = 8;
+		si->green_pos = 8;
+		si->blue_size = 8;
+		si->blue_pos = 0;
+		si->rsvd_size = 8;
+		si->rsvd_pos = 24;
+	} else if (pixel_format == PIXEL_BIT_MASK) {
+		find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
+		find_bits(pixel_info.green_mask, &si->green_pos,
+			  &si->green_size);
+		find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
+		find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
+			  &si->rsvd_size);
+		si->lfb_depth = si->red_size + si->green_size +
+			si->blue_size + si->rsvd_size;
+		si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
+	} else {
+		si->lfb_depth = 4;
+		si->lfb_linelength = si->lfb_width / 2;
+		si->red_size = 0;
+		si->red_pos = 0;
+		si->green_size = 0;
+		si->green_pos = 0;
+		si->blue_size = 0;
+		si->blue_pos = 0;
+		si->rsvd_size = 0;
+		si->rsvd_pos = 0;
+	}
+
+free_handle:
+	efi_call_phys1(sys_table->boottime->free_pool, gop_handle);
+	return status;
+}
+
+/*
+ * See if we have Universal Graphics Adapter (UGA) protocol
+ */
+static efi_status_t setup_uga(struct screen_info *si, efi_guid_t *uga_proto,
+			      unsigned long size)
+{
+	efi_uga_draw_protocol_t *uga, *first_uga;
+	unsigned long nr_ugas;
+	efi_status_t status;
+	u32 width, height;
+	void **uga_handle = NULL;
+	int i;
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, size, &uga_handle);
+	if (status != EFI_SUCCESS)
+		return status;
+
+	status = efi_call_phys5(sys_table->boottime->locate_handle,
+				EFI_LOCATE_BY_PROTOCOL, uga_proto,
+				NULL, &size, uga_handle);
+	if (status != EFI_SUCCESS)
+		goto free_handle;
+
+	first_uga = NULL;
+
+	nr_ugas = size / sizeof(void *);
+	for (i = 0; i < nr_ugas; i++) {
+		efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID;
+		void *handle = uga_handle[i];
+		u32 w, h, depth, refresh;
+		void *pciio;
+
+		status = efi_call_phys3(sys_table->boottime->handle_protocol,
+					handle, uga_proto, &uga);
+		if (status != EFI_SUCCESS)
+			continue;
+
+		efi_call_phys3(sys_table->boottime->handle_protocol,
+			       handle, &pciio_proto, &pciio);
+
+		status = efi_call_phys5(uga->get_mode, uga, &w, &h,
+					&depth, &refresh);
+		if (status == EFI_SUCCESS && (!first_uga || pciio)) {
+			width = w;
+			height = h;
+
+			/*
+			 * Once we've found a UGA supporting PCIIO,
+			 * don't bother looking any further.
+			 */
+			if (pciio)
+				break;
+
+			first_uga = uga;
+		}
+	}
+
+	if (!first_uga)
+		goto free_handle;
+
+	/* EFI framebuffer */
+	si->orig_video_isVGA = VIDEO_TYPE_EFI;
+
+	si->lfb_depth = 32;
+	si->lfb_width = width;
+	si->lfb_height = height;
+
+	si->red_size = 8;
+	si->red_pos = 16;
+	si->green_size = 8;
+	si->green_pos = 8;
+	si->blue_size = 8;
+	si->blue_pos = 0;
+	si->rsvd_size = 8;
+	si->rsvd_pos = 24;
+
+
+free_handle:
+	efi_call_phys1(sys_table->boottime->free_pool, uga_handle);
+	return status;
+}
+
+void setup_graphics(struct boot_params *boot_params)
+{
+	efi_guid_t graphics_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+	struct screen_info *si;
+	efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID;
+	efi_status_t status;
+	unsigned long size;
+	void **gop_handle = NULL;
+	void **uga_handle = NULL;
+
+	si = &boot_params->screen_info;
+	memset(si, 0, sizeof(*si));
+
+	size = 0;
+	status = efi_call_phys5(sys_table->boottime->locate_handle,
+				EFI_LOCATE_BY_PROTOCOL, &graphics_proto,
+				NULL, &size, gop_handle);
+	if (status == EFI_BUFFER_TOO_SMALL)
+		status = setup_gop(si, &graphics_proto, size);
+
+	if (status != EFI_SUCCESS) {
+		size = 0;
+		status = efi_call_phys5(sys_table->boottime->locate_handle,
+					EFI_LOCATE_BY_PROTOCOL, &uga_proto,
+					NULL, &size, uga_handle);
+		if (status == EFI_BUFFER_TOO_SMALL)
+			setup_uga(si, &uga_proto, size);
+	}
+}
+
+struct initrd {
+	efi_file_handle_t *handle;
+	u64 size;
+};
+
+/*
+ * Check the cmdline for a LILO-style initrd= arguments.
+ *
+ * We only support loading an initrd from the same filesystem as the
+ * kernel image.
+ */
+static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
+				    struct setup_header *hdr)
+{
+	struct initrd *initrds;
+	unsigned long initrd_addr;
+	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
+	u64 initrd_total;
+	efi_file_io_interface_t *io;
+	efi_file_handle_t *fh;
+	efi_status_t status;
+	int nr_initrds;
+	char *str;
+	int i, j, k;
+
+	initrd_addr = 0;
+	initrd_total = 0;
+
+	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+
+	j = 0;			/* See close_handles */
+
+	if (!str || !*str)
+		return EFI_SUCCESS;
+
+	for (nr_initrds = 0; *str; nr_initrds++) {
+		str = strstr(str, "initrd=");
+		if (!str)
+			break;
+
+		str += 7;
+
+		/* Skip any leading slashes */
+		while (*str == '/' || *str == '\\')
+			str++;
+
+		while (*str && *str != ' ' && *str != '\n')
+			str++;
+	}
+
+	if (!nr_initrds)
+		return EFI_SUCCESS;
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA,
+				nr_initrds * sizeof(*initrds),
+				&initrds);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	str = (char *)(unsigned long)hdr->cmd_line_ptr;
+	for (i = 0; i < nr_initrds; i++) {
+		struct initrd *initrd;
+		efi_file_handle_t *h;
+		efi_file_info_t *info;
+		efi_char16_t filename[256];
+		unsigned long info_sz;
+		efi_guid_t info_guid = EFI_FILE_INFO_ID;
+		efi_char16_t *p;
+		u64 file_sz;
+
+		str = strstr(str, "initrd=");
+		if (!str)
+			break;
+
+		str += 7;
+
+		initrd = &initrds[i];
+		p = filename;
+
+		/* Skip any leading slashes */
+		while (*str == '/' || *str == '\\')
+			str++;
+
+		while (*str && *str != ' ' && *str != '\n') {
+			if (p >= filename + sizeof(filename))
+				break;
+
+			*p++ = *str++;
+		}
+
+		*p = '\0';
+
+		/* Only open the volume once. */
+		if (!i) {
+			efi_boot_services_t *boottime;
+
+			boottime = sys_table->boottime;
+
+			status = efi_call_phys3(boottime->handle_protocol,
+					image->device_handle, &fs_proto, &io);
+			if (status != EFI_SUCCESS)
+				goto free_initrds;
+
+			status = efi_call_phys2(io->open_volume, io, &fh);
+			if (status != EFI_SUCCESS)
+				goto free_initrds;
+		}
+
+		status = efi_call_phys5(fh->open, fh, &h, filename,
+					EFI_FILE_MODE_READ, (u64)0);
+		if (status != EFI_SUCCESS)
+			goto close_handles;
+
+		initrd->handle = h;
+
+		info_sz = 0;
+		status = efi_call_phys4(h->get_info, h, &info_guid,
+					&info_sz, NULL);
+		if (status != EFI_BUFFER_TOO_SMALL)
+			goto close_handles;
+
+grow:
+		status = efi_call_phys3(sys_table->boottime->allocate_pool,
+					EFI_LOADER_DATA, info_sz, &info);
+		if (status != EFI_SUCCESS)
+			goto close_handles;
+
+		status = efi_call_phys4(h->get_info, h, &info_guid,
+					&info_sz, info);
+		if (status == EFI_BUFFER_TOO_SMALL) {
+			efi_call_phys1(sys_table->boottime->free_pool, info);
+			goto grow;
+		}
+
+		file_sz = info->file_size;
+		efi_call_phys1(sys_table->boottime->free_pool, info);
+
+		if (status != EFI_SUCCESS)
+			goto close_handles;
+
+		initrd->size = file_sz;
+		initrd_total += file_sz;
+	}
+
+	if (initrd_total) {
+		unsigned long addr;
+
+		/*
+		 * Multiple initrd's need to be at consecutive
+		 * addresses in memory, so allocate enough memory for
+		 * all the initrd's.
+		 */
+		status = high_alloc(initrd_total, 0x1000,
+				   &initrd_addr, hdr->initrd_addr_max);
+		if (status != EFI_SUCCESS)
+			goto close_handles;
+
+		/* We've run out of free low memory. */
+		if (initrd_addr > hdr->initrd_addr_max) {
+			status = EFI_INVALID_PARAMETER;
+			goto free_initrd_total;
+		}
+
+		addr = initrd_addr;
+		for (j = 0; j < nr_initrds; j++) {
+			u64 size;
+
+			size = initrds[j].size;
+			status = efi_call_phys3(fh->read, initrds[j].handle,
+						&size, addr);
+			if (status != EFI_SUCCESS)
+				goto free_initrd_total;
+
+			efi_call_phys1(fh->close, initrds[j].handle);
+
+			addr += size;
+		}
+
+	}
+
+	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+
+	hdr->ramdisk_image = initrd_addr;
+	hdr->ramdisk_size = initrd_total;
+
+	return status;
+
+free_initrd_total:
+	low_free(initrd_total, initrd_addr);
+
+close_handles:
+	for (k = j; k < nr_initrds; k++)
+		efi_call_phys1(fh->close, initrds[k].handle);
+free_initrds:
+	efi_call_phys1(sys_table->boottime->free_pool, initrds);
+fail:
+	hdr->ramdisk_image = 0;
+	hdr->ramdisk_size = 0;
+
+	return status;
+}
+
+/*
+ * Because the x86 boot code expects to be passed a boot_params we
+ * need to create one ourselves (usually the bootloader would create
+ * one for us).
+ */
+static efi_status_t make_boot_params(struct boot_params *boot_params,
+				     efi_loaded_image_t *image,
+				     void *handle)
+{
+	struct efi_info *efi = &boot_params->efi_info;
+	struct apm_bios_info *bi = &boot_params->apm_bios_info;
+	struct sys_desc_table *sdt = &boot_params->sys_desc_table;
+	struct e820entry *e820_map = &boot_params->e820_map[0];
+	struct e820entry *prev = NULL;
+	struct setup_header *hdr = &boot_params->hdr;
+	unsigned long size, key, desc_size, _size;
+	efi_memory_desc_t *mem_map;
+	void *options = image->load_options;
+	u32 load_options_size = image->load_options_size / 2; /* ASCII */
+	int options_size = 0;
+	efi_status_t status;
+	__u32 desc_version;
+	unsigned long cmdline;
+	u8 nr_entries;
+	u16 *s2;
+	u8 *s1;
+	int i;
+
+	hdr->type_of_loader = 0x21;
+
+	/* Convert unicode cmdline to ascii */
+	cmdline = 0;
+	s2 = (u16 *)options;
+
+	if (s2) {
+		while (*s2 && *s2 != '\n' && options_size < load_options_size) {
+			s2++;
+			options_size++;
+		}
+
+		if (options_size) {
+			if (options_size > hdr->cmdline_size)
+				options_size = hdr->cmdline_size;
+
+			options_size++;	/* NUL termination */
+
+			status = low_alloc(options_size, 1, &cmdline);
+			if (status != EFI_SUCCESS)
+				goto fail;
+
+			s1 = (u8 *)(unsigned long)cmdline;
+			s2 = (u16 *)options;
+
+			for (i = 0; i < options_size - 1; i++)
+				*s1++ = *s2++;
+
+			*s1 = '\0';
+		}
+	}
+
+	hdr->cmd_line_ptr = cmdline;
+
+	hdr->ramdisk_image = 0;
+	hdr->ramdisk_size = 0;
+
+	status = handle_ramdisks(image, hdr);
+	if (status != EFI_SUCCESS)
+		goto free_cmdline;
+
+	setup_graphics(boot_params);
+
+	/* Clear APM BIOS info */
+	memset(bi, 0, sizeof(*bi));
+
+	memset(sdt, 0, sizeof(*sdt));
+
+	memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
+
+	size = sizeof(*mem_map) * 32;
+
+again:
+	size += sizeof(*mem_map);
+	_size = size;
+	status = low_alloc(size, 1, (unsigned long *)&mem_map);
+	if (status != EFI_SUCCESS)
+		goto free_cmdline;
+
+	status = efi_call_phys5(sys_table->boottime->get_memory_map, &size,
+				mem_map, &key, &desc_size, &desc_version);
+	if (status == EFI_BUFFER_TOO_SMALL) {
+		low_free(_size, (unsigned long)mem_map);
+		goto again;
+	}
+
+	if (status != EFI_SUCCESS)
+		goto free_mem_map;
+
+	efi->efi_systab = (unsigned long)sys_table;
+	efi->efi_memdesc_size = desc_size;
+	efi->efi_memdesc_version = desc_version;
+	efi->efi_memmap = (unsigned long)mem_map;
+	efi->efi_memmap_size = size;
+
+#ifdef CONFIG_X86_64
+	efi->efi_systab_hi = (unsigned long)sys_table >> 32;
+	efi->efi_memmap_hi = (unsigned long)mem_map >> 32;
+#endif
+
+	/* Might as well exit boot services now */
+	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
+				handle, key);
+	if (status != EFI_SUCCESS)
+		goto free_mem_map;
+
+	/* Historic? */
+	boot_params->alt_mem_k = 32 * 1024;
+
+	/*
+	 * Convert the EFI memory map to E820.
+	 */
+	nr_entries = 0;
+	for (i = 0; i < size / desc_size; i++) {
+		efi_memory_desc_t *d;
+		unsigned int e820_type = 0;
+
+		d = (efi_memory_desc_t *)((unsigned long)mem_map + (i * desc_size));
+		switch(d->type) {
+		case EFI_RESERVED_TYPE:
+		case EFI_RUNTIME_SERVICES_CODE:
+		case EFI_RUNTIME_SERVICES_DATA:
+		case EFI_MEMORY_MAPPED_IO:
+		case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
+		case EFI_PAL_CODE:
+			e820_type = E820_RESERVED;
+			break;
+
+		case EFI_UNUSABLE_MEMORY:
+			e820_type = E820_UNUSABLE;
+			break;
+
+		case EFI_ACPI_RECLAIM_MEMORY:
+			e820_type = E820_ACPI;
+			break;
+
+		case EFI_LOADER_CODE:
+		case EFI_LOADER_DATA:
+		case EFI_BOOT_SERVICES_CODE:
+		case EFI_BOOT_SERVICES_DATA:
+		case EFI_CONVENTIONAL_MEMORY:
+			e820_type = E820_RAM;
+			break;
+
+		case EFI_ACPI_MEMORY_NVS:
+			e820_type = E820_NVS;
+			break;
+
+		default:
+			continue;
+		}
+
+		/* Merge adjacent mappings */
+		if (prev && prev->type == e820_type &&
+		    (prev->addr + prev->size) == d->phys_addr)
+			prev->size += d->num_pages << 12;
+		else {
+			e820_map->addr = d->phys_addr;
+			e820_map->size = d->num_pages << 12;
+			e820_map->type = e820_type;
+			prev = e820_map++;
+			nr_entries++;
+		}
+	}
+
+	boot_params->e820_entries = nr_entries;
+
+	return EFI_SUCCESS;
+
+free_mem_map:
+	low_free(_size, (unsigned long)mem_map);
+free_cmdline:
+	if (options_size)
+		low_free(options_size, hdr->cmd_line_ptr);
+fail:
+	return status;
+}
+
+/*
+ * On success we return a pointer to a boot_params structure, and NULL
+ * on failure.
+ */
+struct boot_params *efi_main(void *handle, efi_system_table_t *_table)
+{
+	struct boot_params *boot_params;
+	unsigned long start, nr_pages;
+	struct desc_ptr *gdt, *idt;
+	efi_loaded_image_t *image;
+	struct setup_header *hdr;
+	efi_status_t status;
+	efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
+	struct desc_struct *desc;
+
+	sys_table = _table;
+
+	/* Check if we were booted by the EFI firmware */
+	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+		goto fail;
+
+	status = efi_call_phys3(sys_table->boottime->handle_protocol,
+				handle, &proto, (void *)&image);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	status = low_alloc(0x4000, 1, (unsigned long *)&boot_params);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	memset(boot_params, 0x0, 0x4000);
+
+	/* Copy first two sectors to boot_params */
+	memcpy(boot_params, image->image_base, 1024);
+
+	hdr = &boot_params->hdr;
+
+	/*
+	 * The EFI firmware loader could have placed the kernel image
+	 * anywhere in memory, but the kernel has various restrictions
+	 * on the max physical address it can run at. Attempt to move
+	 * the kernel to boot_params.pref_address, or as low as
+	 * possible.
+	 */
+	start = hdr->pref_address;
+	nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+
+	status = efi_call_phys4(sys_table->boottime->allocate_pages,
+				EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+				nr_pages, &start);
+	if (status != EFI_SUCCESS) {
+		status = low_alloc(hdr->init_size, hdr->kernel_alignment,
+				   &start);
+		if (status != EFI_SUCCESS)
+			goto fail;
+	}
+
+	hdr->code32_start = (__u32)start;
+	hdr->pref_address = (__u64)(unsigned long)image->image_base;
+
+	memcpy((void *)start, image->image_base, image->image_size);
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, sizeof(*gdt),
+				(void **)&gdt);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	gdt->size = 0x800;
+	status = low_alloc(gdt->size, 8, (unsigned long *)&gdt->address);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, sizeof(*idt),
+				(void **)&idt);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	idt->size = 0;
+	idt->address = 0;
+
+	status = make_boot_params(boot_params, image, handle);
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	memset((char *)gdt->address, 0x0, gdt->size);
+	desc = (struct desc_struct *)gdt->address;
+
+	/* The first GDT is a dummy and the second is unused. */
+	desc += 2;
+
+	desc->limit0 = 0xffff;
+	desc->base0 = 0x0000;
+	desc->base1 = 0x0000;
+	desc->type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ;
+	desc->s = DESC_TYPE_CODE_DATA;
+	desc->dpl = 0;
+	desc->p = 1;
+	desc->limit = 0xf;
+	desc->avl = 0;
+	desc->l = 0;
+	desc->d = SEG_OP_SIZE_32BIT;
+	desc->g = SEG_GRANULARITY_4KB;
+	desc->base2 = 0x00;
+
+	desc++;
+	desc->limit0 = 0xffff;
+	desc->base0 = 0x0000;
+	desc->base1 = 0x0000;
+	desc->type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE;
+	desc->s = DESC_TYPE_CODE_DATA;
+	desc->dpl = 0;
+	desc->p = 1;
+	desc->limit = 0xf;
+	desc->avl = 0;
+	desc->l = 0;
+	desc->d = SEG_OP_SIZE_32BIT;
+	desc->g = SEG_GRANULARITY_4KB;
+	desc->base2 = 0x00;
+
+#ifdef CONFIG_X86_64
+	/* Task segment value */
+	desc++;
+	desc->limit0 = 0x0000;
+	desc->base0 = 0x0000;
+	desc->base1 = 0x0000;
+	desc->type = SEG_TYPE_TSS;
+	desc->s = 0;
+	desc->dpl = 0;
+	desc->p = 1;
+	desc->limit = 0x0;
+	desc->avl = 0;
+	desc->l = 0;
+	desc->d = 0;
+	desc->g = SEG_GRANULARITY_4KB;
+	desc->base2 = 0x00;
+#endif /* CONFIG_X86_64 */
+
+	asm volatile ("lidt %0" :: "m" (*idt));
+	asm volatile ("lgdt %0" :: "m" (*gdt));
+
+	asm volatile("cli");
+
+	return boot_params;
+fail:
+	return NULL;
+}
diff --git a/arch/x86/boot/compressed/efi_stub_32.S b/arch/x86/boot/compressed/efi_stub_32.S
new file mode 100644
index 0000000..5047cd9
--- /dev/null
+++ b/arch/x86/boot/compressed/efi_stub_32.S
@@ -0,0 +1,87 @@
+/*
+ * EFI call stub for IA32.
+ *
+ * This stub allows us to make EFI calls in physical mode with interrupts
+ * turned off. Note that this implementation is different from the one in
+ * arch/x86/platform/efi/efi_stub_32.S because we're _already_ in physical
+ * mode at this point.
+ */
+
+#include <linux/linkage.h>
+#include <asm/page_types.h>
+
+/*
+ * efi_call_phys(void *, ...) is a function with variable parameters.
+ * All the callers of this function assure that all the parameters are 4-bytes.
+ */
+
+/*
+ * In gcc calling convention, EBX, ESP, EBP, ESI and EDI are all callee save.
+ * So we'd better save all of them at the beginning of this function and restore
+ * at the end no matter how many we use, because we can not assure EFI runtime
+ * service functions will comply with gcc calling convention, too.
+ */
+
+.text
+ENTRY(efi_call_phys)
+	/*
+	 * 0. The function can only be called in Linux kernel. So CS has been
+	 * set to 0x0010, DS and SS have been set to 0x0018. In EFI, I found
+	 * the values of these registers are the same. And, the corresponding
+	 * GDT entries are identical. So I will do nothing about segment reg
+	 * and GDT, but change GDT base register in prelog and epilog.
+	 */
+
+	/*
+	 * 1. Because we haven't been relocated by this point we need to
+	 * use relative addressing.
+	 */
+	call	1f
+1:	popl	%edx
+	subl	$1b, %edx
+
+	/*
+	 * 2. Now on the top of stack is the return
+	 * address in the caller of efi_call_phys(), then parameter 1,
+	 * parameter 2, ..., param n. To make things easy, we save the return
+	 * address of efi_call_phys in a global variable.
+	 */
+	popl	%ecx
+	movl	%ecx, saved_return_addr(%edx)
+	/* get the function pointer into ECX*/
+	popl	%ecx
+	movl	%ecx, efi_rt_function_ptr(%edx)
+
+	/*
+	 * 3. Call the physical function.
+	 */
+	call	*%ecx
+
+	/*
+	 * 4. Balance the stack. And because EAX contain the return value,
+	 * we'd better not clobber it. We need to calculate our address
+	 * again because %ecx and %edx are not preserved across EFI function
+	 * calls.
+	 */
+	call	1f
+1:	popl	%edx
+	subl	$1b, %edx
+
+	movl	efi_rt_function_ptr(%edx), %ecx
+	pushl	%ecx
+
+	/*
+	 * 10. Push the saved return address onto the stack and return.
+	 */
+	movl	saved_return_addr(%edx), %ecx
+	pushl	%ecx
+	ret
+ENDPROC(efi_call_phys)
+.previous
+
+.data
+saved_return_addr:
+	.long 0
+efi_rt_function_ptr:
+	.long 0
+
diff --git a/arch/x86/boot/compressed/efi_stub_64.S b/arch/x86/boot/compressed/efi_stub_64.S
new file mode 100644
index 0000000..cedc60d
--- /dev/null
+++ b/arch/x86/boot/compressed/efi_stub_64.S
@@ -0,0 +1 @@
+#include "../../platform/efi/efi_stub_64.S"
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 67a655a..a055993 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -32,6 +32,28 @@
 
 	__HEAD
 ENTRY(startup_32)
+#ifdef CONFIG_EFI_STUB
+	/*
+	 * We don't need the return address, so set up the stack so
+	 * efi_main() can find its arugments.
+	 */
+	add	$0x4, %esp
+
+	call	efi_main
+	cmpl	$0, %eax
+	je	preferred_addr
+	movl	%eax, %esi
+	call	1f
+1:
+	popl	%eax
+	subl	$1b, %eax
+	subl	BP_pref_address(%esi), %eax
+	add	BP_code32_start(%esi), %eax
+	leal	preferred_addr(%eax), %eax
+	jmp	*%eax
+
+preferred_addr:
+#endif
 	cld
 	/*
 	 * Test KEEP_SEGMENTS flag to see if the bootloader is asking
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 35af09d..558d76c 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -199,6 +199,26 @@ ENTRY(startup_64)
 	 * an identity mapped page table being provied that maps our
 	 * entire text+data+bss and hopefully all of memory.
 	 */
+#ifdef CONFIG_EFI_STUB
+	pushq	%rsi
+	mov	%rcx, %rdi
+	mov	%rdx, %rsi
+	call	efi_main
+	popq	%rsi
+	cmpq	$0,%rax
+	je	preferred_addr
+	movq	%rax,%rsi
+	call	1f
+1:
+	popq	%rax
+	subq	$1b, %rax
+	subq	BP_pref_address(%rsi), %rax
+	add	BP_code32_start(%esi), %eax
+	leaq	preferred_addr(%rax), %rax
+	jmp	*%rax
+
+preferred_addr:
+#endif
 
 	/* Setup data segments. */
 	xorl	%eax, %eax
diff --git a/arch/x86/boot/compressed/string.c b/arch/x86/boot/compressed/string.c
index 19b3e69..ffb9c5c 100644
--- a/arch/x86/boot/compressed/string.c
+++ b/arch/x86/boot/compressed/string.c
@@ -1,2 +1,11 @@
 #include "misc.h"
+
+int memcmp(const void *s1, const void *s2, size_t len)
+{
+	u8 diff;
+	asm("repe; cmpsb; setnz %0"
+	    : "=qm" (diff), "+D" (s1), "+S" (s2), "+c" (len));
+	return diff;
+}
+
 #include "../string.c"
diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S
index bdb4d45..f1bbeeb 100644
--- a/arch/x86/boot/header.S
+++ b/arch/x86/boot/header.S
@@ -45,6 +45,11 @@ SYSSEG		= 0x1000		/* historical load address >> 4 */
 
 	.global bootsect_start
 bootsect_start:
+#ifdef CONFIG_EFI_STUB
+	# "MZ", MS-DOS header
+	.byte 0x4d
+	.byte 0x5a
+#endif
 
 	# Normalize the start address
 	ljmp	$BOOTSEG, $start2
@@ -79,6 +84,14 @@ bs_die:
 	# invoke the BIOS reset code...
 	ljmp	$0xf000,$0xfff0
 
+#ifdef CONFIG_EFI_STUB
+	.org	0x3c
+	#
+	# Offset to the PE header.
+	#
+	.long	pe_header
+#endif /* CONFIG_EFI_STUB */
+
 	.section ".bsdata", "a"
 bugger_off_msg:
 	.ascii	"Direct booting from floppy is no longer supported.\r\n"
@@ -87,6 +100,141 @@ bugger_off_msg:
 	.ascii	"Remove disk and press any key to reboot . . .\r\n"
 	.byte	0
 
+#ifdef CONFIG_EFI_STUB
+pe_header:
+	.ascii	"PE"
+	.word 	0
+
+coff_header:
+#ifdef CONFIG_X86_32
+	.word	0x14c				# i386
+#else
+	.word	0x8664				# x86-64
+#endif
+	.word	2				# nr_sections
+	.long	0 				# TimeDateStamp
+	.long	0				# PointerToSymbolTable
+	.long	1				# NumberOfSymbols
+	.word	section_table - optional_header	# SizeOfOptionalHeader
+#ifdef CONFIG_X86_32
+	.word	0x306				# Characteristics.
+						# IMAGE_FILE_32BIT_MACHINE |
+						# IMAGE_FILE_DEBUG_STRIPPED |
+						# IMAGE_FILE_EXECUTABLE_IMAGE |
+						# IMAGE_FILE_LINE_NUMS_STRIPPED
+#else
+	.word	0x206				# Characteristics
+						# IMAGE_FILE_DEBUG_STRIPPED |
+						# IMAGE_FILE_EXECUTABLE_IMAGE |
+						# IMAGE_FILE_LINE_NUMS_STRIPPED
+#endif
+
+optional_header:
+#ifdef CONFIG_X86_32
+	.word	0x10b				# PE32 format
+#else
+	.word	0x20b 				# PE32+ format
+#endif
+	.byte	0x02				# MajorLinkerVersion
+	.byte	0x14				# MinorLinkerVersion
+
+	# Filled in by build.c
+	.long	0				# SizeOfCode
+
+	.long	0				# SizeOfInitializedData
+	.long	0				# SizeOfUninitializedData
+
+	# Filled in by build.c
+	.long	0x0000				# AddressOfEntryPoint
+
+	.long	0x0000				# BaseOfCode
+#ifdef CONFIG_X86_32
+	.long	0				# data
+#endif
+
+extra_header_fields:
+#ifdef CONFIG_X86_32
+	.long	0				# ImageBase
+#else
+	.quad	0				# ImageBase
+#endif
+	.long	0x1000				# SectionAlignment
+	.long	0x200				# FileAlignment
+	.word	0				# MajorOperatingSystemVersion
+	.word	0				# MinorOperatingSystemVersion
+	.word	0				# MajorImageVersion
+	.word	0				# MinorImageVersion
+	.word	0				# MajorSubsystemVersion
+	.word	0				# MinorSubsystemVersion
+	.long	0				# Win32VersionValue
+
+	#
+	# The size of the bzImage is written in tools/build.c
+	#
+	.long	0				# SizeOfImage
+
+	.long	0x200				# SizeOfHeaders
+	.long	0				# CheckSum
+	.word	0xa				# Subsystem (EFI application)
+	.word	0				# DllCharacteristics
+#ifdef CONFIG_X86_32
+	.long	0				# SizeOfStackReserve
+	.long	0				# SizeOfStackCommit
+	.long	0				# SizeOfHeapReserve
+	.long	0				# SizeOfHeapCommit
+#else
+	.quad	0				# SizeOfStackReserve
+	.quad	0				# SizeOfStackCommit
+	.quad	0				# SizeOfHeapReserve
+	.quad	0				# SizeOfHeapCommit
+#endif
+	.long	0				# LoaderFlags
+	.long	0x1				# NumberOfRvaAndSizes
+
+	.quad	0				# ExportTable
+	.quad	0				# ImportTable
+	.quad	0				# ResourceTable
+	.quad	0				# ExceptionTable
+	.quad	0				# CertificationTable
+	.quad	0				# BaseRelocationTable
+
+	# Section table
+section_table:
+	.ascii	".text"
+	.byte	0
+	.byte	0
+	.byte	0
+	.long	0
+	.long	0x0				# startup_{32,64}
+	.long	0				# Size of initialized data
+						# on disk
+	.long	0x0				# startup_{32,64}
+	.long	0				# PointerToRelocations
+	.long	0				# PointerToLineNumbers
+	.word	0				# NumberOfRelocations
+	.word	0				# NumberOfLineNumbers
+	.long	0x60500020			# Characteristics (section flags)
+
+	#
+	# The EFI application loader requires a relocation section
+	# because EFI applications are relocatable and not having
+	# this section seems to confuse it. But since we don't need
+	# the loader to fixup any relocs for us just fill it with a
+	# single dummy reloc.
+	#
+	.ascii	".reloc"
+	.byte	0
+	.byte	0
+	.long	reloc_end - reloc_start
+	.long	reloc_start
+	.long	reloc_end - reloc_start		# SizeOfRawData
+	.long	reloc_start			# PointerToRawData
+	.long	0				# PointerToRelocations
+	.long	0				# PointerToLineNumbers
+	.word	0				# NumberOfRelocations
+	.word	0				# NumberOfLineNumbers
+	.long	0x42100040			# Characteristics (section flags)
+#endif /* CONFIG_EFI_STUB */
 
 	# Kernel attributes; used by setup.  This is part 1 of the
 	# header, from the old boot sector.
@@ -318,3 +466,13 @@ die:
 setup_corrupt:
 	.byte	7
 	.string	"No setup signature found...\n"
+
+	.data
+dummy:	.long	0
+
+	.section .reloc
+reloc_start:
+	.long	dummy - reloc_start
+	.long	10
+	.word	0
+reloc_end:
diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c
index 3cbc405..574dedf 100644
--- a/arch/x86/boot/string.c
+++ b/arch/x86/boot/string.c
@@ -111,3 +111,38 @@ unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int bas
 
 	return result;
 }
+
+/**
+ * strlen - Find the length of a string
+ * @s: The string to be sized
+ */
+size_t strlen(const char *s)
+{
+	const char *sc;
+
+	for (sc = s; *sc != '\0'; ++sc)
+		/* nothing */;
+	return sc - s;
+}
+
+/**
+ * strstr - Find the first substring in a %NUL terminated string
+ * @s1: The string to be searched
+ * @s2: The string to search for
+ */
+char *strstr(const char *s1, const char *s2)
+{
+	size_t l1, l2;
+
+	l2 = strlen(s2);
+	if (!l2)
+		return (char *)s1;
+	l1 = strlen(s1);
+	while (l1 >= l2) {
+		l1--;
+		if (!memcmp(s1, s2, l2))
+			return (char *)s1;
+		s1++;
+	}
+	return NULL;
+}
diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c
index fdc60a0..4e9bd6b 100644
--- a/arch/x86/boot/tools/build.c
+++ b/arch/x86/boot/tools/build.c
@@ -135,6 +135,9 @@ static void usage(void)
 
 int main(int argc, char ** argv)
 {
+#ifdef CONFIG_EFI_STUB
+	unsigned int file_sz, pe_header;
+#endif
 	unsigned int i, sz, setup_sectors;
 	int c;
 	u32 sys_size;
@@ -194,6 +197,42 @@ int main(int argc, char ** argv)
 	buf[0x1f6] = sys_size >> 16;
 	buf[0x1f7] = sys_size >> 24;
 
+#ifdef CONFIG_EFI_STUB
+	file_sz = sz + i + ((sys_size * 16) - sz);
+
+	pe_header = *(unsigned int *)&buf[0x3c];
+
+	/* Size of code */
+	*(unsigned int *)&buf[pe_header + 0x1c] = file_sz;
+
+	/* Size of image */
+	*(unsigned int *)&buf[pe_header + 0x50] = file_sz;
+
+#ifdef CONFIG_X86_32
+	/* Address of entry point */
+	*(unsigned int *)&buf[pe_header + 0x28] = i;
+
+	/* .text size */
+	*(unsigned int *)&buf[pe_header + 0xb0] = file_sz;
+
+	/* .text size of initialised data */
+	*(unsigned int *)&buf[pe_header + 0xb8] = file_sz;
+#else
+	/*
+	 * Address of entry point. startup_32 is at the beginning and
+	 * the 64-bit entry point (startup_64) is always 512 bytes
+	 * after.
+	 */
+	*(unsigned int *)&buf[pe_header + 0x28] = i + 512;
+
+	/* .text size */
+	*(unsigned int *)&buf[pe_header + 0xc0] = file_sz;
+
+	/* .text size of initialised data */
+	*(unsigned int *)&buf[pe_header + 0xc8] = file_sz;
+#endif /* CONFIG_X86_32 */
+#endif /* CONFIG_EFI_STUB */
+
 	crc = partial_crc32(buf, i, crc);
 	if (fwrite(buf, 1, i, stdout) != i)
 		die("Writing setup failed");
diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c
index 4f13faf..68de2dc 100644
--- a/arch/x86/kernel/asm-offsets.c
+++ b/arch/x86/kernel/asm-offsets.c
@@ -67,4 +67,6 @@ void common(void) {
 	OFFSET(BP_hardware_subarch, boot_params, hdr.hardware_subarch);
 	OFFSET(BP_version, boot_params, hdr.version);
 	OFFSET(BP_kernel_alignment, boot_params, hdr.kernel_alignment);
+	OFFSET(BP_pref_address, boot_params, hdr.pref_address);
+	OFFSET(BP_code32_start, boot_params, hdr.code32_start);
 }
-- 
1.7.4.4




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

* Re: [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-11-15 18:51     ` Matt Fleming
@ 2011-11-23  0:44       ` Maarten Lankhorst
  2011-11-23 19:27         ` Matt Fleming
  0 siblings, 1 reply; 39+ messages in thread
From: Maarten Lankhorst @ 2011-11-23  0:44 UTC (permalink / raw)
  To: Matt Fleming
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Mike Waychison, Andi Kleen

Hey Matt,

On 11/15/2011 07:51 PM, Matt Fleming wrote:
> On Mon, 2011-10-17 at 22:06 +0200, Maarten Lankhorst wrote:
>> Seems initrd is failing for me. Is there any limitation on the size of
>> the initrd? The default one fedora generates for my kernel is 46mb. It
>> loads the kernel, but it reboots before it gets to userspace. Loading
>> the kernel and initrd with grub2-efi works. EFI booting without initrd
>> works too.
> Sorry it took me so long to reply to this! Could you try version 6 of
> this patch?
>
> From 5f1a572e59915daaa263093f0a2f180c88fa8504 Mon Sep 17 00:00:00 2001
> From: Matt Fleming <matt.fleming@intel.com>
> Date: Wed, 20 Jul 2011 09:24:53 +0100
> Subject: [PATCH v6] x86, efi: EFI boot stub support
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit
>
> v6:
>
>  - Try to allocate initrd memory just below hdr->inird_addr_max.
>
When I tested this with v3.2-rc2 it didn't boot, it hung before it initialized the kernel.
Without initrd it works fine, though.

~Maarten

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

* Re: [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-11-23  0:44       ` Maarten Lankhorst
@ 2011-11-23 19:27         ` Matt Fleming
  2011-11-24 14:43           ` Maarten Lankhorst
  0 siblings, 1 reply; 39+ messages in thread
From: Matt Fleming @ 2011-11-23 19:27 UTC (permalink / raw)
  To: Maarten Lankhorst
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Mike Waychison, Andi Kleen

On Wed, 2011-11-23 at 01:44 +0100, Maarten Lankhorst wrote:
> When I tested this with v3.2-rc2 it didn't boot, it hung before it
> initialized the kernel.
> Without initrd it works fine, though.

Bah, so this change actually makes booting worse? You said before that
you almost made it to userspace but this seems to hang much earlier now.
Is that correct?

... back to the drawing board.

-- 
Matt Fleming, Intel Open Source Technology Center


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

* Re: [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-11-23 19:27         ` Matt Fleming
@ 2011-11-24 14:43           ` Maarten Lankhorst
  2011-11-24 16:58             ` Maarten Lankhorst
  2011-11-24 20:56             ` Matt Fleming
  0 siblings, 2 replies; 39+ messages in thread
From: Maarten Lankhorst @ 2011-11-24 14:43 UTC (permalink / raw)
  To: Matt Fleming
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Mike Waychison, Andi Kleen

Hey Matt,

On 11/23/2011 08:27 PM, Matt Fleming wrote:

> On Wed, 2011-11-23 at 01:44 +0100, Maarten Lankhorst wrote:
>> When I tested this with v3.2-rc2 it didn't boot, it hung before it
>> initialized the kernel.
>> Without initrd it works fine, though.
> Bah, so this change actually makes booting worse? You said before that
> you almost made it to userspace but this seems to hang much earlier now.
> Is that correct?
>
> ... back to the drawing board.
I was looking at why grub2 could boot, seems to be it reads in chunks of
256 kilobytes. I seem to be able to get it to boot with chunks of 4 mb
as well, but didn't test beyond that.

So the fix is to simply read the file in parts, otherwise efi hangs..
As a nice side effect, short reads are also handled, but the efi
firmware seems to choke over huge reads and dies.

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 8627a56..9b076f0 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -695,14 +695,16 @@ grow:
 			u64 size;
 
 			size = initrds[j].size;
-			status = efi_call_phys3(fh->read, initrds[j].handle,
-						&size, addr);
-			if (status != EFI_SUCCESS)
-				goto free_initrd_total;
-
+			while (size) {
+				u64 toread = size > 1024 * 1024 ? 1024 * 1024 : size;
+				status = efi_call_phys3(fh->read, initrds[j].handle,
+							&toread, addr);
+				if (status != EFI_SUCCESS)
+					goto free_initrd_total;
+				size -= toread;
+				addr += toread;
+			}
 			efi_call_phys1(fh->close, initrds[j].handle);
-
-			addr += size;
 		}
 
 	}



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

* Re: [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-11-24 14:43           ` Maarten Lankhorst
@ 2011-11-24 16:58             ` Maarten Lankhorst
  2011-11-24 20:56             ` Matt Fleming
  1 sibling, 0 replies; 39+ messages in thread
From: Maarten Lankhorst @ 2011-11-24 16:58 UTC (permalink / raw)
  To: Matt Fleming
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Mike Waychison, Andi Kleen

Hey Matt,

On 11/24/2011 03:43 PM, Maarten Lankhorst wrote:
> Hey Matt,
>
> On 11/23/2011 08:27 PM, Matt Fleming wrote:
>
>> On Wed, 2011-11-23 at 01:44 +0100, Maarten Lankhorst wrote:
>>> When I tested this with v3.2-rc2 it didn't boot, it hung before it
>>> initialized the kernel.
>>> Without initrd it works fine, though.
>> Bah, so this change actually makes booting worse? You said before that
>> you almost made it to userspace but this seems to hang much earlier now.
>> Is that correct?
>>
>> ... back to the drawing board.
> I was looking at why grub2 could boot, seems to be it reads in chunks of
> 256 kilobytes. I seem to be able to get it to boot with chunks of 4 mb
> as well, but didn't test beyond that.
>
> So the fix is to simply read the file in parts, otherwise efi hangs..
> As a nice side effect, short reads are also handled, but the efi
> firmware seems to choke over huge reads and dies.
So after I was encouraged by mjg59 to find out the limit,
I put this patch to test maximum file size with together..

Turns out that it pukes on 4 mb, my printf was fucked,
in the patch below I corrected it, so I don't know for sure
if that included 4 mb being bad or not. I suppose writing
a test for read(fd, addr, 5 * 1024 * 1024) was too hard for
efi to pass. ;)

Just make a file with dd if=/dev/urandom of=initrd.img bs=1M count=99
to find out when/if YOUR efi implementation will die! :D

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 8627a56..415b875 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -30,6 +30,19 @@
 #define PIXEL_BLT_ONLY					3
 #define PIXEL_FORMAT_MAX				4
 
+typedef struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
+  void* Reset;
+  void *OutputString;
+  void *TestString;
+  void *QueryMode;
+  void *SetMode;
+  void *SetAttribute;
+  void *ClearScreen;
+  void *SetCursorPosition;
+  void *EnableCursor;
+  void *Mode;
+} __attribute__((packed)) EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;
+
 typedef struct {
 	u32 red_mask;
 	u32 green_mask;
@@ -692,17 +705,27 @@ grow:
 
 		addr = initrd_addr;
 		for (j = 0; j < nr_initrds; j++) {
-			u64 size;
-
-			size = initrds[j].size;
-			status = efi_call_phys3(fh->read, initrds[j].handle,
-						&size, addr);
-			if (status != EFI_SUCCESS)
-				goto free_initrd_total;
-
+			u64 size, chunksize;
+			uint16_t str[] = { 'S', 'i', 'z', 'e', ':', ' ', ' ',' ', ' ', '\r', '\n', 0 };
+			EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *output = (void*)sys_table->stderr;
+
+			for (chunksize = 1024 * 1024; chunksize < initrds[j].size; chunksize += 1024 * 1024) {
+				str[5] = ((chunksize / 1024 / 1024) / 10) + '0';
+				str[6] = ((chunksize / 1024 / 1024) % 10) + '0';
+				efi_call_phys2(output->OutputString, sys_table->stderr_handle, str);
+				size = initrds[j].size;
+				efi_call_phys2(fh->set_position, initrds[j].handle, 0);
+				while (size) {
+					u64 toread = size > chunksize ? chunksize : size;
+					status = efi_call_phys3(fh->read, initrds[j].handle,
+							&toread, addr);
+					if (status != EFI_SUCCESS)
+						goto free_initrd_total;
+					size -= toread;
+					addr += toread;
+				}
+			}
 			efi_call_phys1(fh->close, initrds[j].handle);
-
-			addr += size;
 		}
 
 	}



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

* Re: [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-11-24 14:43           ` Maarten Lankhorst
  2011-11-24 16:58             ` Maarten Lankhorst
@ 2011-11-24 20:56             ` Matt Fleming
  2011-11-24 21:07               ` Matthew Garrett
  2011-11-25  1:37               ` [PATCH] x86, efi: Break up large initrd reads Maarten Lankhorst
  1 sibling, 2 replies; 39+ messages in thread
From: Matt Fleming @ 2011-11-24 20:56 UTC (permalink / raw)
  To: Maarten Lankhorst
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Mike Waychison, Andi Kleen, Peter Jones

On Thu, 2011-11-24 at 15:43 +0100, Maarten Lankhorst wrote:
> Hey Matt,
> 
> On 11/23/2011 08:27 PM, Matt Fleming wrote:
> 
> > On Wed, 2011-11-23 at 01:44 +0100, Maarten Lankhorst wrote:
> >> When I tested this with v3.2-rc2 it didn't boot, it hung before it
> >> initialized the kernel.
> >> Without initrd it works fine, though.
> > Bah, so this change actually makes booting worse? You said before that
> > you almost made it to userspace but this seems to hang much earlier now.
> > Is that correct?
> >
> > ... back to the drawing board.
> I was looking at why grub2 could boot, seems to be it reads in chunks of
> 256 kilobytes. I seem to be able to get it to boot with chunks of 4 mb
> as well, but didn't test beyond that.
> 
> So the fix is to simply read the file in parts, otherwise efi hangs..
> As a nice side effect, short reads are also handled, but the efi
> firmware seems to choke over huge reads and dies.

Urrggghh.....

Thanks a lot for diagnosing this, Maarten! The copy of grub that I have
(fedora's version) looks like it uses the disk_io protocol to read from
disks, I wonder if there's a reason for this. I Cc'd Peter Jones in case
he's seen anything like this before.

Aha, read_file() in the elilo source contains this helpful comment,

	/*                                                                      
         * We load by chunks rather than a single big read because              
         * early versions of EFI had troubles loading files                     
         * from floppies in a single big request.  Breaking                     
         * the read down into chunks of 4KB fixed that                          
         * problem. While this problem has been fixed, we still prefer          
         * this method because it tells us whether or not we're making          
         * forward progress.                                                    
         */

(but reading the code it's clear it uses a chunk size of 16KB, not 4KB)

So, it looks like it's not just reading from floppies that hits this bug
in the firmware.

Maarten, could you send a patch against the 'x86/efi-stub' branch at
git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/linux.git that
splits the read up into 16KB chunks and explains,

	- why we need to do the reading in chunks
	- why we chose the 16KB chunk size (because elilo said so)

That way, the bug fix will be properly documented and attributed to you.

-- 
Matt Fleming, Intel Open Source Technology Center


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

* Re: [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-11-24 20:56             ` Matt Fleming
@ 2011-11-24 21:07               ` Matthew Garrett
  2011-11-24 21:31                 ` Matt Fleming
  2011-11-25  1:37               ` [PATCH] x86, efi: Break up large initrd reads Maarten Lankhorst
  1 sibling, 1 reply; 39+ messages in thread
From: Matthew Garrett @ 2011-11-24 21:07 UTC (permalink / raw)
  To: Matt Fleming
  Cc: Maarten Lankhorst, H. Peter Anvin, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Mike Waychison, Andi Kleen, Peter Jones

On Thu, Nov 24, 2011 at 08:56:56PM +0000, Matt Fleming wrote:

> Thanks a lot for diagnosing this, Maarten! The copy of grub that I have
> (fedora's version) looks like it uses the disk_io protocol to read from
> disks, I wonder if there's a reason for this. I Cc'd Peter Jones in case
> he's seen anything like this before.

grub needs to be able to read files off filesystems that may not be 
supported by the UEFI implementation, so has its own filesystem drivers 
that use disk_io.

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

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

* Re: [PATCH v5 10/10] x86, efi: EFI boot stub support
  2011-11-24 21:07               ` Matthew Garrett
@ 2011-11-24 21:31                 ` Matt Fleming
  0 siblings, 0 replies; 39+ messages in thread
From: Matt Fleming @ 2011-11-24 21:31 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: Maarten Lankhorst, H. Peter Anvin, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Mike Waychison, Andi Kleen, Peter Jones

On Thu, 2011-11-24 at 21:07 +0000, Matthew Garrett wrote:
> grub needs to be able to read files off filesystems that may not be 
> supported by the UEFI implementation, so has its own filesystem drivers 
> that use disk_io.

Ah, of course. Thanks Matthew.

-- 
Matt Fleming, Intel Open Source Technology Center


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

* [PATCH] x86, efi: Break up large initrd reads
  2011-11-24 20:56             ` Matt Fleming
  2011-11-24 21:07               ` Matthew Garrett
@ 2011-11-25  1:37               ` Maarten Lankhorst
  2011-11-25  8:48                 ` Matt Fleming
  1 sibling, 1 reply; 39+ messages in thread
From: Maarten Lankhorst @ 2011-11-25  1:37 UTC (permalink / raw)
  To: Matt Fleming
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Mike Waychison, Andi Kleen, Peter Jones

The efi boot stub tries to read the entire initrd in 1 go,
however some efi implementations hang if too much if asked
to read too much data at the same time. After some
experimentation I found out that my asrock p67 board will
hang if asked to read chunks of 4mb, so use a safe value.

>From elilo source code:
/*
 * We load by chunks rather than a single big read because
 * early versions of EFI had troubles loading files
 * from floppies in a single big request.  Breaking
 * the read down into chunks of 4KB fixed that
 * problem. While this problem has been fixed, we still prefer
 * this method because it tells us whether or not we're making
 * forward progress.
 */

While the comment says 4KB, it's using 4 * EFI_PAGE_SIZE (16KB),
so I went by the safest route of following elilo here.

Signed-off-by: Maarten Lankhorst <m.b.lankhorst@gmail.com>
---
 arch/x86/boot/compressed/eboot.c |   20 ++++++++++++++------
 1 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 8627a56..a26be50 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -695,14 +695,22 @@ grow:
 			u64 size;
 
 			size = initrds[j].size;
-			status = efi_call_phys3(fh->read, initrds[j].handle,
-						&size, addr);
-			if (status != EFI_SUCCESS)
-				goto free_initrd_total;
+			while (size) {
+				u64 chunksize;
+				if (size > 4 * EFI_PAGE_SIZE)
+					chunksize = 4 * EFI_PAGE_SIZE;
+				else
+					chunksize = size;
+				status = efi_call_phys3(fh->read,
+							initrds[j].handle,
+							&chunksize, addr);
+				if (status != EFI_SUCCESS)
+					goto free_initrd_total;
+				addr += chunksize;
+				size -= chunksize;
+			}
 
 			efi_call_phys1(fh->close, initrds[j].handle);
-
-			addr += size;
 		}
 
 	}
-- 
1.7.7.1




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

* Re: [PATCH] x86, efi: Break up large initrd reads
  2011-11-25  1:37               ` [PATCH] x86, efi: Break up large initrd reads Maarten Lankhorst
@ 2011-11-25  8:48                 ` Matt Fleming
  2011-12-12 23:10                   ` H. Peter Anvin
  0 siblings, 1 reply; 39+ messages in thread
From: Matt Fleming @ 2011-11-25  8:48 UTC (permalink / raw)
  To: Maarten Lankhorst
  Cc: H. Peter Anvin, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Mike Waychison, Andi Kleen, Peter Jones

On Fri, 2011-11-25 at 02:37 +0100, Maarten Lankhorst wrote:
> The efi boot stub tries to read the entire initrd in 1 go,
> however some efi implementations hang if too much if asked
> to read too much data at the same time. After some
> experimentation I found out that my asrock p67 board will
> hang if asked to read chunks of 4mb, so use a safe value.
> 
> From elilo source code:
> /*
>  * We load by chunks rather than a single big read because
>  * early versions of EFI had troubles loading files
>  * from floppies in a single big request.  Breaking
>  * the read down into chunks of 4KB fixed that
>  * problem. While this problem has been fixed, we still prefer
>  * this method because it tells us whether or not we're making
>  * forward progress.
>  */
> 
> While the comment says 4KB, it's using 4 * EFI_PAGE_SIZE (16KB),
> so I went by the safest route of following elilo here.
> 
> Signed-off-by: Maarten Lankhorst <m.b.lankhorst@gmail.com>
> ---
>  arch/x86/boot/compressed/eboot.c |   20 ++++++++++++++------
>  1 files changed, 14 insertions(+), 6 deletions(-)

Thanks Maarten, applied!

-- 
Matt Fleming, Intel Open Source Technology Center


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

* [tip:x86/efi] x86: Add missing bzImage fields to struct setup_header
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (9 preceding siblings ...)
  2011-10-17 10:40 ` [PATCH v5 10/10] x86, efi: EFI boot stub support Matt Fleming
@ 2011-12-10  2:37 ` tip-bot for Matt Fleming
  2011-12-10  2:38 ` [tip:x86/efi] x86: Don't use magic strings for EFI loader signature tip-bot for Matt Fleming
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: tip-bot for Matt Fleming @ 2011-12-10  2:37 UTC (permalink / raw)
  To: linux-tip-commits; +Cc: linux-kernel, hpa, mingo, tglx, hpa, matt.fleming

Commit-ID:  8af21e7e71d1ac56d9b66fb787a14fd66af7f5f7
Gitweb:     http://git.kernel.org/tip/8af21e7e71d1ac56d9b66fb787a14fd66af7f5f7
Author:     Matt Fleming <matt.fleming@intel.com>
AuthorDate: Sat, 27 Aug 2011 09:35:45 +0100
Committer:  H. Peter Anvin <hpa@linux.intel.com>
CommitDate: Fri, 9 Dec 2011 17:35:33 -0800

x86: Add missing bzImage fields to struct setup_header

commit 37ba7ab5e33c ("x86, boot: make kernel_alignment adjustable; new
bzImage fields") introduced some new fields into the bzImage header
but struct setup_header was not updated accordingly. Add the missing
'pref_address' and 'init_size' fields.

Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Link: http://lkml.kernel.org/r/1318848017-12301-1-git-send-email-matt@console-pimps.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
---
 arch/x86/include/asm/bootparam.h |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/arch/x86/include/asm/bootparam.h b/arch/x86/include/asm/bootparam.h
index e020d88..2f90c51 100644
--- a/arch/x86/include/asm/bootparam.h
+++ b/arch/x86/include/asm/bootparam.h
@@ -64,6 +64,8 @@ struct setup_header {
 	__u32	payload_offset;
 	__u32	payload_length;
 	__u64	setup_data;
+	__u64	pref_address;
+	__u32	init_size;
 } __attribute__((packed));
 
 struct sys_desc_table {

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

* [tip:x86/efi] x86: Don't use magic strings for EFI loader signature
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (10 preceding siblings ...)
  2011-12-10  2:37 ` [tip:x86/efi] x86: Add missing bzImage fields to struct setup_header tip-bot for Matt Fleming
@ 2011-12-10  2:38 ` tip-bot for Matt Fleming
  2011-12-10  2:38 ` [tip:x86/efi] efi.h: Add struct definition for boot time services tip-bot for Matt Fleming
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: tip-bot for Matt Fleming @ 2011-12-10  2:38 UTC (permalink / raw)
  To: linux-tip-commits; +Cc: linux-kernel, mjg, hpa, mingo, tglx, hpa, matt.fleming

Commit-ID:  f7d7d01be53cb47e0ae212c4e968aa28b82d2138
Gitweb:     http://git.kernel.org/tip/f7d7d01be53cb47e0ae212c4e968aa28b82d2138
Author:     Matt Fleming <matt.fleming@intel.com>
AuthorDate: Tue, 15 Nov 2011 12:56:14 +0000
Committer:  H. Peter Anvin <hpa@linux.intel.com>
CommitDate: Fri, 9 Dec 2011 17:35:38 -0800

x86: Don't use magic strings for EFI loader signature

Introduce a symbol, EFI_LOADER_SIGNATURE instead of using the magic
strings, which also helps to reduce the amount of ifdeffery.

Cc: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Link: http://lkml.kernel.org/r/1318848017-12301-1-git-send-email-matt@console-pimps.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
---
 arch/x86/include/asm/efi.h |    4 ++++
 arch/x86/kernel/setup.c    |    7 +------
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index b8d8bfc..26d8c18 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -3,6 +3,8 @@
 
 #ifdef CONFIG_X86_32
 
+#define EFI_LOADER_SIGNATURE	"EL32"
+
 extern unsigned long asmlinkage efi_call_phys(void *, ...);
 
 #define efi_call_phys0(f)		efi_call_phys(f)
@@ -35,6 +37,8 @@ extern unsigned long asmlinkage efi_call_phys(void *, ...);
 
 #else /* !CONFIG_X86_32 */
 
+#define EFI_LOADER_SIGNATURE	"EL64"
+
 extern u64 efi_call0(void *fp);
 extern u64 efi_call1(void *fp, u64 arg1);
 extern u64 efi_call2(void *fp, u64 arg1, u64 arg2);
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 9a9e40f..4d5243c 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -752,12 +752,7 @@ void __init setup_arch(char **cmdline_p)
 #endif
 #ifdef CONFIG_EFI
 	if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
-#ifdef CONFIG_X86_32
-		     "EL32",
-#else
-		     "EL64",
-#endif
-	 4)) {
+		     EFI_LOADER_SIGNATURE, 4)) {
 		efi_enabled = 1;
 		efi_memblock_x86_reserve_range();
 	}

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

* [tip:x86/efi] efi.h: Add struct definition for boot time services
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (11 preceding siblings ...)
  2011-12-10  2:38 ` [tip:x86/efi] x86: Don't use magic strings for EFI loader signature tip-bot for Matt Fleming
@ 2011-12-10  2:38 ` tip-bot for Matt Fleming
  2011-12-10  2:39 ` [tip:x86/efi] efi.h: Add efi_image_loaded_t tip-bot for Matt Fleming
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: tip-bot for Matt Fleming @ 2011-12-10  2:38 UTC (permalink / raw)
  To: linux-tip-commits; +Cc: linux-kernel, mjg, hpa, mingo, tglx, hpa, matt.fleming

Commit-ID:  f30ca6ba0bb2b7d050f24682bb8639c939c79859
Gitweb:     http://git.kernel.org/tip/f30ca6ba0bb2b7d050f24682bb8639c939c79859
Author:     Matt Fleming <matt.fleming@intel.com>
AuthorDate: Tue, 15 Nov 2011 12:56:32 +0000
Committer:  H. Peter Anvin <hpa@linux.intel.com>
CommitDate: Fri, 9 Dec 2011 17:35:40 -0800

efi.h: Add struct definition for boot time services

With the forthcoming efi stub code we're gonna need to access boot
time services so let's define a struct so we can access the functions.

Cc: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Link: http://lkml.kernel.org/r/1318848017-12301-1-git-send-email-matt@console-pimps.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
---
 include/linux/efi.h |   53 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 52 insertions(+), 1 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index 2362a0b..9547597 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -139,6 +139,57 @@ typedef struct {
 } efi_time_cap_t;
 
 /*
+ * EFI Boot Services table
+ */
+typedef struct {
+	efi_table_hdr_t hdr;
+	void *raise_tpl;
+	void *restore_tpl;
+	void *allocate_pages;
+	void *free_pages;
+	void *get_memory_map;
+	void *allocate_pool;
+	void *free_pool;
+	void *create_event;
+	void *set_timer;
+	void *wait_for_event;
+	void *signal_event;
+	void *close_event;
+	void *check_event;
+	void *install_protocol_interface;
+	void *reinstall_protocol_interface;
+	void *uninstall_protocol_interface;
+	void *handle_protocol;
+	void *__reserved;
+	void *register_protocol_notify;
+	void *locate_handle;
+	void *locate_device_path;
+	void *install_configuration_table;
+	void *load_image;
+	void *start_image;
+	void *exit;
+	void *unload_image;
+	void *exit_boot_services;
+	void *get_next_monotonic_count;
+	void *stall;
+	void *set_watchdog_timer;
+	void *connect_controller;
+	void *disconnect_controller;
+	void *open_protocol;
+	void *close_protocol;
+	void *open_protocol_information;
+	void *protocols_per_handle;
+	void *locate_handle_buffer;
+	void *locate_protocol;
+	void *install_multiple_protocol_interfaces;
+	void *uninstall_multiple_protocol_interfaces;
+	void *calculate_crc32;
+	void *copy_mem;
+	void *set_mem;
+	void *create_event_ex;
+} efi_boot_services_t;
+
+/*
  * Types and defines for EFI ResetSystem
  */
 #define EFI_RESET_COLD 0
@@ -261,7 +312,7 @@ typedef struct {
 	unsigned long stderr_handle;
 	unsigned long stderr;
 	efi_runtime_services_t *runtime;
-	unsigned long boottime;
+	efi_boot_services_t *boottime;
 	unsigned long nr_tables;
 	unsigned long tables;
 } efi_system_table_t;

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

* [tip:x86/efi] efi.h: Add efi_image_loaded_t
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (12 preceding siblings ...)
  2011-12-10  2:38 ` [tip:x86/efi] efi.h: Add struct definition for boot time services tip-bot for Matt Fleming
@ 2011-12-10  2:39 ` tip-bot for Matt Fleming
  2011-12-10  2:40 ` [tip:x86/efi] efi.h: Add allocation types for boottime->allocate_pages() tip-bot for Matt Fleming
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: tip-bot for Matt Fleming @ 2011-12-10  2:39 UTC (permalink / raw)
  To: linux-tip-commits; +Cc: linux-kernel, mjg, hpa, mingo, tglx, hpa, matt.fleming

Commit-ID:  8e84f345e2f2189a37492c77c566c7494b7b6b23
Gitweb:     http://git.kernel.org/tip/8e84f345e2f2189a37492c77c566c7494b7b6b23
Author:     Matt Fleming <matt.fleming@intel.com>
AuthorDate: Tue, 15 Nov 2011 12:56:50 +0000
Committer:  H. Peter Anvin <hpa@linux.intel.com>
CommitDate: Fri, 9 Dec 2011 17:35:42 -0800

efi.h: Add efi_image_loaded_t

Add the EFI loaded image structure and protocol guid which are
required by the x86 EFI boot stub. The EFI boot stub uses the
structure to figure out where it was loaded in memory and to pass
command line arguments to the kernel.

Cc: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Link: http://lkml.kernel.org/r/1318848017-12301-1-git-send-email-matt@console-pimps.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
---
 include/linux/efi.h |   19 +++++++++++++++++++
 1 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index 9547597..e35005f 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -287,6 +287,9 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
 #define LINUX_EFI_CRASH_GUID \
     EFI_GUID(  0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
 
+#define LOADED_IMAGE_PROTOCOL_GUID \
+    EFI_GUID(  0x5b1b31a1, 0x9562, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
+
 typedef struct {
 	efi_guid_t guid;
 	unsigned long table;
@@ -326,6 +329,22 @@ struct efi_memory_map {
 	unsigned long desc_size;
 };
 
+typedef struct {
+	u32 revision;
+	void *parent_handle;
+	efi_system_table_t *system_table;
+	void *device_handle;
+	void *file_path;
+	void *reserved;
+	u32 load_options_size;
+	void *load_options;
+	void *image_base;
+	__aligned_u64 image_size;
+	unsigned int image_code_type;
+	unsigned int image_data_type;
+	unsigned long unload;
+} efi_loaded_image_t;
+
 #define EFI_INVALID_TABLE_ADDR		(~0UL)
 
 /*

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

* [tip:x86/efi] efi.h: Add allocation types for boottime->allocate_pages()
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (13 preceding siblings ...)
  2011-12-10  2:39 ` [tip:x86/efi] efi.h: Add efi_image_loaded_t tip-bot for Matt Fleming
@ 2011-12-10  2:40 ` tip-bot for Matt Fleming
  2011-12-10  2:41 ` [tip:x86/efi] efi.h: Add graphics protocol guids tip-bot for Matt Fleming
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 39+ messages in thread
From: tip-bot for Matt Fleming @ 2011-12-10  2:40 UTC (permalink / raw)
  To: linux-tip-commits; +Cc: linux-kernel, mjg, hpa, mingo, tglx, hpa, matt.fleming

Commit-ID:  bb05e4ba452ada7966fbced4e829aa029f546445
Gitweb:     http://git.kernel.org/tip/bb05e4ba452ada7966fbced4e829aa029f546445
Author:     Matt Fleming <matt.fleming@intel.com>
AuthorDate: Tue, 15 Nov 2011 12:57:03 +0000
Committer:  H. Peter Anvin <hpa@linux.intel.com>
CommitDate: Fri, 9 Dec 2011 17:35:44 -0800

efi.h: Add allocation types for boottime->allocate_pages()

Add the allocation types detailed in section 6.2 - "AllocatePages()"
of the UEFI 2.3 specification. These definitions will be used by the
x86 EFI boot stub which needs to allocate memory during boot.

Cc: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Link: http://lkml.kernel.org/r/1318848017-12301-1-git-send-email-matt@console-pimps.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
---
 include/linux/efi.h |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index e35005f..378f2cd 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -109,6 +109,14 @@ typedef struct {
 	u32 imagesize;
 } efi_capsule_header_t;
 
+/*
+ * Allocation types for calls to boottime->allocate_pages.
+ */
+#define EFI_ALLOCATE_ANY_PAGES		0
+#define EFI_ALLOCATE_MAX_ADDRESS	1
+#define EFI_ALLOCATE_ADDRESS		2
+#define EFI_MAX_ALLOCATE_TYPE		3
+
 typedef int (*efi_freemem_callback_t) (u64 start, u64 end, void *arg);
 
 /*

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

* [tip:x86/efi] efi.h: Add graphics protocol guids
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (14 preceding siblings ...)
  2011-12-10  2:40 ` [tip:x86/efi] efi.h: Add allocation types for boottime->allocate_pages() tip-bot for Matt Fleming
@ 2011-12-10  2:41 ` tip-bot for Matt Fleming
  2011-12-10  2:42 ` [tip:x86/efi] efi.h: Add boottime->locate_handle search types tip-bot for Matt Fleming
  2011-12-10  2:42 ` [tip:x86/efi] efi: Add EFI file I/O data types tip-bot for Matt Fleming
  17 siblings, 0 replies; 39+ messages in thread
From: tip-bot for Matt Fleming @ 2011-12-10  2:41 UTC (permalink / raw)
  To: linux-tip-commits; +Cc: linux-kernel, mjg, hpa, mingo, tglx, hpa, matt.fleming

Commit-ID:  0f7c5d477f2ce552997831d80e2c872cca1b9054
Gitweb:     http://git.kernel.org/tip/0f7c5d477f2ce552997831d80e2c872cca1b9054
Author:     Matt Fleming <matt.fleming@intel.com>
AuthorDate: Tue, 15 Nov 2011 12:57:16 +0000
Committer:  H. Peter Anvin <hpa@linux.intel.com>
CommitDate: Fri, 9 Dec 2011 17:35:46 -0800

efi.h: Add graphics protocol guids

The x86 EFI boot stub uses the Graphics Output Protocol and Universal
Graphics Adapter (UGA) protocol guids when initialising graphics
during boot.

Cc: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Link: http://lkml.kernel.org/r/1318848017-12301-1-git-send-email-matt@console-pimps.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
---
 include/linux/efi.h |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index 378f2cd..e46d771 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -298,6 +298,15 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
 #define LOADED_IMAGE_PROTOCOL_GUID \
     EFI_GUID(  0x5b1b31a1, 0x9562, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
 
+#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
+    EFI_GUID(  0x9042a9de, 0x23dc, 0x4a38, 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a )
+
+#define EFI_UGA_PROTOCOL_GUID \
+    EFI_GUID(  0x982c298b, 0xf4fa, 0x41cb, 0xb8, 0x38, 0x77, 0xaa, 0x68, 0x8f, 0xb8, 0x39 )
+
+#define EFI_PCI_IO_PROTOCOL_GUID \
+    EFI_GUID(  0x4cf5b200, 0x68b8, 0x4ca5, 0x9e, 0xec, 0xb2, 0x3e, 0x3f, 0x50, 0x2, 0x9a )
+
 typedef struct {
 	efi_guid_t guid;
 	unsigned long table;

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

* [tip:x86/efi] efi.h: Add boottime->locate_handle search types
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (15 preceding siblings ...)
  2011-12-10  2:41 ` [tip:x86/efi] efi.h: Add graphics protocol guids tip-bot for Matt Fleming
@ 2011-12-10  2:42 ` tip-bot for Matt Fleming
  2011-12-10  2:42 ` [tip:x86/efi] efi: Add EFI file I/O data types tip-bot for Matt Fleming
  17 siblings, 0 replies; 39+ messages in thread
From: tip-bot for Matt Fleming @ 2011-12-10  2:42 UTC (permalink / raw)
  To: linux-tip-commits; +Cc: linux-kernel, mjg, hpa, mingo, tglx, hpa, matt.fleming

Commit-ID:  e2527a7cbec073b69a251193f200a88efbced7ad
Gitweb:     http://git.kernel.org/tip/e2527a7cbec073b69a251193f200a88efbced7ad
Author:     Matt Fleming <matt.fleming@intel.com>
AuthorDate: Tue, 15 Nov 2011 12:57:26 +0000
Committer:  H. Peter Anvin <hpa@linux.intel.com>
CommitDate: Fri, 9 Dec 2011 17:35:49 -0800

efi.h: Add boottime->locate_handle search types

The x86 EFI boot stub needs to locate handles for various protocols.

Cc: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Link: http://lkml.kernel.org/r/1318848017-12301-1-git-send-email-matt@console-pimps.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
---
 include/linux/efi.h |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index e46d771..d407c88 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -472,6 +472,13 @@ extern int __init efi_setup_pcdp_console(char *);
 #define EFI_VARIABLE_RUNTIME_ACCESS     0x0000000000000004
 
 /*
+ * The type of search to perform when calling boottime->locate_handle
+ */
+#define EFI_LOCATE_ALL_HANDLES			0
+#define EFI_LOCATE_BY_REGISTER_NOTIFY		1
+#define EFI_LOCATE_BY_PROTOCOL			2
+
+/*
  * EFI Device Path information
  */
 #define EFI_DEV_HW			0x01

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

* [tip:x86/efi] efi: Add EFI file I/O data types
  2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
                   ` (16 preceding siblings ...)
  2011-12-10  2:42 ` [tip:x86/efi] efi.h: Add boottime->locate_handle search types tip-bot for Matt Fleming
@ 2011-12-10  2:42 ` tip-bot for Matt Fleming
  17 siblings, 0 replies; 39+ messages in thread
From: tip-bot for Matt Fleming @ 2011-12-10  2:42 UTC (permalink / raw)
  To: linux-tip-commits; +Cc: linux-kernel, mjg, hpa, mingo, tglx, hpa, matt.fleming

Commit-ID:  55839d515495e766605d7aaabd9c2758370a8d27
Gitweb:     http://git.kernel.org/tip/55839d515495e766605d7aaabd9c2758370a8d27
Author:     Matt Fleming <matt.fleming@intel.com>
AuthorDate: Thu, 11 Aug 2011 10:28:06 +0100
Committer:  H. Peter Anvin <hpa@linux.intel.com>
CommitDate: Fri, 9 Dec 2011 17:35:51 -0800

efi: Add EFI file I/O data types

The x86 EFI stub needs to access files, for example when loading
initrd's. Add the required data types.

Cc: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Link: http://lkml.kernel.org/r/1318848017-12301-1-git-send-email-matt@console-pimps.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
---
 include/linux/efi.h |   40 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 40 insertions(+), 0 deletions(-)

diff --git a/include/linux/efi.h b/include/linux/efi.h
index d407c88..37c3007 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -307,6 +307,12 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
 #define EFI_PCI_IO_PROTOCOL_GUID \
     EFI_GUID(  0x4cf5b200, 0x68b8, 0x4ca5, 0x9e, 0xec, 0xb2, 0x3e, 0x3f, 0x50, 0x2, 0x9a )
 
+#define EFI_FILE_INFO_ID \
+    EFI_GUID(  0x9576e92, 0x6d3f, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
+
+#define EFI_FILE_SYSTEM_GUID \
+    EFI_GUID(  0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
+
 typedef struct {
 	efi_guid_t guid;
 	unsigned long table;
@@ -362,6 +368,40 @@ typedef struct {
 	unsigned long unload;
 } efi_loaded_image_t;
 
+typedef struct {
+	u64 revision;
+	void *open_volume;
+} efi_file_io_interface_t;
+
+typedef struct {
+	u64 size;
+	u64 file_size;
+	u64 phys_size;
+	efi_time_t create_time;
+	efi_time_t last_access_time;
+	efi_time_t modification_time;
+	__aligned_u64 attribute;
+	efi_char16_t filename[1];
+} efi_file_info_t;
+
+typedef struct {
+	u64 revision;
+	void *open;
+	void *close;
+	void *delete;
+	void *read;
+	void *write;
+	void *get_position;
+	void *set_position;
+	void *get_info;
+	void *set_info;
+	void *flush;
+} efi_file_handle_t;
+
+#define EFI_FILE_MODE_READ	0x0000000000000001
+#define EFI_FILE_MODE_WRITE	0x0000000000000002
+#define EFI_FILE_MODE_CREATE	0x8000000000000000
+
 #define EFI_INVALID_TABLE_ADDR		(~0UL)
 
 /*

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

* Re: [PATCH] x86, efi: Break up large initrd reads
  2011-11-25  8:48                 ` Matt Fleming
@ 2011-12-12 23:10                   ` H. Peter Anvin
  2011-12-13 18:26                     ` H. Peter Anvin
  0 siblings, 1 reply; 39+ messages in thread
From: H. Peter Anvin @ 2011-12-12 23:10 UTC (permalink / raw)
  To: Matt Fleming
  Cc: Maarten Lankhorst, H. Peter Anvin, Matthew Garrett, linux-kernel,
	Ingo Molnar, Thomas Gleixner, x86, Mike Waychison, Andi Kleen,
	Peter Jones

On 11/25/2011 12:48 AM, Matt Fleming wrote:
> On Fri, 2011-11-25 at 02:37 +0100, Maarten Lankhorst wrote:
>> The efi boot stub tries to read the entire initrd in 1 go,
>> however some efi implementations hang if too much if asked
>> to read too much data at the same time. After some
>> experimentation I found out that my asrock p67 board will
>> hang if asked to read chunks of 4mb, so use a safe value.
>>
>> From elilo source code:
>> /*
>>  * We load by chunks rather than a single big read because
>>  * early versions of EFI had troubles loading files
>>  * from floppies in a single big request.  Breaking
>>  * the read down into chunks of 4KB fixed that
>>  * problem. While this problem has been fixed, we still prefer
>>  * this method because it tells us whether or not we're making
>>  * forward progress.
>>  */
>>
>> While the comment says 4KB, it's using 4 * EFI_PAGE_SIZE (16KB),
>> so I went by the safest route of following elilo here.
>>

I'm going to NAK this, because I think the performance impact is too
severe.  I would like to set the cap at 1 MiB for now, unless we can
identify platforms where *that* is known to fail.

Maarten, would you be willing to rev your patch?  Furthermore, please
make the maximum chunksize a define.

	-hpa


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

* Re: [PATCH] x86, efi: Break up large initrd reads
  2011-12-12 23:10                   ` H. Peter Anvin
@ 2011-12-13 18:26                     ` H. Peter Anvin
  2011-12-14 23:33                       ` Maarten Lankhorst
  0 siblings, 1 reply; 39+ messages in thread
From: H. Peter Anvin @ 2011-12-13 18:26 UTC (permalink / raw)
  To: H. Peter Anvin, Maarten Lankhorst
  Cc: Matt Fleming, Matthew Garrett, linux-kernel, Ingo Molnar,
	Thomas Gleixner, x86, Mike Waychison, Andi Kleen, Peter Jones

On 12/12/2011 03:10 PM, H. Peter Anvin wrote:
> On 11/25/2011 12:48 AM, Matt Fleming wrote:
>> On Fri, 2011-11-25 at 02:37 +0100, Maarten Lankhorst wrote:
>>> The efi boot stub tries to read the entire initrd in 1 go,
>>> however some efi implementations hang if too much if asked
>>> to read too much data at the same time. After some
>>> experimentation I found out that my asrock p67 board will
>>> hang if asked to read chunks of 4mb, so use a safe value.
>>>
>>> From elilo source code:
>>> /*
>>>  * We load by chunks rather than a single big read because
>>>  * early versions of EFI had troubles loading files
>>>  * from floppies in a single big request.  Breaking
>>>  * the read down into chunks of 4KB fixed that
>>>  * problem. While this problem has been fixed, we still prefer
>>>  * this method because it tells us whether or not we're making
>>>  * forward progress.
>>>  */
>>>
>>> While the comment says 4KB, it's using 4 * EFI_PAGE_SIZE (16KB),
>>> so I went by the safest route of following elilo here.
>>>
> 
> I'm going to NAK this, because I think the performance impact is too
> severe.  I would like to set the cap at 1 MiB for now, unless we can
> identify platforms where *that* is known to fail.
> 
> Maarten, would you be willing to rev your patch?  Furthermore, please
> make the maximum chunksize a define.
> 

One more thing, Maarten: could you please provide the full DMI
information of the affected system?

	-hpa


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

* Re: [PATCH] x86, efi: Break up large initrd reads
  2011-12-13 18:26                     ` H. Peter Anvin
@ 2011-12-14 23:33                       ` Maarten Lankhorst
  2011-12-14 23:36                         ` H. Peter Anvin
  0 siblings, 1 reply; 39+ messages in thread
From: Maarten Lankhorst @ 2011-12-14 23:33 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: H. Peter Anvin, Matt Fleming, Matthew Garrett, linux-kernel,
	Ingo Molnar, Thomas Gleixner, x86, Mike Waychison, Andi Kleen,
	Peter Jones

Hey,

On 12/13/2011 07:26 PM, H. Peter Anvin wrote:
> On 12/12/2011 03:10 PM, H. Peter Anvin wrote:
>> On 11/25/2011 12:48 AM, Matt Fleming wrote:
>>> On Fri, 2011-11-25 at 02:37 +0100, Maarten Lankhorst wrote:
>>>> The efi boot stub tries to read the entire initrd in 1 go,
>>>> however some efi implementations hang if too much if asked
>>>> to read too much data at the same time. After some
>>>> experimentation I found out that my asrock p67 board will
>>>> hang if asked to read chunks of 4mb, so use a safe value.
>>>>
>>>> From elilo source code:
>>>> /*
>>>>  * We load by chunks rather than a single big read because
>>>>  * early versions of EFI had troubles loading files
>>>>  * from floppies in a single big request.  Breaking
>>>>  * the read down into chunks of 4KB fixed that
>>>>  * problem. While this problem has been fixed, we still prefer
>>>>  * this method because it tells us whether or not we're making
>>>>  * forward progress.
>>>>  */
>>>>
>>>> While the comment says 4KB, it's using 4 * EFI_PAGE_SIZE (16KB),
>>>> so I went by the safest route of following elilo here.
>>>>
>> I'm going to NAK this, because I think the performance impact is too
>> severe.  I would like to set the cap at 1 MiB for now, unless we can
>> identify platforms where *that* is known to fail.
>>
>> Maarten, would you be willing to rev your patch?  Furthermore, please
>> make the maximum chunksize a define.
>>
> One more thing, Maarten: could you please provide the full DMI
> information of the affected system?
>
When I was testing in increments of 1 mb, 4 mb was the magic number that caused a hang.
My system runs fine with 1mb and my original patch used that as blocksize.

What dmi information do you need? Top 2 entries from dmidecode, in case that's sufficient:

Handle 0x0001, DMI type 1, 27 bytes
System Information
        Manufacturer: To Be Filled By O.E.M.
        Product Name: To Be Filled By O.E.M.
        Version: To Be Filled By O.E.M.
        Serial Number: To Be Filled By O.E.M.
        UUID: 03000200-0400-0500-0006-000700080009
        Wake-up Type: Power Switch
        SKU Number: To Be Filled By O.E.M.
        Family: To Be Filled By O.E.M.

Handle 0x0002, DMI type 2, 15 bytes
Base Board Information
        Manufacturer: ASRock
        Product Name: P67 Pro3
        Version:                      
        Serial Number:                      
        Asset Tag:                      
        Features:
                Board is a hosting board
                Board is replaceable
        Location In Chassis:                      
        Chassis Handle: 0x0003
        Type: Motherboard
        Contained Object Handles: 0

But I suspect the other p67 boards from asrock would be affected by the 4mb crash  too.

~Maarten

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

* Re: [PATCH] x86, efi: Break up large initrd reads
  2011-12-14 23:33                       ` Maarten Lankhorst
@ 2011-12-14 23:36                         ` H. Peter Anvin
  2011-12-16 12:30                           ` [PATCH v2] " Maarten Lankhorst
  0 siblings, 1 reply; 39+ messages in thread
From: H. Peter Anvin @ 2011-12-14 23:36 UTC (permalink / raw)
  To: Maarten Lankhorst
  Cc: H. Peter Anvin, Matt Fleming, Matthew Garrett, linux-kernel,
	Ingo Molnar, Thomas Gleixner, x86, Mike Waychison, Andi Kleen,
	Peter Jones

On 12/14/2011 03:33 PM, Maarten Lankhorst wrote:
> When I was testing in increments of 1 mb, 4 mb was the magic number that caused a hang.
> My system runs fine with 1mb and my original patch used that as blocksize.

Let's stick to 1 MiB.  Could you send an updated patch, including
changing the open-coding of the number into a #define?

Thanks,

	-hpa

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

* [PATCH v2] x86, efi: Break up large initrd reads
  2011-12-14 23:36                         ` H. Peter Anvin
@ 2011-12-16 12:30                           ` Maarten Lankhorst
  2011-12-16 18:43                             ` [tip:x86/efi] " tip-bot for Maarten Lankhorst
  0 siblings, 1 reply; 39+ messages in thread
From: Maarten Lankhorst @ 2011-12-16 12:30 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: H. Peter Anvin, Matt Fleming, Matthew Garrett, linux-kernel,
	Ingo Molnar, Thomas Gleixner, x86, Mike Waychison, Andi Kleen,
	Peter Jones

[PATCH 11/11] x86, efi: Break up large initrd reads

The efi boot stub tries to read the entire initrd in 1 go,
however some efi implementations hang if too much if asked
to read too much data at the same time. After some
experimentation I found out that my asrock p67 board will
hang if asked to read chunks of 4MiB, so use a safe value.

elilo reads in chunks of 16KiB, but since that requires
many read calls I use a value of 1 MiB. hpa suggested
adding individual blacklists for when systems are found
where this value causes a crash.

Signed-off-by: Maarten Lankhorst <m.b.lankhorst@gmail.com>
---

v2: Bump read size to 1 MiB, and use a #define for chunk size

 arch/x86/boot/compressed/eboot.c |   21 +++++++++++++++------
 1 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 8627a56..df4f552 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -23,6 +23,7 @@
 #define DESC_TYPE_CODE_DATA	(1 << 0)
 
 #define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
+#define EFI_READ_CHUNK_SIZE	(1024 * 1024)
 
 #define PIXEL_RGB_RESERVED_8BIT_PER_COLOR		0
 #define PIXEL_BGR_RESERVED_8BIT_PER_COLOR		1
@@ -695,14 +696,22 @@ grow:
 			u64 size;
 
 			size = initrds[j].size;
-			status = efi_call_phys3(fh->read, initrds[j].handle,
-						&size, addr);
-			if (status != EFI_SUCCESS)
-				goto free_initrd_total;
+			while (size) {
+				u64 chunksize;
+				if (size > EFI_READ_CHUNK_SIZE)
+					chunksize = EFI_READ_CHUNK_SIZE;
+				else
+					chunksize = size;
+				status = efi_call_phys3(fh->read,
+							initrds[j].handle,
+							&chunksize, addr);
+				if (status != EFI_SUCCESS)
+					goto free_initrd_total;
+				addr += chunksize;
+				size -= chunksize;
+			}
 
 			efi_call_phys1(fh->close, initrds[j].handle);
-
-			addr += size;
 		}
 
 	}
-- 
1.7.7.4



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

* [tip:x86/efi] x86, efi: Break up large initrd reads
  2011-12-16 12:30                           ` [PATCH v2] " Maarten Lankhorst
@ 2011-12-16 18:43                             ` tip-bot for Maarten Lankhorst
  0 siblings, 0 replies; 39+ messages in thread
From: tip-bot for Maarten Lankhorst @ 2011-12-16 18:43 UTC (permalink / raw)
  To: linux-tip-commits; +Cc: linux-kernel, hpa, mingo, m.b.lankhorst, tglx, hpa

Commit-ID:  2d2da60fb40a80cc59383121ccf763e0e0e8a42a
Gitweb:     http://git.kernel.org/tip/2d2da60fb40a80cc59383121ccf763e0e0e8a42a
Author:     Maarten Lankhorst <m.b.lankhorst@gmail.com>
AuthorDate: Fri, 16 Dec 2011 13:30:58 +0100
Committer:  H. Peter Anvin <hpa@linux.intel.com>
CommitDate: Fri, 16 Dec 2011 08:34:35 -0800

x86, efi: Break up large initrd reads

The efi boot stub tries to read the entire initrd in 1 go, however
some efi implementations hang if too much if asked to read too much
data at the same time. After some experimentation I found out that my
asrock p67 board will hang if asked to read chunks of 4MiB, so use a
safe value.

elilo reads in chunks of 16KiB, but since that requires many read
calls I use a value of 1 MiB.  hpa suggested adding individual
blacklists for when systems are found where this value causes a crash.

Signed-off-by: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Link: http://lkml.kernel.org/r/4EEB3A02.3090201@gmail.com
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
---
 arch/x86/boot/compressed/eboot.c |   20 ++++++++++++++------
 arch/x86/boot/compressed/eboot.h |    1 +
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 4055e63..fec216f 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -643,14 +643,22 @@ grow:
 			u64 size;
 
 			size = initrds[j].size;
-			status = efi_call_phys3(fh->read, initrds[j].handle,
-						&size, addr);
-			if (status != EFI_SUCCESS)
-				goto free_initrd_total;
+			while (size) {
+				u64 chunksize;
+				if (size > EFI_READ_CHUNK_SIZE)
+					chunksize = EFI_READ_CHUNK_SIZE;
+				else
+					chunksize = size;
+				status = efi_call_phys3(fh->read,
+							initrds[j].handle,
+							&chunksize, addr);
+				if (status != EFI_SUCCESS)
+					goto free_initrd_total;
+				addr += chunksize;
+				size -= chunksize;
+			}
 
 			efi_call_phys1(fh->close, initrds[j].handle);
-
-			addr += size;
 		}
 
 	}
diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h
index f66d023..3925166 100644
--- a/arch/x86/boot/compressed/eboot.h
+++ b/arch/x86/boot/compressed/eboot.h
@@ -12,6 +12,7 @@
 #define DESC_TYPE_CODE_DATA	(1 << 0)
 
 #define EFI_PAGE_SIZE		(1UL << EFI_PAGE_SHIFT)
+#define EFI_READ_CHUNK_SIZE	(1024 * 1024)
 
 #define PIXEL_RGB_RESERVED_8BIT_PER_COLOR		0
 #define PIXEL_BGR_RESERVED_8BIT_PER_COLOR		1

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

end of thread, other threads:[~2011-12-16 18:43 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-10-17 10:40 [PATCH v5 00/10] x86 EFI boot stub Matt Fleming
2011-10-17 10:40 ` [PATCH 01/10] x86: Add missing bzImage fields to struct setup_header Matt Fleming
2011-10-17 10:40 ` [PATCH 02/10] x86, efi: Make efi_call_phys_prelog() CONFIG_RELOCATABLE-aware Matt Fleming
2011-10-17 10:40 ` [PATCH 03/10] x86: Don't use magic strings for EFI loader signature Matt Fleming
2011-10-17 10:40 ` [PATCH 04/10] efi.h: Add struct definition for boot time services Matt Fleming
2011-10-17 10:40 ` [PATCH v2 05/10] efi.h: Add efi_image_loaded_t Matt Fleming
2011-10-17 10:40 ` [PATCH 06/10] efi.h: Add allocation types for boottime->allocate_pages() Matt Fleming
2011-10-17 10:40 ` [PATCH v2 07/10] efi.h: Add graphics protocol guids Matt Fleming
2011-10-17 10:40 ` [PATCH 08/10] efi.h: Add boottime->locate_handle search types Matt Fleming
2011-10-17 10:40 ` [PATCH v2 09/10] efi: Add EFI file I/O data types Matt Fleming
2011-10-17 10:40 ` [PATCH v5 10/10] x86, efi: EFI boot stub support Matt Fleming
2011-10-17 16:39   ` Maarten Lankhorst
2011-10-17 18:28     ` Matt Fleming
2011-10-18  6:09     ` Ingo Molnar
2011-10-17 20:06   ` Maarten Lankhorst
2011-11-15 18:51     ` Matt Fleming
2011-11-23  0:44       ` Maarten Lankhorst
2011-11-23 19:27         ` Matt Fleming
2011-11-24 14:43           ` Maarten Lankhorst
2011-11-24 16:58             ` Maarten Lankhorst
2011-11-24 20:56             ` Matt Fleming
2011-11-24 21:07               ` Matthew Garrett
2011-11-24 21:31                 ` Matt Fleming
2011-11-25  1:37               ` [PATCH] x86, efi: Break up large initrd reads Maarten Lankhorst
2011-11-25  8:48                 ` Matt Fleming
2011-12-12 23:10                   ` H. Peter Anvin
2011-12-13 18:26                     ` H. Peter Anvin
2011-12-14 23:33                       ` Maarten Lankhorst
2011-12-14 23:36                         ` H. Peter Anvin
2011-12-16 12:30                           ` [PATCH v2] " Maarten Lankhorst
2011-12-16 18:43                             ` [tip:x86/efi] " tip-bot for Maarten Lankhorst
2011-12-10  2:37 ` [tip:x86/efi] x86: Add missing bzImage fields to struct setup_header tip-bot for Matt Fleming
2011-12-10  2:38 ` [tip:x86/efi] x86: Don't use magic strings for EFI loader signature tip-bot for Matt Fleming
2011-12-10  2:38 ` [tip:x86/efi] efi.h: Add struct definition for boot time services tip-bot for Matt Fleming
2011-12-10  2:39 ` [tip:x86/efi] efi.h: Add efi_image_loaded_t tip-bot for Matt Fleming
2011-12-10  2:40 ` [tip:x86/efi] efi.h: Add allocation types for boottime->allocate_pages() tip-bot for Matt Fleming
2011-12-10  2:41 ` [tip:x86/efi] efi.h: Add graphics protocol guids tip-bot for Matt Fleming
2011-12-10  2:42 ` [tip:x86/efi] efi.h: Add boottime->locate_handle search types tip-bot for Matt Fleming
2011-12-10  2:42 ` [tip:x86/efi] efi: Add EFI file I/O data types tip-bot for Matt Fleming

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).