1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Integrate UEFI variables to u-boot env interface 4 * 5 * Copyright (c) 2018 AKASHI Takahiro, Linaro Limited 6 */ 7 8 #include <charset.h> 9 #include <common.h> 10 #include <command.h> 11 #include <efi_loader.h> 12 #include <exports.h> 13 #include <hexdump.h> 14 #include <malloc.h> 15 #include <linux/kernel.h> 16 17 /* 18 * From efi_variable.c, 19 * 20 * Mapping between UEFI variables and u-boot variables: 21 * 22 * efi_$guid_$varname = {attributes}(type)value 23 */ 24 25 static const struct { 26 u32 mask; 27 char *text; 28 } efi_var_attrs[] = { 29 {EFI_VARIABLE_NON_VOLATILE, "NV"}, 30 {EFI_VARIABLE_BOOTSERVICE_ACCESS, "BS"}, 31 {EFI_VARIABLE_RUNTIME_ACCESS, "RT"}, 32 {EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, "AW"}, 33 {EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, "AT"}, 34 }; 35 36 /** 37 * efi_dump_single_var() - show information about a UEFI variable 38 * 39 * @name: Name of the variable 40 * @guid: Vendor GUID 41 * 42 * Show information encoded in one UEFI variable 43 */ 44 static void efi_dump_single_var(u16 *name, efi_guid_t *guid) 45 { 46 u32 attributes; 47 u8 *data; 48 efi_uintn_t size; 49 int count, i; 50 efi_status_t ret; 51 52 data = NULL; 53 size = 0; 54 ret = EFI_CALL(efi_get_variable(name, guid, &attributes, &size, data)); 55 if (ret == EFI_BUFFER_TOO_SMALL) { 56 data = malloc(size); 57 if (!data) 58 goto out; 59 60 ret = EFI_CALL(efi_get_variable(name, guid, &attributes, &size, 61 data)); 62 } 63 if (ret == EFI_NOT_FOUND) { 64 printf("Error: \"%ls\" not defined\n", name); 65 goto out; 66 } 67 if (ret != EFI_SUCCESS) 68 goto out; 69 70 printf("%ls:", name); 71 for (count = 0, i = 0; i < ARRAY_SIZE(efi_var_attrs); i++) 72 if (attributes & efi_var_attrs[i].mask) { 73 if (count) 74 putc('|'); 75 else 76 putc(' '); 77 count++; 78 puts(efi_var_attrs[i].text); 79 } 80 printf(", DataSize = 0x%zx\n", size); 81 print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, data, size, true); 82 83 return; 84 out: 85 free(data); 86 } 87 88 /** 89 * efi_dump_vars() - show information about named UEFI variables 90 * 91 * @argc: Number of arguments (variables) 92 * @argv: Argument (variable name) array 93 * Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE 94 * 95 * Show information encoded in named UEFI variables 96 */ 97 static int efi_dump_vars(int argc, char * const argv[]) 98 { 99 u16 *var_name16, *p; 100 efi_uintn_t buf_size, size; 101 102 buf_size = 128; 103 var_name16 = malloc(buf_size); 104 if (!var_name16) 105 return CMD_RET_FAILURE; 106 107 for (; argc > 0; argc--, argv++) { 108 size = (utf8_utf16_strlen(argv[0]) + 1) * sizeof(u16); 109 if (buf_size < size) { 110 buf_size = size; 111 p = realloc(var_name16, buf_size); 112 if (!p) { 113 free(var_name16); 114 return CMD_RET_FAILURE; 115 } 116 var_name16 = p; 117 } 118 119 p = var_name16; 120 utf8_utf16_strcpy(&p, argv[0]); 121 122 efi_dump_single_var(var_name16, 123 (efi_guid_t *)&efi_global_variable_guid); 124 } 125 126 free(var_name16); 127 128 return CMD_RET_SUCCESS; 129 } 130 131 /** 132 * efi_dump_vars() - show information about all the UEFI variables 133 * 134 * Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE 135 * 136 * Show information encoded in all the UEFI variables 137 */ 138 static int efi_dump_var_all(void) 139 { 140 u16 *var_name16, *p; 141 efi_uintn_t buf_size, size; 142 efi_guid_t guid; 143 efi_status_t ret; 144 145 buf_size = 128; 146 var_name16 = malloc(buf_size); 147 if (!var_name16) 148 return CMD_RET_FAILURE; 149 150 var_name16[0] = 0; 151 for (;;) { 152 size = buf_size; 153 ret = EFI_CALL(efi_get_next_variable_name(&size, var_name16, 154 &guid)); 155 if (ret == EFI_NOT_FOUND) 156 break; 157 if (ret == EFI_BUFFER_TOO_SMALL) { 158 buf_size = size; 159 p = realloc(var_name16, buf_size); 160 if (!p) { 161 free(var_name16); 162 return CMD_RET_FAILURE; 163 } 164 var_name16 = p; 165 ret = EFI_CALL(efi_get_next_variable_name(&size, 166 var_name16, 167 &guid)); 168 } 169 if (ret != EFI_SUCCESS) { 170 free(var_name16); 171 return CMD_RET_FAILURE; 172 } 173 174 efi_dump_single_var(var_name16, &guid); 175 } 176 177 free(var_name16); 178 179 return CMD_RET_SUCCESS; 180 } 181 182 /** 183 * do_env_print_efi() - show information about UEFI variables 184 * 185 * @cmdtp: Command table 186 * @flag: Command flag 187 * @argc: Number of arguments 188 * @argv: Argument array 189 * Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE 190 * 191 * This function is for "env print -e" or "printenv -e" command: 192 * => env print -e [var [...]] 193 * If one or more variable names are specified, show information 194 * named UEFI variables, otherwise show all the UEFI variables. 195 */ 196 int do_env_print_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 197 { 198 efi_status_t ret; 199 200 /* Initialize EFI drivers */ 201 ret = efi_init_obj_list(); 202 if (ret != EFI_SUCCESS) { 203 printf("Error: Cannot initialize UEFI sub-system, r = %lu\n", 204 ret & ~EFI_ERROR_MASK); 205 return CMD_RET_FAILURE; 206 } 207 208 if (argc > 1) 209 /* show specified UEFI variables */ 210 return efi_dump_vars(--argc, ++argv); 211 212 /* enumerate and show all UEFI variables */ 213 return efi_dump_var_all(); 214 } 215 216 /** 217 * append_value() - encode UEFI variable's value 218 * @bufp: Buffer of encoded UEFI variable's value 219 * @sizep: Size of buffer 220 * @data: data to be encoded into the value 221 * Return: 0 on success, -1 otherwise 222 * 223 * Interpret a given data string and append it to buffer. 224 * Buffer will be realloc'ed if necessary. 225 * 226 * Currently supported formats are: 227 * =0x0123...: Hexadecimal number 228 * =H0123...: Hexadecimal-byte array 229 * ="...", =S"..." or <string>: 230 * String 231 */ 232 static int append_value(char **bufp, size_t *sizep, char *data) 233 { 234 char *tmp_buf = NULL, *new_buf = NULL, *value; 235 unsigned long len = 0; 236 237 if (!strncmp(data, "=0x", 2)) { /* hexadecimal number */ 238 union { 239 u8 u8; 240 u16 u16; 241 u32 u32; 242 u64 u64; 243 } tmp_data; 244 unsigned long hex_value; 245 void *hex_ptr; 246 247 data += 3; 248 len = strlen(data); 249 if ((len & 0x1)) /* not multiple of two */ 250 return -1; 251 252 len /= 2; 253 if (len > 8) 254 return -1; 255 else if (len > 4) 256 len = 8; 257 else if (len > 2) 258 len = 4; 259 260 /* convert hex hexadecimal number */ 261 if (strict_strtoul(data, 16, &hex_value) < 0) 262 return -1; 263 264 tmp_buf = malloc(len); 265 if (!tmp_buf) 266 return -1; 267 268 if (len == 1) { 269 tmp_data.u8 = hex_value; 270 hex_ptr = &tmp_data.u8; 271 } else if (len == 2) { 272 tmp_data.u16 = hex_value; 273 hex_ptr = &tmp_data.u16; 274 } else if (len == 4) { 275 tmp_data.u32 = hex_value; 276 hex_ptr = &tmp_data.u32; 277 } else { 278 tmp_data.u64 = hex_value; 279 hex_ptr = &tmp_data.u64; 280 } 281 memcpy(tmp_buf, hex_ptr, len); 282 value = tmp_buf; 283 284 } else if (!strncmp(data, "=H", 2)) { /* hexadecimal-byte array */ 285 data += 2; 286 len = strlen(data); 287 if (len & 0x1) /* not multiple of two */ 288 return -1; 289 290 len /= 2; 291 tmp_buf = malloc(len); 292 if (!tmp_buf) 293 return -1; 294 295 if (hex2bin((u8 *)tmp_buf, data, len) < 0) 296 return -1; 297 298 value = tmp_buf; 299 } else { /* string */ 300 if (!strncmp(data, "=\"", 2) || !strncmp(data, "=S\"", 3)) { 301 if (data[1] == '"') 302 data += 2; 303 else 304 data += 3; 305 value = data; 306 len = strlen(data) - 1; 307 if (data[len] != '"') 308 return -1; 309 } else { 310 value = data; 311 len = strlen(data); 312 } 313 } 314 315 new_buf = realloc(*bufp, *sizep + len); 316 if (!new_buf) 317 goto out; 318 319 memcpy(new_buf + *sizep, value, len); 320 *bufp = new_buf; 321 *sizep += len; 322 323 out: 324 free(tmp_buf); 325 326 return 0; 327 } 328 329 /** 330 * do_env_print_efi() - set UEFI variable 331 * 332 * @cmdtp: Command table 333 * @flag: Command flag 334 * @argc: Number of arguments 335 * @argv: Argument array 336 * Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE 337 * 338 * This function is for "env set -e" or "setenv -e" command: 339 * => env set -e var [value ...]] 340 * Encode values specified and set given UEFI variable. 341 * If no value is specified, delete the variable. 342 */ 343 int do_env_set_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 344 { 345 char *var_name, *value = NULL; 346 efi_uintn_t size = 0; 347 u16 *var_name16 = NULL, *p; 348 size_t len; 349 efi_guid_t guid; 350 efi_status_t ret; 351 352 if (argc == 1) 353 return CMD_RET_USAGE; 354 355 /* Initialize EFI drivers */ 356 ret = efi_init_obj_list(); 357 if (ret != EFI_SUCCESS) { 358 printf("Error: Cannot initialize UEFI sub-system, r = %lu\n", 359 ret & ~EFI_ERROR_MASK); 360 return CMD_RET_FAILURE; 361 } 362 363 var_name = argv[1]; 364 if (argc == 2) { 365 /* delete */ 366 value = NULL; 367 size = 0; 368 } else { /* set */ 369 argc -= 2; 370 argv += 2; 371 372 for ( ; argc > 0; argc--, argv++) 373 if (append_value(&value, &size, argv[0]) < 0) { 374 ret = CMD_RET_FAILURE; 375 goto out; 376 } 377 } 378 379 len = utf8_utf16_strnlen(var_name, strlen(var_name)); 380 var_name16 = malloc((len + 1) * 2); 381 if (!var_name16) { 382 ret = CMD_RET_FAILURE; 383 goto out; 384 } 385 p = var_name16; 386 utf8_utf16_strncpy(&p, var_name, len + 1); 387 388 guid = efi_global_variable_guid; 389 ret = EFI_CALL(efi_set_variable(var_name16, &guid, 390 EFI_VARIABLE_BOOTSERVICE_ACCESS | 391 EFI_VARIABLE_RUNTIME_ACCESS, 392 size, value)); 393 ret = (ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE); 394 out: 395 free(value); 396 free(var_name16); 397 398 return ret; 399 } 400