1
0
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:
Kay Sievers 2015-02-08 17:18:30 +01:00
parent 0fa2cac4f0
commit 0974a682d1
11 changed files with 1613 additions and 653 deletions

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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);