Read in and parse /efi/freebsd/loader.env from the boot device's

partition as if it were on the command line.

Fetch FreeBSD-LoaderEnv UEFI enviornment variable. If set, read in
loader environment variables from it. Otherwise read in
/efi/freebsd/loader.env. Both are read relative to the device
loader.efi loaded from (they aren't full UEFI device paths)

Next fetch FreeBSD-NextLoaderEnv UEFI environment variable. If
present, read the file it points to in as above and delete the UEFI
environment variable so it only happens once.

This lets one set environment variables in the bootloader.
Unfortunately, we don't have all the mechanisms in place to parse the
file, nor do we have the magic pattern matching in place that
loader.conf has. Variables are of the form foo=bar. No quotes are
supported, so spaces aren't allowed, for example. Also, variables like
foo_load=yes are intercepted when we parse the loader.conf file and
things are done based on that. Since those aren't done here, variables
that cause an action to happen won't work.

Reviewed by: bcran
Differential Revision: https://reviews.freebsd.org/D20016
This commit is contained in:
Warner Losh 2019-04-29 05:02:25 +00:00
parent 132af14fde
commit 8ac2d6f5d6

View File

@ -742,6 +742,80 @@ parse_uefi_con_out(void)
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 the 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);
}
}
if (fn) {
printf(" Reading loader env vars from %s\n", fn);
parse_loader_efi_config(boot_img->DeviceHandle, fn);
}
}
EFI_STATUS
main(int argc, CHAR16 *argv[])
{
@ -813,6 +887,38 @@ main(int argc, CHAR16 *argv[])
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.