Merge tag 'efi-2020-07-rc5' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
[oweals/u-boot.git] / lib / efi_loader / efi_bootmgr.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  EFI boot manager
4  *
5  *  Copyright (c) 2017 Rob Clark
6  */
7
8 #include <common.h>
9 #include <charset.h>
10 #include <log.h>
11 #include <malloc.h>
12 #include <efi_loader.h>
13 #include <asm/unaligned.h>
14
15 static const struct efi_boot_services *bs;
16 static const struct efi_runtime_services *rs;
17
18 /*
19  * bootmgr implements the logic of trying to find a payload to boot
20  * based on the BootOrder + BootXXXX variables, and then loading it.
21  *
22  * TODO detecting a special key held (f9?) and displaying a boot menu
23  * like you would get on a PC would be clever.
24  *
25  * TODO if we had a way to write and persist variables after the OS
26  * has started, we'd also want to check OsIndications to see if we
27  * should do normal or recovery boot.
28  */
29
30
31 /**
32  * efi_deserialize_load_option() - parse serialized data
33  *
34  * Parse serialized data describing a load option and transform it to the
35  * efi_load_option structure.
36  *
37  * @lo:         pointer to target
38  * @data:       serialized data
39  * @size:       size of the load option, on return size of the optional data
40  * Return:      status code
41  */
42 efi_status_t efi_deserialize_load_option(struct efi_load_option *lo, u8 *data,
43                                          efi_uintn_t *size)
44 {
45         efi_uintn_t len;
46
47         len = sizeof(u32);
48         if (*size < len + 2 * sizeof(u16))
49                 return EFI_INVALID_PARAMETER;
50         lo->attributes = get_unaligned_le32(data);
51         data += len;
52         *size -= len;
53
54         len = sizeof(u16);
55         lo->file_path_length = get_unaligned_le16(data);
56         data += len;
57         *size -= len;
58
59         lo->label = (u16 *)data;
60         len = u16_strnlen(lo->label, *size / sizeof(u16) - 1);
61         if (lo->label[len])
62                 return EFI_INVALID_PARAMETER;
63         len = (len + 1) * sizeof(u16);
64         if (*size < len)
65                 return EFI_INVALID_PARAMETER;
66         data += len;
67         *size -= len;
68
69         len = lo->file_path_length;
70         if (*size < len)
71                 return EFI_INVALID_PARAMETER;
72         lo->file_path = (struct efi_device_path *)data;
73          /*
74           * TODO: validate device path. There should be an end node within
75           * the indicated file_path_length.
76           */
77         data += len;
78         *size -= len;
79
80         lo->optional_data = data;
81
82         return EFI_SUCCESS;
83 }
84
85 /**
86  * efi_serialize_load_option() - serialize load option
87  *
88  * Serialize efi_load_option structure into byte stream for BootXXXX.
89  *
90  * @data:       buffer for serialized data
91  * @lo:         load option
92  * Return:      size of allocated buffer
93  */
94 unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
95 {
96         unsigned long label_len;
97         unsigned long size;
98         u8 *p;
99
100         label_len = (u16_strlen(lo->label) + 1) * sizeof(u16);
101
102         /* total size */
103         size = sizeof(lo->attributes);
104         size += sizeof(lo->file_path_length);
105         size += label_len;
106         size += lo->file_path_length;
107         if (lo->optional_data)
108                 size += (utf8_utf16_strlen((const char *)lo->optional_data)
109                                            + 1) * sizeof(u16);
110         p = malloc(size);
111         if (!p)
112                 return 0;
113
114         /* copy data */
115         *data = p;
116         memcpy(p, &lo->attributes, sizeof(lo->attributes));
117         p += sizeof(lo->attributes);
118
119         memcpy(p, &lo->file_path_length, sizeof(lo->file_path_length));
120         p += sizeof(lo->file_path_length);
121
122         memcpy(p, lo->label, label_len);
123         p += label_len;
124
125         memcpy(p, lo->file_path, lo->file_path_length);
126         p += lo->file_path_length;
127
128         if (lo->optional_data) {
129                 utf8_utf16_strcpy((u16 **)&p, (const char *)lo->optional_data);
130                 p += sizeof(u16); /* size of trailing \0 */
131         }
132         return size;
133 }
134
135 /**
136  * get_var() - get UEFI variable
137  *
138  * It is the caller's duty to free the returned buffer.
139  *
140  * @name:       name of variable
141  * @vendor:     vendor GUID of variable
142  * @size:       size of allocated buffer
143  * Return:      buffer with variable data or NULL
144  */
145 static void *get_var(u16 *name, const efi_guid_t *vendor,
146                      efi_uintn_t *size)
147 {
148         efi_guid_t *v = (efi_guid_t *)vendor;
149         efi_status_t ret;
150         void *buf = NULL;
151
152         *size = 0;
153         EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf));
154         if (ret == EFI_BUFFER_TOO_SMALL) {
155                 buf = malloc(*size);
156                 EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf));
157         }
158
159         if (ret != EFI_SUCCESS) {
160                 free(buf);
161                 *size = 0;
162                 return NULL;
163         }
164
165         return buf;
166 }
167
168 /**
169  * try_load_entry() - try to load image for boot option
170  *
171  * Attempt to load load-option number 'n', returning device_path and file_path
172  * if successful. This checks that the EFI_LOAD_OPTION is active (enabled)
173  * and that the specified file to boot exists.
174  *
175  * @n:          number of the boot option, e.g. 0x0a13 for Boot0A13
176  * @handle:     on return handle for the newly installed image
177  * Return:      status code
178  */
179 static efi_status_t try_load_entry(u16 n, efi_handle_t *handle)
180 {
181         struct efi_load_option lo;
182         u16 varname[] = L"Boot0000";
183         u16 hexmap[] = L"0123456789ABCDEF";
184         void *load_option;
185         efi_uintn_t size;
186         efi_status_t ret;
187
188         varname[4] = hexmap[(n & 0xf000) >> 12];
189         varname[5] = hexmap[(n & 0x0f00) >> 8];
190         varname[6] = hexmap[(n & 0x00f0) >> 4];
191         varname[7] = hexmap[(n & 0x000f) >> 0];
192
193         load_option = get_var(varname, &efi_global_variable_guid, &size);
194         if (!load_option)
195                 return EFI_LOAD_ERROR;
196
197         ret = efi_deserialize_load_option(&lo, load_option, &size);
198         if (ret != EFI_SUCCESS) {
199                 log_warning("Invalid load option for %ls\n", varname);
200                 goto error;
201         }
202
203         if (lo.attributes & LOAD_OPTION_ACTIVE) {
204                 u32 attributes;
205
206                 debug("%s: trying to load \"%ls\" from %pD\n",
207                       __func__, lo.label, lo.file_path);
208
209                 ret = EFI_CALL(efi_load_image(true, efi_root, lo.file_path,
210                                               NULL, 0, handle));
211                 if (ret != EFI_SUCCESS) {
212                         printf("Loading from Boot%04X '%ls' failed\n", n,
213                                lo.label);
214                         goto error;
215                 }
216
217                 attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
218                              EFI_VARIABLE_RUNTIME_ACCESS;
219                 size = sizeof(n);
220                 ret = EFI_CALL(efi_set_variable(
221                                 L"BootCurrent",
222                                 (efi_guid_t *)&efi_global_variable_guid,
223                                 attributes, size, &n));
224                 if (ret != EFI_SUCCESS) {
225                         if (EFI_CALL(efi_unload_image(*handle))
226                             != EFI_SUCCESS)
227                                 printf("Unloading image failed\n");
228                         goto error;
229                 }
230
231                 printf("Booting: %ls\n", lo.label);
232         } else {
233                 ret = EFI_LOAD_ERROR;
234         }
235
236 error:
237         free(load_option);
238
239         return ret;
240 }
241
242 /**
243  * efi_bootmgr_load() - try to load from BootNext or BootOrder
244  *
245  * Attempt to load from BootNext or in the order specified by BootOrder
246  * EFI variable, the available load-options, finding and returning
247  * the first one that can be loaded successfully.
248  *
249  * @handle:     on return handle for the newly installed image
250  * Return:      status code
251  */
252 efi_status_t efi_bootmgr_load(efi_handle_t *handle)
253 {
254         u16 bootnext, *bootorder;
255         efi_uintn_t size;
256         int i, num;
257         efi_status_t ret;
258
259         bs = systab.boottime;
260         rs = systab.runtime;
261
262         /* BootNext */
263         bootnext = 0;
264         size = sizeof(bootnext);
265         ret = EFI_CALL(efi_get_variable(L"BootNext",
266                                         (efi_guid_t *)&efi_global_variable_guid,
267                                         NULL, &size, &bootnext));
268         if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
269                 /* BootNext does exist here */
270                 if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16))
271                         printf("BootNext must be 16-bit integer\n");
272
273                 /* delete BootNext */
274                 ret = EFI_CALL(efi_set_variable(
275                                         L"BootNext",
276                                         (efi_guid_t *)&efi_global_variable_guid,
277                                         EFI_VARIABLE_NON_VOLATILE, 0,
278                                         &bootnext));
279
280                 /* load BootNext */
281                 if (ret == EFI_SUCCESS) {
282                         if (size == sizeof(u16)) {
283                                 ret = try_load_entry(bootnext, handle);
284                                 if (ret == EFI_SUCCESS)
285                                         return ret;
286                                 printf("Loading from BootNext failed, falling back to BootOrder\n");
287                         }
288                 } else {
289                         printf("Deleting BootNext failed\n");
290                 }
291         }
292
293         /* BootOrder */
294         bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
295         if (!bootorder) {
296                 printf("BootOrder not defined\n");
297                 ret = EFI_NOT_FOUND;
298                 goto error;
299         }
300
301         num = size / sizeof(uint16_t);
302         for (i = 0; i < num; i++) {
303                 debug("%s: trying to load Boot%04X\n", __func__, bootorder[i]);
304                 ret = try_load_entry(bootorder[i], handle);
305                 if (ret == EFI_SUCCESS)
306                         break;
307         }
308
309         free(bootorder);
310
311 error:
312         return ret;
313 }