[systemd-commits] 3 commits - .gitignore Makefile.am man/bootctl.xml src/boot src/sd-boot src/test test/test-efi-create-disk.sh
Tom Gundersen
tomegun at kemper.freedesktop.org
Wed Feb 18 06:23:40 PST 2015
.gitignore | 2
Makefile.am | 83 -
man/bootctl.xml | 18
src/boot/bootctl.c | 34
src/boot/efi/.gitignore | 2
src/boot/efi/boot.c | 2023 +++++++++++++++++++++++++++++++++++++++++++
src/boot/efi/console.c | 141 ++
src/boot/efi/console.h | 34
src/boot/efi/graphics.c | 389 ++++++++
src/boot/efi/graphics.h | 26
src/boot/efi/linux.c | 130 ++
src/boot/efi/linux.h | 24
src/boot/efi/pefile.c | 172 +++
src/boot/efi/pefile.h | 22
src/boot/efi/stub.c | 106 ++
src/boot/efi/util.c | 322 ++++++
src/boot/efi/util.h | 44
src/sd-boot/.gitignore | 2
src/sd-boot/console.c | 141 --
src/sd-boot/console.h | 34
src/sd-boot/graphics.c | 389 --------
src/sd-boot/graphics.h | 26
src/sd-boot/linux.c | 130 --
src/sd-boot/linux.h | 24
src/sd-boot/pefile.c | 172 ---
src/sd-boot/pefile.h | 22
src/sd-boot/sd-boot.c | 2023 -------------------------------------------
src/sd-boot/stub.c | 106 --
src/sd-boot/util.c | 322 ------
src/sd-boot/util.h | 44
src/test/test-utf8.c | 2
test/test-efi-create-disk.sh | 2
32 files changed, 3506 insertions(+), 3505 deletions(-)
New commits:
commit 502184de0f95d3a124d4d4c77ae7a88747a0fac2
Author: Tom Gundersen <teg at jklm.no>
Date: Wed Feb 18 14:33:50 2015 +0100
test: utf8 - fix utf16 tests on BE machines
diff --git a/src/test/test-utf8.c b/src/test/test-utf8.c
index befa385..346f852 100644
--- a/src/test/test-utf8.c
+++ b/src/test/test-utf8.c
@@ -95,7 +95,7 @@ static void test_utf8_escaping_printable(void) {
static void test_utf16_to_utf8(void) {
char *a = NULL;
- const uint16_t utf16[] = { 'a', 0xd800, 'b', 0xdc00, 'c', 0xd801, 0xdc37 };
+ const uint16_t utf16[] = { htole16('a'), htole16(0xd800), htole16('b'), htole16(0xdc00), htole16('c'), htole16(0xd801), htole16(0xdc37) };
const char utf8[] = { 'a', 'b', 'c', 0xf0, 0x90, 0x90, 0xb7, 0 };
a = utf16_to_utf8(utf16, 14);
commit e7dd673d1e0acfe5420599588c559fd85a3a9e8f
Author: Tom Gundersen <teg at jklm.no>
Date: Tue Feb 17 20:12:50 2015 +0100
gummiboot/sd-boot/systemd-boot: rename galore
What used to be gummiboot, was renamed sd-boot when it was merged into
systemd. Let's try to be a bit more consistent with the rest of systemd
and rename it again as follows:
The EFI bootloader is now called 'systemd-bootx64.efi', and its sources are in
'src/boot/efi/'. The drop-in directory where bootctl will find EFI loaders
is now /usr/lib/systemd/boot/efi/.
diff --git a/.gitignore b/.gitignore
index 75699ca..2afbed4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,7 +46,7 @@
/mtd_probe
/networkctl
/linuxx64.efi.stub
-/sd-bootx64.efi
+/systemd-bootx64.efi
/test-efi-disk.img
/scsi_id
/systemadm
diff --git a/Makefile.am b/Makefile.am
index a5e07cf..2a07321 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -111,7 +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
+bootlibdir = $(prefix)/lib/systemd/boot/efi
# And these are the special ones for /
rootprefix=@rootprefix@
@@ -2484,7 +2484,7 @@ bootctl_SOURCES = \
bootctl_CPPFLAGS = \
$(AM_CPPFLAGS) \
-DEFI_MACHINE_TYPE_NAME=\"$(EFI_MACHINE_TYPE_NAME)\" \
- -DSD_BOOTLIBDIR=\"$(sd_bootlibdir)\"
+ -DBOOTLIBDIR=\"$(bootlibdir)\"
bootctl_CFLAGS = \
$(AM_CFLAGS) \
@@ -2548,63 +2548,63 @@ efi_ldflags = \
$(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/
+systemd_boot_headers = \
+ src/boot/efi/util.h \
+ src/boot/efi/console.h \
+ src/boot/efi/graphics.h \
+ src/boot/efi/pefile.h
+
+systemd_boot_sources = \
+ src/boot/efi/util.c \
+ src/boot/efi/console.c \
+ src/boot/efi/graphics.c \
+ src/boot/efi/pefile.c \
+ src/boot/efi/boot.c
+
+systemd_boot_objects = $(addprefix $(top_builddir)/,$(systemd_boot_sources:.c=.o))
+systemd_boot_solib = $(top_builddir)/src/boot/efi/systemd_boot.so
+systemd_boot = systemd-boot$(EFI_MACHINE_TYPE_NAME).efi
+
+bootlib_DATA = $(systemd_boot)
+CLEANFILES += $(systemd_boot_objects) $(systemd_boot_solib) $(systemd_boot)
+EXTRA_DIST += $(systemd_boot_sources) $(systemd_boot_headers)
+
+$(top_builddir)/src/boot/efi/%.o: $(top_srcdir)/src/boot/efi/%.c $(addprefix $(top_srcdir)/,$(systemd_boot_headers))
+ @$(MKDIR_P) $(top_builddir)/src/boot/efi/
$(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) \
+$(systemd_boot_solib): $(systemd_boot_objects)
+ $(AM_V_CCLD)$(LD) $(efi_ldflags) $(systemd_boot_objects) \
-o $@ -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name); \
nm -D -u $@ | grep ' U ' && exit 1 || :
-$(sd_boot): $(sd_boot_solib)
+$(systemd_boot): $(systemd_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
+ src/boot/efi/util.h \
+ src/boot/efi/pefile.h \
+ src/boot/efi/linux.h
stub_sources = \
- src/sd-boot/util.c \
- src/sd-boot/pefile.c \
- src/sd-boot/linux.c \
- src/sd-boot/stub.c
+ src/boot/efi/util.c \
+ src/boot/efi/pefile.c \
+ src/boot/efi/linux.c \
+ src/boot/efi/stub.c
stub_objects = $(addprefix $(top_builddir)/,$(stub_sources:.c=.o))
-stub_solib = $(top_builddir)/src/sd-boot/stub.so
+stub_solib = $(top_builddir)/src/boot/efi/stub.so
stub = linux$(EFI_MACHINE_TYPE_NAME).efi.stub
-sd_bootlib_DATA += $(stub)
+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/
+$(top_builddir)/src/boot/efi/%.o: $(top_srcdir)/src/boot/efi/%.c $(addprefix $(top_srcdir)/,$(stub_headers))
+ @$(MKDIR_P) $(top_builddir)/src/boot/efi/
$(AM_V_CC)$(EFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@
$(stub_solib): $(stub_objects)
@@ -2621,7 +2621,7 @@ $(stub): $(stub_solib)
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
+test-efi-disk.img: $(systemd_boot) $(stub) test/test-efi-create-disk.sh
$(AM_V_GEN)test/test-efi-create-disk.sh
test-efi: test-efi-disk.img
diff --git a/man/bootctl.xml b/man/bootctl.xml
index 99863bf..63ad939 100644
--- a/man/bootctl.xml
+++ b/man/bootctl.xml
@@ -72,22 +72,22 @@
all current EFI boot variables.</para>
<para><command>bootctl update</command> updates all installed
- versions of sd-boot, if the current version is newer than the
+ versions of systemd-boot, if the current version is newer than the
version installed in the EFI system partition. This also includes
- the EFI default/fallback loader at /EFI/Boot/boot*.efi. An
- sd-boot entry in the EFI boot variables is created, if there
- is no current entry. A created entry will be added to the end of
+ the EFI default/fallback loader at /EFI/Boot/boot*.efi. A
+ systemd-boot entry in the EFI boot variables is created, if there
+ is no current entry. The created entry will be added to the end of
the boot order list.</para>
- <para><command>bootctl install</command> installs sd-boot into
- the EFI system partition. A copy of sd-boot will be stored as
- the EFI default/fallback loader at /EFI/Boot/boot*.efi. An sd-boot
+ <para><command>bootctl install</command> installs systemd-boot into
+ the EFI system partition. A copy of systemd-boot will be stored as
+ the EFI default/fallback loader at /EFI/Boot/boot*.efi. A systemd-boot
entry in the EFI boot variables is created and added to the top
of the boot order list.</para>
<para><command>bootctl remove</command> removes all installed
- versions of sd-boot from the EFI system partition, and removes
- sd-boot from the EFI boot variables.</para>
+ versions of systemd-boot from the EFI system partition, and removes
+ systemd-boot from the EFI boot variables.</para>
<para>If no command is passed <command>status</command> is
implied.</para>
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 3b6df42..73450e2 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -215,7 +215,7 @@ fail:
return r;
}
-/* search for "#### LoaderInfo: sd-boot 218 ####" string inside the binary */
+/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
static int get_file_version(FILE *f, char **v) {
struct stat st;
char *buf;
@@ -351,7 +351,7 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) {
r = enumerate_binaries(esp_path, "EFI/systemd", NULL);
if (r == 0)
- fprintf(stderr, "sd-boot not installed in ESP.\n");
+ fprintf(stderr, "systemd-boot not installed in ESP.\n");
else if (r < 0)
return r;
@@ -710,7 +710,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
_cleanup_free_ char *v = NULL;
int r;
- if (asprintf(&p, SD_BOOTLIBDIR "/%s", name) < 0) {
+ if (asprintf(&p, BOOTLIBDIR "/%s", name) < 0) {
fprintf(stderr, "Out of memory.\n");
return -ENOMEM;
}
@@ -722,11 +722,11 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
r = copy_file(p, q, force);
- if (startswith(name, "sd-boot")) {
+ if (startswith(name, "systemd-boot")) {
int k;
/* Create the EFI default boot loader name (specified for removable devices) */
- if (asprintf(&v, "%s/EFI/Boot/BOOT%s", esp_path, name + strlen("sd-boot")) < 0) {
+ if (asprintf(&v, "%s/EFI/Boot/BOOT%s", esp_path, name + strlen("systemd-boot")) < 0) {
fprintf(stderr, "Out of memory.\n");
return -ENOMEM;
}
@@ -756,9 +756,9 @@ static int install_binaries(const char *esp_path, bool force) {
return r;
}
- d = opendir(SD_BOOTLIBDIR);
+ d = opendir(BOOTLIBDIR);
if (!d) {
- fprintf(stderr, "Failed to open "SD_BOOTLIBDIR": %m\n");
+ fprintf(stderr, "Failed to open "BOOTLIBDIR": %m\n");
return -errno;
}
@@ -814,7 +814,7 @@ static int find_slot(sd_id128_t uuid, const char *path, uint16_t *id) {
if (n_options < 0)
return n_options;
- /* find already existing sd-boot entry */
+ /* find already existing systemd-boot entry */
for (i = 0; i < n_options; i++)
if (same_entry(options[i], uuid, path)) {
new_id = options[i];
@@ -1036,7 +1036,7 @@ static int remove_boot_efi(const char *esp_path) {
if (r < 0)
goto finish;
- if (r > 0 && strncmp(v, "sd-boot ", 10) == 0) {
+ if (r > 0 && strncmp(v, "systemd-boot ", 10) == 0) {
r = unlink(q);
if (r < 0) {
@@ -1089,7 +1089,7 @@ static int remove_binaries(const char *esp_path) {
char *p;
int r, q;
- if (asprintf(&p, "%s/EFI/sd-boot", esp_path) < 0) {
+ if (asprintf(&p, "%s/EFI/systemd-boot", esp_path) < 0) {
fprintf(stderr, "Out of memory.\n");
return -ENOMEM;
}
@@ -1113,7 +1113,7 @@ static int remove_binaries(const char *esp_path) {
if (q < 0 && r == 0)
r = q;
- q = rmdir_one(esp_path, "EFI/sd-boot");
+ q = rmdir_one(esp_path, "EFI/systemd-boot");
if (q < 0 && r == 0)
r = q;
@@ -1196,10 +1196,10 @@ static int help(void) {
" --no-variables Don't touch EFI variables\n"
"\n"
"Comands:\n"
- " status Show status of installed sd-boot and EFI variables\n"
- " install Install sd-boot to the ESP and EFI variables\n"
- " update Update sd-boot in the ESP and EFI variables\n"
- " remove Remove sd-boot from the ESP and EFI variables\n",
+ " status Show status of installed systemd-boot and EFI variables\n"
+ " install Install systemd-boot to the ESP and EFI variables\n"
+ " update Update systemd-boot in the ESP and EFI variables\n"
+ " remove Remove systemd-boot from the ESP and EFI variables\n",
program_invocation_short_name);
return 0;
@@ -1371,7 +1371,7 @@ static int bootctl_main(int argc, char*argv[]) {
if (arg_touch_variables)
r = install_variables(arg_path,
part, pstart, psize, uuid,
- "/EFI/systemd/sd-boot" EFI_MACHINE_TYPE_NAME ".efi",
+ "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi",
arg_action == ACTION_INSTALL);
break;
@@ -1379,7 +1379,7 @@ static int bootctl_main(int argc, char*argv[]) {
r = remove_binaries(arg_path);
if (arg_touch_variables) {
- q = remove_variables(uuid, "/EFI/systemd/sd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
+ q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
if (q < 0 && r == 0)
r = q;
}
diff --git a/src/boot/efi/.gitignore b/src/boot/efi/.gitignore
new file mode 100644
index 0000000..55b0da4
--- /dev/null
+++ b/src/boot/efi/.gitignore
@@ -0,0 +1,2 @@
+/sd_boot.so
+/stub.so
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
new file mode 100644
index 0000000..7605c65
--- /dev/null
+++ b/src/boot/efi/boot.c
@@ -0,0 +1,2023 @@
+/*-*- 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-2015 Kay Sievers <kay at vrfy.org>
+ * Copyright (C) 2012-2015 Harald Hoyer <harald at redhat.com>
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "util.h"
+#include "console.h"
+#include "graphics.h"
+#include "pefile.h"
+#include "linux.h"
+
+#ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
+#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
+#endif
+
+/* magic string to find in the binary image */
+static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot " VERSION " ####";
+
+static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
+
+enum loader_type {
+ LOADER_UNDEFINED,
+ LOADER_EFI,
+ LOADER_LINUX
+};
+
+typedef struct {
+ CHAR16 *file;
+ CHAR16 *title_show;
+ CHAR16 *title;
+ CHAR16 *version;
+ CHAR16 *machine_id;
+ EFI_HANDLE *device;
+ enum loader_type type;
+ CHAR16 *loader;
+ CHAR16 *options;
+ CHAR16 *splash;
+ CHAR16 key;
+ EFI_STATUS (*call)(VOID);
+ BOOLEAN no_autoselect;
+ BOOLEAN non_unique;
+} ConfigEntry;
+
+typedef struct {
+ ConfigEntry **entries;
+ UINTN entry_count;
+ INTN idx_default;
+ INTN idx_default_efivar;
+ UINTN timeout_sec;
+ UINTN timeout_sec_config;
+ INTN timeout_sec_efivar;
+ CHAR16 *entry_default_pattern;
+ CHAR16 *splash;
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background;
+ CHAR16 *entry_oneshot;
+ CHAR16 *options_edit;
+ CHAR16 *entries_auto;
+} Config;
+
+static VOID cursor_left(UINTN *cursor, UINTN *first)
+{
+ if ((*cursor) > 0)
+ (*cursor)--;
+ else if ((*first) > 0)
+ (*first)--;
+}
+
+static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
+{
+ if ((*cursor)+1 < x_max)
+ (*cursor)++;
+ else if ((*first) + (*cursor) < len)
+ (*first)++;
+}
+
+static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) {
+ CHAR16 *line;
+ UINTN size;
+ UINTN len;
+ UINTN first;
+ CHAR16 *print;
+ UINTN cursor;
+ UINTN clear;
+ BOOLEAN exit;
+ BOOLEAN enter;
+
+ if (!line_in)
+ line_in = L"";
+ size = StrLen(line_in) + 1024;
+ line = AllocatePool(size * sizeof(CHAR16));
+ StrCpy(line, line_in);
+ len = StrLen(line);
+ print = AllocatePool((x_max+1) * sizeof(CHAR16));
+
+ uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
+
+ first = 0;
+ cursor = 0;
+ clear = 0;
+ enter = FALSE;
+ exit = FALSE;
+ while (!exit) {
+ EFI_STATUS err;
+ UINT64 key;
+ UINTN i;
+
+ i = len - first;
+ if (i >= x_max-1)
+ i = x_max-1;
+ CopyMem(print, line + first, i * sizeof(CHAR16));
+ while (clear > 0 && i < x_max-1) {
+ clear--;
+ print[i++] = ' ';
+ }
+ print[i] = '\0';
+
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+
+ err = console_key_read(&key, TRUE);
+ if (EFI_ERROR(err))
+ continue;
+
+ switch (key) {
+ case KEYPRESS(0, SCAN_ESC, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
+ exit = TRUE;
+ break;
+
+ case KEYPRESS(0, SCAN_HOME, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
+ /* beginning-of-line */
+ cursor = 0;
+ first = 0;
+ continue;
+
+ case KEYPRESS(0, SCAN_END, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
+ /* end-of-line */
+ cursor = len - first;
+ if (cursor+1 >= x_max) {
+ cursor = x_max-1;
+ first = len - (x_max-1);
+ }
+ continue;
+
+ case KEYPRESS(0, SCAN_DOWN, 0):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
+ /* forward-word */
+ while (line[first + cursor] && line[first + cursor] == ' ')
+ cursor_right(&cursor, &first, x_max, len);
+ while (line[first + cursor] && line[first + cursor] != ' ')
+ cursor_right(&cursor, &first, x_max, len);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+ continue;
+
+ case KEYPRESS(0, SCAN_UP, 0):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
+ /* backward-word */
+ if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
+ cursor_left(&cursor, &first);
+ while ((first + cursor) > 0 && line[first + cursor] == ' ')
+ cursor_left(&cursor, &first);
+ }
+ while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
+ cursor_left(&cursor, &first);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+ continue;
+
+ case KEYPRESS(0, SCAN_RIGHT, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
+ /* forward-char */
+ if (first + cursor == len)
+ continue;
+ cursor_right(&cursor, &first, x_max, len);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+ continue;
+
+ case KEYPRESS(0, SCAN_LEFT, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
+ /* backward-char */
+ cursor_left(&cursor, &first);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+ continue;
+
+ case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
+ /* kill-word */
+ clear = 0;
+ for (i = first + cursor; i < len && line[i] == ' '; i++)
+ clear++;
+ for (; i < len && line[i] != ' '; i++)
+ clear++;
+
+ for (i = first + cursor; i + clear < len; i++)
+ line[i] = line[i + clear];
+ len -= clear;
+ line[len] = '\0';
+ continue;
+
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'w'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('w')):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, CHAR_BACKSPACE):
+ /* backward-kill-word */
+ clear = 0;
+ if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
+ cursor_left(&cursor, &first);
+ clear++;
+ while ((first + cursor) > 0 && line[first + cursor] == ' ') {
+ cursor_left(&cursor, &first);
+ clear++;
+ }
+ }
+ while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
+ cursor_left(&cursor, &first);
+ clear++;
+ }
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+
+ for (i = first + cursor; i + clear < len; i++)
+ line[i] = line[i + clear];
+ len -= clear;
+ line[len] = '\0';
+ continue;
+
+ case KEYPRESS(0, SCAN_DELETE, 0):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
+ if (len == 0)
+ continue;
+ if (first + cursor == len)
+ continue;
+ for (i = first + cursor; i < len; i++)
+ line[i] = line[i+1];
+ clear = 1;
+ len--;
+ continue;
+
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
+ /* kill-line */
+ line[first + cursor] = '\0';
+ clear = len - (first + cursor);
+ len = first + cursor;
+ continue;
+
+ case KEYPRESS(0, 0, CHAR_LINEFEED):
+ case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
+ if (StrCmp(line, line_in) != 0) {
+ *line_out = line;
+ line = NULL;
+ }
+ enter = TRUE;
+ exit = TRUE;
+ break;
+
+ case KEYPRESS(0, 0, CHAR_BACKSPACE):
+ if (len == 0)
+ continue;
+ if (first == 0 && cursor == 0)
+ continue;
+ for (i = first + cursor-1; i < len; i++)
+ line[i] = line[i+1];
+ clear = 1;
+ len--;
+ if (cursor > 0)
+ cursor--;
+ if (cursor > 0 || first == 0)
+ continue;
+ /* show full line if it fits */
+ if (len < x_max) {
+ cursor = first;
+ first = 0;
+ continue;
+ }
+ /* jump left to see what we delete */
+ if (first > 10) {
+ first -= 10;
+ cursor = 10;
+ } else {
+ cursor = first;
+ first = 0;
+ }
+ continue;
+
+ case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
+ case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
+ if (len+1 == size)
+ continue;
+ for (i = len; i > first + cursor; i--)
+ line[i] = line[i-1];
+ line[first + cursor] = KEYCHAR(key);
+ len++;
+ line[len] = '\0';
+ if (cursor+1 < x_max)
+ cursor++;
+ else if (first + cursor < len)
+ first++;
+ continue;
+ }
+ }
+
+ uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
+ FreePool(print);
+ FreePool(line);
+ return enter;
+}
+
+static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
+ UINTN i;
+
+ if (key == 0)
+ return -1;
+
+ /* select entry by number key */
+ if (key >= '1' && key <= '9') {
+ i = key - '0';
+ if (i > config->entry_count)
+ i = config->entry_count;
+ return i-1;
+ }
+
+ /* find matching key in config entries */
+ for (i = start; i < config->entry_count; i++)
+ if (config->entries[i]->key == key)
+ return i;
+
+ for (i = 0; i < start; i++)
+ if (config->entries[i]->key == key)
+ return i;
+
+ return -1;
+}
+
+static VOID print_status(Config *config, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
+ UINT64 key;
+ UINTN i;
+ CHAR16 *s;
+ CHAR8 *b;
+ UINTN x;
+ UINTN y;
+ UINTN size;
+ EFI_STATUS err;
+ UINTN color = 0;
+ const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel = config->background;
+
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+
+ /* show splash and wait for key */
+ for (;;) {
+ static const EFI_GRAPHICS_OUTPUT_BLT_PIXEL colors[] = {
+ { .Red = 0xff, .Green = 0xff, .Blue = 0xff },
+ { .Red = 0xc0, .Green = 0xc0, .Blue = 0xc0 },
+ { .Red = 0xff, .Green = 0, .Blue = 0 },
+ { .Red = 0, .Green = 0xff, .Blue = 0 },
+ { .Red = 0, .Green = 0, .Blue = 0xff },
+ { .Red = 0, .Green = 0, .Blue = 0 },
+ };
+
+ err = EFI_NOT_FOUND;
+ if (config->splash)
+ err = graphics_splash(root_dir, config->splash, pixel);
+ if (EFI_ERROR(err))
+ err = graphics_splash(root_dir, L"\\EFI\\systemd\\splash.bmp", pixel);
+ if (EFI_ERROR(err))
+ break;
+
+ /* 'b' rotates through background colors */
+ console_key_read(&key, TRUE);
+ if (key == KEYPRESS(0, 0, 'b')) {
+ pixel = &colors[color++];
+ if (color == ELEMENTSOF(colors))
+ color = 0;
+
+ continue;
+ }
+
+ graphics_mode(FALSE);
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+ break;
+ }
+
+ Print(L"systemd-boot version: " VERSION "\n");
+ Print(L"architecture: " EFI_MACHINE_TYPE_NAME "\n");
+ Print(L"loaded image: %s\n", loaded_image_path);
+ Print(L"UEFI specification: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+ Print(L"firmware vendor: %s\n", ST->FirmwareVendor);
+ Print(L"firmware version: %d.%02d\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+
+ if (uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x, &y) == EFI_SUCCESS)
+ Print(L"console size: %d x %d\n", x, y);
+
+ if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) {
+ Print(L"SecureBoot: %s\n", *b > 0 ? L"enabled" : L"disabled");
+ FreePool(b);
+ }
+
+ if (efivar_get_raw(&global_guid, L"SetupMode", &b, &size) == EFI_SUCCESS) {
+ Print(L"SetupMode: %s\n", *b > 0 ? L"setup" : L"user");
+ FreePool(b);
+ }
+
+ if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
+ Print(L"OsIndicationsSupported: %d\n", (UINT64)*b);
+ FreePool(b);
+ }
+ Print(L"\n");
+
+ Print(L"timeout: %d\n", config->timeout_sec);
+ if (config->timeout_sec_efivar >= 0)
+ Print(L"timeout (EFI var): %d\n", config->timeout_sec_efivar);
+ Print(L"timeout (config): %d\n", config->timeout_sec_config);
+ if (config->entry_default_pattern)
+ Print(L"default pattern: '%s'\n", config->entry_default_pattern);
+ if (config->splash)
+ Print(L"splash '%s'\n", config->splash);
+ if (config->background)
+ Print(L"background '#%02x%02x%02x'\n",
+ config->background->Red,
+ config->background->Green,
+ config->background->Blue);
+ Print(L"\n");
+
+ Print(L"config entry count: %d\n", config->entry_count);
+ Print(L"entry selected idx: %d\n", config->idx_default);
+ if (config->idx_default_efivar >= 0)
+ Print(L"entry EFI var idx: %d\n", config->idx_default_efivar);
+ Print(L"\n");
+
+ if (efivar_get_int(L"LoaderConfigTimeout", &i) == EFI_SUCCESS)
+ Print(L"LoaderConfigTimeout: %d\n", i);
+ if (config->entry_oneshot)
+ Print(L"LoaderEntryOneShot: %s\n", config->entry_oneshot);
+ if (efivar_get(L"LoaderDeviceIdentifier", &s) == EFI_SUCCESS) {
+ Print(L"LoaderDeviceIdentifier: %s\n", s);
+ FreePool(s);
+ }
+ if (efivar_get(L"LoaderDevicePartUUID", &s) == EFI_SUCCESS) {
+ Print(L"LoaderDevicePartUUID: %s\n", s);
+ FreePool(s);
+ }
+ if (efivar_get(L"LoaderEntryDefault", &s) == EFI_SUCCESS) {
+ Print(L"LoaderEntryDefault: %s\n", s);
+ FreePool(s);
+ }
+
+ Print(L"\n--- press key ---\n\n");
+ console_key_read(&key, TRUE);
+
+ for (i = 0; i < config->entry_count; i++) {
+ ConfigEntry *entry;
+
+ if (key == KEYPRESS(0, SCAN_ESC, 0) || key == KEYPRESS(0, 0, 'q'))
+ break;
+
+ entry = config->entries[i];
+
+ if (entry->splash) {
+ err = graphics_splash(root_dir, entry->splash, config->background);
+ if (!EFI_ERROR(err)) {
+ console_key_read(&key, TRUE);
+ graphics_mode(FALSE);
+ }
+ }
+
+ Print(L"config entry: %d/%d\n", i+1, config->entry_count);
+ if (entry->file)
+ Print(L"file '%s'\n", entry->file);
+ Print(L"title show '%s'\n", entry->title_show);
+ if (entry->title)
+ Print(L"title '%s'\n", entry->title);
+ if (entry->version)
+ Print(L"version '%s'\n", entry->version);
+ if (entry->machine_id)
+ Print(L"machine-id '%s'\n", entry->machine_id);
+ if (entry->device) {
+ EFI_DEVICE_PATH *device_path;
+ CHAR16 *str;
+
+ device_path = DevicePathFromHandle(entry->device);
+ if (device_path) {
+ str = DevicePathToStr(device_path);
+ Print(L"device handle '%s'\n", str);
+ FreePool(str);
+ }
+ }
+ if (entry->loader)
+ Print(L"loader '%s'\n", entry->loader);
+ if (entry->options)
+ Print(L"options '%s'\n", entry->options);
+ if (entry->splash)
+ Print(L"splash '%s'\n", entry->splash);
+ Print(L"auto-select %s\n", entry->no_autoselect ? L"no" : L"yes");
+ if (entry->call)
+ Print(L"internal call yes\n");
+
+ Print(L"\n--- press key ---\n\n");
+ console_key_read(&key, TRUE);
+ }
+
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+}
+
+static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
+ EFI_STATUS err;
+ UINTN visible_max;
+ UINTN idx_highlight;
+ UINTN idx_highlight_prev;
+ UINTN idx_first;
+ UINTN idx_last;
+ BOOLEAN refresh;
+ BOOLEAN highlight;
+ UINTN i;
+ UINTN line_width;
+ CHAR16 **lines;
+ UINTN x_start;
+ UINTN y_start;
+ UINTN x_max;
+ UINTN y_max;
+ CHAR16 *status;
+ CHAR16 *clearline;
+ INTN timeout_remain;
+ INT16 idx;
+ BOOLEAN exit = FALSE;
+ BOOLEAN run = TRUE;
+ BOOLEAN wait = FALSE;
+
+ graphics_mode(FALSE);
+ uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
+ uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+
+ /* draw a single character to make ClearScreen work on some firmware */
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L" ");
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+
+ err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
+ if (EFI_ERROR(err)) {
+ x_max = 80;
+ y_max = 25;
+ }
+
+ /* we check 10 times per second for a keystroke */
+ if (config->timeout_sec > 0)
+ timeout_remain = config->timeout_sec * 10;
+ else
+ timeout_remain = -1;
+
+ idx_highlight = config->idx_default;
+ idx_highlight_prev = 0;
+
+ visible_max = y_max - 2;
+
+ if ((UINTN)config->idx_default >= visible_max)
+ idx_first = config->idx_default-1;
+ else
+ idx_first = 0;
+
+ idx_last = idx_first + visible_max-1;
+
+ refresh = TRUE;
+ highlight = FALSE;
+
+ /* length of the longest entry */
+ line_width = 5;
+ for (i = 0; i < config->entry_count; i++) {
+ UINTN entry_len;
+
+ entry_len = StrLen(config->entries[i]->title_show);
+ if (line_width < entry_len)
+ line_width = entry_len;
+ }
+ if (line_width > x_max-6)
+ line_width = x_max-6;
+
+ /* offsets to center the entries on the screen */
+ x_start = (x_max - (line_width)) / 2;
+ if (config->entry_count < visible_max)
+ y_start = ((visible_max - config->entry_count) / 2) + 1;
+ else
+ y_start = 0;
+
+ /* menu entries title lines */
+ lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
+ for (i = 0; i < config->entry_count; i++) {
+ UINTN j, k;
+
+ lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16)));
+ for (j = 0; j < x_start; j++)
+ lines[i][j] = ' ';
+
+ for (k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++)
+ lines[i][j] = config->entries[i]->title_show[k];
+
+ for (; j < x_max; j++)
+ lines[i][j] = ' ';
+ lines[i][x_max] = '\0';
+ }
+
+ status = NULL;
+ clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
+ for (i = 0; i < x_max; i++)
+ clearline[i] = ' ';
+ clearline[i] = 0;
+
+ while (!exit) {
+ UINT64 key;
+
+ if (refresh) {
+ for (i = 0; i < config->entry_count; i++) {
+ if (i < idx_first || i > idx_last)
+ continue;
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first);
+ if (i == idx_highlight)
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
+ EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
+ else
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
+ EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
+ if ((INTN)i == config->idx_default_efivar) {
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
+ }
+ }
+ refresh = FALSE;
+ } else if (highlight) {
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first);
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
+ if ((INTN)idx_highlight_prev == config->idx_default_efivar) {
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
+ }
+
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first);
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
+ if ((INTN)idx_highlight == config->idx_default_efivar) {
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
+ }
+ highlight = FALSE;
+ }
+
+ if (timeout_remain > 0) {
+ FreePool(status);
+ status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10);
+ }
+
+ /* print status at last line of screen */
+ if (status) {
+ UINTN len;
+ UINTN x;
+
+ /* center line */
+ len = StrLen(status);
+ if (len < x_max)
+ x = (x_max - len) / 2;
+ else
+ x = 0;
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x));
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
+ }
+
+ err = console_key_read(&key, wait);
+ if (EFI_ERROR(err)) {
+ /* timeout reached */
+ if (timeout_remain == 0) {
+ exit = TRUE;
+ break;
+ }
+
+ /* sleep and update status */
+ if (timeout_remain > 0) {
+ uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
+ timeout_remain--;
+ continue;
+ }
+
+ /* timeout disabled, wait for next key */
+ wait = TRUE;
+ continue;
+ }
+
+ timeout_remain = -1;
+
+ /* clear status after keystroke */
+ if (status) {
+ FreePool(status);
+ status = NULL;
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
+ }
+
+ idx_highlight_prev = idx_highlight;
+
+ switch (key) {
+ case KEYPRESS(0, SCAN_UP, 0):
+ case KEYPRESS(0, 0, 'k'):
+ if (idx_highlight > 0)
+ idx_highlight--;
+ break;
+
+ case KEYPRESS(0, SCAN_DOWN, 0):
+ case KEYPRESS(0, 0, 'j'):
+ if (idx_highlight < config->entry_count-1)
+ idx_highlight++;
+ break;
+
+ case KEYPRESS(0, SCAN_HOME, 0):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, '<'):
+ if (idx_highlight > 0) {
+ refresh = TRUE;
+ idx_highlight = 0;
+ }
+ break;
+
+ case KEYPRESS(0, SCAN_END, 0):
+ case KEYPRESS(EFI_ALT_PRESSED, 0, '>'):
+ if (idx_highlight < config->entry_count-1) {
+ refresh = TRUE;
+ idx_highlight = config->entry_count-1;
+ }
+ break;
+
+ case KEYPRESS(0, SCAN_PAGE_UP, 0):
+ if (idx_highlight > visible_max)
+ idx_highlight -= visible_max;
+ else
+ idx_highlight = 0;
+ break;
+
+ case KEYPRESS(0, SCAN_PAGE_DOWN, 0):
+ idx_highlight += visible_max;
+ if (idx_highlight > config->entry_count-1)
+ idx_highlight = config->entry_count-1;
+ break;
+
+ case KEYPRESS(0, 0, CHAR_LINEFEED):
+ case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
+ exit = TRUE;
+ break;
+
+ case KEYPRESS(0, SCAN_F1, 0):
+ case KEYPRESS(0, 0, 'h'):
+ case KEYPRESS(0, 0, '?'):
+ status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
+ break;
+
+ case KEYPRESS(0, 0, 'Q'):
+ exit = TRUE;
+ run = FALSE;
+ break;
+
+ case KEYPRESS(0, 0, 'd'):
+ if (config->idx_default_efivar != (INTN)idx_highlight) {
+ /* store the selected entry in a persistent EFI variable */
+ efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE);
+ config->idx_default_efivar = idx_highlight;
+ status = StrDuplicate(L"Default boot entry selected.");
+ } else {
+ /* clear the default entry EFI variable */
+ efivar_set(L"LoaderEntryDefault", NULL, TRUE);
+ config->idx_default_efivar = -1;
+ status = StrDuplicate(L"Default boot entry cleared.");
+ }
+ refresh = TRUE;
+ break;
+
+ case KEYPRESS(0, 0, '-'):
+ case KEYPRESS(0, 0, 'T'):
+ if (config->timeout_sec_efivar > 0) {
+ config->timeout_sec_efivar--;
+ efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
+ if (config->timeout_sec_efivar > 0)
+ status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar);
+ else
+ status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
+ } else if (config->timeout_sec_efivar <= 0){
+ config->timeout_sec_efivar = -1;
+ efivar_set(L"LoaderConfigTimeout", NULL, TRUE);
+ if (config->timeout_sec_config > 0)
+ status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.",
+ config->timeout_sec_config);
+ else
+ status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
+ }
+ break;
+
+ case KEYPRESS(0, 0, '+'):
+ case KEYPRESS(0, 0, 't'):
+ if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0)
+ config->timeout_sec_efivar++;
+ config->timeout_sec_efivar++;
+ efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
+ if (config->timeout_sec_efivar > 0)
+ status = PoolPrint(L"Menu timeout set to %d sec.",
+ config->timeout_sec_efivar);
+ else
+ status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
+ break;
+
+ case KEYPRESS(0, 0, 'e'):
+ /* only the options of configured entries can be edited */
+ if (config->entries[idx_highlight]->type == LOADER_UNDEFINED)
+ break;
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
+ if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1))
+ exit = TRUE;
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
+ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
+ break;
+
+ case KEYPRESS(0, 0, 'v'):
+ status = PoolPrint(L"systemd-boot " VERSION " (" EFI_MACHINE_TYPE_NAME "), UEFI Specification %d.%02d, Vendor %s %d.%02d",
+ ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff,
+ ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+ break;
+
+ case KEYPRESS(0, 0, 'P'):
+ print_status(config, root_dir, loaded_image_path);
+ refresh = TRUE;
+ break;
+
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'):
+ case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')):
+ refresh = TRUE;
+ break;
+
+ default:
+ /* jump with a hotkey directly to a matching entry */
+ idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key));
+ if (idx < 0)
+ break;
+ idx_highlight = idx;
+ refresh = TRUE;
+ }
+
+ if (idx_highlight > idx_last) {
+ idx_last = idx_highlight;
+ idx_first = 1 + idx_highlight - visible_max;
+ refresh = TRUE;
+ }
+ if (idx_highlight < idx_first) {
+ idx_first = idx_highlight;
+ idx_last = idx_highlight + visible_max-1;
+ refresh = TRUE;
+ }
+
+ idx_last = idx_first + visible_max-1;
+
+ if (!refresh && idx_highlight != idx_highlight_prev)
+ highlight = TRUE;
+ }
+
+ *chosen_entry = config->entries[idx_highlight];
+
+ for (i = 0; i < config->entry_count; i++)
+ FreePool(lines[i]);
+ FreePool(lines);
+ FreePool(clearline);
+
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+ return run;
+}
+
+static VOID config_add_entry(Config *config, ConfigEntry *entry) {
+ if ((config->entry_count & 15) == 0) {
+ UINTN i;
+
+ i = config->entry_count + 16;
+ if (config->entry_count == 0)
+ config->entries = AllocatePool(sizeof(VOID *) * i);
+ else
+ config->entries = ReallocatePool(config->entries,
+ sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i);
+ }
+ config->entries[config->entry_count++] = entry;
+}
+
+static VOID config_entry_free(ConfigEntry *entry) {
+ FreePool(entry->title_show);
+ FreePool(entry->title);
+ FreePool(entry->machine_id);
+ FreePool(entry->loader);
+ FreePool(entry->options);
+}
+
+static BOOLEAN is_digit(CHAR16 c)
+{
+ return (c >= '0') && (c <= '9');
+}
+
+static UINTN c_order(CHAR16 c)
+{
+ if (c == '\0')
+ return 0;
+ if (is_digit(c))
+ return 0;
+ else if ((c >= 'a') && (c <= 'z'))
+ return c;
+ else
+ return c + 0x10000;
+}
+
+static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2)
+{
+ CHAR16 *os1 = s1;
+ CHAR16 *os2 = s2;
+
+ while (*s1 || *s2) {
+ INTN first;
+
+ while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
+ INTN order;
+
+ order = c_order(*s1) - c_order(*s2);
+ if (order)
+ return order;
+ s1++;
+ s2++;
+ }
+
+ while (*s1 == '0')
+ s1++;
+ while (*s2 == '0')
+ s2++;
+
+ first = 0;
+ while (is_digit(*s1) && is_digit(*s2)) {
+ if (first == 0)
+ first = *s1 - *s2;
+ s1++;
+ s2++;
+ }
+
+ if (is_digit(*s1))
+ return 1;
+ if (is_digit(*s2))
+ return -1;
+
+ if (first)
+ return first;
+ }
+
+ return StrCmp(os1, os2);
+}
+
+static CHAR8 *line_get_key_value(CHAR8 *content, CHAR8 *sep, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) {
+ CHAR8 *line;
+ UINTN linelen;
+ CHAR8 *value;
+
+skip:
+ line = content + *pos;
+ if (*line == '\0')
+ return NULL;
+
+ linelen = 0;
+ while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen]))
+ linelen++;
+
+ /* move pos to next line */
+ *pos += linelen;
+ if (content[*pos])
+ (*pos)++;
+
+ /* empty line */
+ if (linelen == 0)
+ goto skip;
+
+ /* terminate line */
+ line[linelen] = '\0';
+
+ /* remove leading whitespace */
+ while (strchra((CHAR8 *)" \t", *line)) {
+ line++;
+ linelen--;
+ }
+
+ /* remove trailing whitespace */
+ while (linelen > 0 && strchra(sep, line[linelen-1]))
+ linelen--;
+ line[linelen] = '\0';
+
+ if (*line == '#')
+ goto skip;
+
+ /* split key/value */
+ value = line;
+ while (*value && !strchra(sep, *value))
+ value++;
+ if (*value == '\0')
+ goto skip;
+ *value = '\0';
+ value++;
+ while (*value && strchra(sep, *value))
+ value++;
+
+ /* unquote */
+ if (value[0] == '\"' && line[linelen-1] == '\"') {
+ value++;
+ line[linelen-1] = '\0';
+ }
+
+ *key_ret = line;
+ *value_ret = value;
+ return line;
+}
+
+static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
+ CHAR8 *line;
+ UINTN pos = 0;
+ CHAR8 *key, *value;
+
+ line = content;
+ while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
+ if (strcmpa((CHAR8 *)"timeout", key) == 0) {
+ CHAR16 *s;
+
+ s = stra_to_str(value);
+ config->timeout_sec_config = Atoi(s);
+ config->timeout_sec = config->timeout_sec_config;
+ FreePool(s);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"default", key) == 0) {
+ FreePool(config->entry_default_pattern);
+ config->entry_default_pattern = stra_to_str(value);
+ StrLwr(config->entry_default_pattern);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"splash", key) == 0) {
+ FreePool(config->splash);
+ config->splash = stra_to_path(value);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"background", key) == 0) {
+ CHAR16 c[3];
+
+ /* accept #RRGGBB hex notation */
+ if (value[0] != '#')
+ continue;
+ if (value[7] != '\0')
+ continue;
+
+ FreePool(config->background);
+ config->background = AllocateZeroPool(sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
+ if (!config->background)
+ continue;
+
+ c[0] = value[1];
+ c[1] = value[2];
+ c[2] = '\0';
+ config->background->Red = xtoi(c);
+
+ c[0] = value[3];
+ c[1] = value[4];
+ config->background->Green = xtoi(c);
+
+ c[0] = value[5];
+ c[1] = value[6];
+ config->background->Blue = xtoi(c);
+ continue;
+ }
+ }
+}
+
+static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
+ ConfigEntry *entry;
+ CHAR8 *line;
+ UINTN pos = 0;
+ CHAR8 *key, *value;
+ UINTN len;
+ CHAR16 *initrd = NULL;
+
+ entry = AllocateZeroPool(sizeof(ConfigEntry));
+
+ line = content;
+ while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
+ if (strcmpa((CHAR8 *)"title", key) == 0) {
+ FreePool(entry->title);
+ entry->title = stra_to_str(value);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"version", key) == 0) {
+ FreePool(entry->version);
+ entry->version = stra_to_str(value);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"machine-id", key) == 0) {
+ FreePool(entry->machine_id);
+ entry->machine_id = stra_to_str(value);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"linux", key) == 0) {
+ FreePool(entry->loader);
+ entry->type = LOADER_LINUX;
+ entry->loader = stra_to_path(value);
+ entry->key = 'l';
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"efi", key) == 0) {
+ entry->type = LOADER_EFI;
+ FreePool(entry->loader);
+ entry->loader = stra_to_path(value);
+
+ /* do not add an entry for ourselves */
+ if (StriCmp(entry->loader, loaded_image_path) == 0) {
+ entry->type = LOADER_UNDEFINED;
+ break;
+ }
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"architecture", key) == 0) {
+ /* do not add an entry for an EFI image of architecture not matching with that of the image */
+ if (strcmpa((CHAR8 *)EFI_MACHINE_TYPE_NAME, value) != 0) {
+ entry->type = LOADER_UNDEFINED;
+ break;
+ }
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"initrd", key) == 0) {
+ CHAR16 *new;
+
+ new = stra_to_path(value);
+ if (initrd) {
+ CHAR16 *s;
+
+ s = PoolPrint(L"%s initrd=%s", initrd, new);
+ FreePool(initrd);
+ initrd = s;
+ } else
+ initrd = PoolPrint(L"initrd=%s", new);
+ FreePool(new);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"options", key) == 0) {
+ CHAR16 *new;
+
+ new = stra_to_str(value);
+ if (entry->options) {
+ CHAR16 *s;
+
+ s = PoolPrint(L"%s %s", entry->options, new);
+ FreePool(entry->options);
+ entry->options = s;
+ } else {
+ entry->options = new;
+ new = NULL;
+ }
+ FreePool(new);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"splash", key) == 0) {
+ FreePool(entry->splash);
+ entry->splash = stra_to_path(value);
+ continue;
+ }
+ }
+
+ if (entry->type == LOADER_UNDEFINED) {
+ config_entry_free(entry);
+ FreePool(initrd);
+ FreePool(entry);
+ return;
+ }
+
+ /* add initrd= to options */
+ if (entry->type == LOADER_LINUX && initrd) {
+ if (entry->options) {
+ CHAR16 *s;
+
+ s = PoolPrint(L"%s %s", initrd, entry->options);
+ FreePool(entry->options);
+ entry->options = s;
+ } else {
+ entry->options = initrd;
+ initrd = NULL;
+ }
+ }
+ FreePool(initrd);
+
+ if (entry->machine_id) {
+ CHAR16 *var;
+
+ /* append additional options from EFI variables for this machine-id */
+ var = PoolPrint(L"LoaderEntryOptions-%s", entry->machine_id);
+ if (var) {
+ CHAR16 *s;
+
+ if (efivar_get(var, &s) == EFI_SUCCESS) {
+ if (entry->options) {
+ CHAR16 *s2;
+
+ s2 = PoolPrint(L"%s %s", entry->options, s);
+ FreePool(entry->options);
+ entry->options = s2;
+ } else
+ entry->options = s;
+ }
+ FreePool(var);
+ }
+
+ var = PoolPrint(L"LoaderEntryOptionsOneShot-%s", entry->machine_id);
+ if (var) {
+ CHAR16 *s;
+
+ if (efivar_get(var, &s) == EFI_SUCCESS) {
+ if (entry->options) {
+ CHAR16 *s2;
+
+ s2 = PoolPrint(L"%s %s", entry->options, s);
+ FreePool(entry->options);
+ entry->options = s2;
+ } else
+ entry->options = s;
+ efivar_set(var, NULL, TRUE);
+ }
+ FreePool(var);
+ }
+ }
+
+ entry->device = device;
+ entry->file = StrDuplicate(file);
+ len = StrLen(entry->file);
+ /* remove ".conf" */
+ if (len > 5)
+ entry->file[len - 5] = '\0';
+ StrLwr(entry->file);
+
+ config_add_entry(config, entry);
+}
+
+static VOID config_load(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
+ EFI_FILE_HANDLE entries_dir;
+ EFI_STATUS err;
+ CHAR8 *content = NULL;
+ UINTN sec;
+ UINTN len;
+ UINTN i;
+
+ len = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content);
+ if (len > 0)
+ config_defaults_load_from_file(config, content);
+ FreePool(content);
+
+ err = efivar_get_int(L"LoaderConfigTimeout", &sec);
+ if (!EFI_ERROR(err)) {
+ config->timeout_sec_efivar = sec;
+ config->timeout_sec = sec;
+ } else
+ config->timeout_sec_efivar = -1;
+
+ err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
+ if (!EFI_ERROR(err)) {
+ for (;;) {
+ CHAR16 buf[256];
+ UINTN bufsize;
+ EFI_FILE_INFO *f;
+ CHAR8 *content = NULL;
+ UINTN len;
+
+ bufsize = sizeof(buf);
+ err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf);
+ if (bufsize == 0 || EFI_ERROR(err))
+ break;
+
+ f = (EFI_FILE_INFO *) buf;
+ if (f->FileName[0] == '.')
+ continue;
+ if (f->Attribute & EFI_FILE_DIRECTORY)
+ continue;
+ len = StrLen(f->FileName);
+ if (len < 6)
+ continue;
+ if (StriCmp(f->FileName + len - 5, L".conf") != 0)
+ continue;
+
+ len = file_read(entries_dir, f->FileName, 0, 0, &content);
+ if (len > 0)
+ config_entry_add_from_file(config, device, f->FileName, content, loaded_image_path);
+ FreePool(content);
+ }
+ uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
+ }
+
+ /* sort entries after version number */
+ for (i = 1; i < config->entry_count; i++) {
+ BOOLEAN more;
+ UINTN k;
+
+ more = FALSE;
+ for (k = 0; k < config->entry_count - i; k++) {
+ ConfigEntry *entry;
+
+ if (str_verscmp(config->entries[k]->file, config->entries[k+1]->file) <= 0)
+ continue;
+ entry = config->entries[k];
+ config->entries[k] = config->entries[k+1];
+ config->entries[k+1] = entry;
+ more = TRUE;
+ }
+ if (!more)
+ break;
+ }
+}
+
+static VOID config_default_entry_select(Config *config) {
+ CHAR16 *var;
+ EFI_STATUS err;
+ UINTN i;
+
+ /*
+ * The EFI variable to specify a boot entry for the next, and only the
+ * next reboot. The variable is always cleared directly after it is read.
+ */
+ err = efivar_get(L"LoaderEntryOneShot", &var);
+ if (!EFI_ERROR(err)) {
+ BOOLEAN found = FALSE;
+
+ for (i = 0; i < config->entry_count; i++) {
+ if (StrCmp(config->entries[i]->file, var) == 0) {
+ config->idx_default = i;
+ found = TRUE;
+ break;
+ }
+ }
+
+ config->entry_oneshot = StrDuplicate(var);
+ efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
+ FreePool(var);
+ if (found)
+ return;
+ }
+
+ /*
+ * The EFI variable to select the default boot entry overrides the
+ * configured pattern. The variable can be set and cleared by pressing
+ * the 'd' key in the loader selection menu, the entry is marked with
+ * an '*'.
+ */
+ err = efivar_get(L"LoaderEntryDefault", &var);
+ if (!EFI_ERROR(err)) {
+ BOOLEAN found = FALSE;
+
+ for (i = 0; i < config->entry_count; i++) {
+ if (StrCmp(config->entries[i]->file, var) == 0) {
+ config->idx_default = i;
+ config->idx_default_efivar = i;
+ found = TRUE;
+ break;
+ }
+ }
+ FreePool(var);
+ if (found)
+ return;
+ }
+ config->idx_default_efivar = -1;
+
+ if (config->entry_count == 0)
+ return;
+
+ /*
+ * Match the pattern from the end of the list to the start, find last
+ * entry (largest number) matching the given pattern.
+ */
+ if (config->entry_default_pattern) {
+ i = config->entry_count;
+ while (i--) {
+ if (config->entries[i]->no_autoselect)
+ continue;
+ if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) {
+ config->idx_default = i;
+ return;
+ }
+ }
+ }
+
+ /* select the last suitable entry */
+ i = config->entry_count;
+ while (i--) {
+ if (config->entries[i]->no_autoselect)
+ continue;
+ config->idx_default = i;
+ return;
+ }
+
+ /* no entry found */
+ config->idx_default = -1;
+}
+
+/* generate a unique title, avoiding non-distinguishable menu entries */
+static VOID config_title_generate(Config *config) {
+ UINTN i, k;
+ BOOLEAN unique;
+
+ /* set title */
+ for (i = 0; i < config->entry_count; i++) {
+ CHAR16 *title;
+
+ FreePool(config->entries[i]->title_show);
+ title = config->entries[i]->title;
+ if (!title)
+ title = config->entries[i]->file;
+ config->entries[i]->title_show = StrDuplicate(title);
+ }
+
+ unique = TRUE;
+ for (i = 0; i < config->entry_count; i++) {
+ for (k = 0; k < config->entry_count; k++) {
+ if (i == k)
+ continue;
+ if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
+ continue;
+
+ unique = FALSE;
+ config->entries[i]->non_unique = TRUE;
+ config->entries[k]->non_unique = TRUE;
+ }
+ }
+ if (unique)
+ return;
+
+ /* add version to non-unique titles */
+ for (i = 0; i < config->entry_count; i++) {
+ CHAR16 *s;
+
+ if (!config->entries[i]->non_unique)
+ continue;
+ if (!config->entries[i]->version)
+ continue;
+
+ s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version);
+ FreePool(config->entries[i]->title_show);
+ config->entries[i]->title_show = s;
+ config->entries[i]->non_unique = FALSE;
+ }
+
+ unique = TRUE;
+ for (i = 0; i < config->entry_count; i++) {
+ for (k = 0; k < config->entry_count; k++) {
+ if (i == k)
+ continue;
+ if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
+ continue;
+
+ unique = FALSE;
+ config->entries[i]->non_unique = TRUE;
+ config->entries[k]->non_unique = TRUE;
+ }
+ }
+ if (unique)
+ return;
+
+ /* add machine-id to non-unique titles */
+ for (i = 0; i < config->entry_count; i++) {
+ CHAR16 *s;
+ CHAR16 *m;
+
+ if (!config->entries[i]->non_unique)
+ continue;
+ if (!config->entries[i]->machine_id)
+ continue;
+
+ m = StrDuplicate(config->entries[i]->machine_id);
+ m[8] = '\0';
+ s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, m);
+ FreePool(config->entries[i]->title_show);
+ config->entries[i]->title_show = s;
+ config->entries[i]->non_unique = FALSE;
+ FreePool(m);
+ }
+
+ unique = TRUE;
+ for (i = 0; i < config->entry_count; i++) {
+ for (k = 0; k < config->entry_count; k++) {
+ if (i == k)
+ continue;
+ if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
+ continue;
+
+ unique = FALSE;
+ config->entries[i]->non_unique = TRUE;
+ config->entries[k]->non_unique = TRUE;
+ }
+ }
+ if (unique)
+ return;
+
+ /* add file name to non-unique titles */
+ for (i = 0; i < config->entry_count; i++) {
+ CHAR16 *s;
+
+ if (!config->entries[i]->non_unique)
+ continue;
+ s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->file);
+ FreePool(config->entries[i]->title_show);
+ config->entries[i]->title_show = s;
+ config->entries[i]->non_unique = FALSE;
+ }
+}
+
+static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(VOID)) {
+ ConfigEntry *entry;
+
+ entry = AllocateZeroPool(sizeof(ConfigEntry));
+ entry->title = StrDuplicate(title);
+ entry->call = call;
+ entry->no_autoselect = TRUE;
+ config_add_entry(config, entry);
+ return TRUE;
+}
+
+static ConfigEntry *config_entry_add_loader(Config *config, EFI_HANDLE *device,
+ enum loader_type type,CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
+ ConfigEntry *entry;
+
+ entry = AllocateZeroPool(sizeof(ConfigEntry));
+ entry->type = type;
+ entry->title = StrDuplicate(title);
+ entry->device = device;
+ entry->loader = StrDuplicate(loader);
+ entry->file = StrDuplicate(file);
+ StrLwr(entry->file);
+ entry->key = key;
+ config_add_entry(config, entry);
+
+ return entry;
+}
+
+static BOOLEAN config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
+ CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
+ EFI_FILE_HANDLE handle;
+ ConfigEntry *entry;
+ EFI_STATUS err;
+
+ /* do not add an entry for ourselves */
+ if (loaded_image_path && StriCmp(loader, loaded_image_path) == 0)
+ return FALSE;
+
+ /* check existence */
+ err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0ULL);
+ if (EFI_ERROR(err))
+ return FALSE;
+ uefi_call_wrapper(handle->Close, 1, handle);
+
+ entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, file, key, title, loader);
+ if (!entry)
+ return FALSE;
+
+ /* do not boot right away into auto-detected entries */
+ entry->no_autoselect = TRUE;
+
+ /* do not show a splash; they do not need one, or they draw their own */
+ entry->splash = StrDuplicate(L"");
+
+ /* export identifiers of automatically added entries */
+ if (config->entries_auto) {
+ CHAR16 *s;
+
+ s = PoolPrint(L"%s %s", config->entries_auto, file);
+ FreePool(config->entries_auto);
+ config->entries_auto = s;
+ } else
+ config->entries_auto = StrDuplicate(file);
+
+ return TRUE;
+}
+
+static VOID config_entry_add_osx(Config *config) {
+ EFI_STATUS err;
+ UINTN handle_count = 0;
+ EFI_HANDLE *handles = NULL;
+
+ err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handle_count, &handles);
+ if (!EFI_ERROR(err)) {
+ UINTN i;
+
+ for (i = 0; i < handle_count; i++) {
+ EFI_FILE *root;
+ BOOLEAN found;
+
+ root = LibOpenRoot(handles[i]);
+ if (!root)
+ continue;
+ found = config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"OS X",
+ L"\\System\\Library\\CoreServices\\boot.efi");
+ uefi_call_wrapper(root->Close, 1, root);
+ if (found)
+ break;
+ }
+
+ FreePool(handles);
+ }
+}
+
+static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_image, EFI_FILE *root_dir) {
+ EFI_FILE_HANDLE linux_dir;
+ EFI_STATUS err;
+
+ err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL);
+ if (!EFI_ERROR(err)) {
+ for (;;) {
+ CHAR16 buf[256];
+ UINTN bufsize;
+ EFI_FILE_INFO *f;
+ CHAR8 *sections[] = {
+ (UINT8 *)".osrel",
+ NULL
+ };
+ UINTN offs[ELEMENTSOF(sections)-1] = {};
+ UINTN szs[ELEMENTSOF(sections)-1] = {};
+ UINTN addrs[ELEMENTSOF(sections)-1] = {};
+ CHAR8 *content = NULL;
+ UINTN len;
+ CHAR8 *line;
+ UINTN pos = 0;
+ CHAR8 *key, *value;
+ CHAR16 *os_name = NULL;
+ CHAR16 *os_id = NULL;
+ CHAR16 *os_version = NULL;
+
+ bufsize = sizeof(buf);
+ err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf);
+ if (bufsize == 0 || EFI_ERROR(err))
+ break;
+
+ f = (EFI_FILE_INFO *) buf;
+ if (f->FileName[0] == '.')
+ continue;
+ if (f->Attribute & EFI_FILE_DIRECTORY)
+ continue;
+ len = StrLen(f->FileName);
+ if (len < 5)
+ continue;
+ if (StriCmp(f->FileName + len - 4, L".efi") != 0)
+ continue;
+
+ /* look for an .osrel section in the .efi binary */
+ err = pefile_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
+ if (EFI_ERROR(err))
+ continue;
+
+ len = file_read(linux_dir, f->FileName, offs[0], szs[0], &content);
+ if (len <= 0)
+ continue;
+
+ /* read properties from the embedded os-release file */
+ line = content;
+ while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
+ if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) {
+ os_name = stra_to_str(value);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"ID", key) == 0) {
+ os_id = stra_to_str(value);
+ continue;
+ }
+
+ if (strcmpa((CHAR8 *)"VERSION_ID", key) == 0) {
+ os_version = stra_to_str(value);
+ continue;
+ }
+ }
+
+ if (os_name && os_id && os_version) {
+ CHAR16 *conf;
+ CHAR16 *path;
+
+ conf = PoolPrint(L"%s-%s", os_id, os_version);
+ path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
+ config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path);
+ FreePool(conf);
+ FreePool(path);
+ FreePool(os_name);
+ FreePool(os_id);
+ FreePool(os_version);
+ }
+
+ FreePool(content);
+ }
+ uefi_call_wrapper(linux_dir->Close, 1, linux_dir);
+ }
+}
+
+static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) {
+ EFI_HANDLE image;
+ EFI_DEVICE_PATH *path;
+ CHAR16 *options;
+ EFI_STATUS err;
+
+ path = FileDevicePath(entry->device, entry->loader);
+ if (!path) {
+ Print(L"Error getting device path.");
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
+ if (EFI_ERROR(err)) {
+ Print(L"Error loading %s: %r", entry->loader, err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ goto out;
+ }
+
+ if (config->options_edit)
+ options = config->options_edit;
+ else if (entry->options)
+ options = entry->options;
+ else
+ options = NULL;
+ if (options) {
+ EFI_LOADED_IMAGE *loaded_image;
+
+ err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
+ parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(err)) {
+ Print(L"Error getting LoadedImageProtocol handle: %r", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ goto out_unload;
+ }
+ loaded_image->LoadOptions = options;
+ loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
+ }
+
+ efivar_set_time_usec(L"LoaderTimeExecUSec", 0);
+ err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL);
+out_unload:
+ uefi_call_wrapper(BS->UnloadImage, 1, image);
+out:
+ FreePool(path);
+ return err;
+}
+
+static EFI_STATUS reboot_into_firmware(VOID) {
+ CHAR8 *b;
+ UINTN size;
+ UINT64 osind;
+ EFI_STATUS err;
+
+ osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+
+ err = efivar_get_raw(&global_guid, L"OsIndications", &b, &size);
+ if (!EFI_ERROR(err))
+ osind |= (UINT64)*b;
+ FreePool(b);
+
+ err = efivar_set_raw(&global_guid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE);
+ if (EFI_ERROR(err))
+ return err;
+
+ err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
+ Print(L"Error calling ResetSystem: %r", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ return err;
+}
+
+static VOID config_free(Config *config) {
+ UINTN i;
+
+ for (i = 0; i < config->entry_count; i++)
+ config_entry_free(config->entries[i]);
+ FreePool(config->entries);
+ FreePool(config->entry_default_pattern);
+ FreePool(config->options_edit);
+ FreePool(config->entry_oneshot);
+ FreePool(config->entries_auto);
+ FreePool(config->splash);
+ FreePool(config->background);
+}
+
+EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+ CHAR16 *s;
+ CHAR8 *b;
+ UINTN size;
+ EFI_LOADED_IMAGE *loaded_image;
+ EFI_FILE *root_dir;
+ CHAR16 *loaded_image_path;
+ EFI_DEVICE_PATH *device_path;
+ EFI_STATUS err;
+ Config config;
+ UINT64 init_usec;
+ BOOLEAN menu = FALSE;
+
+ InitializeLib(image, sys_table);
+ init_usec = time_usec();
+ efivar_set_time_usec(L"LoaderTimeInitUSec", init_usec);
+ efivar_set(L"LoaderInfo", L"systemd-boot " VERSION, FALSE);
+ s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+ efivar_set(L"LoaderFirmwareInfo", s, FALSE);
+ FreePool(s);
+ s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+ efivar_set(L"LoaderFirmwareType", s, FALSE);
+ FreePool(s);
+
+ 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;
+ }
+
+ /* export the device path this image is started from */
+ device_path = DevicePathFromHandle(loaded_image->DeviceHandle);
+ if (device_path) {
+ CHAR16 *str;
+ EFI_DEVICE_PATH *path, *paths;
+
+ str = DevicePathToStr(device_path);
+ efivar_set(L"LoaderDeviceIdentifier", str, FALSE);
+ FreePool(str);
+
+ paths = UnpackDevicePath(device_path);
+ for (path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
+ HARDDRIVE_DEVICE_PATH *drive;
+ CHAR16 uuid[37];
+
+ if (DevicePathType(path) != MEDIA_DEVICE_PATH)
+ continue;
+ if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
+ continue;
+ drive = (HARDDRIVE_DEVICE_PATH *)path;
+ if (drive->SignatureType != SIGNATURE_TYPE_GUID)
+ continue;
+
+ GuidToString(uuid, (EFI_GUID *)&drive->Signature);
+ efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
+ break;
+ }
+ FreePool(paths);
+ }
+
+ 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;
+ }
+
+
+ /* the filesystem path to this image, to prevent adding ourselves to the menu */
+ loaded_image_path = DevicePathToStr(loaded_image->FilePath);
+ efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE);
+
+ /* scan "\loader\entries\*.conf" files */
+ ZeroMem(&config, sizeof(Config));
+ config_load(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
+
+ if (!config.background) {
+ config.background = AllocateZeroPool(sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
+ if (StriCmp(L"Apple", ST->FirmwareVendor) == 0) {
+ config.background->Red = 0xc0;
+ config.background->Green = 0xc0;
+ config.background->Blue = 0xc0;
+ }
+ }
+
+ /* if we find some well-known loaders, add them to the end of the list */
+ config_entry_add_linux(&config, loaded_image, root_dir);
+ config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
+ L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
+ config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
+ L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
+ config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
+ L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME ".efi");
+ config_entry_add_osx(&config);
+ efivar_set(L"LoaderEntriesAuto", config.entries_auto, FALSE);
+
+ if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
+ UINT64 osind = (UINT64)*b;
+
+ if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
+ config_entry_add_call(&config, L"Reboot Into Firmware Interface", reboot_into_firmware);
+ FreePool(b);
+ }
+
+ if (config.entry_count == 0) {
+ Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ goto out;
+ }
+
+ config_title_generate(&config);
+
+ /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/
+ config_default_entry_select(&config);
+
+ /* if no configured entry to select from was found, enable the menu */
+ if (config.idx_default == -1) {
+ config.idx_default = 0;
+ if (config.timeout_sec == 0)
+ config.timeout_sec = 10;
+ }
+
+ /* select entry or show menu when key is pressed or timeout is set */
+ if (config.timeout_sec == 0) {
+ UINT64 key;
+
+ err = console_key_read(&key, FALSE);
+ if (!EFI_ERROR(err)) {
+ INT16 idx;
+
+ /* find matching key in config entries */
+ idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key));
+ if (idx >= 0)
+ config.idx_default = idx;
+ else
+ menu = TRUE;
+ }
+ } else
+ menu = TRUE;
+
+ for (;;) {
+ ConfigEntry *entry;
+
+ entry = config.entries[config.idx_default];
+ if (menu) {
+ efivar_set_time_usec(L"LoaderTimeMenuUSec", 0);
+ uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x10000, 0, NULL);
+ if (!menu_run(&config, &entry, root_dir, loaded_image_path))
+ break;
+
+ /* run special entry like "reboot" */
+ if (entry->call) {
+ entry->call();
+ continue;
+ }
+ } else {
+ err = EFI_NOT_FOUND;
+
+ /* splash from entry file */
+ if (entry->splash) {
+ /* some entries disable the splash because they draw their own */
+ if (entry->splash[0] == '\0')
+ err = EFI_SUCCESS;
+ else
+ err = graphics_splash(root_dir, entry->splash, config.background);
+ }
+
+ /* splash from config file */
+ if (EFI_ERROR(err) && config.splash)
+ err = graphics_splash(root_dir, config.splash, config.background);
+
+ /* default splash */
+ if (EFI_ERROR(err))
+ graphics_splash(root_dir, L"\\EFI\\systemd\\splash.bmp", config.background);
+ }
+
+ /* export the selected boot entry to the system */
+ efivar_set(L"LoaderEntrySelected", entry->file, FALSE);
+
+ uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
+ err = image_start(image, &config, entry);
+
+ if (err == EFI_ACCESS_DENIED || err == EFI_SECURITY_VIOLATION) {
+ /* Platform is secure boot and requested image isn't
+ * trusted. Need to go back to prior boot system and
+ * install more keys or hashes. Signal failure by
+ * returning the error */
+ Print(L"\nImage %s gives a security error\n", entry->title);
+ Print(L"Please enrol the hash or signature of %s\n", entry->loader);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ goto out;
+ }
+
+ menu = TRUE;
+ config.timeout_sec = 0;
+ }
+ err = EFI_SUCCESS;
+out:
+ FreePool(loaded_image_path);
+ config_free(&config);
+ uefi_call_wrapper(root_dir->Close, 1, root_dir);
+ uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);
+ return err;
+}
diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c
new file mode 100644
index 0000000..6206c80
--- /dev/null
+++ b/src/boot/efi/console.c
@@ -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 at vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald at 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;
+}
diff --git a/src/boot/efi/console.h b/src/boot/efi/console.h
new file mode 100644
index 0000000..5c7808a
--- /dev/null
+++ b/src/boot/efi/console.h
@@ -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 at vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald at 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
diff --git a/src/boot/efi/graphics.c b/src/boot/efi/graphics.c
new file mode 100644
index 0000000..11305b8
--- /dev/null
+++ b/src/boot/efi/graphics.c
@@ -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 at vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald at redhat.com>
+ * Copyright (C) 2013 Intel Corporation
+ * Authored by Joonas Lahtinen <joonas.lahtinen at 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;
+}
diff --git a/src/boot/efi/graphics.h b/src/boot/efi/graphics.h
new file mode 100644
index 0000000..8665afd
--- /dev/null
+++ b/src/boot/efi/graphics.h
@@ -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 at vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald at redhat.com>
+ * Copyright (C) 2013 Intel Corporation
+ * Authored by Joonas Lahtinen <joonas.lahtinen at 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
diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c
new file mode 100644
index 0000000..809c693
--- /dev/null
+++ b/src/boot/efi/linux.c
@@ -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 at 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;
+}
diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h
new file mode 100644
index 0000000..aff69a9
--- /dev/null
+++ b/src/boot/efi/linux.h
@@ -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 at 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
diff --git a/src/boot/efi/pefile.c b/src/boot/efi/pefile.c
new file mode 100644
index 0000000..e6fedbc
--- /dev/null
+++ b/src/boot/efi/pefile.c
@@ -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 at 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;
+}
diff --git a/src/boot/efi/pefile.h b/src/boot/efi/pefile.h
new file mode 100644
index 0000000..ca2f9a2
--- /dev/null
+++ b/src/boot/efi/pefile.h
@@ -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 at 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
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
new file mode 100644
index 0000000..e18faac
--- /dev/null
+++ b/src/boot/efi/stub.c
@@ -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 at 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;
+}
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
new file mode 100644
index 0000000..5678b50
--- /dev/null
+++ b/src/boot/efi/util.c
@@ -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 at vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald at 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;
+}
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
new file mode 100644
index 0000000..efaafd7
--- /dev/null
+++ b/src/boot/efi/util.h
@@ -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 at vrfy.org>
+ * Copyright (C) 2012 Harald Hoyer <harald at 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
diff --git a/src/sd-boot/.gitignore b/src/sd-boot/.gitignore
deleted file mode 100644
index 55b0da4..0000000
--- a/src/sd-boot/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/sd_boot.so
-/stub.so
diff --git a/src/sd-boot/console.c b/src/sd-boot/console.c
deleted file mode 100644
index 6206c80..0000000
--- a/src/sd-boot/console.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/*-*- 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 at vrfy.org>
- * Copyright (C) 2012 Harald Hoyer <harald at 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;
-}
diff --git a/src/sd-boot/console.h b/src/sd-boot/console.h
deleted file mode 100644
index 5c7808a..0000000
--- a/src/sd-boot/console.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-*- 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 at vrfy.org>
- * Copyright (C) 2012 Harald Hoyer <harald at 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
diff --git a/src/sd-boot/graphics.c b/src/sd-boot/graphics.c
deleted file mode 100644
index 11305b8..0000000
--- a/src/sd-boot/graphics.c
+++ /dev/null
@@ -1,389 +0,0 @@
-/*-*- 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 at vrfy.org>
- * Copyright (C) 2012 Harald Hoyer <harald at redhat.com>
- * Copyright (C) 2013 Intel Corporation
- * Authored by Joonas Lahtinen <joonas.lahtinen at 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;
-}
diff --git a/src/sd-boot/graphics.h b/src/sd-boot/graphics.h
deleted file mode 100644
index 8665afd..0000000
--- a/src/sd-boot/graphics.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*-*- 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 at vrfy.org>
- * Copyright (C) 2012 Harald Hoyer <harald at redhat.com>
- * Copyright (C) 2013 Intel Corporation
- * Authored by Joonas Lahtinen <joonas.lahtinen at 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
diff --git a/src/sd-boot/linux.c b/src/sd-boot/linux.c
deleted file mode 100644
index 809c693..0000000
--- a/src/sd-boot/linux.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*-*- 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 at 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;
-}
diff --git a/src/sd-boot/linux.h b/src/sd-boot/linux.h
deleted file mode 100644
index aff69a9..0000000
--- a/src/sd-boot/linux.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*-*- 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 at 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
diff --git a/src/sd-boot/pefile.c b/src/sd-boot/pefile.c
deleted file mode 100644
index e6fedbc..0000000
--- a/src/sd-boot/pefile.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/*-*- 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 at 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;
-}
diff --git a/src/sd-boot/pefile.h b/src/sd-boot/pefile.h
deleted file mode 100644
index ca2f9a2..0000000
--- a/src/sd-boot/pefile.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*-*- 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 at 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
diff --git a/src/sd-boot/sd-boot.c b/src/sd-boot/sd-boot.c
deleted file mode 100644
index 94039ea..0000000
--- a/src/sd-boot/sd-boot.c
+++ /dev/null
@@ -1,2023 +0,0 @@
-/*-*- 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-2015 Kay Sievers <kay at vrfy.org>
- * Copyright (C) 2012-2015 Harald Hoyer <harald at redhat.com>
- */
-
-#include <efi.h>
-#include <efilib.h>
-
-#include "util.h"
-#include "console.h"
-#include "graphics.h"
-#include "pefile.h"
-#include "linux.h"
-
-#ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
-#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
-#endif
-
-/* magic string to find in the binary image */
-static const char __attribute__((used)) magic[] = "#### LoaderInfo: sd-boot " VERSION " ####";
-
-static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
-
-enum loader_type {
- LOADER_UNDEFINED,
- LOADER_EFI,
- LOADER_LINUX
-};
-
-typedef struct {
- CHAR16 *file;
- CHAR16 *title_show;
- CHAR16 *title;
- CHAR16 *version;
- CHAR16 *machine_id;
- EFI_HANDLE *device;
- enum loader_type type;
- CHAR16 *loader;
- CHAR16 *options;
- CHAR16 *splash;
- CHAR16 key;
- EFI_STATUS (*call)(VOID);
- BOOLEAN no_autoselect;
- BOOLEAN non_unique;
-} ConfigEntry;
-
-typedef struct {
- ConfigEntry **entries;
- UINTN entry_count;
- INTN idx_default;
- INTN idx_default_efivar;
- UINTN timeout_sec;
- UINTN timeout_sec_config;
- INTN timeout_sec_efivar;
- CHAR16 *entry_default_pattern;
- CHAR16 *splash;
- EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background;
- CHAR16 *entry_oneshot;
- CHAR16 *options_edit;
- CHAR16 *entries_auto;
-} Config;
-
-static VOID cursor_left(UINTN *cursor, UINTN *first)
-{
- if ((*cursor) > 0)
- (*cursor)--;
- else if ((*first) > 0)
- (*first)--;
-}
-
-static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
-{
- if ((*cursor)+1 < x_max)
- (*cursor)++;
- else if ((*first) + (*cursor) < len)
- (*first)++;
-}
-
-static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) {
- CHAR16 *line;
- UINTN size;
- UINTN len;
- UINTN first;
- CHAR16 *print;
- UINTN cursor;
- UINTN clear;
- BOOLEAN exit;
- BOOLEAN enter;
-
- if (!line_in)
- line_in = L"";
- size = StrLen(line_in) + 1024;
- line = AllocatePool(size * sizeof(CHAR16));
- StrCpy(line, line_in);
- len = StrLen(line);
- print = AllocatePool((x_max+1) * sizeof(CHAR16));
-
- uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
-
- first = 0;
- cursor = 0;
- clear = 0;
- enter = FALSE;
- exit = FALSE;
- while (!exit) {
- EFI_STATUS err;
- UINT64 key;
- UINTN i;
-
- i = len - first;
- if (i >= x_max-1)
- i = x_max-1;
- CopyMem(print, line + first, i * sizeof(CHAR16));
- while (clear > 0 && i < x_max-1) {
- clear--;
- print[i++] = ' ';
- }
- print[i] = '\0';
-
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
-
- err = console_key_read(&key, TRUE);
- if (EFI_ERROR(err))
- continue;
-
- switch (key) {
- case KEYPRESS(0, SCAN_ESC, 0):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
- exit = TRUE;
- break;
-
- case KEYPRESS(0, SCAN_HOME, 0):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
- /* beginning-of-line */
- cursor = 0;
- first = 0;
- continue;
-
- case KEYPRESS(0, SCAN_END, 0):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
- /* end-of-line */
- cursor = len - first;
- if (cursor+1 >= x_max) {
- cursor = x_max-1;
- first = len - (x_max-1);
- }
- continue;
-
- case KEYPRESS(0, SCAN_DOWN, 0):
- case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
- case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
- /* forward-word */
- while (line[first + cursor] && line[first + cursor] == ' ')
- cursor_right(&cursor, &first, x_max, len);
- while (line[first + cursor] && line[first + cursor] != ' ')
- cursor_right(&cursor, &first, x_max, len);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
- continue;
-
- case KEYPRESS(0, SCAN_UP, 0):
- case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
- case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
- /* backward-word */
- if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
- cursor_left(&cursor, &first);
- while ((first + cursor) > 0 && line[first + cursor] == ' ')
- cursor_left(&cursor, &first);
- }
- while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
- cursor_left(&cursor, &first);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
- continue;
-
- case KEYPRESS(0, SCAN_RIGHT, 0):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
- /* forward-char */
- if (first + cursor == len)
- continue;
- cursor_right(&cursor, &first, x_max, len);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
- continue;
-
- case KEYPRESS(0, SCAN_LEFT, 0):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
- /* backward-char */
- cursor_left(&cursor, &first);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
- continue;
-
- case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
- /* kill-word */
- clear = 0;
- for (i = first + cursor; i < len && line[i] == ' '; i++)
- clear++;
- for (; i < len && line[i] != ' '; i++)
- clear++;
-
- for (i = first + cursor; i + clear < len; i++)
- line[i] = line[i + clear];
- len -= clear;
- line[len] = '\0';
- continue;
-
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'w'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('w')):
- case KEYPRESS(EFI_ALT_PRESSED, 0, CHAR_BACKSPACE):
- /* backward-kill-word */
- clear = 0;
- if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
- cursor_left(&cursor, &first);
- clear++;
- while ((first + cursor) > 0 && line[first + cursor] == ' ') {
- cursor_left(&cursor, &first);
- clear++;
- }
- }
- while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
- cursor_left(&cursor, &first);
- clear++;
- }
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
-
- for (i = first + cursor; i + clear < len; i++)
- line[i] = line[i + clear];
- len -= clear;
- line[len] = '\0';
- continue;
-
- case KEYPRESS(0, SCAN_DELETE, 0):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
- if (len == 0)
- continue;
- if (first + cursor == len)
- continue;
- for (i = first + cursor; i < len; i++)
- line[i] = line[i+1];
- clear = 1;
- len--;
- continue;
-
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
- /* kill-line */
- line[first + cursor] = '\0';
- clear = len - (first + cursor);
- len = first + cursor;
- continue;
-
- case KEYPRESS(0, 0, CHAR_LINEFEED):
- case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
- if (StrCmp(line, line_in) != 0) {
- *line_out = line;
- line = NULL;
- }
- enter = TRUE;
- exit = TRUE;
- break;
-
- case KEYPRESS(0, 0, CHAR_BACKSPACE):
- if (len == 0)
- continue;
- if (first == 0 && cursor == 0)
- continue;
- for (i = first + cursor-1; i < len; i++)
- line[i] = line[i+1];
- clear = 1;
- len--;
- if (cursor > 0)
- cursor--;
- if (cursor > 0 || first == 0)
- continue;
- /* show full line if it fits */
- if (len < x_max) {
- cursor = first;
- first = 0;
- continue;
- }
- /* jump left to see what we delete */
- if (first > 10) {
- first -= 10;
- cursor = 10;
- } else {
- cursor = first;
- first = 0;
- }
- continue;
-
- case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
- case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
- if (len+1 == size)
- continue;
- for (i = len; i > first + cursor; i--)
- line[i] = line[i-1];
- line[first + cursor] = KEYCHAR(key);
- len++;
- line[len] = '\0';
- if (cursor+1 < x_max)
- cursor++;
- else if (first + cursor < len)
- first++;
- continue;
- }
- }
-
- uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
- FreePool(print);
- FreePool(line);
- return enter;
-}
-
-static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
- UINTN i;
-
- if (key == 0)
- return -1;
-
- /* select entry by number key */
- if (key >= '1' && key <= '9') {
- i = key - '0';
- if (i > config->entry_count)
- i = config->entry_count;
- return i-1;
- }
-
- /* find matching key in config entries */
- for (i = start; i < config->entry_count; i++)
- if (config->entries[i]->key == key)
- return i;
-
- for (i = 0; i < start; i++)
- if (config->entries[i]->key == key)
- return i;
-
- return -1;
-}
-
-static VOID print_status(Config *config, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
- UINT64 key;
- UINTN i;
- CHAR16 *s;
- CHAR8 *b;
- UINTN x;
- UINTN y;
- UINTN size;
- EFI_STATUS err;
- UINTN color = 0;
- const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel = config->background;
-
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
- uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
-
- /* show splash and wait for key */
- for (;;) {
- static const EFI_GRAPHICS_OUTPUT_BLT_PIXEL colors[] = {
- { .Red = 0xff, .Green = 0xff, .Blue = 0xff },
- { .Red = 0xc0, .Green = 0xc0, .Blue = 0xc0 },
- { .Red = 0xff, .Green = 0, .Blue = 0 },
- { .Red = 0, .Green = 0xff, .Blue = 0 },
- { .Red = 0, .Green = 0, .Blue = 0xff },
- { .Red = 0, .Green = 0, .Blue = 0 },
- };
-
- err = EFI_NOT_FOUND;
- if (config->splash)
- err = graphics_splash(root_dir, config->splash, pixel);
- if (EFI_ERROR(err))
- err = graphics_splash(root_dir, L"\\EFI\\systemd\\splash.bmp", pixel);
- if (EFI_ERROR(err))
- break;
-
- /* 'b' rotates through background colors */
- console_key_read(&key, TRUE);
- if (key == KEYPRESS(0, 0, 'b')) {
- pixel = &colors[color++];
- if (color == ELEMENTSOF(colors))
- color = 0;
-
- continue;
- }
-
- graphics_mode(FALSE);
- uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
- break;
- }
-
- Print(L"sd-boot version: " VERSION "\n");
- Print(L"architecture: " EFI_MACHINE_TYPE_NAME "\n");
- Print(L"loaded image: %s\n", loaded_image_path);
- Print(L"UEFI specification: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
- Print(L"firmware vendor: %s\n", ST->FirmwareVendor);
- Print(L"firmware version: %d.%02d\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
-
- if (uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x, &y) == EFI_SUCCESS)
- Print(L"console size: %d x %d\n", x, y);
-
- if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) {
- Print(L"SecureBoot: %s\n", *b > 0 ? L"enabled" : L"disabled");
- FreePool(b);
- }
-
- if (efivar_get_raw(&global_guid, L"SetupMode", &b, &size) == EFI_SUCCESS) {
- Print(L"SetupMode: %s\n", *b > 0 ? L"setup" : L"user");
- FreePool(b);
- }
-
- if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
- Print(L"OsIndicationsSupported: %d\n", (UINT64)*b);
- FreePool(b);
- }
- Print(L"\n");
-
- Print(L"timeout: %d\n", config->timeout_sec);
- if (config->timeout_sec_efivar >= 0)
- Print(L"timeout (EFI var): %d\n", config->timeout_sec_efivar);
- Print(L"timeout (config): %d\n", config->timeout_sec_config);
- if (config->entry_default_pattern)
- Print(L"default pattern: '%s'\n", config->entry_default_pattern);
- if (config->splash)
- Print(L"splash '%s'\n", config->splash);
- if (config->background)
- Print(L"background '#%02x%02x%02x'\n",
- config->background->Red,
- config->background->Green,
- config->background->Blue);
- Print(L"\n");
-
- Print(L"config entry count: %d\n", config->entry_count);
- Print(L"entry selected idx: %d\n", config->idx_default);
- if (config->idx_default_efivar >= 0)
- Print(L"entry EFI var idx: %d\n", config->idx_default_efivar);
- Print(L"\n");
-
- if (efivar_get_int(L"LoaderConfigTimeout", &i) == EFI_SUCCESS)
- Print(L"LoaderConfigTimeout: %d\n", i);
- if (config->entry_oneshot)
- Print(L"LoaderEntryOneShot: %s\n", config->entry_oneshot);
- if (efivar_get(L"LoaderDeviceIdentifier", &s) == EFI_SUCCESS) {
- Print(L"LoaderDeviceIdentifier: %s\n", s);
- FreePool(s);
- }
- if (efivar_get(L"LoaderDevicePartUUID", &s) == EFI_SUCCESS) {
- Print(L"LoaderDevicePartUUID: %s\n", s);
- FreePool(s);
- }
- if (efivar_get(L"LoaderEntryDefault", &s) == EFI_SUCCESS) {
- Print(L"LoaderEntryDefault: %s\n", s);
- FreePool(s);
- }
-
- Print(L"\n--- press key ---\n\n");
- console_key_read(&key, TRUE);
-
- for (i = 0; i < config->entry_count; i++) {
- ConfigEntry *entry;
-
- if (key == KEYPRESS(0, SCAN_ESC, 0) || key == KEYPRESS(0, 0, 'q'))
- break;
-
- entry = config->entries[i];
-
- if (entry->splash) {
- err = graphics_splash(root_dir, entry->splash, config->background);
- if (!EFI_ERROR(err)) {
- console_key_read(&key, TRUE);
- graphics_mode(FALSE);
- }
- }
-
- Print(L"config entry: %d/%d\n", i+1, config->entry_count);
- if (entry->file)
- Print(L"file '%s'\n", entry->file);
- Print(L"title show '%s'\n", entry->title_show);
- if (entry->title)
- Print(L"title '%s'\n", entry->title);
- if (entry->version)
- Print(L"version '%s'\n", entry->version);
- if (entry->machine_id)
- Print(L"machine-id '%s'\n", entry->machine_id);
- if (entry->device) {
- EFI_DEVICE_PATH *device_path;
- CHAR16 *str;
-
- device_path = DevicePathFromHandle(entry->device);
- if (device_path) {
- str = DevicePathToStr(device_path);
- Print(L"device handle '%s'\n", str);
- FreePool(str);
- }
- }
- if (entry->loader)
- Print(L"loader '%s'\n", entry->loader);
- if (entry->options)
- Print(L"options '%s'\n", entry->options);
- if (entry->splash)
- Print(L"splash '%s'\n", entry->splash);
- Print(L"auto-select %s\n", entry->no_autoselect ? L"no" : L"yes");
- if (entry->call)
- Print(L"internal call yes\n");
-
- Print(L"\n--- press key ---\n\n");
- console_key_read(&key, TRUE);
- }
-
- uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
-}
-
-static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
- EFI_STATUS err;
- UINTN visible_max;
- UINTN idx_highlight;
- UINTN idx_highlight_prev;
- UINTN idx_first;
- UINTN idx_last;
- BOOLEAN refresh;
- BOOLEAN highlight;
- UINTN i;
- UINTN line_width;
- CHAR16 **lines;
- UINTN x_start;
- UINTN y_start;
- UINTN x_max;
- UINTN y_max;
- CHAR16 *status;
- CHAR16 *clearline;
- INTN timeout_remain;
- INT16 idx;
- BOOLEAN exit = FALSE;
- BOOLEAN run = TRUE;
- BOOLEAN wait = FALSE;
-
- graphics_mode(FALSE);
- uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
- uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
-
- /* draw a single character to make ClearScreen work on some firmware */
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L" ");
- uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
-
- err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
- if (EFI_ERROR(err)) {
- x_max = 80;
- y_max = 25;
- }
-
- /* we check 10 times per second for a keystroke */
- if (config->timeout_sec > 0)
- timeout_remain = config->timeout_sec * 10;
- else
- timeout_remain = -1;
-
- idx_highlight = config->idx_default;
- idx_highlight_prev = 0;
-
- visible_max = y_max - 2;
-
- if ((UINTN)config->idx_default >= visible_max)
- idx_first = config->idx_default-1;
- else
- idx_first = 0;
-
- idx_last = idx_first + visible_max-1;
-
- refresh = TRUE;
- highlight = FALSE;
-
- /* length of the longest entry */
- line_width = 5;
- for (i = 0; i < config->entry_count; i++) {
- UINTN entry_len;
-
- entry_len = StrLen(config->entries[i]->title_show);
- if (line_width < entry_len)
- line_width = entry_len;
- }
- if (line_width > x_max-6)
- line_width = x_max-6;
-
- /* offsets to center the entries on the screen */
- x_start = (x_max - (line_width)) / 2;
- if (config->entry_count < visible_max)
- y_start = ((visible_max - config->entry_count) / 2) + 1;
- else
- y_start = 0;
-
- /* menu entries title lines */
- lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
- for (i = 0; i < config->entry_count; i++) {
- UINTN j, k;
-
- lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16)));
- for (j = 0; j < x_start; j++)
- lines[i][j] = ' ';
-
- for (k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++)
- lines[i][j] = config->entries[i]->title_show[k];
-
- for (; j < x_max; j++)
- lines[i][j] = ' ';
- lines[i][x_max] = '\0';
- }
-
- status = NULL;
- clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
- for (i = 0; i < x_max; i++)
- clearline[i] = ' ';
- clearline[i] = 0;
-
- while (!exit) {
- UINT64 key;
-
- if (refresh) {
- for (i = 0; i < config->entry_count; i++) {
- if (i < idx_first || i > idx_last)
- continue;
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first);
- if (i == idx_highlight)
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
- EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
- else
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
- EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
- if ((INTN)i == config->idx_default_efivar) {
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
- }
- }
- refresh = FALSE;
- } else if (highlight) {
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first);
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
- if ((INTN)idx_highlight_prev == config->idx_default_efivar) {
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
- }
-
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first);
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
- if ((INTN)idx_highlight == config->idx_default_efivar) {
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>");
- }
- highlight = FALSE;
- }
-
- if (timeout_remain > 0) {
- FreePool(status);
- status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10);
- }
-
- /* print status at last line of screen */
- if (status) {
- UINTN len;
- UINTN x;
-
- /* center line */
- len = StrLen(status);
- if (len < x_max)
- x = (x_max - len) / 2;
- else
- x = 0;
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x));
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
- }
-
- err = console_key_read(&key, wait);
- if (EFI_ERROR(err)) {
- /* timeout reached */
- if (timeout_remain == 0) {
- exit = TRUE;
- break;
- }
-
- /* sleep and update status */
- if (timeout_remain > 0) {
- uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
- timeout_remain--;
- continue;
- }
-
- /* timeout disabled, wait for next key */
- wait = TRUE;
- continue;
- }
-
- timeout_remain = -1;
-
- /* clear status after keystroke */
- if (status) {
- FreePool(status);
- status = NULL;
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
- }
-
- idx_highlight_prev = idx_highlight;
-
- switch (key) {
- case KEYPRESS(0, SCAN_UP, 0):
- case KEYPRESS(0, 0, 'k'):
- if (idx_highlight > 0)
- idx_highlight--;
- break;
-
- case KEYPRESS(0, SCAN_DOWN, 0):
- case KEYPRESS(0, 0, 'j'):
- if (idx_highlight < config->entry_count-1)
- idx_highlight++;
- break;
-
- case KEYPRESS(0, SCAN_HOME, 0):
- case KEYPRESS(EFI_ALT_PRESSED, 0, '<'):
- if (idx_highlight > 0) {
- refresh = TRUE;
- idx_highlight = 0;
- }
- break;
-
- case KEYPRESS(0, SCAN_END, 0):
- case KEYPRESS(EFI_ALT_PRESSED, 0, '>'):
- if (idx_highlight < config->entry_count-1) {
- refresh = TRUE;
- idx_highlight = config->entry_count-1;
- }
- break;
-
- case KEYPRESS(0, SCAN_PAGE_UP, 0):
- if (idx_highlight > visible_max)
- idx_highlight -= visible_max;
- else
- idx_highlight = 0;
- break;
-
- case KEYPRESS(0, SCAN_PAGE_DOWN, 0):
- idx_highlight += visible_max;
- if (idx_highlight > config->entry_count-1)
- idx_highlight = config->entry_count-1;
- break;
-
- case KEYPRESS(0, 0, CHAR_LINEFEED):
- case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
- exit = TRUE;
- break;
-
- case KEYPRESS(0, SCAN_F1, 0):
- case KEYPRESS(0, 0, 'h'):
- case KEYPRESS(0, 0, '?'):
- status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
- break;
-
- case KEYPRESS(0, 0, 'Q'):
- exit = TRUE;
- run = FALSE;
- break;
-
- case KEYPRESS(0, 0, 'd'):
- if (config->idx_default_efivar != (INTN)idx_highlight) {
- /* store the selected entry in a persistent EFI variable */
- efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE);
- config->idx_default_efivar = idx_highlight;
- status = StrDuplicate(L"Default boot entry selected.");
- } else {
- /* clear the default entry EFI variable */
- efivar_set(L"LoaderEntryDefault", NULL, TRUE);
- config->idx_default_efivar = -1;
- status = StrDuplicate(L"Default boot entry cleared.");
- }
- refresh = TRUE;
- break;
-
- case KEYPRESS(0, 0, '-'):
- case KEYPRESS(0, 0, 'T'):
- if (config->timeout_sec_efivar > 0) {
- config->timeout_sec_efivar--;
- efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
- if (config->timeout_sec_efivar > 0)
- status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar);
- else
- status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
- } else if (config->timeout_sec_efivar <= 0){
- config->timeout_sec_efivar = -1;
- efivar_set(L"LoaderConfigTimeout", NULL, TRUE);
- if (config->timeout_sec_config > 0)
- status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.",
- config->timeout_sec_config);
- else
- status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
- }
- break;
-
- case KEYPRESS(0, 0, '+'):
- case KEYPRESS(0, 0, 't'):
- if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0)
- config->timeout_sec_efivar++;
- config->timeout_sec_efivar++;
- efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE);
- if (config->timeout_sec_efivar > 0)
- status = PoolPrint(L"Menu timeout set to %d sec.",
- config->timeout_sec_efivar);
- else
- status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
- break;
-
- case KEYPRESS(0, 0, 'e'):
- /* only the options of configured entries can be edited */
- if (config->entries[idx_highlight]->type == LOADER_UNDEFINED)
- break;
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
- if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1))
- exit = TRUE;
- uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
- uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
- break;
-
- case KEYPRESS(0, 0, 'v'):
- status = PoolPrint(L"sd-boot " VERSION " (" EFI_MACHINE_TYPE_NAME "), UEFI Specification %d.%02d, Vendor %s %d.%02d",
- ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff,
- ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
- break;
-
- case KEYPRESS(0, 0, 'P'):
- print_status(config, root_dir, loaded_image_path);
- refresh = TRUE;
- break;
-
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'):
- case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')):
- refresh = TRUE;
- break;
-
- default:
- /* jump with a hotkey directly to a matching entry */
- idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key));
- if (idx < 0)
- break;
- idx_highlight = idx;
- refresh = TRUE;
- }
-
- if (idx_highlight > idx_last) {
- idx_last = idx_highlight;
- idx_first = 1 + idx_highlight - visible_max;
- refresh = TRUE;
- }
- if (idx_highlight < idx_first) {
- idx_first = idx_highlight;
- idx_last = idx_highlight + visible_max-1;
- refresh = TRUE;
- }
-
- idx_last = idx_first + visible_max-1;
-
- if (!refresh && idx_highlight != idx_highlight_prev)
- highlight = TRUE;
- }
-
- *chosen_entry = config->entries[idx_highlight];
-
- for (i = 0; i < config->entry_count; i++)
- FreePool(lines[i]);
- FreePool(lines);
- FreePool(clearline);
-
- uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
- uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
- return run;
-}
-
-static VOID config_add_entry(Config *config, ConfigEntry *entry) {
- if ((config->entry_count & 15) == 0) {
- UINTN i;
-
- i = config->entry_count + 16;
- if (config->entry_count == 0)
- config->entries = AllocatePool(sizeof(VOID *) * i);
- else
- config->entries = ReallocatePool(config->entries,
- sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i);
- }
- config->entries[config->entry_count++] = entry;
-}
-
-static VOID config_entry_free(ConfigEntry *entry) {
- FreePool(entry->title_show);
- FreePool(entry->title);
- FreePool(entry->machine_id);
- FreePool(entry->loader);
- FreePool(entry->options);
-}
-
-static BOOLEAN is_digit(CHAR16 c)
-{
- return (c >= '0') && (c <= '9');
-}
-
-static UINTN c_order(CHAR16 c)
-{
- if (c == '\0')
- return 0;
- if (is_digit(c))
- return 0;
- else if ((c >= 'a') && (c <= 'z'))
- return c;
- else
- return c + 0x10000;
-}
-
-static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2)
-{
- CHAR16 *os1 = s1;
- CHAR16 *os2 = s2;
-
- while (*s1 || *s2) {
- INTN first;
-
- while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
- INTN order;
-
- order = c_order(*s1) - c_order(*s2);
- if (order)
- return order;
- s1++;
- s2++;
- }
-
- while (*s1 == '0')
- s1++;
- while (*s2 == '0')
- s2++;
-
- first = 0;
- while (is_digit(*s1) && is_digit(*s2)) {
- if (first == 0)
- first = *s1 - *s2;
- s1++;
- s2++;
- }
-
- if (is_digit(*s1))
- return 1;
- if (is_digit(*s2))
- return -1;
-
- if (first)
- return first;
- }
-
- return StrCmp(os1, os2);
-}
-
-static CHAR8 *line_get_key_value(CHAR8 *content, CHAR8 *sep, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) {
- CHAR8 *line;
- UINTN linelen;
- CHAR8 *value;
-
-skip:
- line = content + *pos;
- if (*line == '\0')
- return NULL;
-
- linelen = 0;
- while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen]))
- linelen++;
-
- /* move pos to next line */
- *pos += linelen;
- if (content[*pos])
- (*pos)++;
-
- /* empty line */
- if (linelen == 0)
- goto skip;
-
- /* terminate line */
- line[linelen] = '\0';
-
- /* remove leading whitespace */
- while (strchra((CHAR8 *)" \t", *line)) {
- line++;
- linelen--;
- }
-
- /* remove trailing whitespace */
- while (linelen > 0 && strchra(sep, line[linelen-1]))
- linelen--;
- line[linelen] = '\0';
-
- if (*line == '#')
- goto skip;
-
- /* split key/value */
- value = line;
- while (*value && !strchra(sep, *value))
- value++;
- if (*value == '\0')
- goto skip;
- *value = '\0';
- value++;
- while (*value && strchra(sep, *value))
- value++;
-
- /* unquote */
- if (value[0] == '\"' && line[linelen-1] == '\"') {
- value++;
- line[linelen-1] = '\0';
- }
-
- *key_ret = line;
- *value_ret = value;
- return line;
-}
-
-static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
- CHAR8 *line;
- UINTN pos = 0;
- CHAR8 *key, *value;
-
- line = content;
- while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
- if (strcmpa((CHAR8 *)"timeout", key) == 0) {
- CHAR16 *s;
-
- s = stra_to_str(value);
- config->timeout_sec_config = Atoi(s);
- config->timeout_sec = config->timeout_sec_config;
- FreePool(s);
- continue;
- }
-
- if (strcmpa((CHAR8 *)"default", key) == 0) {
- FreePool(config->entry_default_pattern);
- config->entry_default_pattern = stra_to_str(value);
- StrLwr(config->entry_default_pattern);
- continue;
- }
-
- if (strcmpa((CHAR8 *)"splash", key) == 0) {
- FreePool(config->splash);
- config->splash = stra_to_path(value);
- continue;
- }
-
- if (strcmpa((CHAR8 *)"background", key) == 0) {
- CHAR16 c[3];
-
- /* accept #RRGGBB hex notation */
- if (value[0] != '#')
- continue;
- if (value[7] != '\0')
- continue;
-
- FreePool(config->background);
- config->background = AllocateZeroPool(sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
- if (!config->background)
- continue;
-
- c[0] = value[1];
- c[1] = value[2];
- c[2] = '\0';
- config->background->Red = xtoi(c);
-
- c[0] = value[3];
- c[1] = value[4];
- config->background->Green = xtoi(c);
-
- c[0] = value[5];
- c[1] = value[6];
- config->background->Blue = xtoi(c);
- continue;
- }
- }
-}
-
-static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
- ConfigEntry *entry;
- CHAR8 *line;
- UINTN pos = 0;
- CHAR8 *key, *value;
- UINTN len;
- CHAR16 *initrd = NULL;
-
- entry = AllocateZeroPool(sizeof(ConfigEntry));
-
- line = content;
- while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
- if (strcmpa((CHAR8 *)"title", key) == 0) {
- FreePool(entry->title);
- entry->title = stra_to_str(value);
- continue;
- }
-
- if (strcmpa((CHAR8 *)"version", key) == 0) {
- FreePool(entry->version);
- entry->version = stra_to_str(value);
- continue;
- }
-
- if (strcmpa((CHAR8 *)"machine-id", key) == 0) {
- FreePool(entry->machine_id);
- entry->machine_id = stra_to_str(value);
- continue;
- }
-
- if (strcmpa((CHAR8 *)"linux", key) == 0) {
- FreePool(entry->loader);
- entry->type = LOADER_LINUX;
- entry->loader = stra_to_path(value);
- entry->key = 'l';
- continue;
- }
-
- if (strcmpa((CHAR8 *)"efi", key) == 0) {
- entry->type = LOADER_EFI;
- FreePool(entry->loader);
- entry->loader = stra_to_path(value);
-
- /* do not add an entry for ourselves */
- if (StriCmp(entry->loader, loaded_image_path) == 0) {
- entry->type = LOADER_UNDEFINED;
- break;
- }
- continue;
- }
-
- if (strcmpa((CHAR8 *)"architecture", key) == 0) {
- /* do not add an entry for an EFI image of architecture not matching with that of the image */
- if (strcmpa((CHAR8 *)EFI_MACHINE_TYPE_NAME, value) != 0) {
- entry->type = LOADER_UNDEFINED;
- break;
- }
- continue;
- }
-
- if (strcmpa((CHAR8 *)"initrd", key) == 0) {
- CHAR16 *new;
-
- new = stra_to_path(value);
- if (initrd) {
- CHAR16 *s;
-
- s = PoolPrint(L"%s initrd=%s", initrd, new);
- FreePool(initrd);
- initrd = s;
- } else
- initrd = PoolPrint(L"initrd=%s", new);
- FreePool(new);
- continue;
- }
-
- if (strcmpa((CHAR8 *)"options", key) == 0) {
- CHAR16 *new;
-
- new = stra_to_str(value);
- if (entry->options) {
- CHAR16 *s;
-
- s = PoolPrint(L"%s %s", entry->options, new);
- FreePool(entry->options);
- entry->options = s;
- } else {
- entry->options = new;
- new = NULL;
- }
- FreePool(new);
- continue;
- }
-
- if (strcmpa((CHAR8 *)"splash", key) == 0) {
- FreePool(entry->splash);
- entry->splash = stra_to_path(value);
- continue;
- }
- }
-
- if (entry->type == LOADER_UNDEFINED) {
- config_entry_free(entry);
- FreePool(initrd);
- FreePool(entry);
- return;
- }
-
- /* add initrd= to options */
- if (entry->type == LOADER_LINUX && initrd) {
- if (entry->options) {
- CHAR16 *s;
-
- s = PoolPrint(L"%s %s", initrd, entry->options);
- FreePool(entry->options);
- entry->options = s;
- } else {
- entry->options = initrd;
- initrd = NULL;
- }
- }
- FreePool(initrd);
-
- if (entry->machine_id) {
- CHAR16 *var;
-
- /* append additional options from EFI variables for this machine-id */
- var = PoolPrint(L"LoaderEntryOptions-%s", entry->machine_id);
- if (var) {
- CHAR16 *s;
-
- if (efivar_get(var, &s) == EFI_SUCCESS) {
- if (entry->options) {
- CHAR16 *s2;
-
- s2 = PoolPrint(L"%s %s", entry->options, s);
- FreePool(entry->options);
- entry->options = s2;
- } else
- entry->options = s;
- }
- FreePool(var);
- }
-
- var = PoolPrint(L"LoaderEntryOptionsOneShot-%s", entry->machine_id);
- if (var) {
- CHAR16 *s;
-
- if (efivar_get(var, &s) == EFI_SUCCESS) {
- if (entry->options) {
- CHAR16 *s2;
-
- s2 = PoolPrint(L"%s %s", entry->options, s);
- FreePool(entry->options);
- entry->options = s2;
- } else
- entry->options = s;
- efivar_set(var, NULL, TRUE);
- }
- FreePool(var);
- }
- }
-
- entry->device = device;
- entry->file = StrDuplicate(file);
- len = StrLen(entry->file);
- /* remove ".conf" */
- if (len > 5)
- entry->file[len - 5] = '\0';
- StrLwr(entry->file);
-
- config_add_entry(config, entry);
-}
-
-static VOID config_load(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
- EFI_FILE_HANDLE entries_dir;
- EFI_STATUS err;
- CHAR8 *content = NULL;
- UINTN sec;
- UINTN len;
- UINTN i;
-
- len = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content);
- if (len > 0)
- config_defaults_load_from_file(config, content);
- FreePool(content);
-
- err = efivar_get_int(L"LoaderConfigTimeout", &sec);
- if (!EFI_ERROR(err)) {
- config->timeout_sec_efivar = sec;
- config->timeout_sec = sec;
- } else
- config->timeout_sec_efivar = -1;
-
- err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
- if (!EFI_ERROR(err)) {
- for (;;) {
- CHAR16 buf[256];
- UINTN bufsize;
- EFI_FILE_INFO *f;
- CHAR8 *content = NULL;
- UINTN len;
-
- bufsize = sizeof(buf);
- err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf);
- if (bufsize == 0 || EFI_ERROR(err))
- break;
-
- f = (EFI_FILE_INFO *) buf;
- if (f->FileName[0] == '.')
- continue;
- if (f->Attribute & EFI_FILE_DIRECTORY)
- continue;
- len = StrLen(f->FileName);
- if (len < 6)
- continue;
- if (StriCmp(f->FileName + len - 5, L".conf") != 0)
- continue;
-
- len = file_read(entries_dir, f->FileName, 0, 0, &content);
- if (len > 0)
- config_entry_add_from_file(config, device, f->FileName, content, loaded_image_path);
- FreePool(content);
- }
- uefi_call_wrapper(entries_dir->Close, 1, entries_dir);
- }
-
- /* sort entries after version number */
- for (i = 1; i < config->entry_count; i++) {
- BOOLEAN more;
- UINTN k;
-
- more = FALSE;
- for (k = 0; k < config->entry_count - i; k++) {
- ConfigEntry *entry;
-
- if (str_verscmp(config->entries[k]->file, config->entries[k+1]->file) <= 0)
- continue;
- entry = config->entries[k];
- config->entries[k] = config->entries[k+1];
- config->entries[k+1] = entry;
- more = TRUE;
- }
- if (!more)
- break;
- }
-}
-
-static VOID config_default_entry_select(Config *config) {
- CHAR16 *var;
- EFI_STATUS err;
- UINTN i;
-
- /*
- * The EFI variable to specify a boot entry for the next, and only the
- * next reboot. The variable is always cleared directly after it is read.
- */
- err = efivar_get(L"LoaderEntryOneShot", &var);
- if (!EFI_ERROR(err)) {
- BOOLEAN found = FALSE;
-
- for (i = 0; i < config->entry_count; i++) {
- if (StrCmp(config->entries[i]->file, var) == 0) {
- config->idx_default = i;
- found = TRUE;
- break;
- }
- }
-
- config->entry_oneshot = StrDuplicate(var);
- efivar_set(L"LoaderEntryOneShot", NULL, TRUE);
- FreePool(var);
- if (found)
- return;
- }
-
- /*
- * The EFI variable to select the default boot entry overrides the
- * configured pattern. The variable can be set and cleared by pressing
- * the 'd' key in the loader selection menu, the entry is marked with
- * an '*'.
- */
- err = efivar_get(L"LoaderEntryDefault", &var);
- if (!EFI_ERROR(err)) {
- BOOLEAN found = FALSE;
-
- for (i = 0; i < config->entry_count; i++) {
- if (StrCmp(config->entries[i]->file, var) == 0) {
- config->idx_default = i;
- config->idx_default_efivar = i;
- found = TRUE;
- break;
- }
- }
- FreePool(var);
- if (found)
- return;
- }
- config->idx_default_efivar = -1;
-
- if (config->entry_count == 0)
- return;
-
- /*
- * Match the pattern from the end of the list to the start, find last
- * entry (largest number) matching the given pattern.
- */
- if (config->entry_default_pattern) {
- i = config->entry_count;
- while (i--) {
- if (config->entries[i]->no_autoselect)
- continue;
- if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) {
- config->idx_default = i;
- return;
- }
- }
- }
-
- /* select the last suitable entry */
- i = config->entry_count;
- while (i--) {
- if (config->entries[i]->no_autoselect)
- continue;
- config->idx_default = i;
- return;
- }
-
- /* no entry found */
- config->idx_default = -1;
-}
-
-/* generate a unique title, avoiding non-distinguishable menu entries */
-static VOID config_title_generate(Config *config) {
- UINTN i, k;
- BOOLEAN unique;
-
- /* set title */
- for (i = 0; i < config->entry_count; i++) {
- CHAR16 *title;
-
- FreePool(config->entries[i]->title_show);
- title = config->entries[i]->title;
- if (!title)
- title = config->entries[i]->file;
- config->entries[i]->title_show = StrDuplicate(title);
- }
-
- unique = TRUE;
- for (i = 0; i < config->entry_count; i++) {
- for (k = 0; k < config->entry_count; k++) {
- if (i == k)
- continue;
- if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
- continue;
-
- unique = FALSE;
- config->entries[i]->non_unique = TRUE;
- config->entries[k]->non_unique = TRUE;
- }
- }
- if (unique)
- return;
-
- /* add version to non-unique titles */
- for (i = 0; i < config->entry_count; i++) {
- CHAR16 *s;
-
- if (!config->entries[i]->non_unique)
- continue;
- if (!config->entries[i]->version)
- continue;
-
- s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version);
- FreePool(config->entries[i]->title_show);
- config->entries[i]->title_show = s;
- config->entries[i]->non_unique = FALSE;
- }
-
- unique = TRUE;
- for (i = 0; i < config->entry_count; i++) {
- for (k = 0; k < config->entry_count; k++) {
- if (i == k)
- continue;
- if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
- continue;
-
- unique = FALSE;
- config->entries[i]->non_unique = TRUE;
- config->entries[k]->non_unique = TRUE;
- }
- }
- if (unique)
- return;
-
- /* add machine-id to non-unique titles */
- for (i = 0; i < config->entry_count; i++) {
- CHAR16 *s;
- CHAR16 *m;
-
- if (!config->entries[i]->non_unique)
- continue;
- if (!config->entries[i]->machine_id)
- continue;
-
- m = StrDuplicate(config->entries[i]->machine_id);
- m[8] = '\0';
- s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, m);
- FreePool(config->entries[i]->title_show);
- config->entries[i]->title_show = s;
- config->entries[i]->non_unique = FALSE;
- FreePool(m);
- }
-
- unique = TRUE;
- for (i = 0; i < config->entry_count; i++) {
- for (k = 0; k < config->entry_count; k++) {
- if (i == k)
- continue;
- if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0)
- continue;
-
- unique = FALSE;
- config->entries[i]->non_unique = TRUE;
- config->entries[k]->non_unique = TRUE;
- }
- }
- if (unique)
- return;
-
- /* add file name to non-unique titles */
- for (i = 0; i < config->entry_count; i++) {
- CHAR16 *s;
-
- if (!config->entries[i]->non_unique)
- continue;
- s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->file);
- FreePool(config->entries[i]->title_show);
- config->entries[i]->title_show = s;
- config->entries[i]->non_unique = FALSE;
- }
-}
-
-static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(VOID)) {
- ConfigEntry *entry;
-
- entry = AllocateZeroPool(sizeof(ConfigEntry));
- entry->title = StrDuplicate(title);
- entry->call = call;
- entry->no_autoselect = TRUE;
- config_add_entry(config, entry);
- return TRUE;
-}
-
-static ConfigEntry *config_entry_add_loader(Config *config, EFI_HANDLE *device,
- enum loader_type type,CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
- ConfigEntry *entry;
-
- entry = AllocateZeroPool(sizeof(ConfigEntry));
- entry->type = type;
- entry->title = StrDuplicate(title);
- entry->device = device;
- entry->loader = StrDuplicate(loader);
- entry->file = StrDuplicate(file);
- StrLwr(entry->file);
- entry->key = key;
- config_add_entry(config, entry);
-
- return entry;
-}
-
-static BOOLEAN config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
- CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) {
- EFI_FILE_HANDLE handle;
- ConfigEntry *entry;
- EFI_STATUS err;
-
- /* do not add an entry for ourselves */
- if (loaded_image_path && StriCmp(loader, loaded_image_path) == 0)
- return FALSE;
-
- /* check existence */
- err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0ULL);
- if (EFI_ERROR(err))
- return FALSE;
- uefi_call_wrapper(handle->Close, 1, handle);
-
- entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, file, key, title, loader);
- if (!entry)
- return FALSE;
-
- /* do not boot right away into auto-detected entries */
- entry->no_autoselect = TRUE;
-
- /* do not show a splash; they do not need one, or they draw their own */
- entry->splash = StrDuplicate(L"");
-
- /* export identifiers of automatically added entries */
- if (config->entries_auto) {
- CHAR16 *s;
-
- s = PoolPrint(L"%s %s", config->entries_auto, file);
- FreePool(config->entries_auto);
- config->entries_auto = s;
- } else
- config->entries_auto = StrDuplicate(file);
-
- return TRUE;
-}
-
-static VOID config_entry_add_osx(Config *config) {
- EFI_STATUS err;
- UINTN handle_count = 0;
- EFI_HANDLE *handles = NULL;
-
- err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handle_count, &handles);
- if (!EFI_ERROR(err)) {
- UINTN i;
-
- for (i = 0; i < handle_count; i++) {
- EFI_FILE *root;
- BOOLEAN found;
-
- root = LibOpenRoot(handles[i]);
- if (!root)
- continue;
- found = config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"OS X",
- L"\\System\\Library\\CoreServices\\boot.efi");
- uefi_call_wrapper(root->Close, 1, root);
- if (found)
- break;
- }
-
- FreePool(handles);
- }
-}
-
-static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_image, EFI_FILE *root_dir) {
- EFI_FILE_HANDLE linux_dir;
- EFI_STATUS err;
-
- err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL);
- if (!EFI_ERROR(err)) {
- for (;;) {
- CHAR16 buf[256];
- UINTN bufsize;
- EFI_FILE_INFO *f;
- CHAR8 *sections[] = {
- (UINT8 *)".osrel",
- NULL
- };
- UINTN offs[ELEMENTSOF(sections)-1] = {};
- UINTN szs[ELEMENTSOF(sections)-1] = {};
- UINTN addrs[ELEMENTSOF(sections)-1] = {};
- CHAR8 *content = NULL;
- UINTN len;
- CHAR8 *line;
- UINTN pos = 0;
- CHAR8 *key, *value;
- CHAR16 *os_name = NULL;
- CHAR16 *os_id = NULL;
- CHAR16 *os_version = NULL;
-
- bufsize = sizeof(buf);
- err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf);
- if (bufsize == 0 || EFI_ERROR(err))
- break;
-
- f = (EFI_FILE_INFO *) buf;
- if (f->FileName[0] == '.')
- continue;
- if (f->Attribute & EFI_FILE_DIRECTORY)
- continue;
- len = StrLen(f->FileName);
- if (len < 5)
- continue;
- if (StriCmp(f->FileName + len - 4, L".efi") != 0)
- continue;
-
- /* look for an .osrel section in the .efi binary */
- err = pefile_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
- if (EFI_ERROR(err))
- continue;
-
- len = file_read(linux_dir, f->FileName, offs[0], szs[0], &content);
- if (len <= 0)
- continue;
-
- /* read properties from the embedded os-release file */
- line = content;
- while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
- if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) {
- os_name = stra_to_str(value);
- continue;
- }
-
- if (strcmpa((CHAR8 *)"ID", key) == 0) {
- os_id = stra_to_str(value);
- continue;
- }
-
- if (strcmpa((CHAR8 *)"VERSION_ID", key) == 0) {
- os_version = stra_to_str(value);
- continue;
- }
- }
-
- if (os_name && os_id && os_version) {
- CHAR16 *conf;
- CHAR16 *path;
-
- conf = PoolPrint(L"%s-%s", os_id, os_version);
- path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
- config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path);
- FreePool(conf);
- FreePool(path);
- FreePool(os_name);
- FreePool(os_id);
- FreePool(os_version);
- }
-
- FreePool(content);
- }
- uefi_call_wrapper(linux_dir->Close, 1, linux_dir);
- }
-}
-
-static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) {
- EFI_HANDLE image;
- EFI_DEVICE_PATH *path;
- CHAR16 *options;
- EFI_STATUS err;
-
- path = FileDevicePath(entry->device, entry->loader);
- if (!path) {
- Print(L"Error getting device path.");
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- return EFI_INVALID_PARAMETER;
- }
-
- err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
- if (EFI_ERROR(err)) {
- Print(L"Error loading %s: %r", entry->loader, err);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- goto out;
- }
-
- if (config->options_edit)
- options = config->options_edit;
- else if (entry->options)
- options = entry->options;
- else
- options = NULL;
- if (options) {
- EFI_LOADED_IMAGE *loaded_image;
-
- err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
- parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
- if (EFI_ERROR(err)) {
- Print(L"Error getting LoadedImageProtocol handle: %r", err);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- goto out_unload;
- }
- loaded_image->LoadOptions = options;
- loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
- }
-
- efivar_set_time_usec(L"LoaderTimeExecUSec", 0);
- err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL);
-out_unload:
- uefi_call_wrapper(BS->UnloadImage, 1, image);
-out:
- FreePool(path);
- return err;
-}
-
-static EFI_STATUS reboot_into_firmware(VOID) {
- CHAR8 *b;
- UINTN size;
- UINT64 osind;
- EFI_STATUS err;
-
- osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
-
- err = efivar_get_raw(&global_guid, L"OsIndications", &b, &size);
- if (!EFI_ERROR(err))
- osind |= (UINT64)*b;
- FreePool(b);
-
- err = efivar_set_raw(&global_guid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE);
- if (EFI_ERROR(err))
- return err;
-
- err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
- Print(L"Error calling ResetSystem: %r", err);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- return err;
-}
-
-static VOID config_free(Config *config) {
- UINTN i;
-
- for (i = 0; i < config->entry_count; i++)
- config_entry_free(config->entries[i]);
- FreePool(config->entries);
- FreePool(config->entry_default_pattern);
- FreePool(config->options_edit);
- FreePool(config->entry_oneshot);
- FreePool(config->entries_auto);
- FreePool(config->splash);
- FreePool(config->background);
-}
-
-EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
- CHAR16 *s;
- CHAR8 *b;
- UINTN size;
- EFI_LOADED_IMAGE *loaded_image;
- EFI_FILE *root_dir;
- CHAR16 *loaded_image_path;
- EFI_DEVICE_PATH *device_path;
- EFI_STATUS err;
- Config config;
- UINT64 init_usec;
- BOOLEAN menu = FALSE;
-
- InitializeLib(image, sys_table);
- init_usec = time_usec();
- efivar_set_time_usec(L"LoaderTimeInitUSec", init_usec);
- efivar_set(L"LoaderInfo", L"sd-boot " VERSION, FALSE);
- s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
- efivar_set(L"LoaderFirmwareInfo", s, FALSE);
- FreePool(s);
- s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
- efivar_set(L"LoaderFirmwareType", s, FALSE);
- FreePool(s);
-
- 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;
- }
-
- /* export the device path this image is started from */
- device_path = DevicePathFromHandle(loaded_image->DeviceHandle);
- if (device_path) {
- CHAR16 *str;
- EFI_DEVICE_PATH *path, *paths;
-
- str = DevicePathToStr(device_path);
- efivar_set(L"LoaderDeviceIdentifier", str, FALSE);
- FreePool(str);
-
- paths = UnpackDevicePath(device_path);
- for (path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
- HARDDRIVE_DEVICE_PATH *drive;
- CHAR16 uuid[37];
-
- if (DevicePathType(path) != MEDIA_DEVICE_PATH)
- continue;
- if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
- continue;
- drive = (HARDDRIVE_DEVICE_PATH *)path;
- if (drive->SignatureType != SIGNATURE_TYPE_GUID)
- continue;
-
- GuidToString(uuid, (EFI_GUID *)&drive->Signature);
- efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
- break;
- }
- FreePool(paths);
- }
-
- 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;
- }
-
-
- /* the filesystem path to this image, to prevent adding ourselves to the menu */
- loaded_image_path = DevicePathToStr(loaded_image->FilePath);
- efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE);
-
- /* scan "\loader\entries\*.conf" files */
- ZeroMem(&config, sizeof(Config));
- config_load(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
-
- if (!config.background) {
- config.background = AllocateZeroPool(sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
- if (StriCmp(L"Apple", ST->FirmwareVendor) == 0) {
- config.background->Red = 0xc0;
- config.background->Green = 0xc0;
- config.background->Blue = 0xc0;
- }
- }
-
- /* if we find some well-known loaders, add them to the end of the list */
- config_entry_add_linux(&config, loaded_image, root_dir);
- config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
- L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
- config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
- L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
- config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
- L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME ".efi");
- config_entry_add_osx(&config);
- efivar_set(L"LoaderEntriesAuto", config.entries_auto, FALSE);
-
- if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) {
- UINT64 osind = (UINT64)*b;
-
- if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
- config_entry_add_call(&config, L"Reboot Into Firmware Interface", reboot_into_firmware);
- FreePool(b);
- }
-
- if (config.entry_count == 0) {
- Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- goto out;
- }
-
- config_title_generate(&config);
-
- /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/
- config_default_entry_select(&config);
-
- /* if no configured entry to select from was found, enable the menu */
- if (config.idx_default == -1) {
- config.idx_default = 0;
- if (config.timeout_sec == 0)
- config.timeout_sec = 10;
- }
-
- /* select entry or show menu when key is pressed or timeout is set */
- if (config.timeout_sec == 0) {
- UINT64 key;
-
- err = console_key_read(&key, FALSE);
- if (!EFI_ERROR(err)) {
- INT16 idx;
-
- /* find matching key in config entries */
- idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key));
- if (idx >= 0)
- config.idx_default = idx;
- else
- menu = TRUE;
- }
- } else
- menu = TRUE;
-
- for (;;) {
- ConfigEntry *entry;
-
- entry = config.entries[config.idx_default];
- if (menu) {
- efivar_set_time_usec(L"LoaderTimeMenuUSec", 0);
- uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x10000, 0, NULL);
- if (!menu_run(&config, &entry, root_dir, loaded_image_path))
- break;
-
- /* run special entry like "reboot" */
- if (entry->call) {
- entry->call();
- continue;
- }
- } else {
- err = EFI_NOT_FOUND;
-
- /* splash from entry file */
- if (entry->splash) {
- /* some entries disable the splash because they draw their own */
- if (entry->splash[0] == '\0')
- err = EFI_SUCCESS;
- else
- err = graphics_splash(root_dir, entry->splash, config.background);
- }
-
- /* splash from config file */
- if (EFI_ERROR(err) && config.splash)
- err = graphics_splash(root_dir, config.splash, config.background);
-
- /* default splash */
- if (EFI_ERROR(err))
- graphics_splash(root_dir, L"\\EFI\\systemd\\splash.bmp", config.background);
- }
-
- /* export the selected boot entry to the system */
- efivar_set(L"LoaderEntrySelected", entry->file, FALSE);
-
- uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
- err = image_start(image, &config, entry);
-
- if (err == EFI_ACCESS_DENIED || err == EFI_SECURITY_VIOLATION) {
- /* Platform is secure boot and requested image isn't
- * trusted. Need to go back to prior boot system and
- * install more keys or hashes. Signal failure by
- * returning the error */
- Print(L"\nImage %s gives a security error\n", entry->title);
- Print(L"Please enrol the hash or signature of %s\n", entry->loader);
- uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
- goto out;
- }
-
- menu = TRUE;
- config.timeout_sec = 0;
- }
- err = EFI_SUCCESS;
-out:
- FreePool(loaded_image_path);
- config_free(&config);
- uefi_call_wrapper(root_dir->Close, 1, root_dir);
- uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);
- return err;
-}
diff --git a/src/sd-boot/stub.c b/src/sd-boot/stub.c
deleted file mode 100644
index e18faac..0000000
--- a/src/sd-boot/stub.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*-*- 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 at 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;
-}
diff --git a/src/sd-boot/util.c b/src/sd-boot/util.c
deleted file mode 100644
index 5678b50..0000000
--- a/src/sd-boot/util.c
+++ /dev/null
@@ -1,322 +0,0 @@
-/*-*- 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 at vrfy.org>
- * Copyright (C) 2012 Harald Hoyer <harald at 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;
-}
diff --git a/src/sd-boot/util.h b/src/sd-boot/util.h
deleted file mode 100644
index efaafd7..0000000
--- a/src/sd-boot/util.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*-*- 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 at vrfy.org>
- * Copyright (C) 2012 Harald Hoyer <harald at 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
diff --git a/test/test-efi-create-disk.sh b/test/test-efi-create-disk.sh
index 07595b7..4f953ac 100755
--- a/test/test-efi-create-disk.sh
+++ b/test/test-efi-create-disk.sh
@@ -12,7 +12,7 @@ mkdir -p mnt
mount ${LOOP}p1 mnt
mkdir -p mnt/EFI/{Boot,systemd}
-cp sd-bootx64.efi mnt/EFI/Boot/bootx64.efi
+cp systemd-bootx64.efi mnt/EFI/Boot/bootx64.efi
cp test/splash.bmp mnt/EFI/systemd/
[ -e /boot/shellx64.efi ] && cp /boot/shellx64.efi mnt/
commit 8d4efa540a88b19a475452d8ccc3c9824849fa86
Author: Harald Hoyer <harald at redhat.com>
Date: Wed Feb 18 14:45:12 2015 +0100
Makefile.am:efi_cflags compile with gcc v5
Set -std=gnu90 to let efibind.h define the standard types.
[tomegun: retyped the patch as the originl would not apply]
diff --git a/Makefile.am b/Makefile.am
index 51c6d33..a5e07cf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2533,7 +2533,8 @@ if ARCH_X86_64
efi_cflags += \
-mno-red-zone \
-DEFI_FUNCTION_WRAPPER \
- -DGNU_EFI_USE_MS_ABI
+ -DGNU_EFI_USE_MS_ABI \
+ -std=gnu90
endif
efi_ldflags = \
More information about the systemd-commits
mailing list