Author: imp
Date: Wed Apr 11 19:46:24 2018
New Revision: 332416
URL: https://svnweb.freebsd.org/changeset/base/332416

Log:
  Refactor currdev setting
  
  Refactor the currdev setting to find the device we booted from. Limit
  searching when we don't already have a reasonable currdev from that to
  the same device only. Search a little harder for ZFS volumes as that's
  needed for loader.efi to live on an ESP.
  
  Sponsored by: Netflix
  Differential Review: https://reviews.freebsd.org/D13784

Modified:
  head/stand/efi/include/efilib.h
  head/stand/efi/include/efizfs.h
  head/stand/efi/libefi/efipart.c
  head/stand/efi/libefi/efizfs.c
  head/stand/efi/loader/main.c

Modified: head/stand/efi/include/efilib.h
==============================================================================
--- head/stand/efi/include/efilib.h     Wed Apr 11 19:28:54 2018        
(r332415)
+++ head/stand/efi/include/efilib.h     Wed Apr 11 19:46:24 2018        
(r332416)
@@ -59,10 +59,13 @@ typedef struct pdinfo
        uint32_t                pd_unit;        /* unit number */
        uint32_t                pd_open;        /* reference counter */
        void                    *pd_bcache;     /* buffer cache data */
+       struct pdinfo           *pd_parent;     /* Linked items (eg partitions) 
*/
+       struct devsw            *pd_devsw;      /* Back pointer to devsw */
 } pdinfo_t;
 
 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);
 
 void *efi_get_table(EFI_GUID *tbl);
 

Modified: head/stand/efi/include/efizfs.h
==============================================================================
--- head/stand/efi/include/efizfs.h     Wed Apr 11 19:28:54 2018        
(r332415)
+++ head/stand/efi/include/efizfs.h     Wed Apr 11 19:46:24 2018        
(r332416)
@@ -48,6 +48,7 @@ extern void efi_zfs_probe(void);
 extern zfsinfo_list_t *efizfs_get_zfsinfo_list(void);
 extern bool efi_zfs_is_preferred(EFI_HANDLE *h);
 extern EFI_HANDLE efizfs_get_handle_by_guid(uint64_t);
+extern bool efizfs_get_guid_by_handle(EFI_HANDLE, uint64_t *);
 
 #endif
 

Modified: head/stand/efi/libefi/efipart.c
==============================================================================
--- head/stand/efi/libefi/efipart.c     Wed Apr 11 19:28:54 2018        
(r332415)
+++ head/stand/efi/libefi/efipart.c     Wed Apr 11 19:46:24 2018        
(r332416)
@@ -119,6 +119,7 @@ efiblk_get_pdinfo_list(struct devsw *dev)
        return (NULL);
 }
 
+/* XXX this gets called way way too often, investigate */
 pdinfo_t *
 efiblk_get_pdinfo(struct devdesc *dev)
 {
@@ -136,6 +137,40 @@ efiblk_get_pdinfo(struct devdesc *dev)
        return (pd);
 }
 
+static bool
+same_handle(pdinfo_t *pd, EFI_HANDLE h)
+{
+
+       return (pd->pd_handle == h || pd->pd_alias == h);
+}
+
+pdinfo_t *
+efiblk_get_pdinfo_by_handle(EFI_HANDLE h)
+{
+       pdinfo_t *dp, *pp;
+
+       /*
+        * Check hard disks, then cd, then floppy
+        */
+       STAILQ_FOREACH(dp, &hdinfo, pd_link) {
+               if (same_handle(dp, h))
+                       return (dp);
+               STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
+                       if (same_handle(pp, h))
+                               return (pp);
+               }
+       }
+       STAILQ_FOREACH(dp, &cdinfo, pd_link) {
+               if (same_handle(dp, h))
+                       return (dp);
+       }
+       STAILQ_FOREACH(dp, &fdinfo, pd_link) {
+               if (same_handle(dp, h))
+                       return (dp);
+       }
+       return (NULL);
+}
+
 static int
 efiblk_pdinfo_count(pdinfo_list_t *pdi)
 {
@@ -294,6 +329,8 @@ efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EF
        fd->pd_unit = uid;
        fd->pd_handle = handle;
        fd->pd_devpath = devpath;
+       fd->pd_parent = NULL;
+       fd->pd_devsw = &efipart_fddev;
        STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
        return (0);
 }
@@ -364,6 +401,8 @@ efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias
        cd->pd_unit = unit;
        cd->pd_alias = alias;
        cd->pd_devpath = devpath;
+       cd->pd_parent = NULL;
+       cd->pd_devsw = &efipart_cddev;
        STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
        return (0);
 }
@@ -489,6 +528,8 @@ efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE 
                        pd->pd_handle = part_handle;
                        pd->pd_unit = node->PartitionNumber;
                        pd->pd_devpath = part_devpath;
+                       pd->pd_parent = hd;
+                       pd->pd_devsw = &efipart_hddev;
                        STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
                        return (0);
                }
@@ -505,6 +546,8 @@ efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE 
        hd->pd_handle = disk_handle;
        hd->pd_unit = unit;
        hd->pd_devpath = disk_devpath;
+       hd->pd_parent = NULL;
+       hd->pd_devsw = &efipart_hddev;
        STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
 
        if (part_devpath == NULL)
@@ -521,6 +564,8 @@ efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE 
        pd->pd_handle = part_handle;
        pd->pd_unit = node->PartitionNumber;
        pd->pd_devpath = part_devpath;
+       pd->pd_parent = hd;
+       pd->pd_devsw = &efipart_hddev;
        STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
 
        return (0);
@@ -579,6 +624,8 @@ efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
                pd->pd_handle = disk_handle;
                pd->pd_unit = unit;
                pd->pd_devpath = devpath;
+               pd->pd_parent = NULL;
+               pd->pd_devsw = &efipart_hddev;
                STAILQ_INSERT_TAIL(&hdinfo, pd, pd_link);
                free(pathname);
                return (0);
@@ -609,6 +656,8 @@ efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
        pd->pd_handle = disk_handle;
        pd->pd_unit = unit;
        pd->pd_devpath = devpath;
+       pd->pd_parent = last;
+       pd->pd_devsw = &efipart_hddev;
        STAILQ_INSERT_TAIL(&last->pd_part, pd, pd_link);
        free(pathname);
        return (0);

Modified: head/stand/efi/libefi/efizfs.c
==============================================================================
--- head/stand/efi/libefi/efizfs.c      Wed Apr 11 19:28:54 2018        
(r332415)
+++ head/stand/efi/libefi/efizfs.c      Wed Apr 11 19:46:24 2018        
(r332416)
@@ -64,6 +64,22 @@ efizfs_get_handle_by_guid(uint64_t guid)
        return (NULL);
 }
 
+bool
+efizfs_get_guid_by_handle(EFI_HANDLE handle, uint64_t *guid)
+{
+       zfsinfo_t *zi;
+
+       if (guid == NULL)
+               return (false);
+       STAILQ_FOREACH(zi, &zfsinfo, zi_link) {
+               if (zi->zi_handle == handle) {
+                       *guid = zi->zi_pool_guid;
+                       return (true);
+               }
+       }
+       return (false);
+}
+
 static void
 insert_zfs(EFI_HANDLE handle, uint64_t guid)
 {

Modified: head/stand/efi/loader/main.c
==============================================================================
--- head/stand/efi/loader/main.c        Wed Apr 11 19:28:54 2018        
(r332415)
+++ head/stand/efi/loader/main.c        Wed Apr 11 19:46:24 2018        
(r332416)
@@ -78,6 +78,15 @@ EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL;
 
 static EFI_LOADED_IMAGE *img;
 
+/*
+ * Number of seconds to wait for a keystroke before exiting with failure
+ * in the event no currdev is found. -2 means always break, -1 means
+ * never break, 0 means poll once and then reboot, > 0 means wait for
+ * that many seconds. "fail_timeout" can be set in the environment as
+ * well.
+ */
+static int fail_timeout = 5;
+
 #ifdef EFI_ZFS_BOOT
 bool
 efi_zfs_is_preferred(EFI_HANDLE *h)
@@ -169,113 +178,183 @@ out:
 }
 
 static void
-set_devdesc_currdev(struct devsw *dev, int unit)
+set_currdev_devdesc(struct devdesc *currdev)
 {
+       const char *devname;
+
+       devname = efi_fmtdev(currdev);
+
+       printf("Setting currdev to %s\n", devname);
+
+       env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, 
env_nounset);
+       env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset);
+}
+
+static void
+set_currdev_devsw(struct devsw *dev, int unit)
+{
        struct devdesc currdev;
-       char *devname;
 
        currdev.d_dev = dev;
        currdev.d_unit = unit;
+
+       set_currdev_devdesc(&currdev);
+}
+
+static void
+set_currdev_pdinfo(pdinfo_t *dp)
+{
+
+       /*
+        * Disks are special: they have partitions. if the parent
+        * pointer is non-null, we're a partition not a full disk
+        * and we need to adjust currdev appropriately.
+        */
+       if (dp->pd_devsw->dv_type == DEVT_DISK) {
+               struct disk_devdesc currdev;
+
+               currdev.dd.d_dev = dp->pd_devsw;
+               if (dp->pd_parent == NULL) {
+                       currdev.dd.d_unit = dp->pd_unit;
+                       currdev.d_slice = -1;
+                       currdev.d_partition = -1;
+               } else {
+                       currdev.dd.d_unit = dp->pd_parent->pd_unit;
+                       currdev.d_slice = dp->pd_unit;
+                       currdev.d_partition = 255;      /* Assumes GPT */
+               }
+               set_currdev_devdesc((struct devdesc *)&currdev);
+       } else {
+               set_currdev_devsw(dp->pd_devsw, dp->pd_unit);
+       }
+}
+
+static bool
+sanity_check_currdev(void)
+{
+       struct stat st;
+
+       return (stat("/boot/defaults/loader.conf", &st) == 0);
+}
+
+#ifdef EFI_ZFS_BOOT
+static bool
+probe_zfs_currdev(uint64_t guid)
+{
+       char *devname;
+       struct zfs_devdesc currdev;
+
+       currdev.dd.d_dev = &zfs_dev;
+       currdev.dd.d_unit = 0;
+       currdev.pool_guid = guid;
+       currdev.root_guid = 0;
+       set_currdev_devdesc((struct devdesc *)&currdev);
        devname = efi_fmtdev(&currdev);
+       init_zfs_bootenv(devname);
 
-       env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
-           env_nounset);
-       env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset);
+       return (sanity_check_currdev());
 }
+#endif
 
+static bool
+try_as_currdev(pdinfo_t *hd, pdinfo_t *pp)
+{
+       uint64_t guid;
+
+#ifdef EFI_ZFS_BOOT
+       /*
+        * If there's a zpool on this device, try it as a ZFS
+        * filesystem, which has somewhat different setup than all
+        * other types of fs due to imperfect loader integration.
+        * This all stems from ZFS being both a device (zpool) and
+        * a filesystem, plus the boot env feature.
+        */
+       if (efizfs_get_guid_by_handle(pp->pd_handle, &guid))
+               return (probe_zfs_currdev(guid));
+#endif
+       /*
+        * All other filesystems just need the pdinfo
+        * initialized in the standard way.
+        */
+       set_currdev_pdinfo(pp);
+       return (sanity_check_currdev());
+}
+
 static int
 find_currdev(EFI_LOADED_IMAGE *img)
 {
-       pdinfo_list_t *pdi_list;
        pdinfo_t *dp, *pp;
        EFI_DEVICE_PATH *devpath, *copy;
        EFI_HANDLE h;
-       char *devname;
+       CHAR16 *text;
        struct devsw *dev;
        int unit;
        uint64_t extra;
 
 #ifdef EFI_ZFS_BOOT
-       /* Did efi_zfs_probe() detect the boot pool? */
+       /*
+        * Did efi_zfs_probe() detect the boot pool? If so, use the zpool
+        * it found, if it's sane. ZFS is the only thing that looks for
+        * disks and pools to boot. This may change in the future, however,
+        * if we allow specifying which pool to boot from via UEFI variables
+        * rather than the bootenv stuff that FreeBSD uses today.
+        */
        if (pool_guid != 0) {
-               struct zfs_devdesc currdev;
-
-               currdev.dd.d_dev = &zfs_dev;
-               currdev.dd.d_unit = 0;
-               currdev.pool_guid = pool_guid;
-               currdev.root_guid = 0;
-               devname = efi_fmtdev(&currdev);
-
-               env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
-                   env_nounset);
-               env_setenv("loaddev", EV_VOLATILE, devname, env_noset,
-                   env_nounset);
-               init_zfs_bootenv(devname);
-               return (0);
+               printf("Trying ZFS pool\n");
+               if (probe_zfs_currdev(pool_guid))
+                       return (0);
        }
 #endif /* EFI_ZFS_BOOT */
 
-       /* We have device lists for hd, cd, fd, walk them all. */
-       pdi_list = efiblk_get_pdinfo_list(&efipart_hddev);
-       STAILQ_FOREACH(dp, pdi_list, pd_link) {
-               struct disk_devdesc currdev;
-
-               currdev.dd.d_dev = &efipart_hddev;
-               currdev.dd.d_unit = dp->pd_unit;
-               currdev.d_slice = -1;
-               currdev.d_partition = -1;
-
-               if (dp->pd_handle == img->DeviceHandle) {
-                       devname = efi_fmtdev(&currdev);
-
-                       env_setenv("currdev", EV_VOLATILE, devname,
-                           efi_setcurrdev, env_nounset);
-                       env_setenv("loaddev", EV_VOLATILE, devname,
-                           env_noset, env_nounset);
-                       return (0);
+       /*
+        * Try to find the block device by its handle based on the
+        * image we're booting. If we can't find a sane partition,
+        * search all the other partitions of the disk. We do not
+        * search other disks because it's a violation of the UEFI
+        * boot protocol to do so. We fail and let UEFI go on to
+        * the next candidate.
+        */
+       dp = efiblk_get_pdinfo_by_handle(img->DeviceHandle);
+       if (dp != NULL) {
+               text = efi_devpath_name(dp->pd_devpath);
+               if (text != NULL) {
+                       printf("Trying ESP: %S\n", text);
+                       efi_free_devpath_name(text);
                }
-               /* Assuming GPT partitioning. */
-               STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
-                       if (pp->pd_handle == img->DeviceHandle) {
-                               currdev.d_slice = pp->pd_unit;
-                               currdev.d_partition = 255;
-                               devname = efi_fmtdev(&currdev);
-
-                               env_setenv("currdev", EV_VOLATILE, devname,
-                                   efi_setcurrdev, env_nounset);
-                               env_setenv("loaddev", EV_VOLATILE, devname,
-                                   env_noset, env_nounset);
-                               return (0);
+               set_currdev_pdinfo(dp);
+               if (sanity_check_currdev())
+                       return (0);
+               if (dp->pd_parent != NULL) {
+                       dp = dp->pd_parent;
+                       STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
+                               text = efi_devpath_name(pp->pd_devpath);
+                               if (text != NULL) {
+                                       printf("And now the part: %S\n", text);
+                                       efi_free_devpath_name(text);
+                               }
+                               /*
+                                * Roll up the ZFS special case
+                                * for those partitions that have
+                                * zpools on them 
+                                */
+                               if (try_as_currdev(dp, pp))
+                                       return (0);
                        }
                }
+       } else {
+               printf("Can't find device by handle\n");
        }
 
-       pdi_list = efiblk_get_pdinfo_list(&efipart_cddev);
-       STAILQ_FOREACH(dp, pdi_list, pd_link) {
-               if (dp->pd_handle == img->DeviceHandle ||
-                   dp->pd_alias == img->DeviceHandle) {
-                       set_devdesc_currdev(&efipart_cddev, dp->pd_unit);
-                       return (0);
-               }
-       }
-
-       pdi_list = efiblk_get_pdinfo_list(&efipart_fddev);
-       STAILQ_FOREACH(dp, pdi_list, pd_link) {
-               if (dp->pd_handle == img->DeviceHandle) {
-                       set_devdesc_currdev(&efipart_fddev, dp->pd_unit);
-                       return (0);
-               }
-       }
-
        /*
         * Try the device handle from our loaded image first.  If that
         * fails, use the device path from the loaded image and see if
         * any of the nodes in that path match one of the enumerated
-        * handles.
+        * handles. Currently, this handle list is only for netboot.
         */
        if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) {
-               set_devdesc_currdev(dev, unit);
-               return (0);
+               set_currdev_devsw(dev, unit);
+               if (sanity_check_currdev())
+                       return (0);
        }
 
        copy = NULL;
@@ -289,8 +368,9 @@ find_currdev(EFI_LOADED_IMAGE *img)
                copy = NULL;
 
                if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) {
-                       set_devdesc_currdev(dev, unit);
-                       return (0);
+                       set_currdev_devsw(dev, unit);
+                       if (sanity_check_currdev())
+                               return (0);
                }
 
                devpath = efi_lookup_devpath(h);
@@ -304,6 +384,33 @@ find_currdev(EFI_LOADED_IMAGE *img)
        return (ENOENT);
 }
 
+static bool
+interactive_interrupt(const char *msg)
+{
+       time_t now, then, last;
+
+       last = 0;
+       now = then = getsecs();
+       printf("%s\n", msg);
+       if (fail_timeout == -2)         /* Always break to OK */
+               return (true);
+       if (fail_timeout == -1)         /* Never break to OK */
+               return (false);
+       do {
+               if (last != now) {
+                       printf("press any key to interrupt reboot in %d 
seconds\r",
+                           fail_timeout - (int)(now - then));
+                       last = now;
+               }
+
+               /* XXX no pause or timeout wait for char */
+               if (ischar())
+                       return (true);
+               now = getsecs();
+       } while (now - then < fail_timeout);
+       return (false);
+}
+
 EFI_STATUS
 main(int argc, CHAR16 *argv[])
 {
@@ -312,12 +419,13 @@ main(int argc, CHAR16 *argv[])
        int i, j, vargood, howto;
        UINTN k;
        int has_kbd;
+       char *s;
+       EFI_DEVICE_PATH *imgpath;
        CHAR16 *text;
+       EFI_STATUS status;
        UINT16 boot_current;
        size_t sz;
        UINT16 boot_order[100];
-       EFI_DEVICE_PATH *imgpath;
-       EFI_STATUS status;
 #if !defined(__arm__)
        char buf[40];
 #endif
@@ -356,12 +464,15 @@ main(int argc, CHAR16 *argv[])
        /*
         * Parse the args to set the console settings, etc
         * boot1.efi passes these in, if it can read /boot.config or 
/boot/config
-        * or iPXE may be setup to pass these in.
+        * or iPXE may be setup to pass these in. Or the optional argument in 
the
+        * boot environment was used to pass these arguments in (in which case
+        * neither /boot.config nor /boot/config are consulted).
         *
         * Loop through the args, and for each one that contains an '=' that is
         * not the first character, add it to the environment.  This allows
         * loader and kernel env vars to be passed on the command line.  Convert
-        * args from UCS-2 to ASCII (16 to 8 bit) as they are copied.
+        * args from UCS-2 to ASCII (16 to 8 bit) as they are copied (though 
this
+        * method is flawed for non-ASCII characters).
         */
        howto = 0;
        for (i = 1; i < argc; i++) {
@@ -441,6 +552,10 @@ main(int argc, CHAR16 *argv[])
        for (i = 0; howto_names[i].ev != NULL; i++)
                if (howto & howto_names[i].mask)
                        setenv(howto_names[i].ev, "YES", 1);
+
+       /*
+        * XXX we need fallback to this stuff after looking at the ConIn, 
ConOut and ConErr variables
+        */
        if (howto & RB_MULTIPLE) {
                if (howto & RB_SERIAL)
                        setenv("console", "comconsole efi" , 1);
@@ -448,13 +563,17 @@ main(int argc, CHAR16 *argv[])
                        setenv("console", "efi comconsole" , 1);
        } else if (howto & RB_SERIAL) {
                setenv("console", "comconsole" , 1);
-       }
+       } else
+               setenv("console", "efi", 1);
 
        if (efi_copy_init()) {
                printf("failed to allocate staging area\n");
                return (EFI_BUFFER_TOO_SMALL);
        }
 
+       if ((s = getenv("fail_timeout")) != NULL)
+               fail_timeout = strtol(s, NULL, 10);
+
        /*
         * Scan the BLOCK IO MEDIA handles then
         * march through the device switch probing for things.
@@ -479,6 +598,7 @@ main(int argc, CHAR16 *argv[])
 
        printf("\n%s", bootprog_info);
 
+       /* Determine the devpath of our image so we can prefer it. */
        text = efi_devpath_name(img->FilePath);
        if (text != NULL) {
                printf("   Load Path: %S\n", text);
@@ -520,8 +640,16 @@ main(int argc, CHAR16 *argv[])
         */
        BS->SetWatchdogTimer(0, 0, 0, NULL);
 
+       /*
+        * Try and find a good currdev based on the image that was booted.
+        * It might be desirable here to have a short pause to allow falling
+        * through to the boot loader instead of returning instantly to follow
+        * the boot protocol and also allow an escape hatch for users wishing
+        * to try something different.
+        */
        if (find_currdev(img) != 0)
-               return (EFI_NOT_FOUND);
+               if (!interactive_interrupt("Failed to find bootable partition"))
+                       return (EFI_NOT_FOUND);
 
        efi_init_environment();
        setenv("LINES", "24", 1);       /* optional */
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to