diff options
Diffstat (limited to 'drivers/firmware/efi/test/efi_test.c')
-rw-r--r-- | drivers/firmware/efi/test/efi_test.c | 749 |
1 files changed, 749 insertions, 0 deletions
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); | ||