From owner-svn-src-all@freebsd.org Tue May 17 21:25:21 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id C54A5B3F7E3; Tue, 17 May 2016 21:25:21 +0000 (UTC) (envelope-from imp@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 8996C169D; Tue, 17 May 2016 21:25:21 +0000 (UTC) (envelope-from imp@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u4HLPKpV093117; Tue, 17 May 2016 21:25:20 GMT (envelope-from imp@FreeBSD.org) Received: (from imp@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u4HLPKYo093113; Tue, 17 May 2016 21:25:20 GMT (envelope-from imp@FreeBSD.org) Message-Id: <201605172125.u4HLPKYo093113@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: imp set sender to imp@FreeBSD.org using -f From: Warner Losh Date: Tue, 17 May 2016 21:25:20 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r300081 - in head/sys/boot: efi/libefi efi/loader ficl X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 17 May 2016 21:25:21 -0000 Author: imp Date: Tue May 17 21:25:20 2016 New Revision: 300081 URL: https://svnweb.freebsd.org/changeset/base/300081 Log: Implement UEFI set environment variable, as well as exporting the EFI version. This is also scriptable, though additional scripting will be needed. Differential Review: https://reviews.freebsd.org/D4494 MFC After: 3 days Added: head/sys/boot/efi/libefi/env.c (contents, props changed) head/sys/boot/ficl/efi.c (contents, props changed) Modified: head/sys/boot/efi/libefi/Makefile head/sys/boot/efi/loader/main.c Modified: head/sys/boot/efi/libefi/Makefile ============================================================================== --- head/sys/boot/efi/libefi/Makefile Tue May 17 21:25:18 2016 (r300080) +++ head/sys/boot/efi/libefi/Makefile Tue May 17 21:25:20 2016 (r300081) @@ -4,7 +4,7 @@ LIB= efi INTERNALLIB= WARNS?= 2 -SRCS= delay.c efi_console.c efinet.c efipart.c errno.c handles.c \ +SRCS= delay.c efi_console.c efinet.c efipart.c env.c errno.c handles.c \ libefi.c time.c .if ${MACHINE_CPUARCH} == "aarch64" Added: head/sys/boot/efi/libefi/env.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/efi/libefi/env.c Tue May 17 21:25:20 2016 (r300081) @@ -0,0 +1,55 @@ +/* + * 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 + +/* + * 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) +{ + 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) +{ + 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) +{ + return RS->SetVariable(variable_name, vendor_guid, attributes, data_size, data); +} Modified: head/sys/boot/efi/loader/main.c ============================================================================== --- head/sys/boot/efi/loader/main.c Tue May 17 21:25:18 2016 (r300080) +++ head/sys/boot/efi/loader/main.c Tue May 17 21:25:20 2016 (r300081) @@ -38,6 +38,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include + #include #include @@ -336,11 +338,8 @@ main(int argc, CHAR16 *argv[]) 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: "); - /* printf doesn't understand EFI Unicode */ - ST->ConOut->OutputString(ST->ConOut, ST->FirmwareVendor); - printf(" (rev %d.%02d)\n", ST->FirmwareRevision >> 16, - ST->FirmwareRevision & 0xffff); + printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor, + ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); printf("\n"); printf("%s, Revision %s\n", bootprog_name, bootprog_rev); @@ -394,6 +393,9 @@ main(int argc, CHAR16 *argv[]) } } + 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); setenv("LINES", "24", 1); /* optional */ for (k = 0; k < ST->NumberOfTableEntries; k++) { @@ -409,6 +411,19 @@ main(int argc, CHAR16 *argv[]) 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 @@ -616,6 +631,7 @@ command_mode(int argc, char *argv[]) } +/* deprecated */ COMMAND_SET(nvram, "nvram", "get or set NVRAM variables", command_nvram); static int @@ -718,6 +734,186 @@ command_reloadbe(int argc, char *argv[]) } #endif +COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_printenv); + +static int +efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag) +{ + UINTN datasz; + EFI_STATUS status; + UINT32 attr; + CHAR16 *data; + char *str; + uint32_t uuid_status; + + 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); + printf("%s %S=%S\n", str, varnamearg, data); + free(str); + free(data); + return (CMD_OK); +} + +static int +command_efi_printenv(int argc, char *argv[]) +{ + /* + * efi-printenv [-a] + * print all the env + * efi-printenv -u UUID + * print all the env vars tagged with UUID + * efi-printenv -v var + * search all the env vars and print the ones matching var + * eif-printenv -u UUID -v var + * eif-printenv UUID var + * print all the env vars that match UUID and var + */ + /* XXX We assume EFI_GUID is the same as uuid_t */ + int aflag = 0, gflag = 0, lflag = 0, vflag = 0; + int ch; + 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[128]; + CHAR16 varnamearg[128]; + 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; + 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; + + if (vflag && gflag) + return efi_print_var(varnamearg, &matchguid, lflag); + + if (argc == 2) { + optarg = argv[0]; + 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; + 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); + return (CMD_ERROR); + } + return efi_print_var(varnamearg, &matchguid, lflag); + } + + if (argc != 0) { + printf("Too many args\n"); + return (CMD_ERROR); + } + + /* + * Initiate the search -- note the standard takes pain + * to specify the initial call must be a poiner to a NULL + * character. + */ + varsz = nitems(varname); + varname[0] = 0; + status = RS->GetNextVariableName(&varsz, varname, &varguid); + while (status != EFI_NOT_FOUND) { + status = RS->GetNextVariableName(&varsz, varname, + &varguid); + if (aflag) { + efi_print_var(varname, &varguid, lflag); + continue; + } + if (vflag) { + if (wcscmp(varnamearg, varname) == 0) + efi_print_var(varname, &varguid, lflag); + } + if (gflag) { + if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) + efi_print_var(varname, &varguid, lflag); + } + } + + return (CMD_OK); +} + +COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set); + +static int +command_efi_set(int argc, char *argv[]) +{ + return (CMD_OK); +} + +COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset); + +static int +command_efi_unset(int argc, char *argv[]) +{ + return (CMD_OK); +} + #ifdef LOADER_FDT_SUPPORT extern int command_fdt_internal(int argc, char *argv[]); Added: head/sys/boot/ficl/efi.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/ficl/efi.c Tue May 17 21:25:20 2016 (r300081) @@ -0,0 +1,207 @@ +/*- + * Copyright (c) 2014 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$ + */ + +/******************************************************************* +** e f i . c +** Additional words for EFI +** +*******************************************************************/ + +#ifdef TESTMAIN +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif +#include "bootstrap.h" +#include +#include "ficl.h" + +/* + * 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 + * 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 +ficlEfiSetenv(FICL_VM *pVM) +{ +#ifndef TESTMAIN + char *value, *guid; + CHAR16 *name + int i; +#endif + char *namep, *valuep, *guidp; + int names, values, guids, attr; + +#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"); + memcpy(guid, guidp, guids); + + name = (char*)ficlMalloc((names + 1) * sizeof(CHAR16)); + if (name == NULL) + vmThrowErr(pVM, "Error: out of memory"); + for (i = 0; i < names; i++) + name[i] = namep[i]; + name[names] = (CHAR16)0; + + value = (char*)ficlMalloc(values + 1); + if (value != NULL) + vmThrowErr(pVM, "Error: out of memory"); + memcpy(value, valuep, values); + + status = efi_set_variable(name, guid, attr, value); + if (status == EFI_SUCCESS) + stackPushINT(pVM->pStack, 0); + else + stackPushINT(pVM->pStack, -1); + + ficlFree(name); + ficlFree(value); + ficlFree(guid); +#endif + + return; +} + +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) + 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 +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) + vmThrowErr(pVM, "Error: out of memory"); + strncpy(name, namep, names); + name[names] = '\0'; + + unsetenv(name); + ficlFree(name); +#endif + + return; +} +/************************************************************************** + +** Build FreeBSD platform extensions into the system dictionary +**************************************************************************/ +void ficlEfiCompilePlatform(FICL_SYSTEM *pSys) +{ + 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); + + return; +}