18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * EFI Test Driver for Runtime Services
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright(C) 2012-2016 Canonical Ltd.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This driver exports EFI runtime services interfaces into userspace, which
88c2ecf20Sopenharmony_ci * allow to use and test UEFI runtime services provided by firmware.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
168c2ecf20Sopenharmony_ci#include <linux/efi.h>
178c2ecf20Sopenharmony_ci#include <linux/security.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "efi_test.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ivan Hu <ivan.hu@canonical.com>");
248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EFI Test Driver");
258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * Count the bytes in 'str', including the terminating NULL.
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * Note this function returns the number of *bytes*, not the number of
318c2ecf20Sopenharmony_ci * ucs2 characters.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_cistatic inline size_t user_ucs2_strsize(efi_char16_t  __user *str)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	efi_char16_t *s = str, c;
368c2ecf20Sopenharmony_ci	size_t len;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (!str)
398c2ecf20Sopenharmony_ci		return 0;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* Include terminating NULL */
428c2ecf20Sopenharmony_ci	len = sizeof(efi_char16_t);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (get_user(c, s++)) {
458c2ecf20Sopenharmony_ci		/* Can't read userspace memory for size */
468c2ecf20Sopenharmony_ci		return 0;
478c2ecf20Sopenharmony_ci	}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	while (c != 0) {
508c2ecf20Sopenharmony_ci		if (get_user(c, s++)) {
518c2ecf20Sopenharmony_ci			/* Can't read userspace memory for size */
528c2ecf20Sopenharmony_ci			return 0;
538c2ecf20Sopenharmony_ci		}
548c2ecf20Sopenharmony_ci		len += sizeof(efi_char16_t);
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci	return len;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/*
608c2ecf20Sopenharmony_ci * Allocate a buffer and copy a ucs2 string from user space into it.
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_cistatic inline int
638c2ecf20Sopenharmony_cicopy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src,
648c2ecf20Sopenharmony_ci			size_t len)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	efi_char16_t *buf;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (!src) {
698c2ecf20Sopenharmony_ci		*dst = NULL;
708c2ecf20Sopenharmony_ci		return 0;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	buf = memdup_user(src, len);
748c2ecf20Sopenharmony_ci	if (IS_ERR(buf)) {
758c2ecf20Sopenharmony_ci		*dst = NULL;
768c2ecf20Sopenharmony_ci		return PTR_ERR(buf);
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci	*dst = buf;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return 0;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/*
848c2ecf20Sopenharmony_ci * Count the bytes in 'str', including the terminating NULL.
858c2ecf20Sopenharmony_ci *
868c2ecf20Sopenharmony_ci * Just a wrap for user_ucs2_strsize
878c2ecf20Sopenharmony_ci */
888c2ecf20Sopenharmony_cistatic inline int
898c2ecf20Sopenharmony_ciget_ucs2_strsize_from_user(efi_char16_t __user *src, size_t *len)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	*len = user_ucs2_strsize(src);
928c2ecf20Sopenharmony_ci	if (*len == 0)
938c2ecf20Sopenharmony_ci		return -EFAULT;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return 0;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/*
998c2ecf20Sopenharmony_ci * Calculate the required buffer allocation size and copy a ucs2 string
1008c2ecf20Sopenharmony_ci * from user space into it.
1018c2ecf20Sopenharmony_ci *
1028c2ecf20Sopenharmony_ci * This function differs from copy_ucs2_from_user_len() because it
1038c2ecf20Sopenharmony_ci * calculates the size of the buffer to allocate by taking the length of
1048c2ecf20Sopenharmony_ci * the string 'src'.
1058c2ecf20Sopenharmony_ci *
1068c2ecf20Sopenharmony_ci * If a non-zero value is returned, the caller MUST NOT access 'dst'.
1078c2ecf20Sopenharmony_ci *
1088c2ecf20Sopenharmony_ci * It is the caller's responsibility to free 'dst'.
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_cistatic inline int
1118c2ecf20Sopenharmony_cicopy_ucs2_from_user(efi_char16_t **dst, efi_char16_t __user *src)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	size_t len;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	len = user_ucs2_strsize(src);
1168c2ecf20Sopenharmony_ci	if (len == 0)
1178c2ecf20Sopenharmony_ci		return -EFAULT;
1188c2ecf20Sopenharmony_ci	return copy_ucs2_from_user_len(dst, src, len);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/*
1228c2ecf20Sopenharmony_ci * Copy a ucs2 string to a user buffer.
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * This function is a simple wrapper around copy_to_user() that does
1258c2ecf20Sopenharmony_ci * nothing if 'src' is NULL, which is useful for reducing the amount of
1268c2ecf20Sopenharmony_ci * NULL checking the caller has to do.
1278c2ecf20Sopenharmony_ci *
1288c2ecf20Sopenharmony_ci * 'len' specifies the number of bytes to copy.
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_cistatic inline int
1318c2ecf20Sopenharmony_cicopy_ucs2_to_user_len(efi_char16_t __user *dst, efi_char16_t *src, size_t len)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	if (!src)
1348c2ecf20Sopenharmony_ci		return 0;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	return copy_to_user(dst, src, len);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic long efi_runtime_get_variable(unsigned long arg)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct efi_getvariable __user *getvariable_user;
1428c2ecf20Sopenharmony_ci	struct efi_getvariable getvariable;
1438c2ecf20Sopenharmony_ci	unsigned long datasize = 0, prev_datasize, *dz;
1448c2ecf20Sopenharmony_ci	efi_guid_t vendor_guid, *vd = NULL;
1458c2ecf20Sopenharmony_ci	efi_status_t status;
1468c2ecf20Sopenharmony_ci	efi_char16_t *name = NULL;
1478c2ecf20Sopenharmony_ci	u32 attr, *at;
1488c2ecf20Sopenharmony_ci	void *data = NULL;
1498c2ecf20Sopenharmony_ci	int rv = 0;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	getvariable_user = (struct efi_getvariable __user *)arg;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (copy_from_user(&getvariable, getvariable_user,
1548c2ecf20Sopenharmony_ci			   sizeof(getvariable)))
1558c2ecf20Sopenharmony_ci		return -EFAULT;
1568c2ecf20Sopenharmony_ci	if (getvariable.data_size &&
1578c2ecf20Sopenharmony_ci	    get_user(datasize, getvariable.data_size))
1588c2ecf20Sopenharmony_ci		return -EFAULT;
1598c2ecf20Sopenharmony_ci	if (getvariable.vendor_guid) {
1608c2ecf20Sopenharmony_ci		if (copy_from_user(&vendor_guid, getvariable.vendor_guid,
1618c2ecf20Sopenharmony_ci					sizeof(vendor_guid)))
1628c2ecf20Sopenharmony_ci			return -EFAULT;
1638c2ecf20Sopenharmony_ci		vd = &vendor_guid;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (getvariable.variable_name) {
1678c2ecf20Sopenharmony_ci		rv = copy_ucs2_from_user(&name, getvariable.variable_name);
1688c2ecf20Sopenharmony_ci		if (rv)
1698c2ecf20Sopenharmony_ci			return rv;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	at = getvariable.attributes ? &attr : NULL;
1738c2ecf20Sopenharmony_ci	dz = getvariable.data_size ? &datasize : NULL;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (getvariable.data_size && getvariable.data) {
1768c2ecf20Sopenharmony_ci		data = kmalloc(datasize, GFP_KERNEL);
1778c2ecf20Sopenharmony_ci		if (!data) {
1788c2ecf20Sopenharmony_ci			kfree(name);
1798c2ecf20Sopenharmony_ci			return -ENOMEM;
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	prev_datasize = datasize;
1848c2ecf20Sopenharmony_ci	status = efi.get_variable(name, vd, at, dz, data);
1858c2ecf20Sopenharmony_ci	kfree(name);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (put_user(status, getvariable.status)) {
1888c2ecf20Sopenharmony_ci		rv = -EFAULT;
1898c2ecf20Sopenharmony_ci		goto out;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS) {
1938c2ecf20Sopenharmony_ci		if (status == EFI_BUFFER_TOO_SMALL) {
1948c2ecf20Sopenharmony_ci			if (dz && put_user(datasize, getvariable.data_size)) {
1958c2ecf20Sopenharmony_ci				rv = -EFAULT;
1968c2ecf20Sopenharmony_ci				goto out;
1978c2ecf20Sopenharmony_ci			}
1988c2ecf20Sopenharmony_ci		}
1998c2ecf20Sopenharmony_ci		rv = -EINVAL;
2008c2ecf20Sopenharmony_ci		goto out;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (prev_datasize < datasize) {
2048c2ecf20Sopenharmony_ci		rv = -EINVAL;
2058c2ecf20Sopenharmony_ci		goto out;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (data) {
2098c2ecf20Sopenharmony_ci		if (copy_to_user(getvariable.data, data, datasize)) {
2108c2ecf20Sopenharmony_ci			rv = -EFAULT;
2118c2ecf20Sopenharmony_ci			goto out;
2128c2ecf20Sopenharmony_ci		}
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (at && put_user(attr, getvariable.attributes)) {
2168c2ecf20Sopenharmony_ci		rv = -EFAULT;
2178c2ecf20Sopenharmony_ci		goto out;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (dz && put_user(datasize, getvariable.data_size))
2218c2ecf20Sopenharmony_ci		rv = -EFAULT;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ciout:
2248c2ecf20Sopenharmony_ci	kfree(data);
2258c2ecf20Sopenharmony_ci	return rv;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic long efi_runtime_set_variable(unsigned long arg)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct efi_setvariable __user *setvariable_user;
2328c2ecf20Sopenharmony_ci	struct efi_setvariable setvariable;
2338c2ecf20Sopenharmony_ci	efi_guid_t vendor_guid;
2348c2ecf20Sopenharmony_ci	efi_status_t status;
2358c2ecf20Sopenharmony_ci	efi_char16_t *name = NULL;
2368c2ecf20Sopenharmony_ci	void *data;
2378c2ecf20Sopenharmony_ci	int rv = 0;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	setvariable_user = (struct efi_setvariable __user *)arg;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (copy_from_user(&setvariable, setvariable_user, sizeof(setvariable)))
2428c2ecf20Sopenharmony_ci		return -EFAULT;
2438c2ecf20Sopenharmony_ci	if (copy_from_user(&vendor_guid, setvariable.vendor_guid,
2448c2ecf20Sopenharmony_ci				sizeof(vendor_guid)))
2458c2ecf20Sopenharmony_ci		return -EFAULT;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (setvariable.variable_name) {
2488c2ecf20Sopenharmony_ci		rv = copy_ucs2_from_user(&name, setvariable.variable_name);
2498c2ecf20Sopenharmony_ci		if (rv)
2508c2ecf20Sopenharmony_ci			return rv;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	data = memdup_user(setvariable.data, setvariable.data_size);
2548c2ecf20Sopenharmony_ci	if (IS_ERR(data)) {
2558c2ecf20Sopenharmony_ci		kfree(name);
2568c2ecf20Sopenharmony_ci		return PTR_ERR(data);
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	status = efi.set_variable(name, &vendor_guid,
2608c2ecf20Sopenharmony_ci				setvariable.attributes,
2618c2ecf20Sopenharmony_ci				setvariable.data_size, data);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (put_user(status, setvariable.status)) {
2648c2ecf20Sopenharmony_ci		rv = -EFAULT;
2658c2ecf20Sopenharmony_ci		goto out;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	rv = status == EFI_SUCCESS ? 0 : -EINVAL;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ciout:
2718c2ecf20Sopenharmony_ci	kfree(data);
2728c2ecf20Sopenharmony_ci	kfree(name);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return rv;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic long efi_runtime_get_time(unsigned long arg)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct efi_gettime __user *gettime_user;
2808c2ecf20Sopenharmony_ci	struct efi_gettime  gettime;
2818c2ecf20Sopenharmony_ci	efi_status_t status;
2828c2ecf20Sopenharmony_ci	efi_time_cap_t cap;
2838c2ecf20Sopenharmony_ci	efi_time_t efi_time;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	gettime_user = (struct efi_gettime __user *)arg;
2868c2ecf20Sopenharmony_ci	if (copy_from_user(&gettime, gettime_user, sizeof(gettime)))
2878c2ecf20Sopenharmony_ci		return -EFAULT;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	status = efi.get_time(gettime.time ? &efi_time : NULL,
2908c2ecf20Sopenharmony_ci			      gettime.capabilities ? &cap : NULL);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (put_user(status, gettime.status))
2938c2ecf20Sopenharmony_ci		return -EFAULT;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS)
2968c2ecf20Sopenharmony_ci		return -EINVAL;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (gettime.capabilities) {
2998c2ecf20Sopenharmony_ci		efi_time_cap_t __user *cap_local;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		cap_local = (efi_time_cap_t *)gettime.capabilities;
3028c2ecf20Sopenharmony_ci		if (put_user(cap.resolution, &(cap_local->resolution)) ||
3038c2ecf20Sopenharmony_ci			put_user(cap.accuracy, &(cap_local->accuracy)) ||
3048c2ecf20Sopenharmony_ci			put_user(cap.sets_to_zero, &(cap_local->sets_to_zero)))
3058c2ecf20Sopenharmony_ci			return -EFAULT;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci	if (gettime.time) {
3088c2ecf20Sopenharmony_ci		if (copy_to_user(gettime.time, &efi_time, sizeof(efi_time_t)))
3098c2ecf20Sopenharmony_ci			return -EFAULT;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	return 0;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic long efi_runtime_set_time(unsigned long arg)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct efi_settime __user *settime_user;
3188c2ecf20Sopenharmony_ci	struct efi_settime settime;
3198c2ecf20Sopenharmony_ci	efi_status_t status;
3208c2ecf20Sopenharmony_ci	efi_time_t efi_time;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	settime_user = (struct efi_settime __user *)arg;
3238c2ecf20Sopenharmony_ci	if (copy_from_user(&settime, settime_user, sizeof(settime)))
3248c2ecf20Sopenharmony_ci		return -EFAULT;
3258c2ecf20Sopenharmony_ci	if (copy_from_user(&efi_time, settime.time,
3268c2ecf20Sopenharmony_ci					sizeof(efi_time_t)))
3278c2ecf20Sopenharmony_ci		return -EFAULT;
3288c2ecf20Sopenharmony_ci	status = efi.set_time(&efi_time);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (put_user(status, settime.status))
3318c2ecf20Sopenharmony_ci		return -EFAULT;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return status == EFI_SUCCESS ? 0 : -EINVAL;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic long efi_runtime_get_waketime(unsigned long arg)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct efi_getwakeuptime __user *getwakeuptime_user;
3398c2ecf20Sopenharmony_ci	struct efi_getwakeuptime getwakeuptime;
3408c2ecf20Sopenharmony_ci	efi_bool_t enabled, pending;
3418c2ecf20Sopenharmony_ci	efi_status_t status;
3428c2ecf20Sopenharmony_ci	efi_time_t efi_time;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	getwakeuptime_user = (struct efi_getwakeuptime __user *)arg;
3458c2ecf20Sopenharmony_ci	if (copy_from_user(&getwakeuptime, getwakeuptime_user,
3468c2ecf20Sopenharmony_ci				sizeof(getwakeuptime)))
3478c2ecf20Sopenharmony_ci		return -EFAULT;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	status = efi.get_wakeup_time(
3508c2ecf20Sopenharmony_ci		getwakeuptime.enabled ? (efi_bool_t *)&enabled : NULL,
3518c2ecf20Sopenharmony_ci		getwakeuptime.pending ? (efi_bool_t *)&pending : NULL,
3528c2ecf20Sopenharmony_ci		getwakeuptime.time ? &efi_time : NULL);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	if (put_user(status, getwakeuptime.status))
3558c2ecf20Sopenharmony_ci		return -EFAULT;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS)
3588c2ecf20Sopenharmony_ci		return -EINVAL;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (getwakeuptime.enabled && put_user(enabled,
3618c2ecf20Sopenharmony_ci						getwakeuptime.enabled))
3628c2ecf20Sopenharmony_ci		return -EFAULT;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (getwakeuptime.time) {
3658c2ecf20Sopenharmony_ci		if (copy_to_user(getwakeuptime.time, &efi_time,
3668c2ecf20Sopenharmony_ci				sizeof(efi_time_t)))
3678c2ecf20Sopenharmony_ci			return -EFAULT;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	return 0;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic long efi_runtime_set_waketime(unsigned long arg)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct efi_setwakeuptime __user *setwakeuptime_user;
3768c2ecf20Sopenharmony_ci	struct efi_setwakeuptime setwakeuptime;
3778c2ecf20Sopenharmony_ci	efi_bool_t enabled;
3788c2ecf20Sopenharmony_ci	efi_status_t status;
3798c2ecf20Sopenharmony_ci	efi_time_t efi_time;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	setwakeuptime_user = (struct efi_setwakeuptime __user *)arg;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (copy_from_user(&setwakeuptime, setwakeuptime_user,
3848c2ecf20Sopenharmony_ci				sizeof(setwakeuptime)))
3858c2ecf20Sopenharmony_ci		return -EFAULT;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	enabled = setwakeuptime.enabled;
3888c2ecf20Sopenharmony_ci	if (setwakeuptime.time) {
3898c2ecf20Sopenharmony_ci		if (copy_from_user(&efi_time, setwakeuptime.time,
3908c2ecf20Sopenharmony_ci					sizeof(efi_time_t)))
3918c2ecf20Sopenharmony_ci			return -EFAULT;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		status = efi.set_wakeup_time(enabled, &efi_time);
3948c2ecf20Sopenharmony_ci	} else
3958c2ecf20Sopenharmony_ci		status = efi.set_wakeup_time(enabled, NULL);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (put_user(status, setwakeuptime.status))
3988c2ecf20Sopenharmony_ci		return -EFAULT;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return status == EFI_SUCCESS ? 0 : -EINVAL;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic long efi_runtime_get_nextvariablename(unsigned long arg)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct efi_getnextvariablename __user *getnextvariablename_user;
4068c2ecf20Sopenharmony_ci	struct efi_getnextvariablename getnextvariablename;
4078c2ecf20Sopenharmony_ci	unsigned long name_size, prev_name_size = 0, *ns = NULL;
4088c2ecf20Sopenharmony_ci	efi_status_t status;
4098c2ecf20Sopenharmony_ci	efi_guid_t *vd = NULL;
4108c2ecf20Sopenharmony_ci	efi_guid_t vendor_guid;
4118c2ecf20Sopenharmony_ci	efi_char16_t *name = NULL;
4128c2ecf20Sopenharmony_ci	int rv = 0;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	getnextvariablename_user = (struct efi_getnextvariablename __user *)arg;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (copy_from_user(&getnextvariablename, getnextvariablename_user,
4178c2ecf20Sopenharmony_ci			   sizeof(getnextvariablename)))
4188c2ecf20Sopenharmony_ci		return -EFAULT;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (getnextvariablename.variable_name_size) {
4218c2ecf20Sopenharmony_ci		if (get_user(name_size, getnextvariablename.variable_name_size))
4228c2ecf20Sopenharmony_ci			return -EFAULT;
4238c2ecf20Sopenharmony_ci		ns = &name_size;
4248c2ecf20Sopenharmony_ci		prev_name_size = name_size;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (getnextvariablename.vendor_guid) {
4288c2ecf20Sopenharmony_ci		if (copy_from_user(&vendor_guid,
4298c2ecf20Sopenharmony_ci				getnextvariablename.vendor_guid,
4308c2ecf20Sopenharmony_ci				sizeof(vendor_guid)))
4318c2ecf20Sopenharmony_ci			return -EFAULT;
4328c2ecf20Sopenharmony_ci		vd = &vendor_guid;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (getnextvariablename.variable_name) {
4368c2ecf20Sopenharmony_ci		size_t name_string_size = 0;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		rv = get_ucs2_strsize_from_user(
4398c2ecf20Sopenharmony_ci				getnextvariablename.variable_name,
4408c2ecf20Sopenharmony_ci				&name_string_size);
4418c2ecf20Sopenharmony_ci		if (rv)
4428c2ecf20Sopenharmony_ci			return rv;
4438c2ecf20Sopenharmony_ci		/*
4448c2ecf20Sopenharmony_ci		 * The name_size may be smaller than the real buffer size where
4458c2ecf20Sopenharmony_ci		 * variable name located in some use cases. The most typical
4468c2ecf20Sopenharmony_ci		 * case is passing a 0 to get the required buffer size for the
4478c2ecf20Sopenharmony_ci		 * 1st time call. So we need to copy the content from user
4488c2ecf20Sopenharmony_ci		 * space for at least the string size of variable name, or else
4498c2ecf20Sopenharmony_ci		 * the name passed to UEFI may not be terminated as we expected.
4508c2ecf20Sopenharmony_ci		 */
4518c2ecf20Sopenharmony_ci		rv = copy_ucs2_from_user_len(&name,
4528c2ecf20Sopenharmony_ci				getnextvariablename.variable_name,
4538c2ecf20Sopenharmony_ci				prev_name_size > name_string_size ?
4548c2ecf20Sopenharmony_ci				prev_name_size : name_string_size);
4558c2ecf20Sopenharmony_ci		if (rv)
4568c2ecf20Sopenharmony_ci			return rv;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	status = efi.get_next_variable(ns, name, vd);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (put_user(status, getnextvariablename.status)) {
4628c2ecf20Sopenharmony_ci		rv = -EFAULT;
4638c2ecf20Sopenharmony_ci		goto out;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS) {
4678c2ecf20Sopenharmony_ci		if (status == EFI_BUFFER_TOO_SMALL) {
4688c2ecf20Sopenharmony_ci			if (ns && put_user(*ns,
4698c2ecf20Sopenharmony_ci				getnextvariablename.variable_name_size)) {
4708c2ecf20Sopenharmony_ci				rv = -EFAULT;
4718c2ecf20Sopenharmony_ci				goto out;
4728c2ecf20Sopenharmony_ci			}
4738c2ecf20Sopenharmony_ci		}
4748c2ecf20Sopenharmony_ci		rv = -EINVAL;
4758c2ecf20Sopenharmony_ci		goto out;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (name) {
4798c2ecf20Sopenharmony_ci		if (copy_ucs2_to_user_len(getnextvariablename.variable_name,
4808c2ecf20Sopenharmony_ci						name, prev_name_size)) {
4818c2ecf20Sopenharmony_ci			rv = -EFAULT;
4828c2ecf20Sopenharmony_ci			goto out;
4838c2ecf20Sopenharmony_ci		}
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (ns) {
4878c2ecf20Sopenharmony_ci		if (put_user(*ns, getnextvariablename.variable_name_size)) {
4888c2ecf20Sopenharmony_ci			rv = -EFAULT;
4898c2ecf20Sopenharmony_ci			goto out;
4908c2ecf20Sopenharmony_ci		}
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	if (vd) {
4948c2ecf20Sopenharmony_ci		if (copy_to_user(getnextvariablename.vendor_guid, vd,
4958c2ecf20Sopenharmony_ci							sizeof(efi_guid_t)))
4968c2ecf20Sopenharmony_ci			rv = -EFAULT;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ciout:
5008c2ecf20Sopenharmony_ci	kfree(name);
5018c2ecf20Sopenharmony_ci	return rv;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic long efi_runtime_get_nexthighmonocount(unsigned long arg)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	struct efi_getnexthighmonotoniccount __user *getnexthighmonocount_user;
5078c2ecf20Sopenharmony_ci	struct efi_getnexthighmonotoniccount getnexthighmonocount;
5088c2ecf20Sopenharmony_ci	efi_status_t status;
5098c2ecf20Sopenharmony_ci	u32 count;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	getnexthighmonocount_user = (struct
5128c2ecf20Sopenharmony_ci			efi_getnexthighmonotoniccount __user *)arg;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (copy_from_user(&getnexthighmonocount,
5158c2ecf20Sopenharmony_ci			   getnexthighmonocount_user,
5168c2ecf20Sopenharmony_ci			   sizeof(getnexthighmonocount)))
5178c2ecf20Sopenharmony_ci		return -EFAULT;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	status = efi.get_next_high_mono_count(
5208c2ecf20Sopenharmony_ci		getnexthighmonocount.high_count ? &count : NULL);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	if (put_user(status, getnexthighmonocount.status))
5238c2ecf20Sopenharmony_ci		return -EFAULT;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS)
5268c2ecf20Sopenharmony_ci		return -EINVAL;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (getnexthighmonocount.high_count &&
5298c2ecf20Sopenharmony_ci	    put_user(count, getnexthighmonocount.high_count))
5308c2ecf20Sopenharmony_ci		return -EFAULT;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	return 0;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic long efi_runtime_reset_system(unsigned long arg)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct efi_resetsystem __user *resetsystem_user;
5388c2ecf20Sopenharmony_ci	struct efi_resetsystem resetsystem;
5398c2ecf20Sopenharmony_ci	void *data = NULL;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	resetsystem_user = (struct efi_resetsystem __user *)arg;
5428c2ecf20Sopenharmony_ci	if (copy_from_user(&resetsystem, resetsystem_user,
5438c2ecf20Sopenharmony_ci						sizeof(resetsystem)))
5448c2ecf20Sopenharmony_ci		return -EFAULT;
5458c2ecf20Sopenharmony_ci	if (resetsystem.data_size != 0) {
5468c2ecf20Sopenharmony_ci		data = memdup_user((void *)resetsystem.data,
5478c2ecf20Sopenharmony_ci						resetsystem.data_size);
5488c2ecf20Sopenharmony_ci		if (IS_ERR(data))
5498c2ecf20Sopenharmony_ci			return PTR_ERR(data);
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	efi.reset_system(resetsystem.reset_type, resetsystem.status,
5538c2ecf20Sopenharmony_ci				resetsystem.data_size, (efi_char16_t *)data);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	kfree(data);
5568c2ecf20Sopenharmony_ci	return 0;
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic long efi_runtime_query_variableinfo(unsigned long arg)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct efi_queryvariableinfo __user *queryvariableinfo_user;
5628c2ecf20Sopenharmony_ci	struct efi_queryvariableinfo queryvariableinfo;
5638c2ecf20Sopenharmony_ci	efi_status_t status;
5648c2ecf20Sopenharmony_ci	u64 max_storage, remaining, max_size;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	queryvariableinfo_user = (struct efi_queryvariableinfo __user *)arg;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (copy_from_user(&queryvariableinfo, queryvariableinfo_user,
5698c2ecf20Sopenharmony_ci			   sizeof(queryvariableinfo)))
5708c2ecf20Sopenharmony_ci		return -EFAULT;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	status = efi.query_variable_info(queryvariableinfo.attributes,
5738c2ecf20Sopenharmony_ci					 &max_storage, &remaining, &max_size);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	if (put_user(status, queryvariableinfo.status))
5768c2ecf20Sopenharmony_ci		return -EFAULT;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS)
5798c2ecf20Sopenharmony_ci		return -EINVAL;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	if (put_user(max_storage,
5828c2ecf20Sopenharmony_ci		     queryvariableinfo.maximum_variable_storage_size))
5838c2ecf20Sopenharmony_ci		return -EFAULT;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (put_user(remaining,
5868c2ecf20Sopenharmony_ci		     queryvariableinfo.remaining_variable_storage_size))
5878c2ecf20Sopenharmony_ci		return -EFAULT;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	if (put_user(max_size, queryvariableinfo.maximum_variable_size))
5908c2ecf20Sopenharmony_ci		return -EFAULT;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	return 0;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cistatic long efi_runtime_query_capsulecaps(unsigned long arg)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	struct efi_querycapsulecapabilities __user *qcaps_user;
5988c2ecf20Sopenharmony_ci	struct efi_querycapsulecapabilities qcaps;
5998c2ecf20Sopenharmony_ci	efi_capsule_header_t *capsules;
6008c2ecf20Sopenharmony_ci	efi_status_t status;
6018c2ecf20Sopenharmony_ci	u64 max_size;
6028c2ecf20Sopenharmony_ci	int i, reset_type;
6038c2ecf20Sopenharmony_ci	int rv = 0;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	qcaps_user = (struct efi_querycapsulecapabilities __user *)arg;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	if (copy_from_user(&qcaps, qcaps_user, sizeof(qcaps)))
6088c2ecf20Sopenharmony_ci		return -EFAULT;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	if (qcaps.capsule_count == ULONG_MAX)
6118c2ecf20Sopenharmony_ci		return -EINVAL;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	capsules = kcalloc(qcaps.capsule_count + 1,
6148c2ecf20Sopenharmony_ci			   sizeof(efi_capsule_header_t), GFP_KERNEL);
6158c2ecf20Sopenharmony_ci	if (!capsules)
6168c2ecf20Sopenharmony_ci		return -ENOMEM;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	for (i = 0; i < qcaps.capsule_count; i++) {
6198c2ecf20Sopenharmony_ci		efi_capsule_header_t *c;
6208c2ecf20Sopenharmony_ci		/*
6218c2ecf20Sopenharmony_ci		 * We cannot dereference qcaps.capsule_header_array directly to
6228c2ecf20Sopenharmony_ci		 * obtain the address of the capsule as it resides in the
6238c2ecf20Sopenharmony_ci		 * user space
6248c2ecf20Sopenharmony_ci		 */
6258c2ecf20Sopenharmony_ci		if (get_user(c, qcaps.capsule_header_array + i)) {
6268c2ecf20Sopenharmony_ci			rv = -EFAULT;
6278c2ecf20Sopenharmony_ci			goto out;
6288c2ecf20Sopenharmony_ci		}
6298c2ecf20Sopenharmony_ci		if (copy_from_user(&capsules[i], c,
6308c2ecf20Sopenharmony_ci				sizeof(efi_capsule_header_t))) {
6318c2ecf20Sopenharmony_ci			rv = -EFAULT;
6328c2ecf20Sopenharmony_ci			goto out;
6338c2ecf20Sopenharmony_ci		}
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	qcaps.capsule_header_array = &capsules;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	status = efi.query_capsule_caps((efi_capsule_header_t **)
6398c2ecf20Sopenharmony_ci					qcaps.capsule_header_array,
6408c2ecf20Sopenharmony_ci					qcaps.capsule_count,
6418c2ecf20Sopenharmony_ci					&max_size, &reset_type);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	if (put_user(status, qcaps.status)) {
6448c2ecf20Sopenharmony_ci		rv = -EFAULT;
6458c2ecf20Sopenharmony_ci		goto out;
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	if (status != EFI_SUCCESS) {
6498c2ecf20Sopenharmony_ci		rv = -EINVAL;
6508c2ecf20Sopenharmony_ci		goto out;
6518c2ecf20Sopenharmony_ci	}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (put_user(max_size, qcaps.maximum_capsule_size)) {
6548c2ecf20Sopenharmony_ci		rv = -EFAULT;
6558c2ecf20Sopenharmony_ci		goto out;
6568c2ecf20Sopenharmony_ci	}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	if (put_user(reset_type, qcaps.reset_type))
6598c2ecf20Sopenharmony_ci		rv = -EFAULT;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ciout:
6628c2ecf20Sopenharmony_ci	kfree(capsules);
6638c2ecf20Sopenharmony_ci	return rv;
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_cistatic long efi_test_ioctl(struct file *file, unsigned int cmd,
6678c2ecf20Sopenharmony_ci							unsigned long arg)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	switch (cmd) {
6708c2ecf20Sopenharmony_ci	case EFI_RUNTIME_GET_VARIABLE:
6718c2ecf20Sopenharmony_ci		return efi_runtime_get_variable(arg);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	case EFI_RUNTIME_SET_VARIABLE:
6748c2ecf20Sopenharmony_ci		return efi_runtime_set_variable(arg);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	case EFI_RUNTIME_GET_TIME:
6778c2ecf20Sopenharmony_ci		return efi_runtime_get_time(arg);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	case EFI_RUNTIME_SET_TIME:
6808c2ecf20Sopenharmony_ci		return efi_runtime_set_time(arg);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	case EFI_RUNTIME_GET_WAKETIME:
6838c2ecf20Sopenharmony_ci		return efi_runtime_get_waketime(arg);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	case EFI_RUNTIME_SET_WAKETIME:
6868c2ecf20Sopenharmony_ci		return efi_runtime_set_waketime(arg);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	case EFI_RUNTIME_GET_NEXTVARIABLENAME:
6898c2ecf20Sopenharmony_ci		return efi_runtime_get_nextvariablename(arg);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	case EFI_RUNTIME_GET_NEXTHIGHMONOTONICCOUNT:
6928c2ecf20Sopenharmony_ci		return efi_runtime_get_nexthighmonocount(arg);
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	case EFI_RUNTIME_QUERY_VARIABLEINFO:
6958c2ecf20Sopenharmony_ci		return efi_runtime_query_variableinfo(arg);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	case EFI_RUNTIME_QUERY_CAPSULECAPABILITIES:
6988c2ecf20Sopenharmony_ci		return efi_runtime_query_capsulecaps(arg);
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	case EFI_RUNTIME_RESET_SYSTEM:
7018c2ecf20Sopenharmony_ci		return efi_runtime_reset_system(arg);
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	return -ENOTTY;
7058c2ecf20Sopenharmony_ci}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_cistatic int efi_test_open(struct inode *inode, struct file *file)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	int ret = security_locked_down(LOCKDOWN_EFI_TEST);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (ret)
7128c2ecf20Sopenharmony_ci		return ret;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
7158c2ecf20Sopenharmony_ci		return -EACCES;
7168c2ecf20Sopenharmony_ci	/*
7178c2ecf20Sopenharmony_ci	 * nothing special to do here
7188c2ecf20Sopenharmony_ci	 * We do accept multiple open files at the same time as we
7198c2ecf20Sopenharmony_ci	 * synchronize on the per call operation.
7208c2ecf20Sopenharmony_ci	 */
7218c2ecf20Sopenharmony_ci	return 0;
7228c2ecf20Sopenharmony_ci}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_cistatic int efi_test_close(struct inode *inode, struct file *file)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	return 0;
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci/*
7308c2ecf20Sopenharmony_ci *	The various file operations we support.
7318c2ecf20Sopenharmony_ci */
7328c2ecf20Sopenharmony_cistatic const struct file_operations efi_test_fops = {
7338c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
7348c2ecf20Sopenharmony_ci	.unlocked_ioctl	= efi_test_ioctl,
7358c2ecf20Sopenharmony_ci	.open		= efi_test_open,
7368c2ecf20Sopenharmony_ci	.release	= efi_test_close,
7378c2ecf20Sopenharmony_ci	.llseek		= no_llseek,
7388c2ecf20Sopenharmony_ci};
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic struct miscdevice efi_test_dev = {
7418c2ecf20Sopenharmony_ci	MISC_DYNAMIC_MINOR,
7428c2ecf20Sopenharmony_ci	"efi_test",
7438c2ecf20Sopenharmony_ci	&efi_test_fops
7448c2ecf20Sopenharmony_ci};
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_cistatic int __init efi_test_init(void)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	int ret;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	ret = misc_register(&efi_test_dev);
7518c2ecf20Sopenharmony_ci	if (ret) {
7528c2ecf20Sopenharmony_ci		pr_err("efi_test: can't misc_register on minor=%d\n",
7538c2ecf20Sopenharmony_ci			MISC_DYNAMIC_MINOR);
7548c2ecf20Sopenharmony_ci		return ret;
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	return 0;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic void __exit efi_test_exit(void)
7618c2ecf20Sopenharmony_ci{
7628c2ecf20Sopenharmony_ci	misc_deregister(&efi_test_dev);
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cimodule_init(efi_test_init);
7668c2ecf20Sopenharmony_cimodule_exit(efi_test_exit);
767