Group :: System/Kernel and hardware
RPM: shim
Main Changelog Spec Patches Sources Download Gear Bugs and FR Repocop
Patch: shim-15.4-upstream-0020-shim-move-the-bulk-of-set_second_stage-to-its-own-fi.patch
Download
Download
From 9e4f38abe61b600826360c14729cc54ea3ec7c3c Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Fri, 2 Jul 2021 14:22:55 -0400
Subject: [PATCH 20/35] shim: move the bulk of set_second_stage() to its own
file
This moves set_second_stage() and some of the helper functions it uses
out of shim.c, so that it's easier to write test cases for.
Signed-off-by: Peter Jones <pjones@redhat.com>
---
Makefile | 2 +-
include/load-options.h | 20 ++
load-options.c | 445 +++++++++++++++++++++++++++++++++++++++++
shim.c | 424 +--------------------------------------
shim.h | 1 +
5 files changed, 470 insertions(+), 422 deletions(-)
create mode 100644 include/load-options.h
create mode 100644 load-options.c
diff --git a/Makefile b/Makefile
index d80aea82..050c921d 100644
--- a/Makefile
+++ b/Makefile
@@ -38,7 +38,7 @@ CFLAGS += -DENABLE_SHIM_CERT
else
TARGETS += $(MMNAME) $(FBNAME)
endif
-OBJS = shim.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o pe.o httpboot.o csv.o
+OBJS = shim.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o pe.o httpboot.o csv.o load-options.o
KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer
ORIG_SOURCES = shim.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c httpboot.c shim.h version.h $(wildcard include/*.h)
MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o
diff --git a/include/load-options.h b/include/load-options.h
new file mode 100644
index 00000000..d2bee3bb
--- /dev/null
+++ b/include/load-options.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+/*
+ * load-options.h - all the stuff we need to parse the load options
+ */
+
+#ifndef SHIM_ARGV_H_
+#define SHIM_ARGV_H_
+
+EFI_STATUS generate_path_from_image_path(EFI_LOADED_IMAGE *li,
+ CHAR16 *ImagePath,
+ CHAR16 **PathName);
+
+EFI_STATUS parse_load_options(EFI_LOADED_IMAGE *li);
+
+extern CHAR16 *second_stage;
+extern void *load_options;
+extern UINT32 load_options_size;
+
+#endif /* !SHIM_ARGV_H_ */
+// vim:fenc=utf-8:tw=75:noet
diff --git a/load-options.c b/load-options.c
new file mode 100644
index 00000000..6126358d
--- /dev/null
+++ b/load-options.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+/*
+ * load-options.c - all the stuff we need to parse the load options
+ */
+
+#include "shim.h"
+
+CHAR16 *second_stage;
+void *load_options;
+UINT32 load_options_size;
+
+/*
+ * Generate the path of an executable given shim's path and the name
+ * of the executable
+ */
+EFI_STATUS
+generate_path_from_image_path(EFI_LOADED_IMAGE *li,
+ CHAR16 *ImagePath,
+ CHAR16 **PathName)
+{
+ EFI_DEVICE_PATH *devpath;
+ unsigned int i;
+ int j, last = -1;
+ unsigned int pathlen = 0;
+ EFI_STATUS efi_status = EFI_SUCCESS;
+ CHAR16 *bootpath;
+
+ /*
+ * Suuuuper lazy technique here, but check and see if this is a full
+ * path to something on the ESP. Backwards compatibility demands
+ * that we don't just use \\, because we (not particularly brightly)
+ * used to require that the relative file path started with that.
+ *
+ * If it is a full path, don't try to merge it with the directory
+ * from our Loaded Image handle.
+ */
+ if (StrSize(ImagePath) > 5 && StrnCmp(ImagePath, L"\\EFI\\", 5) == 0) {
+ *PathName = StrDuplicate(ImagePath);
+ if (!*PathName) {
+ perror(L"Failed to allocate path buffer\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+ return EFI_SUCCESS;
+ }
+
+ devpath = li->FilePath;
+
+ bootpath = DevicePathToStr(devpath);
+
+ pathlen = StrLen(bootpath);
+
+ /*
+ * DevicePathToStr() concatenates two nodes with '/'.
+ * Convert '/' to '\\'.
+ */
+ for (i = 0; i < pathlen; i++) {
+ if (bootpath[i] == '/')
+ bootpath[i] = '\\';
+ }
+
+ for (i=pathlen; i>0; i--) {
+ if (bootpath[i] == '\\' && bootpath[i-1] == '\\')
+ bootpath[i] = '/';
+ else if (last == -1 && bootpath[i] == '\\')
+ last = i;
+ }
+
+ if (last == -1 && bootpath[0] == '\\')
+ last = 0;
+ bootpath[last+1] = '\0';
+
+ if (last > 0) {
+ for (i = 0, j = 0; bootpath[i] != '\0'; i++) {
+ if (bootpath[i] != '/') {
+ bootpath[j] = bootpath[i];
+ j++;
+ }
+ }
+ bootpath[j] = '\0';
+ }
+
+ for (i = 0, last = 0; i < StrLen(ImagePath); i++)
+ if (ImagePath[i] == '\\')
+ last = i + 1;
+
+ ImagePath = ImagePath + last;
+ *PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath));
+
+ if (!*PathName) {
+ perror(L"Failed to allocate path buffer\n");
+ efi_status = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+
+ *PathName[0] = '\0';
+ if (StrnCaseCmp(bootpath, ImagePath, StrLen(bootpath)))
+ StrCat(*PathName, bootpath);
+ StrCat(*PathName, ImagePath);
+
+error:
+ FreePool(bootpath);
+
+ return efi_status;
+}
+
+/*
+ * Extract the OptionalData and OptionalData fields from an
+ * EFI_LOAD_OPTION.
+ */
+static inline EFI_STATUS
+get_load_option_optional_data(VOID *data, UINT32 data_size,
+ VOID **od, UINT32 *ods)
+{
+ /*
+ * If it's not at least Attributes + FilePathListLength +
+ * Description=L"" + 0x7fff0400 (EndEntrireDevicePath), it can't
+ * be valid.
+ */
+ if (data_size < (sizeof(UINT32) + sizeof(UINT16) + 2 + 4))
+ return EFI_INVALID_PARAMETER;
+
+ UINT8 *start = (UINT8 *)data;
+ UINT8 *cur = start + sizeof(UINT32);
+ UINT16 fplistlen = *(UINT16 *)cur;
+ /*
+ * If there's not enough space for the file path list and the
+ * smallest possible description (L""), it's not valid.
+ */
+ if (fplistlen > data_size - (sizeof(UINT32) + 2 + 4))
+ return EFI_INVALID_PARAMETER;
+
+ cur += sizeof(UINT16);
+ UINT32 limit = data_size - (cur - start) - fplistlen;
+ UINT32 i;
+ for (i = 0; i < limit ; i++) {
+ /* If the description isn't valid UCS2-LE, it's not valid. */
+ if (i % 2 != 0) {
+ if (cur[i] != 0)
+ return EFI_INVALID_PARAMETER;
+ } else if (cur[i] == 0) {
+ /* we've found the end */
+ i++;
+ if (i >= limit || cur[i] != 0)
+ return EFI_INVALID_PARAMETER;
+ break;
+ }
+ }
+ i++;
+ if (i > limit)
+ return EFI_INVALID_PARAMETER;
+
+ /*
+ * If i is limit, we know the rest of this is the FilePathList and
+ * there's no optional data. So just bail now.
+ */
+ if (i == limit) {
+ *od = NULL;
+ *ods = 0;
+ return EFI_SUCCESS;
+ }
+
+ cur += i;
+ limit -= i;
+ limit += fplistlen;
+ i = 0;
+ while (limit - i >= 4) {
+ struct {
+ UINT8 type;
+ UINT8 subtype;
+ UINT16 len;
+ } dp = {
+ .type = cur[i],
+ .subtype = cur[i+1],
+ /*
+ * it's a little endian UINT16, but we're not
+ * guaranteed alignment is sane, so we can't just
+ * typecast it directly.
+ */
+ .len = (cur[i+3] << 8) | cur[i+2],
+ };
+
+ /*
+ * We haven't found an EndEntire, so this has to be a valid
+ * EFI_DEVICE_PATH in order for the data to be valid. That
+ * means it has to fit, and it can't be smaller than 4 bytes.
+ */
+ if (dp.len < 4 || dp.len > limit)
+ return EFI_INVALID_PARAMETER;
+
+ /*
+ * see if this is an EndEntire node...
+ */
+ if (dp.type == 0x7f && dp.subtype == 0xff) {
+ /*
+ * if we've found the EndEntire node, it must be 4
+ * bytes
+ */
+ if (dp.len != 4)
+ return EFI_INVALID_PARAMETER;
+
+ i += dp.len;
+ break;
+ }
+
+ /*
+ * It's just some random DP node; skip it.
+ */
+ i += dp.len;
+ }
+ if (i != fplistlen)
+ return EFI_INVALID_PARAMETER;
+
+ /*
+ * if there's any space left, it's "optional data"
+ */
+ *od = cur + i;
+ *ods = limit - i;
+ return EFI_SUCCESS;
+}
+
+static int
+is_our_path(EFI_LOADED_IMAGE *li, CHAR16 *path)
+{
+ CHAR16 *dppath = NULL;
+ CHAR16 *PathName = NULL;
+ EFI_STATUS efi_status;
+ int ret = 1;
+
+ dppath = DevicePathToStr(li->FilePath);
+ if (!dppath)
+ return 0;
+
+ efi_status = generate_path_from_image_path(li, path, &PathName);
+ if (EFI_ERROR(efi_status)) {
+ perror(L"Unable to generate path %s: %r\n", path,
+ efi_status);
+ goto done;
+ }
+
+ dprint(L"dppath: %s\n", dppath);
+ dprint(L"path: %s\n", path);
+ if (StrnCaseCmp(dppath, PathName, StrLen(dppath)))
+ ret = 0;
+
+done:
+ FreePool(dppath);
+ FreePool(PathName);
+ return ret;
+}
+
+/*
+ * Split the supplied load options in to a NULL terminated
+ * string representing the path of the second stage loader,
+ * and return a pointer to the remaining load options data
+ * and its remaining size.
+ *
+ * This expects the supplied load options to begin with a
+ * string that is either NULL terminated or terminated with
+ * a space and some optional data. It will return NULL if
+ * the supplied load options contains no spaces or NULL
+ * terminators.
+ */
+static CHAR16 *
+split_load_options(VOID *in, UINT32 in_size,
+ VOID **remaining,
+ UINT32 *remaining_size) {
+ UINTN i;
+ CHAR16 *arg0 = NULL;
+ CHAR16 *start = (CHAR16 *)in;
+
+ /* Skip spaces */
+ for (i = 0; i < in_size / sizeof(CHAR16); i++) {
+ if (*start != L' ')
+ break;
+
+ start++;
+ }
+
+ in_size -= ((VOID *)start - in);
+
+ /*
+ * Ensure that the first argument is NULL terminated by
+ * replacing L' ' with L'\0'.
+ */
+ for (i = 0; i < in_size / sizeof(CHAR16); i++) {
+ if (start[i] == L' ' || start[i] == L'\0') {
+ start[i] = L'\0';
+ arg0 = (CHAR16 *)start;
+ break;
+ }
+ }
+
+ if (arg0) {
+ UINTN skip = i + 1;
+ *remaining_size = in_size - (skip * sizeof(CHAR16));
+ *remaining = *remaining_size > 0 ? start + skip : NULL;
+ }
+
+ return arg0;
+}
+
+/*
+ * Check the load options to specify the second stage loader
+ */
+EFI_STATUS
+parse_load_options(EFI_LOADED_IMAGE *li)
+{
+ EFI_STATUS efi_status;
+ VOID *remaining = NULL;
+ UINT32 remaining_size;
+ CHAR16 *loader_str = NULL;
+
+ /* Sanity check since we make several assumptions about the length */
+ if (li->LoadOptionsSize % 2 != 0)
+ return EFI_INVALID_PARAMETER;
+
+ /* So, load options are a giant pain in the ass. If we're invoked
+ * from the EFI shell, we get something like this:
+
+00000000 5c 00 45 00 36 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.|
+00000010 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 6d 00 |o.r.a.\.s.h.i.m.|
+00000020 78 00 36 00 34 00 2e 00 64 00 66 00 69 00 20 00 |x.6.4...e.f.i. .|
+00000030 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.|
+00000040 6f 00 72 00 61 00 5c 00 66 00 77 00 75 00 70 00 |o.r.a.\.f.w.u.p.|
+00000050 64 00 61 00 74 00 65 00 2e 00 65 00 66 00 20 00 |d.a.t.e.e.f.i. .|
+00000060 00 00 66 00 73 00 30 00 3a 00 5c 00 00 00 |..f.s.0.:.\...|
+
+ *
+ * which is just some paths rammed together separated by a UCS-2 NUL.
+ * But if we're invoked from BDS, we get something more like:
+ *
+
+00000000 01 00 00 00 62 00 4c 00 69 00 6e 00 75 00 78 00 |....b.L.i.n.u.x.|
+00000010 20 00 46 00 69 00 72 00 6d 00 77 00 61 00 72 00 | .F.i.r.m.w.a.r.|
+00000020 65 00 20 00 55 00 70 00 64 00 61 00 74 00 65 00 |e. .U.p.d.a.t.e.|
+00000030 72 00 00 00 40 01 2a 00 01 00 00 00 00 08 00 00 |r.....*.........|
+00000040 00 00 00 00 00 40 06 00 00 00 00 00 1a 9e 55 bf |.....@........U.|
+00000050 04 57 f2 4f b4 4a ed 26 4a 40 6a 94 02 02 04 04 |.W.O.:.&J@j.....|
+00000060 34 00 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 |4.\.E.F.I.f.e.d.|
+00000070 64 00 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 |o.r.a.\.s.h.i.m.|
+00000080 6d 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |x.6.4...e.f.i...|
+00000090 00 00 7f ff 40 00 20 00 5c 00 66 00 77 00 75 00 |...... .\.f.w.u.|
+000000a0 70 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |p.x.6.4...e.f.i.|
+000000b0 00 00 |..|
+
+ *
+ * which is clearly an EFI_LOAD_OPTION filled in halfway reasonably.
+ * In short, the UEFI shell is still a useless piece of junk.
+ *
+ * But then on some versions of BDS, we get:
+
+00000000 5c 00 66 00 77 00 75 00 70 00 78 00 36 00 34 00 |\.f.w.u.p.x.6.4.|
+00000010 2e 00 65 00 66 00 69 00 00 00 |..e.f.i...|
+0000001a
+
+ * which as you can see is one perfectly normal UCS2-EL string
+ * containing the load option from the Boot#### variable.
+ *
+ * We also sometimes find a guid or partial guid at the end, because
+ * BDS will add that, but we ignore that here.
+ */
+
+ /*
+ * Maybe there just aren't any options...
+ */
+ if (li->LoadOptionsSize == 0)
+ return EFI_SUCCESS;
+
+ /*
+ * In either case, we've got to have at least a UCS2 NUL...
+ */
+ if (li->LoadOptionsSize < 2)
+ return EFI_BAD_BUFFER_SIZE;
+
+ /*
+ * Some awesome versions of BDS will add entries for Linux. On top
+ * of that, some versions of BDS will "tag" any Boot#### entries they
+ * create by putting a GUID at the very end of the optional data in
+ * the EFI_LOAD_OPTIONS, thus screwing things up for everybody who
+ * tries to actually *use* the optional data for anything. Why they
+ * did this instead of adding a flag to the spec to /say/ it's
+ * created by BDS, I do not know. For shame.
+ *
+ * Anyway, just nerf that out from the start. It's always just
+ * garbage at the end.
+ */
+ if (li->LoadOptionsSize > 16) {
+ if (CompareGuid((EFI_GUID *)(li->LoadOptions
+ + (li->LoadOptionsSize - 16)),
+ &BDS_GUID) == 0)
+ li->LoadOptionsSize -= 16;
+ }
+
+ /*
+ * Apparently sometimes we get L"\0\0"? Which isn't useful at all.
+ */
+ if (is_all_nuls(li->LoadOptions, li->LoadOptionsSize))
+ return EFI_SUCCESS;
+
+ /*
+ * See if this is an EFI_LOAD_OPTION and extract the optional
+ * data if it is. This will return an error if it is not a valid
+ * EFI_LOAD_OPTION.
+ */
+ efi_status = get_load_option_optional_data(li->LoadOptions,
+ li->LoadOptionsSize,
+ &li->LoadOptions,
+ &li->LoadOptionsSize);
+ if (EFI_ERROR(efi_status)) {
+ /*
+ * it's not an EFI_LOAD_OPTION, so it's probably just a string
+ * or list of strings.
+ *
+ * UEFI shell copies the whole line of the command into
+ * LoadOptions. We ignore the first string, i.e. the name of this
+ * program in this case.
+ */
+ CHAR16 *loader_str = split_load_options(li->LoadOptions,
+ li->LoadOptionsSize,
+ &remaining,
+ &remaining_size);
+
+ if (loader_str && is_our_path(li, loader_str)) {
+ li->LoadOptions = remaining;
+ li->LoadOptionsSize = remaining_size;
+ }
+ }
+
+ loader_str = split_load_options(li->LoadOptions, li->LoadOptionsSize,
+ &remaining, &remaining_size);
+
+ /*
+ * Set up the name of the alternative loader and the LoadOptions for
+ * the loader
+ */
+ if (loader_str) {
+ second_stage = loader_str;
+ load_options = remaining;
+ load_options_size = remaining_size;
+ }
+
+ return EFI_SUCCESS;
+}
+
+// vim:fenc=utf-8:tw=75:noet
diff --git a/shim.c b/shim.c
index 2ae60243..94a51768 100644
--- a/shim.c
+++ b/shim.c
@@ -40,10 +40,6 @@ static EFI_HANDLE global_image_handle;
static EFI_LOADED_IMAGE *shim_li;
static EFI_LOADED_IMAGE shim_li_bak;
-static CHAR16 *second_stage;
-void *load_options;
-UINT32 load_options_size;
-
list_t sbat_var;
/*
@@ -788,99 +784,6 @@ error:
return ret;
}
-/*
- * Generate the path of an executable given shim's path and the name
- * of the executable
- */
-static EFI_STATUS generate_path_from_image_path(EFI_LOADED_IMAGE *li,
- CHAR16 *ImagePath,
- CHAR16 **PathName)
-{
- EFI_DEVICE_PATH *devpath;
- unsigned int i;
- int j, last = -1;
- unsigned int pathlen = 0;
- EFI_STATUS efi_status = EFI_SUCCESS;
- CHAR16 *bootpath;
-
- /*
- * Suuuuper lazy technique here, but check and see if this is a full
- * path to something on the ESP. Backwards compatibility demands
- * that we don't just use \\, because we (not particularly brightly)
- * used to require that the relative file path started with that.
- *
- * If it is a full path, don't try to merge it with the directory
- * from our Loaded Image handle.
- */
- if (StrSize(ImagePath) > 5 && StrnCmp(ImagePath, L"\\EFI\\", 5) == 0) {
- *PathName = StrDuplicate(ImagePath);
- if (!*PathName) {
- perror(L"Failed to allocate path buffer\n");
- return EFI_OUT_OF_RESOURCES;
- }
- return EFI_SUCCESS;
- }
-
- devpath = li->FilePath;
-
- bootpath = DevicePathToStr(devpath);
-
- pathlen = StrLen(bootpath);
-
- /*
- * DevicePathToStr() concatenates two nodes with '/'.
- * Convert '/' to '\\'.
- */
- for (i = 0; i < pathlen; i++) {
- if (bootpath[i] == '/')
- bootpath[i] = '\\';
- }
-
- for (i=pathlen; i>0; i--) {
- if (bootpath[i] == '\\' && bootpath[i-1] == '\\')
- bootpath[i] = '/';
- else if (last == -1 && bootpath[i] == '\\')
- last = i;
- }
-
- if (last == -1 && bootpath[0] == '\\')
- last = 0;
- bootpath[last+1] = '\0';
-
- if (last > 0) {
- for (i = 0, j = 0; bootpath[i] != '\0'; i++) {
- if (bootpath[i] != '/') {
- bootpath[j] = bootpath[i];
- j++;
- }
- }
- bootpath[j] = '\0';
- }
-
- for (i = 0, last = 0; i < StrLen(ImagePath); i++)
- if (ImagePath[i] == '\\')
- last = i + 1;
-
- ImagePath = ImagePath + last;
- *PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath));
-
- if (!*PathName) {
- perror(L"Failed to allocate path buffer\n");
- efi_status = EFI_OUT_OF_RESOURCES;
- goto error;
- }
-
- *PathName[0] = '\0';
- if (StrnCaseCmp(bootpath, ImagePath, StrLen(bootpath)))
- StrCat(*PathName, bootpath);
- StrCat(*PathName, ImagePath);
-
-error:
- FreePool(bootpath);
-
- return efi_status;
-}
-
/*
* Open the second stage bootloader and read it into a buffer
*/
@@ -1241,201 +1144,6 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle)
return efi_status;
}
-/*
- * Extract the OptionalData and OptionalData fields from an
- * EFI_LOAD_OPTION.
- */
-static inline EFI_STATUS
-get_load_option_optional_data(VOID *data, UINT32 data_size,
- VOID **od, UINT32 *ods)
-{
- /*
- * If it's not at least Attributes + FilePathListLength +
- * Description=L"" + 0x7fff0400 (EndEntrireDevicePath), it can't
- * be valid.
- */
- if (data_size < (sizeof(UINT32) + sizeof(UINT16) + 2 + 4))
- return EFI_INVALID_PARAMETER;
-
- UINT8 *start = (UINT8 *)data;
- UINT8 *cur = start + sizeof(UINT32);
- UINT16 fplistlen = *(UINT16 *)cur;
- /*
- * If there's not enough space for the file path list and the
- * smallest possible description (L""), it's not valid.
- */
- if (fplistlen > data_size - (sizeof(UINT32) + 2 + 4))
- return EFI_INVALID_PARAMETER;
-
- cur += sizeof(UINT16);
- UINT32 limit = data_size - (cur - start) - fplistlen;
- UINT32 i;
- for (i = 0; i < limit ; i++) {
- /* If the description isn't valid UCS2-LE, it's not valid. */
- if (i % 2 != 0) {
- if (cur[i] != 0)
- return EFI_INVALID_PARAMETER;
- } else if (cur[i] == 0) {
- /* we've found the end */
- i++;
- if (i >= limit || cur[i] != 0)
- return EFI_INVALID_PARAMETER;
- break;
- }
- }
- i++;
- if (i > limit)
- return EFI_INVALID_PARAMETER;
-
- /*
- * If i is limit, we know the rest of this is the FilePathList and
- * there's no optional data. So just bail now.
- */
- if (i == limit) {
- *od = NULL;
- *ods = 0;
- return EFI_SUCCESS;
- }
-
- cur += i;
- limit -= i;
- limit += fplistlen;
- i = 0;
- while (limit - i >= 4) {
- struct {
- UINT8 type;
- UINT8 subtype;
- UINT16 len;
- } dp = {
- .type = cur[i],
- .subtype = cur[i+1],
- /*
- * it's a little endian UINT16, but we're not
- * guaranteed alignment is sane, so we can't just
- * typecast it directly.
- */
- .len = (cur[i+3] << 8) | cur[i+2],
- };
-
- /*
- * We haven't found an EndEntire, so this has to be a valid
- * EFI_DEVICE_PATH in order for the data to be valid. That
- * means it has to fit, and it can't be smaller than 4 bytes.
- */
- if (dp.len < 4 || dp.len > limit)
- return EFI_INVALID_PARAMETER;
-
- /*
- * see if this is an EndEntire node...
- */
- if (dp.type == 0x7f && dp.subtype == 0xff) {
- /*
- * if we've found the EndEntire node, it must be 4
- * bytes
- */
- if (dp.len != 4)
- return EFI_INVALID_PARAMETER;
-
- i += dp.len;
- break;
- }
-
- /*
- * It's just some random DP node; skip it.
- */
- i += dp.len;
- }
- if (i != fplistlen)
- return EFI_INVALID_PARAMETER;
-
- /*
- * if there's any space left, it's "optional data"
- */
- *od = cur + i;
- *ods = limit - i;
- return EFI_SUCCESS;
-}
-
-static int is_our_path(EFI_LOADED_IMAGE *li, CHAR16 *path)
-{
- CHAR16 *dppath = NULL;
- CHAR16 *PathName = NULL;
- EFI_STATUS efi_status;
- int ret = 1;
-
- dppath = DevicePathToStr(li->FilePath);
- if (!dppath)
- return 0;
-
- efi_status = generate_path_from_image_path(li, path, &PathName);
- if (EFI_ERROR(efi_status)) {
- perror(L"Unable to generate path %s: %r\n", path,
- efi_status);
- goto done;
- }
-
- dprint(L"dppath: %s\n", dppath);
- dprint(L"path: %s\n", path);
- if (StrnCaseCmp(dppath, PathName, StrLen(dppath)))
- ret = 0;
-
-done:
- FreePool(dppath);
- FreePool(PathName);
- return ret;
-}
-
-/*
- * Split the supplied load options in to a NULL terminated
- * string representing the path of the second stage loader,
- * and return a pointer to the remaining load options data
- * and its remaining size.
- *
- * This expects the supplied load options to begin with a
- * string that is either NULL terminated or terminated with
- * a space and some optional data. It will return NULL if
- * the supplied load options contains no spaces or NULL
- * terminators.
- */
-static CHAR16 *
-split_load_options(VOID *in, UINT32 in_size,
- VOID **remaining,
- UINT32 *remaining_size) {
- UINTN i;
- CHAR16 *arg0 = NULL;
- CHAR16 *start = (CHAR16 *)in;
-
- /* Skip spaces */
- for (i = 0; i < in_size / sizeof(CHAR16); i++) {
- if (*start != L' ')
- break;
-
- start++;
- }
-
- in_size -= ((VOID *)start - in);
-
- /*
- * Ensure that the first argument is NULL terminated by
- * replacing L' ' with L'\0'.
- */
- for (i = 0; i < in_size / sizeof(CHAR16); i++) {
- if (start[i] == L' ' || start[i] == L'\0') {
- start[i] = L'\0';
- arg0 = (CHAR16 *)start;
- break;
- }
- }
-
- if (arg0) {
- UINTN skip = i + 1;
- *remaining_size = in_size - (skip * sizeof(CHAR16));
- *remaining = *remaining_size > 0 ? start + skip : NULL;
- }
-
- return arg0;
-}
-
/*
* Check the load options to specify the second stage loader
*/
@@ -1443,9 +1151,6 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
{
EFI_STATUS efi_status;
EFI_LOADED_IMAGE *li = NULL;
- VOID *remaining = NULL;
- UINT32 remaining_size;
- CHAR16 *loader_str = NULL;
second_stage = DEFAULT_LOADER;
load_options = NULL;
@@ -1458,133 +1163,10 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
return efi_status;
}
- /* Sanity check since we make several assumptions about the length */
- if (li->LoadOptionsSize % 2 != 0)
- return EFI_INVALID_PARAMETER;
-
- /* So, load options are a giant pain in the ass. If we're invoked
- * from the EFI shell, we get something like this:
-
-00000000 5c 00 45 00 36 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.|
-00000010 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 6d 00 |o.r.a.\.s.h.i.m.|
-00000020 78 00 36 00 34 00 2e 00 64 00 66 00 69 00 20 00 |x.6.4...e.f.i. .|
-00000030 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 64 00 |\.E.F.I.\.f.e.d.|
-00000040 6f 00 72 00 61 00 5c 00 66 00 77 00 75 00 70 00 |o.r.a.\.f.w.u.p.|
-00000050 64 00 61 00 74 00 65 00 2e 00 65 00 66 00 20 00 |d.a.t.e.e.f.i. .|
-00000060 00 00 66 00 73 00 30 00 3a 00 5c 00 00 00 |..f.s.0.:.\...|
-
- *
- * which is just some paths rammed together separated by a UCS-2 NUL.
- * But if we're invoked from BDS, we get something more like:
- *
-
-00000000 01 00 00 00 62 00 4c 00 69 00 6e 00 75 00 78 00 |....b.L.i.n.u.x.|
-00000010 20 00 46 00 69 00 72 00 6d 00 77 00 61 00 72 00 | .F.i.r.m.w.a.r.|
-00000020 65 00 20 00 55 00 70 00 64 00 61 00 74 00 65 00 |e. .U.p.d.a.t.e.|
-00000030 72 00 00 00 40 01 2a 00 01 00 00 00 00 08 00 00 |r.....*.........|
-00000040 00 00 00 00 00 40 06 00 00 00 00 00 1a 9e 55 bf |.....@........U.|
-00000050 04 57 f2 4f b4 4a ed 26 4a 40 6a 94 02 02 04 04 |.W.O.:.&J@j.....|
-00000060 34 00 5c 00 45 00 46 00 49 00 5c 00 66 00 65 00 |4.\.E.F.I.f.e.d.|
-00000070 64 00 6f 00 72 00 61 00 5c 00 73 00 68 00 69 00 |o.r.a.\.s.h.i.m.|
-00000080 6d 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |x.6.4...e.f.i...|
-00000090 00 00 7f ff 40 00 20 00 5c 00 66 00 77 00 75 00 |...... .\.f.w.u.|
-000000a0 70 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 |p.x.6.4...e.f.i.|
-000000b0 00 00 |..|
-
- *
- * which is clearly an EFI_LOAD_OPTION filled in halfway reasonably.
- * In short, the UEFI shell is still a useless piece of junk.
- *
- * But then on some versions of BDS, we get:
-
-00000000 5c 00 66 00 77 00 75 00 70 00 78 00 36 00 34 00 |\.f.w.u.p.x.6.4.|
-00000010 2e 00 65 00 66 00 69 00 00 00 |..e.f.i...|
-0000001a
-
- * which as you can see is one perfectly normal UCS2-EL string
- * containing the load option from the Boot#### variable.
- *
- * We also sometimes find a guid or partial guid at the end, because
- * BDS will add that, but we ignore that here.
- */
-
- /*
- * Maybe there just aren't any options...
- */
- if (li->LoadOptionsSize == 0)
- return EFI_SUCCESS;
-
- /*
- * In either case, we've got to have at least a UCS2 NUL...
- */
- if (li->LoadOptionsSize < 2)
- return EFI_BAD_BUFFER_SIZE;
-
- /*
- * Some awesome versions of BDS will add entries for Linux. On top
- * of that, some versions of BDS will "tag" any Boot#### entries they
- * create by putting a GUID at the very end of the optional data in
- * the EFI_LOAD_OPTIONS, thus screwing things up for everybody who
- * tries to actually *use* the optional data for anything. Why they
- * did this instead of adding a flag to the spec to /say/ it's
- * created by BDS, I do not know. For shame.
- *
- * Anyway, just nerf that out from the start. It's always just
- * garbage at the end.
- */
- if (li->LoadOptionsSize > 16) {
- if (CompareGuid((EFI_GUID *)(li->LoadOptions
- + (li->LoadOptionsSize - 16)),
- &BDS_GUID) == 0)
- li->LoadOptionsSize -= 16;
- }
-
- /*
- * Apparently sometimes we get L"\0\0"? Which isn't useful at all.
- */
- if (is_all_nuls(li->LoadOptions, li->LoadOptionsSize))
- return EFI_SUCCESS;
-
- /*
- * See if this is an EFI_LOAD_OPTION and extract the optional
- * data if it is. This will return an error if it is not a valid
- * EFI_LOAD_OPTION.
- */
- efi_status = get_load_option_optional_data(li->LoadOptions,
- li->LoadOptionsSize,
- &li->LoadOptions,
- &li->LoadOptionsSize);
+ efi_status = parse_load_options(li);
if (EFI_ERROR(efi_status)) {
- /*
- * it's not an EFI_LOAD_OPTION, so it's probably just a string
- * or list of strings.
- *
- * UEFI shell copies the whole line of the command into
- * LoadOptions. We ignore the first string, i.e. the name of this
- * program in this case.
- */
- CHAR16 *loader_str = split_load_options(li->LoadOptions,
- li->LoadOptionsSize,
- &remaining,
- &remaining_size);
-
- if (loader_str && is_our_path(li, loader_str)) {
- li->LoadOptions = remaining;
- li->LoadOptionsSize = remaining_size;
- }
- }
-
- loader_str = split_load_options(li->LoadOptions, li->LoadOptionsSize,
- &remaining, &remaining_size);
-
- /*
- * Set up the name of the alternative loader and the LoadOptions for
- * the loader
- */
- if (loader_str) {
- second_stage = loader_str;
- load_options = remaining;
- load_options_size = remaining_size;
+ perror (L"Failed to get load options: %r\n", efi_status);
+ return efi_status;
}
return EFI_SUCCESS;
diff --git a/shim.h b/shim.h
index 69ad2cc3..94b48bcc 100644
--- a/shim.h
+++ b/shim.h
@@ -167,6 +167,7 @@
#include "include/httpboot.h"
#include "include/ip4config2.h"
#include "include/ip6config.h"
+#include "include/load-options.h"
#include "include/netboot.h"
#include "include/passwordcrypt.h"
#include "include/peimage.h"
--
2.32.0