]>
git.proxmox.com Git - efi-boot-shim.git/blob - load-options.c
1 // SPDX-License-Identifier: BSD-2-Clause-Patent
3 * load-options.c - all the stuff we need to parse the load options
10 UINT32 load_options_size
;
13 * Generate the path of an executable given shim's path and the name
17 generate_path_from_image_path(EFI_LOADED_IMAGE
*li
,
21 EFI_DEVICE_PATH
*devpath
;
24 unsigned int pathlen
= 0;
25 EFI_STATUS efi_status
= EFI_SUCCESS
;
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.
34 * If it is a full path, don't try to merge it with the directory
35 * from our Loaded Image handle.
37 if (StrSize(ImagePath
) > 5 && StrnCmp(ImagePath
, L
"\\EFI\\", 5) == 0) {
38 *PathName
= StrDuplicate(ImagePath
);
40 perror(L
"Failed to allocate path buffer\n");
41 return EFI_OUT_OF_RESOURCES
;
46 devpath
= li
->FilePath
;
48 bootpath
= DevicePathToStr(devpath
);
50 pathlen
= StrLen(bootpath
);
53 * DevicePathToStr() concatenates two nodes with '/'.
54 * Convert '/' to '\\'.
56 for (i
= 0; i
< pathlen
; i
++) {
57 if (bootpath
[i
] == '/')
61 for (i
=pathlen
; i
>0; i
--) {
62 if (bootpath
[i
] == '\\' && bootpath
[i
-1] == '\\')
64 else if (last
== -1 && bootpath
[i
] == '\\')
68 if (last
== -1 && bootpath
[0] == '\\')
70 bootpath
[last
+1] = '\0';
73 for (i
= 0, j
= 0; bootpath
[i
] != '\0'; i
++) {
74 if (bootpath
[i
] != '/') {
75 bootpath
[j
] = bootpath
[i
];
82 for (i
= 0, last
= 0; i
< StrLen(ImagePath
); i
++)
83 if (ImagePath
[i
] == '\\')
86 ImagePath
= ImagePath
+ last
;
87 *PathName
= AllocatePool(StrSize(bootpath
) + StrSize(ImagePath
));
90 perror(L
"Failed to allocate path buffer\n");
91 efi_status
= EFI_OUT_OF_RESOURCES
;
96 if (StrnCaseCmp(bootpath
, ImagePath
, StrLen(bootpath
)))
97 StrCat(*PathName
, bootpath
);
98 StrCat(*PathName
, ImagePath
);
107 * Extract the OptionalData and OptionalData fields from an
110 static inline EFI_STATUS
111 get_load_option_optional_data(VOID
*data
, UINT32 data_size
,
112 VOID
**od
, UINT32
*ods
)
115 * If it's not at least Attributes + FilePathListLength +
116 * Description=L"" + 0x7fff0400 (EndEntrireDevicePath), it can't
119 if (data_size
< (sizeof(UINT32
) + sizeof(UINT16
) + 2 + 4))
120 return EFI_INVALID_PARAMETER
;
122 UINT8
*start
= (UINT8
*)data
;
123 UINT8
*cur
= start
+ sizeof(UINT32
);
124 UINT16 fplistlen
= *(UINT16
*)cur
;
126 * If there's not enough space for the file path list and the
127 * smallest possible description (L""), it's not valid.
129 if (fplistlen
> data_size
- (sizeof(UINT32
) + 2 + 4))
130 return EFI_INVALID_PARAMETER
;
132 cur
+= sizeof(UINT16
);
133 UINT32 limit
= data_size
- (cur
- start
) - fplistlen
;
135 for (i
= 0; i
< limit
; i
++) {
136 /* If the description isn't valid UCS2-LE, it's not valid. */
139 return EFI_INVALID_PARAMETER
;
140 } else if (cur
[i
] == 0) {
141 /* we've found the end */
143 if (i
>= limit
|| cur
[i
] != 0)
144 return EFI_INVALID_PARAMETER
;
150 return EFI_INVALID_PARAMETER
;
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.
166 while (limit
- i
>= 4) {
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.
179 .len
= (cur
[i
+3] << 8) | cur
[i
+2],
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.
187 if (dp
.len
< 4 || dp
.len
> limit
)
188 return EFI_INVALID_PARAMETER
;
191 * see if this is an EndEntire node...
193 if (dp
.type
== 0x7f && dp
.subtype
== 0xff) {
195 * if we've found the EndEntire node, it must be 4
199 return EFI_INVALID_PARAMETER
;
206 * It's just some random DP node; skip it.
211 return EFI_INVALID_PARAMETER
;
214 * if there's any space left, it's "optional data"
222 is_our_path(EFI_LOADED_IMAGE
*li
, CHAR16
*path
)
224 CHAR16
*dppath
= NULL
;
225 CHAR16
*PathName
= NULL
;
226 EFI_STATUS efi_status
;
229 dppath
= DevicePathToStr(li
->FilePath
);
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
,
240 dprint(L
"dppath: %s\n", dppath
);
241 dprint(L
"path: %s\n", path
);
242 if (StrnCaseCmp(dppath
, PathName
, StrLen(dppath
)))
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.
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
264 split_load_options(VOID
*in
, UINT32 in_size
,
266 UINT32
*remaining_size
) {
269 CHAR16
*start
= (CHAR16
*)in
;
272 for (i
= 0; i
< in_size
/ sizeof(CHAR16
); i
++) {
279 in_size
-= ((VOID
*)start
- in
);
282 * Ensure that the first argument is NULL terminated by
283 * replacing L' ' with L'\0'.
285 for (i
= 0; i
< in_size
/ sizeof(CHAR16
); i
++) {
286 if (start
[i
] == L
' ' || start
[i
] == L
'\0') {
288 arg0
= (CHAR16
*)start
;
295 *remaining_size
= in_size
- (skip
* sizeof(CHAR16
));
296 *remaining
= *remaining_size
> 0 ? start
+ skip
: NULL
;
303 * Check the load options to specify the second stage loader
306 parse_load_options(EFI_LOADED_IMAGE
*li
)
308 EFI_STATUS efi_status
;
309 VOID
*remaining
= NULL
;
310 UINT32 remaining_size
;
311 CHAR16
*loader_str
= NULL
;
313 dprint(L
"full load options:\n");
314 dhexdumpat(li
->LoadOptions
, li
->LoadOptionsSize
, 0);
317 * Sanity check since we make several assumptions about the length
318 * Some firmware feeds the following load option when booting from
321 * 0x46 0x4a 0x00 |FJ.|
323 * The string is meaningless for shim and so just ignore it.
325 if (li
->LoadOptionsSize
% 2 != 0)
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:
331 00000000 5c 00 45 00 36 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.|
332 00000010 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 6d 00 |o.r.a.\.s.h.i.m.|
333 00000020 78 00 36 00 34 00 2e 00 64 00 66 00 69 00 20 00 |x.6.4...e.f.i. .|
334 00000030 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.|
335 00000040 6f 00 72 00 61 00 5c 00 66 00 77 00 75 00 70 00 |o.r.a.\.f.w.u.p.|
336 00000050 64 00 61 00 74 00 65 00 2e 00 65 00 66 00 20 00 |d.a.t.e.e.f.i. .|
337 00000060 00 00 66 00 73 00 30 00 3a 00 5c 00 00 00 |..f.s.0.:.\...|
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:
344 00000000 01 00 00 00 62 00 4c 00 69 00 6e 00 75 00 78 00 |....b.L.i.n.u.x.|
345 00000010 20 00 46 00 69 00 72 00 6d 00 77 00 61 00 72 00 | .F.i.r.m.w.a.r.|
346 00000020 65 00 20 00 55 00 70 00 64 00 61 00 74 00 65 00 |e. .U.p.d.a.t.e.|
347 00000030 72 00 00 00 40 01 2a 00 01 00 00 00 00 08 00 00 |r.....*.........|
348 00000040 00 00 00 00 00 40 06 00 00 00 00 00 1a 9e 55 bf |.....@........U.|
349 00000050 04 57 f2 4f b4 4a ed 26 4a 40 6a 94 02 02 04 04 |.W.O.:.&J@j.....|
350 00000060 34 00 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 |4.\.E.F.I.f.e.d.|
351 00000070 64 00 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 |o.r.a.\.s.h.i.m.|
352 00000080 6d 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |x.6.4...e.f.i...|
353 00000090 00 00 7f ff 40 00 20 00 5c 00 66 00 77 00 75 00 |...... .\.f.w.u.|
354 000000a0 70 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |p.x.6.4...e.f.i.|
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.
361 * But then on some versions of BDS, we get:
363 00000000 5c 00 66 00 77 00 75 00 70 00 78 00 36 00 34 00 |\.f.w.u.p.x.6.4.|
364 00000010 2e 00 65 00 66 00 69 00 00 00 |..e.f.i...|
367 * which as you can see is one perfectly normal UCS2-EL string
368 * containing the load option from the Boot#### variable.
370 * We also sometimes find a guid or partial guid at the end, because
371 * BDS will add that, but we ignore that here.
375 * Maybe there just aren't any options...
377 if (li
->LoadOptionsSize
== 0)
381 * In either case, we've got to have at least a UCS2 NUL...
383 if (li
->LoadOptionsSize
< 2)
384 return EFI_BAD_BUFFER_SIZE
;
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.
395 * Anyway, just nerf that out from the start. It's always just
396 * garbage at the end.
398 if (li
->LoadOptionsSize
> 16) {
399 if (CompareGuid((EFI_GUID
*)(li
->LoadOptions
400 + (li
->LoadOptionsSize
- 16)),
402 li
->LoadOptionsSize
-= 16;
406 * Apparently sometimes we get L"\0\0"? Which isn't useful at all.
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".
413 if (((CHAR16
*)li
->LoadOptions
)[0] == 0)
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
421 efi_status
= get_load_option_optional_data(li
->LoadOptions
,
424 &li
->LoadOptionsSize
);
425 if (EFI_ERROR(efi_status
)) {
427 * it's not an EFI_LOAD_OPTION, so it's probably just a string
428 * or list of strings.
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.
434 loader_str
= split_load_options(li
->LoadOptions
,
439 if (loader_str
&& is_our_path(li
, loader_str
)) {
440 li
->LoadOptions
= remaining
;
441 li
->LoadOptionsSize
= remaining_size
;
445 loader_str
= split_load_options(li
->LoadOptions
, li
->LoadOptionsSize
,
446 &remaining
, &remaining_size
);
449 * Set up the name of the alternative loader and the LoadOptions for
453 second_stage
= loader_str
;
454 load_options
= remaining
;
455 load_options_size
= remaining_size
;
461 // vim:fenc=utf-8:tw=75:noet