Index: stable/11/usr.sbin/Makefile =================================================================== --- stable/11/usr.sbin/Makefile (revision 332125) +++ stable/11/usr.sbin/Makefile (revision 332126) @@ -1,223 +1,223 @@ # From: @(#)Makefile 5.20 (Berkeley) 6/12/93 # $FreeBSD$ .include SUBDIR= adduser \ arp \ binmiscctl \ camdd \ cdcontrol \ chkgrp \ chown \ chroot \ ckdist \ clear_locks \ crashinfo \ cron \ ctladm \ ctld \ daemon \ dconschat \ devctl \ devinfo \ digictl \ diskinfo \ dumpcis \ etcupdate \ extattr \ extattrctl \ fifolog \ fstyp \ fwcontrol \ getfmac \ getpmac \ gstat \ i2c \ ifmcstat \ iostat \ iovctl \ kldxref \ mailwrapper \ makefs \ memcontrol \ mergemaster \ mfiutil \ mixer \ mlxcontrol \ mountd \ mount_smbfs \ mpsutil \ mptutil \ mtest \ newsyslog \ nfscbd \ nfsd \ nfsdumpstate \ nfsrevoke \ nfsuserd \ nmtree \ nologin \ pciconf \ periodic \ powerd \ procctl \ pstat \ pw \ pwd_mkdb \ quot \ rarpd \ rmt \ rpcbind \ rpc.lockd \ rpc.statd \ rpc.umntall \ rtprio \ rwhod \ service \ services_mkdb \ sesutil \ setfib \ setfmac \ setpmac \ smbmsg \ snapinfo \ spray \ syslogd \ sysrc \ tcpdrop \ tcpdump \ traceroute \ trpt \ tzsetup \ uefisign \ ugidfw \ vigr \ vipw \ wake \ watch \ watchdogd \ zic \ zonectl # NB: keep these sorted by MK_* knobs SUBDIR.${MK_ACCT}+= accton SUBDIR.${MK_ACCT}+= sa SUBDIR.${MK_AMD}+= amd SUBDIR.${MK_AUDIT}+= audit SUBDIR.${MK_AUDIT}+= auditd .if ${MK_OPENSSL} != "no" SUBDIR.${MK_AUDIT}+= auditdistd .endif SUBDIR.${MK_AUDIT}+= auditreduce SUBDIR.${MK_AUDIT}+= praudit SUBDIR.${MK_AUTHPF}+= authpf SUBDIR.${MK_AUTOFS}+= autofs .if ${MK_BLACKLIST} != "no" SUBDIR.${MK_BLACKLIST}+= blacklistctl SUBDIR.${MK_BLACKLIST}+= blacklistd .endif SUBDIR.${MK_BLUETOOTH}+= bluetooth SUBDIR.${MK_BOOTPARAMD}+= bootparamd SUBDIR.${MK_BSDINSTALL}+= bsdinstall SUBDIR.${MK_BSNMP}+= bsnmpd SUBDIR.${MK_CTM}+= ctm SUBDIR.${MK_CXGBETOOL}+= cxgbetool SUBDIR.${MK_MLX5TOOL}+= mlx5tool SUBDIR.${MK_DIALOG}+= bsdconfig -SUBDIR.${MK_EFI}+= efivar efidp +SUBDIR.${MK_EFI}+= efivar efidp efibootmgr SUBDIR.${MK_FLOPPY}+= fdcontrol SUBDIR.${MK_FLOPPY}+= fdformat SUBDIR.${MK_FLOPPY}+= fdread SUBDIR.${MK_FLOPPY}+= fdwrite SUBDIR.${MK_FMTREE}+= fmtree SUBDIR.${MK_FREEBSD_UPDATE}+= freebsd-update SUBDIR.${MK_GSSAPI}+= gssd SUBDIR.${MK_GPIO}+= gpioctl SUBDIR.${MK_INET6}+= ip6addrctl SUBDIR.${MK_INET6}+= mld6query SUBDIR.${MK_INET6}+= ndp SUBDIR.${MK_INET6}+= rip6query SUBDIR.${MK_INET6}+= route6d SUBDIR.${MK_INET6}+= rrenumd SUBDIR.${MK_INET6}+= rtadvctl SUBDIR.${MK_INET6}+= rtadvd SUBDIR.${MK_INET6}+= rtsold SUBDIR.${MK_INET6}+= traceroute6 SUBDIR.${MK_INETD}+= inetd SUBDIR.${MK_IPFW}+= ipfwpcap SUBDIR.${MK_ISCSI}+= iscsid SUBDIR.${MK_JAIL}+= jail SUBDIR.${MK_JAIL}+= jexec SUBDIR.${MK_JAIL}+= jls # XXX MK_SYSCONS SUBDIR.${MK_LEGACY_CONSOLE}+= kbdcontrol SUBDIR.${MK_LEGACY_CONSOLE}+= kbdmap SUBDIR.${MK_LEGACY_CONSOLE}+= moused SUBDIR.${MK_LEGACY_CONSOLE}+= vidcontrol .if ${MK_LIBTHR} != "no" || ${MK_LIBPTHREAD} != "no" SUBDIR.${MK_PPP}+= pppctl SUBDIR.${MK_NS_CACHING}+= nscd .endif SUBDIR.${MK_LPR}+= lpr SUBDIR.${MK_MAN_UTILS}+= manctl SUBDIR.${MK_NAND}+= nandsim SUBDIR.${MK_NAND}+= nandtool SUBDIR.${MK_NETGRAPH}+= flowctl SUBDIR.${MK_NETGRAPH}+= lmcconfig SUBDIR.${MK_NETGRAPH}+= ngctl SUBDIR.${MK_NETGRAPH}+= nghook SUBDIR.${MK_NIS}+= rpc.yppasswdd SUBDIR.${MK_NIS}+= rpc.ypupdated SUBDIR.${MK_NIS}+= rpc.ypxfrd SUBDIR.${MK_NIS}+= ypbind SUBDIR.${MK_NIS}+= ypldap SUBDIR.${MK_NIS}+= yp_mkdb SUBDIR.${MK_NIS}+= yppoll SUBDIR.${MK_NIS}+= yppush SUBDIR.${MK_NIS}+= ypserv SUBDIR.${MK_NIS}+= ypset SUBDIR.${MK_NTP}+= ntp SUBDIR.${MK_OPENSSL}+= keyserv SUBDIR.${MK_PC_SYSINSTALL}+= pc-sysinstall SUBDIR.${MK_PF}+= ftp-proxy SUBDIR.${MK_PKGBOOTSTRAP}+= pkg SUBDIR.${MK_PMC}+= pmcannotate SUBDIR.${MK_PMC}+= pmccontrol SUBDIR.${MK_PMC}+= pmcstat SUBDIR.${MK_PMC}+= pmcstudy SUBDIR.${MK_PORTSNAP}+= portsnap SUBDIR.${MK_PPP}+= ppp SUBDIR.${MK_QUOTAS}+= edquota SUBDIR.${MK_QUOTAS}+= quotaon SUBDIR.${MK_QUOTAS}+= repquota SUBDIR.${MK_SENDMAIL}+= editmap SUBDIR.${MK_SENDMAIL}+= mailstats SUBDIR.${MK_SENDMAIL}+= makemap SUBDIR.${MK_SENDMAIL}+= praliases SUBDIR.${MK_SENDMAIL}+= sendmail SUBDIR.${MK_TCP_WRAPPERS}+= tcpdchk SUBDIR.${MK_TCP_WRAPPERS}+= tcpdmatch SUBDIR.${MK_TIMED}+= timed SUBDIR.${MK_TOOLCHAIN}+= config SUBDIR.${MK_TOOLCHAIN}+= crunch SUBDIR.${MK_UNBOUND}+= unbound SUBDIR.${MK_USB}+= uathload SUBDIR.${MK_USB}+= uhsoctl SUBDIR.${MK_USB}+= usbconfig SUBDIR.${MK_USB}+= usbdump SUBDIR.${MK_UTMPX}+= ac SUBDIR.${MK_UTMPX}+= lastlogin SUBDIR.${MK_UTMPX}+= utx SUBDIR.${MK_WIRELESS}+= ancontrol SUBDIR.${MK_WIRELESS}+= wlandebug SUBDIR.${MK_WIRELESS}+= wpa SUBDIR.${MK_TESTS}+= tests .include SUBDIR_PARALLEL= .include Index: stable/11/usr.sbin/efibootmgr/efibootmgr.c =================================================================== --- stable/11/usr.sbin/efibootmgr/efibootmgr.c (nonexistent) +++ stable/11/usr.sbin/efibootmgr/efibootmgr.c (revision 332126) @@ -0,0 +1,885 @@ +/*- + * Copyright (c) 2017 Netflix, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifndef LOAD_OPTION_ACTIVE +#define LOAD_OPTION_ACTIVE 0x00000001 +#endif + +#ifndef LOAD_OPTION_CATEGORY_BOOT +#define LOAD_OPTION_CATEGORY_BOOT 0x00000000 +#endif + +#define BAD_LENGTH ((size_t)-1) + +typedef struct _bmgr_opts { + char *env; + char *loader; + char *label; + char *kernel; + char *name; + char *order; + int bootnum; + bool copy; + bool create; + bool delete; + bool delete_bootnext; + bool del_timeout; + bool dry_run; + bool once; + int cp_src; + bool set_active; + bool set_bootnext; + bool set_inactive; + bool set_timeout; + int timeout; + bool verbose; +} bmgr_opts_t; + +static struct option lopts[] = { + {"activate", required_argument, NULL, 'a'}, + {"bootnext", required_argument, NULL, 'n'}, /* set bootnext */ + {"bootorder", required_argument, NULL, 'o'}, /* set order */ + {"copy", required_argument, NULL, 'C'}, /* Copy boot method */ + {"create", no_argument, NULL, 'c'}, + {"deactivate", required_argument, NULL, 'A'}, + {"del-timout", no_argument, NULL, 'T'}, + {"delete", required_argument, NULL, 'B'}, + {"delete-bootnext", required_argument, NULL, 'N'}, + {"device", required_argument, NULL, 'd'}, + {"dry-run", no_argument, NULL, 'D'}, + {"env", required_argument, NULL, 'e'}, + {"help", no_argument, NULL, 'h'}, + {"kernel", required_argument, NULL, 'k'}, + {"label", required_argument, NULL, 'L'}, + {"loader", required_argument, NULL, 'l'}, + {"once", no_argument, NULL, 'O'}, + {"partition", required_argument, NULL, 'p'}, + {"set-timeout", required_argument, NULL, 't'}, + {"verbose", no_argument, NULL, 'v'}, + { NULL, 0, NULL, 0} +}; + +/* global efibootmgr opts */ +static bmgr_opts_t opts; + +static LIST_HEAD(efivars_head, entry) efivars = + LIST_HEAD_INITIALIZER(efivars); + +struct entry { + efi_guid_t guid; + uint32_t attrs; + uint8_t *data; + size_t size; + char *name; + char *label; + int idx; + int part; + + LIST_ENTRY(entry) entries; +}; + +#define MAX_DP_LEN 4096 +#define MAX_LOADOPT_LEN 8192 + + +static char * +mangle_loader(char *loader) +{ + char *c; + + for (c = loader; *c; c++) + if (*c == '/') + *c = '\\'; + + return loader; +} + + +#define COMMON_ATTRS EFI_VARIABLE_NON_VOLATILE | \ + EFI_VARIABLE_BOOTSERVICE_ACCESS | \ + EFI_VARIABLE_RUNTIME_ACCESS + +/* + * We use global guid, and common var attrs and + * find it better to just delete and re-create a var. + */ +static int +set_bootvar(const char *name, uint8_t *data, size_t size) +{ + + return efi_set_variable(EFI_GLOBAL_GUID, name, data, size, + COMMON_ATTRS); +} + + +#define USAGE \ + " [-aAnNB Bootvar] [-t timeout] [-T] [-o bootorder] [-O] [--verbose] [--help] \n\ + [-c -l loader [-k kernel ] [-L label] [--dry-run]]" + +#define CREATE_USAGE \ + " efibootmgr -c -l loader [-k kernel] [-L label] [--dry-run]" +#define ORDER_USAGE \ + " efibootmgr -o bootvarnum1,bootvarnum2,..." +#define TIMEOUT_USAGE \ + " efibootmgr -t seconds" +#define DELETE_USAGE \ + " efibootmgr -B bootvarnum" +#define ACTIVE_USAGE \ + " efibootmgr [-a | -A] bootvarnum" +#define BOOTNEXT_USAGE \ + " efibootmgr [-n | -N] bootvarnum" + +static void +parse_args(int argc, char *argv[]) +{ + int ch; + + while ((ch = getopt_long(argc, argv, "A:a:B:C:cDe:hk:L:l:Nn:Oo:Tt:v", + lopts, NULL)) != -1) { + switch (ch) { + case 'A': + opts.set_inactive = true; + opts.bootnum = strtoul(optarg, NULL, 16); + break; + case 'a': + opts.set_active = true; + opts.bootnum = strtoul(optarg, NULL, 16); + break; + case 'B': + opts.delete = true; + opts.bootnum = strtoul(optarg, NULL, 16); + break; + case 'C': + opts.copy = true; + opts.cp_src = strtoul(optarg, NULL, 16); + case 'c': + opts.create = true; + break; + case 'D': /* should be remove dups XXX */ + opts.dry_run = true; + break; + case 'e': + free(opts.env); + opts.env = strdup(optarg); + break; + case 'h': + default: + errx(1, "%s", USAGE); + break; + case 'k': + free(opts.kernel); + opts.kernel = strdup(optarg); + break; + case 'L': + free(opts.label); + opts.label = strdup(optarg); + break; + case 'l': + free(opts.loader); + opts.loader = strdup(optarg); + opts.loader = mangle_loader(opts.loader); + break; + case 'N': + opts.delete_bootnext = true; + break; + case 'n': + opts.set_bootnext = true; + opts.bootnum = strtoul(optarg, NULL, 16); + break; + case 'O': + opts.once = true; + break; + case 'o': + free(opts.order); + opts.order = strdup(optarg); + break; + case 'T': + opts.del_timeout = true; + break; + case 't': + opts.set_timeout = true; + opts.timeout = strtoul(optarg, NULL, 10); + break; + case 'v': + opts.verbose = true; + break; + } + } + if (opts.create) { + if (!opts.loader) + errx(1, "%s",CREATE_USAGE); + return; + } + if (opts.set_bootnext && !(opts.bootnum)) + errx(1, "%s", BOOTNEXT_USAGE); + + if ((opts.set_active || opts.set_inactive) && !(opts.bootnum)) + errx(1, "%s", ACTIVE_USAGE); + + if (opts.order && !(opts.order)) + errx(1, "%s", ORDER_USAGE); +} + + +static void +print_order(void) +{ + uint32_t attrs; + uint8_t *data; + size_t size, i; + + if (efi_get_variable(EFI_GLOBAL_GUID, "BootOrder", &data, &size, &attrs) < 0) { + printf("BootOrder : Couldn't get value for BootOrder\n"); + return; + } + + if (size % 2 == 1) + errx(1, "Bad BootOrder variable: odd length"); + + printf("BootOrder : "); + for (i = 0; i < size; i += 2) + printf("%04x%s", le16dec(data + i), i == size - 2 ? "\n" : ", "); +} + + +static void +read_vars(void) +{ + + efi_guid_t *guid; + char *next_name = NULL; + int ret = 0; + + struct entry *nent; + + LIST_INIT(&efivars); + while ((ret = efi_get_next_variable_name(&guid, &next_name)) > 0) { + /* + * Only pay attention to EFI:BootXXXX variables to get the list. + */ + if (efi_guid_cmp(guid, &EFI_GLOBAL_GUID) != 0 || + strlen(next_name) != 8 || + strncmp(next_name, "Boot", 4) != 0 || + !isxdigit(next_name[4]) || + !isxdigit(next_name[5]) || + !isxdigit(next_name[6]) || + !isxdigit(next_name[7])) + continue; + nent = malloc(sizeof(struct entry)); + nent->name = strdup(next_name); + + ret = efi_get_variable(*guid, next_name, &nent->data, + &nent->size, &nent->attrs); + if (ret < 0) + err(1, "efi_get_variable"); + nent->guid = *guid; + nent->idx = strtoul(&next_name[4], NULL, 16); + LIST_INSERT_HEAD(&efivars, nent, entries); + } +} + + +static void +set_boot_order(char *order) +{ + uint16_t *new_data; + size_t size; + char *next, *cp; + int cnt; + int i; + + cp = order; + cnt = 1; + while (*cp) { + if (*cp++ == ',') + cnt++; + } + size = sizeof(uint16_t) * cnt; + new_data = malloc(size); + + i = 0; + cp = strdup(order); + while ((next = strsep(&cp, ",")) != NULL) { + new_data[i] = strtoul(next, NULL, 16); + if (new_data[i] == 0 && errno == EINVAL) { + warnx("can't parse %s as a numb", next); + errx(1, "%s", ORDER_USAGE); + } + i++; + } + free(cp); + if (set_bootvar("BootOrder", (uint8_t*)new_data, size) < 0) + err(1, "Unabke to set BootOrder to %s", order); + free(new_data); +} + +static void +handle_activity(int bootnum, bool active) +{ + uint32_t attrs, load_attrs; + uint8_t *data; + size_t size; + char *name; + + asprintf(&name, "%s%04X", "Boot", bootnum); + if (name == NULL) + err(1, "asprintf"); + if (efi_get_variable(EFI_GLOBAL_GUID, name, &data, &size, &attrs) < 0) + err(1, "No such bootvar %s\n", name); + + load_attrs = le32dec(data); + + if (active) + load_attrs |= LOAD_OPTION_ACTIVE; + else + load_attrs &= ~LOAD_OPTION_ACTIVE; + + le32enc(data, load_attrs); + + if (set_bootvar(name, data, size) < 0) + err(1, "handle activity efi_set_variable"); +} + + +/* + * add boot var to boot order. + * called by create boot var. There is no option + * to add one independent of create. + * + * Note: we currently don't support where it goes + * so it goes on the front, inactive. + * use -o 2,3,7 etc to affect order, -a to activate. + */ +static void +add_to_boot_order(char *bootvar) +{ + size_t size; + uint32_t attrs; + uint16_t val; + uint8_t *data, *new; + + val = strtoul(&bootvar[4], NULL, 16); + + if (efi_get_variable(EFI_GLOBAL_GUID, "BootOrder", &data, &size, &attrs) < 0) { + if (errno == ENOENT) { /* create it and set this bootvar to active */ + size = 0; + data = NULL; + } else + err(1, "efi_get_variable BootOrder"); + } + + /* + * We have BootOrder with the current order + * so grow the array by one, add the value + * and write the new variable value. + */ + size += sizeof(uint16_t); + new = malloc(size); + if (!new) + err(1, "malloc"); + + le16enc(new, val); + if (size > sizeof(uint16_t)) + memcpy(new + sizeof(uint16_t), data, size - sizeof(uint16_t)); + + if (set_bootvar("BootOrder", new, size) < 0) + err(1, "set_bootvar"); + free(new); +} + + +static void +remove_from_order(uint16_t bootnum) +{ + uint32_t attrs; + size_t size, i, j; + uint8_t *new, *data; + + if (efi_get_variable(EFI_GLOBAL_GUID, "BootOrder", &data, &size, &attrs) < 0) + return; + + new = malloc(size); + if (new == NULL) + err(1, "malloc"); + + for (j = i = 0; i < size; i += sizeof(uint16_t)) { + if (le16dec(data + i) == bootnum) + continue; + memcpy(new + j, data + i, sizeof(uint16_t)); + j += sizeof(uint16_t); + } + if (i == j) + warnx("Boot variable %04x not in BootOrder", bootnum); + else if (set_bootvar("BootOrder", new, j) < 0) + err(1, "Unable to update BootOrder with new value"); + free(new); +} + + +static void +delete_bootvar(int bootnum) +{ + char *name; + int defer = 0; + + /* + * Try to delete the boot variable and remocve it + * from the boot order. We always do both actions + * to make it easy to clean up from oopses. + */ + if (bootnum < 0 || bootnum > 0xffff) + errx(1, "Bad boot variable %#x", bootnum); + asprintf(&name, "%s%04X", "Boot", bootnum); + if (name == NULL) + err(1, "asprintf"); + printf("Removing boot variable '%s'\n", name); + if (efi_del_variable(EFI_GLOBAL_GUID, name) < 0) { + defer = 1; + warn("cannot delete variable %s", name); + } + printf("Removing 0x%x from BootOrder\n", bootnum); + remove_from_order(bootnum); + free(name); + if (defer) + exit(defer); +} + + +static void +del_bootnext(void) +{ + + if (efi_del_variable(EFI_GLOBAL_GUID, "BootNext") < 0) + err(1, "efi_del_variable"); +} + +static void +handle_bootnext(uint16_t bootnum) +{ + uint16_t num; + + le16enc(&num, bootnum); + if (set_bootvar("BootNext", (uint8_t*)&bootnum, sizeof(uint16_t)) < 0) + err(1, "set_bootvar"); +} + + +static int +compare(const void *a, const void *b) +{ + uint16_t c; + uint16_t d; + + memcpy(&c, a, sizeof(uint16_t)); + memcpy(&d, b, sizeof(uint16_t)); + + if (c < d) + return -1; + if (c == d) + return 0; + return 1; +} + +static char * +make_next_boot_var_name(void) +{ + struct entry *v; + uint16_t *vals, next_free = 0; + char *name; + int cnt = 0; + int i; + + LIST_FOREACH(v, &efivars, entries) { + cnt++; + } + + vals = malloc(sizeof(uint16_t) * cnt); + if (!vals) + return NULL; + + i = 0; + LIST_FOREACH(v, &efivars, entries) { + vals[i++] = v->idx; + } + qsort(vals, cnt, sizeof(uint16_t), compare); + /* if the hole is at the beginning, just return zero */ + if (vals[0] > 0) { + next_free = 0; + } else { + /* now just run the list looking for the first hole */ + for (i = 0; i < cnt - 1 && next_free == 0; i++) + if (vals[i] + 1 != vals[i + 1]) + next_free = vals[i] + 1; + if (next_free == 0) + next_free = vals[cnt - 1] + 1; + /* In theory we could have used all 65k slots -- what to do? */ + } + free(vals); + + asprintf(&name, "%s%04X", "Boot", next_free); + if (name == NULL) + err(1, "asprintf"); + return name; +} + + +static size_t +create_loadopt(uint8_t *buf, size_t bufmax, uint32_t attributes, efidp dp, size_t dp_size, + const char *description, const uint8_t *optional_data, size_t optional_data_size) +{ + efi_char *bbuf = NULL; + uint8_t *pos = buf; + size_t desc_len = 0; + size_t len; + + if (optional_data == NULL && optional_data_size != 0) + return BAD_LENGTH; + if (dp == NULL && dp_size != 0) + return BAD_LENGTH; + + /* + * Compute the length to make sure the passed in buffer is long enough. + */ + utf8_to_ucs2(description, &bbuf, &desc_len); + len = sizeof(uint32_t) + sizeof(uint16_t) + desc_len + dp_size + optional_data_size; + if (len > bufmax) { + free(bbuf); + return BAD_LENGTH; + } + + le32enc(pos, attributes); + pos += sizeof (attributes); + + le16enc(pos, dp_size); + pos += sizeof (uint16_t); + + memcpy(pos, bbuf, desc_len); /* NB:desc_len includes strailing NUL */ + pos += desc_len; + free(bbuf); + + memcpy(pos, dp, dp_size); + pos += dp_size; + + if (optional_data && optional_data_size > 0) { + memcpy(pos, optional_data, optional_data_size); + pos += optional_data_size; + } + + return pos - buf; +} + + +static int +make_boot_var(const char *label, const char *loader, const char *kernel, const char *env, bool dry_run) +{ + struct entry *new_ent; + uint32_t load_attrs = 0; + uint8_t *load_opt_buf; + size_t lopt_size, llen, klen; + efidp dp, loaderdp, kerneldp; + char *bootvar = NULL; + int ret; + + assert(label != NULL); + + bootvar = make_next_boot_var_name(); + if (bootvar == NULL) + err(1, "bootvar creation"); + if (loader == NULL) + errx(1, "Must specify boot loader"); + if (efivar_unix_path_to_device_path(loader, &loaderdp) != 0) + err(1, "Cannot translate unix loader path '%s' to UEFI", loader); + if (kernel != NULL) { + if (efivar_unix_path_to_device_path(kernel, &kerneldp) != 0) + err(1, "Cannot translate unix kernel path '%s' to UEFI", kernel); + } else { + kerneldp = NULL; + } + llen = efidp_size(loaderdp); + if (llen > MAX_DP_LEN) + errx(1, "Loader path too long."); + klen = efidp_size(kerneldp); + if (klen > MAX_DP_LEN) + errx(1, "Kernel path too long."); + dp = malloc(llen + klen); + if (dp == NULL) + errx(1, "Can't allocate memory for new device paths"); + memcpy(dp, loaderdp, llen); + if (kerneldp != NULL) + memcpy((char *)dp + llen, kerneldp, klen); + + /* don't make the new bootvar active by default, use the -a option later */ + load_attrs = LOAD_OPTION_CATEGORY_BOOT; + load_opt_buf = malloc(MAX_LOADOPT_LEN); + if (load_opt_buf == NULL) + err(1, "malloc"); + + lopt_size = create_loadopt(load_opt_buf, MAX_LOADOPT_LEN, load_attrs, + dp, llen + klen, label, env, env ? strlen(env) + 1 : 0); + if (lopt_size == BAD_LENGTH) + errx(1, "Can't crate loadopt"); + + ret = 0; + if (!dry_run) { + ret = efi_set_variable(EFI_GLOBAL_GUID, bootvar, + (uint8_t*)load_opt_buf, lopt_size, COMMON_ATTRS); + } + + if (ret) + err(1, "efi_set_variable"); + + add_to_boot_order(bootvar); /* first, still not active */ + new_ent = malloc(sizeof(struct entry)); + if (new_ent == NULL) + err(1, "malloc"); + memset(new_ent, 0, sizeof(struct entry)); + new_ent->name = bootvar; + new_ent->guid = EFI_GLOBAL_GUID; + LIST_INSERT_HEAD(&efivars, new_ent, entries); + free(load_opt_buf); + free(dp); + + return 0; +} + + +static void +print_loadopt_str(uint8_t *data, size_t datalen) +{ + char *dev, *relpath, *abspath; + uint32_t attr; + uint16_t fplen; + efi_char *descr; + uint8_t *ep = data + datalen; + uint8_t *walker = data; + efidp dp, edp; + char buf[1024]; + int len; + int rv; + int indent; + + if (datalen < sizeof(attr) + sizeof(fplen) + sizeof(efi_char)) + return; + // First 4 bytes are attribute flags + attr = le32dec(walker); + walker += sizeof(attr); + // Next two bytes are length of the file paths + fplen = le16dec(walker); + walker += sizeof(fplen); + // Next we have a 0 terminated UCS2 string that we know to be aligned + descr = (efi_char *)(intptr_t)(void *)walker; + len = ucs2len(descr); // XXX need to sanity check that len < (datalen - (ep - walker) / 2) + walker += (len + 1) * sizeof(efi_char); + if (walker > ep) + return; + // Now we have fplen bytes worth of file path stuff + dp = (efidp)walker; + walker += fplen; + if (walker > ep) + return; + edp = (efidp)walker; + /* + * Everything left is the binary option args + * opt = walker; + * optlen = ep - walker; + */ + indent = 1; + while (dp < edp) { + efidp_format_device_path(buf, sizeof(buf), dp, + (intptr_t)(void *)edp - (intptr_t)(void *)dp); + printf("%*s%s\n", indent, "", buf); + indent = 10 + len + 1; + rv = efivar_device_path_to_unix_path(dp, &dev, &relpath, &abspath); + if (rv == 0) { + printf("%*s%s:%s %s\n", indent + 4, "", dev, relpath, abspath); + free(dev); + free(relpath); + free(abspath); + } + dp = (efidp)((char *)dp + efidp_size(dp)); + } +} + +static char * +get_descr(uint8_t *data) +{ + uint8_t *pos = data; + efi_char *desc; + int len; + char *buf; + int i = 0; + + pos += sizeof(uint32_t) + sizeof(uint16_t); + desc = (efi_char*)(intptr_t)(void *)pos; + len = ucs2len(desc); + buf = malloc(len + 1); + memset(buf, 0, len + 1); + while (desc[i]) { + buf[i] = desc[i]; + i++; + } + return (char*)buf; +} + + +/* Cmd epilogue, or just the default with no args. + * The order is [bootnext] bootcurrent, timeout, order, and the bootvars [-v] + */ +static int +print_boot_vars(bool verbose) +{ + /* + * just read and print the current values + * as a command epilogue + */ + struct entry *v; + uint8_t *data; + char *d; + size_t size; + uint32_t attrs, load_attrs; + int ret; + + ret = efi_get_variable(EFI_GLOBAL_GUID, "BootNext", &data, &size, &attrs); + if (ret > 0) { + printf("BootNext : %04x\n", le16dec(data)); + } + + ret = efi_get_variable(EFI_GLOBAL_GUID, "BootCurrent", &data, &size,&attrs); + printf("BootCurrent: %04x\n", le16dec(data)); + + ret = efi_get_variable(EFI_GLOBAL_GUID, "Timeout", &data, &size, &attrs); + if (ret > 0) { + printf("Timeout : %d seconds\n", le16dec(data)); + } + print_order(); + + /* now we want to fetch 'em all fresh again + * which possibly includes a newly created bootvar + */ + LIST_FOREACH(v, &efivars, entries) { + attrs = 0; + ret = efi_get_variable(EFI_GLOBAL_GUID, v->name, &data, + &size, &attrs); + if (ret < 0) + continue; /* we must have deleted it */ + load_attrs = le32dec(data); + d = get_descr(data); + printf("%s%c %s", v->name, + ((load_attrs & LOAD_OPTION_ACTIVE) ? '*': ' '), d); + free(d); + if (verbose) + print_loadopt_str(data, size); + else + printf("\n"); + } + return 0; +} + +static void +delete_timeout(void) +{ + + efi_del_variable(EFI_GLOBAL_GUID,"Timeout"); +} + +static void +handle_timeout(int to) +{ + uint16_t timeout; + + le16enc(&timeout, to); + if (set_bootvar("Timeout", (uint8_t *)&timeout, sizeof(timeout)) < 0) + errx(1, "Can't set Timeout for booting."); +} + +int +main(int argc, char *argv[]) +{ + + if (!efi_variables_supported()) + errx(1, "efi variables not supported on this system. root? kldload efirt?"); + + memset(&opts, 0, sizeof (bmgr_opts_t)); + parse_args(argc, argv); + read_vars(); + + if (opts.create) + /* + * side effect, adds to boot order, but not yet active. + */ + make_boot_var(opts.label ? opts.label : "", + opts.loader, opts.kernel, opts.env, opts.dry_run); + else if (opts.set_active || opts.set_inactive ) + handle_activity(opts.bootnum, opts.set_active); + else if (opts.order != NULL) + set_boot_order(opts.order); /* create a new bootorder with opts.order */ + else if (opts.set_bootnext) + handle_bootnext(opts.bootnum); + else if (opts.delete_bootnext) + del_bootnext(); + else if (opts.delete) + delete_bootvar(opts.bootnum); + else if (opts.del_timeout) + delete_timeout(); + else if (opts.set_timeout) + handle_timeout(opts.timeout); + + print_boot_vars(opts.verbose); +} Property changes on: stable/11/usr.sbin/efibootmgr/efibootmgr.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/11/usr.sbin/efibootmgr/Makefile =================================================================== --- stable/11/usr.sbin/efibootmgr/Makefile (nonexistent) +++ stable/11/usr.sbin/efibootmgr/Makefile (revision 332126) @@ -0,0 +1,14 @@ +# $FreeBSD$ +EFIBOOT=${SRCTOP}/stand/efi +EFIINCL=${SRCTOP}/stand/efi/include +EFIVAR=${SRCTOP}/usr.sbin/efivar +.PATH: ${EFIBOOT}/libefi ${EFIVAR} +CFLAGS+= -I${EFIVAR} -I${EFIINCL} + +PROG=efibootmgr +MAN= efibootmgr.8 +SRCS= efichar.c efiutil.c efibootmgr.c + +LIBADD= efivar geom + +.include Property changes on: stable/11/usr.sbin/efibootmgr/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/11/usr.sbin/efibootmgr/efibootmgr.8 =================================================================== --- stable/11/usr.sbin/efibootmgr/efibootmgr.8 (nonexistent) +++ stable/11/usr.sbin/efibootmgr/efibootmgr.8 (revision 332126) @@ -0,0 +1,150 @@ +.\" Copyright (c) 2017 Netflix, Inc +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd December 8, 2017 +.Dt EFIBOOTMGR 8 +.Os +.Sh NAME +.Nm efibootmgr +.Nd manipulate the EFI Boot Manager +.Sh SYNOPSIS +.Op Fl aAnNB Ar Bootvar +.Op Fl t Ar timeout +.Op Fl T +.Op Fl o Ar bootorder +.Op Fl verbose +.Op Fl c -k Ar kernel -l Ar loader [-L Ar label] [--dry-run] +.Sh "DESCRIPTION" +.Nm +manipulates how UEFI Boot Managers boot the system. +Methods of booting can be created and destroyed. +Boot methods can be activated or deactivated. +The order of boot methods tried can be changed. +Temporary boot methods can override the usual booting methods. +.Pp +The UEFI standard defines how hosts may control what is used to +bootstrap the system. +Each method is encapsulated within a persistent UEFI variable, stored +by the UEFI BIOS of the form +.Va BootXXXX . +These variables are numbered, describe where to load the bootstrap +program from, and whether or not the method is active. +The boot order of these methods is controlled by another variable +.Va BootOrder . +The currently booting method is communicated using +.Va BootCurrent . +A global timeout can also be set. +.Pp +.Nm +requires that the kernel efirt module to get and set these +non-volatile variables. +.Pp +The following options are available: +.Bl -tag -width 28m +.It Fl c Fl -create +Create a new Boot Variable +.It Fl l -loader Ar loader +The path to and name of the loader. +.It Fl k -kernel Ar kernel +The path to and name of the kernel. +.It Fl L -label Ar label +An optional description for the entry. +.It Fl D -dry-run +Process but do not set the given BootVar. +.It Fl B -delete Ar BootVarNum +Delete the given BootVar Num. +.It Fl a -activate Ar BootVarNum +Activate the given BootVarNum. +.It Fl A -deactivate Ar BootVarNum +Deactivate the given BootVarNum. +.It Fl n -bootnext Ar BootVarNum +Set BootVarNum as the BootNext variable. +.It Fl N -delete-bootnext +Delete the BootNext optional BootVar. +.It Fl o -bootorder Ar bootorder +Set BootOrder variable to the given comma delimited set of BootVarNums. +The numbers are in hex to match BootXXXX, but may omit leading zeros. +.It Fl t -set-timeout Ar timeout +Set the bootmenu timeout value. +.It Fl T -del-timeout +Delete the BootTimeout variable. +.It Fl v -verbose +Display the device path of BootVars in the output. +.El +.Pp +.Sh Exammples +.Pp +To Display the current Boot related variables in the system: +.Pp +.Dl efibootmgr [-v] +.Pp +This will display the optional BootNext BootVar, BootCurrent, +or currently booted BootVar, followed by the optional Timeout value, any +BootOrder that maybe set, followed finally by all currently defined Boot +variables, active or not. The verbose flag will augment this output with +the hardrive partition uuids, size/offset and device-path of the +variable. +.Pp +The +.Nm +program can be used to create new EFI boot variables. To create a new +boot var pointing to an installation on partition 1 of device ada0 using +the given loader with a description FreeBSD-11: +.Pp +.Dl efibootmgr -c -d ada0 -p 1 -l/EFI/BOOT/BOOTX64.EFI -L FreeBSD-11 +.Pp +This will result in the next available BootVarNum being assigned to a +new UEFI load variable, and given the label "FreeBSD-11" such as: +.Pp +.Dl Boot0009 FreeBSD-11 +.Pp +Note newly created BootVars are created inactive. The active state is denoted +by an '*' following the BootVar name in the output. They are also inserted +into the first position of current BootOrder variable if it exists. They +must first be set to active before being considered available to attempt booting from, else they +are ignored. +.Pp +.Dl efibootmgr -B 0009 +.Pp +Will delete the given BootVar Boot0009 +.Pp +To set a given newly created BootVar active use: +.Pp +.Dl efibootmgr -a 0009 +.Pp +To set a given BootVar to be used as the BootNext variable, irrespective +of its active state, use: +.Pp +.Dl efibootmgr -n 0009 +.Pp +To set the BootOrder for the next reboot use: +.Pp +.Dl efibootmgr -o 0009,0003,... +.Pp +.Sh SEE ALSO +.Xr efivar 8 , +.Xr uefi 8 , +.Xr gpart 8 Property changes on: stable/11/usr.sbin/efibootmgr/efibootmgr.8 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/11 =================================================================== --- stable/11 (revision 332125) +++ stable/11 (revision 332126) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r326725-326728,326771,326800-326804,326806,327163,327572-327573,327610-327611,327877,331069