sd-boot: add EFI boot manager and stub loader

This commit is contained in:
Kay Sievers 2015-02-08 12:25:35 +01:00
parent 484adfd914
commit 0fa2cac4f0
19 changed files with 3690 additions and 6 deletions

3
.gitignore vendored
View File

@ -45,6 +45,9 @@
/machinectl
/mtd_probe
/networkctl
/linuxx64.efi.stub
/sd-bootx64.efi
/test-efi-disk.img
/scsi_id
/systemadm
/systemctl

View File

@ -111,6 +111,7 @@ catalogdir=$(prefix)/lib/systemd/catalog
kernelinstalldir = $(prefix)/lib/kernel/install.d
factory_etcdir = $(prefix)/share/factory/etc
factory_pamdir = $(prefix)/share/factory/etc/pam.d
sd_bootlibdir = $(prefix)/lib/systemd/sd-boot
# And these are the special ones for /
rootprefix=@rootprefix@
@ -2497,6 +2498,126 @@ dist_bashcompletion_DATA += \
dist_zshcompletion_DATA += \
shell-completion/zsh/_bootctl
# ------------------------------------------------------------------------------
efi_cppflags = \
$(EFI_CPPFLAGS) \
-I$(top_builddir) -include config.h \
-I$(EFI_INC_DIR)/efi \
-I$(EFI_INC_DIR)/efi/$(EFI_ARCH) \
-DEFI_MACHINE_TYPE_NAME=\"$(EFI_MACHINE_TYPE_NAME)\"
efi_cflags = \
$(EFI_CFLAGS) \
-Wall \
-Wextra \
-nostdinc \
-ggdb -O0 \
-fpic \
-fshort-wchar \
-nostdinc \
-ffreestanding \
-fno-strict-aliasing \
-fno-stack-protector \
-Wsign-compare \
-mno-sse \
-mno-mmx
if ARCH_X86_64
efi_cflags += \
-mno-red-zone \
-DEFI_FUNCTION_WRAPPER \
-DGNU_EFI_USE_MS_ABI
endif
efi_ldflags = \
$(EFI_LDFLAGS) \
-T $(EFI_LDS_DIR)/elf_$(EFI_ARCH)_efi.lds \
-shared \
-Bsymbolic \
-nostdlib \
-znocombreloc \
-L $(EFI_LIB_DIR) \
$(EFI_LDS_DIR)/crt0-efi-$(EFI_ARCH).o
# ------------------------------------------------------------------------------
sd_boot_headers = \
src/sd-boot/util.h \
src/sd-boot/console.h \
src/sd-boot/graphics.h \
src/sd-boot/pefile.h
sd_boot_sources = \
src/sd-boot/util.c \
src/sd-boot/console.c \
src/sd-boot/graphics.c \
src/sd-boot/pefile.c \
src/sd-boot/sd-boot.c
sd_boot_objects = $(addprefix $(top_builddir)/,$(sd_boot_sources:.c=.o))
sd_boot_solib = $(top_builddir)/src/sd-boot/sd_boot.so
sd_boot = sd-boot$(EFI_MACHINE_TYPE_NAME).efi
sd_bootlib_DATA = $(sd_boot)
CLEANFILES += $(sd_boot_objects) $(sd_boot_solib) $(sd_boot)
EXTRA_DIST += $(sd_boot_sources) $(sd_boot_headers)
$(top_builddir)/src/sd-boot/%.o: $(top_srcdir)/src/sd-boot/%.c $(addprefix $(top_srcdir)/,$(sd_boot_headers))
@$(MKDIR_P) $(top_builddir)/src/sd-boot/
$(AM_V_CC)$(EFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@
$(sd_boot_solib): $(sd_boot_objects)
$(AM_V_CCLD)$(LD) $(efi_ldflags) $(sd_boot_objects) \
-o $@ -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name); \
nm -D -u $@ | grep ' U ' && exit 1 || :
$(sd_boot): $(sd_boot_solib)
$(AM_V_GEN) objcopy -j .text -j .sdata -j .data -j .dynamic \
-j .dynsym -j .rel -j .rela -j .reloc \
--target=efi-app-$(EFI_ARCH) $< $@
# ------------------------------------------------------------------------------
stub_headers = \
src/sd-boot/util.h \
src/sd-boot/pefile.h \
src/sd-boot/linux.h
stub_sources = \
src/sd-boot/util.c \
src/sd-boot/pefile.c \
src/sd-boot/linux.c \
src/sd-boot/stub.c
stub_objects = $(addprefix $(top_builddir)/,$(stub_sources:.c=.o))
stub_solib = $(top_builddir)/src/sd-boot/stub.so
stub = linux$(EFI_MACHINE_TYPE_NAME).efi.stub
sd_bootlib_DATA += $(stub)
CLEANFILES += $(stub_objects) $(stub_solib) $(stub)
EXTRA_DIST += $(stub_sources) $(stub_headers)
$(top_builddir)/src/sd-boot/%.o: $(top_srcdir)/src/sd-boot/%.c $(addprefix $(top_srcdir)/,$(stub_headers))
@$(MKDIR_P) $(top_builddir)/src/sd-boot/
$(AM_V_CC)$(EFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@
$(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 || :
$(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-$(EFI_ARCH) $< $@
# ------------------------------------------------------------------------------
CLEANFILES += test-efi-disk.img
EXTRA_DIST += test/test-efi-create-disk.sh
test-efi-disk.img: $(sd_boot) $(stub) test/test-efi-create-disk.sh
$(AM_V_GEN)test/test-efi-create-disk.sh
test-efi: test-efi-disk.img
$(QEMU) -machine accel=kvm -m 1024 -bios $(QEMU_BIOS) -snapshot test-efi-disk.img
endif
# ------------------------------------------------------------------------------

View File

@ -38,19 +38,17 @@ AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax no-di
AM_SILENT_RULES([yes])
AC_CANONICAL_HOST
AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.])
AS_IF([test "x$host_cpu" = "xmips" || test "x$host_cpu" = "xmipsel" ||
test "x$host_cpu" = "xmips64" || test "x$host_cpu" = "xmips64el"],
[AC_DEFINE(ARCH_MIPS, [], [Whether on mips arch])])
LT_PREREQ(2.2)
LT_INIT([disable-static])
AS_IF([test "x$enable_static" = "xyes"], [AC_MSG_ERROR([--enable-static is not supported by systemd])])
AS_IF([test "x$enable_largefile" = "xno"], [AC_MSG_ERROR([--disable-largefile is not supported by systemd])])
# i18n stuff for the PolicyKit policy files
SET_ARCH(X86_64, x86_64*)
SET_ARCH(IA32, i*86*)
SET_ARCH(MIPS, mips*)
# Check whether intltool can be found, disable NLS otherwise
# i18n stuff for the PolicyKit policy files, heck whether intltool can be found, disable NLS otherwise
AC_CHECK_PROG(intltool_found, [intltool-merge], [yes], [no])
AS_IF([test x"$intltool_found" != xyes],
[AS_IF([test x"$enable_nls" = xyes],
@ -1144,6 +1142,63 @@ if test "x$enable_efi" != "xno"; then
fi
AM_CONDITIONAL(ENABLE_EFI, [test "x$have_efi" = "xyes"])
# ------------------------------------------------------------------------------
EFI_CC=gcc
AC_SUBST([EFI_CC])
EFI_ARCH=`echo $host | sed "s/\(-\).*$//"`
AM_COND_IF(ARCH_IA32, [
EFI_ARCH=ia32
EFI_MACHINE_TYPE_NAME=ia32])
AM_COND_IF(ARCH_X86_64, [
EFI_MACHINE_TYPE_NAME=x64])
AC_SUBST([EFI_ARCH])
AC_SUBST([EFI_MACHINE_TYPE_NAME])
have_gnuefi=no
AC_ARG_ENABLE(gnuefi, AS_HELP_STRING([--enable-gnuefi], [Disable optional gnuefi support]))
AS_IF([test "x$enable_gnuefi" != "xno"], [
AC_CHECK_HEADERS(efi/${EFI_ARCH}/efibind.h,
[AC_DEFINE(HAVE_GNUEFI, 1, [Define if gnuefi is available])
have_gnuefi=yes],
[AS_IF([test "x$have_gnuefi" = xyes], [AC_MSG_ERROR([*** gnuefi support requested but headers not found])])
])
])
AM_CONDITIONAL(HAVE_GNUEFI, [test "$have_gnuefi" = "yes"])
if test "x$enable_gnuefi" != "xno"; then
efiroot=$(echo $(cd /usr/lib/$(gcc -print-multi-os-directory); pwd))
EFI_LIB_DIR="$efiroot"
AC_ARG_WITH(efi-libdir,
AS_HELP_STRING([--with-efi-libdir=PATH], [Path to efi lib directory]),
[EFI_LIB_DIR="$withval"], [EFI_LIB_DIR="$efiroot"]
)
AC_SUBST([EFI_LIB_DIR])
AC_ARG_WITH(efi-ldsdir,
AS_HELP_STRING([--with-efi-ldsdir=PATH], [Path to efi lds directory]),
[EFI_LDS_DIR="$withval"],
[
for EFI_LDS_DIR in "${efiroot}/gnuefi" "${efiroot}"; do
for lds in ${EFI_LDS_DIR}/elf_${EFI_ARCH}_efi.lds; do
test -f ${lds} && break 2
done
done
]
)
AC_SUBST([EFI_LDS_DIR])
AC_ARG_WITH(efi-includedir,
AS_HELP_STRING([--with-efi-includedir=PATH], [Path to efi include directory]),
[EFI_INC_DIR="$withval"], [EFI_INC_DIR="/usr/include"]
)
AC_SUBST([EFI_INC_DIR])
fi
# ------------------------------------------------------------------------------
AC_ARG_WITH(unifont,
AS_HELP_STRING([--with-unifont=PATH],
@ -1392,6 +1447,14 @@ AS_IF([test "x$0" != "x./configure"], [
AC_SUBST([INTLTOOL_UPDATE], [/bin/true])
])
# QEMU and OVMF UEFI firmware
AS_IF([test x"$cross_compiling" = "xyes"], [], [
AC_PATH_PROG([QEMU], [qemu-system-x86_64])
AC_CHECK_FILE([/usr/share/qemu/bios-ovmf.bin], [QEMU_BIOS=/usr/share/qemu/bios-ovmf.bin])
AC_CHECK_FILE([/usr/share/qemu-ovmf/bios.bin], [QEMU_BIOS=/usr/share/qemu-ovmf/bios.bin])
AC_SUBST([QEMU_BIOS])
])
AC_ARG_ENABLE(tests,
[AC_HELP_STRING([--disable-tests], [disable tests])],
enable_tests=$enableval, enable_tests=yes)
@ -1496,6 +1559,13 @@ AC_MSG_RESULT([
coredump: ${have_coredump}
polkit: ${have_polkit}
efi: ${have_efi}
gnuefi: ${have_gnuefi}
efi arch: ${EFI_ARCH}
EFI machine type: ${EFI_MACHINE_TYPE_NAME}
EFI CC ${EFI_CC}
EFI libdir: ${EFI_LIB_DIR}
EFI ldsdir: ${EFI_LDS_DIR}
EFI includedir: ${EFI_INC_DIR}
kmod: ${have_kmod}
xkbcommon: ${have_xkbcommon}
blkid: ${have_blkid}

13
m4/arch.m4 Normal file
View File

@ -0,0 +1,13 @@
dnl SET_ARCH(ARCHNAME, PATTERN)
dnl
dnl Define ARCH_<archname> condition if the pattern match with the current
dnl architecture
dnl
AC_DEFUN([SET_ARCH], [
cpu_$1=false
case "$host" in
$2) cpu_$1=true ;;
esac
AM_CONDITIONAL(AS_TR_CPP(ARCH_$1), [test "x$cpu_$1" = xtrue])
])

2
src/sd-boot/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/sd_boot.so
/stub.so

141
src/sd-boot/console.c Normal file
View File

@ -0,0 +1,141 @@
/*-*- 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) 2012-2013 Kay Sievers <kay@vrfy.org>
* Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
*/
#include <efi.h>
#include <efilib.h>
#include "util.h"
#include "console.h"
#define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
{ 0xdd9e7534, 0x7762, 0x4698, { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa } }
struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
typedef EFI_STATUS (EFIAPI *EFI_INPUT_RESET_EX)(
struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
BOOLEAN ExtendedVerification;
);
typedef UINT8 EFI_KEY_TOGGLE_STATE;
typedef struct {
UINT32 KeyShiftState;
EFI_KEY_TOGGLE_STATE KeyToggleState;
} EFI_KEY_STATE;
typedef struct {
EFI_INPUT_KEY Key;
EFI_KEY_STATE KeyState;
} EFI_KEY_DATA;
typedef EFI_STATUS (EFIAPI *EFI_INPUT_READ_KEY_EX)(
struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
EFI_KEY_DATA *KeyData;
);
typedef EFI_STATUS (EFIAPI *EFI_SET_STATE)(
struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
EFI_KEY_TOGGLE_STATE *KeyToggleState;
);
typedef EFI_STATUS (EFIAPI *EFI_KEY_NOTIFY_FUNCTION)(
EFI_KEY_DATA *KeyData;
);
typedef EFI_STATUS (EFIAPI *EFI_REGISTER_KEYSTROKE_NOTIFY)(
struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
EFI_KEY_DATA KeyData;
EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction;
VOID **NotifyHandle;
);
typedef EFI_STATUS (EFIAPI *EFI_UNREGISTER_KEYSTROKE_NOTIFY)(
struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This;
VOID *NotificationHandle;
);
typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL {
EFI_INPUT_RESET_EX Reset;
EFI_INPUT_READ_KEY_EX ReadKeyStrokeEx;
EFI_EVENT WaitForKeyEx;
EFI_SET_STATE SetState;
EFI_REGISTER_KEYSTROKE_NOTIFY RegisterKeyNotify;
EFI_UNREGISTER_KEYSTROKE_NOTIFY UnregisterKeyNotify;
} EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) {
EFI_GUID EfiSimpleTextInputExProtocolGuid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx;
static BOOLEAN checked;
UINTN index;
EFI_INPUT_KEY k;
EFI_STATUS err;
if (!checked) {
err = LibLocateProtocol(&EfiSimpleTextInputExProtocolGuid, (VOID **)&TextInputEx);
if (EFI_ERROR(err))
TextInputEx = NULL;
checked = TRUE;
}
/* wait until key is pressed */
if (wait) {
if (TextInputEx)
uefi_call_wrapper(BS->WaitForEvent, 3, 1, &TextInputEx->WaitForKeyEx, &index);
else
uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
}
if (TextInputEx) {
EFI_KEY_DATA keydata;
UINT64 keypress;
err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata);
if (!EFI_ERROR(err)) {
UINT32 shift = 0;
/* do not distinguish between left and right keys */
if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) {
if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED))
shift |= EFI_CONTROL_PRESSED;
if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED))
shift |= EFI_ALT_PRESSED;
};
/* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar);
if (keypress > 0) {
*key = keypress;
return 0;
}
}
}
/* fallback for firmware which does not support SimpleTextInputExProtocol
*
* This is also called in case ReadKeyStrokeEx did not return a key, because
* some broken firmwares offer SimpleTextInputExProtocol, but never acually
* handle any key. */
err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k);
if (EFI_ERROR(err))
return err;
*key = KEYPRESS(0, k.ScanCode, k.UnicodeChar);
return 0;
}

34
src/sd-boot/console.h Normal file
View File

@ -0,0 +1,34 @@
/*-*- 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) 2012-2013 Kay Sievers <kay@vrfy.org>
* Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
*/
#ifndef __SDBOOT_CONSOLE_H
#define __SDBOOT_CONSOLE_H
#define EFI_SHIFT_STATE_VALID 0x80000000
#define EFI_RIGHT_CONTROL_PRESSED 0x00000004
#define EFI_LEFT_CONTROL_PRESSED 0x00000008
#define EFI_RIGHT_ALT_PRESSED 0x00000010
#define EFI_LEFT_ALT_PRESSED 0x00000020
#define EFI_CONTROL_PRESSED (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED)
#define EFI_ALT_PRESSED (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED)
#define KEYPRESS(keys, scan, uni) ((((UINT64)keys) << 32) | ((scan) << 16) | (uni))
#define KEYCHAR(k) ((k) & 0xffff)
#define CHAR_CTRL(c) ((c) - 'a' + 1)
EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait);
#endif

389
src/sd-boot/graphics.c Normal file
View File

@ -0,0 +1,389 @@
/*-*- 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) 2012-2013 Kay Sievers <kay@vrfy.org>
* Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
* Copyright (C) 2013 Intel Corporation
* Authored by Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
*/
#include <efi.h>
#include <efilib.h>
#include "util.h"
#include "graphics.h"
EFI_STATUS graphics_mode(BOOLEAN on) {
#define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \
{ 0xf42f7782, 0x12e, 0x4c12, { 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21 } };
struct _EFI_CONSOLE_CONTROL_PROTOCOL;
typedef enum {
EfiConsoleControlScreenText,
EfiConsoleControlScreenGraphics,
EfiConsoleControlScreenMaxValue,
} EFI_CONSOLE_CONTROL_SCREEN_MODE;
typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE)(
struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode,
BOOLEAN *UgaExists,
BOOLEAN *StdInLocked
);
typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE)(
struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
);
typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN)(
struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
CHAR16 *Password
);
typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL {
EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode;
EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn;
} EFI_CONSOLE_CONTROL_PROTOCOL;
EFI_GUID ConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
EFI_CONSOLE_CONTROL_SCREEN_MODE new;
EFI_CONSOLE_CONTROL_SCREEN_MODE current;
BOOLEAN uga_exists;
BOOLEAN stdin_locked;
EFI_STATUS err;
err = LibLocateProtocol(&ConsoleControlProtocolGuid, (VOID **)&ConsoleControl);
if (EFI_ERROR(err)) {
/* console control protocol is nonstandard and might not exist. */
return err == EFI_NOT_FOUND ? EFI_SUCCESS : err;
}
/* check current mode */
err = uefi_call_wrapper(ConsoleControl->GetMode, 4, ConsoleControl, &current, &uga_exists, &stdin_locked);
if (EFI_ERROR(err))
return err;
/* do not touch the mode */
new = on ? EfiConsoleControlScreenGraphics : EfiConsoleControlScreenText;
if (new == current)
return EFI_SUCCESS;
err = uefi_call_wrapper(ConsoleControl->SetMode, 2, ConsoleControl, new);
/* some firmware enables the cursor when switching modes */
uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
return err;
}
struct bmp_file {
CHAR8 signature[2];
UINT32 size;
UINT16 reserved[2];
UINT32 offset;
} __attribute__((packed));
/* we require at least BITMAPINFOHEADER, later versions are
accepted, but their features ignored */
struct bmp_dib {
UINT32 size;
UINT32 x;
UINT32 y;
UINT16 planes;
UINT16 depth;
UINT32 compression;
UINT32 image_size;
INT32 x_pixel_meter;
INT32 y_pixel_meter;
UINT32 colors_used;
UINT32 colors_important;
} __attribute__((packed));
struct bmp_map {
UINT8 blue;
UINT8 green;
UINT8 red;
UINT8 reserved;
} __attribute__((packed));
EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib,
struct bmp_map **ret_map, UINT8 **pixmap) {
struct bmp_file *file;
struct bmp_dib *dib;
struct bmp_map *map;
UINTN row_size;
if (size < sizeof(struct bmp_file) + sizeof(struct bmp_dib))
return EFI_INVALID_PARAMETER;
/* check file header */
file = (struct bmp_file *)bmp;
if (file->signature[0] != 'B' || file->signature[1] != 'M')
return EFI_INVALID_PARAMETER;
if (file->size != size)
return EFI_INVALID_PARAMETER;
if (file->size < file->offset)
return EFI_INVALID_PARAMETER;
/* check device-independent bitmap */
dib = (struct bmp_dib *)(bmp + sizeof(struct bmp_file));
if (dib->size < sizeof(struct bmp_dib))
return EFI_UNSUPPORTED;
switch (dib->depth) {
case 1:
case 4:
case 8:
case 24:
if (dib->compression != 0)
return EFI_UNSUPPORTED;
break;
case 16:
case 32:
if (dib->compression != 0 && dib->compression != 3)
return EFI_UNSUPPORTED;
break;
default:
return EFI_UNSUPPORTED;
}
row_size = (((dib->depth * dib->x) + 31) / 32) * 4;
if (file->size - file->offset < dib->y * row_size)
return EFI_INVALID_PARAMETER;
if (row_size * dib->y > 64 * 1024 * 1024)
return EFI_INVALID_PARAMETER;
/* check color table */
map = (struct bmp_map *)(bmp + sizeof(struct bmp_file) + dib->size);
if (file->offset < sizeof(struct bmp_file) + dib->size)
return EFI_INVALID_PARAMETER;
if (file->offset > sizeof(struct bmp_file) + dib->size) {
UINT32 map_count;
UINTN map_size;
if (dib->colors_used)
map_count = dib->colors_used;
else {
switch (dib->depth) {
case 1:
case 4:
case 8:
map_count = 1 << dib->depth;
break;
default:
map_count = 0;
break;
}
}
map_size = file->offset - (sizeof(struct bmp_file) + dib->size);
if (map_size != sizeof(struct bmp_map) * map_count)
return EFI_INVALID_PARAMETER;
}
*ret_map = map;
*ret_dib = dib;
*pixmap = bmp + file->offset;
return EFI_SUCCESS;
}
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);
/* convert src from RGBA to XRGB */
src = source >> 8;
/* decompose into RB and G components */
src_rb = (src & 0xff00ff);
src_g = (src & 0x00ff00);
dst_rb = (*dst & 0xff00ff);
dst_g = (*dst & 0x00ff00);
/* blend */
rb = ((((src_rb - dst_rb) * alpha + 0x800080) >> 8) + dst_rb) & 0xff00ff;
g = ((((src_g - dst_g) * alpha + 0x008000) >> 8) + dst_g) & 0x00ff00;
*dst = (rb | g);
}
EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf,
struct bmp_dib *dib, struct bmp_map *map,
UINT8 *pixmap) {
UINT8 *in;
UINTN y;
/* transform and copy pixels */
in = pixmap;
for (y = 0; y < dib->y; y++) {
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *out;
UINTN row_size;
UINTN x;
out = &buf[(dib->y - y - 1) * dib->x];
for (x = 0; x < dib->x; x++, in++, out++) {
switch (dib->depth) {
case 1: {
UINTN i;
for (i = 0; i < 8 && x < dib->x; i++) {
out->Red = map[((*in) >> (7 - i)) & 1].red;
out->Green = map[((*in) >> (7 - i)) & 1].green;
out->Blue = map[((*in) >> (7 - i)) & 1].blue;
out++;
x++;
}
out--;
x--;
break;
}
case 4: {
UINTN i;
i = (*in) >> 4;
out->Red = map[i].red;
out->Green = map[i].green;
out->Blue = map[i].blue;
if (x < (dib->x - 1)) {
out++;
x++;
i = (*in) & 0x0f;
out->Red = map[i].red;
out->Green = map[i].green;
out->Blue = map[i].blue;
}
break;
}
case 8:
out->Red = map[*in].red;
out->Green = map[*in].green;
out->Blue = map[*in].blue;
break;
case 16: {
UINT16 i = *(UINT16 *) in;
out->Red = (i & 0x7c00) >> 7;
out->Green = (i & 0x3e0) >> 2;
out->Blue = (i & 0x1f) << 3;
in += 1;
break;
}
case 24:
out->Red = in[2];
out->Green = in[1];
out->Blue = in[0];
in += 2;
break;
case 32: {
UINT32 i = *(UINT32 *) in;
pixel_blend((UINT32 *)out, i);
in += 3;
break;
}
}
}
/* add row padding; new lines always start at 32 bit boundary */
row_size = in - pixmap;
in += ((row_size + 3) & ~3) - row_size;
}
return EFI_SUCCESS;
}
EFI_STATUS graphics_splash(EFI_FILE *root_dir, CHAR16 *path,
const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background) {
EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
UINT8 *content;
INTN len;
struct bmp_dib *dib;
struct bmp_map *map;
UINT8 *pixmap;
UINT64 blt_size;
VOID *blt = NULL;
UINTN x_pos = 0;
UINTN y_pos = 0;
EFI_STATUS err;
err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
if (EFI_ERROR(err))
return err;
len = file_read(root_dir, path, 0, 0, &content);
if (len < 0)
return EFI_LOAD_ERROR;
err = bmp_parse_header(content, len, &dib, &map, &pixmap);
if (EFI_ERROR(err))
goto err;
if(dib->x < GraphicsOutput->Mode->Info->HorizontalResolution)
x_pos = (GraphicsOutput->Mode->Info->HorizontalResolution - dib->x) / 2;
if(dib->y < GraphicsOutput->Mode->Info->VerticalResolution)
y_pos = (GraphicsOutput->Mode->Info->VerticalResolution - dib->y) / 2;
uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput,
(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)background,
EfiBltVideoFill, 0, 0, 0, 0,
GraphicsOutput->Mode->Info->HorizontalResolution,
GraphicsOutput->Mode->Info->VerticalResolution, 0);
/* EFI buffer */
blt_size = dib->x * dib->y * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
blt = AllocatePool(blt_size);
if (!blt)
return EFI_OUT_OF_RESOURCES;
err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput,
blt, EfiBltVideoToBltBuffer, x_pos, y_pos, 0, 0,
dib->x, dib->y, 0);
if (EFI_ERROR(err))
goto err;
err = bmp_to_blt(blt, dib, map, pixmap);
if (EFI_ERROR(err))
goto err;
err = graphics_mode(TRUE);
if (EFI_ERROR(err))
goto err;
err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput,
blt, EfiBltBufferToVideo, 0, 0, x_pos, y_pos,
dib->x, dib->y, 0);
err:
FreePool(blt);
FreePool(content);
return err;
}

26
src/sd-boot/graphics.h Normal file
View File

@ -0,0 +1,26 @@
/*-*- 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) 2012-2013 Kay Sievers <kay@vrfy.org>
* Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
* Copyright (C) 2013 Intel Corporation
* Authored by Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
*/
#ifndef __SDBOOT_GRAPHICS_H
#define __SDBOOT_GRAPHICS_H
EFI_STATUS graphics_mode(BOOLEAN on);
EFI_STATUS graphics_splash(EFI_FILE *root_dir, CHAR16 *path,
const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background);
#endif

130
src/sd-boot/linux.c Normal file
View File

@ -0,0 +1,130 @@
/*-*- 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)setup->code32_start + setup->handover_offset);
handover(image, ST, setup);
}
#endif
EFI_STATUS linux_exec(EFI_HANDLE *image,
CHAR8 *cmdline, UINTN cmdline_len,
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 *)(UINTN)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(cmdline_len + 1), &addr);
if (EFI_ERROR(err))
return err;
CopyMem((VOID *)(UINTN)addr, cmdline, cmdline_len);
((CHAR8 *)addr)[cmdline_len] = 0;
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;
}

24
src/sd-boot/linux.h Normal file
View File

@ -0,0 +1,24 @@
/*-*- 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 __SDBOOT_kernel_H
#define __SDBOOT_kernel_H
EFI_STATUS linux_exec(EFI_HANDLE *image,
CHAR8 *cmdline, UINTN cmdline_size,
UINTN linux_addr,
UINTN initrd_addr, UINTN initrd_size);
#endif

172
src/sd-boot/pefile.c Normal file
View File

@ -0,0 +1,172 @@
/*-*- 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"
struct DosFileHeader {
UINT8 Magic[2];
UINT16 LastSize;
UINT16 nBlocks;
UINT16 nReloc;
UINT16 HdrSize;
UINT16 MinAlloc;
UINT16 MaxAlloc;
UINT16 ss;
UINT16 sp;
UINT16 Checksum;
UINT16 ip;
UINT16 cs;
UINT16 RelocPos;
UINT16 nOverlay;
UINT16 reserved[4];
UINT16 OEMId;
UINT16 OEMInfo;
UINT16 reserved2[10];
UINT32 ExeHeader;
} __attribute__((packed));
#define PE_HEADER_MACHINE_I386 0x014c
#define PE_HEADER_MACHINE_X64 0x8664
struct PeFileHeader {
UINT16 Machine;
UINT16 NumberOfSections;
UINT32 TimeDateStamp;
UINT32 PointerToSymbolTable;
UINT32 NumberOfSymbols;
UINT16 SizeOfOptionalHeader;
UINT16 Characteristics;
} __attribute__((packed));
struct PeSectionHeader {
UINT8 Name[8];
UINT32 VirtualSize;
UINT32 VirtualAddress;
UINT32 SizeOfRawData;
UINT32 PointerToRawData;
UINT32 PointerToRelocations;
UINT32 PointerToLinenumbers;
UINT16 NumberOfRelocations;
UINT16 NumberOfLinenumbers;
UINT32 Characteristics;
} __attribute__((packed));
EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) {
EFI_FILE_HANDLE handle;
struct DosFileHeader dos;
uint8_t magic[4];
struct PeFileHeader pe;
UINTN len;
UINTN i;
EFI_STATUS err;
err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
return err;
/* MS-DOS stub */
len = sizeof(dos);
err = uefi_call_wrapper(handle->Read, 3, handle, &len, &dos);
if (EFI_ERROR(err))
goto out;
if (len != sizeof(dos)) {
err = EFI_LOAD_ERROR;
goto out;
}
if (CompareMem(dos.Magic, "MZ", 2) != 0) {
err = EFI_LOAD_ERROR;
goto out;
}
err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader);
if (EFI_ERROR(err))
goto out;
/* PE header */
len = sizeof(magic);
err = uefi_call_wrapper(handle->Read, 3, handle, &len, &magic);
if (EFI_ERROR(err))
goto out;
if (len != sizeof(magic)) {
err = EFI_LOAD_ERROR;
goto out;
}
if (CompareMem(magic, "PE\0\0", 2) != 0) {
err = EFI_LOAD_ERROR;
goto out;
}
len = sizeof(pe);
err = uefi_call_wrapper(handle->Read, 3, handle, &len, &pe);
if (EFI_ERROR(err))
goto out;
if (len != sizeof(pe)) {
err = EFI_LOAD_ERROR;
goto out;
}
/* PE32+ Subsystem type */
if (pe.Machine != PE_HEADER_MACHINE_X64 &&
pe.Machine != PE_HEADER_MACHINE_I386) {
err = EFI_LOAD_ERROR;
goto out;
}
if (pe.NumberOfSections > 96) {
err = EFI_LOAD_ERROR;
goto out;
}
/* the sections start directly after the headers */
err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader + sizeof(magic) + sizeof(pe) + pe.SizeOfOptionalHeader);
if (EFI_ERROR(err))
goto out;
for (i = 0; i < pe.NumberOfSections; i++) {
struct PeSectionHeader sect;
UINTN j;
len = sizeof(sect);
err = uefi_call_wrapper(handle->Read, 3, handle, &len, &sect);
if (EFI_ERROR(err))
goto out;
if (len != sizeof(sect)) {
err = EFI_LOAD_ERROR;
goto out;
}
for (j = 0; sections[j]; j++) {
if (CompareMem(sect.Name, sections[j], strlena(sections[j])) != 0)
continue;
if (addrs)
addrs[j] = (UINTN)sect.VirtualAddress;
if (offsets)
offsets[j] = (UINTN)sect.PointerToRawData;
if (sizes)
sizes[j] = (UINTN)sect.VirtualSize;
}
}
out:
uefi_call_wrapper(handle->Close, 1, handle);
return err;
}

22
src/sd-boot/pefile.h Normal file
View File

@ -0,0 +1,22 @@
/*-*- 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 __SDBOOT_PEFILE_H
#define __SDBOOT_PEFILE_H
EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path,
CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
#endif

2023
src/sd-boot/sd-boot.c Normal file

File diff suppressed because it is too large Load Diff

106
src/sd-boot/stub.c Normal file
View File

@ -0,0 +1,106 @@
/*-*- 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"
/* magic string to find in the binary image */
static const char __attribute__((used)) magic[] = "#### LoaderInfo: stub " VERSION " ####";
static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
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 *b;
UINTN size;
BOOLEAN secure = FALSE;
CHAR8 *sections[] = {
(UINT8 *)".cmdline",
(UINT8 *)".linux",
(UINT8 *)".initrd",
NULL
};
UINTN addrs[ELEMENTSOF(sections)-1] = {};
UINTN offs[ELEMENTSOF(sections)-1] = {};
UINTN szs[ELEMENTSOF(sections)-1] = {};
CHAR8 *cmdline = NULL;
UINTN cmdline_len;
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);
if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) {
if (*b > 0)
secure = TRUE;
FreePool(b);
}
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;
}
if (szs[0] > 0)
cmdline = (CHAR8 *)(loaded_image->ImageBase + addrs[0]);
cmdline_len = szs[0];
/* if we are not in secure boot mode, accept a custom command line and replace the built-in one */
if (!secure && loaded_image->LoadOptionsSize > 0) {
CHAR16 *options;
CHAR8 *line;
UINTN i;
options = (CHAR16 *)loaded_image->LoadOptions;
cmdline_len = (loaded_image->LoadOptionsSize / sizeof(CHAR16)) * sizeof(CHAR8);
line = AllocatePool(cmdline_len);
for (i = 0; i < cmdline_len; i++)
line[i] = options[i];
cmdline = line;
}
err = linux_exec(image, cmdline, cmdline_len,
(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;
}

322
src/sd-boot/util.c Normal file
View File

@ -0,0 +1,322 @@
/*-*- 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) 2012-2013 Kay Sievers <kay@vrfy.org>
* Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
*/
#include <efi.h>
#include <efilib.h>
#include "util.h"
/*
* Allocated random UUID, intended to be shared across tools that implement
* the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
* associated EFI variables.
*/
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 a, d;
__asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
return (d << 32) | a;
}
#else
UINT64 ticks_read(VOID) {
UINT64 val;
__asm__ volatile ("rdtsc" : "=A" (val));
return val;
}
#endif
/* count TSC ticks during a millisecond delay */
UINT64 ticks_freq(VOID) {
UINT64 ticks_start, ticks_end;
ticks_start = ticks_read();
uefi_call_wrapper(BS->Stall, 1, 1000);
ticks_end = ticks_read();
return (ticks_end - ticks_start) * 1000;
}
UINT64 time_usec(VOID) {
UINT64 ticks;
static UINT64 freq;
ticks = ticks_read();
if (ticks == 0)
return 0;
if (freq == 0) {
freq = ticks_freq();
if (freq == 0)
return 0;
}
return 1000 * 1000 * ticks / freq;
}
EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) {
UINT32 flags;
flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
if (persistent)
flags |= EFI_VARIABLE_NON_VOLATILE;
return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf);
}
EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) {
return efivar_set_raw(&loader_guid, name, (CHAR8 *)value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent);
}
EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) {
CHAR16 str[32];
SPrint(str, 32, L"%d", i);
return efivar_set(name, str, persistent);
}
EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) {
CHAR8 *buf;
CHAR16 *val;
UINTN size;
EFI_STATUS err;
err = efivar_get_raw(&loader_guid, name, &buf, &size);
if (EFI_ERROR(err))
return err;
val = StrDuplicate((CHAR16 *)buf);
if (!val) {
FreePool(buf);
return EFI_OUT_OF_RESOURCES;
}
*value = val;
return EFI_SUCCESS;
}
EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) {
CHAR16 *val;
EFI_STATUS err;
err = efivar_get(name, &val);
if (!EFI_ERROR(err)) {
*i = Atoi(val);
FreePool(val);
}
return err;
}
EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
CHAR8 *buf;
UINTN l;
EFI_STATUS err;
l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
buf = AllocatePool(l);
if (!buf)
return EFI_OUT_OF_RESOURCES;
err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf);
if (!EFI_ERROR(err)) {
*buffer = buf;
if (size)
*size = l;
} else
FreePool(buf);
return err;
}
VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) {
CHAR16 str[32];
if (usec == 0)
usec = time_usec();
if (usec == 0)
return;
SPrint(str, 32, L"%ld", usec);
efivar_set(name, str, FALSE);
}
static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
CHAR16 unichar;
UINTN len;
UINTN i;
if (stra[0] < 0x80)
len = 1;
else if ((stra[0] & 0xe0) == 0xc0)
len = 2;
else if ((stra[0] & 0xf0) == 0xe0)
len = 3;
else if ((stra[0] & 0xf8) == 0xf0)
len = 4;
else if ((stra[0] & 0xfc) == 0xf8)
len = 5;
else if ((stra[0] & 0xfe) == 0xfc)
len = 6;
else
return -1;
switch (len) {
case 1:
unichar = stra[0];
break;
case 2:
unichar = stra[0] & 0x1f;
break;
case 3:
unichar = stra[0] & 0x0f;
break;
case 4:
unichar = stra[0] & 0x07;
break;
case 5:
unichar = stra[0] & 0x03;
break;
case 6:
unichar = stra[0] & 0x01;
break;
}
for (i = 1; i < len; i++) {
if ((stra[i] & 0xc0) != 0x80)
return -1;
unichar <<= 6;
unichar |= stra[i] & 0x3f;
}
*c = unichar;
return len;
}
CHAR16 *stra_to_str(CHAR8 *stra) {
UINTN strlen;
UINTN len;
UINTN i;
CHAR16 *str;
len = strlena(stra);
str = AllocatePool((len + 1) * sizeof(CHAR16));
strlen = 0;
i = 0;
while (i < len) {
INTN utf8len;
utf8len = utf8_to_16(stra + i, str + strlen);
if (utf8len <= 0) {
/* invalid utf8 sequence, skip the garbage */
i++;
continue;
}
strlen++;
i += utf8len;
}
str[strlen] = '\0';
return str;
}
CHAR16 *stra_to_path(CHAR8 *stra) {
CHAR16 *str;
UINTN strlen;
UINTN len;
UINTN i;
len = strlena(stra);
str = AllocatePool((len + 2) * sizeof(CHAR16));
str[0] = '\\';
strlen = 1;
i = 0;
while (i < len) {
INTN utf8len;
utf8len = utf8_to_16(stra + i, str + strlen);
if (utf8len <= 0) {
/* invalid utf8 sequence, skip the garbage */
i++;
continue;
}
if (str[strlen] == '/')
str[strlen] = '\\';
if (str[strlen] == '\\' && str[strlen-1] == '\\') {
/* skip double slashes */
i += utf8len;
continue;
}
strlen++;
i += utf8len;
}
str[strlen] = '\0';
return str;
}
CHAR8 *strchra(CHAR8 *s, CHAR8 c) {
do {
if (*s == c)
return s;
} while (*s++);
return NULL;
}
INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content) {
EFI_FILE_HANDLE handle;
CHAR8 *buf;
UINTN buflen;
EFI_STATUS err;
UINTN len;
err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
return err;
if (size == 0) {
EFI_FILE_INFO *info;
info = LibFileInfo(handle);
buflen = info->FileSize+1;
FreePool(info);
} else
buflen = size;
if (off > 0) {
err = uefi_call_wrapper(handle->SetPosition, 2, handle, off);
if (EFI_ERROR(err))
return err;
}
buf = AllocatePool(buflen);
err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf);
if (!EFI_ERROR(err)) {
buf[buflen] = '\0';
*content = buf;
len = buflen;
} else {
len = err;
FreePool(buf);
}
uefi_call_wrapper(handle->Close, 1, handle);
return len;
}

44
src/sd-boot/util.h Normal file
View File

@ -0,0 +1,44 @@
/*-*- 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) 2012-2013 Kay Sievers <kay@vrfy.org>
* Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
*/
#ifndef __SDBOOT_UTIL_H
#define __SDBOOT_UTIL_H
#include <efi.h>
#include <efilib.h>
#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
UINT64 ticks_read(void);
UINT64 ticks_freq(void);
UINT64 time_usec(void);
EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent);
EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent);
EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent);
VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec);
EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value);
EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size);
EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i);
CHAR8 *strchra(CHAR8 *s, CHAR8 c);
CHAR16 *stra_to_path(CHAR8 *stra);
CHAR16 *stra_to_str(CHAR8 *stra);
INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content);
#endif

BIN
test/splash.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

42
test/test-efi-create-disk.sh Executable file
View File

@ -0,0 +1,42 @@
#!/bin/bash -e
# create GPT table with EFI System Partition
rm -f test-efi-disk.img
dd if=/dev/null of=test-efi-disk.img bs=1M seek=512 count=1
parted --script test-efi-disk.img "mklabel gpt" "mkpart ESP fat32 1MiB 511MiB" "set 1 boot on"
# create FAT32 file system
LOOP=$(losetup --show -f -P test-efi-disk.img)
mkfs.vfat -F32 ${LOOP}p1
mkdir -p mnt
mount ${LOOP}p1 mnt
mkdir -p mnt/EFI/{Boot,systemd}
cp sd-bootx64.efi mnt/EFI/Boot/bootx64.efi
cp test/splash.bmp mnt/EFI/systemd/
[ -e /boot/shellx64.efi ] && cp /boot/shellx64.efi mnt/
mkdir mnt/EFI/Linux
echo -n "foo=yes bar=no root=/dev/fakeroot debug rd.break=initqueue" > mnt/cmdline.txt
objcopy \
--add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \
--add-section .cmdline=mnt/cmdline.txt --change-section-vma .cmdline=0x30000 \
--add-section .linux=/boot/$(cat /etc/machine-id)/$(uname -r)/linux --change-section-vma .linux=0x40000 \
--add-section .initrd=/boot/$(cat /etc/machine-id)/$(uname -r)/initrd --change-section-vma .initrd=0x3000000 \
linuxx64.efi.stub mnt/EFI/Linux/linux-test.efi
# install entries
mkdir -p mnt/loader/entries
echo -e "timeout 3\nsplash /EFI/systemd/splash.bmp\n" > mnt/loader/loader.conf
echo -e "title Test\nefi /test\n" > mnt/loader/entries/test.conf
echo -e "title Test2\nlinux /test2\noptions option=yes word number=1000 more\n" > mnt/loader/entries/test2.conf
echo -e "title Test3\nlinux /test3\n" > mnt/loader/entries/test3.conf
echo -e "title Test4\nlinux /test4\n" > mnt/loader/entries/test4.conf
echo -e "title Test5\nefi /test5\n" > mnt/loader/entries/test5.conf
echo -e "title Test6\nlinux /test6\n" > mnt/loader/entries/test6.conf
sync
umount mnt
rmdir mnt
losetup -d $LOOP