mirror of
https://github.com/systemd/systemd.git
synced 2025-02-23 13:57:33 +03:00
bootctl: add sd-boot support
This commit is contained in:
parent
0fa2cac4f0
commit
0974a682d1
18
Makefile.am
18
Makefile.am
@ -2479,15 +2479,21 @@ systemd_efi_boot_generator_LDADD = \
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
bootctl_SOURCES = \
|
||||
src/boot/boot.h \
|
||||
src/boot/boot-loader.h \
|
||||
src/boot/bootctl.c \
|
||||
src/boot/boot-loader.c \
|
||||
src/boot/boot-efi.c
|
||||
src/boot/bootctl.c
|
||||
|
||||
bootctl_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
-DEFI_MACHINE_TYPE_NAME=\"$(EFI_MACHINE_TYPE_NAME)\" \
|
||||
-DSD_BOOTLIBDIR=\"$(sd_bootlibdir)\"
|
||||
|
||||
bootctl_CFLAGS = \
|
||||
$(AM_CFLAGS) \
|
||||
$(BLKID_CFLAGS)
|
||||
|
||||
bootctl_LDADD = \
|
||||
libsystemd-shared.la \
|
||||
libsystemd-internal.la
|
||||
libsystemd-internal.la \
|
||||
$(BLKID_LIBS)
|
||||
|
||||
bin_PROGRAMS += \
|
||||
bootctl
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
<refentry id="bootctl" conditional='ENABLE_EFI'
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>bootctl</title>
|
||||
<productname>systemd</productname>
|
||||
@ -48,65 +47,82 @@
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>bootctl</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="req">COMMAND</arg>
|
||||
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg>status</command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg>update</command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg>install</command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg>remove</command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>bootctl</command> may be used to query or (in the
|
||||
future) change the firmware and boot manager settings.</para>
|
||||
<para><command>bootctl</command> checks, updates,
|
||||
installs or removes the boot loader from the current
|
||||
system.</para>
|
||||
|
||||
<para>Firmware information is available only on EFI systems.
|
||||
</para>
|
||||
<para><command>bootctl status</command> checks and prints the
|
||||
currently installed versions of the boot loader binaries and the
|
||||
all current EFI boot variables.</para>
|
||||
|
||||
<para>Currently, only the
|
||||
<citerefentry project='gummiboot'><refentrytitle>gummiboot</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
boot manager implements the required boot loader interface to
|
||||
provide complete boot manager information.</para>
|
||||
<para><command>bootctl update</command> updates all installed
|
||||
versions of sd-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 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
|
||||
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>
|
||||
|
||||
<para>If no command is passed <command>status</command> is
|
||||
implied.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
|
||||
<para>The following options are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
</variablelist>
|
||||
|
||||
<para>The following commands are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><command>status</command></term>
|
||||
<term><option>--path</option></term>
|
||||
<listitem><para>Path to the EFI system partition. The default is /boot.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<listitem><para>Show firmware and boot manager information
|
||||
about the system, including secure boot mode status and
|
||||
selected firmware entry (where available).</para></listitem>
|
||||
<varlistentry>
|
||||
<term><option>--no-variables</option></term>
|
||||
<listitem><para>Do not touch the EFI boot variables.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Exit status</title>
|
||||
|
||||
<para>On success, 0 is returned, a non-zero failure code
|
||||
otherwise.</para>
|
||||
<para>On success 0 is returned, a non-zero failure
|
||||
code otherwise.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<ulink url="http://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface">Boot loader interface</ulink>,
|
||||
<ulink url="http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec">Boot loader specification</ulink>,
|
||||
<ulink url="http://www.freedesktop.org/wiki/Software/gummiboot/">gummiboot</ulink>
|
||||
<ulink url="http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec">Boot loader specification</ulink>
|
||||
<ulink url="http://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface">Systemd boot loader interface</ulink>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
|
@ -87,10 +87,6 @@
|
||||
</a>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="citerefentry[@project='gummiboot']">
|
||||
<xsl:call-template name="inline.charseq"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="refsect1/title|refsect1/info/title">
|
||||
<!-- the ID is output in the block.object call for refsect1 -->
|
||||
<h2>
|
||||
|
@ -80,7 +80,6 @@
|
||||
<citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.automount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>gummiboot</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
@ -1,190 +0,0 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Kay Sievers
|
||||
|
||||
systemd 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.
|
||||
|
||||
systemd 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.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
#include <fnmatch.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/timex.h>
|
||||
|
||||
#include "boot.h"
|
||||
#include "boot-loader.h"
|
||||
#include "build.h"
|
||||
#include "util.h"
|
||||
#include "strv.h"
|
||||
#include "efivars.h"
|
||||
#include "conf-files.h"
|
||||
|
||||
static char *tilt_slashes(char *s) {
|
||||
char *p;
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
for (p = s; *p; p++)
|
||||
if (*p == '\\')
|
||||
*p = '/';
|
||||
return s;
|
||||
}
|
||||
|
||||
static int get_boot_entries(struct boot_info *info) {
|
||||
uint16_t *list = NULL;
|
||||
int i, n;
|
||||
int err = 0;
|
||||
|
||||
n = efi_get_boot_options(&list);
|
||||
if (n < 0)
|
||||
return n;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
struct boot_info_entry *e;
|
||||
|
||||
e = realloc(info->fw_entries, (info->fw_entries_count+1) * sizeof(struct boot_info_entry));
|
||||
if (!e) {
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
info->fw_entries = e;
|
||||
|
||||
e = &info->fw_entries[info->fw_entries_count];
|
||||
memzero(e, sizeof(struct boot_info_entry));
|
||||
e->order = -1;
|
||||
|
||||
err = efi_get_boot_option(list[i], &e->title, &e->part_uuid, &e->path);
|
||||
if (err < 0)
|
||||
continue;
|
||||
|
||||
if (isempty(e->title)) {
|
||||
free(e->title);
|
||||
e->title = NULL;
|
||||
}
|
||||
tilt_slashes(e->path);
|
||||
|
||||
e->id = list[i];
|
||||
info->fw_entries_count++;
|
||||
}
|
||||
|
||||
free(list);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int find_active_entry(struct boot_info *info) {
|
||||
uint16_t boot_cur;
|
||||
void *buf;
|
||||
size_t l;
|
||||
size_t i;
|
||||
int err;
|
||||
|
||||
err = efi_get_variable(EFI_VENDOR_GLOBAL, "BootCurrent", NULL, &buf, &l);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
memcpy(&boot_cur, buf, sizeof(uint16_t));
|
||||
for (i = 0; i < info->fw_entries_count; i++) {
|
||||
if (info->fw_entries[i].id != boot_cur)
|
||||
continue;
|
||||
info->fw_entry_active = i;
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
free(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int get_boot_order(struct boot_info *info) {
|
||||
size_t i, k;
|
||||
int r;
|
||||
|
||||
r = efi_get_boot_order(&info->fw_entries_order);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
info->fw_entries_order_count = r;
|
||||
|
||||
for (i = 0; i < info->fw_entries_order_count; i++) {
|
||||
for (k = 0; k < info->fw_entries_count; k++) {
|
||||
if (info->fw_entries[k].id != info->fw_entries_order[i])
|
||||
continue;
|
||||
info->fw_entries[k].order = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_cmp(const void *a, const void *b) {
|
||||
const struct boot_info_entry *e1 = a;
|
||||
const struct boot_info_entry *e2 = b;
|
||||
|
||||
/* boot order of active entries */
|
||||
if (e1->order > 0 && e2->order > 0)
|
||||
return e1->order - e2->order;
|
||||
|
||||
/* sort active entries before inactive ones */
|
||||
if (e1->order > 0)
|
||||
return 1;
|
||||
if (e2->order > 0)
|
||||
return -1;
|
||||
|
||||
/* order of inactive entries */
|
||||
return e1->id - e2->id;
|
||||
}
|
||||
|
||||
int boot_info_query(struct boot_info *info) {
|
||||
char str[64];
|
||||
char buf[64];
|
||||
char *loader_active = NULL;
|
||||
|
||||
info->fw_secure_boot = is_efi_secure_boot();
|
||||
info->fw_secure_boot_setup_mode = is_efi_secure_boot_setup_mode();
|
||||
|
||||
efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderInfo", &info->loader);
|
||||
|
||||
get_boot_entries(info);
|
||||
if (info->fw_entries_count > 0) {
|
||||
get_boot_order(info);
|
||||
qsort(info->fw_entries, info->fw_entries_count, sizeof(struct boot_info_entry), entry_cmp);
|
||||
find_active_entry(info);
|
||||
}
|
||||
|
||||
efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareType", &info->fw_type);
|
||||
efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareInfo", &info->fw_info);
|
||||
efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderImageIdentifier", &info->loader_image_path);
|
||||
tilt_slashes(info->loader_image_path);
|
||||
efi_loader_get_device_part_uuid(&info->loader_part_uuid);
|
||||
|
||||
boot_loader_read_entries(info);
|
||||
efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntrySelected", &loader_active);
|
||||
if (loader_active) {
|
||||
boot_loader_find_active_entry(info, loader_active);
|
||||
free(loader_active);
|
||||
}
|
||||
|
||||
snprintf(str, sizeof(str), "LoaderEntryOptions-%s", sd_id128_to_string(info->machine_id, buf));
|
||||
efi_get_variable_string(EFI_VENDOR_LOADER, str, &info->loader_options_added);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Kay Sievers
|
||||
|
||||
systemd 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.
|
||||
|
||||
systemd 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.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/timex.h>
|
||||
|
||||
#include "boot.h"
|
||||
#include "boot-loader.h"
|
||||
#include "build.h"
|
||||
#include "util.h"
|
||||
#include "strv.h"
|
||||
#include "conf-files.h"
|
||||
|
||||
static char *loader_fragment_read_title(const char *fragment) {
|
||||
FILE *f;
|
||||
char line[LINE_MAX];
|
||||
char *title = NULL;
|
||||
|
||||
f = fopen(fragment, "re");
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
while (fgets(line, sizeof(line), f) != NULL) {
|
||||
char *s;
|
||||
size_t l;
|
||||
|
||||
l = strlen(line);
|
||||
if (l < 1)
|
||||
continue;
|
||||
if (line[l-1] == '\n')
|
||||
line[l-1] = '\0';
|
||||
|
||||
s = line;
|
||||
while (isspace(s[0]))
|
||||
s++;
|
||||
|
||||
if (s[0] == '#')
|
||||
continue;
|
||||
|
||||
if (!startswith(s, "title"))
|
||||
continue;
|
||||
|
||||
s += strlen("title");
|
||||
if (!isspace(s[0]))
|
||||
continue;
|
||||
while (isspace(s[0]))
|
||||
s++;
|
||||
|
||||
title = strdup(s);
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return title;
|
||||
}
|
||||
|
||||
int boot_loader_read_entries(struct boot_info *info) {
|
||||
_cleanup_strv_free_ char **files = NULL;
|
||||
static const char *loader_dir[] = { "/boot/loader/entries", NULL};
|
||||
unsigned int count;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
err = conf_files_list_strv(&files, ".conf", NULL, loader_dir);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
count = strv_length(files);
|
||||
info->loader_entries = new0(struct boot_info_entry, count);
|
||||
if (!info->loader_entries)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
info->loader_entries[i].title = loader_fragment_read_title(files[i]);
|
||||
info->loader_entries[i].path = strdup(files[i]);
|
||||
if (!info->loader_entries[i].title || !info->loader_entries[i].path) {
|
||||
free(info->loader_entries[i].title);
|
||||
free(info->loader_entries[i].path);
|
||||
return -ENOMEM;
|
||||
}
|
||||
info->loader_entries_count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int boot_loader_find_active_entry(struct boot_info *info, const char *loader_active) {
|
||||
char *fn;
|
||||
unsigned int i;
|
||||
|
||||
if (!loader_active)
|
||||
return -ENOENT;
|
||||
if (info->loader_entries_count == 0)
|
||||
return -ENOENT;
|
||||
|
||||
if (asprintf(&fn, "/boot/loader/entries/%s.conf", loader_active) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < info->loader_entries_count; i++) {
|
||||
if (streq(fn, info->loader_entries[i].path)) {
|
||||
info->loader_entry_active = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(fn);
|
||||
return 0;
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Kay Sievers
|
||||
|
||||
systemd 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.
|
||||
|
||||
systemd 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.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "boot.h"
|
||||
|
||||
int boot_loader_read_entries(struct boot_info *info);
|
||||
int boot_loader_find_active_entry(struct boot_info *info, const char *loader_active);
|
@ -1,64 +0,0 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Kay Sievers
|
||||
|
||||
systemd 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.
|
||||
|
||||
systemd 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.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
/*
|
||||
* Firmware and boot manager information to be filled in
|
||||
* by the platform.
|
||||
*
|
||||
* This is partly EFI specific, if you add things, keep this
|
||||
* as generic as possible to be able to re-use it on other
|
||||
* platforms.
|
||||
*/
|
||||
|
||||
struct boot_info_entry {
|
||||
uint16_t id;
|
||||
uint16_t order;
|
||||
char *title;
|
||||
sd_id128_t part_uuid;
|
||||
char *path;
|
||||
};
|
||||
|
||||
struct boot_info {
|
||||
sd_id128_t machine_id;
|
||||
sd_id128_t boot_id;
|
||||
char *fw_type;
|
||||
char *fw_info;
|
||||
int fw_secure_boot;
|
||||
int fw_secure_boot_setup_mode;
|
||||
struct boot_info_entry *fw_entries;
|
||||
size_t fw_entries_count;
|
||||
uint16_t *fw_entries_order;
|
||||
size_t fw_entries_order_count;
|
||||
ssize_t fw_entry_active;
|
||||
char *loader;
|
||||
char *loader_image_path;
|
||||
sd_id128_t loader_part_uuid;
|
||||
struct boot_info_entry *loader_entries;
|
||||
size_t loader_entries_count;
|
||||
ssize_t loader_entry_active;
|
||||
char *loader_options_added;
|
||||
};
|
||||
|
||||
int boot_info_query(struct boot_info *info);
|
1506
src/boot/bootctl.c
1506
src/boot/bootctl.c
File diff suppressed because it is too large
Load Diff
@ -31,6 +31,40 @@
|
||||
|
||||
#ifdef ENABLE_EFI
|
||||
|
||||
#define LOAD_OPTION_ACTIVE 0x00000001
|
||||
#define MEDIA_DEVICE_PATH 0x04
|
||||
#define MEDIA_HARDDRIVE_DP 0x01
|
||||
#define MEDIA_FILEPATH_DP 0x04
|
||||
#define SIGNATURE_TYPE_GUID 0x02
|
||||
#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02
|
||||
#define END_DEVICE_PATH_TYPE 0x7f
|
||||
#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff
|
||||
|
||||
struct boot_option {
|
||||
uint32_t attr;
|
||||
uint16_t path_len;
|
||||
uint16_t title[];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct drive_path {
|
||||
uint32_t part_nr;
|
||||
uint64_t part_start;
|
||||
uint64_t part_size;
|
||||
char signature[16];
|
||||
uint8_t mbr_type;
|
||||
uint8_t signature_type;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct device_path {
|
||||
uint8_t type;
|
||||
uint8_t sub_type;
|
||||
uint16_t length;
|
||||
union {
|
||||
uint16_t path[0];
|
||||
struct drive_path drive;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
bool is_efi_boot(void) {
|
||||
return access("/sys/firmware/efi", F_OK) >= 0;
|
||||
}
|
||||
@ -128,6 +162,66 @@ int efi_get_variable(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int efi_set_variable(
|
||||
sd_id128_t vendor,
|
||||
const char *name,
|
||||
const void *value,
|
||||
size_t size) {
|
||||
|
||||
struct var {
|
||||
uint32_t attr;
|
||||
char buf[];
|
||||
} __attribute__((packed)) *buf = NULL;
|
||||
char *p = NULL;
|
||||
int fd = -1;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
if (asprintf(&p,
|
||||
"/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
name, SD_ID128_FORMAT_VAL(vendor)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if (size == 0) {
|
||||
r = unlink(p);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
buf = malloc(sizeof(uint32_t) + size);
|
||||
if (!buf) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
|
||||
memcpy(buf->buf, value, size);
|
||||
|
||||
r = write(fd, buf, sizeof(uint32_t) + size);
|
||||
if (r < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((size_t)r != sizeof(uint32_t) + size) {
|
||||
r = -EIO;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
finish:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
free(buf);
|
||||
free(p);
|
||||
return r;
|
||||
}
|
||||
|
||||
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
|
||||
_cleanup_free_ void *s = NULL;
|
||||
size_t ss = 0;
|
||||
@ -179,8 +273,8 @@ int efi_get_boot_option(
|
||||
uint16_t id,
|
||||
char **title,
|
||||
sd_id128_t *part_uuid,
|
||||
char **path) {
|
||||
|
||||
char **path,
|
||||
bool *active) {
|
||||
struct boot_option {
|
||||
uint32_t attr;
|
||||
uint16_t path_len;
|
||||
@ -250,23 +344,23 @@ int efi_get_boot_option(
|
||||
break;
|
||||
|
||||
/* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
|
||||
if (dpath->type == 0x7f && dpath->sub_type == 0xff)
|
||||
if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE)
|
||||
break;
|
||||
|
||||
dnext += dpath->length;
|
||||
|
||||
/* Type 0x04 – Media Device Path */
|
||||
if (dpath->type != 0x04)
|
||||
if (dpath->type != MEDIA_DEVICE_PATH)
|
||||
continue;
|
||||
|
||||
/* Sub-Type 1 – Hard Drive */
|
||||
if (dpath->sub_type == 0x01) {
|
||||
if (dpath->sub_type == MEDIA_HARDDRIVE_DP) {
|
||||
/* 0x02 – GUID Partition Table */
|
||||
if (dpath->drive.mbr_type != 0x02)
|
||||
if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER)
|
||||
continue;
|
||||
|
||||
/* 0x02 – GUID signature */
|
||||
if (dpath->drive.signature_type != 0x02)
|
||||
if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID)
|
||||
continue;
|
||||
|
||||
if (part_uuid)
|
||||
@ -275,8 +369,9 @@ int efi_get_boot_option(
|
||||
}
|
||||
|
||||
/* Sub-Type 4 – File Path */
|
||||
if (dpath->sub_type == 0x04 && !p && path) {
|
||||
if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) {
|
||||
p = utf16_to_utf8(dpath->path, dpath->length-4);
|
||||
efi_tilt_backslashes(p);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -288,6 +383,8 @@ int efi_get_boot_option(
|
||||
*part_uuid = p_uuid;
|
||||
if (path)
|
||||
*path = p;
|
||||
if (active)
|
||||
*active = !!header->attr & LOAD_OPTION_ACTIVE;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
@ -296,6 +393,126 @@ err:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void to_utf16(uint16_t *dest, const char *src) {
|
||||
int i;
|
||||
|
||||
for (i = 0; src[i] != '\0'; i++)
|
||||
dest[i] = src[i];
|
||||
dest[i] = '\0';
|
||||
}
|
||||
|
||||
struct guid {
|
||||
uint32_t u1;
|
||||
uint16_t u2;
|
||||
uint16_t u3;
|
||||
uint8_t u4[8];
|
||||
} __attribute__((packed));
|
||||
|
||||
static void id128_to_efi_guid(sd_id128_t id, void *guid) {
|
||||
struct guid *uuid = guid;
|
||||
|
||||
uuid->u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3];
|
||||
uuid->u2 = id.bytes[4] << 8 | id.bytes[5];
|
||||
uuid->u3 = id.bytes[6] << 8 | id.bytes[7];
|
||||
memcpy(uuid->u4, id.bytes+8, sizeof(uuid->u4));
|
||||
}
|
||||
|
||||
static uint16_t *tilt_slashes(uint16_t *s) {
|
||||
uint16_t *p;
|
||||
|
||||
for (p = s; *p; p++)
|
||||
if (*p == '/')
|
||||
*p = '\\';
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
char *efi_tilt_backslashes(char *s) {
|
||||
char *p;
|
||||
|
||||
for (p = s; *p; p++)
|
||||
if (*p == '\\')
|
||||
*p = '/';
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int efi_add_boot_option(uint16_t id, const char *title,
|
||||
uint32_t part, uint64_t pstart, uint64_t psize,
|
||||
sd_id128_t part_uuid, const char *path) {
|
||||
char boot_id[9];
|
||||
char *buf;
|
||||
size_t size;
|
||||
size_t title_len;
|
||||
size_t path_len;
|
||||
struct boot_option *option;
|
||||
struct device_path *devicep;
|
||||
int err;
|
||||
|
||||
title_len = (strlen(title)+1) * 2;
|
||||
path_len = (strlen(path)+1) * 2;
|
||||
|
||||
buf = calloc(sizeof(struct boot_option) + title_len +
|
||||
sizeof(struct drive_path) +
|
||||
sizeof(struct device_path) + path_len, 1);
|
||||
if (!buf) {
|
||||
err = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* header */
|
||||
option = (struct boot_option *)buf;
|
||||
option->attr = LOAD_OPTION_ACTIVE;
|
||||
option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) +
|
||||
offsetof(struct device_path, path) + path_len +
|
||||
offsetof(struct device_path, path);
|
||||
to_utf16(option->title, title);
|
||||
size = offsetof(struct boot_option, title) + title_len;
|
||||
|
||||
/* partition info */
|
||||
devicep = (struct device_path *)(buf + size);
|
||||
devicep->type = MEDIA_DEVICE_PATH;
|
||||
devicep->sub_type = MEDIA_HARDDRIVE_DP;
|
||||
devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path);
|
||||
devicep->drive.part_nr = part;
|
||||
devicep->drive.part_start = pstart;
|
||||
devicep->drive.part_size = psize;
|
||||
devicep->drive.signature_type = SIGNATURE_TYPE_GUID;
|
||||
devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
|
||||
id128_to_efi_guid(part_uuid, devicep->drive.signature);
|
||||
size += devicep->length;
|
||||
|
||||
/* path to loader */
|
||||
devicep = (struct device_path *)(buf + size);
|
||||
devicep->type = MEDIA_DEVICE_PATH;
|
||||
devicep->sub_type = MEDIA_FILEPATH_DP;
|
||||
devicep->length = offsetof(struct device_path, path) + path_len;
|
||||
to_utf16(devicep->path, path);
|
||||
tilt_slashes(devicep->path);
|
||||
size += devicep->length;
|
||||
|
||||
/* end of path */
|
||||
devicep = (struct device_path *)(buf + size);
|
||||
devicep->type = END_DEVICE_PATH_TYPE;
|
||||
devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE;
|
||||
devicep->length = offsetof(struct device_path, path);
|
||||
size += devicep->length;
|
||||
|
||||
snprintf(boot_id, sizeof(boot_id), "Boot%04X", id);
|
||||
err = efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, buf, size);
|
||||
|
||||
finish:
|
||||
free(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
int efi_remove_boot_option(uint16_t id) {
|
||||
char boot_id[9];
|
||||
|
||||
snprintf(boot_id, sizeof(boot_id), "Boot%04X", id);
|
||||
return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0);
|
||||
}
|
||||
|
||||
int efi_get_boot_order(uint16_t **order) {
|
||||
void *buf;
|
||||
size_t l;
|
||||
@ -320,6 +537,10 @@ int efi_get_boot_order(uint16_t **order) {
|
||||
return (int) (l / sizeof(uint16_t));
|
||||
}
|
||||
|
||||
int efi_set_boot_order(uint16_t *order, size_t n) {
|
||||
return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
static int boot_id_hex(const char s[4]) {
|
||||
int i;
|
||||
int id = 0;
|
||||
|
@ -30,17 +30,26 @@
|
||||
|
||||
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
|
||||
#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
|
||||
#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
|
||||
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
|
||||
#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
|
||||
|
||||
bool is_efi_boot(void);
|
||||
int is_efi_secure_boot(void);
|
||||
int is_efi_secure_boot_setup_mode(void);
|
||||
|
||||
int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
|
||||
int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size);
|
||||
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p);
|
||||
|
||||
int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *partuuid, char **path);
|
||||
int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active);
|
||||
int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path);
|
||||
int efi_remove_boot_option(uint16_t id);
|
||||
int efi_get_boot_order(uint16_t **order);
|
||||
int efi_set_boot_order(uint16_t *order, size_t n);
|
||||
int efi_get_boot_options(uint16_t **options);
|
||||
|
||||
int efi_loader_get_device_part_uuid(sd_id128_t *u);
|
||||
int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader);
|
||||
|
||||
char *efi_tilt_backslashes(char *s);
|
||||
|
Loading…
x
Reference in New Issue
Block a user