mirror of
https://github.com/systemd/systemd.git
synced 2025-03-25 18:50:18 +03:00
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
|
||||
/mtd_probe
|
||||
/networkctl
|
||||
/linuxx64.efi.stub
|
||||
/sd-bootx64.efi
|
||||
/test-efi-disk.img
|
||||
/scsi_id
|
||||
/systemadm
|
||||
/systemctl
|
||||
|
121
Makefile.am
121
Makefile.am
@ -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
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
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])
|
||||
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
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…
x
Reference in New Issue
Block a user