Page MenuHomeFreeBSD

D16403.diff
No OneTemporary

D16403.diff

Index: head/UPDATING
===================================================================
--- head/UPDATING
+++ head/UPDATING
@@ -31,6 +31,10 @@
disable the most expensive debugging functionality run
"ln -s 'abort:false,junk:false' /etc/malloc.conf".)
+20180723:
+ loader.efi has been augmented to participate more fully in the
+ UEFI boot manager protocol.
+
20180720:
zfsloader's functionality has now been folded into loader.
zfsloader is no longer necesasary once you've updated your
Index: head/stand/efi/loader/main.c
===================================================================
--- head/stand/efi/loader/main.c
+++ head/stand/efi/loader/main.c
@@ -1,6 +1,7 @@
/*-
* Copyright (c) 2008-2010 Rui Paulo
* Copyright (c) 2006 Marcel Moolenaar
+ * Copyright (c) 2018 Netflix, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,6 +42,7 @@
#include <efi.h>
#include <efilib.h>
+#include <efichar.h>
#include <uuid.h>
@@ -83,6 +85,11 @@
*/
static int fail_timeout = 5;
+/*
+ * Current boot variable
+ */
+UINT16 boot_current;
+
static bool
has_keyboard(void)
{
@@ -274,9 +281,145 @@
return (sanity_check_currdev());
}
+/*
+ * Sometimes we get filenames that are all upper case
+ * and/or have backslashes in them. Filter all this out
+ * if it looks like we need to do so.
+ */
+static void
+fix_dosisms(char *p)
+{
+ while (*p) {
+ if (isupper(*p))
+ *p = tolower(*p);
+ else if (*p == '\\')
+ *p = '/';
+ p++;
+ }
+}
+
+enum { BOOT_INFO_OK = 0, BAD_CHOICE = 1, NOT_SPECIFIC = 2 };
static int
-find_currdev(EFI_LOADED_IMAGE *img)
+match_boot_info(EFI_LOADED_IMAGE *img __unused, char *boot_info, size_t bisz)
{
+ uint32_t attr;
+ uint16_t fplen;
+ size_t len;
+ char *walker, *ep;
+ EFI_DEVICE_PATH *dp, *edp, *first_dp, *last_dp;
+ pdinfo_t *pp;
+ CHAR16 *descr;
+ char *kernel = NULL;
+ FILEPATH_DEVICE_PATH *fp;
+ struct stat st;
+
+ /*
+ * FreeBSD encodes it's boot loading path into the boot loader
+ * BootXXXX variable. We look for the last one in the path
+ * and use that to load the kernel. However, if we only fine
+ * one DEVICE_PATH, then there's nothing specific and we should
+ * fall back.
+ *
+ * In an ideal world, we'd look at the image handle we were
+ * passed, match up with the loader we are and then return the
+ * next one in the path. This would be most flexible and cover
+ * many chain booting scenarios where you need to use this
+ * boot loader to get to the next boot loader. However, that
+ * doesn't work. We rarely have the path to the image booted
+ * (just the device) so we can't count on that. So, we do the
+ * enxt best thing, we look through the device path(s) passed
+ * in the BootXXXX varaible. If there's only one, we return
+ * NOT_SPECIFIC. Otherwise, we look at the last one and try to
+ * load that. If we can, we return BOOT_INFO_OK. Otherwise we
+ * return BAD_CHOICE for the caller to sort out.
+ */
+ if (bisz < sizeof(attr) + sizeof(fplen) + sizeof(CHAR16))
+ return NOT_SPECIFIC;
+ walker = boot_info;
+ ep = walker + bisz;
+ memcpy(&attr, walker, sizeof(attr));
+ walker += sizeof(attr);
+ memcpy(&fplen, walker, sizeof(fplen));
+ walker += sizeof(fplen);
+ descr = (CHAR16 *)(intptr_t)walker;
+ len = ucs2len(descr);
+ walker += (len + 1) * sizeof(CHAR16);
+ last_dp = first_dp = dp = (EFI_DEVICE_PATH *)walker;
+ edp = (EFI_DEVICE_PATH *)(walker + fplen);
+ if ((char *)edp > ep)
+ return NOT_SPECIFIC;
+ while (dp < edp) {
+ last_dp = dp;
+ dp = (EFI_DEVICE_PATH *)((char *)dp + efi_devpath_length(dp));
+ }
+
+ /*
+ * If there's only one item in the list, then nothing was
+ * specified.
+ */
+ if (last_dp == first_dp)
+ return NOT_SPECIFIC;
+
+ /*
+ * OK. At this point we either have a good path or a bad one.
+ * Let's check.
+ */
+ pp = efiblk_get_pdinfo_by_device_path(last_dp);
+ if (pp == NULL)
+ return BAD_CHOICE;
+ set_currdev_pdinfo(pp);
+ if (!sanity_check_currdev())
+ return BAD_CHOICE;
+
+ /*
+ * OK. We've found a device that matches, next we need to check the last
+ * component of the path. If it's a file, then we set the default kernel
+ * to that. Otherwise, just use this as the default root.
+ *
+ * Reminder: we're running very early, before we've parsed the defaults
+ * file, so we may need to have a hack override.
+ */
+ dp = efi_devpath_last_node(last_dp);
+ if (DevicePathType(dp) != MEDIA_DEVICE_PATH ||
+ DevicePathSubType(dp) != MEDIA_FILEPATH_DP)
+ return (BOOT_INFO_OK); /* use currdir, default kernel */
+ fp = (FILEPATH_DEVICE_PATH *)dp;
+ ucs2_to_utf8(fp->PathName, &kernel);
+ if (kernel == NULL)
+ return (BAD_CHOICE);
+ if (*kernel == '\\' || isupper(*kernel))
+ fix_dosisms(kernel);
+ if (stat(kernel, &st) != 0) {
+ free(kernel);
+ return (BAD_CHOICE);
+ }
+ setenv("kernel", kernel, 1);
+ free(kernel);
+
+ return (BOOT_INFO_OK);
+}
+
+/*
+ * Look at the passed-in boot_info, if any. If we find it then we need
+ * to see if we can find ourselves in the boot chain. If we can, and
+ * there's another specified thing to boot next, assume that the file
+ * is loaded from / and use that for the root filesystem. If can't
+ * find the specified thing, we must fail the boot. If we're last on
+ * the list, then we fallback to looking for the first available /
+ * candidate (ZFS, if there's a bootable zpool, otherwise a UFS
+ * partition that has either /boot/defaults/loader.conf on it or
+ * /boot/kernel/kernel (the default kernel) that we can use.
+ *
+ * We always fail if we can't find the right thing. However, as
+ * a concession to buggy UEFI implementations, like u-boot, if
+ * we have determined that the host is violating the UEFI boot
+ * manager protocol, we'll signal the rest of the program that
+ * a drop to the OK boot loader prompt is possible.
+ */
+static int
+find_currdev(EFI_LOADED_IMAGE *img, bool do_bootmgr, bool is_last,
+ char *boot_info, size_t boot_info_sz)
+{
pdinfo_t *dp, *pp;
EFI_DEVICE_PATH *devpath, *copy;
EFI_HANDLE h;
@@ -284,8 +427,13 @@
struct devsw *dev;
int unit;
uint64_t extra;
+ int rv;
char *rootdev;
+ /*
+ * First choice: if rootdev is already set, use that, even if
+ * it's wrong.
+ */
rootdev = getenv("rootdev");
if (rootdev != NULL) {
printf("Setting currdev to configured rootdev %s\n", rootdev);
@@ -293,6 +441,25 @@
return (0);
}
+ /*
+ * Second choice: If we can find out image boot_info, and there's
+ * a follow-on boot image in that boot_info, use that. In this
+ * case root will be the partition specified in that image and
+ * we'll load the kernel specified by the file path. Should there
+ * not be a filepath, we use the default. This filepath overrides
+ * loader.conf.
+ */
+ if (do_bootmgr) {
+ rv = match_boot_info(img, boot_info, boot_info_sz);
+ switch (rv) {
+ case BOOT_INFO_OK: /* We found it */
+ return (0);
+ case BAD_CHOICE: /* specified file not found -> error */
+ /* XXX do we want to have an escape hatch for last in boot order? */
+ return (ENOENT);
+ } /* Nothing specified, try normal match */
+ }
+
#ifdef EFI_ZFS_BOOT
/*
* Did efi_zfs_probe() detect the boot pool? If so, use the zpool
@@ -332,7 +499,7 @@
/*
* Roll up the ZFS special case
* for those partitions that have
- * zpools on them
+ * zpools on them.
*/
if (try_as_currdev(dp, pp))
return (0);
@@ -529,15 +696,17 @@
EFI_GUID *guid;
int howto, i, uhowto;
UINTN k;
- bool has_kbd;
+ bool has_kbd, is_last;
char *s;
EFI_DEVICE_PATH *imgpath;
CHAR16 *text;
- EFI_STATUS status;
- UINT16 boot_current;
- size_t sz;
+ EFI_STATUS rv;
+ size_t sz, bosz = 0, bisz = 0;
UINT16 boot_order[100];
+ char boot_info[4096];
EFI_LOADED_IMAGE *img;
+ char buf[32];
+ bool uefi_boot_mgr;
archsw.arch_autoload = efi_autoload;
archsw.arch_getdev = efi_getdev;
@@ -669,8 +838,8 @@
efi_free_devpath_name(text);
}
- status = BS->HandleProtocol(img->DeviceHandle, &devid, (void **)&imgpath);
- if (status == EFI_SUCCESS) {
+ rv = BS->HandleProtocol(img->DeviceHandle, &devid, (void **)&imgpath);
+ if (rv == EFI_SUCCESS) {
text = efi_devpath_name(imgpath);
if (text != NULL) {
printf(" Load Device: %S\n", text);
@@ -679,20 +848,55 @@
}
}
+ uefi_boot_mgr = true;
boot_current = 0;
sz = sizeof(boot_current);
- efi_global_getenv("BootCurrent", &boot_current, &sz);
- printf(" BootCurrent: %04x\n", boot_current);
+ rv = efi_global_getenv("BootCurrent", &boot_current, &sz);
+ if (rv == EFI_SUCCESS)
+ printf(" BootCurrent: %04x\n", boot_current);
+ else {
+ boot_current = 0xffff;
+ uefi_boot_mgr = false;
+ }
sz = sizeof(boot_order);
- efi_global_getenv("BootOrder", &boot_order, &sz);
- printf(" BootOrder:");
- for (i = 0; i < sz / sizeof(boot_order[0]); i++)
- printf(" %04x%s", boot_order[i],
- boot_order[i] == boot_current ? "[*]" : "");
- printf("\n");
+ rv = efi_global_getenv("BootOrder", &boot_order, &sz);
+ if (rv == EFI_SUCCESS) {
+ printf(" BootOrder:");
+ for (i = 0; i < sz / sizeof(boot_order[0]); i++)
+ printf(" %04x%s", boot_order[i],
+ boot_order[i] == boot_current ? "[*]" : "");
+ printf("\n");
+ is_last = boot_order[(sz / sizeof(boot_order[0])) - 1] == boot_current;
+ bosz = sz;
+ } else if (uefi_boot_mgr) {
+ /*
+ * u-boot doesn't set BootOrder, but otherwise participates in the
+ * boot manager protocol. So we fake it here and don't consider it
+ * a failure.
+ */
+ bosz = sizeof(boot_order[0]);
+ boot_order[0] = boot_current;
+ is_last = true;
+ }
/*
+ * Next, find the boot info structure the UEFI boot manager is
+ * supposed to setup. We need this so we can walk through it to
+ * find where we are in the booting process and what to try to
+ * boot next.
+ */
+ if (uefi_boot_mgr) {
+ snprintf(buf, sizeof(buf), "Boot%04X", boot_current);
+ sz = sizeof(boot_info);
+ rv = efi_global_getenv(buf, &boot_info, &sz);
+ if (rv == EFI_SUCCESS)
+ bisz = sz;
+ else
+ uefi_boot_mgr = false;
+ }
+
+ /*
* Disable the watchdog timer. By default the boot manager sets
* the timer to 5 minutes before invoking a boot option. If we
* want to return to the boot manager, we have to disable the
@@ -710,7 +914,7 @@
* the boot protocol and also allow an escape hatch for users wishing
* to try something different.
*/
- if (find_currdev(img) != 0)
+ if (find_currdev(img, uefi_boot_mgr, is_last, boot_info, bisz) != 0)
if (!interactive_interrupt("Failed to find bootable partition"))
return (EFI_NOT_FOUND);

File Metadata

Mime Type
text/plain
Expires
Wed, Mar 12, 9:41 PM (13 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17126522
Default Alt Text
D16403.diff (10 KB)

Event Timeline