1 /*- 2 * Copyright (c) 2016 Netflix, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <ctype.h> 31 #include <efivar.h> 32 #include <efivar-dp.h> 33 #include <err.h> 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <getopt.h> 37 #include <stddef.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include "efiutil.h" 43 #include "efichar.h" 44 45 /* options descriptor */ 46 static struct option longopts[] = { 47 { "append", no_argument, NULL, 'a' }, 48 { "ascii", no_argument, NULL, 'A' }, 49 { "attributes", required_argument, NULL, 't' }, 50 { "binary", no_argument, NULL, 'b' }, 51 { "delete", no_argument, NULL, 'D' }, 52 { "device", no_argument, NULL, 'd' }, 53 { "device-path", no_argument, NULL, 'd' }, 54 { "fromfile", required_argument, NULL, 'f' }, 55 { "guid", no_argument, NULL, 'g' }, 56 { "hex", no_argument, NULL, 'H' }, 57 { "list-guids", no_argument, NULL, 'L' }, 58 { "list", no_argument, NULL, 'l' }, 59 { "load-option", no_argument, NULL, 'O' }, 60 { "name", required_argument, NULL, 'n' }, 61 { "no-name", no_argument, NULL, 'N' }, 62 { "print", no_argument, NULL, 'p' }, 63 { "print-decimal", no_argument, NULL, 'd' }, 64 { "raw-guid", no_argument, NULL, 'R' }, 65 { "utf8", no_argument, NULL, 'u' }, 66 { "write", no_argument, NULL, 'w' }, 67 { NULL, 0, NULL, 0 } 68 }; 69 70 71 static int aflag, Aflag, bflag, dflag, Dflag, gflag, Hflag, Nflag, 72 lflag, Lflag, Rflag, wflag, pflag, uflag, load_opt_flag; 73 static char *varname; 74 static char *fromfile; 75 static u_long attrib = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; 76 77 static void 78 usage(void) 79 { 80 81 errx(1, "efivar [-abdDHlLNpRtuw] [-n name] [-f file] [--append] [--ascii]\n" 82 "\t[--attributes] [--binary] [--delete] [--fromfile file] [--hex]\n" 83 "\t[--list-guids] [--list] [--load-option] [--name name] [--no-name]\n" 84 "\t[--print] [--print-decimal] [--raw-guid] [--utf8] [--write]\n" 85 "\tname[=value]"); 86 } 87 88 static void 89 breakdown_name(char *name, efi_guid_t *guid, char **vname) 90 { 91 char *cp; 92 93 cp = strrchr(name, '-'); 94 if (cp == NULL) 95 errx(1, "Invalid name: %s", name); 96 *vname = cp + 1; 97 *cp = '\0'; 98 if (efi_name_to_guid(name, guid) < 0) 99 errx(1, "Invalid guid %s", name); 100 } 101 102 static uint8_t * 103 get_value(char *val, size_t *datalen) 104 { 105 static char buffer[16*1024]; 106 107 if (val != NULL) { 108 *datalen = strlen(val); 109 return ((uint8_t *)val); 110 } 111 /* Read from stdin */ 112 *datalen = sizeof(buffer); 113 *datalen = read(0, buffer, *datalen); 114 return ((uint8_t *)buffer); 115 } 116 117 static void 118 append_variable(char *name, char *val) 119 { 120 char *vname; 121 efi_guid_t guid; 122 size_t datalen; 123 uint8_t *data; 124 125 breakdown_name(name, &guid, &vname); 126 data = get_value(val, &datalen); 127 if (efi_append_variable(guid, vname, data, datalen, attrib) < 0) 128 err(1, "efi_append_variable"); 129 } 130 131 static void 132 delete_variable(char *name) 133 { 134 char *vname; 135 efi_guid_t guid; 136 137 breakdown_name(name, &guid, &vname); 138 if (efi_del_variable(guid, vname) < 0) 139 err(1, "efi_del_variable"); 140 } 141 142 static void 143 write_variable(char *name, char *val) 144 { 145 char *vname; 146 efi_guid_t guid; 147 size_t datalen; 148 uint8_t *data; 149 150 breakdown_name(name, &guid, &vname); 151 data = get_value(val, &datalen); 152 if (efi_set_variable(guid, vname, data, datalen, attrib) < 0) 153 err(1, "efi_set_variable"); 154 } 155 156 static void 157 devpath_dump(uint8_t *data, size_t datalen) 158 { 159 char buffer[1024]; 160 161 efidp_format_device_path(buffer, sizeof(buffer), 162 (const_efidp)data, datalen); 163 if (!Nflag) 164 printf(": "); 165 printf("%s\n", buffer); 166 } 167 168 static void 169 pretty_guid(efi_guid_t *guid, char **gname) 170 { 171 char *pretty = NULL; 172 173 if (gflag) 174 efi_guid_to_name(guid, &pretty); 175 176 if (pretty == NULL) 177 efi_guid_to_str(guid, gname); 178 else 179 *gname = pretty; 180 } 181 182 static void 183 print_var(efi_guid_t *guid, char *name) 184 { 185 uint32_t att; 186 uint8_t *data; 187 size_t datalen; 188 char *gname = NULL; 189 int rv; 190 191 if (guid) 192 pretty_guid(guid, &gname); 193 if (pflag || fromfile) { 194 if (fromfile) { 195 int fd; 196 197 fd = open(fromfile, O_RDONLY); 198 if (fd < 0) 199 err(1, "open %s", fromfile); 200 data = malloc(64 * 1024); 201 if (data == NULL) 202 err(1, "malloc"); 203 datalen = read(fd, data, 64 * 1024); 204 if (datalen <= 0) 205 err(1, "read"); 206 close(fd); 207 } else { 208 rv = efi_get_variable(*guid, name, &data, &datalen, &att); 209 if (rv < 0) 210 err(1, "fetching %s-%s", gname, name); 211 } 212 213 214 if (!Nflag) 215 printf("%s-%s\n", gname, name); 216 if (load_opt_flag) 217 efi_print_load_option(data, datalen, Aflag, bflag, uflag); 218 else if (Aflag) 219 asciidump(data, datalen); 220 else if (uflag) 221 utf8dump(data, datalen); 222 else if (bflag) 223 bindump(data, datalen); 224 else if (dflag) 225 devpath_dump(data, datalen); 226 else 227 hexdump(data, datalen); 228 } else { 229 printf("%s-%s", gname, name); 230 } 231 free(gname); 232 if (!Nflag) 233 printf("\n"); 234 } 235 236 static void 237 print_variable(char *name) 238 { 239 char *vname; 240 efi_guid_t guid; 241 242 breakdown_name(name, &guid, &vname); 243 print_var(&guid, vname); 244 } 245 246 static void 247 print_variables(void) 248 { 249 int rv; 250 char *name = NULL; 251 efi_guid_t *guid = NULL; 252 253 while ((rv = efi_get_next_variable_name(&guid, &name)) > 0) 254 print_var(guid, name); 255 256 if (rv < 0) 257 err(1, "Error listing names"); 258 } 259 260 static void 261 print_known_guid(void) 262 { 263 struct uuid_table *tbl; 264 int i, n; 265 266 n = efi_known_guid(&tbl); 267 for (i = 0; i < n; i++) 268 printf("%s %s\n", tbl[i].uuid_str, tbl[i].name); 269 } 270 271 static void 272 parse_args(int argc, char **argv) 273 { 274 int ch, i; 275 276 while ((ch = getopt_long(argc, argv, "aAbdDf:gHlLNn:OpRt:uw", 277 longopts, NULL)) != -1) { 278 switch (ch) { 279 case 'a': 280 aflag++; 281 break; 282 case 'A': 283 Aflag++; 284 break; 285 case 'b': 286 bflag++; 287 break; 288 case 'd': 289 dflag++; 290 break; 291 case 'D': 292 Dflag++; 293 break; 294 case 'g': 295 gflag++; 296 break; 297 case 'H': 298 Hflag++; 299 break; 300 case 'l': 301 lflag++; 302 break; 303 case 'L': 304 Lflag++; 305 break; 306 case 'n': 307 varname = optarg; 308 break; 309 case 'N': 310 Nflag++; 311 break; 312 case 'O': 313 load_opt_flag++; 314 break; 315 case 'p': 316 pflag++; 317 break; 318 case 'R': 319 Rflag++; 320 break; 321 case 't': 322 attrib = strtoul(optarg, NULL, 16); 323 break; 324 case 'u': 325 uflag++; 326 break; 327 case 'w': 328 wflag++; 329 break; 330 case 'f': 331 free(fromfile); 332 fromfile = strdup(optarg); 333 break; 334 case 0: 335 errx(1, "unknown or unimplemented option\n"); 336 break; 337 default: 338 usage(); 339 } 340 } 341 argc -= optind; 342 argv += optind; 343 344 if (argc == 1) 345 varname = argv[0]; 346 347 if (aflag + Dflag + wflag > 1) { 348 warnx("Can only use one of -a (--append), " 349 "-D (--delete) and -w (--write)"); 350 usage(); 351 } 352 353 if (aflag + Dflag + wflag > 0 && varname == NULL) { 354 warnx("Must specify a variable for -a (--append), " 355 "-D (--delete) or -w (--write)"); 356 usage(); 357 } 358 359 if (aflag) 360 append_variable(varname, NULL); 361 else if (Dflag) 362 delete_variable(varname); 363 else if (wflag) 364 write_variable(varname, NULL); 365 else if (Lflag) 366 print_known_guid(); 367 else if (fromfile) { 368 Nflag = 1; 369 print_var(NULL, NULL); 370 } else if (varname) { 371 pflag++; 372 print_variable(varname); 373 } else if (argc > 0) { 374 pflag++; 375 for (i = 0; i < argc; i++) 376 print_variable(argv[i]); 377 } else 378 print_variables(); 379 } 380 381 int 382 main(int argc, char **argv) 383 { 384 385 parse_args(argc, argv); 386 } 387