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