bootctl: add boot loader and firmware interface tool

This commit is contained in:
Kay Sievers 2013-02-08 17:24:43 +01:00
parent c937e0d5c5
commit 7b4d7cc082
10 changed files with 869 additions and 4 deletions

1
.gitignore vendored
View file

@ -2,6 +2,7 @@
/TAGS
/accelerometer
/ata_id
/bootctl
/build-aux
/cdrom_id
/collect

View file

@ -3069,7 +3069,6 @@ timedatectl_LDADD = \
bin_PROGRAMS += \
timedatectl
endif
polkitpolicy_in_files += \
@ -3078,6 +3077,21 @@ polkitpolicy_in_files += \
EXTRA_DIST += \
units/systemd-timedated.service.in
# ------------------------------------------------------------------------------
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
bootctl_LDADD = \
libsystemd-shared.la \
libsystemd-id128.la
bin_PROGRAMS += \
bootctl
# ------------------------------------------------------------------------------
if HAVE_MYHOSTNAME
libnss_myhostname_la_SOURCES = \

171
src/boot/boot-efi.c Normal file
View file

@ -0,0 +1,171 @@
/*-*- 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 int get_boot_entries(struct boot_info *info) {
DIR *d = NULL;
struct dirent *dent;
int err = 0;
d = opendir("/sys/firmware/efi/efivars");
if (!d)
return -errno;
for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
unsigned int id;
struct boot_info_entry *e;
if (dent->d_name[0] == '.')
continue;
if (sscanf(dent->d_name, "Boot%04X-8be4df61-93ca-11d2-aa0d-00e098032b8c", &id) != 1)
continue;
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];
memset(e, 0, sizeof(struct boot_info_entry));
e->order = -1;
err = efi_get_boot_option(id, NULL, &e->title, &e->part_uuid, &e->path, &e->data, &e->data_size);
if (err < 0)
break;
e->id = id;
info->fw_entries_count++;
}
closedir(d);
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 = -ENOENT;
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 err;
err = efi_get_boot_order(&info->fw_entries_order, &info->fw_entries_order_count);
if (err < 0)
return err;
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;
info->loader = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderInfo");
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);
}
info->fw_type = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareType");
info->fw_info = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareInfo");
info->loader_image_path = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderImageIdentifier");
efi_get_loader_device_part_uuid(&info->loader_part_uuid);
boot_loader_read_entries(info);
loader_active = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntrySelected");
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));
info->loader_options_added = efi_get_variable_string(EFI_VENDOR_LOADER, str);
return 0;
}

131
src/boot/boot-loader.c Normal file
View file

@ -0,0 +1,131 @@
/*-*- 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 (strcmp(fn, info->loader_entries[i].path) == 0) {
info->loader_entry_active = i;
break;
}
}
free(fn);
return 0;
}

25
src/boot/boot-loader.h Normal file
View file

@ -0,0 +1,25 @@
/*-*- 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/>.
***/
int boot_loader_read_entries(struct boot_info *info);
int boot_loader_find_active_entry(struct boot_info *info, const char *loader_active);

64
src/boot/boot.h Normal file
View file

@ -0,0 +1,64 @@
/*-*- 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;
char *data;
size_t data_size;
};
struct boot_info {
sd_id128_t machine_id;
sd_id128_t boot_id;
char *fw_type;
char *fw_info;
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);

271
src/boot/bootctl.c Normal file
View file

@ -0,0 +1,271 @@
/*-*- 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 <sys/timex.h>
#include "boot.h"
#include "build.h"
#include "util.h"
#include "utf8.h"
static int help(void) {
printf("%s [OPTIONS...] COMMAND ...\n\n"
"Query or change firmware and boot mananger settings.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
"Commands:\n"
" status Show current time settings\n",
program_invocation_short_name);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ NULL, 0, NULL, 0 }
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "+hH:P", options, NULL)) >= 0) {
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
puts(PACKAGE_STRING);
puts(SYSTEMD_FEATURES);
return 0;
case '?':
return -EINVAL;
default:
log_error("Unknown option code %c", c);
return -EINVAL;
}
}
return 1;
}
static int boot_info_new(struct boot_info **info) {
struct boot_info *in;
int err;
in = new0(struct boot_info, 1);
if (!in)
return -ENOMEM;
err = sd_id128_get_machine(&in->machine_id);
if (err < 0)
goto err;
err = sd_id128_get_boot(&in->boot_id);
if (err < 0)
goto err;
in->fw_entry_active = -1;
in->loader_entry_active = -1;
*info = in;
return 0;
err:
free(in);
return err;
}
static void boot_info_entries_free(struct boot_info_entry *entries, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
free(entries[i].title);
free(entries[i].path);
}
free(entries);
}
static void boot_info_free(struct boot_info *info) {
free(info->fw_type);
free(info->fw_info);
boot_info_entries_free(info->fw_entries, info->fw_entries_count);
free(info->fw_entries_order);
free(info->loader);
free(info->loader_image_path);
free(info->loader_options_added);
boot_info_entries_free(info->loader_entries, info->loader_entries_count);
free(info);
}
static int show_status(char **args, unsigned n) {
char buf[64];
struct boot_info *info;
int err;
err = boot_info_new(&info);
if (err < 0)
return -ENOMEM;
err = boot_info_query(info);
printf(" Machine ID: %s\n", sd_id128_to_string(info->machine_id, buf));
printf(" Boot ID: %s\n", sd_id128_to_string(info->boot_id, buf));
if (info->fw_type)
printf(" Firmware: %s (%s)\n", info->fw_type, strna(info->fw_info));
if (info->fw_entry_active >= 0) {
printf("Firmware entry: %s\n", info->fw_entries[info->fw_entry_active].title);
if (info->fw_entries[info->fw_entry_active].path)
printf(" %s\n", info->fw_entries[info->fw_entry_active].path);
if (!sd_id128_equal(info->fw_entries[info->fw_entry_active].part_uuid, SD_ID128_NULL))
printf(" %s\n", sd_id128_to_string(info->fw_entries[info->fw_entry_active].part_uuid, buf));
}
if (info->loader) {
printf(" Loader: %s\n", info->loader);
printf(" %s\n", strna(info->loader_image_path));
if (!sd_id128_equal(info->loader_part_uuid, SD_ID128_NULL))
printf(" %s\n", sd_id128_to_string(info->loader_part_uuid, buf));
if (info->loader_entry_active >= 0) {
printf(" Loader entry: %s\n", info->loader_entries[info->loader_entry_active].title);
printf(" %s\n", info->loader_entries[info->loader_entry_active].path);
}
printf("Loader options: %s\n", strna(info->loader_options_added));
} else
printf("No suitable data is provided by the boot manager. See:\n"
" http://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface\n"
" http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec\n"
"for details.\n");
printf("\n");
boot_info_free(info);
return err;
}
static int bootctl_main(int argc, char *argv[]) {
static const struct {
const char* verb;
const enum {
MORE,
LESS,
EQUAL
} argc_cmp;
const int argc;
int (* const dispatch)(char **args, unsigned n);
} verbs[] = {
{ "status", LESS, 1, show_status },
};
int left;
unsigned i;
assert(argc >= 0);
assert(argv);
left = argc - optind;
if (left <= 0)
/* Special rule: no arguments means "status" */
i = 0;
else {
if (streq(argv[optind], "help")) {
help();
return 0;
}
for (i = 0; i < ELEMENTSOF(verbs); i++)
if (streq(argv[optind], verbs[i].verb))
break;
if (i >= ELEMENTSOF(verbs)) {
log_error("Unknown operation %s", argv[optind]);
return -EINVAL;
}
}
switch (verbs[i].argc_cmp) {
case EQUAL:
if (left != verbs[i].argc) {
log_error("Invalid number of arguments.");
return -EINVAL;
}
break;
case MORE:
if (left < verbs[i].argc) {
log_error("Too few arguments.");
return -EINVAL;
}
break;
case LESS:
if (left > verbs[i].argc) {
log_error("Too many arguments.");
return -EINVAL;
}
break;
default:
assert_not_reached("Unknown comparison operator.");
}
return verbs[i].dispatch(argv + optind, left);
}
int main(int argc, char *argv[]) {
int r, retval = EXIT_FAILURE;
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r < 0)
goto finish;
else if (r == 0) {
retval = EXIT_SUCCESS;
goto finish;
}
r = bootctl_main(argc, argv);
retval = r < 0 ? EXIT_FAILURE : r;
finish:
return retval;
}

View file

@ -20,14 +20,13 @@
***/
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include "util.h"
#include "utf8.h"
#include "efivars.h"
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
bool is_efiboot(void) {
return access("/sys/firmware/efi", F_OK) >= 0;
}
@ -93,6 +92,188 @@ int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, v
return 0;
}
char *efi_get_variable_string(sd_id128_t vendor, const char *name) {
_cleanup_free_ void *s = NULL;
size_t ss;
int err;
err = efi_get_variable(vendor, name, NULL, &s, &ss);
if (err < 0)
return NULL;
return utf16_to_utf8(s, ss);
}
static size_t utf16_size(const uint16_t *s) {
size_t l = 0;
while (s[l] > 0)
l++;
return (l+1) * sizeof(uint16_t);
}
static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) {
struct uuid {
uint32_t u1;
uint16_t u2;
uint16_t u3;
uint8_t u4[8];
} _packed_;
const struct uuid *uuid = guid;
id128->bytes[0] = (uuid->u1 >> 24) & 0xff;
id128->bytes[1] = (uuid->u1 >> 16) & 0xff;
id128->bytes[2] = (uuid->u1 >> 8) & 0xff;
id128->bytes[3] = (uuid->u1) & 0xff;
id128->bytes[4] = (uuid->u2 >> 8) & 0xff;
id128->bytes[5] = (uuid->u2) & 0xff;
id128->bytes[6] = (uuid->u3 >> 8) & 0xff;
id128->bytes[7] = (uuid->u3) & 0xff;
memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4));
}
int efi_get_boot_option(uint32_t id, uint32_t *attributes, char **title, sd_id128_t *part_uuid, char **path, char **data, size_t *data_size) {
struct boot_option {
uint32_t attr;
uint16_t path_len;
uint16_t title[];
} _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;
} _packed_;
struct device_path {
uint8_t type;
uint8_t sub_type;
uint16_t length;
union {
uint16_t path[0];
struct drive_path drive;
};
} _packed_;
char boot_id[32];
_cleanup_free_ char *buf = NULL;
size_t l;
struct boot_option *header;
size_t title_size;
char *s = NULL;
char *p = NULL;
sd_id128_t p_uuid = SD_ID128_NULL;
char *d = NULL;
size_t d_size = 0;
int err;
snprintf(boot_id, sizeof(boot_id), "Boot%04X", id);
err = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
if (err < 0)
return err;
if (l < sizeof(struct boot_option))
return -ENOENT;
header = (struct boot_option *)buf;
title_size = utf16_size(header->title);
if (title_size > l - offsetof(struct boot_option, title))
return -EINVAL;
s = utf16_to_utf8(header->title, title_size);
if (!s) {
err = -ENOMEM;
goto err;
}
if (header->path_len > 0) {
char *dbuf;
size_t dnext;
dbuf = buf + offsetof(struct boot_option, title) + title_size;
dnext = 0;
while (dnext < header->path_len) {
struct device_path *dpath;
dpath = (struct device_path *)(dbuf + dnext);
if (dpath->length < 4)
break;
/* Type 0x7F End of Hardware Device Path, Sub-Type 0xFF End Entire Device Path */
if (dpath->type == 0x7f && dpath->sub_type == 0xff)
break;
dnext += dpath->length;
/* Type 0x04 Media Device Path */
if (dpath->type != 0x04)
continue;
/* Sub-Type 1 Hard Drive */
if (dpath->sub_type == 0x01) {
/* 0x02 GUID Partition Table */
if (dpath->drive.mbr_type != 0x02)
continue;
/* 0x02 GUID signature */
if (dpath->drive.signature_type != 0x02)
continue;
efi_guid_to_id128(dpath->drive.signature, &p_uuid);
continue;
}
/* Sub-Type 4 File Path */
if (dpath->sub_type == 0x04) {
p = utf16_to_utf8(dpath->path, dpath->length-4);
continue;
}
}
}
*title = s;
if (part_uuid)
*part_uuid = p_uuid;
if (path)
*path = p;
if (data)
*data = d;
if (data_size)
*data_size = d_size;
return 0;
err:
free(s);
free(p);
free(d);
return err;
}
int efi_get_boot_order(uint16_t **order, size_t *count) {
void *buf;
size_t l;
int err;
err = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
if (err < 0)
return err;
if (l == 0) {
free(buf);
return -ENOENT;
}
if ((l % sizeof(uint16_t) > 0)) {
free(buf);
return -EINVAL;
}
*order = buf;
*count = l / sizeof(uint16_t);
return 0;
}
static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
_cleanup_free_ void *i = NULL;
_cleanup_free_ char *j = NULL;

View file

@ -28,9 +28,16 @@
#include "sd-id128.h"
#include "time-util.h"
#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)
bool is_efiboot(void);
int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
char *efi_get_variable_string(sd_id128_t vendor, const char *name);
int efi_get_boot_option(uint32_t nr, uint32_t *attributes, char **title, sd_id128_t *partuuid, char **path, char **data, size_t *data_size);
int efi_get_boot_order(uint16_t **order, size_t *count);
int efi_get_boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader);

View file

@ -317,6 +317,6 @@ char *utf16_to_utf8(const void *s, size_t length) {
}
*t = 0;
return r;
return r;
}