chiark / gitweb /
update TODO
[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 char *tilt_slashes(char *s) {
41         char *p;
42
43         if (!s)
44                 return NULL;
45
46         for (p = s; *p; p++)
47                 if (*p == '\\')
48                         *p = '/';
49         return s;
50 }
51
52 static int get_boot_entries(struct boot_info *info) {
53         uint16_t *list;
54         int i, n;
55         int err = 0;
56
57         n = efi_get_boot_options(&list);
58         if (n < 0)
59                 return n;
60
61         for (i = 0; i < n; i++) {
62                 struct boot_info_entry *e;
63
64                 e = realloc(info->fw_entries, (info->fw_entries_count+1) * sizeof(struct boot_info_entry));
65                 if (!e) {
66                         err = -ENOMEM;
67                                 break;
68                 }
69                 info->fw_entries = e;
70
71                 e = &info->fw_entries[info->fw_entries_count];
72                 memset(e, 0, sizeof(struct boot_info_entry));
73                 e->order = -1;
74
75                 err = efi_get_boot_option(list[i], &e->title, &e->part_uuid, &e->path);
76                 if (err < 0)
77                         continue;
78
79                 if (isempty(e->title)) {
80                         free(e->title);
81                         e->title = NULL;
82                 }
83                 tilt_slashes(e->path);
84
85                 e->id = list[i];
86                 info->fw_entries_count++;
87         }
88
89         free(list);
90         return err;
91 }
92
93 static int find_active_entry(struct boot_info *info) {
94         uint16_t boot_cur;
95         void *buf;
96         size_t l;
97         size_t i;
98         int err = -ENOENT;
99
100         err = efi_get_variable(EFI_VENDOR_GLOBAL, "BootCurrent", NULL, &buf, &l);
101         if (err < 0)
102                 return err;
103
104         memcpy(&boot_cur, buf, sizeof(uint16_t));
105         for (i = 0; i < info->fw_entries_count; i++) {
106                 if (info->fw_entries[i].id != boot_cur)
107                         continue;
108                 info->fw_entry_active = i;
109                 err = 0;
110                 break;
111         }
112         free(buf);
113         return err;
114 }
115
116 static int get_boot_order(struct boot_info *info) {
117         size_t i, k;
118         int r;
119
120         r = efi_get_boot_order(&info->fw_entries_order);
121         if (r < 0)
122                 return r;
123
124         info->fw_entries_order_count = r;
125
126         for (i = 0; i < info->fw_entries_order_count; i++) {
127                 for (k = 0; k < info->fw_entries_count; k++) {
128                         if (info->fw_entries[k].id != info->fw_entries_order[i])
129                                 continue;
130                         info->fw_entries[k].order = i;
131                         break;
132                 }
133         }
134
135         return 0;
136 }
137
138 static int entry_cmp(const void *a, const void *b) {
139         const struct boot_info_entry *e1 = a;
140         const struct boot_info_entry *e2 = b;
141
142         /* boot order of active entries */
143         if (e1->order > 0 && e2->order > 0)
144                 return e1->order - e2->order;
145
146         /* sort active entries before inactive ones */
147         if (e1->order > 0)
148                 return 1;
149         if (e2->order > 0)
150                 return -1;
151
152         /* order of inactive entries */
153         return e1->id - e2->id;
154 }
155
156 int boot_info_query(struct boot_info *info) {
157         char str[64];
158         char buf[64];
159         char *loader_active = NULL;
160
161         info->fw_secure_boot = is_efi_secure_boot();
162         info->fw_secure_boot_setup_mode = is_efi_secure_boot_setup_mode();
163
164         efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderInfo", &info->loader);
165
166         get_boot_entries(info);
167         if (info->fw_entries_count > 0) {
168                 get_boot_order(info);
169                 qsort(info->fw_entries, info->fw_entries_count, sizeof(struct boot_info_entry), entry_cmp);
170                 find_active_entry(info);
171         }
172
173         efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareType", &info->fw_type);
174         efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareInfo", &info->fw_info);
175         efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderImageIdentifier", &info->loader_image_path);
176         tilt_slashes(info->loader_image_path);
177         efi_get_loader_device_part_uuid(&info->loader_part_uuid);
178
179         boot_loader_read_entries(info);
180         efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntrySelected", &loader_active);
181         if (loader_active) {
182                 boot_loader_find_active_entry(info, loader_active);
183                 free(loader_active);
184         }
185
186         snprintf(str, sizeof(str), "LoaderEntryOptions-%s", sd_id128_to_string(info->machine_id, buf));
187         efi_get_variable_string(EFI_VENDOR_LOADER, str, &info->loader_options_added);
188
189         return 0;
190 }