From 35f0d9ff069bce02aa41d96431ddfb0b984beb44 Mon Sep 17 00:00:00 2001 From: Sascha Wildner Date: Mon, 28 Nov 2016 23:35:25 +0100 Subject: [PATCH] boot/efi: Add three new commands, efi-show, efi-set and efi-unset. For accessing EFI variables. These replace the nvram command. Taken-from: FreeBSD --- sys/boot/efi/loader/main.c | 346 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 313 insertions(+), 33 deletions(-) diff --git a/sys/boot/efi/loader/main.c b/sys/boot/efi/loader/main.c index 09fe9a10d3..ce19f24aa3 100644 --- a/sys/boot/efi/loader/main.c +++ b/sys/boot/efi/loader/main.c @@ -38,6 +38,8 @@ __FBSDID("$FreeBSD: head/sys/boot/efi/loader/main.c 295408 2016-02-08 19:34:17Z #include #include +#include + #include #include @@ -78,6 +80,21 @@ print_str16(const CHAR16 *str) printf("%c", (char)str[i]); } +/* + * cpy8to16 copies a traditional C string into a CHAR16 string and + * 0 terminates it. len is the size of *dst in bytes. + */ +static void +cpy8to16(const char *src, CHAR16 *dst, size_t len) +{ + len <<= 1; /* Assume CHAR16 is 2 bytes */ + while (len > 0 && *src) { + *dst++ = *src++; + len--; + } + *dst++ = (CHAR16)0; +} + static void cp16to8(const CHAR16 *src, char *dst, size_t len) { @@ -380,6 +397,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 @@ -586,50 +616,300 @@ command_mode(int argc, char *argv[]) return (CMD_OK); } - -COMMAND_SET(nvram, "nvram", "get or set NVRAM variables", command_nvram); +COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show); static int -command_nvram(int argc, char *argv[]) +efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag) { - CHAR16 var[128]; - CHAR16 *data; - EFI_STATUS status; - EFI_GUID varguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; - UINTN varsz, datasz, i; - SIMPLE_TEXT_OUTPUT_INTERFACE *conout; - - conout = ST->ConOut; - - /* Initiate the search */ - status = RS->GetNextVariableName(&varsz, NULL, NULL); - - for (; status != EFI_NOT_FOUND; ) { - status = RS->GetNextVariableName(&varsz, var, &varguid); - //if (EFI_ERROR(status)) - //break; - - conout->OutputString(conout, var); - printf("="); - datasz = 0; - status = RS->GetVariable(var, &varguid, NULL, &datasz, NULL); - /* XXX: check status */ - data = malloc(datasz); - status = RS->GetVariable(var, &varguid, NULL, &datasz, data); - if (EFI_ERROR(status)) - printf(""); + UINTN datasz, i; + EFI_STATUS status; + UINT32 attr; + CHAR16 *data; + char *str; + uint32_t uuid_status; + int is_ascii; + + 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); + if (lflag) { + printf("%s 0x%x %S", str, attr, varnamearg); + } else { + printf("%s 0x%x %S=", str, attr, varnamearg); + is_ascii = 1; + free(str); + str = (char *)data; + for (i = 0; i < datasz - 1; i++) { + /* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */ + if ((str[i] < 32 || str[i] > 126) && str[i] != 9 && str[i] != 10 && str[i] != 13) { + is_ascii = 0; + break; + } + } + if (str[datasz - 1] != '\0') + is_ascii = 0; + if (is_ascii) + printf("%s", str); else { - for (i = 0; i < datasz; i++) { + for (i = 0; i < datasz / 2; i++) { if (isalnum(data[i]) || isspace(data[i])) printf("%c", data[i]); else printf("\\x%02x", data[i]); } } - /* XXX */ - pager_output("\n"); - free(data); } + free(data); + pager_output("\n"); + return (CMD_OK); +} + +static int +command_efi_show(int argc, char *argv[]) +{ + /* + * efi-show [-a] + * print all the env + * efi-show -u UUID + * print all the env vars tagged with UUID + * efi-show -v var + * search all the env vars and print the ones matching var + * eif-show -u UUID -v var + * eif-show UUID var + * print all the env vars that match UUID and var + */ + /* NB: We assume EFI_GUID is the same as uuid_t */ + int aflag = 0, gflag = 0, lflag = 0, vflag = 0; + int ch, rv; + 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; + CHAR16 *newnm; + CHAR16 varnamearg[128]; + UINTN varalloc; + 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; + break; + 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; + pager_open(); + if (vflag && gflag) { + rv = efi_print_var(varnamearg, &matchguid, lflag); + pager_close(); + return (rv); + } + + if (argc == 2) { + optarg = argv[0]; + if (strlen(optarg) >= nitems(varnamearg)) { + printf("Variable %s is longer than %zd characters\n", + optarg, nitems(varnamearg)); + pager_close(); + 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); + pager_close(); + return (CMD_ERROR); + } + rv = efi_print_var(varnamearg, &matchguid, lflag); + pager_close(); + return (rv); + } + + if (argc > 0) { + printf("Too many args %d\n", argc); + pager_close(); + return (CMD_ERROR); + } + + /* + * Initiate the search -- note the standard takes pain + * to specify the initial call must be a poiner to a NULL + * character. + */ + varalloc = 1024; + varname = malloc(varalloc); + if (varname == NULL) { + printf("Can't allocate memory to get variables\n"); + pager_close(); + return (CMD_ERROR); + } + varname[0] = 0; + while (1) { + varsz = varalloc; + status = RS->GetNextVariableName(&varsz, varname, &varguid); + if (status == EFI_BUFFER_TOO_SMALL) { + varalloc = varsz; + newnm = malloc(varalloc); + if (newnm == NULL) { + printf("Can't allocate memory to get variables\n"); + free(varname); + pager_close(); + return (CMD_ERROR); + } + memcpy(newnm, varname, varsz); + free(varname); + varname = newnm; + continue; /* Try again with bigger buffer */ + } + if (status != EFI_SUCCESS) + break; + if (aflag) { + if (efi_print_var(varname, &varguid, lflag) != CMD_OK) + break; + continue; + } + if (vflag) { + if (wcscmp(varnamearg, varname) == 0) { + if (efi_print_var(varname, &varguid, lflag) != CMD_OK) + break; + continue; + } + } + if (gflag) { + if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) { + if (efi_print_var(varname, &varguid, lflag) != CMD_OK) + break; + continue; + } + } + } + free(varname); + pager_close(); + + return (CMD_OK); +} + +COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set); + +static int +command_efi_set(int argc, char *argv[]) +{ + char *uuid, *var, *val; + CHAR16 wvar[128]; + EFI_GUID guid; + uint32_t status; + EFI_STATUS err; + + if (argc != 4) { + printf("efi-set uuid var new-value\n"); + return (CMD_ERROR); + } + uuid = argv[1]; + var = argv[2]; + val = argv[3]; + uuid_from_string(uuid, (uuid_t *)&guid, &status); + if (status != uuid_s_ok) { + printf("Invalid uuid %s %d\n", uuid, status); + return (CMD_ERROR); + } + cpy8to16(var, wvar, sizeof(wvar)); + err = RS->SetVariable(wvar, &guid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, + strlen(val) + 1, val); + if (EFI_ERROR(err)) { + printf("Failed to set variable: error %lu\n", EFI_ERROR_CODE(err)); + return (CMD_ERROR); + } + return (CMD_OK); +} + +COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset); + +static int +command_efi_unset(int argc, char *argv[]) +{ + char *uuid, *var; + CHAR16 wvar[128]; + EFI_GUID guid; + uint32_t status; + EFI_STATUS err; + + if (argc != 3) { + printf("efi-unset uuid var\n"); + return (CMD_ERROR); + } + uuid = argv[1]; + var = argv[2]; + uuid_from_string(uuid, (uuid_t *)&guid, &status); + if (status != uuid_s_ok) { + printf("Invalid uuid %s\n", uuid); + return (CMD_ERROR); + } + cpy8to16(var, wvar, sizeof(wvar)); + err = RS->SetVariable(wvar, &guid, 0, 0, NULL); + if (EFI_ERROR(err)) { + printf("Failed to unset variable: error %lu\n", EFI_ERROR_CODE(err)); + return (CMD_ERROR); + } return (CMD_OK); } -- 2.11.4.GIT