Index: stand/efi/loader/main.c =================================================================== --- stand/efi/loader/main.c +++ stand/efi/loader/main.c @@ -478,7 +478,26 @@ } /* - * Second choice: If we can find out image boot_info, and there's + * Second choice: If rootdev_uefi is set, translate that UEFI device + * path to the loader's internal name and use that. + */ + do { + rootdev = getenv("rootdev_uefi"); + if (rootdev == NULL) + break; + devpath = efi_name_to_devpath(rootdev); + if (devpath == NULL) + break; + dp = efiblk_get_pdinfo_by_device_path(devpath); + efi_devpath_free(devpath); + if (dp == NULL) + break; + set_currdev_pdinfo(dp); + return (0); + } while (0); + + /* + * Third 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 @@ -742,6 +761,79 @@ return (how); } +void +parse_loader_efi_config(EFI_HANDLE h, const char *env_fn) +{ + pdinfo_t *dp; + struct stat st; + int fd = -1; + char *env = NULL; + + dp = efiblk_get_pdinfo_by_handle(h); + if (dp == NULL) + return; + set_currdev_pdinfo(dp); + if (stat(env_fn, &st) != 0) + return; + fd = open(env_fn, O_RDONLY); + if (fd == -1) + return; + env = malloc(st.st_size + 1); + if (env == NULL) + goto out; + if (read(fd, env, st.st_size) != st.st_size) + goto out; + env[st.st_size] = '\0'; + boot_parse_cmdline(env); +out: + free(env); + close(fd); +} + +static void +read_loader_env(const char *name, char *def_fn, bool once) +{ + UINTN len; + char *fn, *freeme = NULL; + + len = 0; + fn = def_fn; + if (efi_freebsd_getenv(name, NULL, &len) == EFI_BUFFER_TOO_SMALL) { + freeme = fn = malloc(len + 1); + if (fn != NULL) { + if (efi_freebsd_getenv(name, fn, &len) != EFI_SUCCESS) { + free(fn); + fn = NULL; + printf( + "Can't fetch FreeBSD::%s we know is there\n", name); + } else { + /* + * if tagged as 'once' delete the env variable so we + * only use it once. + */ + if (once) + efi_freebsd_delenv(name); + /* + * We malloced 1 more than len above, then redid the call. + * so now we have room at te end of the string to NUL terminate + * it here, even if the typical idium would have '- 1' here to + * not overflow. len should be the same on return both times. + */ + fn[len] = '\0'; + } + } else { + printf( + "Can't allocate %d bytes to fetch FreeBSD::%s env var\n", + len, name); + } + } + printf("reading env variable %s default %s actual %s\n", name, def_fn ? def_fn : "", fn ? fn : ""); + if (fn) + parse_loader_efi_config(boot_img->DeviceHandle, fn); +} + + + EFI_STATUS main(int argc, CHAR16 *argv[]) { @@ -813,6 +905,38 @@ howto &= ~RB_PROBE; uhowto = parse_uefi_con_out(); + /* + * Scan the BLOCK IO MEDIA handles then + * march through the device switch probing for things. + */ + i = efipart_inithandles(); + if (i != 0 && i != ENOENT) { + printf("efipart_inithandles failed with ERRNO %d, expect " + "failures\n", i); + } + + for (i = 0; devsw[i] != NULL; i++) + if (devsw[i]->dv_init != NULL) + (devsw[i]->dv_init)(); + + /* + * Read additional environment variables from the boot device's + * "LoaderEnv" file. Any boot loader environment variable may be set + * there, which are subtly different than loader.conf variables. Only + * the 'simple' ones may be set so things like foo_load="YES" won't work + * for two reasons. First, the parser is simplistic and doesn't grok + * quotes. Second, because the variables that cause an action to happen + * are parsed by the lua, 4th or whatever code that's not yet + * loaded. This is relative to the root directory when loader.efi is + * loaded off the UFS root drive (when chain booted), or from the ESP + * when directly loaded by the BIOS. + * + * We also read in NextLoaderEnv if it was specified. This allows next boot + * functionality to be implemented and to override anything in LoaderEnv. + */ + read_loader_env("LoaderEnv", "/efi/freebsd/loader.env", false); + read_loader_env("NextLoaderEnv", NULL, true); + /* * We now have two notions of console. howto should be viewed as * overrides. If console is already set, don't set it again. @@ -899,36 +1023,41 @@ } } - uefi_boot_mgr = true; - boot_current = 0; - sz = sizeof(boot_current); - rv = efi_global_getenv("BootCurrent", &boot_current, &sz); - if (rv == EFI_SUCCESS) - printf(" BootCurrent: %04x\n", boot_current); - else { - boot_current = 0xffff; + if (getenv("uefi_ignore_boot_mgr") != NULL) { + printf(" Ignoring UEFI boot manager\n"); uefi_boot_mgr = false; - } + } else { + uefi_boot_mgr = true; + boot_current = 0; + sz = sizeof(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); - 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; + sz = sizeof(boot_order); + 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; + } } /* @@ -977,7 +1106,8 @@ * to try something different. */ if (find_currdev(uefi_boot_mgr, is_last, boot_info, bisz) != 0) - if (!interactive_interrupt("Failed to find bootable partition")) + if (uefi_boot_mgr && + !interactive_interrupt("Failed to find bootable partition")) return (EFI_NOT_FOUND); efi_init_environment();