18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Originally from efivars.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/capability.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/mm.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/string.h> 168c2ecf20Sopenharmony_ci#include <linux/smp.h> 178c2ecf20Sopenharmony_ci#include <linux/efi.h> 188c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 198c2ecf20Sopenharmony_ci#include <linux/device.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/ctype.h> 228c2ecf20Sopenharmony_ci#include <linux/ucs2_string.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* Private pointer to registered efivars */ 258c2ecf20Sopenharmony_cistatic struct efivars *__efivars; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * efivars_lock protects three things: 298c2ecf20Sopenharmony_ci * 1) efivarfs_list and efivars_sysfs_list 308c2ecf20Sopenharmony_ci * 2) ->ops calls 318c2ecf20Sopenharmony_ci * 3) (un)registration of __efivars 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_cistatic DEFINE_SEMAPHORE(efivars_lock); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic bool 368c2ecf20Sopenharmony_civalidate_device_path(efi_char16_t *var_name, int match, u8 *buffer, 378c2ecf20Sopenharmony_ci unsigned long len) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct efi_generic_dev_path *node; 408c2ecf20Sopenharmony_ci int offset = 0; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci node = (struct efi_generic_dev_path *)buffer; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (len < sizeof(*node)) 458c2ecf20Sopenharmony_ci return false; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci while (offset <= len - sizeof(*node) && 488c2ecf20Sopenharmony_ci node->length >= sizeof(*node) && 498c2ecf20Sopenharmony_ci node->length <= len - offset) { 508c2ecf20Sopenharmony_ci offset += node->length; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if ((node->type == EFI_DEV_END_PATH || 538c2ecf20Sopenharmony_ci node->type == EFI_DEV_END_PATH2) && 548c2ecf20Sopenharmony_ci node->sub_type == EFI_DEV_END_ENTIRE) 558c2ecf20Sopenharmony_ci return true; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci node = (struct efi_generic_dev_path *)(buffer + offset); 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* 618c2ecf20Sopenharmony_ci * If we're here then either node->length pointed past the end 628c2ecf20Sopenharmony_ci * of the buffer or we reached the end of the buffer without 638c2ecf20Sopenharmony_ci * finding a device path end node. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci return false; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic bool 698c2ecf20Sopenharmony_civalidate_boot_order(efi_char16_t *var_name, int match, u8 *buffer, 708c2ecf20Sopenharmony_ci unsigned long len) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci /* An array of 16-bit integers */ 738c2ecf20Sopenharmony_ci if ((len % 2) != 0) 748c2ecf20Sopenharmony_ci return false; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return true; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic bool 808c2ecf20Sopenharmony_civalidate_load_option(efi_char16_t *var_name, int match, u8 *buffer, 818c2ecf20Sopenharmony_ci unsigned long len) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci u16 filepathlength; 848c2ecf20Sopenharmony_ci int i, desclength = 0, namelen; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci namelen = ucs2_strnlen(var_name, EFI_VAR_NAME_LEN); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* Either "Boot" or "Driver" followed by four digits of hex */ 898c2ecf20Sopenharmony_ci for (i = match; i < match+4; i++) { 908c2ecf20Sopenharmony_ci if (var_name[i] > 127 || 918c2ecf20Sopenharmony_ci hex_to_bin(var_name[i] & 0xff) < 0) 928c2ecf20Sopenharmony_ci return true; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* Reject it if there's 4 digits of hex and then further content */ 968c2ecf20Sopenharmony_ci if (namelen > match + 4) 978c2ecf20Sopenharmony_ci return false; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* A valid entry must be at least 8 bytes */ 1008c2ecf20Sopenharmony_ci if (len < 8) 1018c2ecf20Sopenharmony_ci return false; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci filepathlength = buffer[4] | buffer[5] << 8; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* 1068c2ecf20Sopenharmony_ci * There's no stored length for the description, so it has to be 1078c2ecf20Sopenharmony_ci * found by hand 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci desclength = ucs2_strsize((efi_char16_t *)(buffer + 6), len - 6) + 2; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Each boot entry must have a descriptor */ 1128c2ecf20Sopenharmony_ci if (!desclength) 1138c2ecf20Sopenharmony_ci return false; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* 1168c2ecf20Sopenharmony_ci * If the sum of the length of the description, the claimed filepath 1178c2ecf20Sopenharmony_ci * length and the original header are greater than the length of the 1188c2ecf20Sopenharmony_ci * variable, it's malformed 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci if ((desclength + filepathlength + 6) > len) 1218c2ecf20Sopenharmony_ci return false; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* 1248c2ecf20Sopenharmony_ci * And, finally, check the filepath 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci return validate_device_path(var_name, match, buffer + desclength + 6, 1278c2ecf20Sopenharmony_ci filepathlength); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic bool 1318c2ecf20Sopenharmony_civalidate_uint16(efi_char16_t *var_name, int match, u8 *buffer, 1328c2ecf20Sopenharmony_ci unsigned long len) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci /* A single 16-bit integer */ 1358c2ecf20Sopenharmony_ci if (len != 2) 1368c2ecf20Sopenharmony_ci return false; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return true; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic bool 1428c2ecf20Sopenharmony_civalidate_ascii_string(efi_char16_t *var_name, int match, u8 *buffer, 1438c2ecf20Sopenharmony_ci unsigned long len) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci int i; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 1488c2ecf20Sopenharmony_ci if (buffer[i] > 127) 1498c2ecf20Sopenharmony_ci return false; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (buffer[i] == 0) 1528c2ecf20Sopenharmony_ci return true; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return false; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistruct variable_validate { 1598c2ecf20Sopenharmony_ci efi_guid_t vendor; 1608c2ecf20Sopenharmony_ci char *name; 1618c2ecf20Sopenharmony_ci bool (*validate)(efi_char16_t *var_name, int match, u8 *data, 1628c2ecf20Sopenharmony_ci unsigned long len); 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* 1668c2ecf20Sopenharmony_ci * This is the list of variables we need to validate, as well as the 1678c2ecf20Sopenharmony_ci * whitelist for what we think is safe not to default to immutable. 1688c2ecf20Sopenharmony_ci * 1698c2ecf20Sopenharmony_ci * If it has a validate() method that's not NULL, it'll go into the 1708c2ecf20Sopenharmony_ci * validation routine. If not, it is assumed valid, but still used for 1718c2ecf20Sopenharmony_ci * whitelisting. 1728c2ecf20Sopenharmony_ci * 1738c2ecf20Sopenharmony_ci * Note that it's sorted by {vendor,name}, but globbed names must come after 1748c2ecf20Sopenharmony_ci * any other name with the same prefix. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistatic const struct variable_validate variable_validate[] = { 1778c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "BootNext", validate_uint16 }, 1788c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "BootOrder", validate_boot_order }, 1798c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "Boot*", validate_load_option }, 1808c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "DriverOrder", validate_boot_order }, 1818c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "Driver*", validate_load_option }, 1828c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "ConIn", validate_device_path }, 1838c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "ConInDev", validate_device_path }, 1848c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "ConOut", validate_device_path }, 1858c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "ConOutDev", validate_device_path }, 1868c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "ErrOut", validate_device_path }, 1878c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "ErrOutDev", validate_device_path }, 1888c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "Lang", validate_ascii_string }, 1898c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "OsIndications", NULL }, 1908c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "PlatformLang", validate_ascii_string }, 1918c2ecf20Sopenharmony_ci { EFI_GLOBAL_VARIABLE_GUID, "Timeout", validate_uint16 }, 1928c2ecf20Sopenharmony_ci { LINUX_EFI_CRASH_GUID, "*", NULL }, 1938c2ecf20Sopenharmony_ci { NULL_GUID, "", NULL }, 1948c2ecf20Sopenharmony_ci}; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* 1978c2ecf20Sopenharmony_ci * Check if @var_name matches the pattern given in @match_name. 1988c2ecf20Sopenharmony_ci * 1998c2ecf20Sopenharmony_ci * @var_name: an array of @len non-NUL characters. 2008c2ecf20Sopenharmony_ci * @match_name: a NUL-terminated pattern string, optionally ending in "*". A 2018c2ecf20Sopenharmony_ci * final "*" character matches any trailing characters @var_name, 2028c2ecf20Sopenharmony_ci * including the case when there are none left in @var_name. 2038c2ecf20Sopenharmony_ci * @match: on output, the number of non-wildcard characters in @match_name 2048c2ecf20Sopenharmony_ci * that @var_name matches, regardless of the return value. 2058c2ecf20Sopenharmony_ci * @return: whether @var_name fully matches @match_name. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_cistatic bool 2088c2ecf20Sopenharmony_civariable_matches(const char *var_name, size_t len, const char *match_name, 2098c2ecf20Sopenharmony_ci int *match) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci for (*match = 0; ; (*match)++) { 2128c2ecf20Sopenharmony_ci char c = match_name[*match]; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci switch (c) { 2158c2ecf20Sopenharmony_ci case '*': 2168c2ecf20Sopenharmony_ci /* Wildcard in @match_name means we've matched. */ 2178c2ecf20Sopenharmony_ci return true; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci case '\0': 2208c2ecf20Sopenharmony_ci /* @match_name has ended. Has @var_name too? */ 2218c2ecf20Sopenharmony_ci return (*match == len); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci default: 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * We've reached a non-wildcard char in @match_name. 2268c2ecf20Sopenharmony_ci * Continue only if there's an identical character in 2278c2ecf20Sopenharmony_ci * @var_name. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci if (*match < len && c == var_name[*match]) 2308c2ecf20Sopenharmony_ci continue; 2318c2ecf20Sopenharmony_ci return false; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cibool 2378c2ecf20Sopenharmony_ciefivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data, 2388c2ecf20Sopenharmony_ci unsigned long data_size) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci int i; 2418c2ecf20Sopenharmony_ci unsigned long utf8_size; 2428c2ecf20Sopenharmony_ci u8 *utf8_name; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci utf8_size = ucs2_utf8size(var_name); 2458c2ecf20Sopenharmony_ci utf8_name = kmalloc(utf8_size + 1, GFP_KERNEL); 2468c2ecf20Sopenharmony_ci if (!utf8_name) 2478c2ecf20Sopenharmony_ci return false; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci ucs2_as_utf8(utf8_name, var_name, utf8_size); 2508c2ecf20Sopenharmony_ci utf8_name[utf8_size] = '\0'; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci for (i = 0; variable_validate[i].name[0] != '\0'; i++) { 2538c2ecf20Sopenharmony_ci const char *name = variable_validate[i].name; 2548c2ecf20Sopenharmony_ci int match = 0; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (efi_guidcmp(vendor, variable_validate[i].vendor)) 2578c2ecf20Sopenharmony_ci continue; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (variable_matches(utf8_name, utf8_size+1, name, &match)) { 2608c2ecf20Sopenharmony_ci if (variable_validate[i].validate == NULL) 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci kfree(utf8_name); 2638c2ecf20Sopenharmony_ci return variable_validate[i].validate(var_name, match, 2648c2ecf20Sopenharmony_ci data, data_size); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci kfree(utf8_name); 2688c2ecf20Sopenharmony_ci return true; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_validate); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cibool 2738c2ecf20Sopenharmony_ciefivar_variable_is_removable(efi_guid_t vendor, const char *var_name, 2748c2ecf20Sopenharmony_ci size_t len) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci int i; 2778c2ecf20Sopenharmony_ci bool found = false; 2788c2ecf20Sopenharmony_ci int match = 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* 2818c2ecf20Sopenharmony_ci * Check if our variable is in the validated variables list 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci for (i = 0; variable_validate[i].name[0] != '\0'; i++) { 2848c2ecf20Sopenharmony_ci if (efi_guidcmp(variable_validate[i].vendor, vendor)) 2858c2ecf20Sopenharmony_ci continue; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (variable_matches(var_name, len, 2888c2ecf20Sopenharmony_ci variable_validate[i].name, &match)) { 2898c2ecf20Sopenharmony_ci found = true; 2908c2ecf20Sopenharmony_ci break; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* 2958c2ecf20Sopenharmony_ci * If it's in our list, it is removable. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ci return found; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_variable_is_removable); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic efi_status_t 3028c2ecf20Sopenharmony_cicheck_var_size(u32 attributes, unsigned long size) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci const struct efivar_operations *fops; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (!__efivars) 3078c2ecf20Sopenharmony_ci return EFI_UNSUPPORTED; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci fops = __efivars->ops; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (!fops->query_variable_store) 3128c2ecf20Sopenharmony_ci return EFI_UNSUPPORTED; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return fops->query_variable_store(attributes, size, false); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic efi_status_t 3188c2ecf20Sopenharmony_cicheck_var_size_nonblocking(u32 attributes, unsigned long size) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci const struct efivar_operations *fops; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (!__efivars) 3238c2ecf20Sopenharmony_ci return EFI_UNSUPPORTED; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci fops = __efivars->ops; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (!fops->query_variable_store) 3288c2ecf20Sopenharmony_ci return EFI_UNSUPPORTED; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return fops->query_variable_store(attributes, size, true); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor, 3348c2ecf20Sopenharmony_ci struct list_head *head) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct efivar_entry *entry, *n; 3378c2ecf20Sopenharmony_ci unsigned long strsize1, strsize2; 3388c2ecf20Sopenharmony_ci bool found = false; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci strsize1 = ucs2_strsize(variable_name, 1024); 3418c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, n, head, list) { 3428c2ecf20Sopenharmony_ci strsize2 = ucs2_strsize(entry->var.VariableName, 1024); 3438c2ecf20Sopenharmony_ci if (strsize1 == strsize2 && 3448c2ecf20Sopenharmony_ci !memcmp(variable_name, &(entry->var.VariableName), 3458c2ecf20Sopenharmony_ci strsize2) && 3468c2ecf20Sopenharmony_ci !efi_guidcmp(entry->var.VendorGuid, 3478c2ecf20Sopenharmony_ci *vendor)) { 3488c2ecf20Sopenharmony_ci found = true; 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci return found; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/* 3568c2ecf20Sopenharmony_ci * Returns the size of variable_name, in bytes, including the 3578c2ecf20Sopenharmony_ci * terminating NULL character, or variable_name_size if no NULL 3588c2ecf20Sopenharmony_ci * character is found among the first variable_name_size bytes. 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_cistatic unsigned long var_name_strnsize(efi_char16_t *variable_name, 3618c2ecf20Sopenharmony_ci unsigned long variable_name_size) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci unsigned long len; 3648c2ecf20Sopenharmony_ci efi_char16_t c; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* 3678c2ecf20Sopenharmony_ci * The variable name is, by definition, a NULL-terminated 3688c2ecf20Sopenharmony_ci * string, so make absolutely sure that variable_name_size is 3698c2ecf20Sopenharmony_ci * the value we expect it to be. If not, return the real size. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci for (len = 2; len <= variable_name_size; len += sizeof(c)) { 3728c2ecf20Sopenharmony_ci c = variable_name[(len / sizeof(c)) - 1]; 3738c2ecf20Sopenharmony_ci if (!c) 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return min(len, variable_name_size); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci/* 3818c2ecf20Sopenharmony_ci * Print a warning when duplicate EFI variables are encountered and 3828c2ecf20Sopenharmony_ci * disable the sysfs workqueue since the firmware is buggy. 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_cistatic void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid, 3858c2ecf20Sopenharmony_ci unsigned long len16) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci size_t i, len8 = len16 / sizeof(efi_char16_t); 3888c2ecf20Sopenharmony_ci char *str8; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci str8 = kzalloc(len8, GFP_KERNEL); 3918c2ecf20Sopenharmony_ci if (!str8) 3928c2ecf20Sopenharmony_ci return; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci for (i = 0; i < len8; i++) 3958c2ecf20Sopenharmony_ci str8[i] = str16[i]; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci printk(KERN_WARNING "efivars: duplicate variable: %s-%pUl\n", 3988c2ecf20Sopenharmony_ci str8, vendor_guid); 3998c2ecf20Sopenharmony_ci kfree(str8); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci/** 4038c2ecf20Sopenharmony_ci * efivar_init - build the initial list of EFI variables 4048c2ecf20Sopenharmony_ci * @func: callback function to invoke for every variable 4058c2ecf20Sopenharmony_ci * @data: function-specific data to pass to @func 4068c2ecf20Sopenharmony_ci * @duplicates: error if we encounter duplicates on @head? 4078c2ecf20Sopenharmony_ci * @head: initialised head of variable list 4088c2ecf20Sopenharmony_ci * 4098c2ecf20Sopenharmony_ci * Get every EFI variable from the firmware and invoke @func. @func 4108c2ecf20Sopenharmony_ci * should call efivar_entry_add() to build the list of variables. 4118c2ecf20Sopenharmony_ci * 4128c2ecf20Sopenharmony_ci * Returns 0 on success, or a kernel error code on failure. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ciint efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *), 4158c2ecf20Sopenharmony_ci void *data, bool duplicates, struct list_head *head) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci const struct efivar_operations *ops; 4188c2ecf20Sopenharmony_ci unsigned long variable_name_size = 1024; 4198c2ecf20Sopenharmony_ci efi_char16_t *variable_name; 4208c2ecf20Sopenharmony_ci efi_status_t status; 4218c2ecf20Sopenharmony_ci efi_guid_t vendor_guid; 4228c2ecf20Sopenharmony_ci int err = 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (!__efivars) 4258c2ecf20Sopenharmony_ci return -EFAULT; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci ops = __efivars->ops; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci variable_name = kzalloc(variable_name_size, GFP_KERNEL); 4308c2ecf20Sopenharmony_ci if (!variable_name) { 4318c2ecf20Sopenharmony_ci printk(KERN_ERR "efivars: Memory allocation failed.\n"); 4328c2ecf20Sopenharmony_ci return -ENOMEM; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (down_interruptible(&efivars_lock)) { 4368c2ecf20Sopenharmony_ci err = -EINTR; 4378c2ecf20Sopenharmony_ci goto free; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* 4418c2ecf20Sopenharmony_ci * Per EFI spec, the maximum storage allocated for both 4428c2ecf20Sopenharmony_ci * the variable name and variable data is 1024 bytes. 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci do { 4468c2ecf20Sopenharmony_ci variable_name_size = 1024; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci status = ops->get_next_variable(&variable_name_size, 4498c2ecf20Sopenharmony_ci variable_name, 4508c2ecf20Sopenharmony_ci &vendor_guid); 4518c2ecf20Sopenharmony_ci switch (status) { 4528c2ecf20Sopenharmony_ci case EFI_SUCCESS: 4538c2ecf20Sopenharmony_ci if (duplicates) 4548c2ecf20Sopenharmony_ci up(&efivars_lock); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci variable_name_size = var_name_strnsize(variable_name, 4578c2ecf20Sopenharmony_ci variable_name_size); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* 4608c2ecf20Sopenharmony_ci * Some firmware implementations return the 4618c2ecf20Sopenharmony_ci * same variable name on multiple calls to 4628c2ecf20Sopenharmony_ci * get_next_variable(). Terminate the loop 4638c2ecf20Sopenharmony_ci * immediately as there is no guarantee that 4648c2ecf20Sopenharmony_ci * we'll ever see a different variable name, 4658c2ecf20Sopenharmony_ci * and may end up looping here forever. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci if (duplicates && 4688c2ecf20Sopenharmony_ci variable_is_present(variable_name, &vendor_guid, 4698c2ecf20Sopenharmony_ci head)) { 4708c2ecf20Sopenharmony_ci dup_variable_bug(variable_name, &vendor_guid, 4718c2ecf20Sopenharmony_ci variable_name_size); 4728c2ecf20Sopenharmony_ci status = EFI_NOT_FOUND; 4738c2ecf20Sopenharmony_ci } else { 4748c2ecf20Sopenharmony_ci err = func(variable_name, vendor_guid, 4758c2ecf20Sopenharmony_ci variable_name_size, data); 4768c2ecf20Sopenharmony_ci if (err) 4778c2ecf20Sopenharmony_ci status = EFI_NOT_FOUND; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (duplicates) { 4818c2ecf20Sopenharmony_ci if (down_interruptible(&efivars_lock)) { 4828c2ecf20Sopenharmony_ci err = -EINTR; 4838c2ecf20Sopenharmony_ci goto free; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci case EFI_UNSUPPORTED: 4898c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 4908c2ecf20Sopenharmony_ci status = EFI_NOT_FOUND; 4918c2ecf20Sopenharmony_ci break; 4928c2ecf20Sopenharmony_ci case EFI_NOT_FOUND: 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci default: 4958c2ecf20Sopenharmony_ci printk(KERN_WARNING "efivars: get_next_variable: status=%lx\n", 4968c2ecf20Sopenharmony_ci status); 4978c2ecf20Sopenharmony_ci status = EFI_NOT_FOUND; 4988c2ecf20Sopenharmony_ci break; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci } while (status != EFI_NOT_FOUND); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci up(&efivars_lock); 5048c2ecf20Sopenharmony_cifree: 5058c2ecf20Sopenharmony_ci kfree(variable_name); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return err; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_init); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci/** 5128c2ecf20Sopenharmony_ci * efivar_entry_add - add entry to variable list 5138c2ecf20Sopenharmony_ci * @entry: entry to add to list 5148c2ecf20Sopenharmony_ci * @head: list head 5158c2ecf20Sopenharmony_ci * 5168c2ecf20Sopenharmony_ci * Returns 0 on success, or a kernel error code on failure. 5178c2ecf20Sopenharmony_ci */ 5188c2ecf20Sopenharmony_ciint efivar_entry_add(struct efivar_entry *entry, struct list_head *head) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci if (down_interruptible(&efivars_lock)) 5218c2ecf20Sopenharmony_ci return -EINTR; 5228c2ecf20Sopenharmony_ci list_add(&entry->list, head); 5238c2ecf20Sopenharmony_ci up(&efivars_lock); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return 0; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_entry_add); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci/** 5308c2ecf20Sopenharmony_ci * efivar_entry_remove - remove entry from variable list 5318c2ecf20Sopenharmony_ci * @entry: entry to remove from list 5328c2ecf20Sopenharmony_ci * 5338c2ecf20Sopenharmony_ci * Returns 0 on success, or a kernel error code on failure. 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ciint efivar_entry_remove(struct efivar_entry *entry) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci if (down_interruptible(&efivars_lock)) 5388c2ecf20Sopenharmony_ci return -EINTR; 5398c2ecf20Sopenharmony_ci list_del(&entry->list); 5408c2ecf20Sopenharmony_ci up(&efivars_lock); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_entry_remove); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/* 5478c2ecf20Sopenharmony_ci * efivar_entry_list_del_unlock - remove entry from variable list 5488c2ecf20Sopenharmony_ci * @entry: entry to remove 5498c2ecf20Sopenharmony_ci * 5508c2ecf20Sopenharmony_ci * Remove @entry from the variable list and release the list lock. 5518c2ecf20Sopenharmony_ci * 5528c2ecf20Sopenharmony_ci * NOTE: slightly weird locking semantics here - we expect to be 5538c2ecf20Sopenharmony_ci * called with the efivars lock already held, and we release it before 5548c2ecf20Sopenharmony_ci * returning. This is because this function is usually called after 5558c2ecf20Sopenharmony_ci * set_variable() while the lock is still held. 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_cistatic void efivar_entry_list_del_unlock(struct efivar_entry *entry) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci list_del(&entry->list); 5608c2ecf20Sopenharmony_ci up(&efivars_lock); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci/** 5648c2ecf20Sopenharmony_ci * __efivar_entry_delete - delete an EFI variable 5658c2ecf20Sopenharmony_ci * @entry: entry containing EFI variable to delete 5668c2ecf20Sopenharmony_ci * 5678c2ecf20Sopenharmony_ci * Delete the variable from the firmware but leave @entry on the 5688c2ecf20Sopenharmony_ci * variable list. 5698c2ecf20Sopenharmony_ci * 5708c2ecf20Sopenharmony_ci * This function differs from efivar_entry_delete() because it does 5718c2ecf20Sopenharmony_ci * not remove @entry from the variable list. Also, it is safe to be 5728c2ecf20Sopenharmony_ci * called from within a efivar_entry_iter_begin() and 5738c2ecf20Sopenharmony_ci * efivar_entry_iter_end() region, unlike efivar_entry_delete(). 5748c2ecf20Sopenharmony_ci * 5758c2ecf20Sopenharmony_ci * Returns 0 on success, or a converted EFI status code if 5768c2ecf20Sopenharmony_ci * set_variable() fails. 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_ciint __efivar_entry_delete(struct efivar_entry *entry) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci efi_status_t status; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (!__efivars) 5838c2ecf20Sopenharmony_ci return -EINVAL; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci status = __efivars->ops->set_variable(entry->var.VariableName, 5868c2ecf20Sopenharmony_ci &entry->var.VendorGuid, 5878c2ecf20Sopenharmony_ci 0, 0, NULL); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci return efi_status_to_err(status); 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__efivar_entry_delete); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci/** 5948c2ecf20Sopenharmony_ci * efivar_entry_delete - delete variable and remove entry from list 5958c2ecf20Sopenharmony_ci * @entry: entry containing variable to delete 5968c2ecf20Sopenharmony_ci * 5978c2ecf20Sopenharmony_ci * Delete the variable from the firmware and remove @entry from the 5988c2ecf20Sopenharmony_ci * variable list. It is the caller's responsibility to free @entry 5998c2ecf20Sopenharmony_ci * once we return. 6008c2ecf20Sopenharmony_ci * 6018c2ecf20Sopenharmony_ci * Returns 0 on success, -EINTR if we can't grab the semaphore, 6028c2ecf20Sopenharmony_ci * converted EFI status code if set_variable() fails. 6038c2ecf20Sopenharmony_ci */ 6048c2ecf20Sopenharmony_ciint efivar_entry_delete(struct efivar_entry *entry) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci const struct efivar_operations *ops; 6078c2ecf20Sopenharmony_ci efi_status_t status; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (down_interruptible(&efivars_lock)) 6108c2ecf20Sopenharmony_ci return -EINTR; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (!__efivars) { 6138c2ecf20Sopenharmony_ci up(&efivars_lock); 6148c2ecf20Sopenharmony_ci return -EINVAL; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci ops = __efivars->ops; 6178c2ecf20Sopenharmony_ci status = ops->set_variable(entry->var.VariableName, 6188c2ecf20Sopenharmony_ci &entry->var.VendorGuid, 6198c2ecf20Sopenharmony_ci 0, 0, NULL); 6208c2ecf20Sopenharmony_ci if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) { 6218c2ecf20Sopenharmony_ci up(&efivars_lock); 6228c2ecf20Sopenharmony_ci return efi_status_to_err(status); 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci efivar_entry_list_del_unlock(entry); 6268c2ecf20Sopenharmony_ci return 0; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_entry_delete); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci/** 6318c2ecf20Sopenharmony_ci * efivar_entry_set - call set_variable() 6328c2ecf20Sopenharmony_ci * @entry: entry containing the EFI variable to write 6338c2ecf20Sopenharmony_ci * @attributes: variable attributes 6348c2ecf20Sopenharmony_ci * @size: size of @data buffer 6358c2ecf20Sopenharmony_ci * @data: buffer containing variable data 6368c2ecf20Sopenharmony_ci * @head: head of variable list 6378c2ecf20Sopenharmony_ci * 6388c2ecf20Sopenharmony_ci * Calls set_variable() for an EFI variable. If creating a new EFI 6398c2ecf20Sopenharmony_ci * variable, this function is usually followed by efivar_entry_add(). 6408c2ecf20Sopenharmony_ci * 6418c2ecf20Sopenharmony_ci * Before writing the variable, the remaining EFI variable storage 6428c2ecf20Sopenharmony_ci * space is checked to ensure there is enough room available. 6438c2ecf20Sopenharmony_ci * 6448c2ecf20Sopenharmony_ci * If @head is not NULL a lookup is performed to determine whether 6458c2ecf20Sopenharmony_ci * the entry is already on the list. 6468c2ecf20Sopenharmony_ci * 6478c2ecf20Sopenharmony_ci * Returns 0 on success, -EINTR if we can't grab the semaphore, 6488c2ecf20Sopenharmony_ci * -EEXIST if a lookup is performed and the entry already exists on 6498c2ecf20Sopenharmony_ci * the list, or a converted EFI status code if set_variable() fails. 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_ciint efivar_entry_set(struct efivar_entry *entry, u32 attributes, 6528c2ecf20Sopenharmony_ci unsigned long size, void *data, struct list_head *head) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci const struct efivar_operations *ops; 6558c2ecf20Sopenharmony_ci efi_status_t status; 6568c2ecf20Sopenharmony_ci efi_char16_t *name = entry->var.VariableName; 6578c2ecf20Sopenharmony_ci efi_guid_t vendor = entry->var.VendorGuid; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (down_interruptible(&efivars_lock)) 6608c2ecf20Sopenharmony_ci return -EINTR; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (!__efivars) { 6638c2ecf20Sopenharmony_ci up(&efivars_lock); 6648c2ecf20Sopenharmony_ci return -EINVAL; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci ops = __efivars->ops; 6678c2ecf20Sopenharmony_ci if (head && efivar_entry_find(name, vendor, head, false)) { 6688c2ecf20Sopenharmony_ci up(&efivars_lock); 6698c2ecf20Sopenharmony_ci return -EEXIST; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci status = check_var_size(attributes, size + ucs2_strsize(name, 1024)); 6738c2ecf20Sopenharmony_ci if (status == EFI_SUCCESS || status == EFI_UNSUPPORTED) 6748c2ecf20Sopenharmony_ci status = ops->set_variable(name, &vendor, 6758c2ecf20Sopenharmony_ci attributes, size, data); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci up(&efivars_lock); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci return efi_status_to_err(status); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_entry_set); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci/* 6858c2ecf20Sopenharmony_ci * efivar_entry_set_nonblocking - call set_variable_nonblocking() 6868c2ecf20Sopenharmony_ci * 6878c2ecf20Sopenharmony_ci * This function is guaranteed to not block and is suitable for calling 6888c2ecf20Sopenharmony_ci * from crash/panic handlers. 6898c2ecf20Sopenharmony_ci * 6908c2ecf20Sopenharmony_ci * Crucially, this function will not block if it cannot acquire 6918c2ecf20Sopenharmony_ci * efivars_lock. Instead, it returns -EBUSY. 6928c2ecf20Sopenharmony_ci */ 6938c2ecf20Sopenharmony_cistatic int 6948c2ecf20Sopenharmony_ciefivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor, 6958c2ecf20Sopenharmony_ci u32 attributes, unsigned long size, void *data) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci const struct efivar_operations *ops; 6988c2ecf20Sopenharmony_ci efi_status_t status; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (down_trylock(&efivars_lock)) 7018c2ecf20Sopenharmony_ci return -EBUSY; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (!__efivars) { 7048c2ecf20Sopenharmony_ci up(&efivars_lock); 7058c2ecf20Sopenharmony_ci return -EINVAL; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci status = check_var_size_nonblocking(attributes, 7098c2ecf20Sopenharmony_ci size + ucs2_strsize(name, 1024)); 7108c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) { 7118c2ecf20Sopenharmony_ci up(&efivars_lock); 7128c2ecf20Sopenharmony_ci return -ENOSPC; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci ops = __efivars->ops; 7168c2ecf20Sopenharmony_ci status = ops->set_variable_nonblocking(name, &vendor, attributes, 7178c2ecf20Sopenharmony_ci size, data); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci up(&efivars_lock); 7208c2ecf20Sopenharmony_ci return efi_status_to_err(status); 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci/** 7248c2ecf20Sopenharmony_ci * efivar_entry_set_safe - call set_variable() if enough space in firmware 7258c2ecf20Sopenharmony_ci * @name: buffer containing the variable name 7268c2ecf20Sopenharmony_ci * @vendor: variable vendor guid 7278c2ecf20Sopenharmony_ci * @attributes: variable attributes 7288c2ecf20Sopenharmony_ci * @block: can we block in this context? 7298c2ecf20Sopenharmony_ci * @size: size of @data buffer 7308c2ecf20Sopenharmony_ci * @data: buffer containing variable data 7318c2ecf20Sopenharmony_ci * 7328c2ecf20Sopenharmony_ci * Ensures there is enough free storage in the firmware for this variable, and 7338c2ecf20Sopenharmony_ci * if so, calls set_variable(). If creating a new EFI variable, this function 7348c2ecf20Sopenharmony_ci * is usually followed by efivar_entry_add(). 7358c2ecf20Sopenharmony_ci * 7368c2ecf20Sopenharmony_ci * Returns 0 on success, -ENOSPC if the firmware does not have enough 7378c2ecf20Sopenharmony_ci * space for set_variable() to succeed, or a converted EFI status code 7388c2ecf20Sopenharmony_ci * if set_variable() fails. 7398c2ecf20Sopenharmony_ci */ 7408c2ecf20Sopenharmony_ciint efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes, 7418c2ecf20Sopenharmony_ci bool block, unsigned long size, void *data) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci const struct efivar_operations *ops; 7448c2ecf20Sopenharmony_ci efi_status_t status; 7458c2ecf20Sopenharmony_ci unsigned long varsize; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (!__efivars) 7488c2ecf20Sopenharmony_ci return -EINVAL; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci ops = __efivars->ops; 7518c2ecf20Sopenharmony_ci if (!ops->query_variable_store) 7528c2ecf20Sopenharmony_ci return -ENOSYS; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* 7558c2ecf20Sopenharmony_ci * If the EFI variable backend provides a non-blocking 7568c2ecf20Sopenharmony_ci * ->set_variable() operation and we're in a context where we 7578c2ecf20Sopenharmony_ci * cannot block, then we need to use it to avoid live-locks, 7588c2ecf20Sopenharmony_ci * since the implication is that the regular ->set_variable() 7598c2ecf20Sopenharmony_ci * will block. 7608c2ecf20Sopenharmony_ci * 7618c2ecf20Sopenharmony_ci * If no ->set_variable_nonblocking() is provided then 7628c2ecf20Sopenharmony_ci * ->set_variable() is assumed to be non-blocking. 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_ci if (!block && ops->set_variable_nonblocking) 7658c2ecf20Sopenharmony_ci return efivar_entry_set_nonblocking(name, vendor, attributes, 7668c2ecf20Sopenharmony_ci size, data); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci varsize = size + ucs2_strsize(name, 1024); 7698c2ecf20Sopenharmony_ci if (!block) { 7708c2ecf20Sopenharmony_ci if (down_trylock(&efivars_lock)) 7718c2ecf20Sopenharmony_ci return -EBUSY; 7728c2ecf20Sopenharmony_ci status = check_var_size_nonblocking(attributes, varsize); 7738c2ecf20Sopenharmony_ci } else { 7748c2ecf20Sopenharmony_ci if (down_interruptible(&efivars_lock)) 7758c2ecf20Sopenharmony_ci return -EINTR; 7768c2ecf20Sopenharmony_ci status = check_var_size(attributes, varsize); 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) { 7808c2ecf20Sopenharmony_ci up(&efivars_lock); 7818c2ecf20Sopenharmony_ci return -ENOSPC; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci status = ops->set_variable(name, &vendor, attributes, size, data); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci up(&efivars_lock); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci return efi_status_to_err(status); 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_entry_set_safe); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci/** 7938c2ecf20Sopenharmony_ci * efivar_entry_find - search for an entry 7948c2ecf20Sopenharmony_ci * @name: the EFI variable name 7958c2ecf20Sopenharmony_ci * @guid: the EFI variable vendor's guid 7968c2ecf20Sopenharmony_ci * @head: head of the variable list 7978c2ecf20Sopenharmony_ci * @remove: should we remove the entry from the list? 7988c2ecf20Sopenharmony_ci * 7998c2ecf20Sopenharmony_ci * Search for an entry on the variable list that has the EFI variable 8008c2ecf20Sopenharmony_ci * name @name and vendor guid @guid. If an entry is found on the list 8018c2ecf20Sopenharmony_ci * and @remove is true, the entry is removed from the list. 8028c2ecf20Sopenharmony_ci * 8038c2ecf20Sopenharmony_ci * The caller MUST call efivar_entry_iter_begin() and 8048c2ecf20Sopenharmony_ci * efivar_entry_iter_end() before and after the invocation of this 8058c2ecf20Sopenharmony_ci * function, respectively. 8068c2ecf20Sopenharmony_ci * 8078c2ecf20Sopenharmony_ci * Returns the entry if found on the list, %NULL otherwise. 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_cistruct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid, 8108c2ecf20Sopenharmony_ci struct list_head *head, bool remove) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct efivar_entry *entry, *n; 8138c2ecf20Sopenharmony_ci int strsize1, strsize2; 8148c2ecf20Sopenharmony_ci bool found = false; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, n, head, list) { 8178c2ecf20Sopenharmony_ci strsize1 = ucs2_strsize(name, 1024); 8188c2ecf20Sopenharmony_ci strsize2 = ucs2_strsize(entry->var.VariableName, 1024); 8198c2ecf20Sopenharmony_ci if (strsize1 == strsize2 && 8208c2ecf20Sopenharmony_ci !memcmp(name, &(entry->var.VariableName), strsize1) && 8218c2ecf20Sopenharmony_ci !efi_guidcmp(guid, entry->var.VendorGuid)) { 8228c2ecf20Sopenharmony_ci found = true; 8238c2ecf20Sopenharmony_ci break; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (!found) 8288c2ecf20Sopenharmony_ci return NULL; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (remove) { 8318c2ecf20Sopenharmony_ci if (entry->scanning) { 8328c2ecf20Sopenharmony_ci /* 8338c2ecf20Sopenharmony_ci * The entry will be deleted 8348c2ecf20Sopenharmony_ci * after scanning is completed. 8358c2ecf20Sopenharmony_ci */ 8368c2ecf20Sopenharmony_ci entry->deleting = true; 8378c2ecf20Sopenharmony_ci } else 8388c2ecf20Sopenharmony_ci list_del(&entry->list); 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci return entry; 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_entry_find); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci/** 8468c2ecf20Sopenharmony_ci * efivar_entry_size - obtain the size of a variable 8478c2ecf20Sopenharmony_ci * @entry: entry for this variable 8488c2ecf20Sopenharmony_ci * @size: location to store the variable's size 8498c2ecf20Sopenharmony_ci */ 8508c2ecf20Sopenharmony_ciint efivar_entry_size(struct efivar_entry *entry, unsigned long *size) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci const struct efivar_operations *ops; 8538c2ecf20Sopenharmony_ci efi_status_t status; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci *size = 0; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (down_interruptible(&efivars_lock)) 8588c2ecf20Sopenharmony_ci return -EINTR; 8598c2ecf20Sopenharmony_ci if (!__efivars) { 8608c2ecf20Sopenharmony_ci up(&efivars_lock); 8618c2ecf20Sopenharmony_ci return -EINVAL; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci ops = __efivars->ops; 8648c2ecf20Sopenharmony_ci status = ops->get_variable(entry->var.VariableName, 8658c2ecf20Sopenharmony_ci &entry->var.VendorGuid, NULL, size, NULL); 8668c2ecf20Sopenharmony_ci up(&efivars_lock); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (status != EFI_BUFFER_TOO_SMALL) 8698c2ecf20Sopenharmony_ci return efi_status_to_err(status); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci return 0; 8728c2ecf20Sopenharmony_ci} 8738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_entry_size); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci/** 8768c2ecf20Sopenharmony_ci * __efivar_entry_get - call get_variable() 8778c2ecf20Sopenharmony_ci * @entry: read data for this variable 8788c2ecf20Sopenharmony_ci * @attributes: variable attributes 8798c2ecf20Sopenharmony_ci * @size: size of @data buffer 8808c2ecf20Sopenharmony_ci * @data: buffer to store variable data 8818c2ecf20Sopenharmony_ci * 8828c2ecf20Sopenharmony_ci * The caller MUST call efivar_entry_iter_begin() and 8838c2ecf20Sopenharmony_ci * efivar_entry_iter_end() before and after the invocation of this 8848c2ecf20Sopenharmony_ci * function, respectively. 8858c2ecf20Sopenharmony_ci */ 8868c2ecf20Sopenharmony_ciint __efivar_entry_get(struct efivar_entry *entry, u32 *attributes, 8878c2ecf20Sopenharmony_ci unsigned long *size, void *data) 8888c2ecf20Sopenharmony_ci{ 8898c2ecf20Sopenharmony_ci efi_status_t status; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (!__efivars) 8928c2ecf20Sopenharmony_ci return -EINVAL; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci status = __efivars->ops->get_variable(entry->var.VariableName, 8958c2ecf20Sopenharmony_ci &entry->var.VendorGuid, 8968c2ecf20Sopenharmony_ci attributes, size, data); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci return efi_status_to_err(status); 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__efivar_entry_get); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci/** 9038c2ecf20Sopenharmony_ci * efivar_entry_get - call get_variable() 9048c2ecf20Sopenharmony_ci * @entry: read data for this variable 9058c2ecf20Sopenharmony_ci * @attributes: variable attributes 9068c2ecf20Sopenharmony_ci * @size: size of @data buffer 9078c2ecf20Sopenharmony_ci * @data: buffer to store variable data 9088c2ecf20Sopenharmony_ci */ 9098c2ecf20Sopenharmony_ciint efivar_entry_get(struct efivar_entry *entry, u32 *attributes, 9108c2ecf20Sopenharmony_ci unsigned long *size, void *data) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci efi_status_t status; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (down_interruptible(&efivars_lock)) 9158c2ecf20Sopenharmony_ci return -EINTR; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (!__efivars) { 9188c2ecf20Sopenharmony_ci up(&efivars_lock); 9198c2ecf20Sopenharmony_ci return -EINVAL; 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci status = __efivars->ops->get_variable(entry->var.VariableName, 9238c2ecf20Sopenharmony_ci &entry->var.VendorGuid, 9248c2ecf20Sopenharmony_ci attributes, size, data); 9258c2ecf20Sopenharmony_ci up(&efivars_lock); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci return efi_status_to_err(status); 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_entry_get); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci/** 9328c2ecf20Sopenharmony_ci * efivar_entry_set_get_size - call set_variable() and get new size (atomic) 9338c2ecf20Sopenharmony_ci * @entry: entry containing variable to set and get 9348c2ecf20Sopenharmony_ci * @attributes: attributes of variable to be written 9358c2ecf20Sopenharmony_ci * @size: size of data buffer 9368c2ecf20Sopenharmony_ci * @data: buffer containing data to write 9378c2ecf20Sopenharmony_ci * @set: did the set_variable() call succeed? 9388c2ecf20Sopenharmony_ci * 9398c2ecf20Sopenharmony_ci * This is a pretty special (complex) function. See efivarfs_file_write(). 9408c2ecf20Sopenharmony_ci * 9418c2ecf20Sopenharmony_ci * Atomically call set_variable() for @entry and if the call is 9428c2ecf20Sopenharmony_ci * successful, return the new size of the variable from get_variable() 9438c2ecf20Sopenharmony_ci * in @size. The success of set_variable() is indicated by @set. 9448c2ecf20Sopenharmony_ci * 9458c2ecf20Sopenharmony_ci * Returns 0 on success, -EINVAL if the variable data is invalid, 9468c2ecf20Sopenharmony_ci * -ENOSPC if the firmware does not have enough available space, or a 9478c2ecf20Sopenharmony_ci * converted EFI status code if either of set_variable() or 9488c2ecf20Sopenharmony_ci * get_variable() fail. 9498c2ecf20Sopenharmony_ci * 9508c2ecf20Sopenharmony_ci * If the EFI variable does not exist when calling set_variable() 9518c2ecf20Sopenharmony_ci * (EFI_NOT_FOUND), @entry is removed from the variable list. 9528c2ecf20Sopenharmony_ci */ 9538c2ecf20Sopenharmony_ciint efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes, 9548c2ecf20Sopenharmony_ci unsigned long *size, void *data, bool *set) 9558c2ecf20Sopenharmony_ci{ 9568c2ecf20Sopenharmony_ci const struct efivar_operations *ops; 9578c2ecf20Sopenharmony_ci efi_char16_t *name = entry->var.VariableName; 9588c2ecf20Sopenharmony_ci efi_guid_t *vendor = &entry->var.VendorGuid; 9598c2ecf20Sopenharmony_ci efi_status_t status; 9608c2ecf20Sopenharmony_ci int err; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci *set = false; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (efivar_validate(*vendor, name, data, *size) == false) 9658c2ecf20Sopenharmony_ci return -EINVAL; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci /* 9688c2ecf20Sopenharmony_ci * The lock here protects the get_variable call, the conditional 9698c2ecf20Sopenharmony_ci * set_variable call, and removal of the variable from the efivars 9708c2ecf20Sopenharmony_ci * list (in the case of an authenticated delete). 9718c2ecf20Sopenharmony_ci */ 9728c2ecf20Sopenharmony_ci if (down_interruptible(&efivars_lock)) 9738c2ecf20Sopenharmony_ci return -EINTR; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci if (!__efivars) { 9768c2ecf20Sopenharmony_ci err = -EINVAL; 9778c2ecf20Sopenharmony_ci goto out; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* 9818c2ecf20Sopenharmony_ci * Ensure that the available space hasn't shrunk below the safe level 9828c2ecf20Sopenharmony_ci */ 9838c2ecf20Sopenharmony_ci status = check_var_size(attributes, *size + ucs2_strsize(name, 1024)); 9848c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) { 9858c2ecf20Sopenharmony_ci if (status != EFI_UNSUPPORTED) { 9868c2ecf20Sopenharmony_ci err = efi_status_to_err(status); 9878c2ecf20Sopenharmony_ci goto out; 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (*size > 65536) { 9918c2ecf20Sopenharmony_ci err = -ENOSPC; 9928c2ecf20Sopenharmony_ci goto out; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci ops = __efivars->ops; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci status = ops->set_variable(name, vendor, attributes, *size, data); 9998c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) { 10008c2ecf20Sopenharmony_ci err = efi_status_to_err(status); 10018c2ecf20Sopenharmony_ci goto out; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci *set = true; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci /* 10078c2ecf20Sopenharmony_ci * Writing to the variable may have caused a change in size (which 10088c2ecf20Sopenharmony_ci * could either be an append or an overwrite), or the variable to be 10098c2ecf20Sopenharmony_ci * deleted. Perform a GetVariable() so we can tell what actually 10108c2ecf20Sopenharmony_ci * happened. 10118c2ecf20Sopenharmony_ci */ 10128c2ecf20Sopenharmony_ci *size = 0; 10138c2ecf20Sopenharmony_ci status = ops->get_variable(entry->var.VariableName, 10148c2ecf20Sopenharmony_ci &entry->var.VendorGuid, 10158c2ecf20Sopenharmony_ci NULL, size, NULL); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci if (status == EFI_NOT_FOUND) 10188c2ecf20Sopenharmony_ci efivar_entry_list_del_unlock(entry); 10198c2ecf20Sopenharmony_ci else 10208c2ecf20Sopenharmony_ci up(&efivars_lock); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (status && status != EFI_BUFFER_TOO_SMALL) 10238c2ecf20Sopenharmony_ci return efi_status_to_err(status); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci return 0; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ciout: 10288c2ecf20Sopenharmony_ci up(&efivars_lock); 10298c2ecf20Sopenharmony_ci return err; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_entry_set_get_size); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci/** 10358c2ecf20Sopenharmony_ci * efivar_entry_iter_begin - begin iterating the variable list 10368c2ecf20Sopenharmony_ci * 10378c2ecf20Sopenharmony_ci * Lock the variable list to prevent entry insertion and removal until 10388c2ecf20Sopenharmony_ci * efivar_entry_iter_end() is called. This function is usually used in 10398c2ecf20Sopenharmony_ci * conjunction with __efivar_entry_iter() or efivar_entry_iter(). 10408c2ecf20Sopenharmony_ci */ 10418c2ecf20Sopenharmony_ciint efivar_entry_iter_begin(void) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci return down_interruptible(&efivars_lock); 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_entry_iter_begin); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci/** 10488c2ecf20Sopenharmony_ci * efivar_entry_iter_end - finish iterating the variable list 10498c2ecf20Sopenharmony_ci * 10508c2ecf20Sopenharmony_ci * Unlock the variable list and allow modifications to the list again. 10518c2ecf20Sopenharmony_ci */ 10528c2ecf20Sopenharmony_civoid efivar_entry_iter_end(void) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci up(&efivars_lock); 10558c2ecf20Sopenharmony_ci} 10568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_entry_iter_end); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci/** 10598c2ecf20Sopenharmony_ci * __efivar_entry_iter - iterate over variable list 10608c2ecf20Sopenharmony_ci * @func: callback function 10618c2ecf20Sopenharmony_ci * @head: head of the variable list 10628c2ecf20Sopenharmony_ci * @data: function-specific data to pass to callback 10638c2ecf20Sopenharmony_ci * @prev: entry to begin iterating from 10648c2ecf20Sopenharmony_ci * 10658c2ecf20Sopenharmony_ci * Iterate over the list of EFI variables and call @func with every 10668c2ecf20Sopenharmony_ci * entry on the list. It is safe for @func to remove entries in the 10678c2ecf20Sopenharmony_ci * list via efivar_entry_delete(). 10688c2ecf20Sopenharmony_ci * 10698c2ecf20Sopenharmony_ci * You MUST call efivar_entry_iter_begin() before this function, and 10708c2ecf20Sopenharmony_ci * efivar_entry_iter_end() afterwards. 10718c2ecf20Sopenharmony_ci * 10728c2ecf20Sopenharmony_ci * It is possible to begin iteration from an arbitrary entry within 10738c2ecf20Sopenharmony_ci * the list by passing @prev. @prev is updated on return to point to 10748c2ecf20Sopenharmony_ci * the last entry passed to @func. To begin iterating from the 10758c2ecf20Sopenharmony_ci * beginning of the list @prev must be %NULL. 10768c2ecf20Sopenharmony_ci * 10778c2ecf20Sopenharmony_ci * The restrictions for @func are the same as documented for 10788c2ecf20Sopenharmony_ci * efivar_entry_iter(). 10798c2ecf20Sopenharmony_ci */ 10808c2ecf20Sopenharmony_ciint __efivar_entry_iter(int (*func)(struct efivar_entry *, void *), 10818c2ecf20Sopenharmony_ci struct list_head *head, void *data, 10828c2ecf20Sopenharmony_ci struct efivar_entry **prev) 10838c2ecf20Sopenharmony_ci{ 10848c2ecf20Sopenharmony_ci struct efivar_entry *entry, *n; 10858c2ecf20Sopenharmony_ci int err = 0; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci if (!prev || !*prev) { 10888c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, n, head, list) { 10898c2ecf20Sopenharmony_ci err = func(entry, data); 10908c2ecf20Sopenharmony_ci if (err) 10918c2ecf20Sopenharmony_ci break; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (prev) 10958c2ecf20Sopenharmony_ci *prev = entry; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci return err; 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci list_for_each_entry_safe_continue((*prev), n, head, list) { 11028c2ecf20Sopenharmony_ci err = func(*prev, data); 11038c2ecf20Sopenharmony_ci if (err) 11048c2ecf20Sopenharmony_ci break; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci return err; 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__efivar_entry_iter); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci/** 11128c2ecf20Sopenharmony_ci * efivar_entry_iter - iterate over variable list 11138c2ecf20Sopenharmony_ci * @func: callback function 11148c2ecf20Sopenharmony_ci * @head: head of variable list 11158c2ecf20Sopenharmony_ci * @data: function-specific data to pass to callback 11168c2ecf20Sopenharmony_ci * 11178c2ecf20Sopenharmony_ci * Iterate over the list of EFI variables and call @func with every 11188c2ecf20Sopenharmony_ci * entry on the list. It is safe for @func to remove entries in the 11198c2ecf20Sopenharmony_ci * list via efivar_entry_delete() while iterating. 11208c2ecf20Sopenharmony_ci * 11218c2ecf20Sopenharmony_ci * Some notes for the callback function: 11228c2ecf20Sopenharmony_ci * - a non-zero return value indicates an error and terminates the loop 11238c2ecf20Sopenharmony_ci * - @func is called from atomic context 11248c2ecf20Sopenharmony_ci */ 11258c2ecf20Sopenharmony_ciint efivar_entry_iter(int (*func)(struct efivar_entry *, void *), 11268c2ecf20Sopenharmony_ci struct list_head *head, void *data) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci int err = 0; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci err = efivar_entry_iter_begin(); 11318c2ecf20Sopenharmony_ci if (err) 11328c2ecf20Sopenharmony_ci return err; 11338c2ecf20Sopenharmony_ci err = __efivar_entry_iter(func, head, data, NULL); 11348c2ecf20Sopenharmony_ci efivar_entry_iter_end(); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci return err; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_entry_iter); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci/** 11418c2ecf20Sopenharmony_ci * efivars_kobject - get the kobject for the registered efivars 11428c2ecf20Sopenharmony_ci * 11438c2ecf20Sopenharmony_ci * If efivars_register() has not been called we return NULL, 11448c2ecf20Sopenharmony_ci * otherwise return the kobject used at registration time. 11458c2ecf20Sopenharmony_ci */ 11468c2ecf20Sopenharmony_cistruct kobject *efivars_kobject(void) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci if (!__efivars) 11498c2ecf20Sopenharmony_ci return NULL; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci return __efivars->kobject; 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivars_kobject); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci/** 11568c2ecf20Sopenharmony_ci * efivars_register - register an efivars 11578c2ecf20Sopenharmony_ci * @efivars: efivars to register 11588c2ecf20Sopenharmony_ci * @ops: efivars operations 11598c2ecf20Sopenharmony_ci * @kobject: @efivars-specific kobject 11608c2ecf20Sopenharmony_ci * 11618c2ecf20Sopenharmony_ci * Only a single efivars can be registered at any time. 11628c2ecf20Sopenharmony_ci */ 11638c2ecf20Sopenharmony_ciint efivars_register(struct efivars *efivars, 11648c2ecf20Sopenharmony_ci const struct efivar_operations *ops, 11658c2ecf20Sopenharmony_ci struct kobject *kobject) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci if (down_interruptible(&efivars_lock)) 11688c2ecf20Sopenharmony_ci return -EINTR; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci efivars->ops = ops; 11718c2ecf20Sopenharmony_ci efivars->kobject = kobject; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci __efivars = efivars; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci pr_info("Registered efivars operations\n"); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci up(&efivars_lock); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci return 0; 11808c2ecf20Sopenharmony_ci} 11818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivars_register); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci/** 11848c2ecf20Sopenharmony_ci * efivars_unregister - unregister an efivars 11858c2ecf20Sopenharmony_ci * @efivars: efivars to unregister 11868c2ecf20Sopenharmony_ci * 11878c2ecf20Sopenharmony_ci * The caller must have already removed every entry from the list, 11888c2ecf20Sopenharmony_ci * failure to do so is an error. 11898c2ecf20Sopenharmony_ci */ 11908c2ecf20Sopenharmony_ciint efivars_unregister(struct efivars *efivars) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci int rv; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if (down_interruptible(&efivars_lock)) 11958c2ecf20Sopenharmony_ci return -EINTR; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if (!__efivars) { 11988c2ecf20Sopenharmony_ci printk(KERN_ERR "efivars not registered\n"); 11998c2ecf20Sopenharmony_ci rv = -EINVAL; 12008c2ecf20Sopenharmony_ci goto out; 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (__efivars != efivars) { 12048c2ecf20Sopenharmony_ci rv = -EINVAL; 12058c2ecf20Sopenharmony_ci goto out; 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci pr_info("Unregistered efivars operations\n"); 12098c2ecf20Sopenharmony_ci __efivars = NULL; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci rv = 0; 12128c2ecf20Sopenharmony_ciout: 12138c2ecf20Sopenharmony_ci up(&efivars_lock); 12148c2ecf20Sopenharmony_ci return rv; 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivars_unregister); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ciint efivar_supports_writes(void) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci return __efivars && __efivars->ops->set_variable; 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(efivar_supports_writes); 1223