Index: stand/efi/loader/main.c =================================================================== --- stand/efi/loader/main.c +++ stand/efi/loader/main.c @@ -73,14 +73,8 @@ EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; static EFI_LOADED_IMAGE *img; - -#ifdef EFI_ZFS_BOOT -bool -efi_zfs_is_preferred(EFI_HANDLE *h) -{ - return (h == img->DeviceHandle); -} -#endif +static EFI_DEVICE_PATH *imgpath; +static EFI_DEVICE_PATH *imgprefix; static int has_keyboard(void) @@ -164,153 +158,197 @@ return retval; } +/* Check if this is a preferred device */ +#ifndef LOADER_CHECK_ALL_DEVS +static bool +check_preferred(EFI_HANDLE *h) +{ + EFI_DEVICE_PATH *path = efi_lookup_devpath(h); + bool out; + + if ((path = efi_lookup_devpath(h)) == NULL) + return (false); + + out = efi_devpath_is_prefix(imgpath, path) || + efi_devpath_is_prefix(imgprefix, path); + + return (out); +} + +static bool +zfs_check_preferred(uint64_t check_guid) +{ + return (pool_guid != 0 && check_guid == pool_guid); +} +#else +static bool +check_preferred(EFI_HANDLE *h) +{ + return (true); +} + +static bool +zfs_check_preferred(uint64_t check_guid) +{ + return (true); +} +#endif + +static void +set_vars(struct devdesc *currdev) +{ + char *devname; + + devname = efi_fmtdev(currdev); + env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, + env_nounset); + env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset); +} + static void set_devdesc_currdev(struct devsw *dev, int unit) { struct devdesc currdev; - char *devname; currdev.d_dev = dev; currdev.d_type = currdev.d_dev->dv_type; currdev.d_unit = unit; currdev.d_opendata = NULL; - devname = efi_fmtdev(&currdev); - - env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, - env_nounset); - env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset); + set_vars(&currdev); } +/* Directly check a devdesc for an installed system */ 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; - struct devsw *dev; - int unit; - uint64_t extra; - -#ifdef EFI_ZFS_BOOT - /* Did efi_zfs_probe() detect the boot pool? */ - if (pool_guid != 0) { - struct zfs_devdesc currdev; - - currdev.d_dev = &zfs_dev; - currdev.d_unit = 0; - currdev.d_type = currdev.d_dev->dv_type; - currdev.d_opendata = NULL; - 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); - } -#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.d_dev = &efipart_hddev; - currdev.d_type = currdev.d_dev->dv_type; - currdev.d_unit = dp->pd_unit; - currdev.d_opendata = NULL; - 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); - } - /* 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); - } - } - } - - 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); - } - } +check_devdesc(struct devdesc *currdev) { + /* Files that indicate the presence of an installed system */ + static const char* paths[] = { + "/boot/loader.conf", + "/boot/kernel", + NULL + }; + + struct stat sb; + int i, err; + + /* Check for the presence of any of the files */ + for (i = 0; paths[i] != NULL; i++) { + err = stat(paths[i], &sb); + if (errno != ENOENT) { + return (err); + } + } + + return (err); +} - 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); - } - } +/* Set up a dev and then check it for an installed system */ +static int +check_dev(struct devsw *dev, int unit) { + struct devdesc currdev; - /* - * 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. - */ - if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) { - set_devdesc_currdev(dev, unit); - return (0); - } + currdev.d_dev = dev; + currdev.d_type = currdev.d_dev->dv_type; + currdev.d_unit = unit; + currdev.d_opendata = NULL; + set_vars(&currdev); - copy = NULL; - devpath = efi_lookup_image_devpath(IH); - while (devpath != NULL) { - h = efi_devpath_handle(devpath); - if (h == NULL) - break; + return (check_devdesc(&currdev)); +} - free(copy); - copy = NULL; +static int +find_currdev(void) +{ + pdinfo_list_t *pdi_list; + pdinfo_t *dp, *pp; + zfsinfo_list_t *zfsi_list; + zfsinfo_t *zi; + char *devname; - if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) { - set_devdesc_currdev(dev, unit); - return (0); - } +#ifdef EFI_ZFS_BOOT + zfsi_list = efizfs_get_zfsinfo_list(); + STAILQ_FOREACH(zi, zfsi_list, zi_link) { + if (zfs_check_preferred(zi->zi_pool_guid)) { + struct zfs_devdesc currdev; + + currdev.d_dev = &zfs_dev; + currdev.d_unit = 0; + currdev.d_type = currdev.d_dev->dv_type; + currdev.d_opendata = NULL; + currdev.pool_guid = zi->zi_pool_guid; + currdev.root_guid = 0; + devname = efi_fmtdev(&currdev); + set_vars((struct devdesc*)(&currdev)); + init_zfs_bootenv(devname); + + if (check_devdesc((struct devdesc*)(&currdev)) == 0) + 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.d_dev = &efipart_hddev; + currdev.d_type = currdev.d_dev->dv_type; + currdev.d_unit = dp->pd_unit; + currdev.d_opendata = NULL; + currdev.d_slice = -1; + currdev.d_partition = -1; + set_vars((struct devdesc*)(&currdev)); + + if (check_preferred(dp->pd_handle) && + check_devdesc((struct devdesc*)(&currdev)) == 0) + return (0); + + /* Assuming GPT partitioning. */ + STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { + if (check_preferred(pp->pd_handle)) { + currdev.d_slice = pp->pd_unit; + currdev.d_partition = 255; + set_vars((struct devdesc*)(&currdev)); + + if (check_devdesc((struct devdesc*) + (&currdev)) == 0) + return (0); + } + } + } + +#ifdef LOADER_CHECK_ALL_DEVS + pdi_list = efiblk_get_pdinfo_list(&efipart_cddev); + STAILQ_FOREACH(dp, pdi_list, pd_link) { + if (check_preferred(dp->pd_handle) && + check_dev(&efipart_cddev, dp->pd_unit) == 0) + return (0); + } + + pdi_list = efiblk_get_pdinfo_list(&efipart_fddev); + STAILQ_FOREACH(dp, pdi_list, pd_link) { + if (check_preferred(dp->pd_handle) && + check_dev(&efipart_fddev, dp->pd_unit) == 0) + return (0); + } +#endif + return (ENOENT); +} - devpath = efi_lookup_devpath(h); - if (devpath != NULL) { - copy = efi_devpath_trim(devpath); - devpath = copy; - } - } - free(copy); - return (ENOENT); +#ifdef EFI_ZFS_BOOT +bool +efi_zfs_is_preferred(EFI_HANDLE *h) +{ + return (h == img->DeviceHandle || check_preferred(h)); } +#endif EFI_STATUS main(int argc, CHAR16 *argv[]) { char var[128]; EFI_GUID *guid; + EFI_STATUS status; int i, j, vargood, howto; UINTN k; int has_kbd; @@ -349,10 +387,24 @@ */ bcache_init(32768, 512); + if ((status = BS->HandleProtocol(img->DeviceHandle, &devid, + (VOID**)&imgpath)) != + EFI_SUCCESS) { + panic("Failed to query LoadedImage (%lu)\n", + EFI_ERROR_CODE(status)); + } + + /* The loaded image device path ends with a partition, then a + * file path. Trim them both to get the actual disk. + */ + if ((imgprefix = efi_devpath_trim(imgpath)) == NULL) { + panic("Couldn't trim device path"); + } + /* - * 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. + * 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. * * Loop through the args, and for each one that contains an '=' that is * not the first character, add it to the environment. This allows @@ -482,7 +534,7 @@ */ BS->SetWatchdogTimer(0, 0, 0, NULL); - if (find_currdev(img) != 0) + if (find_currdev() != 0) return (EFI_NOT_FOUND); efi_init_environment();