2 * EFI Test Driver for Runtime Services
4 * Copyright(C) 2012-2016 Canonical Ltd.
6 * This driver exports EFI runtime services interfaces into userspace, which
7 * allow to use and test UEFI runtime services provided by firmware.
11 #include <linux/miscdevice.h>
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/proc_fs.h>
15 #include <linux/efi.h>
16 #include <linux/slab.h>
17 #include <linux/uaccess.h>
22 MODULE_DESCRIPTION("EFI Test Driver");
23 MODULE_LICENSE("GPL");
26 * Count the bytes in 'str', including the terminating NULL.
28 * Note this function returns the number of *bytes*, not the number of
31 static inline size_t user_ucs2_strsize(efi_char16_t __user *str)
33 efi_char16_t *s = str, c;
39 /* Include terminating NULL */
40 len = sizeof(efi_char16_t);
42 if (get_user(c, s++)) {
43 /* Can't read userspace memory for size */
48 if (get_user(c, s++)) {
49 /* Can't read userspace memory for size */
52 len += sizeof(efi_char16_t);
58 * Allocate a buffer and copy a ucs2 string from user space into it.
61 copy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src,
71 if (!access_ok(VERIFY_READ, src, 1))
74 buf = memdup_user(src, len);
85 * Count the bytes in 'str', including the terminating NULL.
87 * Just a wrap for user_ucs2_strsize
90 get_ucs2_strsize_from_user(efi_char16_t __user *src, size_t *len)
92 if (!access_ok(VERIFY_READ, src, 1))
95 *len = user_ucs2_strsize(src);
103 * Calculate the required buffer allocation size and copy a ucs2 string
104 * from user space into it.
106 * This function differs from copy_ucs2_from_user_len() because it
107 * calculates the size of the buffer to allocate by taking the length of
110 * If a non-zero value is returned, the caller MUST NOT access 'dst'.
112 * It is the caller's responsibility to free 'dst'.
115 copy_ucs2_from_user(efi_char16_t **dst, efi_char16_t __user *src)
119 if (!access_ok(VERIFY_READ, src, 1))
122 len = user_ucs2_strsize(src);
125 return copy_ucs2_from_user_len(dst, src, len);
129 * Copy a ucs2 string to a user buffer.
131 * This function is a simple wrapper around copy_to_user() that does
132 * nothing if 'src' is NULL, which is useful for reducing the amount of
133 * NULL checking the caller has to do.
135 * 'len' specifies the number of bytes to copy.
138 copy_ucs2_to_user_len(efi_char16_t __user *dst, efi_char16_t *src, size_t len)
143 if (!access_ok(VERIFY_WRITE, dst, 1))
146 return copy_to_user(dst, src, len);
149 static long efi_runtime_get_variable(unsigned long arg)
151 struct efi_getvariable __user *getvariable_user;
152 struct efi_getvariable getvariable;
153 unsigned long datasize = 0, prev_datasize, *dz;
154 efi_guid_t vendor_guid, *vd = NULL;
156 efi_char16_t *name = NULL;
161 getvariable_user = (struct efi_getvariable __user *)arg;
163 if (copy_from_user(&getvariable, getvariable_user,
164 sizeof(getvariable)))
166 if (getvariable.data_size &&
167 get_user(datasize, getvariable.data_size))
169 if (getvariable.vendor_guid) {
170 if (copy_from_user(&vendor_guid, getvariable.vendor_guid,
171 sizeof(vendor_guid)))
176 if (getvariable.variable_name) {
177 rv = copy_ucs2_from_user(&name, getvariable.variable_name);
182 at = getvariable.attributes ? &attr : NULL;
183 dz = getvariable.data_size ? &datasize : NULL;
185 if (getvariable.data_size && getvariable.data) {
186 data = kmalloc(datasize, GFP_KERNEL);
193 prev_datasize = datasize;
194 status = efi.get_variable(name, vd, at, dz, data);
197 if (put_user(status, getvariable.status)) {
202 if (status != EFI_SUCCESS) {
203 if (status == EFI_BUFFER_TOO_SMALL) {
204 if (dz && put_user(datasize, getvariable.data_size)) {
213 if (prev_datasize < datasize) {
219 if (copy_to_user(getvariable.data, data, datasize)) {
225 if (at && put_user(attr, getvariable.attributes)) {
230 if (dz && put_user(datasize, getvariable.data_size))
239 static long efi_runtime_set_variable(unsigned long arg)
241 struct efi_setvariable __user *setvariable_user;
242 struct efi_setvariable setvariable;
243 efi_guid_t vendor_guid;
245 efi_char16_t *name = NULL;
249 setvariable_user = (struct efi_setvariable __user *)arg;
251 if (copy_from_user(&setvariable, setvariable_user, sizeof(setvariable)))
253 if (copy_from_user(&vendor_guid, setvariable.vendor_guid,
254 sizeof(vendor_guid)))
257 if (setvariable.variable_name) {
258 rv = copy_ucs2_from_user(&name, setvariable.variable_name);
263 data = memdup_user(setvariable.data, setvariable.data_size);
266 return PTR_ERR(data);
269 status = efi.set_variable(name, &vendor_guid,
270 setvariable.attributes,
271 setvariable.data_size, data);
273 if (put_user(status, setvariable.status)) {
278 rv = status == EFI_SUCCESS ? 0 : -EINVAL;
287 static long efi_runtime_get_time(unsigned long arg)
289 struct efi_gettime __user *gettime_user;
290 struct efi_gettime gettime;
295 gettime_user = (struct efi_gettime __user *)arg;
296 if (copy_from_user(&gettime, gettime_user, sizeof(gettime)))
299 status = efi.get_time(gettime.time ? &efi_time : NULL,
300 gettime.capabilities ? &cap : NULL);
302 if (put_user(status, gettime.status))
305 if (status != EFI_SUCCESS)
308 if (gettime.capabilities) {
309 efi_time_cap_t __user *cap_local;
311 cap_local = (efi_time_cap_t *)gettime.capabilities;
312 if (put_user(cap.resolution, &(cap_local->resolution)) ||
313 put_user(cap.accuracy, &(cap_local->accuracy)) ||
314 put_user(cap.sets_to_zero, &(cap_local->sets_to_zero)))
318 if (copy_to_user(gettime.time, &efi_time, sizeof(efi_time_t)))
325 static long efi_runtime_set_time(unsigned long arg)
327 struct efi_settime __user *settime_user;
328 struct efi_settime settime;
332 settime_user = (struct efi_settime __user *)arg;
333 if (copy_from_user(&settime, settime_user, sizeof(settime)))
335 if (copy_from_user(&efi_time, settime.time,
338 status = efi.set_time(&efi_time);
340 if (put_user(status, settime.status))
343 return status == EFI_SUCCESS ? 0 : -EINVAL;
346 static long efi_runtime_get_waketime(unsigned long arg)
348 struct efi_getwakeuptime __user *getwakeuptime_user;
349 struct efi_getwakeuptime getwakeuptime;
350 efi_bool_t enabled, pending;
354 getwakeuptime_user = (struct efi_getwakeuptime __user *)arg;
355 if (copy_from_user(&getwakeuptime, getwakeuptime_user,
356 sizeof(getwakeuptime)))
359 status = efi.get_wakeup_time(
360 getwakeuptime.enabled ? (efi_bool_t *)&enabled : NULL,
361 getwakeuptime.pending ? (efi_bool_t *)&pending : NULL,
362 getwakeuptime.time ? &efi_time : NULL);
364 if (put_user(status, getwakeuptime.status))
367 if (status != EFI_SUCCESS)
370 if (getwakeuptime.enabled && put_user(enabled,
371 getwakeuptime.enabled))
374 if (getwakeuptime.time) {
375 if (copy_to_user(getwakeuptime.time, &efi_time,
383 static long efi_runtime_set_waketime(unsigned long arg)
385 struct efi_setwakeuptime __user *setwakeuptime_user;
386 struct efi_setwakeuptime setwakeuptime;
391 setwakeuptime_user = (struct efi_setwakeuptime __user *)arg;
393 if (copy_from_user(&setwakeuptime, setwakeuptime_user,
394 sizeof(setwakeuptime)))
397 enabled = setwakeuptime.enabled;
398 if (setwakeuptime.time) {
399 if (copy_from_user(&efi_time, setwakeuptime.time,
403 status = efi.set_wakeup_time(enabled, &efi_time);
405 status = efi.set_wakeup_time(enabled, NULL);
407 if (put_user(status, setwakeuptime.status))
410 return status == EFI_SUCCESS ? 0 : -EINVAL;
413 static long efi_runtime_get_nextvariablename(unsigned long arg)
415 struct efi_getnextvariablename __user *getnextvariablename_user;
416 struct efi_getnextvariablename getnextvariablename;
417 unsigned long name_size, prev_name_size = 0, *ns = NULL;
419 efi_guid_t *vd = NULL;
420 efi_guid_t vendor_guid;
421 efi_char16_t *name = NULL;
424 getnextvariablename_user = (struct efi_getnextvariablename __user *)arg;
426 if (copy_from_user(&getnextvariablename, getnextvariablename_user,
427 sizeof(getnextvariablename)))
430 if (getnextvariablename.variable_name_size) {
431 if (get_user(name_size, getnextvariablename.variable_name_size))
434 prev_name_size = name_size;
437 if (getnextvariablename.vendor_guid) {
438 if (copy_from_user(&vendor_guid,
439 getnextvariablename.vendor_guid,
440 sizeof(vendor_guid)))
445 if (getnextvariablename.variable_name) {
446 size_t name_string_size = 0;
448 rv = get_ucs2_strsize_from_user(
449 getnextvariablename.variable_name,
454 * The name_size may be smaller than the real buffer size where
455 * variable name located in some use cases. The most typical
456 * case is passing a 0 to get the required buffer size for the
457 * 1st time call. So we need to copy the content from user
458 * space for at least the string size of variable name, or else
459 * the name passed to UEFI may not be terminated as we expected.
461 rv = copy_ucs2_from_user_len(&name,
462 getnextvariablename.variable_name,
463 prev_name_size > name_string_size ?
464 prev_name_size : name_string_size);
469 status = efi.get_next_variable(ns, name, vd);
471 if (put_user(status, getnextvariablename.status)) {
476 if (status != EFI_SUCCESS) {
477 if (status == EFI_BUFFER_TOO_SMALL) {
478 if (ns && put_user(*ns,
479 getnextvariablename.variable_name_size)) {
489 if (copy_ucs2_to_user_len(getnextvariablename.variable_name,
490 name, prev_name_size)) {
497 if (put_user(*ns, getnextvariablename.variable_name_size)) {
504 if (copy_to_user(getnextvariablename.vendor_guid, vd,
514 static long efi_runtime_get_nexthighmonocount(unsigned long arg)
516 struct efi_getnexthighmonotoniccount __user *getnexthighmonocount_user;
517 struct efi_getnexthighmonotoniccount getnexthighmonocount;
521 getnexthighmonocount_user = (struct
522 efi_getnexthighmonotoniccount __user *)arg;
524 if (copy_from_user(&getnexthighmonocount,
525 getnexthighmonocount_user,
526 sizeof(getnexthighmonocount)))
529 status = efi.get_next_high_mono_count(
530 getnexthighmonocount.high_count ? &count : NULL);
532 if (put_user(status, getnexthighmonocount.status))
535 if (status != EFI_SUCCESS)
538 if (getnexthighmonocount.high_count &&
539 put_user(count, getnexthighmonocount.high_count))
545 static long efi_runtime_reset_system(unsigned long arg)
547 struct efi_resetsystem __user *resetsystem_user;
548 struct efi_resetsystem resetsystem;
551 resetsystem_user = (struct efi_resetsystem __user *)arg;
552 if (copy_from_user(&resetsystem, resetsystem_user,
553 sizeof(resetsystem)))
555 if (resetsystem.data_size != 0) {
556 data = memdup_user((void *)resetsystem.data,
557 resetsystem.data_size);
559 return PTR_ERR(data);
562 efi.reset_system(resetsystem.reset_type, resetsystem.status,
563 resetsystem.data_size, (efi_char16_t *)data);
569 static long efi_runtime_query_variableinfo(unsigned long arg)
571 struct efi_queryvariableinfo __user *queryvariableinfo_user;
572 struct efi_queryvariableinfo queryvariableinfo;
574 u64 max_storage, remaining, max_size;
576 queryvariableinfo_user = (struct efi_queryvariableinfo __user *)arg;
578 if (copy_from_user(&queryvariableinfo, queryvariableinfo_user,
579 sizeof(queryvariableinfo)))
582 status = efi.query_variable_info(queryvariableinfo.attributes,
583 &max_storage, &remaining, &max_size);
585 if (put_user(status, queryvariableinfo.status))
588 if (status != EFI_SUCCESS)
591 if (put_user(max_storage,
592 queryvariableinfo.maximum_variable_storage_size))
595 if (put_user(remaining,
596 queryvariableinfo.remaining_variable_storage_size))
599 if (put_user(max_size, queryvariableinfo.maximum_variable_size))
605 static long efi_runtime_query_capsulecaps(unsigned long arg)
607 struct efi_querycapsulecapabilities __user *qcaps_user;
608 struct efi_querycapsulecapabilities qcaps;
609 efi_capsule_header_t *capsules;
615 qcaps_user = (struct efi_querycapsulecapabilities __user *)arg;
617 if (copy_from_user(&qcaps, qcaps_user, sizeof(qcaps)))
620 if (qcaps.capsule_count == ULONG_MAX)
623 capsules = kcalloc(qcaps.capsule_count + 1,
624 sizeof(efi_capsule_header_t), GFP_KERNEL);
628 for (i = 0; i < qcaps.capsule_count; i++) {
629 efi_capsule_header_t *c;
631 * We cannot dereference qcaps.capsule_header_array directly to
632 * obtain the address of the capsule as it resides in the
635 if (get_user(c, qcaps.capsule_header_array + i)) {
639 if (copy_from_user(&capsules[i], c,
640 sizeof(efi_capsule_header_t))) {
646 qcaps.capsule_header_array = &capsules;
648 status = efi.query_capsule_caps((efi_capsule_header_t **)
649 qcaps.capsule_header_array,
651 &max_size, &reset_type);
653 if (put_user(status, qcaps.status)) {
658 if (status != EFI_SUCCESS) {
663 if (put_user(max_size, qcaps.maximum_capsule_size)) {
668 if (put_user(reset_type, qcaps.reset_type))
676 static long efi_test_ioctl(struct file *file, unsigned int cmd,
680 case EFI_RUNTIME_GET_VARIABLE:
681 return efi_runtime_get_variable(arg);
683 case EFI_RUNTIME_SET_VARIABLE:
684 return efi_runtime_set_variable(arg);
686 case EFI_RUNTIME_GET_TIME:
687 return efi_runtime_get_time(arg);
689 case EFI_RUNTIME_SET_TIME:
690 return efi_runtime_set_time(arg);
692 case EFI_RUNTIME_GET_WAKETIME:
693 return efi_runtime_get_waketime(arg);
695 case EFI_RUNTIME_SET_WAKETIME:
696 return efi_runtime_set_waketime(arg);
698 case EFI_RUNTIME_GET_NEXTVARIABLENAME:
699 return efi_runtime_get_nextvariablename(arg);
701 case EFI_RUNTIME_GET_NEXTHIGHMONOTONICCOUNT:
702 return efi_runtime_get_nexthighmonocount(arg);
704 case EFI_RUNTIME_QUERY_VARIABLEINFO:
705 return efi_runtime_query_variableinfo(arg);
707 case EFI_RUNTIME_QUERY_CAPSULECAPABILITIES:
708 return efi_runtime_query_capsulecaps(arg);
710 case EFI_RUNTIME_RESET_SYSTEM:
711 return efi_runtime_reset_system(arg);
717 static int efi_test_open(struct inode *inode, struct file *file)
720 * nothing special to do here
721 * We do accept multiple open files at the same time as we
722 * synchronize on the per call operation.
727 static int efi_test_close(struct inode *inode, struct file *file)
733 * The various file operations we support.
735 static const struct file_operations efi_test_fops = {
736 .owner = THIS_MODULE,
737 .unlocked_ioctl = efi_test_ioctl,
738 .open = efi_test_open,
739 .release = efi_test_close,
743 static struct miscdevice efi_test_dev = {
749 static int __init efi_test_init(void)
753 ret = misc_register(&efi_test_dev);
755 pr_err("efi_test: can't misc_register on minor=%d\n",
763 static void __exit efi_test_exit(void)
765 misc_deregister(&efi_test_dev);
768 module_init(efi_test_init);
769 module_exit(efi_test_exit);