1 /* 2 * Copyright (c) 2015 Netflix, Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 #include <sys/param.h> 30 #include <stand.h> 31 #include <string.h> 32 #include <efi.h> 33 #include <efilib.h> 34 #include <uuid.h> 35 #include <stdbool.h> 36 #include "bootstrap.h" 37 38 void 39 efi_init_environment(void) 40 { 41 char var[128]; 42 43 snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16, 44 ST->Hdr.Revision & 0xffff); 45 env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset); 46 } 47 48 COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show); 49 50 static int 51 efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag) 52 { 53 UINTN datasz, i; 54 EFI_STATUS status; 55 UINT32 attr; 56 CHAR16 *data; 57 char *str; 58 uint32_t uuid_status; 59 int is_ascii; 60 61 datasz = 0; 62 status = RS->GetVariable(varnamearg, matchguid, &attr, 63 &datasz, NULL); 64 if (status != EFI_BUFFER_TOO_SMALL) { 65 printf("Can't get the variable: error %#lx\n", 66 EFI_ERROR_CODE(status)); 67 return (CMD_ERROR); 68 } 69 data = malloc(datasz); 70 status = RS->GetVariable(varnamearg, matchguid, &attr, 71 &datasz, data); 72 if (status != EFI_SUCCESS) { 73 printf("Can't get the variable: error %#lx\n", 74 EFI_ERROR_CODE(status)); 75 return (CMD_ERROR); 76 } 77 uuid_to_string((uuid_t *)matchguid, &str, &uuid_status); 78 if (lflag) { 79 printf("%s 0x%x %S", str, attr, varnamearg); 80 } else { 81 printf("%s 0x%x %S=", str, attr, varnamearg); 82 is_ascii = 1; 83 free(str); 84 str = (char *)data; 85 for (i = 0; i < datasz - 1; i++) { 86 /* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */ 87 if ((str[i] < 32 || str[i] > 126) && str[i] != 9 && str[i] != 10 && str[i] != 13) { 88 is_ascii = 0; 89 break; 90 } 91 } 92 if (str[datasz - 1] != '\0') 93 is_ascii = 0; 94 if (is_ascii) 95 printf("%s", str); 96 else { 97 for (i = 0; i < datasz / 2; i++) { 98 if (isalnum(data[i]) || isspace(data[i])) 99 printf("%c", data[i]); 100 else 101 printf("\\x%02x", data[i]); 102 } 103 } 104 } 105 free(data); 106 if (pager_output("\n")) 107 return (CMD_WARN); 108 return (CMD_OK); 109 } 110 111 static int 112 command_efi_show(int argc, char *argv[]) 113 { 114 /* 115 * efi-show [-a] 116 * print all the env 117 * efi-show -g UUID 118 * print all the env vars tagged with UUID 119 * efi-show -v var 120 * search all the env vars and print the ones matching var 121 * efi-show -g UUID -v var 122 * efi-show UUID var 123 * print all the env vars that match UUID and var 124 */ 125 /* NB: We assume EFI_GUID is the same as uuid_t */ 126 int aflag = 0, gflag = 0, lflag = 0, vflag = 0; 127 int ch, rv; 128 unsigned i; 129 EFI_STATUS status; 130 EFI_GUID varguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; 131 EFI_GUID matchguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; 132 uint32_t uuid_status; 133 CHAR16 *varname; 134 CHAR16 *newnm; 135 CHAR16 varnamearg[128]; 136 UINTN varalloc; 137 UINTN varsz; 138 139 while ((ch = getopt(argc, argv, "ag:lv:")) != -1) { 140 switch (ch) { 141 case 'a': 142 aflag = 1; 143 break; 144 case 'g': 145 gflag = 1; 146 uuid_from_string(optarg, (uuid_t *)&matchguid, 147 &uuid_status); 148 if (uuid_status != uuid_s_ok) { 149 printf("uid %s could not be parsed\n", optarg); 150 return (CMD_ERROR); 151 } 152 break; 153 case 'l': 154 lflag = 1; 155 break; 156 case 'v': 157 vflag = 1; 158 if (strlen(optarg) >= nitems(varnamearg)) { 159 printf("Variable %s is longer than %zd characters\n", 160 optarg, nitems(varnamearg)); 161 return (CMD_ERROR); 162 } 163 for (i = 0; i < strlen(optarg); i++) 164 varnamearg[i] = optarg[i]; 165 varnamearg[i] = 0; 166 break; 167 default: 168 printf("Invalid argument %c\n", ch); 169 return (CMD_ERROR); 170 } 171 } 172 173 if (aflag && (gflag || vflag)) { 174 printf("-a isn't compatible with -v or -u\n"); 175 return (CMD_ERROR); 176 } 177 178 if (aflag && optind < argc) { 179 printf("-a doesn't take any args\n"); 180 return (CMD_ERROR); 181 } 182 183 if (optind == argc) 184 aflag = 1; 185 186 argc -= optind; 187 argv += optind; 188 189 pager_open(); 190 if (vflag && gflag) { 191 rv = efi_print_var(varnamearg, &matchguid, lflag); 192 pager_close(); 193 return (rv); 194 } 195 196 if (argc == 2) { 197 optarg = argv[0]; 198 if (strlen(optarg) >= nitems(varnamearg)) { 199 printf("Variable %s is longer than %zd characters\n", 200 optarg, nitems(varnamearg)); 201 pager_close(); 202 return (CMD_ERROR); 203 } 204 for (i = 0; i < strlen(optarg); i++) 205 varnamearg[i] = optarg[i]; 206 varnamearg[i] = 0; 207 optarg = argv[1]; 208 uuid_from_string(optarg, (uuid_t *)&matchguid, 209 &uuid_status); 210 if (uuid_status != uuid_s_ok) { 211 printf("uid %s could not be parsed\n", optarg); 212 pager_close(); 213 return (CMD_ERROR); 214 } 215 rv = efi_print_var(varnamearg, &matchguid, lflag); 216 pager_close(); 217 return (rv); 218 } 219 220 if (argc > 0) { 221 printf("Too many args %d\n", argc); 222 pager_close(); 223 return (CMD_ERROR); 224 } 225 226 /* 227 * Initiate the search -- note the standard takes pain 228 * to specify the initial call must be a poiner to a NULL 229 * character. 230 */ 231 varalloc = 1024; 232 varname = malloc(varalloc); 233 if (varname == NULL) { 234 printf("Can't allocate memory to get variables\n"); 235 pager_close(); 236 return (CMD_ERROR); 237 } 238 varname[0] = 0; 239 while (1) { 240 varsz = varalloc; 241 status = RS->GetNextVariableName(&varsz, varname, &varguid); 242 if (status == EFI_BUFFER_TOO_SMALL) { 243 varalloc = varsz; 244 newnm = realloc(varname, varalloc); 245 if (newnm == NULL) { 246 printf("Can't allocate memory to get variables\n"); 247 free(varname); 248 pager_close(); 249 return (CMD_ERROR); 250 } 251 varname = newnm; 252 continue; /* Try again with bigger buffer */ 253 } 254 if (status != EFI_SUCCESS) 255 break; 256 if (aflag) { 257 if (efi_print_var(varname, &varguid, lflag) != CMD_OK) 258 break; 259 continue; 260 } 261 if (vflag) { 262 if (wcscmp(varnamearg, varname) == 0) { 263 if (efi_print_var(varname, &varguid, lflag) != CMD_OK) 264 break; 265 continue; 266 } 267 } 268 if (gflag) { 269 if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) { 270 if (efi_print_var(varname, &varguid, lflag) != CMD_OK) 271 break; 272 continue; 273 } 274 } 275 } 276 free(varname); 277 pager_close(); 278 279 return (CMD_OK); 280 } 281 282 COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set); 283 284 static int 285 command_efi_set(int argc, char *argv[]) 286 { 287 char *uuid, *var, *val; 288 CHAR16 wvar[128]; 289 EFI_GUID guid; 290 uint32_t status; 291 EFI_STATUS err; 292 293 if (argc != 4) { 294 printf("efi-set uuid var new-value\n"); 295 return (CMD_ERROR); 296 } 297 uuid = argv[1]; 298 var = argv[2]; 299 val = argv[3]; 300 uuid_from_string(uuid, (uuid_t *)&guid, &status); 301 if (status != uuid_s_ok) { 302 printf("Invalid uuid %s %d\n", uuid, status); 303 return (CMD_ERROR); 304 } 305 cpy8to16(var, wvar, sizeof(wvar)); 306 err = RS->SetVariable(wvar, &guid, 307 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, 308 strlen(val) + 1, val); 309 if (EFI_ERROR(err)) { 310 printf("Failed to set variable: error %lu\n", EFI_ERROR_CODE(err)); 311 return (CMD_ERROR); 312 } 313 return (CMD_OK); 314 } 315 316 COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset); 317 318 static int 319 command_efi_unset(int argc, char *argv[]) 320 { 321 char *uuid, *var; 322 CHAR16 wvar[128]; 323 EFI_GUID guid; 324 uint32_t status; 325 EFI_STATUS err; 326 327 if (argc != 3) { 328 printf("efi-unset uuid var\n"); 329 return (CMD_ERROR); 330 } 331 uuid = argv[1]; 332 var = argv[2]; 333 uuid_from_string(uuid, (uuid_t *)&guid, &status); 334 if (status != uuid_s_ok) { 335 printf("Invalid uuid %s\n", uuid); 336 return (CMD_ERROR); 337 } 338 cpy8to16(var, wvar, sizeof(wvar)); 339 err = RS->SetVariable(wvar, &guid, 0, 0, NULL); 340 if (EFI_ERROR(err)) { 341 printf("Failed to unset variable: error %lu\n", EFI_ERROR_CODE(err)); 342 return (CMD_ERROR); 343 } 344 return (CMD_OK); 345 } 346