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 /* 39 * Simple wrappers to the underlying UEFI functions. 40 * See http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES 41 * for details. 42 */ 43 EFI_STATUS 44 efi_get_next_variable_name(UINTN *variable_name_size, CHAR16 *variable_name, 45 EFI_GUID *vendor_guid) 46 { 47 return (RS->GetNextVariableName(variable_name_size, variable_name, 48 vendor_guid)); 49 } 50 51 EFI_STATUS 52 efi_get_variable(CHAR16 *variable_name, EFI_GUID *vendor_guid, 53 UINT32 *attributes, UINTN *data_size, void *data) 54 { 55 return (RS->GetVariable(variable_name, vendor_guid, attributes, 56 data_size, data)); 57 } 58 59 EFI_STATUS 60 efi_set_variable(CHAR16 *variable_name, EFI_GUID *vendor_guid, 61 UINT32 attributes, UINTN data_size, void *data) 62 { 63 return (RS->SetVariable(variable_name, vendor_guid, attributes, 64 data_size, data)); 65 } 66 67 void 68 efi_init_environment(void) 69 { 70 char var[128]; 71 72 snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16, 73 ST->Hdr.Revision & 0xffff); 74 env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset); 75 } 76 77 COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show); 78 79 static int 80 efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag) 81 { 82 UINTN datasz, i; 83 EFI_STATUS status; 84 UINT32 attr; 85 CHAR16 *data; 86 char *str; 87 uint32_t uuid_status; 88 int is_ascii; 89 90 datasz = 0; 91 status = RS->GetVariable(varnamearg, matchguid, &attr, 92 &datasz, NULL); 93 if (status != EFI_BUFFER_TOO_SMALL) { 94 printf("Can't get the variable: error %#lx\n", 95 EFI_ERROR_CODE(status)); 96 return (CMD_ERROR); 97 } 98 data = malloc(datasz); 99 status = RS->GetVariable(varnamearg, matchguid, &attr, 100 &datasz, data); 101 if (status != EFI_SUCCESS) { 102 printf("Can't get the variable: error %#lx\n", 103 EFI_ERROR_CODE(status)); 104 return (CMD_ERROR); 105 } 106 uuid_to_string((uuid_t *)matchguid, &str, &uuid_status); 107 if (lflag) { 108 printf("%s 0x%x %S", str, attr, varnamearg); 109 } else { 110 printf("%s 0x%x %S=", str, attr, varnamearg); 111 is_ascii = 1; 112 free(str); 113 str = (char *)data; 114 for (i = 0; i < datasz - 1; i++) { 115 /* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */ 116 if ((str[i] < 32 || str[i] > 126) && str[i] != 9 && str[i] != 10 && str[i] != 13) { 117 is_ascii = 0; 118 break; 119 } 120 } 121 if (str[datasz - 1] != '\0') 122 is_ascii = 0; 123 if (is_ascii) 124 printf("%s", str); 125 else { 126 for (i = 0; i < datasz / 2; i++) { 127 if (isalnum(data[i]) || isspace(data[i])) 128 printf("%c", data[i]); 129 else 130 printf("\\x%02x", data[i]); 131 } 132 } 133 } 134 free(data); 135 if (pager_output("\n")) 136 return (CMD_WARN); 137 return (CMD_OK); 138 } 139 140 static int 141 command_efi_show(int argc, char *argv[]) 142 { 143 /* 144 * efi-show [-a] 145 * print all the env 146 * efi-show -u UUID 147 * print all the env vars tagged with UUID 148 * efi-show -v var 149 * search all the env vars and print the ones matching var 150 * eif-show -u UUID -v var 151 * eif-show UUID var 152 * print all the env vars that match UUID and var 153 */ 154 /* NB: We assume EFI_GUID is the same as uuid_t */ 155 int aflag = 0, gflag = 0, lflag = 0, vflag = 0; 156 int ch, rv; 157 unsigned i; 158 EFI_STATUS status; 159 EFI_GUID varguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; 160 EFI_GUID matchguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; 161 uint32_t uuid_status; 162 CHAR16 *varname; 163 CHAR16 *newnm; 164 CHAR16 varnamearg[128]; 165 UINTN varalloc; 166 UINTN varsz; 167 168 while ((ch = getopt(argc, argv, "ag:lv:")) != -1) { 169 switch (ch) { 170 case 'a': 171 aflag = 1; 172 break; 173 case 'g': 174 gflag = 1; 175 uuid_from_string(optarg, (uuid_t *)&matchguid, 176 &uuid_status); 177 if (uuid_status != uuid_s_ok) { 178 printf("uid %s could not be parsed\n", optarg); 179 return (CMD_ERROR); 180 } 181 break; 182 case 'l': 183 lflag = 1; 184 break; 185 case 'v': 186 vflag = 1; 187 if (strlen(optarg) >= nitems(varnamearg)) { 188 printf("Variable %s is longer than %zd characters\n", 189 optarg, nitems(varnamearg)); 190 return (CMD_ERROR); 191 } 192 for (i = 0; i < strlen(optarg); i++) 193 varnamearg[i] = optarg[i]; 194 varnamearg[i] = 0; 195 break; 196 default: 197 printf("Invalid argument %c\n", ch); 198 return (CMD_ERROR); 199 } 200 } 201 202 if (aflag && (gflag || vflag)) { 203 printf("-a isn't compatible with -v or -u\n"); 204 return (CMD_ERROR); 205 } 206 207 if (aflag && optind < argc) { 208 printf("-a doesn't take any args\n"); 209 return (CMD_ERROR); 210 } 211 212 if (optind == argc) 213 aflag = 1; 214 215 argc -= optind; 216 argv += optind; 217 218 pager_open(); 219 if (vflag && gflag) { 220 rv = efi_print_var(varnamearg, &matchguid, lflag); 221 pager_close(); 222 return (rv); 223 } 224 225 if (argc == 2) { 226 optarg = argv[0]; 227 if (strlen(optarg) >= nitems(varnamearg)) { 228 printf("Variable %s is longer than %zd characters\n", 229 optarg, nitems(varnamearg)); 230 pager_close(); 231 return (CMD_ERROR); 232 } 233 for (i = 0; i < strlen(optarg); i++) 234 varnamearg[i] = optarg[i]; 235 varnamearg[i] = 0; 236 optarg = argv[1]; 237 uuid_from_string(optarg, (uuid_t *)&matchguid, 238 &uuid_status); 239 if (uuid_status != uuid_s_ok) { 240 printf("uid %s could not be parsed\n", optarg); 241 pager_close(); 242 return (CMD_ERROR); 243 } 244 rv = efi_print_var(varnamearg, &matchguid, lflag); 245 pager_close(); 246 return (rv); 247 } 248 249 if (argc > 0) { 250 printf("Too many args %d\n", argc); 251 pager_close(); 252 return (CMD_ERROR); 253 } 254 255 /* 256 * Initiate the search -- note the standard takes pain 257 * to specify the initial call must be a poiner to a NULL 258 * character. 259 */ 260 varalloc = 1024; 261 varname = malloc(varalloc); 262 if (varname == NULL) { 263 printf("Can't allocate memory to get variables\n"); 264 pager_close(); 265 return (CMD_ERROR); 266 } 267 varname[0] = 0; 268 while (1) { 269 varsz = varalloc; 270 status = RS->GetNextVariableName(&varsz, varname, &varguid); 271 if (status == EFI_BUFFER_TOO_SMALL) { 272 varalloc = varsz; 273 newnm = realloc(varname, varalloc); 274 if (newnm == NULL) { 275 printf("Can't allocate memory to get variables\n"); 276 free(varname); 277 pager_close(); 278 return (CMD_ERROR); 279 } 280 varname = newnm; 281 continue; /* Try again with bigger buffer */ 282 } 283 if (status != EFI_SUCCESS) 284 break; 285 if (aflag) { 286 if (efi_print_var(varname, &varguid, lflag) != CMD_OK) 287 break; 288 continue; 289 } 290 if (vflag) { 291 if (wcscmp(varnamearg, varname) == 0) { 292 if (efi_print_var(varname, &varguid, lflag) != CMD_OK) 293 break; 294 continue; 295 } 296 } 297 if (gflag) { 298 if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) { 299 if (efi_print_var(varname, &varguid, lflag) != CMD_OK) 300 break; 301 continue; 302 } 303 } 304 } 305 free(varname); 306 pager_close(); 307 308 return (CMD_OK); 309 } 310 311 COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set); 312 313 static int 314 command_efi_set(int argc, char *argv[]) 315 { 316 char *uuid, *var, *val; 317 CHAR16 wvar[128]; 318 EFI_GUID guid; 319 uint32_t status; 320 EFI_STATUS err; 321 322 if (argc != 4) { 323 printf("efi-set uuid var new-value\n"); 324 return (CMD_ERROR); 325 } 326 uuid = argv[1]; 327 var = argv[2]; 328 val = argv[3]; 329 uuid_from_string(uuid, (uuid_t *)&guid, &status); 330 if (status != uuid_s_ok) { 331 printf("Invalid uuid %s %d\n", uuid, status); 332 return (CMD_ERROR); 333 } 334 cpy8to16(var, wvar, sizeof(wvar)); 335 err = RS->SetVariable(wvar, &guid, 336 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, 337 strlen(val) + 1, val); 338 if (EFI_ERROR(err)) { 339 printf("Failed to set variable: error %lu\n", EFI_ERROR_CODE(err)); 340 return (CMD_ERROR); 341 } 342 return (CMD_OK); 343 } 344 345 COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset); 346 347 static int 348 command_efi_unset(int argc, char *argv[]) 349 { 350 char *uuid, *var; 351 CHAR16 wvar[128]; 352 EFI_GUID guid; 353 uint32_t status; 354 EFI_STATUS err; 355 356 if (argc != 3) { 357 printf("efi-unset uuid var\n"); 358 return (CMD_ERROR); 359 } 360 uuid = argv[1]; 361 var = argv[2]; 362 uuid_from_string(uuid, (uuid_t *)&guid, &status); 363 if (status != uuid_s_ok) { 364 printf("Invalid uuid %s\n", uuid); 365 return (CMD_ERROR); 366 } 367 cpy8to16(var, wvar, sizeof(wvar)); 368 err = RS->SetVariable(wvar, &guid, 0, 0, NULL); 369 if (EFI_ERROR(err)) { 370 printf("Failed to unset variable: error %lu\n", EFI_ERROR_CODE(err)); 371 return (CMD_ERROR); 372 } 373 return (CMD_OK); 374 } 375