1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * EFI utils 4 * 5 * Copyright (c) 2017 Rob Clark 6 */ 7 8 #include <malloc.h> 9 #include <charset.h> 10 #include <efi_loader.h> 11 #include <hexdump.h> 12 #include <environment.h> 13 #include <search.h> 14 #include <uuid.h> 15 16 #define READ_ONLY BIT(31) 17 18 /* 19 * Mapping between EFI variables and u-boot variables: 20 * 21 * efi_$guid_$varname = {attributes}(type)value 22 * 23 * For example: 24 * 25 * efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported= 26 * "{ro,boot,run}(blob)0000000000000000" 27 * efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder= 28 * "(blob)00010000" 29 * 30 * The attributes are a comma separated list of these possible 31 * attributes: 32 * 33 * + ro - read-only 34 * + boot - boot-services access 35 * + run - runtime access 36 * 37 * NOTE: with current implementation, no variables are available after 38 * ExitBootServices, and all are persisted (if possible). 39 * 40 * If not specified, the attributes default to "{boot}". 41 * 42 * The required type is one of: 43 * 44 * + utf8 - raw utf8 string 45 * + blob - arbitrary length hex string 46 * 47 * Maybe a utf16 type would be useful to for a string value to be auto 48 * converted to utf16? 49 */ 50 51 #define PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_")) 52 53 /** 54 * efi_to_native() - convert the UEFI variable name and vendor GUID to U-Boot 55 * variable name 56 * 57 * The U-Boot variable name is a concatenation of prefix 'efi', the hexstring 58 * encoded vendor GUID, and the UTF-8 encoded UEFI variable name separated by 59 * underscores, e.g. 'efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder'. 60 * 61 * @native: pointer to pointer to U-Boot variable name 62 * @variable_name: UEFI variable name 63 * @vendor: vendor GUID 64 * Return: status code 65 */ 66 static efi_status_t efi_to_native(char **native, const u16 *variable_name, 67 const efi_guid_t *vendor) 68 { 69 size_t len; 70 char *pos; 71 72 len = PREFIX_LEN + utf16_utf8_strlen(variable_name) + 1; 73 *native = malloc(len); 74 if (!*native) 75 return EFI_OUT_OF_RESOURCES; 76 77 pos = *native; 78 pos += sprintf(pos, "efi_%pUl_", vendor); 79 utf16_utf8_strcpy(&pos, variable_name); 80 81 return EFI_SUCCESS; 82 } 83 84 /** 85 * prefix() - skip over prefix 86 * 87 * Skip over a prefix string. 88 * 89 * @str: string with prefix 90 * @prefix: prefix string 91 * Return: string without prefix, or NULL if prefix not found 92 */ 93 static const char *prefix(const char *str, const char *prefix) 94 { 95 size_t n = strlen(prefix); 96 if (!strncmp(prefix, str, n)) 97 return str + n; 98 return NULL; 99 } 100 101 /** 102 * parse_attr() - decode attributes part of variable value 103 * 104 * Convert the string encoded attributes of a UEFI variable to a bit mask. 105 * TODO: Several attributes are not supported. 106 * 107 * @str: value of U-Boot variable 108 * @attrp: pointer to UEFI attributes 109 * Return: pointer to remainder of U-Boot variable value 110 */ 111 static const char *parse_attr(const char *str, u32 *attrp) 112 { 113 u32 attr = 0; 114 char sep = '{'; 115 116 if (*str != '{') { 117 *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS; 118 return str; 119 } 120 121 while (*str == sep) { 122 const char *s; 123 124 str++; 125 126 if ((s = prefix(str, "ro"))) { 127 attr |= READ_ONLY; 128 } else if ((s = prefix(str, "boot"))) { 129 attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS; 130 } else if ((s = prefix(str, "run"))) { 131 attr |= EFI_VARIABLE_RUNTIME_ACCESS; 132 } else { 133 printf("invalid attribute: %s\n", str); 134 break; 135 } 136 137 str = s; 138 sep = ','; 139 } 140 141 str++; 142 143 *attrp = attr; 144 145 return str; 146 } 147 148 /** 149 * efi_efi_get_variable() - retrieve value of a UEFI variable 150 * 151 * This function implements the GetVariable runtime service. 152 * 153 * See the Unified Extensible Firmware Interface (UEFI) specification for 154 * details. 155 * 156 * @variable_name: name of the variable 157 * @vendor: vendor GUID 158 * @attributes: attributes of the variable 159 * @data_size: size of the buffer to which the variable value is copied 160 * @data: buffer to which the variable value is copied 161 * Return: status code 162 */ 163 efi_status_t EFIAPI efi_get_variable(u16 *variable_name, 164 const efi_guid_t *vendor, u32 *attributes, 165 efi_uintn_t *data_size, void *data) 166 { 167 char *native_name; 168 efi_status_t ret; 169 unsigned long in_size; 170 const char *val, *s; 171 u32 attr; 172 173 EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes, 174 data_size, data); 175 176 if (!variable_name || !vendor || !data_size) 177 return EFI_EXIT(EFI_INVALID_PARAMETER); 178 179 ret = efi_to_native(&native_name, variable_name, vendor); 180 if (ret) 181 return EFI_EXIT(ret); 182 183 debug("%s: get '%s'\n", __func__, native_name); 184 185 val = env_get(native_name); 186 free(native_name); 187 if (!val) 188 return EFI_EXIT(EFI_NOT_FOUND); 189 190 val = parse_attr(val, &attr); 191 192 in_size = *data_size; 193 194 if ((s = prefix(val, "(blob)"))) { 195 size_t len = strlen(s); 196 197 /* number of hexadecimal digits must be even */ 198 if (len & 1) 199 return EFI_EXIT(EFI_DEVICE_ERROR); 200 201 /* two characters per byte: */ 202 len /= 2; 203 *data_size = len; 204 205 if (in_size < len) 206 return EFI_EXIT(EFI_BUFFER_TOO_SMALL); 207 208 if (!data) 209 return EFI_EXIT(EFI_INVALID_PARAMETER); 210 211 if (hex2bin(data, s, len)) 212 return EFI_EXIT(EFI_DEVICE_ERROR); 213 214 debug("%s: got value: \"%s\"\n", __func__, s); 215 } else if ((s = prefix(val, "(utf8)"))) { 216 unsigned len = strlen(s) + 1; 217 218 *data_size = len; 219 220 if (in_size < len) 221 return EFI_EXIT(EFI_BUFFER_TOO_SMALL); 222 223 if (!data) 224 return EFI_EXIT(EFI_INVALID_PARAMETER); 225 226 memcpy(data, s, len); 227 ((char *)data)[len] = '\0'; 228 229 debug("%s: got value: \"%s\"\n", __func__, (char *)data); 230 } else { 231 debug("%s: invalid value: '%s'\n", __func__, val); 232 return EFI_EXIT(EFI_DEVICE_ERROR); 233 } 234 235 if (attributes) 236 *attributes = attr & EFI_VARIABLE_MASK; 237 238 return EFI_EXIT(EFI_SUCCESS); 239 } 240 241 static char *efi_variables_list; 242 static char *efi_cur_variable; 243 244 /** 245 * parse_uboot_variable() - parse a u-boot variable and get uefi-related 246 * information 247 * @variable: whole data of u-boot variable (ie. name=value) 248 * @variable_name_size: size of variable_name buffer in byte 249 * @variable_name: name of uefi variable in u16, null-terminated 250 * @vendor: vendor's guid 251 * @attributes: attributes 252 * 253 * A uefi variable is encoded into a u-boot variable as described above. 254 * This function parses such a u-boot variable and retrieve uefi-related 255 * information into respective parameters. In return, variable_name_size 256 * is the size of variable name including NULL. 257 * 258 * Return: EFI_SUCCESS if parsing is OK, EFI_NOT_FOUND when 259 the entire variable list has been returned, 260 otherwise non-zero status code 261 */ 262 static efi_status_t parse_uboot_variable(char *variable, 263 efi_uintn_t *variable_name_size, 264 u16 *variable_name, 265 const efi_guid_t *vendor, 266 u32 *attributes) 267 { 268 char *guid, *name, *end, c; 269 unsigned long name_len; 270 u16 *p; 271 272 guid = strchr(variable, '_'); 273 if (!guid) 274 return EFI_INVALID_PARAMETER; 275 guid++; 276 name = strchr(guid, '_'); 277 if (!name) 278 return EFI_INVALID_PARAMETER; 279 name++; 280 end = strchr(name, '='); 281 if (!end) 282 return EFI_INVALID_PARAMETER; 283 284 name_len = end - name; 285 if (*variable_name_size < (name_len + 1)) { 286 *variable_name_size = name_len + 1; 287 return EFI_BUFFER_TOO_SMALL; 288 } 289 end++; /* point to value */ 290 291 /* variable name */ 292 p = variable_name; 293 utf8_utf16_strncpy(&p, name, name_len); 294 variable_name[name_len] = 0; 295 *variable_name_size = name_len + 1; 296 297 /* guid */ 298 c = *(name - 1); 299 *(name - 1) = '\0'; /* guid need be null-terminated here */ 300 uuid_str_to_bin(guid, (unsigned char *)vendor, UUID_STR_FORMAT_GUID); 301 *(name - 1) = c; 302 303 /* attributes */ 304 parse_attr(end, attributes); 305 306 return EFI_SUCCESS; 307 } 308 309 /** 310 * efi_get_next_variable_name() - enumerate the current variable names 311 * @variable_name_size: size of variable_name buffer in byte 312 * @variable_name: name of uefi variable's name in u16 313 * @vendor: vendor's guid 314 * 315 * This function implements the GetNextVariableName service. 316 * 317 * See the Unified Extensible Firmware Interface (UEFI) specification for 318 * details: http://wiki.phoenix.com/wiki/index.php/ 319 * EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 320 * 321 * Return: status code 322 */ 323 efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, 324 u16 *variable_name, 325 const efi_guid_t *vendor) 326 { 327 char *native_name, *variable; 328 ssize_t name_len, list_len; 329 char regex[256]; 330 char * const regexlist[] = {regex}; 331 u32 attributes; 332 int i; 333 efi_status_t ret; 334 335 EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor); 336 337 if (!variable_name_size || !variable_name || !vendor) 338 EFI_EXIT(EFI_INVALID_PARAMETER); 339 340 if (variable_name[0]) { 341 /* check null-terminated string */ 342 for (i = 0; i < *variable_name_size; i++) 343 if (!variable_name[i]) 344 break; 345 if (i >= *variable_name_size) 346 return EFI_EXIT(EFI_INVALID_PARAMETER); 347 348 /* search for the last-returned variable */ 349 ret = efi_to_native(&native_name, variable_name, vendor); 350 if (ret) 351 return EFI_EXIT(ret); 352 353 name_len = strlen(native_name); 354 for (variable = efi_variables_list; variable && *variable;) { 355 if (!strncmp(variable, native_name, name_len) && 356 variable[name_len] == '=') 357 break; 358 359 variable = strchr(variable, '\n'); 360 if (variable) 361 variable++; 362 } 363 364 free(native_name); 365 if (!(variable && *variable)) 366 return EFI_EXIT(EFI_INVALID_PARAMETER); 367 368 /* next variable */ 369 variable = strchr(variable, '\n'); 370 if (variable) 371 variable++; 372 if (!(variable && *variable)) 373 return EFI_EXIT(EFI_NOT_FOUND); 374 } else { 375 /* 376 *new search: free a list used in the previous search 377 */ 378 free(efi_variables_list); 379 efi_variables_list = NULL; 380 efi_cur_variable = NULL; 381 382 snprintf(regex, 256, "efi_.*-.*-.*-.*-.*_.*"); 383 list_len = hexport_r(&env_htab, '\n', 384 H_MATCH_REGEX | H_MATCH_KEY, 385 &efi_variables_list, 0, 1, regexlist); 386 /* 1 indicates that no match was found */ 387 if (list_len <= 1) 388 return EFI_EXIT(EFI_NOT_FOUND); 389 390 variable = efi_variables_list; 391 } 392 393 ret = parse_uboot_variable(variable, variable_name_size, variable_name, 394 vendor, &attributes); 395 396 return EFI_EXIT(ret); 397 } 398 399 /** 400 * efi_efi_set_variable() - set value of a UEFI variable 401 * 402 * This function implements the SetVariable runtime service. 403 * 404 * See the Unified Extensible Firmware Interface (UEFI) specification for 405 * details. 406 * 407 * @variable_name: name of the variable 408 * @vendor: vendor GUID 409 * @attributes: attributes of the variable 410 * @data_size: size of the buffer with the variable value 411 * @data: buffer with the variable value 412 * Return: status code 413 */ 414 efi_status_t EFIAPI efi_set_variable(u16 *variable_name, 415 const efi_guid_t *vendor, u32 attributes, 416 efi_uintn_t data_size, const void *data) 417 { 418 char *native_name = NULL, *val = NULL, *s; 419 efi_status_t ret = EFI_SUCCESS; 420 u32 attr; 421 422 EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes, 423 data_size, data); 424 425 if (!variable_name || !vendor) { 426 ret = EFI_INVALID_PARAMETER; 427 goto out; 428 } 429 430 ret = efi_to_native(&native_name, variable_name, vendor); 431 if (ret) 432 goto out; 433 434 #define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS) 435 436 if ((data_size == 0) || !(attributes & ACCESS_ATTR)) { 437 /* delete the variable: */ 438 env_set(native_name, NULL); 439 ret = EFI_SUCCESS; 440 goto out; 441 } 442 443 val = env_get(native_name); 444 if (val) { 445 parse_attr(val, &attr); 446 447 if (attr & READ_ONLY) { 448 /* We should not free val */ 449 val = NULL; 450 ret = EFI_WRITE_PROTECTED; 451 goto out; 452 } 453 } 454 455 val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1); 456 if (!val) { 457 ret = EFI_OUT_OF_RESOURCES; 458 goto out; 459 } 460 461 s = val; 462 463 /* 464 * store attributes 465 * TODO: several attributes are not supported 466 */ 467 attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS); 468 s += sprintf(s, "{"); 469 while (attributes) { 470 u32 attr = 1 << (ffs(attributes) - 1); 471 472 if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) 473 s += sprintf(s, "boot"); 474 else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) 475 s += sprintf(s, "run"); 476 477 attributes &= ~attr; 478 if (attributes) 479 s += sprintf(s, ","); 480 } 481 s += sprintf(s, "}"); 482 483 /* store payload: */ 484 s += sprintf(s, "(blob)"); 485 s = bin2hex(s, data, data_size); 486 *s = '\0'; 487 488 debug("%s: setting: %s=%s\n", __func__, native_name, val); 489 490 if (env_set(native_name, val)) 491 ret = EFI_DEVICE_ERROR; 492 493 out: 494 free(native_name); 495 free(val); 496 497 return EFI_EXIT(ret); 498 } 499