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 = \
|
bootctl_SOURCES = \
|
||||||
src/boot/boot.h \
|
src/boot/bootctl.c
|
||||||
src/boot/boot-loader.h \
|
|
||||||
src/boot/bootctl.c \
|
bootctl_CPPFLAGS = \
|
||||||
src/boot/boot-loader.c \
|
$(AM_CPPFLAGS) \
|
||||||
src/boot/boot-efi.c
|
-DEFI_MACHINE_TYPE_NAME=\"$(EFI_MACHINE_TYPE_NAME)\" \
|
||||||
|
-DSD_BOOTLIBDIR=\"$(sd_bootlibdir)\"
|
||||||
|
|
||||||
|
bootctl_CFLAGS = \
|
||||||
|
$(AM_CFLAGS) \
|
||||||
|
$(BLKID_CFLAGS)
|
||||||
|
|
||||||
bootctl_LDADD = \
|
bootctl_LDADD = \
|
||||||
libsystemd-shared.la \
|
libsystemd-shared.la \
|
||||||
libsystemd-internal.la
|
libsystemd-internal.la \
|
||||||
|
$(BLKID_LIBS)
|
||||||
|
|
||||||
bin_PROGRAMS += \
|
bin_PROGRAMS += \
|
||||||
bootctl
|
bootctl
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
<refentry id="bootctl" conditional='ENABLE_EFI'
|
<refentry id="bootctl" conditional='ENABLE_EFI'
|
||||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||||
|
|
||||||
<refentryinfo>
|
<refentryinfo>
|
||||||
<title>bootctl</title>
|
<title>bootctl</title>
|
||||||
<productname>systemd</productname>
|
<productname>systemd</productname>
|
||||||
@ -48,65 +47,82 @@
|
|||||||
|
|
||||||
<refsynopsisdiv>
|
<refsynopsisdiv>
|
||||||
<cmdsynopsis>
|
<cmdsynopsis>
|
||||||
<command>bootctl</command>
|
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg>status</command>
|
||||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
</cmdsynopsis>
|
||||||
<arg choice="req">COMMAND</arg>
|
<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>
|
</cmdsynopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>Description</title>
|
<title>Description</title>
|
||||||
|
|
||||||
<para><command>bootctl</command> may be used to query or (in the
|
<para><command>bootctl</command> checks, updates,
|
||||||
future) change the firmware and boot manager settings.</para>
|
installs or removes the boot loader from the current
|
||||||
|
system.</para>
|
||||||
|
|
||||||
<para>Firmware information is available only on EFI systems.
|
<para><command>bootctl status</command> checks and prints the
|
||||||
</para>
|
currently installed versions of the boot loader binaries and the
|
||||||
|
all current EFI boot variables.</para>
|
||||||
|
|
||||||
<para>Currently, only the
|
<para><command>bootctl update</command> updates all installed
|
||||||
<citerefentry project='gummiboot'><refentrytitle>gummiboot</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
versions of sd-boot, if the current version is newer than the
|
||||||
boot manager implements the required boot loader interface to
|
version installed in the EFI system partition. This also includes
|
||||||
provide complete boot manager information.</para>
|
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>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>Options</title>
|
<title>Options</title>
|
||||||
|
|
||||||
<para>The following options are understood:</para>
|
<para>The following options are understood:</para>
|
||||||
|
|
||||||
<variablelist>
|
<variablelist>
|
||||||
<xi:include href="standard-options.xml" xpointer="help" />
|
<xi:include href="standard-options.xml" xpointer="help" />
|
||||||
<xi:include href="standard-options.xml" xpointer="version" />
|
<xi:include href="standard-options.xml" xpointer="version" />
|
||||||
</variablelist>
|
|
||||||
|
|
||||||
<para>The following commands are understood:</para>
|
|
||||||
|
|
||||||
<variablelist>
|
|
||||||
<varlistentry>
|
<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
|
<varlistentry>
|
||||||
about the system, including secure boot mode status and
|
<term><option>--no-variables</option></term>
|
||||||
selected firmware entry (where available).</para></listitem>
|
<listitem><para>Do not touch the EFI boot variables.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>Exit status</title>
|
<title>Exit status</title>
|
||||||
|
<para>On success 0 is returned, a non-zero failure
|
||||||
<para>On success, 0 is returned, a non-zero failure code
|
code otherwise.</para>
|
||||||
otherwise.</para>
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>See Also</title>
|
<title>See Also</title>
|
||||||
<para>
|
<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/Specifications/BootLoaderSpec">Boot loader specification</ulink>,
|
<ulink url="http://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface">Systemd boot loader interface</ulink>
|
||||||
<ulink url="http://www.freedesktop.org/wiki/Software/gummiboot/">gummiboot</ulink>
|
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
</refentry>
|
</refentry>
|
||||||
|
@ -87,10 +87,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<xsl:template match="citerefentry[@project='gummiboot']">
|
|
||||||
<xsl:call-template name="inline.charseq"/>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="refsect1/title|refsect1/info/title">
|
<xsl:template match="refsect1/title|refsect1/info/title">
|
||||||
<!-- the ID is output in the block.object call for refsect1 -->
|
<!-- the ID is output in the block.object call for refsect1 -->
|
||||||
<h2>
|
<h2>
|
||||||
|
@ -80,7 +80,6 @@
|
|||||||
<citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>systemd.automount</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>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>
|
<citerefentry><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</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
|
#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) {
|
bool is_efi_boot(void) {
|
||||||
return access("/sys/firmware/efi", F_OK) >= 0;
|
return access("/sys/firmware/efi", F_OK) >= 0;
|
||||||
}
|
}
|
||||||
@ -128,6 +162,66 @@ int efi_get_variable(
|
|||||||
return 0;
|
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) {
|
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
|
||||||
_cleanup_free_ void *s = NULL;
|
_cleanup_free_ void *s = NULL;
|
||||||
size_t ss = 0;
|
size_t ss = 0;
|
||||||
@ -179,8 +273,8 @@ int efi_get_boot_option(
|
|||||||
uint16_t id,
|
uint16_t id,
|
||||||
char **title,
|
char **title,
|
||||||
sd_id128_t *part_uuid,
|
sd_id128_t *part_uuid,
|
||||||
char **path) {
|
char **path,
|
||||||
|
bool *active) {
|
||||||
struct boot_option {
|
struct boot_option {
|
||||||
uint32_t attr;
|
uint32_t attr;
|
||||||
uint16_t path_len;
|
uint16_t path_len;
|
||||||
@ -250,23 +344,23 @@ int efi_get_boot_option(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
/* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
|
/* 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;
|
break;
|
||||||
|
|
||||||
dnext += dpath->length;
|
dnext += dpath->length;
|
||||||
|
|
||||||
/* Type 0x04 – Media Device Path */
|
/* Type 0x04 – Media Device Path */
|
||||||
if (dpath->type != 0x04)
|
if (dpath->type != MEDIA_DEVICE_PATH)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Sub-Type 1 – Hard Drive */
|
/* Sub-Type 1 – Hard Drive */
|
||||||
if (dpath->sub_type == 0x01) {
|
if (dpath->sub_type == MEDIA_HARDDRIVE_DP) {
|
||||||
/* 0x02 – GUID Partition Table */
|
/* 0x02 – GUID Partition Table */
|
||||||
if (dpath->drive.mbr_type != 0x02)
|
if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* 0x02 – GUID signature */
|
/* 0x02 – GUID signature */
|
||||||
if (dpath->drive.signature_type != 0x02)
|
if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (part_uuid)
|
if (part_uuid)
|
||||||
@ -275,8 +369,9 @@ int efi_get_boot_option(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Sub-Type 4 – File Path */
|
/* 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);
|
p = utf16_to_utf8(dpath->path, dpath->length-4);
|
||||||
|
efi_tilt_backslashes(p);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,6 +383,8 @@ int efi_get_boot_option(
|
|||||||
*part_uuid = p_uuid;
|
*part_uuid = p_uuid;
|
||||||
if (path)
|
if (path)
|
||||||
*path = p;
|
*path = p;
|
||||||
|
if (active)
|
||||||
|
*active = !!header->attr & LOAD_OPTION_ACTIVE;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
err:
|
||||||
@ -296,6 +393,126 @@ err:
|
|||||||
return 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) {
|
int efi_get_boot_order(uint16_t **order) {
|
||||||
void *buf;
|
void *buf;
|
||||||
size_t l;
|
size_t l;
|
||||||
@ -320,6 +537,10 @@ int efi_get_boot_order(uint16_t **order) {
|
|||||||
return (int) (l / sizeof(uint16_t));
|
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]) {
|
static int boot_id_hex(const char s[4]) {
|
||||||
int i;
|
int i;
|
||||||
int id = 0;
|
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_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_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);
|
bool is_efi_boot(void);
|
||||||
int is_efi_secure_boot(void);
|
int is_efi_secure_boot(void);
|
||||||
int is_efi_secure_boot_setup_mode(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_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_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_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_get_boot_options(uint16_t **options);
|
||||||
|
|
||||||
int efi_loader_get_device_part_uuid(sd_id128_t *u);
|
int efi_loader_get_device_part_uuid(sd_id128_t *u);
|
||||||
int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader);
|
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