summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKay Sievers <kay@vrfy.org>2015-01-24 21:09:41 +0100
committerKay Sievers <kay@vrfy.org>2015-01-25 06:36:40 +0100
commitd96381e90e5f1952ee6d7bf6e51daa2b344319af (patch)
tree39b4e934d6e02c398d8496b963c1ece67ab80e40
parent83c71c06efa3fea9b77c85322957f2ec80805904 (diff)
stub: execute EFI image with an embedded kernel, initrd, cmdline sections
Locate the following embedded PE sections .linux - bzImage .inird - initrd cpio archive .cmdline - kernel command line and hand over control to the bzImage.
-rw-r--r--.gitignore5
-rw-r--r--Makefile.am83
-rw-r--r--src/efi/graphics.c2
-rw-r--r--src/efi/gummiboot.c13
-rw-r--r--src/efi/linux.c128
-rw-r--r--src/efi/linux.h23
-rw-r--r--src/efi/pefile.c4
-rw-r--r--src/efi/stub.c71
-rw-r--r--src/efi/util.c8
-rwxr-xr-xtest/test-create-disk.sh7
10 files changed, 303 insertions, 41 deletions
diff --git a/.gitignore b/.gitignore
index 047bb03..d482a5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,8 +9,11 @@
.dirstamp
/gummiboot
/gummiboot.so
-/gummibootia32.efi
/gummibootx64.efi
+/gummibootia32.efi
+/stub.so
+/stubx64.efi
+/stubia32.efi
/test-disk
Makefile
diff --git a/Makefile.am b/Makefile.am
index c4cda7f..528f83c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -74,20 +74,6 @@ CLEANFILES += man/gummiboot.8
# EFI compilation -- this part of the build system uses custom make rules and
# bypasses regular automake to provide absolute control on compiler and linker
# flags.
-efi_loadername = gummiboot$(MACHINE_TYPE_NAME).efi
-efi_sources = \
- src/efi/util.c \
- src/efi/console.c \
- src/efi/graphics.c \
- src/efi/pefile.c \
- src/efi/gummiboot.c
-
-efi_headers = \
- src/efi/util.h \
- src/efi/console.h \
- src/efi/graphics.h \
- src/efi/pefile.h
-
efi_cppflags = \
$(EFI_CPPFLAGS) \
-I$(top_builddir) -include config.h \
@@ -128,25 +114,74 @@ efi_ldflags = \
-L $(EFI_LIB_DIR) \
$(EFI_LDS_DIR)/crt0-efi-$(ARCH).o
-efi_objects = $(addprefix $(top_builddir)/,$(efi_sources:.c=.o))
-efi_solib = $(top_builddir)/src/efi/gummiboot.so
+# ------------------------------------------------------------------------------
+gummiboot_headers = \
+ src/efi/util.h \
+ src/efi/console.h \
+ src/efi/graphics.h \
+ src/efi/pefile.h
+
+gummiboot_sources = \
+ src/efi/util.c \
+ src/efi/console.c \
+ src/efi/graphics.c \
+ src/efi/pefile.c \
+ src/efi/gummiboot.c
+
+gummiboot_objects = $(addprefix $(top_builddir)/,$(gummiboot_sources:.c=.o))
+gummiboot_solib = $(top_builddir)/src/efi/gummiboot.so
+gummiboot = gummiboot$(MACHINE_TYPE_NAME).efi
+
+gummibootlib_DATA = $(gummiboot)
+CLEANFILES += $(gummiboot_objects) $(gummiboot_solib) $(gummiboot)
+EXTRA_DIST += $(gimmiboot_sources) $(gummiboot_headers)
+
+$(top_builddir)/src/efi/%.o: $(top_srcdir)/src/efi/%.c $(addprefix $(top_srcdir)/,$(gummiboot_headers))
+ @$(MKDIR_P) $(top_builddir)/src/efi/
+ $(AM_V_CC)$(EFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@
+
+$(gummiboot_solib): $(gummiboot_objects)
+ $(AM_V_CCLD)$(LD) $(efi_ldflags) $(gummiboot_objects) \
+ -o $@ -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name); \
+ nm -D -u $@ | grep ' U ' && exit 1 || :
+.DELETE_ON_ERROR: $(gummboot_solib)
+
+$(gummiboot): $(gummiboot_solib)
+ $(AM_V_GEN) objcopy -j .text -j .sdata -j .data -j .dynamic \
+ -j .dynsym -j .rel -j .rela -j .reloc \
+ --target=efi-app-$(ARCH) $< $@
# ------------------------------------------------------------------------------
-gummibootlib_DATA = $(efi_loadername)
-CLEANFILES += $(efi_objects) $(efi_solib) $(efi_loadername)
-EXTRA_DIST += $(efi_sources) $(efi_headers)
+stub_headers = \
+ src/efi/util.h \
+ src/efi/pefile.h \
+ src/efi/linux.h
+
+stub_sources = \
+ src/efi/util.c \
+ src/efi/pefile.c \
+ src/efi/linux.c \
+ src/efi/stub.c
+
+stub_objects = $(addprefix $(top_builddir)/,$(stub_sources:.c=.o))
+stub_solib = $(top_builddir)/src/efi/stub.so
+stub = stub$(MACHINE_TYPE_NAME).efi
+
+gummibootlib_DATA += $(stub)
+CLEANFILES += $(stub_objects) $(stub_solib) $(stub)
+EXTRA_DIST += $(stub_sources) $(stub_headers)
-$(top_builddir)/src/efi/%.o: $(top_srcdir)/src/efi/%.c $(addprefix $(top_srcdir)/,$(efi_headers))
+$(top_builddir)/src/efi/%.o: $(top_srcdir)/src/efi/%.c $(addprefix $(top_srcdir)/,$(stub_headers))
@$(MKDIR_P) $(top_builddir)/src/efi/
$(AM_V_CC)$(EFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@
-$(efi_solib): $(efi_objects)
- $(AM_V_CCLD)$(LD) $(efi_ldflags) $(efi_objects) \
+$(stub_solib): $(stub_objects)
+ $(AM_V_CCLD)$(LD) $(efi_ldflags) $(stub_objects) \
-o $@ -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name); \
nm -D -u $@ | grep ' U ' && exit 1 || :
-.DELETE_ON_ERROR: $(efi_solib)
+.DELETE_ON_ERROR: $(gummboot_solib)
-$(efi_loadername): $(efi_solib)
+$(stub): $(stub_solib)
$(AM_V_GEN) objcopy -j .text -j .sdata -j .data -j .dynamic \
-j .dynsym -j .rel -j .rela -j .reloc \
--target=efi-app-$(ARCH) $< $@
diff --git a/src/efi/graphics.c b/src/efi/graphics.c
index 0810538..11305b8 100644
--- a/src/efi/graphics.c
+++ b/src/efi/graphics.c
@@ -208,7 +208,7 @@ EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib,
return EFI_SUCCESS;
}
-static void pixel_blend(UINT32 *dst, const UINT32 source) {
+static VOID pixel_blend(UINT32 *dst, const UINT32 source) {
UINT32 alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g;
alpha = (source & 0xff);
diff --git a/src/efi/gummiboot.c b/src/efi/gummiboot.c
index f9da90d..ff1226d 100644
--- a/src/efi/gummiboot.c
+++ b/src/efi/gummiboot.c
@@ -32,6 +32,7 @@
#include "console.h"
#include "graphics.h"
#include "pefile.h"
+#include "linux.h"
#ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
@@ -64,7 +65,7 @@ typedef struct {
CHAR16 *options;
CHAR16 *splash;
CHAR16 key;
- EFI_STATUS (*call)(void);
+ EFI_STATUS (*call)(VOID);
BOOLEAN no_autoselect;
BOOLEAN non_unique;
} ConfigEntry;
@@ -85,7 +86,7 @@ typedef struct {
CHAR16 *entries_auto;
} Config;
-static void cursor_left(UINTN *cursor, UINTN *first)
+static VOID cursor_left(UINTN *cursor, UINTN *first)
{
if ((*cursor) > 0)
(*cursor)--;
@@ -93,7 +94,7 @@ static void cursor_left(UINTN *cursor, UINTN *first)
(*first)--;
}
-static void cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
+static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
{
if ((*cursor)+1 < x_max)
(*cursor)++;
@@ -1558,7 +1559,7 @@ static VOID config_title_generate(Config *config) {
}
}
-static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(void)) {
+static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(VOID)) {
ConfigEntry *entry;
entry = AllocateZeroPool(sizeof(ConfigEntry));
@@ -1766,7 +1767,7 @@ static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, con
if (options) {
EFI_LOADED_IMAGE *loaded_image;
- err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (void **)&loaded_image,
+ err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(err)) {
Print(L"Error getting LoadedImageProtocol handle: %r", err);
@@ -1847,7 +1848,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
efivar_set(L"LoaderFirmwareType", s, FALSE);
FreePool(s);
- err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (void **)&loaded_image,
+ err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(err)) {
Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
diff --git a/src/efi/linux.c b/src/efi/linux.c
new file mode 100644
index 0000000..922a30f
--- /dev/null
+++ b/src/efi/linux.c
@@ -0,0 +1,128 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "util.h"
+#include "linux.h"
+
+#define SETUP_MAGIC 0x53726448 /* "HdrS" */
+struct SetupHeader {
+ UINT8 boot_sector[0x01f1];
+ UINT8 setup_secs;
+ UINT16 root_flags;
+ UINT32 sys_size;
+ UINT16 ram_size;
+ UINT16 video_mode;
+ UINT16 root_dev;
+ UINT16 signature;
+ UINT16 jump;
+ UINT32 header;
+ UINT16 version;
+ UINT16 su_switch;
+ UINT16 setup_seg;
+ UINT16 start_sys;
+ UINT16 kernel_ver;
+ UINT8 loader_id;
+ UINT8 load_flags;
+ UINT16 movesize;
+ UINT32 code32_start;
+ UINT32 ramdisk_start;
+ UINT32 ramdisk_len;
+ UINT32 bootsect_kludge;
+ UINT16 heap_end;
+ UINT8 ext_loader_ver;
+ UINT8 ext_loader_type;
+ UINT32 cmd_line_ptr;
+ UINT32 ramdisk_max;
+ UINT32 kernel_alignment;
+ UINT8 relocatable_kernel;
+ UINT8 min_alignment;
+ UINT16 xloadflags;
+ UINT32 cmdline_size;
+ UINT32 hardware_subarch;
+ UINT64 hardware_subarch_data;
+ UINT32 payload_offset;
+ UINT32 payload_length;
+ UINT64 setup_data;
+ UINT64 pref_address;
+ UINT32 init_size;
+ UINT32 handover_offset;
+} __attribute__((packed));
+
+#ifdef __x86_64__
+typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup);
+static inline VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) {
+ handover_f handover;
+
+ asm volatile ("cli");
+ handover = (handover_f)((UINTN)setup->code32_start + 512 + setup->handover_offset);
+ handover(image, ST, setup);
+}
+#else
+typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup) __attribute__((regparm(0)));
+static inline VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) {
+ handover_f handover;
+
+ handover = (handover_f)((UINTN)boot_setup->code32_start + setup->handover_offset);
+ handover(image, ST, setup);
+}
+#endif
+
+EFI_STATUS linux_exec(EFI_HANDLE *image,
+ CHAR8 *cmdline, UINTN linux_addr,
+ UINTN initrd_addr, UINTN initrd_size) {
+ struct SetupHeader *image_setup;
+ struct SetupHeader *boot_setup;
+ EFI_PHYSICAL_ADDRESS addr;
+ EFI_STATUS err;
+
+ image_setup = (struct SetupHeader *)(linux_addr);
+ if (image_setup->signature != 0xAA55 || image_setup->header != SETUP_MAGIC)
+ return EFI_LOAD_ERROR;
+
+ if (image_setup->version < 0x20b || !image_setup->relocatable_kernel)
+ return EFI_LOAD_ERROR;
+
+ addr = 0x3fffffff;
+ err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
+ EFI_SIZE_TO_PAGES(0x4000), &addr);
+ if (EFI_ERROR(err))
+ return err;
+ boot_setup = (struct SetupHeader *)addr;
+ ZeroMem(boot_setup, 0x4000);
+ CopyMem(boot_setup, image_setup, sizeof(struct SetupHeader));
+ boot_setup->loader_id = 0xff;
+
+ boot_setup->code32_start = (UINT32)linux_addr + (image_setup->setup_secs+1) * 512;
+
+ if (cmdline) {
+ addr = 0xA0000;
+ err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
+ EFI_SIZE_TO_PAGES(strlena(cmdline) + 1), &addr);
+ if (EFI_ERROR(err))
+ return err;
+ CopyMem((VOID *)addr, cmdline, strlena(cmdline) + 1);
+ boot_setup->cmd_line_ptr = (UINT32)addr;
+ }
+
+ boot_setup->ramdisk_start = (UINT32)initrd_addr;
+ boot_setup->ramdisk_len = (UINT32)initrd_size;
+
+ linux_efi_handover(image, boot_setup);
+ return EFI_LOAD_ERROR;
+}
diff --git a/src/efi/linux.h b/src/efi/linux.h
new file mode 100644
index 0000000..020c728
--- /dev/null
+++ b/src/efi/linux.h
@@ -0,0 +1,23 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
+ */
+
+#ifndef __GUMMIBOOT_kernel_H
+#define __GUMMIBOOT_kernel_H
+
+EFI_STATUS linux_exec(EFI_HANDLE *image,
+ CHAR8 *cmdline, UINTN linux_addr,
+ UINTN initrd_addr, UINTN initrd_size);
+#endif
diff --git a/src/efi/pefile.c b/src/efi/pefile.c
index 6ac28da..5d750e0 100644
--- a/src/efi/pefile.c
+++ b/src/efi/pefile.c
@@ -11,8 +11,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
- * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
- * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
+ * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
*/
#include <efi.h>
@@ -154,7 +153,6 @@ EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections,
err = EFI_LOAD_ERROR;
goto out;
}
-
for (j = 0; sections[j]; j++) {
if (strcmpa(sections[j], sect.Name) != 0)
continue;
diff --git a/src/efi/stub.c b/src/efi/stub.c
new file mode 100644
index 0000000..c1be3d6
--- /dev/null
+++ b/src/efi/stub.c
@@ -0,0 +1,71 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/* This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "util.h"
+#include "pefile.h"
+#include "linux.h"
+
+EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+ EFI_LOADED_IMAGE *loaded_image;
+ EFI_FILE *root_dir;
+ CHAR16 *loaded_image_path;
+ CHAR8 *sections[] = {
+ (UINT8 *)".cmdline",
+ (UINT8 *)".linux",
+ (UINT8 *)".initrd",
+ NULL
+ };
+ UINTN addrs[3] = {};
+ UINTN offs[3] = {};
+ UINTN szs[3] = {};
+ EFI_STATUS err;
+
+ InitializeLib(image, sys_table);
+
+ err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
+ image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(err)) {
+ Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return err;
+ }
+
+ root_dir = LibOpenRoot(loaded_image->DeviceHandle);
+ if (!root_dir) {
+ Print(L"Unable to open root directory: %r ", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return EFI_LOAD_ERROR;
+ }
+
+ loaded_image_path = DevicePathToStr(loaded_image->FilePath);
+
+ err = pefile_locate_sections(root_dir, loaded_image_path, sections, addrs, offs, szs);
+ if (EFI_ERROR(err)) {
+ Print(L"Unable to locate embedded .linux section: %r ", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return err;
+ }
+
+ err = linux_exec(image, (CHAR8 *)"",
+ (UINTN)loaded_image->ImageBase + addrs[1],
+ (UINTN)loaded_image->ImageBase + addrs[2], szs[2]);
+
+ Print(L"Execution of embedded linux image failed: %r\n", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return err;
+}
diff --git a/src/efi/util.c b/src/efi/util.c
index 191c071..5678b50 100644
--- a/src/efi/util.c
+++ b/src/efi/util.c
@@ -28,13 +28,13 @@
static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} };
#ifdef __x86_64__
-UINT64 ticks_read(void) {
+UINT64 ticks_read(VOID) {
UINT64 a, d;
__asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
return (d << 32) | a;
}
#else
-UINT64 ticks_read(void) {
+UINT64 ticks_read(VOID) {
UINT64 val;
__asm__ volatile ("rdtsc" : "=A" (val));
return val;
@@ -42,7 +42,7 @@ UINT64 ticks_read(void) {
#endif
/* count TSC ticks during a millisecond delay */
-UINT64 ticks_freq(void) {
+UINT64 ticks_freq(VOID) {
UINT64 ticks_start, ticks_end;
ticks_start = ticks_read();
@@ -52,7 +52,7 @@ UINT64 ticks_freq(void) {
return (ticks_end - ticks_start) * 1000;
}
-UINT64 time_usec(void) {
+UINT64 time_usec(VOID) {
UINT64 ticks;
static UINT64 freq;
diff --git a/test/test-create-disk.sh b/test/test-create-disk.sh
index 3982223..9fdb3c3 100755
--- a/test/test-create-disk.sh
+++ b/test/test-create-disk.sh
@@ -19,8 +19,11 @@ cp test/splash.bmp mnt/EFI/gummiboot/
[ -e /boot/shellx64.efi ] && cp /boot/shellx64.efi mnt/
mkdir mnt/EFI/Linux
-objcopy --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \
- gummibootx64.efi mnt/EFI/Linux/Test-Linux.efi
+objcopy \
+ --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \
+ --add-section .linux=/boot/$(cat /etc/machine-id)/$(uname -r)/linux --change-section-vma .linux=0x30000 \
+ --add-section .initrd=/boot/$(cat /etc/machine-id)/$(uname -r)/initrd --change-section-vma .initrd=0x3000000 \
+ stubx64.efi mnt/EFI/Linux/linux-test.efi
# install entries
mkdir -p mnt/loader/entries