Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[PATCH v2 00/10] x86 EFI boot stub

584 views
Skip to first unread message

Matt Fleming

unread,
Sep 12, 2011, 10:40:01 AM9/12/11
to
From: Matt Fleming <matt.f...@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 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 | 928 ++++++++++++++++++++++++++++++++
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 | 37 ++
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, 1470 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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Matt Fleming

unread,
Sep 12, 2011, 10:40:01 AM9/12/11
to
From: Matt Fleming <matt.f...@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 <m...@redhat.com>
Cc: "H. Peter Anvin" <h...@linux.intel.com>
Signed-off-by: Matt Fleming <matt.f...@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)

/*

Matt Fleming

unread,
Sep 12, 2011, 10:40:02 AM9/12/11
to
From: Matt Fleming <matt.f...@intel.com>

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

Cc: Matthew Garrett <m...@redhat.com>
Cc: "H. Peter Anvin" <h...@linux.intel.com>
Signed-off-by: Matt Fleming <matt.f...@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

Matt Fleming

unread,
Sep 12, 2011, 10:40:02 AM9/12/11
to
From: Matt Fleming <matt.f...@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 <tg...@linutronix.de>
Cc: Ingo Molnar <mi...@elte.hu>
Cc: H. Peter Anvin <h...@linux.intel.com>
Signed-off-by: Matt Fleming <matt.f...@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 {

Matt Fleming

unread,
Sep 12, 2011, 10:40:02 AM9/12/11
to
From: Matt Fleming <matt.f...@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 <tg...@linutronix.de>
Cc: Ingo Molnar <mi...@elte.hu>
Cc: "H. Peter Anvin" <h...@linux.intel.com>
Cc: Matthew Garrett <m...@redhat.com>
Signed-off-by: Matt Fleming <matt.f...@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;

Matt Fleming

unread,
Sep 12, 2011, 10:40:02 AM9/12/11
to
From: Matt Fleming <matt.f...@intel.com>

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

Cc: Matthew Garrett <m...@redhat.com>
Cc: "H. Peter Anvin" <h...@linux.intel.com>
Signed-off-by: Matt Fleming <matt.f...@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)

/*

Matt Fleming

unread,
Sep 12, 2011, 10:40:02 AM9/12/11
to
From: Matt Fleming <matt.f...@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 <m...@redhat.com>
Cc: "H. Peter Anvin" <h...@linux.intel.com>
Signed-off-by: Matt Fleming <matt.f...@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);

/*

Matt Fleming

unread,
Sep 12, 2011, 10:40:02 AM9/12/11
to
From: Matt Fleming <matt.f...@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 <h...@linux.intel.com>

Cc: Thomas Gleixner <tg...@linutronix.de>
Cc: Ingo Molnar <mi...@elte.hu>

Cc: Mike Waychison <mi...@google.com>
Cc: Matthew Garrett <m...@redhat.com>
Cc: Andi Kleen <an...@firstfloor.org>
Cc: Maarten Lankhorst <m.b.la...@gmail.com>


Signed-off-by: Matt Fleming <matt.f...@intel.com>
---

v2:
- File alignment was too large, was 8192 should be 512. Reported by
Maarten Lankhorst.
- 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 | 928 ++++++++++++++++++++++++++++++++
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 | 37 ++

arch/x86/kernel/asm-offsets.c | 2 +
12 files changed, 1315 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..6cedc6c
--- /dev/null
+++ b/arch/x86/boot/compressed/eboot.c
@@ -0,0 +1,928 @@
+/* -----------------------------------------------------------------------
+ *
+ * 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;
+
+/*
+ * When the kernel decompresses itself it will expand to higher
+ * addresses. So, we need to have a way to allocate memory safely
+ * below the decompress address.
+ */
+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:
+ 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_mode_information_t *info;
+ efi_graphics_output_protocol_t *gop;
+ unsigned long nr_gops;
+ efi_status_t status;
+ void **gop_handle;
+ 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;
+
+ nr_gops = size / sizeof(void *);
+ for (i = 0; i < nr_gops; i++) {
+ void *h = gop_handle[i];
+
+ status = efi_call_phys3(sys_table->boottime->handle_protocol,
+ h, proto, &gop);
+ if (status != EFI_SUCCESS)
+ continue;
+
+ status = efi_call_phys4(gop->query_mode, gop, gop->mode->mode,
+ &size, &info);
+ if (status == EFI_SUCCESS)
+ break;
+ }
+
+ if (i == nr_gops)
+ goto free_handle;
+
+ /* EFI framebuffer */
+ si->orig_video_isVGA = VIDEO_TYPE_EFI;
+
+ si->lfb_width = info->horizontal_resolution;
+ si->lfb_height = info->vertical_resolution;
+ si->lfb_base = gop->mode->frame_buffer_base;
+ si->lfb_size = gop->mode->frame_buffer_size;
+ si->pages = 1;
+
+ if (info->pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
+ si->lfb_depth = 32;
+ si->lfb_linelength = info->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 (info->pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
+ si->lfb_depth = 32;
+ si->lfb_linelength = info->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 (info->pixel_format == PIXEL_BIT_MASK) {
+ find_bits(info->pixel_information.red_mask,
+ &si->red_pos, &si->red_size);
+ find_bits(info->pixel_information.green_mask,
+ &si->green_pos, &si->green_size);
+ find_bits(info->pixel_information.blue_mask,
+ &si->blue_pos, &si->blue_size);
+ find_bits(info->pixel_information.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 = (info->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)
+{
+ u32 width, height, depth, refresh;
+ efi_uga_draw_protocol_t *uga;
+ unsigned long nr_ugas;
+ efi_status_t status;
+ 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;
+
+ nr_ugas = size / sizeof(void *);
+ for (i = 0; i < nr_ugas; i++) {
+ efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID;
+ void *h = uga_handle[i];
+ void *pci;
+
+ status = efi_call_phys3(sys_table->boottime->handle_protocol,
+ h, uga_proto, &uga);
+ if (status != EFI_SUCCESS)
+ continue;
+
+ status = efi_call_phys3(sys_table->boottime->handle_protocol,
+ h, &pciio_proto, &pci);
+ if (status != EFI_SUCCESS)
+ continue;
+
+ status = efi_call_phys5(uga->get_mode, uga, &width, &height,
+ &depth, &refresh);
+ if (status != EFI_SUCCESS)
+ continue;
+
+ /* 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;
+
+ break;
+ }
+
+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++;
+
+ if (*str == ' ')
+ str++;
+ }
+
+ 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;
+
+ 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 != ' ') {
+ if (p >= filename + sizeof(filename))
+ break;
+
+ *p++ = *str++;
+ }
+
+ if (*str == ' ')
+ 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;
+ }
+
+ if (status != EFI_SUCCESS) {
+ efi_call_phys1(sys_table->boottime->free_pool, info);
+ goto close_handles;
+ }
+
+ initrd->size = info->file_size;
+ initrd_total += info->file_size;
+
+ efi_call_phys1(sys_table->boottime->free_pool, info);
+ }
+
+ 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;
+
+ 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 options_size = image->load_options_size;
+ efi_status_t status;
+ __u32 desc_version;
+ unsigned long cmdline;
+ u8 nr_entries;
+ u16 *s2;
+ u8 *s1;
+ int i;
+
+ hdr->type_of_loader = 0x21;
+
+ status = low_alloc(options_size, 1, &cmdline);
+ if (status != EFI_SUCCESS)
+ goto fail;
+
+ hdr->cmd_line_ptr = cmdline;
+
+ /* Convert unicode cmdline to ascii */
+ s1 = (u8 *)(unsigned long)hdr->cmd_line_ptr;
+ s2 = (u16 *)options;
+
+ if (s2 && options_size) {
+ /* Skip first word, that's the kernel name */
+ while (*s2 && *s2 != ' ' && *s2 != '\n') {
+ options_size--;
+ s2++;
+ }
+
+ /* skip space */
+ if (*s2 == ' ') {
+ options_size--;
+ s2++;
+ }
+
+ while (options_size-- != 0) {
+ *s1++ = *s2++;
+ hdr->cmdline_size++;
+ }
+
+ *s1 = '\0';
+ }
+
+ 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:
+ 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..b2da51c 100644
--- a/arch/x86/boot/tools/build.c
+++ b/arch/x86/boot/tools/build.c
@@ -135,6 +135,7 @@ static void usage(void)

int main(int argc, char ** argv)
{
+ unsigned int file_sz, pe_header;
unsigned int i, sz, setup_sectors;
int c;
u32 sys_size;
@@ -194,6 +195,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);

Matt Fleming

unread,
Sep 12, 2011, 10:40:02 AM9/12/11
to
From: Matt Fleming <matt.f...@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 <m...@redhat.com>
Cc: "H. Peter Anvin" <h...@linux.intel.com>


Signed-off-by: Matt Fleming <matt.f...@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;

Matt Fleming

unread,
Sep 12, 2011, 10:40:03 AM9/12/11
to
From: Matt Fleming <matt.f...@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 <m...@redhat.com>
Cc: "H. Peter Anvin" <h...@linux.intel.com>
Signed-off-by: Matt Fleming <matt.f...@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;

Matt Fleming

unread,
Sep 12, 2011, 10:40:02 AM9/12/11
to
From: Matt Fleming <matt.f...@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 <m...@redhat.com>
Cc: "H. Peter Anvin" <h...@linux.intel.com>
Signed-off-by: Matt Fleming <matt.f...@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();

Matthew Garrett

unread,
Sep 13, 2011, 9:40:02 AM9/13/11
to
On Mon, Sep 12, 2011 at 03:34:54PM +0100, Matt Fleming wrote:

> + 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;
> +
> + nr_gops = size / sizeof(void *);
> + for (i = 0; i < nr_gops; i++) {
> + void *h = gop_handle[i];
> +
> + status = efi_call_phys3(sys_table->boottime->handle_protocol,
> + h, proto, &gop);
> + if (status != EFI_SUCCESS)
> + continue;
> +
> + status = efi_call_phys4(gop->query_mode, gop, gop->mode->mode,
> + &size, &info);
> + if (status == EFI_SUCCESS)
> + break;
> + }

Apple do something funky here, which is to provide a GOP that's not
backed by real hardware but which muxes writes out to multiple outputs
in order to handle the multiple display case without screwing up aspect
ratio. That's fine, except that the stride reported for the fake one is
just the horizontal width of the logical display. That results in things
failing badly. The real one also implements PCIIO. My workaround so far
has been to look for a GOP that handles PCIIO and use it if found, and
otherwise just fall back to the first GOP.

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

Matt Fleming

unread,
Sep 13, 2011, 10:10:02 AM9/13/11
to
Ah right, thanks for that. I actually implement that strategy when
searching for a UGA (checking for PCIIO) but without falling back to the
first one if no PCIIO protocol is found. Your proposed scheme seems much
more robust so I'll respin this patch.

--
Matt Fleming, Intel Open Source Technology Center

Maarten Lankhorst

unread,
Sep 13, 2011, 10:40:02 AM9/13/11
to
Hey Matt,

This version seems to boot for me. Is it useful to add 32-bits support though?
It seems that only some older versions of OSX use it. I could see if I can
revive my mac mini, iirc it has 32-bits efi, or at least used to have.

Do I need to pass anything to add it to efibootmgr?

I tried something like this:
echo "args" | efibootmgr -c -l '\vmlinuz.efi' -L 'Native EFI linux boot' -@ - -u -d /dev/sdb

And it boots vmlinuz.efi, but the arguments I passed do not appear to have any effect.

~Maarten

Matt Fleming

unread,
Sep 14, 2011, 12:10:02 PM9/14/11
to
On Tue, 2011-09-13 at 16:33 +0200, Maarten Lankhorst wrote:
> This version seems to boot for me.

Yay! Thanks for testing.

> Is it useful to add 32-bits support though?
> It seems that only some older versions of OSX use it. I could see if I can
> revive my mac mini, iirc it has 32-bits efi, or at least used to have.

32-bit UEFI platforms do exist, so I think it's worth supporting them.

> Do I need to pass anything to add it to efibootmgr?
>
> I tried something like this:
> echo "args" | efibootmgr -c -l '\vmlinuz.efi' -L 'Native EFI linux boot' -@ - -u -d /dev/sdb
>
> And it boots vmlinuz.efi, but the arguments I passed do not appear to
> have any effect.

No idea, I've never used efibootmgr. Let's add Matt Domsch to the
discussion (now Cc'd).

--
Matt Fleming, Intel Open Source Technology Center

--

Maarten Lankhorst

unread,
Sep 15, 2011, 4:10:01 AM9/15/11
to
Hey Matt,

On 09/15/2011 06:52 AM, Matt Domsch wrote:
> On Wed, Sep 14, 2011 at 11:07:58AM -0500, Matt Fleming wrote:
>> On Tue, 2011-09-13 at 16:33 +0200, Maarten Lankhorst wrote:
>>> This version seems to boot for me.
>> Yay! Thanks for testing.
>>
>>> Is it useful to add 32-bits support though?
>>> It seems that only some older versions of OSX use it. I could see if I can
>>> revive my mac mini, iirc it has 32-bits efi, or at least used to have.
>> 32-bit UEFI platforms do exist, so I think it's worth supporting them.
>>
>>> Do I need to pass anything to add it to efibootmgr?
>>>
>>> I tried something like this:
>>> echo "args" | efibootmgr -c -l '\vmlinuz.efi' -L 'Native EFI linux boot' -@ - -u -d /dev/sdb
>>>
>>> And it boots vmlinuz.efi, but the arguments I passed do not appear to
>>> have any effect.
>> No idea, I've never used efibootmgr. Let's add Matt Domsch to the
>> discussion (now Cc'd).
> Maarten, do you not see your 'args' in /proc/cmdline after booting the
> entry? From reading this thread, that's what you should see.
>
> Can you provide an 'efibootmgr -v' and hexdump -C
> /sys/firmware/efi/vars/Boot* to see the args are appended as expected
> in the boot variable in nvram?
>
> Adding Jordan Hargrave, who is maintainer for efibootmgr now.
Thanks, that helped. It looks like efibootmgr stores the arguments without converting it to UCS-2.

Patch below is a rough check for ascii, in which case it passes it unmodified.

After this 'args' is passed succesfully. :)

Should probably be folded in 10/10.

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

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 6c34828..b24affb 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -619,12 +619,12 @@ static efi_status_t make_boot_params(struct boot_params *boot_params,
unsigned long cmdline;
u8 nr_entries;
u16 *s2;
- u8 *s1;
+ u8 *s1, *s2_8;
int i;

hdr->type_of_loader = 0x21;

- status = low_alloc(options_size, 1, &cmdline);
+ status = low_alloc(options_size+1, 1, &cmdline);
if (status != EFI_SUCCESS)
goto fail;

@@ -633,6 +633,14 @@ static efi_status_t make_boot_params(struct boot_params *boot_params,
/* Convert unicode cmdline to ascii */
s1 = (u8 *)(unsigned long)hdr->cmd_line_ptr;
s2 = (u16 *)options;
+ s2_8 = (u8*)options;
+ /* Assume the first byte is < 0x128 */
+ if (options_size > 1 && s2_8[1] && s2_8[1] < 0x80 && s2_8[0] < 0x80) {
+ s2 = NULL;
+ memcpy(s1, s2_8, options_size);
+ s1[options_size] = 0;
+ hdr->cmdline_size = options_size;
+ }

if (s2 && options_size) {
/* Skip first word, that's the kernel name */


Maarten Lankhorst

unread,
Sep 15, 2011, 5:10:02 AM9/15/11
to
Woops, typo.

> + if (options_size > 1 && s2_8[1] && s2_8[1] < 0x80 && s2_8[0] < 0x80) {
> + s2 = NULL;
> + memcpy(s1, s2_8, options_size);
> + s1[options_size] = 0;
> + hdr->cmdline_size = options_size;
> + }
>
> if (s2 && options_size) {
> /* Skip first word, that's the kernel name */
I noticed 10/10 doesn't include the size of null pointer is this expected?
And when options_size = 0, you do a null allocation and never null-terminate.
Also in s2 && options_size you initially check for newline, but when copying
arguments, you ignore it. With echo moo | efibootmgr -@ - you'd pass a newline,
which should probably be stripped. I think this would fix both cases:

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 6c34828..dbaaf54 100644


--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -619,12 +619,12 @@ static efi_status_t make_boot_params(struct boot_params *boot_params,
unsigned long cmdline;
u8 nr_entries;
u16 *s2;
- u8 *s1;
+ u8 *s1, *s2_8;
int i;

hdr->type_of_loader = 0x21;

- status = low_alloc(options_size, 1, &cmdline);
+ status = low_alloc(options_size+1, 1, &cmdline);
if (status != EFI_SUCCESS)
goto fail;

@@ -633,6 +633,14 @@ static efi_status_t make_boot_params(struct boot_params *boot_params,
/* Convert unicode cmdline to ascii */
s1 = (u8 *)(unsigned long)hdr->cmd_line_ptr;
s2 = (u16 *)options;
+ s2_8 = (u8*)options;

+ /* Assume the first byte is < 128 */
+ if (options_size > 1 && s2_8[1] s2_8[1] < 0x80 && s2_8[0] < 0x80) {


+ s2 = NULL;
+ memcpy(s1, s2_8, options_size);
+ s1[options_size] = '\0';
+ hdr->cmdline_size = options_size;
+ }

if (s2 && options_size) {
/* Skip first word, that's the kernel name */

@@ -655,6 +663,11 @@ static efi_status_t make_boot_params(struct boot_params *boot_params,
*s1 = '\0';
}

+ if (!hdr->cmdline_size)
+ *s1 = '\0';
+ else if (hdr->cmd_line_ptr[hdr->cmdline_size-1] == '\n')
+ hdr->cmd_line_ptr[--hdr->cmdline_size] = '\0';


+
hdr->ramdisk_image = 0;

hdr->ramdisk_size = 0;

Maarten Lankhorst

unread,
Sep 15, 2011, 8:50:02 AM9/15/11
to
Hey,

On 09/15/2011 01:52 PM, Matt Domsch wrote:

> When using -@, you are correct. Given a file (or stdin), it places it
> unmodified onto the command line of the boot loader.
>
> When using efibootmgr -u, all extra arguments passed to efibootmgr's
> command line are converted to UCS-2.
>
> When using efibootmgr -a, all extra arguments passed to efibootmgr's
> command line are not converted and are treated as ascii.
>
> So it's a matter of how you invoke efibootmgr as to which kind of blob
> winds up appended to the boot loader's command line. Using -@ means
> it's entirely up to you to create the blob you need apriori. This was
> done to allow arbitrary blobs to be passed in.
>
Thanks, that makes much more sense. The man page makes mention
of extra arguments, but it kind of looked like the way to pass it was by
using -u -@ - which didn't work of course. :)

So for reference:
efibootmgr -L 'EFI Native Linux Boot' -l '\vmlinuz.efi' -d /dev/sdb -u root=/dev/sdb2 console=ttyS0,115200n8

or as ASCII (reads much prettier in efibootmgr -v)
efibootmgr -L 'EFI Native Linux Boot' -l '\vmlinuz.efi' -d /dev/sdb root=/dev/sdb2 console=ttyS0,115200n8

This is the fixed patch I'm using for booting native linux kernel,
with passing args tested for UCS-2 and ASCII. It seems that
options_size can be halved safely, otherwise too much data is
copied from input.

I keep the first word, since otherwise the first argument is stripped off,
and it's probably harmless for the kernel to read something like
\vmlinuz.efi when you don't do a direct boot.

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

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 6c34828..f77f9f5 100644


--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -619,12 +619,12 @@ static efi_status_t make_boot_params(struct boot_params *boot_params,
unsigned long cmdline;
u8 nr_entries;
u16 *s2;
- u8 *s1;
+ u8 *s1, *s2_8;
int i;

hdr->type_of_loader = 0x21;

- status = low_alloc(options_size, 1, &cmdline);
+ status = low_alloc(options_size+1, 1, &cmdline);
if (status != EFI_SUCCESS)
goto fail;

@@ -633,27 +633,29 @@ static efi_status_t make_boot_params(struct boot_params *boot_params,


/* Convert unicode cmdline to ascii */
s1 = (u8 *)(unsigned long)hdr->cmd_line_ptr;
s2 = (u16 *)options;
+ s2_8 = (u8*)options;

- if (s2 && options_size) {
- /* Skip first word, that's the kernel name */
- while (*s2 && *s2 != ' ' && *s2 != '\n') {
- options_size--;
- s2++;
- }
-
- /* skip space */
- if (*s2 == ' ') {
- options_size--;
- s2++;
- }
-
- while (options_size-- != 0) {
+ if (options_size < 2 || !s2) {
+ *s1 = '\0';
+ } else if (s2_8[1] && s2_8[1] < 0x80 && s2_8[0] < 0x80) {
+ /* Passed as ASCII */


+ s2 = NULL;
+ memcpy(s1, s2_8, options_size);

+ hdr->cmdline_size = options_size;

+ } else {
+ options_size /= 2; /* Passed as UCS-2 */
+ while (options_size-- != 0 && *s2) {
*s1++ = *s2++;
hdr->cmdline_size++;
}
-
*s1 = '\0';
+ s1 = (u8 *)(unsigned long)hdr->cmd_line_ptr;
}
+ if (hdr->cmdline_size && s1[hdr->cmdline_size - 1] == '\0')
+ hdr->cmdline_size--;
+ if (hdr->cmdline_size && s1[hdr->cmdline_size - 1] == '\n')
+ hdr->cmdline_size--;
+ s1[hdr->cmdline_size] = '\0';



hdr->ramdisk_image = 0;
hdr->ramdisk_size = 0;

--

Matt Fleming

unread,
Sep 17, 2011, 7:50:01 AM9/17/11
to
On Thu, 2011-09-15 at 14:44 +0200, Maarten Lankhorst wrote:
> Thanks, that makes much more sense. The man page makes mention
> of extra arguments, but it kind of looked like the way to pass it was by
> using -u -@ - which didn't work of course. :)
>
> So for reference:
> efibootmgr -L 'EFI Native Linux Boot' -l '\vmlinuz.efi' -d /dev/sdb -u root=/dev/sdb2 console=ttyS0,115200n8
>
> or as ASCII (reads much prettier in efibootmgr -v)
> efibootmgr -L 'EFI Native Linux Boot' -l '\vmlinuz.efi' -d /dev/sdb root=/dev/sdb2 console=ttyS0,115200n8
>
> This is the fixed patch I'm using for booting native linux kernel,
> with passing args tested for UCS-2 and ASCII. It seems that
> options_size can be halved safely, otherwise too much data is
> copied from input.

Thanks for looking at this Maarten. I've been staring at this code for
far too long to notice buglets like this ;-)

> I keep the first word, since otherwise the first argument is stripped off,
> and it's probably harmless for the kernel to read something like
> \vmlinuz.efi when you don't do a direct boot.
>
> Signed-off-by: Maarten Lankhorst <m.b.la...@gmail.com>
>
> diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
> index 6c34828..f77f9f5 100644
> --- a/arch/x86/boot/compressed/eboot.c
> +++ b/arch/x86/boot/compressed/eboot.c
> @@ -619,12 +619,12 @@ static efi_status_t make_boot_params(struct boot_params *boot_params,
> unsigned long cmdline;
> u8 nr_entries;
> u16 *s2;
> - u8 *s1;
> + u8 *s1, *s2_8;
> int i;
>
> hdr->type_of_loader = 0x21;
>
> - status = low_alloc(options_size, 1, &cmdline);
> + status = low_alloc(options_size+1, 1, &cmdline);
> if (status != EFI_SUCCESS)
> goto fail;

If no options were passed to the stub then we really don't want to do
the allocation at all. Speaking of 'options_size', have you tested the
values that get passed in from image->load_options? Running with the
OVMF firmware under qemu I get sizes that are always larger than the
NUL-byte in the argument string. For example, executing "bzImage foo"
results in image->load_options being 38!

Remember that ->options_size represents 16-bit characters and cmdline is
going to be ASCII. options_size will always be larger than the value we
need, so there's no need for the +1. At the moment we're actually
allocating too much memory and that needs to be fixed.
Hmm... I'm really not convinced that we need to support ASCII cmdline
arguments, sorry. efibootmgr has support for UCS-2, so that's what
should be used.

Thanks for the review!

--
Matt Fleming, Intel Open Source Technology Center

Maarten Lankhorst

unread,
Sep 17, 2011, 8:20:02 AM9/17/11
to
Hey Matt,

I tested on my own physical system, not qemu or anything.

Anyhow it seems the cmdline_size is meant for reading, and setting it
is completely ignored, see below. Could we make it a static array so
no allocation needs to be done?


> Remember that ->options_size represents 16-bit characters and cmdline is
> going to be ASCII. options_size will always be larger than the value we
> need, so there's no need for the +1. At the moment we're actually
> allocating too much memory and that needs to be fixed.

Reading up a bit more, I think the sane approach would be to allocate
COMMAND_LINE_SIZE and null terminate it. cmdline_size is meant to be
read only it seems:

Documentation/x86/boot.txt:
Field name: cmdline_size
Type: read
Offset/size: 0x238/4
Protocol: 2.06+

The maximum size of the command line without the terminating
zero. This means that the command line can contain at most
cmdline_size characters. With protocol version 2.05 and earlier, the
maximum size was 255.

I know, but this is nicer for displaying, compare this efibootmgr -v output:

Boot0002* Linux ASCII Boot HD(...)File(\vmlinuz.efi)root=/dev/sdb2 console=ttyS0,115200n8.
Boot0003* Linux UCS-2 Boot HD(...)File(\vmlinuz.efi)r.o.o.t.=./.d.e.v./.s.d.b.2. .c.o.n.s.o.l.e.=.t.t.y.S.0.,.1.1.5.2.0.0.n.8...

And it's more foolproof, since it's easy to pass strings as
ASCII since it's the default, and it would show up readable in efibootmgr.

~Maarten

Matt Fleming

unread,
Sep 21, 2011, 8:00:02 AM9/21/11
to
On Sat, 2011-09-17 at 14:12 +0200, Maarten Lankhorst wrote:
> Anyhow it seems the cmdline_size is meant for reading, and setting it
> is completely ignored, see below. Could we make it a static array so
> no allocation needs to be done?

D'oh. Yeah, you're right, there's no point in updating cmdline_size.

What would be the benefit of making cmdline a static array and skipping
the allocation? I doubt it would improve boot time, and we'd end up
making the bzImage larger.

> > Hmm... I'm really not convinced that we need to support ASCII cmdline
> > arguments, sorry. efibootmgr has support for UCS-2, so that's what
> > should be used.
> >
> I know, but this is nicer for displaying, compare this efibootmgr -v output:
>
> Boot0002* Linux ASCII Boot HD(...)File(\vmlinuz.efi)root=/dev/sdb2 console=ttyS0,115200n8.
> Boot0003* Linux UCS-2 Boot HD(...)File(\vmlinuz.efi)r.o.o.t.=./.d.e.v./.s.d.b.2. .c.o.n.s.o.l.e.=.t.t.y.S.0.,.1.1.5.2.0.0.n.8...
>
> And it's more foolproof, since it's easy to pass strings as
> ASCII since it's the default, and it would show up readable in efibootmgr.

But if you don't like the output from efibootmgr, you should fix
efibootmgr, not cram more string manipulation code into the EFI stub.

If there were other cases where an ASCII cmdline was passed to the stub
I'd be more inclined to support it. But if it's just a case of "the EFI
boot stub doesn't parse arguments when efibootmgr passes them as ASCII"
my response is "Pass them as UCS-2 then".

--
Matt Fleming, Intel Open Source Technology Center

--

Matt Fleming

unread,
Sep 21, 2011, 8:20:02 AM9/21/11
to
From: Matt Fleming <matt.f...@intel.com>

Signed-off-by: Matt Fleming <matt.f...@intel.com>
---

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 suggested:
- 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 | 974 ++++++++++++++++++++++++++++++++


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, 1363 insertions(+), 1 deletions(-)

index 0000000..3f59e4c
--- /dev/null
+++ b/arch/x86/boot/compressed/eboot.c
@@ -0,0 +1,974 @@

+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.
+ */

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

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

+ 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. */

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

+ if (options_size)

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)

Maarten Lankhorst

unread,
Sep 22, 2011, 7:50:01 AM9/22/11
to
Hey,

Even if load_options_size is not always completely reliable, could you at least prevent reading more than image->load_options_size? Code below doesn't seem to do that currently..

Thanks,
Maarten

Matt Fleming

unread,
Sep 22, 2011, 8:00:02 AM9/22/11
to
On Thu, 2011-09-22 at 13:43 +0200, Maarten Lankhorst wrote:

> Even if load_options_size is not always completely reliable, could you
> at least prevent reading more than image->load_options_size? Code
> below doesn't seem to do that currently..

Yeah, that's a very good point.

--
Matt Fleming, Intel Open Source Technology Center

Henrik Rydberg

unread,
Sep 29, 2011, 6:10:02 AM9/29/11
to
Using this version 3 of the patch, and a builtin commandline, I am
able to boot a MacBookAir3,1 directly into a blessed bzImage. The
screen comes up correctly.

Tested-by: Henrik Rydberg <ryd...@euromail.se>

As a side note, resume from suspend seems to leave the screen blank.

Thanks,
Henrik

Matt Fleming

unread,
Sep 30, 2011, 3:50:02 AM9/30/11
to
On Thu, 2011-09-29 at 12:14 +0200, Henrik Rydberg wrote:
> Using this version 3 of the patch, and a builtin commandline, I am
> able to boot a MacBookAir3,1 directly into a blessed bzImage. The
> screen comes up correctly.
>
> Tested-by: Henrik Rydberg <ryd...@euromail.se>

Great! Thanks for testing it out.

> As a side note, resume from suspend seems to leave the screen blank.

Hmm.. interesting. Presumably this only happens with these patches
applied? i.e. if you remove the patches you don't see a blank screen on
resume? I'll see if I can reproduce this on my macbook.

--
Matt Fleming, Intel Open Source Technology Center

Matt Fleming

unread,
Sep 30, 2011, 5:00:02 AM9/30/11
to
Cc: Andi Kleen <an...@firstfloor.org>
Cc: Maarten Lankhorst <m.b.la...@gmail.com>
Tested-by: Henrik Rydberg <ryd...@euromail.se>
Signed-off-by: Matt Fleming <matt.f...@intel.com>
---

v4:

- Don't read more than image->load_options_size (Maarten Lankhorst)
- Added Henrik's Tested-by

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(-)
index 0000000..c1b494d
--- /dev/null
+++ b/arch/x86/boot/compressed/eboot.c
@@ -0,0 +1,975 @@
+ u32 load_options_size = image->load_options_size;
+ 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) {
+ * the kernel to boot_params.pref_address, or as close as
+ * possible to it.

Henrik Rydberg

unread,
Sep 30, 2011, 7:10:01 AM9/30/11
to
On Fri, Sep 30, 2011 at 08:41:05AM +0100, Matt Fleming wrote:
> On Thu, 2011-09-29 at 12:14 +0200, Henrik Rydberg wrote:
> > Using this version 3 of the patch, and a builtin commandline, I am
> > able to boot a MacBookAir3,1 directly into a blessed bzImage. The
> > screen comes up correctly.
> >
> > Tested-by: Henrik Rydberg <ryd...@euromail.se>
>
> Great! Thanks for testing it out.
>
> > As a side note, resume from suspend seems to leave the screen blank.
>
> Hmm.. interesting. Presumably this only happens with these patches
> applied? i.e. if you remove the patches you don't see a blank screen on
> resume? I'll see if I can reproduce this on my macbook.

No, sorry, it never worked on this machine when booting from EFI.
AFAICT, there is no known way to control backlight either (nvidia
320M). If you have a similar setup, sharing findings would be most
interesting (off-line).

Thanks,
Henrik

Shea Levy

unread,
Sep 30, 2011, 12:30:01 PM9/30/11
to
> if (heap> ((-__PAGE_OFFSET-(128<<20)-1)& 0x7fffffff))
> + while (!(mask& 0x1)) {

> + mask = mask>> 1;
> + first++;
> + }
> +
> + while (mask& 0x1) {
> + 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)) {
> + 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,
> + 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)) {
> + 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);
> + while (*str&& *str != ' '&& *str != '\n')
> + 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,
> + 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;
> + 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) {
> + 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;

Hi Matt,

I would like to test this patch series (My EFI systems are MBP 5,5, Asus
p8p67 pro, and the VirtualBox EFI setup). Will you want to hear the
results? If so, is there a specific procedure or set of things to test
that you'll want to know about? Also, other than patch 10 should I use
the v3 version of the patches sent to this list, or is there a better
place to pull the patches from?

Thanks,
Shea Levy

Matt Fleming

unread,
Sep 30, 2011, 4:20:02 PM9/30/11
to
On Fri, 2011-09-30 at 12:24 -0400, Shea Levy wrote:
> Hi Matt,
>
> I would like to test this patch series (My EFI systems are MBP 5,5, Asus
> p8p67 pro, and the VirtualBox EFI setup). Will you want to hear the
> results? If so, is there a specific procedure or set of things to test
> that you'll want to know about? Also, other than patch 10 should I use
> the v3 version of the patches sent to this list, or is there a better
> place to pull the patches from?

Hi Shea,

I'd definitely like to hear the results if you could try it out on your
machines! There's no specific procedure to try, just try to break it ;-)

You can find the patches at the top of the 'x86/efi-stub' branch at,

https://github.com/mfleming/linux-2.6.git

--
Matt Fleming, Intel Open Source Technology Center

--

Maarten Lankhorst

unread,
Oct 1, 2011, 4:00:02 AM10/1/11
to
Hi Matt,

On 09/30/2011 10:11 PM, Matt Fleming wrote:
> On Fri, 2011-09-30 at 12:24 -0400, Shea Levy wrote:
>> Hi Matt,
>>
>> I would like to test this patch series (My EFI systems are MBP 5,5, Asus
>> p8p67 pro, and the VirtualBox EFI setup). Will you want to hear the
>> results? If so, is there a specific procedure or set of things to test
>> that you'll want to know about? Also, other than patch 10 should I use
>> the v3 version of the patches sent to this list, or is there a better
>> place to pull the patches from?
> Hi Shea,
>
> I'd definitely like to hear the results if you could try it out on your
> machines! There's no specific procedure to try, just try to break it ;-)
>
> You can find the patches at the top of the 'x86/efi-stub' branch at,
>
> https://github.com/mfleming/linux-2.6.git
>
Cute, I think you reintroduced the same bug in v4 with load_options_size ;)

Shouldn't it be /= 2 ?

Conditionally-signed-off-by-if-fixed-in-v5: Maarten Lankhorst <m.b.la...@gmail.com>

Still would love to see ASCII support though :(

~Maarten

Matt Fleming

unread,
Oct 1, 2011, 5:20:02 AM10/1/11
to
On Sat, 2011-10-01 at 09:50 +0200, Maarten Lankhorst wrote:
> Cute, I think you reintroduced the same bug in v4 with load_options_size ;)
>
> Shouldn't it be /= 2 ?

Gah! Yeah, you're right, it's just been one of those weeks. Thanks.

--
Matt Fleming, Intel Open Source Technology Center

Matthew Garrett

unread,
Oct 3, 2011, 1:00:02 PM10/3/11
to
On Fri, Sep 30, 2011 at 01:13:12PM +0200, Henrik Rydberg wrote:

> No, sorry, it never worked on this machine when booting from EFI.
> AFAICT, there is no known way to control backlight either (nvidia
> 320M). If you have a similar setup, sharing findings would be most
> interesting (off-line).

Should work with current nouveau git.

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

Henrik Rydberg

unread,
Oct 3, 2011, 2:30:03 PM10/3/11
to
On Mon, Oct 03, 2011 at 05:53:32PM +0100, Matthew Garrett wrote:
> On Fri, Sep 30, 2011 at 01:13:12PM +0200, Henrik Rydberg wrote:
>
> > No, sorry, it never worked on this machine when booting from EFI.
> > AFAICT, there is no known way to control backlight either (nvidia
> > 320M). If you have a similar setup, sharing findings would be most
> > interesting (off-line).
>
> Should work with current nouveau git.

Indeed! Graphics, backlight and resume works. Outstanding, thank you.

Henrik

0 new messages