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