env.c revision 346472
1300081Simp/* 2300081Simp * Copyright (c) 2015 Netflix, Inc. All Rights Reserved. 3300081Simp * 4300081Simp * Redistribution and use in source and binary forms, with or without 5300081Simp * modification, are permitted provided that the following conditions 6300081Simp * are met: 7300081Simp * 1. Redistributions of source code must retain the above copyright 8300081Simp * notice, this list of conditions and the following disclaimer. 9300081Simp * 2. Redistributions in binary form must reproduce the above copyright 10300081Simp * notice, this list of conditions and the following disclaimer in the 11300081Simp * documentation and/or other materials provided with the distribution. 12300081Simp * 13300081Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14300081Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15300081Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16300081Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17300081Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18300081Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19300081Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20300081Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21300081Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22300081Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23300081Simp * SUCH DAMAGE. 24300081Simp */ 25300081Simp 26300081Simp#include <sys/cdefs.h> 27300081Simp__FBSDID("$FreeBSD: stable/11/stand/efi/libefi/env.c 346472 2019-04-21 03:16:09Z kevans $"); 28300081Simp 29329099Skevans#include <sys/param.h> 30329011Skevans#include <stand.h> 31329011Skevans#include <string.h> 32300081Simp#include <efi.h> 33300081Simp#include <efilib.h> 34329011Skevans#include <uuid.h> 35329099Skevans#include <stdbool.h> 36329011Skevans#include "bootstrap.h" 37300081Simp 38329099Skevansvoid 39329099Skevansefi_init_environment(void) 40329099Skevans{ 41329099Skevans char var[128]; 42329099Skevans 43329099Skevans snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16, 44329099Skevans ST->Hdr.Revision & 0xffff); 45329099Skevans env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset); 46329099Skevans} 47329099Skevans 48329099SkevansCOMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show); 49329099Skevans 50329099Skevansstatic int 51329099Skevansefi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag) 52329099Skevans{ 53329099Skevans UINTN datasz, i; 54329099Skevans EFI_STATUS status; 55329099Skevans UINT32 attr; 56329099Skevans CHAR16 *data; 57329099Skevans char *str; 58329099Skevans uint32_t uuid_status; 59329099Skevans int is_ascii; 60329099Skevans 61329099Skevans datasz = 0; 62329099Skevans status = RS->GetVariable(varnamearg, matchguid, &attr, 63329099Skevans &datasz, NULL); 64329099Skevans if (status != EFI_BUFFER_TOO_SMALL) { 65329099Skevans printf("Can't get the variable: error %#lx\n", 66329099Skevans EFI_ERROR_CODE(status)); 67329099Skevans return (CMD_ERROR); 68329099Skevans } 69329099Skevans data = malloc(datasz); 70329099Skevans status = RS->GetVariable(varnamearg, matchguid, &attr, 71329099Skevans &datasz, data); 72329099Skevans if (status != EFI_SUCCESS) { 73329099Skevans printf("Can't get the variable: error %#lx\n", 74329099Skevans EFI_ERROR_CODE(status)); 75329099Skevans return (CMD_ERROR); 76329099Skevans } 77329099Skevans uuid_to_string((uuid_t *)matchguid, &str, &uuid_status); 78329099Skevans if (lflag) { 79329099Skevans printf("%s 0x%x %S", str, attr, varnamearg); 80329099Skevans } else { 81329099Skevans printf("%s 0x%x %S=", str, attr, varnamearg); 82329099Skevans is_ascii = 1; 83329099Skevans free(str); 84329099Skevans str = (char *)data; 85329099Skevans for (i = 0; i < datasz - 1; i++) { 86329099Skevans /* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */ 87329099Skevans if ((str[i] < 32 || str[i] > 126) && str[i] != 9 && str[i] != 10 && str[i] != 13) { 88329099Skevans is_ascii = 0; 89329099Skevans break; 90329099Skevans } 91329099Skevans } 92329099Skevans if (str[datasz - 1] != '\0') 93329099Skevans is_ascii = 0; 94329099Skevans if (is_ascii) 95329099Skevans printf("%s", str); 96329099Skevans else { 97329099Skevans for (i = 0; i < datasz / 2; i++) { 98329099Skevans if (isalnum(data[i]) || isspace(data[i])) 99329099Skevans printf("%c", data[i]); 100329099Skevans else 101329099Skevans printf("\\x%02x", data[i]); 102329099Skevans } 103329099Skevans } 104329099Skevans } 105329099Skevans free(data); 106329099Skevans if (pager_output("\n")) 107329099Skevans return (CMD_WARN); 108329099Skevans return (CMD_OK); 109329099Skevans} 110329099Skevans 111329099Skevansstatic int 112329099Skevanscommand_efi_show(int argc, char *argv[]) 113329099Skevans{ 114329099Skevans /* 115329099Skevans * efi-show [-a] 116329099Skevans * print all the env 117346472Skevans * efi-show -g UUID 118329099Skevans * print all the env vars tagged with UUID 119329099Skevans * efi-show -v var 120329099Skevans * search all the env vars and print the ones matching var 121346472Skevans * efi-show -g UUID -v var 122346472Skevans * efi-show UUID var 123329099Skevans * print all the env vars that match UUID and var 124329099Skevans */ 125329099Skevans /* NB: We assume EFI_GUID is the same as uuid_t */ 126329099Skevans int aflag = 0, gflag = 0, lflag = 0, vflag = 0; 127329099Skevans int ch, rv; 128329099Skevans unsigned i; 129329099Skevans EFI_STATUS status; 130329099Skevans EFI_GUID varguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; 131329099Skevans EFI_GUID matchguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; 132329099Skevans uint32_t uuid_status; 133329099Skevans CHAR16 *varname; 134329099Skevans CHAR16 *newnm; 135329099Skevans CHAR16 varnamearg[128]; 136329099Skevans UINTN varalloc; 137329099Skevans UINTN varsz; 138329099Skevans 139329099Skevans while ((ch = getopt(argc, argv, "ag:lv:")) != -1) { 140329099Skevans switch (ch) { 141329099Skevans case 'a': 142329099Skevans aflag = 1; 143329099Skevans break; 144329099Skevans case 'g': 145329099Skevans gflag = 1; 146329099Skevans uuid_from_string(optarg, (uuid_t *)&matchguid, 147329099Skevans &uuid_status); 148329099Skevans if (uuid_status != uuid_s_ok) { 149329099Skevans printf("uid %s could not be parsed\n", optarg); 150329099Skevans return (CMD_ERROR); 151329099Skevans } 152329099Skevans break; 153329099Skevans case 'l': 154329099Skevans lflag = 1; 155329099Skevans break; 156329099Skevans case 'v': 157329099Skevans vflag = 1; 158329099Skevans if (strlen(optarg) >= nitems(varnamearg)) { 159329099Skevans printf("Variable %s is longer than %zd characters\n", 160329099Skevans optarg, nitems(varnamearg)); 161329099Skevans return (CMD_ERROR); 162329099Skevans } 163329099Skevans for (i = 0; i < strlen(optarg); i++) 164329099Skevans varnamearg[i] = optarg[i]; 165329099Skevans varnamearg[i] = 0; 166329099Skevans break; 167329099Skevans default: 168329099Skevans printf("Invalid argument %c\n", ch); 169329099Skevans return (CMD_ERROR); 170329099Skevans } 171329099Skevans } 172329099Skevans 173329099Skevans if (aflag && (gflag || vflag)) { 174329099Skevans printf("-a isn't compatible with -v or -u\n"); 175329099Skevans return (CMD_ERROR); 176329099Skevans } 177329099Skevans 178329099Skevans if (aflag && optind < argc) { 179329099Skevans printf("-a doesn't take any args\n"); 180329099Skevans return (CMD_ERROR); 181329099Skevans } 182329099Skevans 183329099Skevans if (optind == argc) 184329099Skevans aflag = 1; 185329099Skevans 186329099Skevans argc -= optind; 187329099Skevans argv += optind; 188329099Skevans 189329099Skevans pager_open(); 190329099Skevans if (vflag && gflag) { 191329099Skevans rv = efi_print_var(varnamearg, &matchguid, lflag); 192329099Skevans pager_close(); 193329099Skevans return (rv); 194329099Skevans } 195329099Skevans 196329099Skevans if (argc == 2) { 197329099Skevans optarg = argv[0]; 198329099Skevans if (strlen(optarg) >= nitems(varnamearg)) { 199329099Skevans printf("Variable %s is longer than %zd characters\n", 200329099Skevans optarg, nitems(varnamearg)); 201329099Skevans pager_close(); 202329099Skevans return (CMD_ERROR); 203329099Skevans } 204329099Skevans for (i = 0; i < strlen(optarg); i++) 205329099Skevans varnamearg[i] = optarg[i]; 206329099Skevans varnamearg[i] = 0; 207329099Skevans optarg = argv[1]; 208329099Skevans uuid_from_string(optarg, (uuid_t *)&matchguid, 209329099Skevans &uuid_status); 210329099Skevans if (uuid_status != uuid_s_ok) { 211329099Skevans printf("uid %s could not be parsed\n", optarg); 212329099Skevans pager_close(); 213329099Skevans return (CMD_ERROR); 214329099Skevans } 215329099Skevans rv = efi_print_var(varnamearg, &matchguid, lflag); 216329099Skevans pager_close(); 217329099Skevans return (rv); 218329099Skevans } 219329099Skevans 220329099Skevans if (argc > 0) { 221329099Skevans printf("Too many args %d\n", argc); 222329099Skevans pager_close(); 223329099Skevans return (CMD_ERROR); 224329099Skevans } 225329099Skevans 226329099Skevans /* 227329099Skevans * Initiate the search -- note the standard takes pain 228329099Skevans * to specify the initial call must be a poiner to a NULL 229329099Skevans * character. 230329099Skevans */ 231329099Skevans varalloc = 1024; 232329099Skevans varname = malloc(varalloc); 233329099Skevans if (varname == NULL) { 234329099Skevans printf("Can't allocate memory to get variables\n"); 235329099Skevans pager_close(); 236329099Skevans return (CMD_ERROR); 237329099Skevans } 238329099Skevans varname[0] = 0; 239329099Skevans while (1) { 240329099Skevans varsz = varalloc; 241329099Skevans status = RS->GetNextVariableName(&varsz, varname, &varguid); 242329099Skevans if (status == EFI_BUFFER_TOO_SMALL) { 243329099Skevans varalloc = varsz; 244329099Skevans newnm = realloc(varname, varalloc); 245329099Skevans if (newnm == NULL) { 246329099Skevans printf("Can't allocate memory to get variables\n"); 247329099Skevans free(varname); 248329099Skevans pager_close(); 249329099Skevans return (CMD_ERROR); 250329099Skevans } 251329099Skevans varname = newnm; 252329099Skevans continue; /* Try again with bigger buffer */ 253329099Skevans } 254329099Skevans if (status != EFI_SUCCESS) 255329099Skevans break; 256329099Skevans if (aflag) { 257329099Skevans if (efi_print_var(varname, &varguid, lflag) != CMD_OK) 258329099Skevans break; 259329099Skevans continue; 260329099Skevans } 261329099Skevans if (vflag) { 262329099Skevans if (wcscmp(varnamearg, varname) == 0) { 263329099Skevans if (efi_print_var(varname, &varguid, lflag) != CMD_OK) 264329099Skevans break; 265329099Skevans continue; 266329099Skevans } 267329099Skevans } 268329099Skevans if (gflag) { 269329099Skevans if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) { 270329099Skevans if (efi_print_var(varname, &varguid, lflag) != CMD_OK) 271329099Skevans break; 272329099Skevans continue; 273329099Skevans } 274329099Skevans } 275329099Skevans } 276329099Skevans free(varname); 277329099Skevans pager_close(); 278329099Skevans 279329099Skevans return (CMD_OK); 280329099Skevans} 281329099Skevans 282329099SkevansCOMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set); 283329099Skevans 284329099Skevansstatic int 285329099Skevanscommand_efi_set(int argc, char *argv[]) 286329099Skevans{ 287329099Skevans char *uuid, *var, *val; 288329099Skevans CHAR16 wvar[128]; 289329099Skevans EFI_GUID guid; 290329099Skevans uint32_t status; 291329099Skevans EFI_STATUS err; 292329099Skevans 293329099Skevans if (argc != 4) { 294329099Skevans printf("efi-set uuid var new-value\n"); 295329099Skevans return (CMD_ERROR); 296329099Skevans } 297329099Skevans uuid = argv[1]; 298329099Skevans var = argv[2]; 299329099Skevans val = argv[3]; 300329099Skevans uuid_from_string(uuid, (uuid_t *)&guid, &status); 301329099Skevans if (status != uuid_s_ok) { 302329099Skevans printf("Invalid uuid %s %d\n", uuid, status); 303329099Skevans return (CMD_ERROR); 304329099Skevans } 305329099Skevans cpy8to16(var, wvar, sizeof(wvar)); 306329099Skevans err = RS->SetVariable(wvar, &guid, 307329099Skevans EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, 308329099Skevans strlen(val) + 1, val); 309329099Skevans if (EFI_ERROR(err)) { 310329099Skevans printf("Failed to set variable: error %lu\n", EFI_ERROR_CODE(err)); 311329099Skevans return (CMD_ERROR); 312329099Skevans } 313329099Skevans return (CMD_OK); 314329099Skevans} 315329099Skevans 316329099SkevansCOMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset); 317329099Skevans 318329099Skevansstatic int 319329099Skevanscommand_efi_unset(int argc, char *argv[]) 320329099Skevans{ 321329099Skevans char *uuid, *var; 322329099Skevans CHAR16 wvar[128]; 323329099Skevans EFI_GUID guid; 324329099Skevans uint32_t status; 325329099Skevans EFI_STATUS err; 326329099Skevans 327329099Skevans if (argc != 3) { 328329099Skevans printf("efi-unset uuid var\n"); 329329099Skevans return (CMD_ERROR); 330329099Skevans } 331329099Skevans uuid = argv[1]; 332329099Skevans var = argv[2]; 333329099Skevans uuid_from_string(uuid, (uuid_t *)&guid, &status); 334329099Skevans if (status != uuid_s_ok) { 335329099Skevans printf("Invalid uuid %s\n", uuid); 336329099Skevans return (CMD_ERROR); 337329099Skevans } 338329099Skevans cpy8to16(var, wvar, sizeof(wvar)); 339329099Skevans err = RS->SetVariable(wvar, &guid, 0, 0, NULL); 340329099Skevans if (EFI_ERROR(err)) { 341329099Skevans printf("Failed to unset variable: error %lu\n", EFI_ERROR_CODE(err)); 342329099Skevans return (CMD_ERROR); 343329099Skevans } 344329099Skevans return (CMD_OK); 345329099Skevans} 346