Index: head/sys/boot/efi/include/efilib.h =================================================================== --- head/sys/boot/efi/include/efilib.h (revision 313041) +++ head/sys/boot/efi/include/efilib.h (revision 313042) @@ -1,68 +1,76 @@ /*- * Copyright (c) 2000 Doug Rabson * Copyright (c) 2006 Marcel Moolenaar * 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$ */ #ifndef _LOADER_EFILIB_H #define _LOADER_EFILIB_H #include extern EFI_HANDLE IH; extern EFI_SYSTEM_TABLE *ST; extern EFI_BOOT_SERVICES *BS; extern EFI_RUNTIME_SERVICES *RS; extern struct devsw efipart_dev; extern struct devsw efinet_dev; extern struct netif_driver efinetif; void *efi_get_table(EFI_GUID *tbl); int efi_register_handles(struct devsw *, EFI_HANDLE *, EFI_HANDLE *, int); EFI_HANDLE efi_find_handle(struct devsw *, int); int efi_handle_lookup(EFI_HANDLE, struct devsw **, int *, uint64_t *); int efi_handle_update_dev(EFI_HANDLE, struct devsw *, int, uint64_t); EFI_DEVICE_PATH *efi_lookup_image_devpath(EFI_HANDLE); EFI_DEVICE_PATH *efi_lookup_devpath(EFI_HANDLE); EFI_HANDLE efi_devpath_handle(EFI_DEVICE_PATH *); EFI_DEVICE_PATH *efi_devpath_last_node(EFI_DEVICE_PATH *); EFI_DEVICE_PATH *efi_devpath_trim(EFI_DEVICE_PATH *); CHAR16 *efi_devpath_name(EFI_DEVICE_PATH *); void efi_free_devpath_name(CHAR16 *); int efi_status_to_errno(EFI_STATUS); void efi_time_init(void); void efi_time_fini(void); EFI_STATUS main(int argc, CHAR16 *argv[]); void exit(EFI_STATUS status); void delay(int usecs); +/* EFI environment initialization. */ +void efi_init_environment(void); + +/* CHAR16 utility functions. */ +int wcscmp(CHAR16 *, CHAR16 *); +void cpy8to16(const char *, CHAR16 *, size_t); +void cpy16to8(const CHAR16 *, char *, size_t); + #endif /* _LOADER_EFILIB_H */ Index: head/sys/boot/efi/libefi/Makefile =================================================================== --- head/sys/boot/efi/libefi/Makefile (revision 313041) +++ head/sys/boot/efi/libefi/Makefile (revision 313042) @@ -1,53 +1,51 @@ # $FreeBSD$ .include .if ${MK_FORTH} != "no" +CFLAGS+= -DBOOT_FORTH .include "${.CURDIR}/../../Makefile.ficl" .endif LIB= efi INTERNALLIB= WARNS?= 2 -SRCS= delay.c devpath.c efi_console.c efinet.c efipart.c errno.c \ - handles.c libefi.c +SRCS= delay.c devpath.c efi_console.c efinet.c efipart.c env.c errno.c \ + handles.c wchar.c libefi.c .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" SRCS+= time.c .elif ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "arm" SRCS+= time_event.c -.endif -.if ${MK_FORTH} != "no" -SRCS+= env.c .endif # We implement a slightly non-standard %S in that it always takes a # CHAR16 that's common in UEFI-land instead of a wchar_t. This only # seems to matter on arm64 where wchar_t defaults to an int instead # of a short. There's no good cast to use here so just ignore the # warnings for now. CWARNFLAGS.efinet.c+= -Wno-format .if ${MACHINE_CPUARCH} == "aarch64" CFLAGS+= -msoft-float -mgeneral-regs-only .endif .if ${MACHINE_ARCH} == "amd64" CFLAGS+= -fPIC -mno-red-zone .endif CFLAGS+= -I${.CURDIR}/../include CFLAGS+= -I${.CURDIR}/../include/${MACHINE} CFLAGS+= -I${.CURDIR}/../../../../lib/libstand # Pick up the bootstrap header for some interface items CFLAGS+= -I${.CURDIR}/../../common # Handle FreeBSD specific %b and %D printf format specifiers CFLAGS+= ${FORMAT_EXTENSIONS} # Do not use TERM_EMU on arm and arm64 as it doesn't behave well with serial console .if ${MACHINE_CPUARCH} != "arm" && ${MACHINE_CPUARCH} != "aarch64" CFLAGS+= -DTERM_EMU .endif .include Index: head/sys/boot/efi/libefi/env.c =================================================================== --- head/sys/boot/efi/libefi/env.c (revision 313041) +++ head/sys/boot/efi/libefi/env.c (revision 313042) @@ -1,234 +1,532 @@ /* * Copyright (c) 2015 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. */ #include __FBSDID("$FreeBSD$"); +#include #include #include #include #include #include +#include #include "bootstrap.h" +#ifdef BOOT_FORTH #include "ficl.h" +#endif -int efi_variable_support = 1; - /* * Simple wrappers to the underlying UEFI functions. * See http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES * for details. */ EFI_STATUS -efi_get_next_variable_name(UINTN *variable_name_size, CHAR16 *variable_name, EFI_GUID *vendor_guid) +efi_get_next_variable_name(UINTN *variable_name_size, CHAR16 *variable_name, + EFI_GUID *vendor_guid) { - return RS->GetNextVariableName(variable_name_size, variable_name, vendor_guid); + return (RS->GetNextVariableName(variable_name_size, variable_name, + vendor_guid)); } EFI_STATUS -efi_get_variable(CHAR16 *variable_name, EFI_GUID *vendor_guid, UINT32 *attributes, UINTN *data_size, - void *data) +efi_get_variable(CHAR16 *variable_name, EFI_GUID *vendor_guid, + UINT32 *attributes, UINTN *data_size, void *data) { - return RS->GetVariable(variable_name, vendor_guid, attributes, data_size, data); + return (RS->GetVariable(variable_name, vendor_guid, attributes, + data_size, data)); } EFI_STATUS -efi_set_variable(CHAR16 *variable_name, EFI_GUID *vendor_guid, UINT32 attributes, UINTN data_size, - void *data) +efi_set_variable(CHAR16 *variable_name, EFI_GUID *vendor_guid, + UINT32 attributes, UINTN data_size, void *data) { - return RS->SetVariable(variable_name, vendor_guid, attributes, data_size, data); + return (RS->SetVariable(variable_name, vendor_guid, attributes, + data_size, data)); } +void +efi_init_environment(void) +{ + char var[128]; + + snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16, + ST->Hdr.Revision & 0xffff); + env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset); +} + +COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show); + +static int +efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag) +{ + UINTN datasz, i; + EFI_STATUS status; + UINT32 attr; + CHAR16 *data; + char *str; + uint32_t uuid_status; + int is_ascii; + + datasz = 0; + status = RS->GetVariable(varnamearg, matchguid, &attr, + &datasz, NULL); + if (status != EFI_BUFFER_TOO_SMALL) { + printf("Can't get the variable: error %#lx\n", status); + return (CMD_ERROR); + } + data = malloc(datasz); + status = RS->GetVariable(varnamearg, matchguid, &attr, + &datasz, data); + if (status != EFI_SUCCESS) { + printf("Can't get the variable: error %#lx\n", status); + return (CMD_ERROR); + } + uuid_to_string((uuid_t *)matchguid, &str, &uuid_status); + if (lflag) { + printf("%s 0x%x %S", str, attr, varnamearg); + } else { + printf("%s 0x%x %S=", str, attr, varnamearg); + is_ascii = 1; + free(str); + str = (char *)data; + for (i = 0; i < datasz - 1; i++) { + /* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */ + if ((str[i] < 32 || str[i] > 126) && str[i] != 9 && str[i] != 10 && str[i] != 13) { + is_ascii = 0; + break; + } + } + if (str[datasz - 1] != '\0') + is_ascii = 0; + if (is_ascii) + printf("%s", str); + else { + for (i = 0; i < datasz / 2; i++) { + if (isalnum(data[i]) || isspace(data[i])) + printf("%c", data[i]); + else + printf("\\x%02x", data[i]); + } + } + } + free(data); + if (pager_output("\n")) + return (CMD_WARN); + return (CMD_OK); +} + +static int +command_efi_show(int argc, char *argv[]) +{ + /* + * efi-show [-a] + * print all the env + * efi-show -u UUID + * print all the env vars tagged with UUID + * efi-show -v var + * search all the env vars and print the ones matching var + * eif-show -u UUID -v var + * eif-show UUID var + * print all the env vars that match UUID and var + */ + /* NB: We assume EFI_GUID is the same as uuid_t */ + int aflag = 0, gflag = 0, lflag = 0, vflag = 0; + int ch, rv; + unsigned i; + EFI_STATUS status; + EFI_GUID varguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; + EFI_GUID matchguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; + uint32_t uuid_status; + CHAR16 *varname; + CHAR16 *newnm; + CHAR16 varnamearg[128]; + UINTN varalloc; + UINTN varsz; + + while ((ch = getopt(argc, argv, "ag:lv:")) != -1) { + switch (ch) { + case 'a': + aflag = 1; + break; + case 'g': + gflag = 1; + uuid_from_string(optarg, (uuid_t *)&matchguid, + &uuid_status); + if (uuid_status != uuid_s_ok) { + printf("uid %s could not be parsed\n", optarg); + return (CMD_ERROR); + } + break; + case 'l': + lflag = 1; + break; + case 'v': + vflag = 1; + if (strlen(optarg) >= nitems(varnamearg)) { + printf("Variable %s is longer than %zd characters\n", + optarg, nitems(varnamearg)); + return (CMD_ERROR); + } + for (i = 0; i < strlen(optarg); i++) + varnamearg[i] = optarg[i]; + varnamearg[i] = 0; + break; + default: + printf("Invalid argument %c\n", ch); + return (CMD_ERROR); + } + } + + if (aflag && (gflag || vflag)) { + printf("-a isn't compatible with -v or -u\n"); + return (CMD_ERROR); + } + + if (aflag && optind < argc) { + printf("-a doesn't take any args\n"); + return (CMD_ERROR); + } + + if (optind == argc) + aflag = 1; + + argc -= optind; + argv += optind; + + pager_open(); + if (vflag && gflag) { + rv = efi_print_var(varnamearg, &matchguid, lflag); + pager_close(); + return (rv); + } + + if (argc == 2) { + optarg = argv[0]; + if (strlen(optarg) >= nitems(varnamearg)) { + printf("Variable %s is longer than %zd characters\n", + optarg, nitems(varnamearg)); + pager_close(); + return (CMD_ERROR); + } + for (i = 0; i < strlen(optarg); i++) + varnamearg[i] = optarg[i]; + varnamearg[i] = 0; + optarg = argv[1]; + uuid_from_string(optarg, (uuid_t *)&matchguid, + &uuid_status); + if (uuid_status != uuid_s_ok) { + printf("uid %s could not be parsed\n", optarg); + pager_close(); + return (CMD_ERROR); + } + rv = efi_print_var(varnamearg, &matchguid, lflag); + pager_close(); + return (rv); + } + + if (argc > 0) { + printf("Too many args %d\n", argc); + pager_close(); + return (CMD_ERROR); + } + + /* + * Initiate the search -- note the standard takes pain + * to specify the initial call must be a poiner to a NULL + * character. + */ + varalloc = 1024; + varname = malloc(varalloc); + if (varname == NULL) { + printf("Can't allocate memory to get variables\n"); + pager_close(); + return (CMD_ERROR); + } + varname[0] = 0; + while (1) { + varsz = varalloc; + status = RS->GetNextVariableName(&varsz, varname, &varguid); + if (status == EFI_BUFFER_TOO_SMALL) { + varalloc = varsz; + newnm = realloc(varname, varalloc); + if (newnm == NULL) { + printf("Can't allocate memory to get variables\n"); + free(varname); + pager_close(); + return (CMD_ERROR); + } + varname = newnm; + continue; /* Try again with bigger buffer */ + } + if (status != EFI_SUCCESS) + break; + if (aflag) { + if (efi_print_var(varname, &varguid, lflag) != CMD_OK) + break; + continue; + } + if (vflag) { + if (wcscmp(varnamearg, varname) == 0) { + if (efi_print_var(varname, &varguid, lflag) != CMD_OK) + break; + continue; + } + } + if (gflag) { + if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) { + if (efi_print_var(varname, &varguid, lflag) != CMD_OK) + break; + continue; + } + } + } + free(varname); + pager_close(); + + return (CMD_OK); +} + +COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set); + +static int +command_efi_set(int argc, char *argv[]) +{ + char *uuid, *var, *val; + CHAR16 wvar[128]; + EFI_GUID guid; + uint32_t status; + EFI_STATUS err; + + if (argc != 4) { + printf("efi-set uuid var new-value\n"); + return (CMD_ERROR); + } + uuid = argv[1]; + var = argv[2]; + val = argv[3]; + uuid_from_string(uuid, (uuid_t *)&guid, &status); + if (status != uuid_s_ok) { + printf("Invalid uuid %s %d\n", uuid, status); + return (CMD_ERROR); + } + cpy8to16(var, wvar, sizeof(wvar)); + err = RS->SetVariable(wvar, &guid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, + strlen(val) + 1, val); + if (EFI_ERROR(err)) { + printf("Failed to set variable: error %lu\n", EFI_ERROR_CODE(err)); + return (CMD_ERROR); + } + return (CMD_OK); +} + +COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset); + +static int +command_efi_unset(int argc, char *argv[]) +{ + char *uuid, *var; + CHAR16 wvar[128]; + EFI_GUID guid; + uint32_t status; + EFI_STATUS err; + + if (argc != 3) { + printf("efi-unset uuid var\n"); + return (CMD_ERROR); + } + uuid = argv[1]; + var = argv[2]; + uuid_from_string(uuid, (uuid_t *)&guid, &status); + if (status != uuid_s_ok) { + printf("Invalid uuid %s\n", uuid); + return (CMD_ERROR); + } + cpy8to16(var, wvar, sizeof(wvar)); + err = RS->SetVariable(wvar, &guid, 0, 0, NULL); + if (EFI_ERROR(err)) { + printf("Failed to unset variable: error %lu\n", EFI_ERROR_CODE(err)); + return (CMD_ERROR); + } + return (CMD_OK); +} + +#ifdef BOOT_FORTH /* - * FreeBSD's loader interaction words and extras + * FreeBSD's loader interaction words and extras * - * efi-setenv ( value n name n guid n attr -- 0 | -1) - * efi-getenv ( guid n addr n -- addr' n' | -1 ) - * efi-unsetenv ( name n guid n'' -- ) + * efi-setenv ( value n name n guid n attr -- 0 | -1) + * efi-getenv ( guid n addr n -- addr' n' | -1 ) + * efi-unsetenv ( name n guid n'' -- ) */ /* * efi-setenv - * efi-setenv ( value n name n guid n attr -- 0 | -1) + * efi-setenv ( value n name n guid n attr -- 0 | -1) * * Set environment variables using the SetVariable EFI runtime service. * * Value and guid are passed through in binary form (so guid needs to be * converted to binary form from its string form). Name is converted from * ASCII to CHAR16. Since ficl doesn't have support for internationalization, * there's no native CHAR16 interface provided. * * attr is an int in the bitmask of the following attributes for this variable. * * 1 Non volatile * 2 Boot service access * 4 Run time access * (corresponding to the same bits in the UEFI spec). */ -void +static void ficlEfiSetenv(FICL_VM *pVM) { -#ifndef TESTMAIN char *value = NULL, *guid = NULL; CHAR16 *name = NULL; int i; -#endif char *namep, *valuep, *guidp; int names, values, guids, attr; - int status; + EFI_STATUS status; uuid_t u; uint32_t ustatus; + bool error = true; #if FICL_ROBUST > 1 vmCheckStack(pVM, 6, 0); #endif attr = stackPopINT(pVM->pStack); guids = stackPopINT(pVM->pStack); guidp = (char*)stackPopPtr(pVM->pStack); names = stackPopINT(pVM->pStack); namep = (char*)stackPopPtr(pVM->pStack); values = stackPopINT(pVM->pStack); valuep = (char*)stackPopPtr(pVM->pStack); -#ifndef TESTMAIN guid = (char*)ficlMalloc(guids); if (guid == NULL) - vmThrowErr(pVM, "Error: out of memory"); + goto out; memcpy(guid, guidp, guids); uuid_from_string(guid, &u, &ustatus); if (ustatus != uuid_s_ok) { stackPushINT(pVM->pStack, -1); goto out; } - name = (CHAR16 *)ficlMalloc((names + 1) * sizeof(CHAR16)); + name = ficlMalloc((names + 1) * sizeof(CHAR16)); if (name == NULL) - vmThrowErr(pVM, "Error: out of memory"); + goto out; for (i = 0; i < names; i++) name[i] = namep[i]; - name[names] = (CHAR16)0; + name[names] = 0; - value = (char*)ficlMalloc(values + 1); + value = ficlMalloc(values + 1); if (value == NULL) - vmThrowErr(pVM, "Error: out of memory"); + goto out; memcpy(value, valuep, values); status = efi_set_variable(name, (EFI_GUID *)&u, attr, values, value); if (status == EFI_SUCCESS) stackPushINT(pVM->pStack, 0); else stackPushINT(pVM->pStack, -1); + error = false; out: ficlFree(name); ficlFree(value); ficlFree(guid); -#endif - return; + if (error == true) + vmThrowErr(pVM, "Error: out of memory"); } -void +static void ficlEfiGetenv(FICL_VM *pVM) { -#ifndef TESTMAIN char *name, *value; -#endif char *namep; int names; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 2); #endif names = stackPopINT(pVM->pStack); namep = (char*) stackPopPtr(pVM->pStack); -#ifndef TESTMAIN name = (char*) ficlMalloc(names+1); if (name == NULL) vmThrowErr(pVM, "Error: out of memory"); strncpy(name, namep, names); name[names] = '\0'; value = getenv(name); ficlFree(name); if(value != NULL) { stackPushPtr(pVM->pStack, value); stackPushINT(pVM->pStack, strlen(value)); } else -#endif stackPushINT(pVM->pStack, -1); - - return; } -void +static void ficlEfiUnsetenv(FICL_VM *pVM) { -#ifndef TESTMAIN char *name; -#endif char *namep; int names; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif names = stackPopINT(pVM->pStack); namep = (char*) stackPopPtr(pVM->pStack); -#ifndef TESTMAIN name = (char*) ficlMalloc(names+1); if (name == NULL) vmThrowErr(pVM, "Error: out of memory"); strncpy(name, namep, names); name[names] = '\0'; unsetenv(name); ficlFree(name); -#endif - - return; } /************************************************************************** ** Add FreeBSD UEFI platform extensions into the system dictionary **************************************************************************/ void ficlEfiCompilePlatform(FICL_SYSTEM *pSys) { - FICL_DICT *dp = pSys->dp; - assert (dp); + FICL_DICT *dp = pSys->dp; + assert (dp); - dictAppendWord(dp, "efi-setenv", ficlEfiSetenv, FW_DEFAULT); - dictAppendWord(dp, "efi-getenv", ficlEfiGetenv, FW_DEFAULT); - dictAppendWord(dp, "efi-unsetenv", ficlEfiUnsetenv, FW_DEFAULT); - - /* Would like to export the EFI version, but this will do for now */ - ficlSetEnv(pSys, "efi-boot", 1); - - return; + dictAppendWord(dp, "efi-setenv", ficlEfiSetenv, FW_DEFAULT); + dictAppendWord(dp, "efi-getenv", ficlEfiGetenv, FW_DEFAULT); + dictAppendWord(dp, "efi-unsetenv", ficlEfiUnsetenv, FW_DEFAULT); } FICL_COMPILE_SET(ficlEfiCompilePlatform); + +#endif /* BOOT_FORTH */ Index: head/sys/boot/efi/libefi/wchar.c =================================================================== --- head/sys/boot/efi/libefi/wchar.c (nonexistent) +++ head/sys/boot/efi/libefi/wchar.c (revision 313042) @@ -0,0 +1,73 @@ +/*- + * Copyright 2016 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +/* + * CHAR16 related functions moved from loader. + * Perhaps we should move those to libstand afterall, but they are + * needed only by UEFI. + */ + +int +wcscmp(CHAR16 *a, CHAR16 *b) +{ + + while (*a && *b && *a == *b) { + a++; + b++; + } + return *a - *b; +} + +/* + * cpy8to16 copies a traditional C string into a CHAR16 string and + * 0 terminates it. len is the size of *dst in bytes. + */ +void +cpy8to16(const char *src, CHAR16 *dst, size_t len) +{ + len <<= 1; /* Assume CHAR16 is 2 bytes */ + while (len > 0 && *src) { + *dst++ = *src++; + len--; + } + *dst++ = (CHAR16)0; +} + +void +cpy16to8(const CHAR16 *src, char *dst, size_t len) +{ + size_t i; + + for (i = 0; i < len && src[i]; i++) + dst[i] = (char)src[i]; + if (i < len) + dst[i] = '\0'; +} Property changes on: head/sys/boot/efi/libefi/wchar.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: head/sys/boot/efi/loader/main.c =================================================================== --- head/sys/boot/efi/loader/main.c (revision 313041) +++ head/sys/boot/efi/loader/main.c (revision 313042) @@ -1,1109 +1,753 @@ /*- * Copyright (c) 2008-2010 Rui Paulo * Copyright (c) 2006 Marcel Moolenaar * 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 ``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 #ifdef EFI_ZFS_BOOT #include #endif #include "loader_efi.h" extern char bootprog_info[]; -#ifdef BOOT_FORTH -/* - * Normally, efi.o from libefi.a would be brought in due to a function we call - * there that's defined there. However, none of its functions are callable from - * here since it just adds words to the FORTH environment or implement those - * words. So, add a reference to a symbol in efi.o to force it to be be brought - * in so the init function there gets added to the "compile" linker set happens - * correctly. - * - * This assumes there's no global analysys that notices dummy1 isn't used - * anywhere and tries to eliminate it. - */ -extern int efi_variable_support; -int *dummy1 = &efi_variable_support; -#endif - struct arch_switch archsw; /* MI/MD interface boundary */ EFI_GUID acpi = ACPI_TABLE_GUID; EFI_GUID acpi20 = ACPI_20_TABLE_GUID; EFI_GUID devid = DEVICE_PATH_PROTOCOL; EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; EFI_GUID mps = MPS_TABLE_GUID; EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL; EFI_GUID smbios = SMBIOS_TABLE_GUID; EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; EFI_GUID hoblist = HOB_LIST_TABLE_GUID; EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; EFI_GUID fdtdtb = FDT_TABLE_GUID; EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; #ifdef EFI_ZFS_BOOT static void efi_zfs_probe(void); #endif -/* - * cpy8to16 copies a traditional C string into a CHAR16 string and - * 0 terminates it. len is the size of *dst in bytes. - */ -static void -cpy8to16(const char *src, CHAR16 *dst, size_t len) -{ - len <<= 1; /* Assume CHAR16 is 2 bytes */ - while (len > 0 && *src) { - *dst++ = *src++; - len--; - } - *dst++ = (CHAR16)0; -} - -static void -cpy16to8(const CHAR16 *src, char *dst, size_t len) -{ - size_t i; - - for (i = 0; i < len && src[i]; i++) - dst[i] = (char)src[i]; - if (i < len) - dst[i] = '\0'; -} - static int has_keyboard(void) { EFI_STATUS status; EFI_DEVICE_PATH *path; EFI_HANDLE *hin, *hin_end, *walker; UINTN sz; int retval = 0; /* * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and * do the typical dance to get the right sized buffer. */ sz = 0; hin = NULL; status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0); if (status == EFI_BUFFER_TOO_SMALL) { hin = (EFI_HANDLE *)malloc(sz); status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, hin); if (EFI_ERROR(status)) free(hin); } if (EFI_ERROR(status)) return retval; /* * Look at each of the handles. If it supports the device path protocol, * use it to get the device path for this handle. Then see if that * device path matches either the USB device path for keyboards or the * legacy device path for keyboards. */ hin_end = &hin[sz / sizeof(*hin)]; for (walker = hin; walker < hin_end; walker++) { status = BS->HandleProtocol(*walker, &devid, (VOID **)&path); if (EFI_ERROR(status)) continue; while (!IsDevicePathEnd(path)) { /* * Check for the ACPI keyboard node. All PNP3xx nodes * are keyboards of different flavors. Note: It is * unclear of there's always a keyboard node when * there's a keyboard controller, or if there's only one * when a keyboard is detected at boot. */ if (DevicePathType(path) == ACPI_DEVICE_PATH && (DevicePathSubType(path) == ACPI_DP || DevicePathSubType(path) == ACPI_EXTENDED_DP)) { ACPI_HID_DEVICE_PATH *acpi; acpi = (ACPI_HID_DEVICE_PATH *)(void *)path; if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 && (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) { retval = 1; goto out; } /* * Check for USB keyboard node, if present. Unlike a * PS/2 keyboard, these definitely only appear when * connected to the system. */ } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH && DevicePathSubType(path) == MSG_USB_CLASS_DP) { USB_CLASS_DEVICE_PATH *usb; usb = (USB_CLASS_DEVICE_PATH *)(void *)path; if (usb->DeviceClass == 3 && /* HID */ usb->DeviceSubClass == 1 && /* Boot devices */ usb->DeviceProtocol == 1) { /* Boot keyboards */ retval = 1; goto out; } } path = NextDevicePathNode(path); } } out: free(hin); return retval; } static int find_currdev(EFI_LOADED_IMAGE *img, struct devsw **dev, int *unit, uint64_t *extra) { EFI_DEVICE_PATH *devpath, *copy; EFI_HANDLE h; /* * 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) return (0); copy = NULL; devpath = efi_lookup_image_devpath(IH); while (devpath != NULL) { h = efi_devpath_handle(devpath); if (h == NULL) break; free(copy); copy = NULL; if (efi_handle_lookup(h, dev, unit, extra) == 0) return (0); devpath = efi_lookup_devpath(h); if (devpath != NULL) { copy = efi_devpath_trim(devpath); devpath = copy; } } free(copy); /* Try to fallback on first device */ if (devsw[0] != NULL) { *dev = devsw[0]; return (0); } return (ENOENT); } EFI_STATUS main(int argc, CHAR16 *argv[]) { char var[128]; EFI_LOADED_IMAGE *img; EFI_GUID *guid; int i, j, vargood, unit, howto; struct devsw *dev; uint64_t pool_guid; UINTN k; int has_kbd; char buf[40]; archsw.arch_autoload = efi_autoload; archsw.arch_getdev = efi_getdev; archsw.arch_copyin = efi_copyin; archsw.arch_copyout = efi_copyout; archsw.arch_readin = efi_readin; #ifdef EFI_ZFS_BOOT /* Note this needs to be set before ZFS init. */ archsw.arch_zfs_probe = efi_zfs_probe; #endif /* Init the time source */ efi_time_init(); has_kbd = has_keyboard(); /* * XXX Chicken-and-egg problem; we want to have console output * early, but some console attributes may depend on reading from * eg. the boot device, which we can't do yet. We can use * printf() etc. once this is done. */ cons_probe(); /* * Initialise the block cache. Set the upper limit. */ bcache_init(32768, 512); /* * 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 * 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. */ howto = 0; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { for (j = 1; argv[i][j] != 0; j++) { int ch; ch = argv[i][j]; switch (ch) { case 'a': howto |= RB_ASKNAME; break; case 'd': howto |= RB_KDB; break; case 'D': howto |= RB_MULTIPLE; break; case 'h': howto |= RB_SERIAL; break; case 'm': howto |= RB_MUTE; break; case 'p': howto |= RB_PAUSE; break; case 'P': if (!has_kbd) howto |= RB_SERIAL | RB_MULTIPLE; break; case 'r': howto |= RB_DFLTROOT; break; case 's': howto |= RB_SINGLE; break; case 'S': if (argv[i][j + 1] == 0) { if (i + 1 == argc) { setenv("comconsole_speed", "115200", 1); } else { cpy16to8(&argv[i + 1][0], var, sizeof(var)); setenv("comconsole_speedspeed", var, 1); } i++; break; } else { cpy16to8(&argv[i][j + 1], var, sizeof(var)); setenv("comconsole_speed", var, 1); break; } case 'v': howto |= RB_VERBOSE; break; } } } else { vargood = 0; for (j = 0; argv[i][j] != 0; j++) { if (j == sizeof(var)) { vargood = 0; break; } if (j > 0 && argv[i][j] == '=') vargood = 1; var[j] = (char)argv[i][j]; } if (vargood) { var[j] = 0; putenv(var); } } } for (i = 0; howto_names[i].ev != NULL; i++) if (howto & howto_names[i].mask) setenv(howto_names[i].ev, "YES", 1); if (howto & RB_MULTIPLE) { if (howto & RB_SERIAL) setenv("console", "comconsole efi" , 1); else setenv("console", "efi comconsole" , 1); } else if (howto & RB_SERIAL) { setenv("console", "comconsole" , 1); } if (efi_copy_init()) { printf("failed to allocate staging area\n"); return (EFI_BUFFER_TOO_SMALL); } /* * March through the device switch probing for things. */ for (i = 0; devsw[i] != NULL; i++) if (devsw[i]->dv_init != NULL) (devsw[i]->dv_init)(); /* Get our loaded image protocol interface structure. */ BS->HandleProtocol(IH, &imgid, (VOID**)&img); printf("Command line arguments:"); for (i = 0; i < argc; i++) printf(" %S", argv[i]); printf("\n"); printf("Image base: 0x%lx\n", (u_long)img->ImageBase); printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); printf("\n%s", bootprog_info); /* * Disable the watchdog timer. By default the boot manager sets * the timer to 5 minutes before invoking a boot option. If we * want to return to the boot manager, we have to disable the * watchdog timer and since we're an interactive program, we don't * want to wait until the user types "quit". The timer may have * fired by then. We don't care if this fails. It does not prevent * normal functioning in any way... */ BS->SetWatchdogTimer(0, 0, 0, NULL); if (find_currdev(img, &dev, &unit, &pool_guid) != 0) return (EFI_NOT_FOUND); switch (dev->dv_type) { #ifdef EFI_ZFS_BOOT case DEVT_ZFS: { struct zfs_devdesc currdev; currdev.d_dev = dev; currdev.d_unit = unit; currdev.d_type = currdev.d_dev->dv_type; currdev.d_opendata = NULL; currdev.pool_guid = pool_guid; currdev.root_guid = 0; env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), efi_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, env_nounset); init_zfs_bootenv(zfs_fmtdev(&currdev)); break; } #endif default: { struct devdesc currdev; currdev.d_dev = dev; currdev.d_unit = unit; currdev.d_opendata = NULL; currdev.d_type = currdev.d_dev->dv_type; env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), efi_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, env_nounset); break; } } - snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16, - ST->Hdr.Revision & 0xffff); - env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset); + efi_init_environment(); setenv("LINES", "24", 1); /* optional */ for (k = 0; k < ST->NumberOfTableEntries; k++) { guid = &ST->ConfigurationTable[k].VendorGuid; if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { snprintf(buf, sizeof(buf), "%p", ST->ConfigurationTable[k].VendorTable); setenv("hint.smbios.0.mem", buf, 1); smbios_detect(ST->ConfigurationTable[k].VendorTable); break; } } interact(NULL); /* doesn't return */ return (EFI_SUCCESS); /* keep compiler happy */ } -/* XXX move to lib stand ? */ -static int -wcscmp(CHAR16 *a, CHAR16 *b) -{ - - while (*a && *b && *a == *b) { - a++; - b++; - } - return *a - *b; -} - - COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int command_reboot(int argc, char *argv[]) { int i; for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 23, (CHAR16 *)"Reboot from the loader"); /* NOTREACHED */ return (CMD_ERROR); } COMMAND_SET(quit, "quit", "exit the loader", command_quit); static int command_quit(int argc, char *argv[]) { exit(0); return (CMD_OK); } COMMAND_SET(memmap, "memmap", "print memory map", command_memmap); static int command_memmap(int argc, char *argv[]) { UINTN sz; EFI_MEMORY_DESCRIPTOR *map, *p; UINTN key, dsz; UINT32 dver; EFI_STATUS status; int i, ndesc; char line[80]; static char *types[] = { "Reserved", "LoaderCode", "LoaderData", "BootServicesCode", "BootServicesData", "RuntimeServicesCode", "RuntimeServicesData", "ConventionalMemory", "UnusableMemory", "ACPIReclaimMemory", "ACPIMemoryNVS", "MemoryMappedIO", "MemoryMappedIOPortSpace", "PalCode" }; sz = 0; status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); if (status != EFI_BUFFER_TOO_SMALL) { printf("Can't determine memory map size\n"); return (CMD_ERROR); } map = malloc(sz); status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); if (EFI_ERROR(status)) { printf("Can't read memory map\n"); return (CMD_ERROR); } ndesc = sz / dsz; snprintf(line, sizeof(line), "%23s %12s %12s %8s %4s\n", "Type", "Physical", "Virtual", "#Pages", "Attr"); pager_open(); if (pager_output(line)) { pager_close(); return (CMD_OK); } for (i = 0, p = map; i < ndesc; i++, p = NextMemoryDescriptor(p, dsz)) { printf("%23s %012jx %012jx %08jx ", types[p->Type], (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart, (uintmax_t)p->NumberOfPages); if (p->Attribute & EFI_MEMORY_UC) printf("UC "); if (p->Attribute & EFI_MEMORY_WC) printf("WC "); if (p->Attribute & EFI_MEMORY_WT) printf("WT "); if (p->Attribute & EFI_MEMORY_WB) printf("WB "); if (p->Attribute & EFI_MEMORY_UCE) printf("UCE "); if (p->Attribute & EFI_MEMORY_WP) printf("WP "); if (p->Attribute & EFI_MEMORY_RP) printf("RP "); if (p->Attribute & EFI_MEMORY_XP) printf("XP "); if (pager_output("\n")) break; } pager_close(); return (CMD_OK); } COMMAND_SET(configuration, "configuration", "print configuration tables", command_configuration); static const char * guid_to_string(EFI_GUID *guid) { static char buf[40]; sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); return (buf); } static int command_configuration(int argc, char *argv[]) { char line[80]; UINTN i; snprintf(line, sizeof(line), "NumberOfTableEntries=%lu\n", (unsigned long)ST->NumberOfTableEntries); pager_open(); if (pager_output(line)) { pager_close(); return (CMD_OK); } for (i = 0; i < ST->NumberOfTableEntries; i++) { EFI_GUID *guid; printf(" "); guid = &ST->ConfigurationTable[i].VendorGuid; if (!memcmp(guid, &mps, sizeof(EFI_GUID))) printf("MPS Table"); else if (!memcmp(guid, &acpi, sizeof(EFI_GUID))) printf("ACPI Table"); else if (!memcmp(guid, &acpi20, sizeof(EFI_GUID))) printf("ACPI 2.0 Table"); else if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) printf("SMBIOS Table %p", ST->ConfigurationTable[i].VendorTable); else if (!memcmp(guid, &dxe, sizeof(EFI_GUID))) printf("DXE Table"); else if (!memcmp(guid, &hoblist, sizeof(EFI_GUID))) printf("HOB List Table"); else if (!memcmp(guid, &memtype, sizeof(EFI_GUID))) printf("Memory Type Information Table"); else if (!memcmp(guid, &debugimg, sizeof(EFI_GUID))) printf("Debug Image Info Table"); else if (!memcmp(guid, &fdtdtb, sizeof(EFI_GUID))) printf("FDT Table"); else printf("Unknown Table (%s)", guid_to_string(guid)); snprintf(line, sizeof(line), " at %p\n", ST->ConfigurationTable[i].VendorTable); if (pager_output(line)) break; } pager_close(); return (CMD_OK); } COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode); static int command_mode(int argc, char *argv[]) { UINTN cols, rows; unsigned int mode; int i; char *cp; char rowenv[8]; EFI_STATUS status; SIMPLE_TEXT_OUTPUT_INTERFACE *conout; extern void HO(void); conout = ST->ConOut; if (argc > 1) { mode = strtol(argv[1], &cp, 0); if (cp[0] != '\0') { printf("Invalid mode\n"); return (CMD_ERROR); } status = conout->QueryMode(conout, mode, &cols, &rows); if (EFI_ERROR(status)) { printf("invalid mode %d\n", mode); return (CMD_ERROR); } status = conout->SetMode(conout, mode); if (EFI_ERROR(status)) { printf("couldn't set mode %d\n", mode); return (CMD_ERROR); } sprintf(rowenv, "%u", (unsigned)rows); setenv("LINES", rowenv, 1); HO(); /* set cursor */ return (CMD_OK); } printf("Current mode: %d\n", conout->Mode->Mode); for (i = 0; i <= conout->Mode->MaxMode; i++) { status = conout->QueryMode(conout, i, &cols, &rows); if (EFI_ERROR(status)) continue; printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols, (unsigned)rows); } if (i != 0) printf("Select a mode with the command \"mode \"\n"); return (CMD_OK); } #ifdef EFI_ZFS_BOOT COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset", command_lszfs); static int command_lszfs(int argc, char *argv[]) { int err; if (argc != 2) { command_errmsg = "wrong number of arguments"; return (CMD_ERROR); } err = zfs_list(argv[1]); if (err != 0) { command_errmsg = strerror(err); return (CMD_ERROR); } return (CMD_OK); } COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments", command_reloadbe); static int command_reloadbe(int argc, char *argv[]) { int err; char *root; if (argc > 2) { command_errmsg = "wrong number of arguments"; return (CMD_ERROR); } if (argc == 2) { err = zfs_bootenv(argv[1]); } else { root = getenv("zfs_be_root"); if (root == NULL) { return (CMD_OK); } err = zfs_bootenv(root); } if (err != 0) { command_errmsg = strerror(err); return (CMD_ERROR); } return (CMD_OK); } #endif - -COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show); - -static int -efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag) -{ - UINTN datasz, i; - EFI_STATUS status; - UINT32 attr; - CHAR16 *data; - char *str; - uint32_t uuid_status; - int is_ascii; - - datasz = 0; - status = RS->GetVariable(varnamearg, matchguid, &attr, - &datasz, NULL); - if (status != EFI_BUFFER_TOO_SMALL) { - printf("Can't get the variable: error %#lx\n", status); - return (CMD_ERROR); - } - data = malloc(datasz); - status = RS->GetVariable(varnamearg, matchguid, &attr, - &datasz, data); - if (status != EFI_SUCCESS) { - printf("Can't get the variable: error %#lx\n", status); - return (CMD_ERROR); - } - uuid_to_string((uuid_t *)matchguid, &str, &uuid_status); - if (lflag) { - printf("%s 0x%x %S", str, attr, varnamearg); - } else { - printf("%s 0x%x %S=", str, attr, varnamearg); - is_ascii = 1; - free(str); - str = (char *)data; - for (i = 0; i < datasz - 1; i++) { - /* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */ - if ((str[i] < 32 || str[i] > 126) && str[i] != 9 && str[i] != 10 && str[i] != 13) { - is_ascii = 0; - break; - } - } - if (str[datasz - 1] != '\0') - is_ascii = 0; - if (is_ascii) - printf("%s", str); - else { - for (i = 0; i < datasz / 2; i++) { - if (isalnum(data[i]) || isspace(data[i])) - printf("%c", data[i]); - else - printf("\\x%02x", data[i]); - } - } - } - free(data); - if (pager_output("\n")) - return (CMD_WARN); - return (CMD_OK); -} - -static int -command_efi_show(int argc, char *argv[]) -{ - /* - * efi-show [-a] - * print all the env - * efi-show -u UUID - * print all the env vars tagged with UUID - * efi-show -v var - * search all the env vars and print the ones matching var - * eif-show -u UUID -v var - * eif-show UUID var - * print all the env vars that match UUID and var - */ - /* NB: We assume EFI_GUID is the same as uuid_t */ - int aflag = 0, gflag = 0, lflag = 0, vflag = 0; - int ch, rv; - unsigned i; - EFI_STATUS status; - EFI_GUID varguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; - EFI_GUID matchguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; - uint32_t uuid_status; - CHAR16 *varname; - CHAR16 *newnm; - CHAR16 varnamearg[128]; - UINTN varalloc; - UINTN varsz; - - while ((ch = getopt(argc, argv, "ag:lv:")) != -1) { - switch (ch) { - case 'a': - aflag = 1; - break; - case 'g': - gflag = 1; - uuid_from_string(optarg, (uuid_t *)&matchguid, - &uuid_status); - if (uuid_status != uuid_s_ok) { - printf("uid %s could not be parsed\n", optarg); - return (CMD_ERROR); - } - break; - case 'l': - lflag = 1; - break; - case 'v': - vflag = 1; - if (strlen(optarg) >= nitems(varnamearg)) { - printf("Variable %s is longer than %zd characters\n", - optarg, nitems(varnamearg)); - return (CMD_ERROR); - } - for (i = 0; i < strlen(optarg); i++) - varnamearg[i] = optarg[i]; - varnamearg[i] = 0; - break; - default: - printf("Invalid argument %c\n", ch); - return (CMD_ERROR); - } - } - - if (aflag && (gflag || vflag)) { - printf("-a isn't compatible with -v or -u\n"); - return (CMD_ERROR); - } - - if (aflag && optind < argc) { - printf("-a doesn't take any args"); - return (CMD_ERROR); - } - - if (optind == argc) - aflag = 1; - - argc -= optind; - argv += optind; - - pager_open(); - if (vflag && gflag) { - rv = efi_print_var(varnamearg, &matchguid, lflag); - pager_close(); - return (rv); - } - - if (argc == 2) { - optarg = argv[0]; - if (strlen(optarg) >= nitems(varnamearg)) { - printf("Variable %s is longer than %zd characters\n", - optarg, nitems(varnamearg)); - pager_close(); - return (CMD_ERROR); - } - for (i = 0; i < strlen(optarg); i++) - varnamearg[i] = optarg[i]; - varnamearg[i] = 0; - optarg = argv[1]; - uuid_from_string(optarg, (uuid_t *)&matchguid, - &uuid_status); - if (uuid_status != uuid_s_ok) { - printf("uid %s could not be parsed\n", optarg); - pager_close(); - return (CMD_ERROR); - } - rv = efi_print_var(varnamearg, &matchguid, lflag); - pager_close(); - return (rv); - } - - if (argc > 0) { - printf("Too many args %d\n", argc); - pager_close(); - return (CMD_ERROR); - } - - /* - * Initiate the search -- note the standard takes pain - * to specify the initial call must be a poiner to a NULL - * character. - */ - varalloc = 1024; - varname = malloc(varalloc); - if (varname == NULL) { - printf("Can't allocate memory to get variables\n"); - pager_close(); - return (CMD_ERROR); - } - varname[0] = 0; - while (1) { - varsz = varalloc; - status = RS->GetNextVariableName(&varsz, varname, &varguid); - if (status == EFI_BUFFER_TOO_SMALL) { - varalloc = varsz; - newnm = malloc(varalloc); - if (newnm == NULL) { - printf("Can't allocate memory to get variables\n"); - free(varname); - pager_close(); - return (CMD_ERROR); - } - memcpy(newnm, varname, varsz); - free(varname); - varname = newnm; - continue; /* Try again with bigger buffer */ - } - if (status != EFI_SUCCESS) - break; - if (aflag) { - if (efi_print_var(varname, &varguid, lflag) != CMD_OK) - break; - continue; - } - if (vflag) { - if (wcscmp(varnamearg, varname) == 0) { - if (efi_print_var(varname, &varguid, lflag) != CMD_OK) - break; - continue; - } - } - if (gflag) { - if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) { - if (efi_print_var(varname, &varguid, lflag) != CMD_OK) - break; - continue; - } - } - } - free(varname); - pager_close(); - - return (CMD_OK); -} - -COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set); - -static int -command_efi_set(int argc, char *argv[]) -{ - char *uuid, *var, *val; - CHAR16 wvar[128]; - EFI_GUID guid; - uint32_t status; - EFI_STATUS err; - - if (argc != 4) { - printf("efi-set uuid var new-value\n"); - return (CMD_ERROR); - } - uuid = argv[1]; - var = argv[2]; - val = argv[3]; - uuid_from_string(uuid, (uuid_t *)&guid, &status); - if (status != uuid_s_ok) { - printf("Invalid uuid %s %d\n", uuid, status); - return (CMD_ERROR); - } - cpy8to16(var, wvar, sizeof(wvar)); - err = RS->SetVariable(wvar, &guid, - EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, - strlen(val) + 1, val); - if (EFI_ERROR(err)) { - printf("Failed to set variable: error %lu\n", EFI_ERROR_CODE(err)); - return (CMD_ERROR); - } - return (CMD_OK); -} - -COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset); - -static int -command_efi_unset(int argc, char *argv[]) -{ - char *uuid, *var; - CHAR16 wvar[128]; - EFI_GUID guid; - uint32_t status; - EFI_STATUS err; - - if (argc != 3) { - printf("efi-unset uuid var\n"); - return (CMD_ERROR); - } - uuid = argv[1]; - var = argv[2]; - uuid_from_string(uuid, (uuid_t *)&guid, &status); - if (status != uuid_s_ok) { - printf("Invalid uuid %s\n", uuid); - return (CMD_ERROR); - } - cpy8to16(var, wvar, sizeof(wvar)); - err = RS->SetVariable(wvar, &guid, 0, 0, NULL); - if (EFI_ERROR(err)) { - printf("Failed to unset variable: error %lu\n", EFI_ERROR_CODE(err)); - return (CMD_ERROR); - } - return (CMD_OK); -} #ifdef LOADER_FDT_SUPPORT extern int command_fdt_internal(int argc, char *argv[]); /* * Since proper fdt command handling function is defined in fdt_loader_cmd.c, * and declaring it as extern is in contradiction with COMMAND_SET() macro * (which uses static pointer), we're defining wrapper function, which * calls the proper fdt handling routine. */ static int command_fdt(int argc, char *argv[]) { return (command_fdt_internal(argc, argv)); } COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); #endif #ifdef EFI_ZFS_BOOT static void efi_zfs_probe(void) { EFI_HANDLE h; u_int unit; int i; char dname[SPECNAMELEN + 1]; uint64_t guid; unit = 0; h = efi_find_handle(&efipart_dev, 0); for (i = 0; h != NULL; h = efi_find_handle(&efipart_dev, ++i)) { snprintf(dname, sizeof(dname), "%s%d:", efipart_dev.dv_name, i); if (zfs_probe_dev(dname, &guid) == 0) (void)efi_handle_update_dev(h, &zfs_dev, unit++, guid); } } #endif Index: head/sys/boot/forth/loader.4th =================================================================== --- head/sys/boot/forth/loader.4th (revision 313041) +++ head/sys/boot/forth/loader.4th (revision 313042) @@ -1,266 +1,266 @@ \ Copyright (c) 1999 Daniel C. Sobral \ Copyright (c) 2011-2015 Devin Teske \ 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$ only forth definitions s" arch-i386" environment? [if] [if] s" loader_version" environment? [if] 11 < [if] .( Loader version 1.1+ required) cr abort [then] [else] .( Could not get loader version!) cr abort [then] [then] [then] 256 dictthreshold ! \ 256 cells minimum free space 2048 dictincrease ! \ 2048 additional cells each time include /boot/support.4th include /boot/color.4th include /boot/delay.4th include /boot/check-password.4th -s" efi-boot" environment? [if] [if] +s" efi-version" getenv? [if] include /boot/efi.4th -[then] [then] +[then] only forth definitions : bootmsg ( -- ) loader_color? dup ( -- bool bool ) if 7 fg 4 bg then ." Booting..." if me then cr ; : try-menu-unset \ menu-unset may not be present s" beastie_disable" getenv dup -1 <> if s" YES" compare-insensitive 0= if exit then else drop then s" menu-unset" sfind if execute else drop then s" menusets-unset" sfind if execute else drop then ; only forth also support-functions also builtins definitions : boot 0= if ( interpreted ) get_arguments then \ Unload only if a path was passed dup if >r over r> swap c@ [char] - <> if 0 1 unload drop else s" kernelname" getenv? if ( a kernel has been loaded ) try-menu-unset bootmsg 1 boot exit then load_kernel_and_modules ?dup if exit then try-menu-unset bootmsg 0 1 boot exit then else s" kernelname" getenv? if ( a kernel has been loaded ) try-menu-unset bootmsg 1 boot exit then load_kernel_and_modules ?dup if exit then try-menu-unset bootmsg 0 1 boot exit then load_kernel_and_modules ?dup 0= if bootmsg 0 1 boot then ; \ ***** boot-conf \ \ Prepares to boot as specified by loaded configuration files. : boot-conf 0= if ( interpreted ) get_arguments then 0 1 unload drop load_kernel_and_modules ?dup 0= if 0 1 autoboot then ; also forth definitions previous builtin: boot builtin: boot-conf only forth definitions also support-functions \ ***** start \ \ Initializes support.4th global variables, sets loader_conf_files, \ processes conf files, and, if any one such file was successfully \ read to the end, loads kernel and modules. : start ( -- ) ( throws: abort & user-defined ) s" /boot/defaults/loader.conf" initialize include_conf_files include_nextboot_file \ If the user defined a post-initialize hook, call it now s" post-initialize" sfind if execute else drop then \ Will *NOT* try to load kernel and modules if no configuration file \ was successfully loaded! any_conf_read? if s" loader_delay" getenv -1 = if load_xen_throw load_kernel load_modules else drop ." Loading Kernel and Modules (Ctrl-C to Abort)" cr s" also support-functions" evaluate s" set delay_command='load_xen_throw load_kernel load_modules'" evaluate s" set delay_showdots" evaluate delay_execute then then ; \ ***** initialize \ \ Overrides support.4th initialization word with one that does \ everything start one does, short of loading the kernel and \ modules. Returns a flag. : initialize ( -- flag ) s" /boot/defaults/loader.conf" initialize include_conf_files include_nextboot_file \ If the user defined a post-initialize hook, call it now s" post-initialize" sfind if execute else drop then any_conf_read? ; \ ***** read-conf \ \ Read a configuration file, whose name was specified on the command \ line, if interpreted, or given on the stack, if compiled in. : (read-conf) ( addr len -- ) conf_files string= include_conf_files \ Will recurse on new loader_conf_files definitions ; : read-conf ( | addr len -- ) ( throws: abort & user-defined ) state @ if \ Compiling postpone (read-conf) else \ Interpreting bl parse (read-conf) then ; immediate \ show, enable, disable, toggle module loading. They all take module from \ the next word : set-module-flag ( module_addr val -- ) \ set and print flag over module.flag ! dup module.name strtype module.flag @ if ." will be loaded" else ." will not be loaded" then cr ; : enable-module find-module ?dup if true set-module-flag then ; : disable-module find-module ?dup if false set-module-flag then ; : toggle-module find-module ?dup if dup module.flag @ 0= set-module-flag then ; \ ***** show-module \ \ Show loading information about a module. : show-module ( -- ) find-module ?dup if show-one-module then ; \ Words to be used inside configuration files : retry false ; \ For use in load error commands : ignore true ; \ For use in load error commands \ Return to strict forth vocabulary : #type over - >r type r> spaces ; : .? 2 spaces 2swap 15 #type 2 spaces type cr ; \ Execute the ? command to print all the commands defined in \ C, then list the ones we support here. Please note that this \ doesn't use pager_* routines that the C implementation of ? \ does, so these will always appear, even if you stop early \ there. And they may cause the commands to scroll off the \ screen if the number of commands modulus LINES is close \ to LINEs.... : ? ['] ? execute s" boot-conf" s" load kernel and modules, then autoboot" .? s" read-conf" s" read a configuration file" .? s" enable-module" s" enable loading of a module" .? s" disable-module" s" disable loading of a module" .? s" toggle-module" s" toggle loading of a module" .? s" show-module" s" show module load data" .? s" try-include" s" try to load/interpret files" .? ; : try-include ( -- ) \ see loader.4th(8) ['] include ( -- xt ) \ get the execution token of `include' catch ( xt -- exception# | 0 ) if \ failed LF parse ( c -- s-addr/u ) 2drop \ advance >in to EOL (drop data) \ ... prevents words unused by `include' from being interpreted then ; immediate \ interpret immediately for access to `source' (aka tib) only forth definitions