162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * EFI Test Driver for Runtime Services
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright(C) 2012-2016 Canonical Ltd.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This driver exports EFI runtime services interfaces into userspace, which
862306a36Sopenharmony_ci * allow to use and test UEFI runtime services provided by firmware.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/miscdevice.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/proc_fs.h>
1662306a36Sopenharmony_ci#include <linux/efi.h>
1762306a36Sopenharmony_ci#include <linux/security.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/uaccess.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "efi_test.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ciMODULE_AUTHOR("Ivan Hu <ivan.hu@canonical.com>");
2462306a36Sopenharmony_ciMODULE_DESCRIPTION("EFI Test Driver");
2562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/*
2862306a36Sopenharmony_ci * Count the bytes in 'str', including the terminating NULL.
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * Note this function returns the number of *bytes*, not the number of
3162306a36Sopenharmony_ci * ucs2 characters.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_cistatic inline size_t user_ucs2_strsize(efi_char16_t  __user *str)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	efi_char16_t *s = str, c;
3662306a36Sopenharmony_ci	size_t len;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (!str)
3962306a36Sopenharmony_ci		return 0;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/* Include terminating NULL */
4262306a36Sopenharmony_ci	len = sizeof(efi_char16_t);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (get_user(c, s++)) {
4562306a36Sopenharmony_ci		/* Can't read userspace memory for size */
4662306a36Sopenharmony_ci		return 0;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	while (c != 0) {
5062306a36Sopenharmony_ci		if (get_user(c, s++)) {
5162306a36Sopenharmony_ci			/* Can't read userspace memory for size */
5262306a36Sopenharmony_ci			return 0;
5362306a36Sopenharmony_ci		}
5462306a36Sopenharmony_ci		len += sizeof(efi_char16_t);
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci	return len;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/*
6062306a36Sopenharmony_ci * Allocate a buffer and copy a ucs2 string from user space into it.
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_cistatic inline int
6362306a36Sopenharmony_cicopy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src,
6462306a36Sopenharmony_ci			size_t len)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	efi_char16_t *buf;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (!src) {
6962306a36Sopenharmony_ci		*dst = NULL;
7062306a36Sopenharmony_ci		return 0;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	buf = memdup_user(src, len);
7462306a36Sopenharmony_ci	if (IS_ERR(buf)) {
7562306a36Sopenharmony_ci		*dst = NULL;
7662306a36Sopenharmony_ci		return PTR_ERR(buf);
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci	*dst = buf;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * Count the bytes in 'str', including the terminating NULL.
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci * Just a wrap for user_ucs2_strsize
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_cistatic inline int
8962306a36Sopenharmony_ciget_ucs2_strsize_from_user(efi_char16_t __user *src, size_t *len)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	*len = user_ucs2_strsize(src);
9262306a36Sopenharmony_ci	if (*len == 0)
9362306a36Sopenharmony_ci		return -EFAULT;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/*
9962306a36Sopenharmony_ci * Calculate the required buffer allocation size and copy a ucs2 string
10062306a36Sopenharmony_ci * from user space into it.
10162306a36Sopenharmony_ci *
10262306a36Sopenharmony_ci * This function differs from copy_ucs2_from_user_len() because it
10362306a36Sopenharmony_ci * calculates the size of the buffer to allocate by taking the length of
10462306a36Sopenharmony_ci * the string 'src'.
10562306a36Sopenharmony_ci *
10662306a36Sopenharmony_ci * If a non-zero value is returned, the caller MUST NOT access 'dst'.
10762306a36Sopenharmony_ci *
10862306a36Sopenharmony_ci * It is the caller's responsibility to free 'dst'.
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_cistatic inline int
11162306a36Sopenharmony_cicopy_ucs2_from_user(efi_char16_t **dst, efi_char16_t __user *src)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	size_t len;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	len = user_ucs2_strsize(src);
11662306a36Sopenharmony_ci	if (len == 0)
11762306a36Sopenharmony_ci		return -EFAULT;
11862306a36Sopenharmony_ci	return copy_ucs2_from_user_len(dst, src, len);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/*
12262306a36Sopenharmony_ci * Copy a ucs2 string to a user buffer.
12362306a36Sopenharmony_ci *
12462306a36Sopenharmony_ci * This function is a simple wrapper around copy_to_user() that does
12562306a36Sopenharmony_ci * nothing if 'src' is NULL, which is useful for reducing the amount of
12662306a36Sopenharmony_ci * NULL checking the caller has to do.
12762306a36Sopenharmony_ci *
12862306a36Sopenharmony_ci * 'len' specifies the number of bytes to copy.
12962306a36Sopenharmony_ci */
13062306a36Sopenharmony_cistatic inline int
13162306a36Sopenharmony_cicopy_ucs2_to_user_len(efi_char16_t __user *dst, efi_char16_t *src, size_t len)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	if (!src)
13462306a36Sopenharmony_ci		return 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return copy_to_user(dst, src, len);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic long efi_runtime_get_variable(unsigned long arg)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct efi_getvariable __user *getvariable_user;
14262306a36Sopenharmony_ci	struct efi_getvariable getvariable;
14362306a36Sopenharmony_ci	unsigned long datasize = 0, prev_datasize, *dz;
14462306a36Sopenharmony_ci	efi_guid_t vendor_guid, *vd = NULL;
14562306a36Sopenharmony_ci	efi_status_t status;
14662306a36Sopenharmony_ci	efi_char16_t *name = NULL;
14762306a36Sopenharmony_ci	u32 attr, *at;
14862306a36Sopenharmony_ci	void *data = NULL;
14962306a36Sopenharmony_ci	int rv = 0;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	getvariable_user = (struct efi_getvariable __user *)arg;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (copy_from_user(&getvariable, getvariable_user,
15462306a36Sopenharmony_ci			   sizeof(getvariable)))
15562306a36Sopenharmony_ci		return -EFAULT;
15662306a36Sopenharmony_ci	if (getvariable.data_size &&
15762306a36Sopenharmony_ci	    get_user(datasize, getvariable.data_size))
15862306a36Sopenharmony_ci		return -EFAULT;
15962306a36Sopenharmony_ci	if (getvariable.vendor_guid) {
16062306a36Sopenharmony_ci		if (copy_from_user(&vendor_guid, getvariable.vendor_guid,
16162306a36Sopenharmony_ci					sizeof(vendor_guid)))
16262306a36Sopenharmony_ci			return -EFAULT;
16362306a36Sopenharmony_ci		vd = &vendor_guid;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (getvariable.variable_name) {
16762306a36Sopenharmony_ci		rv = copy_ucs2_from_user(&name, getvariable.variable_name);
16862306a36Sopenharmony_ci		if (rv)
16962306a36Sopenharmony_ci			return rv;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	at = getvariable.attributes ? &attr : NULL;
17362306a36Sopenharmony_ci	dz = getvariable.data_size ? &datasize : NULL;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (getvariable.data_size && getvariable.data) {
17662306a36Sopenharmony_ci		data = kmalloc(datasize, GFP_KERNEL);
17762306a36Sopenharmony_ci		if (!data) {
17862306a36Sopenharmony_ci			kfree(name);
17962306a36Sopenharmony_ci			return -ENOMEM;
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	prev_datasize = datasize;
18462306a36Sopenharmony_ci	status = efi.get_variable(name, vd, at, dz, data);
18562306a36Sopenharmony_ci	kfree(name);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (put_user(status, getvariable.status)) {
18862306a36Sopenharmony_ci		rv = -EFAULT;
18962306a36Sopenharmony_ci		goto out;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (status != EFI_SUCCESS) {
19362306a36Sopenharmony_ci		if (status == EFI_BUFFER_TOO_SMALL) {
19462306a36Sopenharmony_ci			if (dz && put_user(datasize, getvariable.data_size)) {
19562306a36Sopenharmony_ci				rv = -EFAULT;
19662306a36Sopenharmony_ci				goto out;
19762306a36Sopenharmony_ci			}
19862306a36Sopenharmony_ci		}
19962306a36Sopenharmony_ci		rv = -EINVAL;
20062306a36Sopenharmony_ci		goto out;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (prev_datasize < datasize) {
20462306a36Sopenharmony_ci		rv = -EINVAL;
20562306a36Sopenharmony_ci		goto out;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (data) {
20962306a36Sopenharmony_ci		if (copy_to_user(getvariable.data, data, datasize)) {
21062306a36Sopenharmony_ci			rv = -EFAULT;
21162306a36Sopenharmony_ci			goto out;
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (at && put_user(attr, getvariable.attributes)) {
21662306a36Sopenharmony_ci		rv = -EFAULT;
21762306a36Sopenharmony_ci		goto out;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (dz && put_user(datasize, getvariable.data_size))
22162306a36Sopenharmony_ci		rv = -EFAULT;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ciout:
22462306a36Sopenharmony_ci	kfree(data);
22562306a36Sopenharmony_ci	return rv;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic long efi_runtime_set_variable(unsigned long arg)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct efi_setvariable __user *setvariable_user;
23262306a36Sopenharmony_ci	struct efi_setvariable setvariable;
23362306a36Sopenharmony_ci	efi_guid_t vendor_guid;
23462306a36Sopenharmony_ci	efi_status_t status;
23562306a36Sopenharmony_ci	efi_char16_t *name = NULL;
23662306a36Sopenharmony_ci	void *data;
23762306a36Sopenharmony_ci	int rv = 0;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	setvariable_user = (struct efi_setvariable __user *)arg;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (copy_from_user(&setvariable, setvariable_user, sizeof(setvariable)))
24262306a36Sopenharmony_ci		return -EFAULT;
24362306a36Sopenharmony_ci	if (copy_from_user(&vendor_guid, setvariable.vendor_guid,
24462306a36Sopenharmony_ci				sizeof(vendor_guid)))
24562306a36Sopenharmony_ci		return -EFAULT;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (setvariable.variable_name) {
24862306a36Sopenharmony_ci		rv = copy_ucs2_from_user(&name, setvariable.variable_name);
24962306a36Sopenharmony_ci		if (rv)
25062306a36Sopenharmony_ci			return rv;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	data = memdup_user(setvariable.data, setvariable.data_size);
25462306a36Sopenharmony_ci	if (IS_ERR(data)) {
25562306a36Sopenharmony_ci		kfree(name);
25662306a36Sopenharmony_ci		return PTR_ERR(data);
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	status = efi.set_variable(name, &vendor_guid,
26062306a36Sopenharmony_ci				setvariable.attributes,
26162306a36Sopenharmony_ci				setvariable.data_size, data);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (put_user(status, setvariable.status)) {
26462306a36Sopenharmony_ci		rv = -EFAULT;
26562306a36Sopenharmony_ci		goto out;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	rv = status == EFI_SUCCESS ? 0 : -EINVAL;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ciout:
27162306a36Sopenharmony_ci	kfree(data);
27262306a36Sopenharmony_ci	kfree(name);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return rv;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic long efi_runtime_get_time(unsigned long arg)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct efi_gettime __user *gettime_user;
28062306a36Sopenharmony_ci	struct efi_gettime  gettime;
28162306a36Sopenharmony_ci	efi_status_t status;
28262306a36Sopenharmony_ci	efi_time_cap_t cap;
28362306a36Sopenharmony_ci	efi_time_t efi_time;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	gettime_user = (struct efi_gettime __user *)arg;
28662306a36Sopenharmony_ci	if (copy_from_user(&gettime, gettime_user, sizeof(gettime)))
28762306a36Sopenharmony_ci		return -EFAULT;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	status = efi.get_time(gettime.time ? &efi_time : NULL,
29062306a36Sopenharmony_ci			      gettime.capabilities ? &cap : NULL);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (put_user(status, gettime.status))
29362306a36Sopenharmony_ci		return -EFAULT;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (status != EFI_SUCCESS)
29662306a36Sopenharmony_ci		return -EINVAL;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (gettime.capabilities) {
29962306a36Sopenharmony_ci		efi_time_cap_t __user *cap_local;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		cap_local = (efi_time_cap_t *)gettime.capabilities;
30262306a36Sopenharmony_ci		if (put_user(cap.resolution, &(cap_local->resolution)) ||
30362306a36Sopenharmony_ci			put_user(cap.accuracy, &(cap_local->accuracy)) ||
30462306a36Sopenharmony_ci			put_user(cap.sets_to_zero, &(cap_local->sets_to_zero)))
30562306a36Sopenharmony_ci			return -EFAULT;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci	if (gettime.time) {
30862306a36Sopenharmony_ci		if (copy_to_user(gettime.time, &efi_time, sizeof(efi_time_t)))
30962306a36Sopenharmony_ci			return -EFAULT;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic long efi_runtime_set_time(unsigned long arg)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct efi_settime __user *settime_user;
31862306a36Sopenharmony_ci	struct efi_settime settime;
31962306a36Sopenharmony_ci	efi_status_t status;
32062306a36Sopenharmony_ci	efi_time_t efi_time;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	settime_user = (struct efi_settime __user *)arg;
32362306a36Sopenharmony_ci	if (copy_from_user(&settime, settime_user, sizeof(settime)))
32462306a36Sopenharmony_ci		return -EFAULT;
32562306a36Sopenharmony_ci	if (copy_from_user(&efi_time, settime.time,
32662306a36Sopenharmony_ci					sizeof(efi_time_t)))
32762306a36Sopenharmony_ci		return -EFAULT;
32862306a36Sopenharmony_ci	status = efi.set_time(&efi_time);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (put_user(status, settime.status))
33162306a36Sopenharmony_ci		return -EFAULT;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return status == EFI_SUCCESS ? 0 : -EINVAL;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic long efi_runtime_get_waketime(unsigned long arg)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct efi_getwakeuptime __user *getwakeuptime_user;
33962306a36Sopenharmony_ci	struct efi_getwakeuptime getwakeuptime;
34062306a36Sopenharmony_ci	efi_bool_t enabled, pending;
34162306a36Sopenharmony_ci	efi_status_t status;
34262306a36Sopenharmony_ci	efi_time_t efi_time;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	getwakeuptime_user = (struct efi_getwakeuptime __user *)arg;
34562306a36Sopenharmony_ci	if (copy_from_user(&getwakeuptime, getwakeuptime_user,
34662306a36Sopenharmony_ci				sizeof(getwakeuptime)))
34762306a36Sopenharmony_ci		return -EFAULT;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	status = efi.get_wakeup_time(
35062306a36Sopenharmony_ci		getwakeuptime.enabled ? (efi_bool_t *)&enabled : NULL,
35162306a36Sopenharmony_ci		getwakeuptime.pending ? (efi_bool_t *)&pending : NULL,
35262306a36Sopenharmony_ci		getwakeuptime.time ? &efi_time : NULL);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (put_user(status, getwakeuptime.status))
35562306a36Sopenharmony_ci		return -EFAULT;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (status != EFI_SUCCESS)
35862306a36Sopenharmony_ci		return -EINVAL;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (getwakeuptime.enabled && put_user(enabled,
36162306a36Sopenharmony_ci						getwakeuptime.enabled))
36262306a36Sopenharmony_ci		return -EFAULT;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (getwakeuptime.time) {
36562306a36Sopenharmony_ci		if (copy_to_user(getwakeuptime.time, &efi_time,
36662306a36Sopenharmony_ci				sizeof(efi_time_t)))
36762306a36Sopenharmony_ci			return -EFAULT;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	return 0;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic long efi_runtime_set_waketime(unsigned long arg)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct efi_setwakeuptime __user *setwakeuptime_user;
37662306a36Sopenharmony_ci	struct efi_setwakeuptime setwakeuptime;
37762306a36Sopenharmony_ci	efi_bool_t enabled;
37862306a36Sopenharmony_ci	efi_status_t status;
37962306a36Sopenharmony_ci	efi_time_t efi_time;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	setwakeuptime_user = (struct efi_setwakeuptime __user *)arg;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (copy_from_user(&setwakeuptime, setwakeuptime_user,
38462306a36Sopenharmony_ci				sizeof(setwakeuptime)))
38562306a36Sopenharmony_ci		return -EFAULT;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	enabled = setwakeuptime.enabled;
38862306a36Sopenharmony_ci	if (setwakeuptime.time) {
38962306a36Sopenharmony_ci		if (copy_from_user(&efi_time, setwakeuptime.time,
39062306a36Sopenharmony_ci					sizeof(efi_time_t)))
39162306a36Sopenharmony_ci			return -EFAULT;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		status = efi.set_wakeup_time(enabled, &efi_time);
39462306a36Sopenharmony_ci	} else
39562306a36Sopenharmony_ci		status = efi.set_wakeup_time(enabled, NULL);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (put_user(status, setwakeuptime.status))
39862306a36Sopenharmony_ci		return -EFAULT;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	return status == EFI_SUCCESS ? 0 : -EINVAL;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic long efi_runtime_get_nextvariablename(unsigned long arg)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	struct efi_getnextvariablename __user *getnextvariablename_user;
40662306a36Sopenharmony_ci	struct efi_getnextvariablename getnextvariablename;
40762306a36Sopenharmony_ci	unsigned long name_size, prev_name_size = 0, *ns = NULL;
40862306a36Sopenharmony_ci	efi_status_t status;
40962306a36Sopenharmony_ci	efi_guid_t *vd = NULL;
41062306a36Sopenharmony_ci	efi_guid_t vendor_guid;
41162306a36Sopenharmony_ci	efi_char16_t *name = NULL;
41262306a36Sopenharmony_ci	int rv = 0;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	getnextvariablename_user = (struct efi_getnextvariablename __user *)arg;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	if (copy_from_user(&getnextvariablename, getnextvariablename_user,
41762306a36Sopenharmony_ci			   sizeof(getnextvariablename)))
41862306a36Sopenharmony_ci		return -EFAULT;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (getnextvariablename.variable_name_size) {
42162306a36Sopenharmony_ci		if (get_user(name_size, getnextvariablename.variable_name_size))
42262306a36Sopenharmony_ci			return -EFAULT;
42362306a36Sopenharmony_ci		ns = &name_size;
42462306a36Sopenharmony_ci		prev_name_size = name_size;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (getnextvariablename.vendor_guid) {
42862306a36Sopenharmony_ci		if (copy_from_user(&vendor_guid,
42962306a36Sopenharmony_ci				getnextvariablename.vendor_guid,
43062306a36Sopenharmony_ci				sizeof(vendor_guid)))
43162306a36Sopenharmony_ci			return -EFAULT;
43262306a36Sopenharmony_ci		vd = &vendor_guid;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (getnextvariablename.variable_name) {
43662306a36Sopenharmony_ci		size_t name_string_size = 0;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		rv = get_ucs2_strsize_from_user(
43962306a36Sopenharmony_ci				getnextvariablename.variable_name,
44062306a36Sopenharmony_ci				&name_string_size);
44162306a36Sopenharmony_ci		if (rv)
44262306a36Sopenharmony_ci			return rv;
44362306a36Sopenharmony_ci		/*
44462306a36Sopenharmony_ci		 * The name_size may be smaller than the real buffer size where
44562306a36Sopenharmony_ci		 * variable name located in some use cases. The most typical
44662306a36Sopenharmony_ci		 * case is passing a 0 to get the required buffer size for the
44762306a36Sopenharmony_ci		 * 1st time call. So we need to copy the content from user
44862306a36Sopenharmony_ci		 * space for at least the string size of variable name, or else
44962306a36Sopenharmony_ci		 * the name passed to UEFI may not be terminated as we expected.
45062306a36Sopenharmony_ci		 */
45162306a36Sopenharmony_ci		rv = copy_ucs2_from_user_len(&name,
45262306a36Sopenharmony_ci				getnextvariablename.variable_name,
45362306a36Sopenharmony_ci				prev_name_size > name_string_size ?
45462306a36Sopenharmony_ci				prev_name_size : name_string_size);
45562306a36Sopenharmony_ci		if (rv)
45662306a36Sopenharmony_ci			return rv;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	status = efi.get_next_variable(ns, name, vd);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (put_user(status, getnextvariablename.status)) {
46262306a36Sopenharmony_ci		rv = -EFAULT;
46362306a36Sopenharmony_ci		goto out;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (status != EFI_SUCCESS) {
46762306a36Sopenharmony_ci		if (status == EFI_BUFFER_TOO_SMALL) {
46862306a36Sopenharmony_ci			if (ns && put_user(*ns,
46962306a36Sopenharmony_ci				getnextvariablename.variable_name_size)) {
47062306a36Sopenharmony_ci				rv = -EFAULT;
47162306a36Sopenharmony_ci				goto out;
47262306a36Sopenharmony_ci			}
47362306a36Sopenharmony_ci		}
47462306a36Sopenharmony_ci		rv = -EINVAL;
47562306a36Sopenharmony_ci		goto out;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	if (name) {
47962306a36Sopenharmony_ci		if (copy_ucs2_to_user_len(getnextvariablename.variable_name,
48062306a36Sopenharmony_ci						name, prev_name_size)) {
48162306a36Sopenharmony_ci			rv = -EFAULT;
48262306a36Sopenharmony_ci			goto out;
48362306a36Sopenharmony_ci		}
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (ns) {
48762306a36Sopenharmony_ci		if (put_user(*ns, getnextvariablename.variable_name_size)) {
48862306a36Sopenharmony_ci			rv = -EFAULT;
48962306a36Sopenharmony_ci			goto out;
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (vd) {
49462306a36Sopenharmony_ci		if (copy_to_user(getnextvariablename.vendor_guid, vd,
49562306a36Sopenharmony_ci							sizeof(efi_guid_t)))
49662306a36Sopenharmony_ci			rv = -EFAULT;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ciout:
50062306a36Sopenharmony_ci	kfree(name);
50162306a36Sopenharmony_ci	return rv;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic long efi_runtime_get_nexthighmonocount(unsigned long arg)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct efi_getnexthighmonotoniccount __user *getnexthighmonocount_user;
50762306a36Sopenharmony_ci	struct efi_getnexthighmonotoniccount getnexthighmonocount;
50862306a36Sopenharmony_ci	efi_status_t status;
50962306a36Sopenharmony_ci	u32 count;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	getnexthighmonocount_user = (struct
51262306a36Sopenharmony_ci			efi_getnexthighmonotoniccount __user *)arg;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (copy_from_user(&getnexthighmonocount,
51562306a36Sopenharmony_ci			   getnexthighmonocount_user,
51662306a36Sopenharmony_ci			   sizeof(getnexthighmonocount)))
51762306a36Sopenharmony_ci		return -EFAULT;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	status = efi.get_next_high_mono_count(
52062306a36Sopenharmony_ci		getnexthighmonocount.high_count ? &count : NULL);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (put_user(status, getnexthighmonocount.status))
52362306a36Sopenharmony_ci		return -EFAULT;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (status != EFI_SUCCESS)
52662306a36Sopenharmony_ci		return -EINVAL;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (getnexthighmonocount.high_count &&
52962306a36Sopenharmony_ci	    put_user(count, getnexthighmonocount.high_count))
53062306a36Sopenharmony_ci		return -EFAULT;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	return 0;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic long efi_runtime_reset_system(unsigned long arg)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct efi_resetsystem __user *resetsystem_user;
53862306a36Sopenharmony_ci	struct efi_resetsystem resetsystem;
53962306a36Sopenharmony_ci	void *data = NULL;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	resetsystem_user = (struct efi_resetsystem __user *)arg;
54262306a36Sopenharmony_ci	if (copy_from_user(&resetsystem, resetsystem_user,
54362306a36Sopenharmony_ci						sizeof(resetsystem)))
54462306a36Sopenharmony_ci		return -EFAULT;
54562306a36Sopenharmony_ci	if (resetsystem.data_size != 0) {
54662306a36Sopenharmony_ci		data = memdup_user((void *)resetsystem.data,
54762306a36Sopenharmony_ci						resetsystem.data_size);
54862306a36Sopenharmony_ci		if (IS_ERR(data))
54962306a36Sopenharmony_ci			return PTR_ERR(data);
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	efi.reset_system(resetsystem.reset_type, resetsystem.status,
55362306a36Sopenharmony_ci				resetsystem.data_size, (efi_char16_t *)data);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	kfree(data);
55662306a36Sopenharmony_ci	return 0;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic long efi_runtime_query_variableinfo(unsigned long arg)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct efi_queryvariableinfo __user *queryvariableinfo_user;
56262306a36Sopenharmony_ci	struct efi_queryvariableinfo queryvariableinfo;
56362306a36Sopenharmony_ci	efi_status_t status;
56462306a36Sopenharmony_ci	u64 max_storage, remaining, max_size;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	queryvariableinfo_user = (struct efi_queryvariableinfo __user *)arg;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (copy_from_user(&queryvariableinfo, queryvariableinfo_user,
56962306a36Sopenharmony_ci			   sizeof(queryvariableinfo)))
57062306a36Sopenharmony_ci		return -EFAULT;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	status = efi.query_variable_info(queryvariableinfo.attributes,
57362306a36Sopenharmony_ci					 &max_storage, &remaining, &max_size);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	if (put_user(status, queryvariableinfo.status))
57662306a36Sopenharmony_ci		return -EFAULT;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	if (status != EFI_SUCCESS)
57962306a36Sopenharmony_ci		return -EINVAL;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (put_user(max_storage,
58262306a36Sopenharmony_ci		     queryvariableinfo.maximum_variable_storage_size))
58362306a36Sopenharmony_ci		return -EFAULT;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (put_user(remaining,
58662306a36Sopenharmony_ci		     queryvariableinfo.remaining_variable_storage_size))
58762306a36Sopenharmony_ci		return -EFAULT;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (put_user(max_size, queryvariableinfo.maximum_variable_size))
59062306a36Sopenharmony_ci		return -EFAULT;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	return 0;
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic long efi_runtime_query_capsulecaps(unsigned long arg)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	struct efi_querycapsulecapabilities __user *qcaps_user;
59862306a36Sopenharmony_ci	struct efi_querycapsulecapabilities qcaps;
59962306a36Sopenharmony_ci	efi_capsule_header_t *capsules;
60062306a36Sopenharmony_ci	efi_status_t status;
60162306a36Sopenharmony_ci	u64 max_size;
60262306a36Sopenharmony_ci	int i, reset_type;
60362306a36Sopenharmony_ci	int rv = 0;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	qcaps_user = (struct efi_querycapsulecapabilities __user *)arg;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	if (copy_from_user(&qcaps, qcaps_user, sizeof(qcaps)))
60862306a36Sopenharmony_ci		return -EFAULT;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (qcaps.capsule_count == ULONG_MAX)
61162306a36Sopenharmony_ci		return -EINVAL;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	capsules = kcalloc(qcaps.capsule_count + 1,
61462306a36Sopenharmony_ci			   sizeof(efi_capsule_header_t), GFP_KERNEL);
61562306a36Sopenharmony_ci	if (!capsules)
61662306a36Sopenharmony_ci		return -ENOMEM;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	for (i = 0; i < qcaps.capsule_count; i++) {
61962306a36Sopenharmony_ci		efi_capsule_header_t *c;
62062306a36Sopenharmony_ci		/*
62162306a36Sopenharmony_ci		 * We cannot dereference qcaps.capsule_header_array directly to
62262306a36Sopenharmony_ci		 * obtain the address of the capsule as it resides in the
62362306a36Sopenharmony_ci		 * user space
62462306a36Sopenharmony_ci		 */
62562306a36Sopenharmony_ci		if (get_user(c, qcaps.capsule_header_array + i)) {
62662306a36Sopenharmony_ci			rv = -EFAULT;
62762306a36Sopenharmony_ci			goto out;
62862306a36Sopenharmony_ci		}
62962306a36Sopenharmony_ci		if (copy_from_user(&capsules[i], c,
63062306a36Sopenharmony_ci				sizeof(efi_capsule_header_t))) {
63162306a36Sopenharmony_ci			rv = -EFAULT;
63262306a36Sopenharmony_ci			goto out;
63362306a36Sopenharmony_ci		}
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	qcaps.capsule_header_array = &capsules;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	status = efi.query_capsule_caps((efi_capsule_header_t **)
63962306a36Sopenharmony_ci					qcaps.capsule_header_array,
64062306a36Sopenharmony_ci					qcaps.capsule_count,
64162306a36Sopenharmony_ci					&max_size, &reset_type);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	if (put_user(status, qcaps.status)) {
64462306a36Sopenharmony_ci		rv = -EFAULT;
64562306a36Sopenharmony_ci		goto out;
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (status != EFI_SUCCESS) {
64962306a36Sopenharmony_ci		rv = -EINVAL;
65062306a36Sopenharmony_ci		goto out;
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (put_user(max_size, qcaps.maximum_capsule_size)) {
65462306a36Sopenharmony_ci		rv = -EFAULT;
65562306a36Sopenharmony_ci		goto out;
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if (put_user(reset_type, qcaps.reset_type))
65962306a36Sopenharmony_ci		rv = -EFAULT;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ciout:
66262306a36Sopenharmony_ci	kfree(capsules);
66362306a36Sopenharmony_ci	return rv;
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic long efi_runtime_get_supported_mask(unsigned long arg)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	unsigned int __user *supported_mask;
66962306a36Sopenharmony_ci	int rv = 0;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	supported_mask = (unsigned int *)arg;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (put_user(efi.runtime_supported_mask, supported_mask))
67462306a36Sopenharmony_ci		rv = -EFAULT;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	return rv;
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_cistatic long efi_test_ioctl(struct file *file, unsigned int cmd,
68062306a36Sopenharmony_ci							unsigned long arg)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	switch (cmd) {
68362306a36Sopenharmony_ci	case EFI_RUNTIME_GET_VARIABLE:
68462306a36Sopenharmony_ci		return efi_runtime_get_variable(arg);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	case EFI_RUNTIME_SET_VARIABLE:
68762306a36Sopenharmony_ci		return efi_runtime_set_variable(arg);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	case EFI_RUNTIME_GET_TIME:
69062306a36Sopenharmony_ci		return efi_runtime_get_time(arg);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	case EFI_RUNTIME_SET_TIME:
69362306a36Sopenharmony_ci		return efi_runtime_set_time(arg);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	case EFI_RUNTIME_GET_WAKETIME:
69662306a36Sopenharmony_ci		return efi_runtime_get_waketime(arg);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	case EFI_RUNTIME_SET_WAKETIME:
69962306a36Sopenharmony_ci		return efi_runtime_set_waketime(arg);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	case EFI_RUNTIME_GET_NEXTVARIABLENAME:
70262306a36Sopenharmony_ci		return efi_runtime_get_nextvariablename(arg);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	case EFI_RUNTIME_GET_NEXTHIGHMONOTONICCOUNT:
70562306a36Sopenharmony_ci		return efi_runtime_get_nexthighmonocount(arg);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	case EFI_RUNTIME_QUERY_VARIABLEINFO:
70862306a36Sopenharmony_ci		return efi_runtime_query_variableinfo(arg);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	case EFI_RUNTIME_QUERY_CAPSULECAPABILITIES:
71162306a36Sopenharmony_ci		return efi_runtime_query_capsulecaps(arg);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	case EFI_RUNTIME_RESET_SYSTEM:
71462306a36Sopenharmony_ci		return efi_runtime_reset_system(arg);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	case EFI_RUNTIME_GET_SUPPORTED_MASK:
71762306a36Sopenharmony_ci		return efi_runtime_get_supported_mask(arg);
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	return -ENOTTY;
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic int efi_test_open(struct inode *inode, struct file *file)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	int ret = security_locked_down(LOCKDOWN_EFI_TEST);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (ret)
72862306a36Sopenharmony_ci		return ret;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
73162306a36Sopenharmony_ci		return -EACCES;
73262306a36Sopenharmony_ci	/*
73362306a36Sopenharmony_ci	 * nothing special to do here
73462306a36Sopenharmony_ci	 * We do accept multiple open files at the same time as we
73562306a36Sopenharmony_ci	 * synchronize on the per call operation.
73662306a36Sopenharmony_ci	 */
73762306a36Sopenharmony_ci	return 0;
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic int efi_test_close(struct inode *inode, struct file *file)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	return 0;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci/*
74662306a36Sopenharmony_ci *	The various file operations we support.
74762306a36Sopenharmony_ci */
74862306a36Sopenharmony_cistatic const struct file_operations efi_test_fops = {
74962306a36Sopenharmony_ci	.owner		= THIS_MODULE,
75062306a36Sopenharmony_ci	.unlocked_ioctl	= efi_test_ioctl,
75162306a36Sopenharmony_ci	.open		= efi_test_open,
75262306a36Sopenharmony_ci	.release	= efi_test_close,
75362306a36Sopenharmony_ci	.llseek		= no_llseek,
75462306a36Sopenharmony_ci};
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic struct miscdevice efi_test_dev = {
75762306a36Sopenharmony_ci	MISC_DYNAMIC_MINOR,
75862306a36Sopenharmony_ci	"efi_test",
75962306a36Sopenharmony_ci	&efi_test_fops
76062306a36Sopenharmony_ci};
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic int __init efi_test_init(void)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	int ret;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	ret = misc_register(&efi_test_dev);
76762306a36Sopenharmony_ci	if (ret) {
76862306a36Sopenharmony_ci		pr_err("efi_test: can't misc_register on minor=%d\n",
76962306a36Sopenharmony_ci			MISC_DYNAMIC_MINOR);
77062306a36Sopenharmony_ci		return ret;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	return 0;
77462306a36Sopenharmony_ci}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic void __exit efi_test_exit(void)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	misc_deregister(&efi_test_dev);
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cimodule_init(efi_test_init);
78262306a36Sopenharmony_cimodule_exit(efi_test_exit);
783