Page MenuHomeFreeBSD

D16403.id45719.diff
No OneTemporary

D16403.id45719.diff

Index: UPDATING
===================================================================
--- UPDATING
+++ 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: stand/efi/include/efilib.h
===================================================================
--- stand/efi/include/efilib.h
+++ stand/efi/include/efilib.h
@@ -66,6 +66,7 @@
pdinfo_list_t *efiblk_get_pdinfo_list(struct devsw *dev);
pdinfo_t *efiblk_get_pdinfo(struct devdesc *dev);
pdinfo_t *efiblk_get_pdinfo_by_handle(EFI_HANDLE h);
+pdinfo_t *efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path);
void *efi_get_table(EFI_GUID *tbl);
@@ -85,9 +86,12 @@
EFI_DEVICE_PATH *efi_devpath_last_node(EFI_DEVICE_PATH *);
EFI_DEVICE_PATH *efi_devpath_trim(EFI_DEVICE_PATH *);
bool efi_devpath_match(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *);
+bool efi_devpath_match_node(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *);
bool efi_devpath_is_prefix(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *);
CHAR16 *efi_devpath_name(EFI_DEVICE_PATH *);
void efi_free_devpath_name(CHAR16 *);
+EFI_DEVICE_PATH *efi_devpath_to_media_path(EFI_DEVICE_PATH *);
+UINTN efi_devpath_length(EFI_DEVICE_PATH *);
int efi_status_to_errno(EFI_STATUS);
EFI_STATUS errno_to_efi_status(int errno);
Index: stand/efi/libefi/devpath.c
===================================================================
--- stand/efi/libefi/devpath.c
+++ stand/efi/libefi/devpath.c
@@ -140,25 +140,33 @@
}
bool
-efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
+efi_devpath_match_node(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
{
size_t len;
if (devpath1 == NULL || devpath2 == NULL)
return (false);
+ if (DevicePathType(devpath1) != DevicePathType(devpath2) ||
+ DevicePathSubType(devpath1) != DevicePathSubType(devpath2))
+ return (false);
+ len = DevicePathNodeLength(devpath1);
+ if (len != DevicePathNodeLength(devpath2))
+ return (false);
+ if (memcmp(devpath1, devpath2, len) != 0)
+ return (false);
+ return (true);
+}
- while (true) {
- if (DevicePathType(devpath1) != DevicePathType(devpath2) ||
- DevicePathSubType(devpath1) != DevicePathSubType(devpath2))
- return (false);
-
- len = DevicePathNodeLength(devpath1);
- if (len != DevicePathNodeLength(devpath2))
- return (false);
+bool
+efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
+{
- if (memcmp(devpath1, devpath2, len) != 0)
- return (false);
+ if (devpath1 == NULL || devpath2 == NULL)
+ return (false);
+ while (true) {
+ if (!efi_devpath_match_node(devpath1, devpath2))
+ return false;
if (IsDevicePathEnd(devpath1))
break;
devpath1 = NextDevicePathNode(devpath1);
@@ -195,3 +203,29 @@
}
return (true);
}
+
+/*
+ * Skip over the 'prefix' part of path and return the part of the path
+ * that starts with the first node that's a MEDIA_DEVICE_PATH.
+ */
+EFI_DEVICE_PATH *
+efi_devpath_to_media_path(EFI_DEVICE_PATH *path)
+{
+
+ while (!IsDevicePathEnd(path)) {
+ if (DevicePathType(path) == MEDIA_DEVICE_PATH)
+ return (path);
+ path = NextDevicePathNode(path);
+ }
+ return (NULL);
+}
+
+UINTN
+efi_devpath_length(EFI_DEVICE_PATH *path)
+{
+ EFI_DEVICE_PATH *start = path;
+
+ while (!IsDevicePathEnd(path))
+ path = NextDevicePathNode(path);
+ return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path);
+}
Index: stand/efi/libefi/efipart.c
===================================================================
--- stand/efi/libefi/efipart.c
+++ stand/efi/libefi/efipart.c
@@ -137,6 +137,28 @@
return (pd);
}
+pdinfo_t *
+efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path)
+{
+ unsigned i;
+ EFI_DEVICE_PATH *media, *devpath;
+ EFI_HANDLE h;
+
+ media = efi_devpath_to_media_path(path);
+ if (media == NULL)
+ return (NULL);
+ for (i = 0; i < efipart_nhandles; i++) {
+ h = efipart_handles[i];
+ devpath = efi_lookup_devpath(h);
+ if (devpath == NULL)
+ continue;
+ if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath)))
+ continue;
+ return (efiblk_get_pdinfo_by_handle(h));
+ }
+ return (NULL);
+}
+
static bool
same_handle(pdinfo_t *pd, EFI_HANDLE h)
{
@@ -210,7 +232,7 @@
return (efi_status_to_errno(status));
efipart_handles = hin;
- efipart_nhandles = sz;
+ efipart_nhandles = sz / sizeof(*hin);
#ifdef EFIPART_DEBUG
printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__,
efipart_nhandles);
@@ -246,7 +268,7 @@
static bool
efipart_hdd(EFI_DEVICE_PATH *dp)
{
- unsigned i, nin;
+ unsigned i;
EFI_DEVICE_PATH *devpath, *node;
EFI_BLOCK_IO *blkio;
EFI_STATUS status;
@@ -264,8 +286,7 @@
* Test every EFI BLOCK IO handle to make sure dp is not device path
* for CD/DVD.
*/
- nin = efipart_nhandles / sizeof (*efipart_handles);
- for (i = 0; i < nin; i++) {
+ for (i = 0; i < efipart_nhandles; i++) {
devpath = efi_lookup_devpath(efipart_handles[i]);
if (devpath == NULL)
return (false);
@@ -340,10 +361,9 @@
{
EFI_DEVICE_PATH *devpath, *node;
ACPI_HID_DEVICE_PATH *acpi;
- int i, nin;
+ int i;
- nin = efipart_nhandles / sizeof (*efipart_handles);
- for (i = 0; i < nin; i++) {
+ for (i = 0; i < efipart_nhandles; i++) {
devpath = efi_lookup_devpath(efipart_handles[i]);
if (devpath == NULL)
continue;
@@ -410,14 +430,13 @@
static void
efipart_updatecd(void)
{
- int i, nin;
+ int i;
EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
EFI_HANDLE handle;
EFI_BLOCK_IO *blkio;
EFI_STATUS status;
- nin = efipart_nhandles / sizeof (*efipart_handles);
- for (i = 0; i < nin; i++) {
+ for (i = 0; i < efipart_nhandles; i++) {
devpath = efi_lookup_devpath(efipart_handles[i]);
if (devpath == NULL)
continue;
@@ -666,14 +685,13 @@
static void
efipart_updatehd(void)
{
- int i, nin;
+ int i;
EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
EFI_HANDLE handle;
EFI_BLOCK_IO *blkio;
EFI_STATUS status;
- nin = efipart_nhandles / sizeof (*efipart_handles);
- for (i = 0; i < nin; i++) {
+ for (i = 0; i < efipart_nhandles; i++) {
devpath = efi_lookup_devpath(efipart_handles[i]);
if (devpath == NULL)
continue;
Index: stand/efi/loader/main.c
===================================================================
--- stand/efi/loader/main.c
+++ 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,8 +281,144 @@
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;
@@ -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);
@@ -415,6 +582,12 @@
bool vargood;
char var[128];
+ /*
+ * XXX move this to a routine -- we'll need something akin to
+ * it for parsing /boot.config or /boot/config if there was no
+ * command line args passed in (not 100% on that last bit).
+ * But there's a chicken and egg problem with geli...
+ */
/*
* Parse the args to set the console settings, etc
* boot1.efi passes these in, if it can read /boot.config or /boot/config
@@ -529,15 +702,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 +844,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,18 +854,53 @@
}
}
+ 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
@@ -710,7 +920,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, Sep 2, 2:42 AM (3 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37880800
Default Alt Text
D16403.id45719.diff (16 KB)

Event Timeline