]> git.proxmox.com Git - efi-boot-shim.git/blame - load-options.c
Force usage of newest revocations at build time
[efi-boot-shim.git] / load-options.c
CommitLineData
8529e0f7
SM
1// SPDX-License-Identifier: BSD-2-Clause-Patent
2/*
3 * load-options.c - all the stuff we need to parse the load options
4 */
5
6#include "shim.h"
7
8CHAR16 *second_stage;
9void *load_options;
10UINT32 load_options_size;
11
12/*
13 * Generate the path of an executable given shim's path and the name
14 * of the executable
15 */
16EFI_STATUS
17generate_path_from_image_path(EFI_LOADED_IMAGE *li,
18 CHAR16 *ImagePath,
19 CHAR16 **PathName)
20{
21 EFI_DEVICE_PATH *devpath;
22 unsigned int i;
23 int j, last = -1;
24 unsigned int pathlen = 0;
25 EFI_STATUS efi_status = EFI_SUCCESS;
26 CHAR16 *bootpath;
27
28 /*
29 * Suuuuper lazy technique here, but check and see if this is a full
30 * path to something on the ESP. Backwards compatibility demands
31 * that we don't just use \\, because we (not particularly brightly)
32 * used to require that the relative file path started with that.
33 *
34 * If it is a full path, don't try to merge it with the directory
35 * from our Loaded Image handle.
36 */
37 if (StrSize(ImagePath) > 5 && StrnCmp(ImagePath, L"\\EFI\\", 5) == 0) {
38 *PathName = StrDuplicate(ImagePath);
39 if (!*PathName) {
40 perror(L"Failed to allocate path buffer\n");
41 return EFI_OUT_OF_RESOURCES;
42 }
43 return EFI_SUCCESS;
44 }
45
46 devpath = li->FilePath;
47
48 bootpath = DevicePathToStr(devpath);
49
50 pathlen = StrLen(bootpath);
51
52 /*
53 * DevicePathToStr() concatenates two nodes with '/'.
54 * Convert '/' to '\\'.
55 */
56 for (i = 0; i < pathlen; i++) {
57 if (bootpath[i] == '/')
58 bootpath[i] = '\\';
59 }
60
61 for (i=pathlen; i>0; i--) {
62 if (bootpath[i] == '\\' && bootpath[i-1] == '\\')
63 bootpath[i] = '/';
64 else if (last == -1 && bootpath[i] == '\\')
65 last = i;
66 }
67
68 if (last == -1 && bootpath[0] == '\\')
69 last = 0;
70 bootpath[last+1] = '\0';
71
72 if (last > 0) {
73 for (i = 0, j = 0; bootpath[i] != '\0'; i++) {
74 if (bootpath[i] != '/') {
75 bootpath[j] = bootpath[i];
76 j++;
77 }
78 }
79 bootpath[j] = '\0';
80 }
81
82 for (i = 0, last = 0; i < StrLen(ImagePath); i++)
83 if (ImagePath[i] == '\\')
84 last = i + 1;
85
86 ImagePath = ImagePath + last;
87 *PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath));
88
89 if (!*PathName) {
90 perror(L"Failed to allocate path buffer\n");
91 efi_status = EFI_OUT_OF_RESOURCES;
92 goto error;
93 }
94
95 *PathName[0] = '\0';
96 if (StrnCaseCmp(bootpath, ImagePath, StrLen(bootpath)))
97 StrCat(*PathName, bootpath);
98 StrCat(*PathName, ImagePath);
99
100error:
101 FreePool(bootpath);
102
103 return efi_status;
104}
105
106/*
107 * Extract the OptionalData and OptionalData fields from an
108 * EFI_LOAD_OPTION.
109 */
110static inline EFI_STATUS
111get_load_option_optional_data(VOID *data, UINT32 data_size,
112 VOID **od, UINT32 *ods)
113{
114 /*
115 * If it's not at least Attributes + FilePathListLength +
116 * Description=L"" + 0x7fff0400 (EndEntrireDevicePath), it can't
117 * be valid.
118 */
119 if (data_size < (sizeof(UINT32) + sizeof(UINT16) + 2 + 4))
120 return EFI_INVALID_PARAMETER;
121
122 UINT8 *start = (UINT8 *)data;
123 UINT8 *cur = start + sizeof(UINT32);
124 UINT16 fplistlen = *(UINT16 *)cur;
125 /*
126 * If there's not enough space for the file path list and the
127 * smallest possible description (L""), it's not valid.
128 */
129 if (fplistlen > data_size - (sizeof(UINT32) + 2 + 4))
130 return EFI_INVALID_PARAMETER;
131
132 cur += sizeof(UINT16);
133 UINT32 limit = data_size - (cur - start) - fplistlen;
134 UINT32 i;
135 for (i = 0; i < limit ; i++) {
136 /* If the description isn't valid UCS2-LE, it's not valid. */
137 if (i % 2 != 0) {
138 if (cur[i] != 0)
139 return EFI_INVALID_PARAMETER;
140 } else if (cur[i] == 0) {
141 /* we've found the end */
142 i++;
143 if (i >= limit || cur[i] != 0)
144 return EFI_INVALID_PARAMETER;
145 break;
146 }
147 }
148 i++;
149 if (i > limit)
150 return EFI_INVALID_PARAMETER;
151
152 /*
153 * If i is limit, we know the rest of this is the FilePathList and
154 * there's no optional data. So just bail now.
155 */
156 if (i == limit) {
157 *od = NULL;
158 *ods = 0;
159 return EFI_SUCCESS;
160 }
161
162 cur += i;
163 limit -= i;
164 limit += fplistlen;
165 i = 0;
166 while (limit - i >= 4) {
167 struct {
168 UINT8 type;
169 UINT8 subtype;
170 UINT16 len;
171 } dp = {
172 .type = cur[i],
173 .subtype = cur[i+1],
174 /*
175 * it's a little endian UINT16, but we're not
176 * guaranteed alignment is sane, so we can't just
177 * typecast it directly.
178 */
179 .len = (cur[i+3] << 8) | cur[i+2],
180 };
181
182 /*
183 * We haven't found an EndEntire, so this has to be a valid
184 * EFI_DEVICE_PATH in order for the data to be valid. That
185 * means it has to fit, and it can't be smaller than 4 bytes.
186 */
187 if (dp.len < 4 || dp.len > limit)
188 return EFI_INVALID_PARAMETER;
189
190 /*
191 * see if this is an EndEntire node...
192 */
193 if (dp.type == 0x7f && dp.subtype == 0xff) {
194 /*
195 * if we've found the EndEntire node, it must be 4
196 * bytes
197 */
198 if (dp.len != 4)
199 return EFI_INVALID_PARAMETER;
200
201 i += dp.len;
202 break;
203 }
204
205 /*
206 * It's just some random DP node; skip it.
207 */
208 i += dp.len;
209 }
210 if (i != fplistlen)
211 return EFI_INVALID_PARAMETER;
212
213 /*
214 * if there's any space left, it's "optional data"
215 */
216 *od = cur + i;
217 *ods = limit - i;
218 return EFI_SUCCESS;
219}
220
221static int
222is_our_path(EFI_LOADED_IMAGE *li, CHAR16 *path)
223{
224 CHAR16 *dppath = NULL;
225 CHAR16 *PathName = NULL;
226 EFI_STATUS efi_status;
227 int ret = 1;
228
229 dppath = DevicePathToStr(li->FilePath);
230 if (!dppath)
231 return 0;
232
233 efi_status = generate_path_from_image_path(li, path, &PathName);
234 if (EFI_ERROR(efi_status)) {
235 perror(L"Unable to generate path %s: %r\n", path,
236 efi_status);
237 goto done;
238 }
239
240 dprint(L"dppath: %s\n", dppath);
241 dprint(L"path: %s\n", path);
242 if (StrnCaseCmp(dppath, PathName, StrLen(dppath)))
243 ret = 0;
244
245done:
246 FreePool(dppath);
247 FreePool(PathName);
248 return ret;
249}
250
251/*
252 * Split the supplied load options in to a NULL terminated
253 * string representing the path of the second stage loader,
254 * and return a pointer to the remaining load options data
255 * and its remaining size.
256 *
257 * This expects the supplied load options to begin with a
258 * string that is either NULL terminated or terminated with
259 * a space and some optional data. It will return NULL if
260 * the supplied load options contains no spaces or NULL
261 * terminators.
262 */
263static CHAR16 *
264split_load_options(VOID *in, UINT32 in_size,
265 VOID **remaining,
266 UINT32 *remaining_size) {
267 UINTN i;
268 CHAR16 *arg0 = NULL;
269 CHAR16 *start = (CHAR16 *)in;
270
271 /* Skip spaces */
272 for (i = 0; i < in_size / sizeof(CHAR16); i++) {
273 if (*start != L' ')
274 break;
275
276 start++;
277 }
278
279 in_size -= ((VOID *)start - in);
280
281 /*
282 * Ensure that the first argument is NULL terminated by
283 * replacing L' ' with L'\0'.
284 */
285 for (i = 0; i < in_size / sizeof(CHAR16); i++) {
286 if (start[i] == L' ' || start[i] == L'\0') {
287 start[i] = L'\0';
288 arg0 = (CHAR16 *)start;
289 break;
290 }
291 }
292
293 if (arg0) {
294 UINTN skip = i + 1;
295 *remaining_size = in_size - (skip * sizeof(CHAR16));
296 *remaining = *remaining_size > 0 ? start + skip : NULL;
297 }
298
299 return arg0;
300}
301
302/*
303 * Check the load options to specify the second stage loader
304 */
305EFI_STATUS
306parse_load_options(EFI_LOADED_IMAGE *li)
307{
308 EFI_STATUS efi_status;
309 VOID *remaining = NULL;
310 UINT32 remaining_size;
311 CHAR16 *loader_str = NULL;
312
313 dprint(L"full load options:\n");
314 dhexdumpat(li->LoadOptions, li->LoadOptionsSize, 0);
315
316 /*
317 * Sanity check since we make several assumptions about the length
318 * Some firmware feeds the following load option when booting from
319 * an USB device:
320 *
321 * 0x46 0x4a 0x00 |FJ.|
322 *
323 * The string is meaningless for shim and so just ignore it.
324 */
325 if (li->LoadOptionsSize % 2 != 0)
326 return EFI_SUCCESS;
327
328 /* So, load options are a giant pain in the ass. If we're invoked
329 * from the EFI shell, we get something like this:
330
33100000000 5c 00 45 00 36 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.|
33200000010 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 6d 00 |o.r.a.\.s.h.i.m.|
33300000020 78 00 36 00 34 00 2e 00 64 00 66 00 69 00 20 00 |x.6.4...e.f.i. .|
33400000030 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.|
33500000040 6f 00 72 00 61 00 5c 00 66 00 77 00 75 00 70 00 |o.r.a.\.f.w.u.p.|
33600000050 64 00 61 00 74 00 65 00 2e 00 65 00 66 00 20 00 |d.a.t.e.e.f.i. .|
33700000060 00 00 66 00 73 00 30 00 3a 00 5c 00 00 00 |..f.s.0.:.\...|
338
339 *
340 * which is just some paths rammed together separated by a UCS-2 NUL.
341 * But if we're invoked from BDS, we get something more like:
342 *
343
34400000000 01 00 00 00 62 00 4c 00 69 00 6e 00 75 00 78 00 |....b.L.i.n.u.x.|
34500000010 20 00 46 00 69 00 72 00 6d 00 77 00 61 00 72 00 | .F.i.r.m.w.a.r.|
34600000020 65 00 20 00 55 00 70 00 64 00 61 00 74 00 65 00 |e. .U.p.d.a.t.e.|
34700000030 72 00 00 00 40 01 2a 00 01 00 00 00 00 08 00 00 |r.....*.........|
34800000040 00 00 00 00 00 40 06 00 00 00 00 00 1a 9e 55 bf |.....@........U.|
34900000050 04 57 f2 4f b4 4a ed 26 4a 40 6a 94 02 02 04 04 |.W.O.:.&J@j.....|
35000000060 34 00 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 |4.\.E.F.I.f.e.d.|
35100000070 64 00 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 |o.r.a.\.s.h.i.m.|
35200000080 6d 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |x.6.4...e.f.i...|
35300000090 00 00 7f ff 40 00 20 00 5c 00 66 00 77 00 75 00 |...... .\.f.w.u.|
354000000a0 70 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |p.x.6.4...e.f.i.|
355000000b0 00 00 |..|
356
357 *
358 * which is clearly an EFI_LOAD_OPTION filled in halfway reasonably.
359 * In short, the UEFI shell is still a useless piece of junk.
360 *
361 * But then on some versions of BDS, we get:
362
36300000000 5c 00 66 00 77 00 75 00 70 00 78 00 36 00 34 00 |\.f.w.u.p.x.6.4.|
36400000010 2e 00 65 00 66 00 69 00 00 00 |..e.f.i...|
3650000001a
366
367 * which as you can see is one perfectly normal UCS2-EL string
368 * containing the load option from the Boot#### variable.
369 *
370 * We also sometimes find a guid or partial guid at the end, because
371 * BDS will add that, but we ignore that here.
372 */
373
374 /*
375 * Maybe there just aren't any options...
376 */
377 if (li->LoadOptionsSize == 0)
378 return EFI_SUCCESS;
379
380 /*
381 * In either case, we've got to have at least a UCS2 NUL...
382 */
383 if (li->LoadOptionsSize < 2)
384 return EFI_BAD_BUFFER_SIZE;
385
386 /*
387 * Some awesome versions of BDS will add entries for Linux. On top
388 * of that, some versions of BDS will "tag" any Boot#### entries they
389 * create by putting a GUID at the very end of the optional data in
390 * the EFI_LOAD_OPTIONS, thus screwing things up for everybody who
391 * tries to actually *use* the optional data for anything. Why they
392 * did this instead of adding a flag to the spec to /say/ it's
393 * created by BDS, I do not know. For shame.
394 *
395 * Anyway, just nerf that out from the start. It's always just
396 * garbage at the end.
397 */
398 if (li->LoadOptionsSize > 16) {
399 if (CompareGuid((EFI_GUID *)(li->LoadOptions
400 + (li->LoadOptionsSize - 16)),
401 &BDS_GUID) == 0)
402 li->LoadOptionsSize -= 16;
403 }
404
405 /*
406 * Apparently sometimes we get L"\0\0"? Which isn't useful at all.
2dd2f760
SM
407 *
408 * Possibly related, but some boards have additional data before the
409 * size which is garbage (it's a weird path to the directory
410 * containing the loaders). Known boards that do this: Kontron VX3040
411 * (AMI), ASUS B85M-E, and at least one "older Dell laptop".
8529e0f7 412 */
2dd2f760 413 if (((CHAR16 *)li->LoadOptions)[0] == 0)
8529e0f7
SM
414 return EFI_SUCCESS;
415
416 /*
417 * See if this is an EFI_LOAD_OPTION and extract the optional
418 * data if it is. This will return an error if it is not a valid
419 * EFI_LOAD_OPTION.
420 */
421 efi_status = get_load_option_optional_data(li->LoadOptions,
422 li->LoadOptionsSize,
423 &li->LoadOptions,
424 &li->LoadOptionsSize);
425 if (EFI_ERROR(efi_status)) {
426 /*
427 * it's not an EFI_LOAD_OPTION, so it's probably just a string
428 * or list of strings.
429 *
430 * UEFI shell copies the whole line of the command into
431 * LoadOptions. We ignore the first string, i.e. the name of this
432 * program in this case.
433 */
434 loader_str = split_load_options(li->LoadOptions,
435 li->LoadOptionsSize,
436 &remaining,
437 &remaining_size);
438
439 if (loader_str && is_our_path(li, loader_str)) {
440 li->LoadOptions = remaining;
441 li->LoadOptionsSize = remaining_size;
442 }
443 }
444
445 loader_str = split_load_options(li->LoadOptions, li->LoadOptionsSize,
446 &remaining, &remaining_size);
447
448 /*
449 * Set up the name of the alternative loader and the LoadOptions for
450 * the loader
451 */
452 if (loader_str) {
453 second_stage = loader_str;
454 load_options = remaining;
455 load_options_size = remaining_size;
456 }
457
458 return EFI_SUCCESS;
459}
460
461// vim:fenc=utf-8:tw=75:noet