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