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 if (list_len <= 0) 387 return EFI_EXIT(EFI_NOT_FOUND); 388 389 variable = efi_variables_list; 390 } 391 392 ret = parse_uboot_variable(variable, variable_name_size, variable_name, 393 vendor, &attributes); 394 395 return EFI_EXIT(ret); 396 } 397 398 /** 399 * efi_efi_set_variable() - set value of a UEFI variable 400 * 401 * This function implements the SetVariable runtime service. 402 * 403 * See the Unified Extensible Firmware Interface (UEFI) specification for 404 * details. 405 * 406 * @variable_name: name of the variable 407 * @vendor: vendor GUID 408 * @attributes: attributes of the variable 409 * @data_size: size of the buffer with the variable value 410 * @data: buffer with the variable value 411 * Return: status code 412 */ 413 efi_status_t EFIAPI efi_set_variable(u16 *variable_name, 414 const efi_guid_t *vendor, u32 attributes, 415 efi_uintn_t data_size, const void *data) 416 { 417 char *native_name = NULL, *val = NULL, *s; 418 efi_status_t ret = EFI_SUCCESS; 419 u32 attr; 420 421 EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes, 422 data_size, data); 423 424 if (!variable_name || !vendor) { 425 ret = EFI_INVALID_PARAMETER; 426 goto out; 427 } 428 429 ret = efi_to_native(&native_name, variable_name, vendor); 430 if (ret) 431 goto out; 432 433 #define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS) 434 435 if ((data_size == 0) || !(attributes & ACCESS_ATTR)) { 436 /* delete the variable: */ 437 env_set(native_name, NULL); 438 ret = EFI_SUCCESS; 439 goto out; 440 } 441 442 val = env_get(native_name); 443 if (val) { 444 parse_attr(val, &attr); 445 446 if (attr & READ_ONLY) { 447 /* We should not free val */ 448 val = NULL; 449 ret = EFI_WRITE_PROTECTED; 450 goto out; 451 } 452 } 453 454 val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1); 455 if (!val) { 456 ret = EFI_OUT_OF_RESOURCES; 457 goto out; 458 } 459 460 s = val; 461 462 /* 463 * store attributes 464 * TODO: several attributes are not supported 465 */ 466 attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS); 467 s += sprintf(s, "{"); 468 while (attributes) { 469 u32 attr = 1 << (ffs(attributes) - 1); 470 471 if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) 472 s += sprintf(s, "boot"); 473 else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) 474 s += sprintf(s, "run"); 475 476 attributes &= ~attr; 477 if (attributes) 478 s += sprintf(s, ","); 479 } 480 s += sprintf(s, "}"); 481 482 /* store payload: */ 483 s += sprintf(s, "(blob)"); 484 s = bin2hex(s, data, data_size); 485 *s = '\0'; 486 487 debug("%s: setting: %s=%s\n", __func__, native_name, val); 488 489 if (env_set(native_name, val)) 490 ret = EFI_DEVICE_ERROR; 491 492 out: 493 free(native_name); 494 free(val); 495 496 return EFI_EXIT(ret); 497 } 498