xref: /OK3568_Linux_fs/kernel/drivers/firmware/efi/test/efi_test.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * EFI Test Driver for Runtime Services
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright(C) 2012-2016 Canonical Ltd.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * This driver exports EFI runtime services interfaces into userspace, which
8*4882a593Smuzhiyun  * allow to use and test UEFI runtime services provided by firmware.
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/miscdevice.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/init.h>
15*4882a593Smuzhiyun #include <linux/proc_fs.h>
16*4882a593Smuzhiyun #include <linux/efi.h>
17*4882a593Smuzhiyun #include <linux/security.h>
18*4882a593Smuzhiyun #include <linux/slab.h>
19*4882a593Smuzhiyun #include <linux/uaccess.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #include "efi_test.h"
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun MODULE_AUTHOR("Ivan Hu <ivan.hu@canonical.com>");
24*4882a593Smuzhiyun MODULE_DESCRIPTION("EFI Test Driver");
25*4882a593Smuzhiyun MODULE_LICENSE("GPL");
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun /*
28*4882a593Smuzhiyun  * Count the bytes in 'str', including the terminating NULL.
29*4882a593Smuzhiyun  *
30*4882a593Smuzhiyun  * Note this function returns the number of *bytes*, not the number of
31*4882a593Smuzhiyun  * ucs2 characters.
32*4882a593Smuzhiyun  */
user_ucs2_strsize(efi_char16_t __user * str)33*4882a593Smuzhiyun static inline size_t user_ucs2_strsize(efi_char16_t  __user *str)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun 	efi_char16_t *s = str, c;
36*4882a593Smuzhiyun 	size_t len;
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	if (!str)
39*4882a593Smuzhiyun 		return 0;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	/* Include terminating NULL */
42*4882a593Smuzhiyun 	len = sizeof(efi_char16_t);
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	if (get_user(c, s++)) {
45*4882a593Smuzhiyun 		/* Can't read userspace memory for size */
46*4882a593Smuzhiyun 		return 0;
47*4882a593Smuzhiyun 	}
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	while (c != 0) {
50*4882a593Smuzhiyun 		if (get_user(c, s++)) {
51*4882a593Smuzhiyun 			/* Can't read userspace memory for size */
52*4882a593Smuzhiyun 			return 0;
53*4882a593Smuzhiyun 		}
54*4882a593Smuzhiyun 		len += sizeof(efi_char16_t);
55*4882a593Smuzhiyun 	}
56*4882a593Smuzhiyun 	return len;
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun /*
60*4882a593Smuzhiyun  * Allocate a buffer and copy a ucs2 string from user space into it.
61*4882a593Smuzhiyun  */
62*4882a593Smuzhiyun static inline int
copy_ucs2_from_user_len(efi_char16_t ** dst,efi_char16_t __user * src,size_t len)63*4882a593Smuzhiyun copy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src,
64*4882a593Smuzhiyun 			size_t len)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun 	efi_char16_t *buf;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	if (!src) {
69*4882a593Smuzhiyun 		*dst = NULL;
70*4882a593Smuzhiyun 		return 0;
71*4882a593Smuzhiyun 	}
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	buf = memdup_user(src, len);
74*4882a593Smuzhiyun 	if (IS_ERR(buf)) {
75*4882a593Smuzhiyun 		*dst = NULL;
76*4882a593Smuzhiyun 		return PTR_ERR(buf);
77*4882a593Smuzhiyun 	}
78*4882a593Smuzhiyun 	*dst = buf;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	return 0;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun /*
84*4882a593Smuzhiyun  * Count the bytes in 'str', including the terminating NULL.
85*4882a593Smuzhiyun  *
86*4882a593Smuzhiyun  * Just a wrap for user_ucs2_strsize
87*4882a593Smuzhiyun  */
88*4882a593Smuzhiyun static inline int
get_ucs2_strsize_from_user(efi_char16_t __user * src,size_t * len)89*4882a593Smuzhiyun get_ucs2_strsize_from_user(efi_char16_t __user *src, size_t *len)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun 	*len = user_ucs2_strsize(src);
92*4882a593Smuzhiyun 	if (*len == 0)
93*4882a593Smuzhiyun 		return -EFAULT;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	return 0;
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun /*
99*4882a593Smuzhiyun  * Calculate the required buffer allocation size and copy a ucs2 string
100*4882a593Smuzhiyun  * from user space into it.
101*4882a593Smuzhiyun  *
102*4882a593Smuzhiyun  * This function differs from copy_ucs2_from_user_len() because it
103*4882a593Smuzhiyun  * calculates the size of the buffer to allocate by taking the length of
104*4882a593Smuzhiyun  * the string 'src'.
105*4882a593Smuzhiyun  *
106*4882a593Smuzhiyun  * If a non-zero value is returned, the caller MUST NOT access 'dst'.
107*4882a593Smuzhiyun  *
108*4882a593Smuzhiyun  * It is the caller's responsibility to free 'dst'.
109*4882a593Smuzhiyun  */
110*4882a593Smuzhiyun static inline int
copy_ucs2_from_user(efi_char16_t ** dst,efi_char16_t __user * src)111*4882a593Smuzhiyun copy_ucs2_from_user(efi_char16_t **dst, efi_char16_t __user *src)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	size_t len;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	len = user_ucs2_strsize(src);
116*4882a593Smuzhiyun 	if (len == 0)
117*4882a593Smuzhiyun 		return -EFAULT;
118*4882a593Smuzhiyun 	return copy_ucs2_from_user_len(dst, src, len);
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun /*
122*4882a593Smuzhiyun  * Copy a ucs2 string to a user buffer.
123*4882a593Smuzhiyun  *
124*4882a593Smuzhiyun  * This function is a simple wrapper around copy_to_user() that does
125*4882a593Smuzhiyun  * nothing if 'src' is NULL, which is useful for reducing the amount of
126*4882a593Smuzhiyun  * NULL checking the caller has to do.
127*4882a593Smuzhiyun  *
128*4882a593Smuzhiyun  * 'len' specifies the number of bytes to copy.
129*4882a593Smuzhiyun  */
130*4882a593Smuzhiyun static inline int
copy_ucs2_to_user_len(efi_char16_t __user * dst,efi_char16_t * src,size_t len)131*4882a593Smuzhiyun copy_ucs2_to_user_len(efi_char16_t __user *dst, efi_char16_t *src, size_t len)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun 	if (!src)
134*4882a593Smuzhiyun 		return 0;
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	return copy_to_user(dst, src, len);
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun 
efi_runtime_get_variable(unsigned long arg)139*4882a593Smuzhiyun static long efi_runtime_get_variable(unsigned long arg)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun 	struct efi_getvariable __user *getvariable_user;
142*4882a593Smuzhiyun 	struct efi_getvariable getvariable;
143*4882a593Smuzhiyun 	unsigned long datasize = 0, prev_datasize, *dz;
144*4882a593Smuzhiyun 	efi_guid_t vendor_guid, *vd = NULL;
145*4882a593Smuzhiyun 	efi_status_t status;
146*4882a593Smuzhiyun 	efi_char16_t *name = NULL;
147*4882a593Smuzhiyun 	u32 attr, *at;
148*4882a593Smuzhiyun 	void *data = NULL;
149*4882a593Smuzhiyun 	int rv = 0;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	getvariable_user = (struct efi_getvariable __user *)arg;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	if (copy_from_user(&getvariable, getvariable_user,
154*4882a593Smuzhiyun 			   sizeof(getvariable)))
155*4882a593Smuzhiyun 		return -EFAULT;
156*4882a593Smuzhiyun 	if (getvariable.data_size &&
157*4882a593Smuzhiyun 	    get_user(datasize, getvariable.data_size))
158*4882a593Smuzhiyun 		return -EFAULT;
159*4882a593Smuzhiyun 	if (getvariable.vendor_guid) {
160*4882a593Smuzhiyun 		if (copy_from_user(&vendor_guid, getvariable.vendor_guid,
161*4882a593Smuzhiyun 					sizeof(vendor_guid)))
162*4882a593Smuzhiyun 			return -EFAULT;
163*4882a593Smuzhiyun 		vd = &vendor_guid;
164*4882a593Smuzhiyun 	}
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	if (getvariable.variable_name) {
167*4882a593Smuzhiyun 		rv = copy_ucs2_from_user(&name, getvariable.variable_name);
168*4882a593Smuzhiyun 		if (rv)
169*4882a593Smuzhiyun 			return rv;
170*4882a593Smuzhiyun 	}
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	at = getvariable.attributes ? &attr : NULL;
173*4882a593Smuzhiyun 	dz = getvariable.data_size ? &datasize : NULL;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	if (getvariable.data_size && getvariable.data) {
176*4882a593Smuzhiyun 		data = kmalloc(datasize, GFP_KERNEL);
177*4882a593Smuzhiyun 		if (!data) {
178*4882a593Smuzhiyun 			kfree(name);
179*4882a593Smuzhiyun 			return -ENOMEM;
180*4882a593Smuzhiyun 		}
181*4882a593Smuzhiyun 	}
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	prev_datasize = datasize;
184*4882a593Smuzhiyun 	status = efi.get_variable(name, vd, at, dz, data);
185*4882a593Smuzhiyun 	kfree(name);
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	if (put_user(status, getvariable.status)) {
188*4882a593Smuzhiyun 		rv = -EFAULT;
189*4882a593Smuzhiyun 		goto out;
190*4882a593Smuzhiyun 	}
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	if (status != EFI_SUCCESS) {
193*4882a593Smuzhiyun 		if (status == EFI_BUFFER_TOO_SMALL) {
194*4882a593Smuzhiyun 			if (dz && put_user(datasize, getvariable.data_size)) {
195*4882a593Smuzhiyun 				rv = -EFAULT;
196*4882a593Smuzhiyun 				goto out;
197*4882a593Smuzhiyun 			}
198*4882a593Smuzhiyun 		}
199*4882a593Smuzhiyun 		rv = -EINVAL;
200*4882a593Smuzhiyun 		goto out;
201*4882a593Smuzhiyun 	}
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	if (prev_datasize < datasize) {
204*4882a593Smuzhiyun 		rv = -EINVAL;
205*4882a593Smuzhiyun 		goto out;
206*4882a593Smuzhiyun 	}
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	if (data) {
209*4882a593Smuzhiyun 		if (copy_to_user(getvariable.data, data, datasize)) {
210*4882a593Smuzhiyun 			rv = -EFAULT;
211*4882a593Smuzhiyun 			goto out;
212*4882a593Smuzhiyun 		}
213*4882a593Smuzhiyun 	}
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	if (at && put_user(attr, getvariable.attributes)) {
216*4882a593Smuzhiyun 		rv = -EFAULT;
217*4882a593Smuzhiyun 		goto out;
218*4882a593Smuzhiyun 	}
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	if (dz && put_user(datasize, getvariable.data_size))
221*4882a593Smuzhiyun 		rv = -EFAULT;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun out:
224*4882a593Smuzhiyun 	kfree(data);
225*4882a593Smuzhiyun 	return rv;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun 
efi_runtime_set_variable(unsigned long arg)229*4882a593Smuzhiyun static long efi_runtime_set_variable(unsigned long arg)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun 	struct efi_setvariable __user *setvariable_user;
232*4882a593Smuzhiyun 	struct efi_setvariable setvariable;
233*4882a593Smuzhiyun 	efi_guid_t vendor_guid;
234*4882a593Smuzhiyun 	efi_status_t status;
235*4882a593Smuzhiyun 	efi_char16_t *name = NULL;
236*4882a593Smuzhiyun 	void *data;
237*4882a593Smuzhiyun 	int rv = 0;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	setvariable_user = (struct efi_setvariable __user *)arg;
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	if (copy_from_user(&setvariable, setvariable_user, sizeof(setvariable)))
242*4882a593Smuzhiyun 		return -EFAULT;
243*4882a593Smuzhiyun 	if (copy_from_user(&vendor_guid, setvariable.vendor_guid,
244*4882a593Smuzhiyun 				sizeof(vendor_guid)))
245*4882a593Smuzhiyun 		return -EFAULT;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	if (setvariable.variable_name) {
248*4882a593Smuzhiyun 		rv = copy_ucs2_from_user(&name, setvariable.variable_name);
249*4882a593Smuzhiyun 		if (rv)
250*4882a593Smuzhiyun 			return rv;
251*4882a593Smuzhiyun 	}
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	data = memdup_user(setvariable.data, setvariable.data_size);
254*4882a593Smuzhiyun 	if (IS_ERR(data)) {
255*4882a593Smuzhiyun 		kfree(name);
256*4882a593Smuzhiyun 		return PTR_ERR(data);
257*4882a593Smuzhiyun 	}
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	status = efi.set_variable(name, &vendor_guid,
260*4882a593Smuzhiyun 				setvariable.attributes,
261*4882a593Smuzhiyun 				setvariable.data_size, data);
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	if (put_user(status, setvariable.status)) {
264*4882a593Smuzhiyun 		rv = -EFAULT;
265*4882a593Smuzhiyun 		goto out;
266*4882a593Smuzhiyun 	}
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	rv = status == EFI_SUCCESS ? 0 : -EINVAL;
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun out:
271*4882a593Smuzhiyun 	kfree(data);
272*4882a593Smuzhiyun 	kfree(name);
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	return rv;
275*4882a593Smuzhiyun }
276*4882a593Smuzhiyun 
efi_runtime_get_time(unsigned long arg)277*4882a593Smuzhiyun static long efi_runtime_get_time(unsigned long arg)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun 	struct efi_gettime __user *gettime_user;
280*4882a593Smuzhiyun 	struct efi_gettime  gettime;
281*4882a593Smuzhiyun 	efi_status_t status;
282*4882a593Smuzhiyun 	efi_time_cap_t cap;
283*4882a593Smuzhiyun 	efi_time_t efi_time;
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	gettime_user = (struct efi_gettime __user *)arg;
286*4882a593Smuzhiyun 	if (copy_from_user(&gettime, gettime_user, sizeof(gettime)))
287*4882a593Smuzhiyun 		return -EFAULT;
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	status = efi.get_time(gettime.time ? &efi_time : NULL,
290*4882a593Smuzhiyun 			      gettime.capabilities ? &cap : NULL);
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	if (put_user(status, gettime.status))
293*4882a593Smuzhiyun 		return -EFAULT;
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	if (status != EFI_SUCCESS)
296*4882a593Smuzhiyun 		return -EINVAL;
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	if (gettime.capabilities) {
299*4882a593Smuzhiyun 		efi_time_cap_t __user *cap_local;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 		cap_local = (efi_time_cap_t *)gettime.capabilities;
302*4882a593Smuzhiyun 		if (put_user(cap.resolution, &(cap_local->resolution)) ||
303*4882a593Smuzhiyun 			put_user(cap.accuracy, &(cap_local->accuracy)) ||
304*4882a593Smuzhiyun 			put_user(cap.sets_to_zero, &(cap_local->sets_to_zero)))
305*4882a593Smuzhiyun 			return -EFAULT;
306*4882a593Smuzhiyun 	}
307*4882a593Smuzhiyun 	if (gettime.time) {
308*4882a593Smuzhiyun 		if (copy_to_user(gettime.time, &efi_time, sizeof(efi_time_t)))
309*4882a593Smuzhiyun 			return -EFAULT;
310*4882a593Smuzhiyun 	}
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	return 0;
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun 
efi_runtime_set_time(unsigned long arg)315*4882a593Smuzhiyun static long efi_runtime_set_time(unsigned long arg)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun 	struct efi_settime __user *settime_user;
318*4882a593Smuzhiyun 	struct efi_settime settime;
319*4882a593Smuzhiyun 	efi_status_t status;
320*4882a593Smuzhiyun 	efi_time_t efi_time;
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	settime_user = (struct efi_settime __user *)arg;
323*4882a593Smuzhiyun 	if (copy_from_user(&settime, settime_user, sizeof(settime)))
324*4882a593Smuzhiyun 		return -EFAULT;
325*4882a593Smuzhiyun 	if (copy_from_user(&efi_time, settime.time,
326*4882a593Smuzhiyun 					sizeof(efi_time_t)))
327*4882a593Smuzhiyun 		return -EFAULT;
328*4882a593Smuzhiyun 	status = efi.set_time(&efi_time);
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	if (put_user(status, settime.status))
331*4882a593Smuzhiyun 		return -EFAULT;
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	return status == EFI_SUCCESS ? 0 : -EINVAL;
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun 
efi_runtime_get_waketime(unsigned long arg)336*4882a593Smuzhiyun static long efi_runtime_get_waketime(unsigned long arg)
337*4882a593Smuzhiyun {
338*4882a593Smuzhiyun 	struct efi_getwakeuptime __user *getwakeuptime_user;
339*4882a593Smuzhiyun 	struct efi_getwakeuptime getwakeuptime;
340*4882a593Smuzhiyun 	efi_bool_t enabled, pending;
341*4882a593Smuzhiyun 	efi_status_t status;
342*4882a593Smuzhiyun 	efi_time_t efi_time;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	getwakeuptime_user = (struct efi_getwakeuptime __user *)arg;
345*4882a593Smuzhiyun 	if (copy_from_user(&getwakeuptime, getwakeuptime_user,
346*4882a593Smuzhiyun 				sizeof(getwakeuptime)))
347*4882a593Smuzhiyun 		return -EFAULT;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	status = efi.get_wakeup_time(
350*4882a593Smuzhiyun 		getwakeuptime.enabled ? (efi_bool_t *)&enabled : NULL,
351*4882a593Smuzhiyun 		getwakeuptime.pending ? (efi_bool_t *)&pending : NULL,
352*4882a593Smuzhiyun 		getwakeuptime.time ? &efi_time : NULL);
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	if (put_user(status, getwakeuptime.status))
355*4882a593Smuzhiyun 		return -EFAULT;
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	if (status != EFI_SUCCESS)
358*4882a593Smuzhiyun 		return -EINVAL;
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	if (getwakeuptime.enabled && put_user(enabled,
361*4882a593Smuzhiyun 						getwakeuptime.enabled))
362*4882a593Smuzhiyun 		return -EFAULT;
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	if (getwakeuptime.time) {
365*4882a593Smuzhiyun 		if (copy_to_user(getwakeuptime.time, &efi_time,
366*4882a593Smuzhiyun 				sizeof(efi_time_t)))
367*4882a593Smuzhiyun 			return -EFAULT;
368*4882a593Smuzhiyun 	}
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	return 0;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun 
efi_runtime_set_waketime(unsigned long arg)373*4882a593Smuzhiyun static long efi_runtime_set_waketime(unsigned long arg)
374*4882a593Smuzhiyun {
375*4882a593Smuzhiyun 	struct efi_setwakeuptime __user *setwakeuptime_user;
376*4882a593Smuzhiyun 	struct efi_setwakeuptime setwakeuptime;
377*4882a593Smuzhiyun 	efi_bool_t enabled;
378*4882a593Smuzhiyun 	efi_status_t status;
379*4882a593Smuzhiyun 	efi_time_t efi_time;
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	setwakeuptime_user = (struct efi_setwakeuptime __user *)arg;
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	if (copy_from_user(&setwakeuptime, setwakeuptime_user,
384*4882a593Smuzhiyun 				sizeof(setwakeuptime)))
385*4882a593Smuzhiyun 		return -EFAULT;
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	enabled = setwakeuptime.enabled;
388*4882a593Smuzhiyun 	if (setwakeuptime.time) {
389*4882a593Smuzhiyun 		if (copy_from_user(&efi_time, setwakeuptime.time,
390*4882a593Smuzhiyun 					sizeof(efi_time_t)))
391*4882a593Smuzhiyun 			return -EFAULT;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 		status = efi.set_wakeup_time(enabled, &efi_time);
394*4882a593Smuzhiyun 	} else
395*4882a593Smuzhiyun 		status = efi.set_wakeup_time(enabled, NULL);
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	if (put_user(status, setwakeuptime.status))
398*4882a593Smuzhiyun 		return -EFAULT;
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	return status == EFI_SUCCESS ? 0 : -EINVAL;
401*4882a593Smuzhiyun }
402*4882a593Smuzhiyun 
efi_runtime_get_nextvariablename(unsigned long arg)403*4882a593Smuzhiyun static long efi_runtime_get_nextvariablename(unsigned long arg)
404*4882a593Smuzhiyun {
405*4882a593Smuzhiyun 	struct efi_getnextvariablename __user *getnextvariablename_user;
406*4882a593Smuzhiyun 	struct efi_getnextvariablename getnextvariablename;
407*4882a593Smuzhiyun 	unsigned long name_size, prev_name_size = 0, *ns = NULL;
408*4882a593Smuzhiyun 	efi_status_t status;
409*4882a593Smuzhiyun 	efi_guid_t *vd = NULL;
410*4882a593Smuzhiyun 	efi_guid_t vendor_guid;
411*4882a593Smuzhiyun 	efi_char16_t *name = NULL;
412*4882a593Smuzhiyun 	int rv = 0;
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	getnextvariablename_user = (struct efi_getnextvariablename __user *)arg;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	if (copy_from_user(&getnextvariablename, getnextvariablename_user,
417*4882a593Smuzhiyun 			   sizeof(getnextvariablename)))
418*4882a593Smuzhiyun 		return -EFAULT;
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	if (getnextvariablename.variable_name_size) {
421*4882a593Smuzhiyun 		if (get_user(name_size, getnextvariablename.variable_name_size))
422*4882a593Smuzhiyun 			return -EFAULT;
423*4882a593Smuzhiyun 		ns = &name_size;
424*4882a593Smuzhiyun 		prev_name_size = name_size;
425*4882a593Smuzhiyun 	}
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	if (getnextvariablename.vendor_guid) {
428*4882a593Smuzhiyun 		if (copy_from_user(&vendor_guid,
429*4882a593Smuzhiyun 				getnextvariablename.vendor_guid,
430*4882a593Smuzhiyun 				sizeof(vendor_guid)))
431*4882a593Smuzhiyun 			return -EFAULT;
432*4882a593Smuzhiyun 		vd = &vendor_guid;
433*4882a593Smuzhiyun 	}
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	if (getnextvariablename.variable_name) {
436*4882a593Smuzhiyun 		size_t name_string_size = 0;
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 		rv = get_ucs2_strsize_from_user(
439*4882a593Smuzhiyun 				getnextvariablename.variable_name,
440*4882a593Smuzhiyun 				&name_string_size);
441*4882a593Smuzhiyun 		if (rv)
442*4882a593Smuzhiyun 			return rv;
443*4882a593Smuzhiyun 		/*
444*4882a593Smuzhiyun 		 * The name_size may be smaller than the real buffer size where
445*4882a593Smuzhiyun 		 * variable name located in some use cases. The most typical
446*4882a593Smuzhiyun 		 * case is passing a 0 to get the required buffer size for the
447*4882a593Smuzhiyun 		 * 1st time call. So we need to copy the content from user
448*4882a593Smuzhiyun 		 * space for at least the string size of variable name, or else
449*4882a593Smuzhiyun 		 * the name passed to UEFI may not be terminated as we expected.
450*4882a593Smuzhiyun 		 */
451*4882a593Smuzhiyun 		rv = copy_ucs2_from_user_len(&name,
452*4882a593Smuzhiyun 				getnextvariablename.variable_name,
453*4882a593Smuzhiyun 				prev_name_size > name_string_size ?
454*4882a593Smuzhiyun 				prev_name_size : name_string_size);
455*4882a593Smuzhiyun 		if (rv)
456*4882a593Smuzhiyun 			return rv;
457*4882a593Smuzhiyun 	}
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 	status = efi.get_next_variable(ns, name, vd);
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 	if (put_user(status, getnextvariablename.status)) {
462*4882a593Smuzhiyun 		rv = -EFAULT;
463*4882a593Smuzhiyun 		goto out;
464*4882a593Smuzhiyun 	}
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	if (status != EFI_SUCCESS) {
467*4882a593Smuzhiyun 		if (status == EFI_BUFFER_TOO_SMALL) {
468*4882a593Smuzhiyun 			if (ns && put_user(*ns,
469*4882a593Smuzhiyun 				getnextvariablename.variable_name_size)) {
470*4882a593Smuzhiyun 				rv = -EFAULT;
471*4882a593Smuzhiyun 				goto out;
472*4882a593Smuzhiyun 			}
473*4882a593Smuzhiyun 		}
474*4882a593Smuzhiyun 		rv = -EINVAL;
475*4882a593Smuzhiyun 		goto out;
476*4882a593Smuzhiyun 	}
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 	if (name) {
479*4882a593Smuzhiyun 		if (copy_ucs2_to_user_len(getnextvariablename.variable_name,
480*4882a593Smuzhiyun 						name, prev_name_size)) {
481*4882a593Smuzhiyun 			rv = -EFAULT;
482*4882a593Smuzhiyun 			goto out;
483*4882a593Smuzhiyun 		}
484*4882a593Smuzhiyun 	}
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 	if (ns) {
487*4882a593Smuzhiyun 		if (put_user(*ns, getnextvariablename.variable_name_size)) {
488*4882a593Smuzhiyun 			rv = -EFAULT;
489*4882a593Smuzhiyun 			goto out;
490*4882a593Smuzhiyun 		}
491*4882a593Smuzhiyun 	}
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 	if (vd) {
494*4882a593Smuzhiyun 		if (copy_to_user(getnextvariablename.vendor_guid, vd,
495*4882a593Smuzhiyun 							sizeof(efi_guid_t)))
496*4882a593Smuzhiyun 			rv = -EFAULT;
497*4882a593Smuzhiyun 	}
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun out:
500*4882a593Smuzhiyun 	kfree(name);
501*4882a593Smuzhiyun 	return rv;
502*4882a593Smuzhiyun }
503*4882a593Smuzhiyun 
efi_runtime_get_nexthighmonocount(unsigned long arg)504*4882a593Smuzhiyun static long efi_runtime_get_nexthighmonocount(unsigned long arg)
505*4882a593Smuzhiyun {
506*4882a593Smuzhiyun 	struct efi_getnexthighmonotoniccount __user *getnexthighmonocount_user;
507*4882a593Smuzhiyun 	struct efi_getnexthighmonotoniccount getnexthighmonocount;
508*4882a593Smuzhiyun 	efi_status_t status;
509*4882a593Smuzhiyun 	u32 count;
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 	getnexthighmonocount_user = (struct
512*4882a593Smuzhiyun 			efi_getnexthighmonotoniccount __user *)arg;
513*4882a593Smuzhiyun 
514*4882a593Smuzhiyun 	if (copy_from_user(&getnexthighmonocount,
515*4882a593Smuzhiyun 			   getnexthighmonocount_user,
516*4882a593Smuzhiyun 			   sizeof(getnexthighmonocount)))
517*4882a593Smuzhiyun 		return -EFAULT;
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	status = efi.get_next_high_mono_count(
520*4882a593Smuzhiyun 		getnexthighmonocount.high_count ? &count : NULL);
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 	if (put_user(status, getnexthighmonocount.status))
523*4882a593Smuzhiyun 		return -EFAULT;
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	if (status != EFI_SUCCESS)
526*4882a593Smuzhiyun 		return -EINVAL;
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	if (getnexthighmonocount.high_count &&
529*4882a593Smuzhiyun 	    put_user(count, getnexthighmonocount.high_count))
530*4882a593Smuzhiyun 		return -EFAULT;
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	return 0;
533*4882a593Smuzhiyun }
534*4882a593Smuzhiyun 
efi_runtime_reset_system(unsigned long arg)535*4882a593Smuzhiyun static long efi_runtime_reset_system(unsigned long arg)
536*4882a593Smuzhiyun {
537*4882a593Smuzhiyun 	struct efi_resetsystem __user *resetsystem_user;
538*4882a593Smuzhiyun 	struct efi_resetsystem resetsystem;
539*4882a593Smuzhiyun 	void *data = NULL;
540*4882a593Smuzhiyun 
541*4882a593Smuzhiyun 	resetsystem_user = (struct efi_resetsystem __user *)arg;
542*4882a593Smuzhiyun 	if (copy_from_user(&resetsystem, resetsystem_user,
543*4882a593Smuzhiyun 						sizeof(resetsystem)))
544*4882a593Smuzhiyun 		return -EFAULT;
545*4882a593Smuzhiyun 	if (resetsystem.data_size != 0) {
546*4882a593Smuzhiyun 		data = memdup_user((void *)resetsystem.data,
547*4882a593Smuzhiyun 						resetsystem.data_size);
548*4882a593Smuzhiyun 		if (IS_ERR(data))
549*4882a593Smuzhiyun 			return PTR_ERR(data);
550*4882a593Smuzhiyun 	}
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	efi.reset_system(resetsystem.reset_type, resetsystem.status,
553*4882a593Smuzhiyun 				resetsystem.data_size, (efi_char16_t *)data);
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	kfree(data);
556*4882a593Smuzhiyun 	return 0;
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun 
efi_runtime_query_variableinfo(unsigned long arg)559*4882a593Smuzhiyun static long efi_runtime_query_variableinfo(unsigned long arg)
560*4882a593Smuzhiyun {
561*4882a593Smuzhiyun 	struct efi_queryvariableinfo __user *queryvariableinfo_user;
562*4882a593Smuzhiyun 	struct efi_queryvariableinfo queryvariableinfo;
563*4882a593Smuzhiyun 	efi_status_t status;
564*4882a593Smuzhiyun 	u64 max_storage, remaining, max_size;
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun 	queryvariableinfo_user = (struct efi_queryvariableinfo __user *)arg;
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun 	if (copy_from_user(&queryvariableinfo, queryvariableinfo_user,
569*4882a593Smuzhiyun 			   sizeof(queryvariableinfo)))
570*4882a593Smuzhiyun 		return -EFAULT;
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	status = efi.query_variable_info(queryvariableinfo.attributes,
573*4882a593Smuzhiyun 					 &max_storage, &remaining, &max_size);
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun 	if (put_user(status, queryvariableinfo.status))
576*4882a593Smuzhiyun 		return -EFAULT;
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	if (status != EFI_SUCCESS)
579*4882a593Smuzhiyun 		return -EINVAL;
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	if (put_user(max_storage,
582*4882a593Smuzhiyun 		     queryvariableinfo.maximum_variable_storage_size))
583*4882a593Smuzhiyun 		return -EFAULT;
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun 	if (put_user(remaining,
586*4882a593Smuzhiyun 		     queryvariableinfo.remaining_variable_storage_size))
587*4882a593Smuzhiyun 		return -EFAULT;
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	if (put_user(max_size, queryvariableinfo.maximum_variable_size))
590*4882a593Smuzhiyun 		return -EFAULT;
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 	return 0;
593*4882a593Smuzhiyun }
594*4882a593Smuzhiyun 
efi_runtime_query_capsulecaps(unsigned long arg)595*4882a593Smuzhiyun static long efi_runtime_query_capsulecaps(unsigned long arg)
596*4882a593Smuzhiyun {
597*4882a593Smuzhiyun 	struct efi_querycapsulecapabilities __user *qcaps_user;
598*4882a593Smuzhiyun 	struct efi_querycapsulecapabilities qcaps;
599*4882a593Smuzhiyun 	efi_capsule_header_t *capsules;
600*4882a593Smuzhiyun 	efi_status_t status;
601*4882a593Smuzhiyun 	u64 max_size;
602*4882a593Smuzhiyun 	int i, reset_type;
603*4882a593Smuzhiyun 	int rv = 0;
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun 	qcaps_user = (struct efi_querycapsulecapabilities __user *)arg;
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	if (copy_from_user(&qcaps, qcaps_user, sizeof(qcaps)))
608*4882a593Smuzhiyun 		return -EFAULT;
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	if (qcaps.capsule_count == ULONG_MAX)
611*4882a593Smuzhiyun 		return -EINVAL;
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 	capsules = kcalloc(qcaps.capsule_count + 1,
614*4882a593Smuzhiyun 			   sizeof(efi_capsule_header_t), GFP_KERNEL);
615*4882a593Smuzhiyun 	if (!capsules)
616*4882a593Smuzhiyun 		return -ENOMEM;
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun 	for (i = 0; i < qcaps.capsule_count; i++) {
619*4882a593Smuzhiyun 		efi_capsule_header_t *c;
620*4882a593Smuzhiyun 		/*
621*4882a593Smuzhiyun 		 * We cannot dereference qcaps.capsule_header_array directly to
622*4882a593Smuzhiyun 		 * obtain the address of the capsule as it resides in the
623*4882a593Smuzhiyun 		 * user space
624*4882a593Smuzhiyun 		 */
625*4882a593Smuzhiyun 		if (get_user(c, qcaps.capsule_header_array + i)) {
626*4882a593Smuzhiyun 			rv = -EFAULT;
627*4882a593Smuzhiyun 			goto out;
628*4882a593Smuzhiyun 		}
629*4882a593Smuzhiyun 		if (copy_from_user(&capsules[i], c,
630*4882a593Smuzhiyun 				sizeof(efi_capsule_header_t))) {
631*4882a593Smuzhiyun 			rv = -EFAULT;
632*4882a593Smuzhiyun 			goto out;
633*4882a593Smuzhiyun 		}
634*4882a593Smuzhiyun 	}
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	qcaps.capsule_header_array = &capsules;
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun 	status = efi.query_capsule_caps((efi_capsule_header_t **)
639*4882a593Smuzhiyun 					qcaps.capsule_header_array,
640*4882a593Smuzhiyun 					qcaps.capsule_count,
641*4882a593Smuzhiyun 					&max_size, &reset_type);
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun 	if (put_user(status, qcaps.status)) {
644*4882a593Smuzhiyun 		rv = -EFAULT;
645*4882a593Smuzhiyun 		goto out;
646*4882a593Smuzhiyun 	}
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	if (status != EFI_SUCCESS) {
649*4882a593Smuzhiyun 		rv = -EINVAL;
650*4882a593Smuzhiyun 		goto out;
651*4882a593Smuzhiyun 	}
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	if (put_user(max_size, qcaps.maximum_capsule_size)) {
654*4882a593Smuzhiyun 		rv = -EFAULT;
655*4882a593Smuzhiyun 		goto out;
656*4882a593Smuzhiyun 	}
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun 	if (put_user(reset_type, qcaps.reset_type))
659*4882a593Smuzhiyun 		rv = -EFAULT;
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun out:
662*4882a593Smuzhiyun 	kfree(capsules);
663*4882a593Smuzhiyun 	return rv;
664*4882a593Smuzhiyun }
665*4882a593Smuzhiyun 
efi_test_ioctl(struct file * file,unsigned int cmd,unsigned long arg)666*4882a593Smuzhiyun static long efi_test_ioctl(struct file *file, unsigned int cmd,
667*4882a593Smuzhiyun 							unsigned long arg)
668*4882a593Smuzhiyun {
669*4882a593Smuzhiyun 	switch (cmd) {
670*4882a593Smuzhiyun 	case EFI_RUNTIME_GET_VARIABLE:
671*4882a593Smuzhiyun 		return efi_runtime_get_variable(arg);
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun 	case EFI_RUNTIME_SET_VARIABLE:
674*4882a593Smuzhiyun 		return efi_runtime_set_variable(arg);
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun 	case EFI_RUNTIME_GET_TIME:
677*4882a593Smuzhiyun 		return efi_runtime_get_time(arg);
678*4882a593Smuzhiyun 
679*4882a593Smuzhiyun 	case EFI_RUNTIME_SET_TIME:
680*4882a593Smuzhiyun 		return efi_runtime_set_time(arg);
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	case EFI_RUNTIME_GET_WAKETIME:
683*4882a593Smuzhiyun 		return efi_runtime_get_waketime(arg);
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun 	case EFI_RUNTIME_SET_WAKETIME:
686*4882a593Smuzhiyun 		return efi_runtime_set_waketime(arg);
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 	case EFI_RUNTIME_GET_NEXTVARIABLENAME:
689*4882a593Smuzhiyun 		return efi_runtime_get_nextvariablename(arg);
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	case EFI_RUNTIME_GET_NEXTHIGHMONOTONICCOUNT:
692*4882a593Smuzhiyun 		return efi_runtime_get_nexthighmonocount(arg);
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun 	case EFI_RUNTIME_QUERY_VARIABLEINFO:
695*4882a593Smuzhiyun 		return efi_runtime_query_variableinfo(arg);
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	case EFI_RUNTIME_QUERY_CAPSULECAPABILITIES:
698*4882a593Smuzhiyun 		return efi_runtime_query_capsulecaps(arg);
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun 	case EFI_RUNTIME_RESET_SYSTEM:
701*4882a593Smuzhiyun 		return efi_runtime_reset_system(arg);
702*4882a593Smuzhiyun 	}
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun 	return -ENOTTY;
705*4882a593Smuzhiyun }
706*4882a593Smuzhiyun 
efi_test_open(struct inode * inode,struct file * file)707*4882a593Smuzhiyun static int efi_test_open(struct inode *inode, struct file *file)
708*4882a593Smuzhiyun {
709*4882a593Smuzhiyun 	int ret = security_locked_down(LOCKDOWN_EFI_TEST);
710*4882a593Smuzhiyun 
711*4882a593Smuzhiyun 	if (ret)
712*4882a593Smuzhiyun 		return ret;
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 	if (!capable(CAP_SYS_ADMIN))
715*4882a593Smuzhiyun 		return -EACCES;
716*4882a593Smuzhiyun 	/*
717*4882a593Smuzhiyun 	 * nothing special to do here
718*4882a593Smuzhiyun 	 * We do accept multiple open files at the same time as we
719*4882a593Smuzhiyun 	 * synchronize on the per call operation.
720*4882a593Smuzhiyun 	 */
721*4882a593Smuzhiyun 	return 0;
722*4882a593Smuzhiyun }
723*4882a593Smuzhiyun 
efi_test_close(struct inode * inode,struct file * file)724*4882a593Smuzhiyun static int efi_test_close(struct inode *inode, struct file *file)
725*4882a593Smuzhiyun {
726*4882a593Smuzhiyun 	return 0;
727*4882a593Smuzhiyun }
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun /*
730*4882a593Smuzhiyun  *	The various file operations we support.
731*4882a593Smuzhiyun  */
732*4882a593Smuzhiyun static const struct file_operations efi_test_fops = {
733*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
734*4882a593Smuzhiyun 	.unlocked_ioctl	= efi_test_ioctl,
735*4882a593Smuzhiyun 	.open		= efi_test_open,
736*4882a593Smuzhiyun 	.release	= efi_test_close,
737*4882a593Smuzhiyun 	.llseek		= no_llseek,
738*4882a593Smuzhiyun };
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun static struct miscdevice efi_test_dev = {
741*4882a593Smuzhiyun 	MISC_DYNAMIC_MINOR,
742*4882a593Smuzhiyun 	"efi_test",
743*4882a593Smuzhiyun 	&efi_test_fops
744*4882a593Smuzhiyun };
745*4882a593Smuzhiyun 
efi_test_init(void)746*4882a593Smuzhiyun static int __init efi_test_init(void)
747*4882a593Smuzhiyun {
748*4882a593Smuzhiyun 	int ret;
749*4882a593Smuzhiyun 
750*4882a593Smuzhiyun 	ret = misc_register(&efi_test_dev);
751*4882a593Smuzhiyun 	if (ret) {
752*4882a593Smuzhiyun 		pr_err("efi_test: can't misc_register on minor=%d\n",
753*4882a593Smuzhiyun 			MISC_DYNAMIC_MINOR);
754*4882a593Smuzhiyun 		return ret;
755*4882a593Smuzhiyun 	}
756*4882a593Smuzhiyun 
757*4882a593Smuzhiyun 	return 0;
758*4882a593Smuzhiyun }
759*4882a593Smuzhiyun 
efi_test_exit(void)760*4882a593Smuzhiyun static void __exit efi_test_exit(void)
761*4882a593Smuzhiyun {
762*4882a593Smuzhiyun 	misc_deregister(&efi_test_dev);
763*4882a593Smuzhiyun }
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun module_init(efi_test_init);
766*4882a593Smuzhiyun module_exit(efi_test_exit);
767