chiark / gitweb /
efi: unify BootXXXX reading
[elogind.git] / src / boot / boot-efi.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Kay Sievers
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <stdbool.h>
24 #include <unistd.h>
25 #include <getopt.h>
26 #include <locale.h>
27 #include <string.h>
28 #include <fnmatch.h>
29 #include <fcntl.h>
30 #include <sys/timex.h>
31
32 #include "boot.h"
33 #include "boot-loader.h"
34 #include "build.h"
35 #include "util.h"
36 #include "strv.h"
37 #include "efivars.h"
38 #include "conf-files.h"
39
40 static int get_boot_entries(struct boot_info *info) {
41         uint16_t *list;
42         int i, n;
43         int err = 0;
44
45         n = efi_get_boot_options(&list);
46         if (n < 0)
47                 return n;
48
49         for (i = 0; i < n; i++) {
50                 struct boot_info_entry *e;
51
52                 e = realloc(info->fw_entries, (info->fw_entries_count+1) * sizeof(struct boot_info_entry));
53                 if (!e) {
54                         err = -ENOMEM;
55                                 break;
56                 }
57                 info->fw_entries = e;
58
59                 e = &info->fw_entries[info->fw_entries_count];
60                 memset(e, 0, sizeof(struct boot_info_entry));
61                 e->order = -1;
62
63                 err = efi_get_boot_option(list[i], &e->title, &e->part_uuid, &e->path);
64                 if (err < 0)
65                         continue;
66
67                 e->id = list[i];
68                 info->fw_entries_count++;
69         }
70
71         free(list);
72         return err;
73 }
74
75 static int find_active_entry(struct boot_info *info) {
76         uint16_t boot_cur;
77         void *buf;
78         size_t l;
79         size_t i;
80         int err = -ENOENT;
81
82         err = efi_get_variable(EFI_VENDOR_GLOBAL, "BootCurrent", NULL, &buf, &l);
83         if (err < 0)
84                 return err;
85
86         memcpy(&boot_cur, buf, sizeof(uint16_t));
87         for (i = 0; i < info->fw_entries_count; i++) {
88                 if (info->fw_entries[i].id != boot_cur)
89                         continue;
90                 info->fw_entry_active = i;
91                 err = 0;
92                 break;
93         }
94         free(buf);
95         return err;
96 }
97
98 static int get_boot_order(struct boot_info *info) {
99         size_t i, k;
100         int r;
101
102         r = efi_get_boot_order(&info->fw_entries_order);
103         if (r < 0)
104                 return r;
105
106         info->fw_entries_order_count = r;
107
108         for (i = 0; i < info->fw_entries_order_count; i++) {
109                 for (k = 0; k < info->fw_entries_count; k++) {
110                         if (info->fw_entries[k].id != info->fw_entries_order[i])
111                                 continue;
112                         info->fw_entries[k].order = i;
113                         break;
114                 }
115         }
116
117         return 0;
118 }
119
120 static int entry_cmp(const void *a, const void *b) {
121         const struct boot_info_entry *e1 = a;
122         const struct boot_info_entry *e2 = b;
123
124         /* boot order of active entries */
125         if (e1->order > 0 && e2->order > 0)
126                 return e1->order - e2->order;
127
128         /* sort active entries before inactive ones */
129         if (e1->order > 0)
130                 return 1;
131         if (e2->order > 0)
132                 return -1;
133
134         /* order of inactive entries */
135         return e1->id - e2->id;
136 }
137
138 int boot_info_query(struct boot_info *info) {
139         char str[64];
140         char buf[64];
141         char *loader_active = NULL;
142
143         efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderInfo", &info->loader);
144
145         get_boot_entries(info);
146         if (info->fw_entries_count > 0) {
147                 get_boot_order(info);
148                 qsort(info->fw_entries, info->fw_entries_count, sizeof(struct boot_info_entry), entry_cmp);
149                 find_active_entry(info);
150         }
151
152         efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareType", &info->fw_type);
153         efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareInfo", &info->fw_info);
154         efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderImageIdentifier", &info->loader_image_path);
155         efi_get_loader_device_part_uuid(&info->loader_part_uuid);
156
157         boot_loader_read_entries(info);
158         efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntrySelected", &loader_active);
159         if (loader_active) {
160                 boot_loader_find_active_entry(info, loader_active);
161                 free(loader_active);
162         }
163
164         snprintf(str, sizeof(str), "LoaderEntryOptions-%s", sd_id128_to_string(info->machine_id, buf));
165         efi_get_variable_string(EFI_VENDOR_LOADER, str, &info->loader_options_added);
166
167         return 0;
168 }