sd-boot: add EFI boot manager and stub loader
This commit is contained in:
parent
484adfd914
commit
0fa2cac4f0
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -45,6 +45,9 @@
|
||||||
/machinectl
|
/machinectl
|
||||||
/mtd_probe
|
/mtd_probe
|
||||||
/networkctl
|
/networkctl
|
||||||
|
/linuxx64.efi.stub
|
||||||
|
/sd-bootx64.efi
|
||||||
|
/test-efi-disk.img
|
||||||
/scsi_id
|
/scsi_id
|
||||||
/systemadm
|
/systemadm
|
||||||
/systemctl
|
/systemctl
|
||||||
|
|
121
Makefile.am
121
Makefile.am
|
@ -111,6 +111,7 @@ catalogdir=$(prefix)/lib/systemd/catalog
|
||||||
kernelinstalldir = $(prefix)/lib/kernel/install.d
|
kernelinstalldir = $(prefix)/lib/kernel/install.d
|
||||||
factory_etcdir = $(prefix)/share/factory/etc
|
factory_etcdir = $(prefix)/share/factory/etc
|
||||||
factory_pamdir = $(prefix)/share/factory/etc/pam.d
|
factory_pamdir = $(prefix)/share/factory/etc/pam.d
|
||||||
|
sd_bootlibdir = $(prefix)/lib/systemd/sd-boot
|
||||||
|
|
||||||
# And these are the special ones for /
|
# And these are the special ones for /
|
||||||
rootprefix=@rootprefix@
|
rootprefix=@rootprefix@
|
||||||
|
@ -2497,6 +2498,126 @@ dist_bashcompletion_DATA += \
|
||||||
dist_zshcompletion_DATA += \
|
dist_zshcompletion_DATA += \
|
||||||
shell-completion/zsh/_bootctl
|
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
|
endif
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
82
configure.ac
82
configure.ac
|
@ -38,19 +38,17 @@ AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax no-di
|
||||||
AM_SILENT_RULES([yes])
|
AM_SILENT_RULES([yes])
|
||||||
AC_CANONICAL_HOST
|
AC_CANONICAL_HOST
|
||||||
AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.])
|
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_PREREQ(2.2)
|
||||||
LT_INIT([disable-static])
|
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_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])])
|
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])
|
AC_CHECK_PROG(intltool_found, [intltool-merge], [yes], [no])
|
||||||
AS_IF([test x"$intltool_found" != xyes],
|
AS_IF([test x"$intltool_found" != xyes],
|
||||||
[AS_IF([test x"$enable_nls" = xyes],
|
[AS_IF([test x"$enable_nls" = xyes],
|
||||||
|
@ -1144,6 +1142,63 @@ if test "x$enable_efi" != "xno"; then
|
||||||
fi
|
fi
|
||||||
AM_CONDITIONAL(ENABLE_EFI, [test "x$have_efi" = "xyes"])
|
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,
|
AC_ARG_WITH(unifont,
|
||||||
AS_HELP_STRING([--with-unifont=PATH],
|
AS_HELP_STRING([--with-unifont=PATH],
|
||||||
|
@ -1392,6 +1447,14 @@ AS_IF([test "x$0" != "x./configure"], [
|
||||||
AC_SUBST([INTLTOOL_UPDATE], [/bin/true])
|
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_ARG_ENABLE(tests,
|
||||||
[AC_HELP_STRING([--disable-tests], [disable tests])],
|
[AC_HELP_STRING([--disable-tests], [disable tests])],
|
||||||
enable_tests=$enableval, enable_tests=yes)
|
enable_tests=$enableval, enable_tests=yes)
|
||||||
|
@ -1496,6 +1559,13 @@ AC_MSG_RESULT([
|
||||||
coredump: ${have_coredump}
|
coredump: ${have_coredump}
|
||||||
polkit: ${have_polkit}
|
polkit: ${have_polkit}
|
||||||
efi: ${have_efi}
|
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}
|
kmod: ${have_kmod}
|
||||||
xkbcommon: ${have_xkbcommon}
|
xkbcommon: ${have_xkbcommon}
|
||||||
blkid: ${have_blkid}
|
blkid: ${have_blkid}
|
||||||
|
|
13
m4/arch.m4
Normal file
13
m4/arch.m4
Normal 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
2
src/sd-boot/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/sd_boot.so
|
||||||
|
/stub.so
|
141
src/sd-boot/console.c
Normal file
141
src/sd-boot/console.c
Normal 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
34
src/sd-boot/console.h
Normal 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
389
src/sd-boot/graphics.c
Normal 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, ¤t, &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
26
src/sd-boot/graphics.h
Normal 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
130
src/sd-boot/linux.c
Normal 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
24
src/sd-boot/linux.h
Normal 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
172
src/sd-boot/pefile.c
Normal 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, §);
|
||||||
|
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
22
src/sd-boot/pefile.h
Normal 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
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
106
src/sd-boot/stub.c
Normal 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
322
src/sd-boot/util.c
Normal 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
44
src/sd-boot/util.h
Normal 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
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
42
test/test-efi-create-disk.sh
Executable 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
|
Loading…
Reference in a new issue