162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * EFI Test Driver for Runtime Services 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright(C) 2012-2016 Canonical Ltd. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This driver exports EFI runtime services interfaces into userspace, which 862306a36Sopenharmony_ci * allow to use and test UEFI runtime services provided by firmware. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/miscdevice.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/proc_fs.h> 1662306a36Sopenharmony_ci#include <linux/efi.h> 1762306a36Sopenharmony_ci#include <linux/security.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/uaccess.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "efi_test.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciMODULE_AUTHOR("Ivan Hu <ivan.hu@canonical.com>"); 2462306a36Sopenharmony_ciMODULE_DESCRIPTION("EFI Test Driver"); 2562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * Count the bytes in 'str', including the terminating NULL. 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * Note this function returns the number of *bytes*, not the number of 3162306a36Sopenharmony_ci * ucs2 characters. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_cistatic inline size_t user_ucs2_strsize(efi_char16_t __user *str) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci efi_char16_t *s = str, c; 3662306a36Sopenharmony_ci size_t len; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (!str) 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* Include terminating NULL */ 4262306a36Sopenharmony_ci len = sizeof(efi_char16_t); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (get_user(c, s++)) { 4562306a36Sopenharmony_ci /* Can't read userspace memory for size */ 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci while (c != 0) { 5062306a36Sopenharmony_ci if (get_user(c, s++)) { 5162306a36Sopenharmony_ci /* Can't read userspace memory for size */ 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci len += sizeof(efi_char16_t); 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci return len; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* 6062306a36Sopenharmony_ci * Allocate a buffer and copy a ucs2 string from user space into it. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistatic inline int 6362306a36Sopenharmony_cicopy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src, 6462306a36Sopenharmony_ci size_t len) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci efi_char16_t *buf; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (!src) { 6962306a36Sopenharmony_ci *dst = NULL; 7062306a36Sopenharmony_ci return 0; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci buf = memdup_user(src, len); 7462306a36Sopenharmony_ci if (IS_ERR(buf)) { 7562306a36Sopenharmony_ci *dst = NULL; 7662306a36Sopenharmony_ci return PTR_ERR(buf); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci *dst = buf; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* 8462306a36Sopenharmony_ci * Count the bytes in 'str', including the terminating NULL. 8562306a36Sopenharmony_ci * 8662306a36Sopenharmony_ci * Just a wrap for user_ucs2_strsize 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_cistatic inline int 8962306a36Sopenharmony_ciget_ucs2_strsize_from_user(efi_char16_t __user *src, size_t *len) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci *len = user_ucs2_strsize(src); 9262306a36Sopenharmony_ci if (*len == 0) 9362306a36Sopenharmony_ci return -EFAULT; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * Calculate the required buffer allocation size and copy a ucs2 string 10062306a36Sopenharmony_ci * from user space into it. 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * This function differs from copy_ucs2_from_user_len() because it 10362306a36Sopenharmony_ci * calculates the size of the buffer to allocate by taking the length of 10462306a36Sopenharmony_ci * the string 'src'. 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * If a non-zero value is returned, the caller MUST NOT access 'dst'. 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * It is the caller's responsibility to free 'dst'. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_cistatic inline int 11162306a36Sopenharmony_cicopy_ucs2_from_user(efi_char16_t **dst, efi_char16_t __user *src) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci size_t len; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci len = user_ucs2_strsize(src); 11662306a36Sopenharmony_ci if (len == 0) 11762306a36Sopenharmony_ci return -EFAULT; 11862306a36Sopenharmony_ci return copy_ucs2_from_user_len(dst, src, len); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * Copy a ucs2 string to a user buffer. 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * This function is a simple wrapper around copy_to_user() that does 12562306a36Sopenharmony_ci * nothing if 'src' is NULL, which is useful for reducing the amount of 12662306a36Sopenharmony_ci * NULL checking the caller has to do. 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * 'len' specifies the number of bytes to copy. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic inline int 13162306a36Sopenharmony_cicopy_ucs2_to_user_len(efi_char16_t __user *dst, efi_char16_t *src, size_t len) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci if (!src) 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return copy_to_user(dst, src, len); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic long efi_runtime_get_variable(unsigned long arg) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct efi_getvariable __user *getvariable_user; 14262306a36Sopenharmony_ci struct efi_getvariable getvariable; 14362306a36Sopenharmony_ci unsigned long datasize = 0, prev_datasize, *dz; 14462306a36Sopenharmony_ci efi_guid_t vendor_guid, *vd = NULL; 14562306a36Sopenharmony_ci efi_status_t status; 14662306a36Sopenharmony_ci efi_char16_t *name = NULL; 14762306a36Sopenharmony_ci u32 attr, *at; 14862306a36Sopenharmony_ci void *data = NULL; 14962306a36Sopenharmony_ci int rv = 0; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci getvariable_user = (struct efi_getvariable __user *)arg; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (copy_from_user(&getvariable, getvariable_user, 15462306a36Sopenharmony_ci sizeof(getvariable))) 15562306a36Sopenharmony_ci return -EFAULT; 15662306a36Sopenharmony_ci if (getvariable.data_size && 15762306a36Sopenharmony_ci get_user(datasize, getvariable.data_size)) 15862306a36Sopenharmony_ci return -EFAULT; 15962306a36Sopenharmony_ci if (getvariable.vendor_guid) { 16062306a36Sopenharmony_ci if (copy_from_user(&vendor_guid, getvariable.vendor_guid, 16162306a36Sopenharmony_ci sizeof(vendor_guid))) 16262306a36Sopenharmony_ci return -EFAULT; 16362306a36Sopenharmony_ci vd = &vendor_guid; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (getvariable.variable_name) { 16762306a36Sopenharmony_ci rv = copy_ucs2_from_user(&name, getvariable.variable_name); 16862306a36Sopenharmony_ci if (rv) 16962306a36Sopenharmony_ci return rv; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci at = getvariable.attributes ? &attr : NULL; 17362306a36Sopenharmony_ci dz = getvariable.data_size ? &datasize : NULL; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (getvariable.data_size && getvariable.data) { 17662306a36Sopenharmony_ci data = kmalloc(datasize, GFP_KERNEL); 17762306a36Sopenharmony_ci if (!data) { 17862306a36Sopenharmony_ci kfree(name); 17962306a36Sopenharmony_ci return -ENOMEM; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci prev_datasize = datasize; 18462306a36Sopenharmony_ci status = efi.get_variable(name, vd, at, dz, data); 18562306a36Sopenharmony_ci kfree(name); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (put_user(status, getvariable.status)) { 18862306a36Sopenharmony_ci rv = -EFAULT; 18962306a36Sopenharmony_ci goto out; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (status != EFI_SUCCESS) { 19362306a36Sopenharmony_ci if (status == EFI_BUFFER_TOO_SMALL) { 19462306a36Sopenharmony_ci if (dz && put_user(datasize, getvariable.data_size)) { 19562306a36Sopenharmony_ci rv = -EFAULT; 19662306a36Sopenharmony_ci goto out; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci rv = -EINVAL; 20062306a36Sopenharmony_ci goto out; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (prev_datasize < datasize) { 20462306a36Sopenharmony_ci rv = -EINVAL; 20562306a36Sopenharmony_ci goto out; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (data) { 20962306a36Sopenharmony_ci if (copy_to_user(getvariable.data, data, datasize)) { 21062306a36Sopenharmony_ci rv = -EFAULT; 21162306a36Sopenharmony_ci goto out; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (at && put_user(attr, getvariable.attributes)) { 21662306a36Sopenharmony_ci rv = -EFAULT; 21762306a36Sopenharmony_ci goto out; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (dz && put_user(datasize, getvariable.data_size)) 22162306a36Sopenharmony_ci rv = -EFAULT; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ciout: 22462306a36Sopenharmony_ci kfree(data); 22562306a36Sopenharmony_ci return rv; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic long efi_runtime_set_variable(unsigned long arg) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct efi_setvariable __user *setvariable_user; 23262306a36Sopenharmony_ci struct efi_setvariable setvariable; 23362306a36Sopenharmony_ci efi_guid_t vendor_guid; 23462306a36Sopenharmony_ci efi_status_t status; 23562306a36Sopenharmony_ci efi_char16_t *name = NULL; 23662306a36Sopenharmony_ci void *data; 23762306a36Sopenharmony_ci int rv = 0; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci setvariable_user = (struct efi_setvariable __user *)arg; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (copy_from_user(&setvariable, setvariable_user, sizeof(setvariable))) 24262306a36Sopenharmony_ci return -EFAULT; 24362306a36Sopenharmony_ci if (copy_from_user(&vendor_guid, setvariable.vendor_guid, 24462306a36Sopenharmony_ci sizeof(vendor_guid))) 24562306a36Sopenharmony_ci return -EFAULT; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (setvariable.variable_name) { 24862306a36Sopenharmony_ci rv = copy_ucs2_from_user(&name, setvariable.variable_name); 24962306a36Sopenharmony_ci if (rv) 25062306a36Sopenharmony_ci return rv; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci data = memdup_user(setvariable.data, setvariable.data_size); 25462306a36Sopenharmony_ci if (IS_ERR(data)) { 25562306a36Sopenharmony_ci kfree(name); 25662306a36Sopenharmony_ci return PTR_ERR(data); 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci status = efi.set_variable(name, &vendor_guid, 26062306a36Sopenharmony_ci setvariable.attributes, 26162306a36Sopenharmony_ci setvariable.data_size, data); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (put_user(status, setvariable.status)) { 26462306a36Sopenharmony_ci rv = -EFAULT; 26562306a36Sopenharmony_ci goto out; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci rv = status == EFI_SUCCESS ? 0 : -EINVAL; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ciout: 27162306a36Sopenharmony_ci kfree(data); 27262306a36Sopenharmony_ci kfree(name); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return rv; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic long efi_runtime_get_time(unsigned long arg) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct efi_gettime __user *gettime_user; 28062306a36Sopenharmony_ci struct efi_gettime gettime; 28162306a36Sopenharmony_ci efi_status_t status; 28262306a36Sopenharmony_ci efi_time_cap_t cap; 28362306a36Sopenharmony_ci efi_time_t efi_time; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci gettime_user = (struct efi_gettime __user *)arg; 28662306a36Sopenharmony_ci if (copy_from_user(&gettime, gettime_user, sizeof(gettime))) 28762306a36Sopenharmony_ci return -EFAULT; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci status = efi.get_time(gettime.time ? &efi_time : NULL, 29062306a36Sopenharmony_ci gettime.capabilities ? &cap : NULL); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (put_user(status, gettime.status)) 29362306a36Sopenharmony_ci return -EFAULT; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (status != EFI_SUCCESS) 29662306a36Sopenharmony_ci return -EINVAL; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (gettime.capabilities) { 29962306a36Sopenharmony_ci efi_time_cap_t __user *cap_local; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci cap_local = (efi_time_cap_t *)gettime.capabilities; 30262306a36Sopenharmony_ci if (put_user(cap.resolution, &(cap_local->resolution)) || 30362306a36Sopenharmony_ci put_user(cap.accuracy, &(cap_local->accuracy)) || 30462306a36Sopenharmony_ci put_user(cap.sets_to_zero, &(cap_local->sets_to_zero))) 30562306a36Sopenharmony_ci return -EFAULT; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci if (gettime.time) { 30862306a36Sopenharmony_ci if (copy_to_user(gettime.time, &efi_time, sizeof(efi_time_t))) 30962306a36Sopenharmony_ci return -EFAULT; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic long efi_runtime_set_time(unsigned long arg) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct efi_settime __user *settime_user; 31862306a36Sopenharmony_ci struct efi_settime settime; 31962306a36Sopenharmony_ci efi_status_t status; 32062306a36Sopenharmony_ci efi_time_t efi_time; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci settime_user = (struct efi_settime __user *)arg; 32362306a36Sopenharmony_ci if (copy_from_user(&settime, settime_user, sizeof(settime))) 32462306a36Sopenharmony_ci return -EFAULT; 32562306a36Sopenharmony_ci if (copy_from_user(&efi_time, settime.time, 32662306a36Sopenharmony_ci sizeof(efi_time_t))) 32762306a36Sopenharmony_ci return -EFAULT; 32862306a36Sopenharmony_ci status = efi.set_time(&efi_time); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (put_user(status, settime.status)) 33162306a36Sopenharmony_ci return -EFAULT; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return status == EFI_SUCCESS ? 0 : -EINVAL; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic long efi_runtime_get_waketime(unsigned long arg) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct efi_getwakeuptime __user *getwakeuptime_user; 33962306a36Sopenharmony_ci struct efi_getwakeuptime getwakeuptime; 34062306a36Sopenharmony_ci efi_bool_t enabled, pending; 34162306a36Sopenharmony_ci efi_status_t status; 34262306a36Sopenharmony_ci efi_time_t efi_time; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci getwakeuptime_user = (struct efi_getwakeuptime __user *)arg; 34562306a36Sopenharmony_ci if (copy_from_user(&getwakeuptime, getwakeuptime_user, 34662306a36Sopenharmony_ci sizeof(getwakeuptime))) 34762306a36Sopenharmony_ci return -EFAULT; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci status = efi.get_wakeup_time( 35062306a36Sopenharmony_ci getwakeuptime.enabled ? (efi_bool_t *)&enabled : NULL, 35162306a36Sopenharmony_ci getwakeuptime.pending ? (efi_bool_t *)&pending : NULL, 35262306a36Sopenharmony_ci getwakeuptime.time ? &efi_time : NULL); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (put_user(status, getwakeuptime.status)) 35562306a36Sopenharmony_ci return -EFAULT; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (status != EFI_SUCCESS) 35862306a36Sopenharmony_ci return -EINVAL; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (getwakeuptime.enabled && put_user(enabled, 36162306a36Sopenharmony_ci getwakeuptime.enabled)) 36262306a36Sopenharmony_ci return -EFAULT; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (getwakeuptime.time) { 36562306a36Sopenharmony_ci if (copy_to_user(getwakeuptime.time, &efi_time, 36662306a36Sopenharmony_ci sizeof(efi_time_t))) 36762306a36Sopenharmony_ci return -EFAULT; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic long efi_runtime_set_waketime(unsigned long arg) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct efi_setwakeuptime __user *setwakeuptime_user; 37662306a36Sopenharmony_ci struct efi_setwakeuptime setwakeuptime; 37762306a36Sopenharmony_ci efi_bool_t enabled; 37862306a36Sopenharmony_ci efi_status_t status; 37962306a36Sopenharmony_ci efi_time_t efi_time; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci setwakeuptime_user = (struct efi_setwakeuptime __user *)arg; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (copy_from_user(&setwakeuptime, setwakeuptime_user, 38462306a36Sopenharmony_ci sizeof(setwakeuptime))) 38562306a36Sopenharmony_ci return -EFAULT; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci enabled = setwakeuptime.enabled; 38862306a36Sopenharmony_ci if (setwakeuptime.time) { 38962306a36Sopenharmony_ci if (copy_from_user(&efi_time, setwakeuptime.time, 39062306a36Sopenharmony_ci sizeof(efi_time_t))) 39162306a36Sopenharmony_ci return -EFAULT; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci status = efi.set_wakeup_time(enabled, &efi_time); 39462306a36Sopenharmony_ci } else 39562306a36Sopenharmony_ci status = efi.set_wakeup_time(enabled, NULL); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (put_user(status, setwakeuptime.status)) 39862306a36Sopenharmony_ci return -EFAULT; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return status == EFI_SUCCESS ? 0 : -EINVAL; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic long efi_runtime_get_nextvariablename(unsigned long arg) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct efi_getnextvariablename __user *getnextvariablename_user; 40662306a36Sopenharmony_ci struct efi_getnextvariablename getnextvariablename; 40762306a36Sopenharmony_ci unsigned long name_size, prev_name_size = 0, *ns = NULL; 40862306a36Sopenharmony_ci efi_status_t status; 40962306a36Sopenharmony_ci efi_guid_t *vd = NULL; 41062306a36Sopenharmony_ci efi_guid_t vendor_guid; 41162306a36Sopenharmony_ci efi_char16_t *name = NULL; 41262306a36Sopenharmony_ci int rv = 0; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci getnextvariablename_user = (struct efi_getnextvariablename __user *)arg; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (copy_from_user(&getnextvariablename, getnextvariablename_user, 41762306a36Sopenharmony_ci sizeof(getnextvariablename))) 41862306a36Sopenharmony_ci return -EFAULT; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (getnextvariablename.variable_name_size) { 42162306a36Sopenharmony_ci if (get_user(name_size, getnextvariablename.variable_name_size)) 42262306a36Sopenharmony_ci return -EFAULT; 42362306a36Sopenharmony_ci ns = &name_size; 42462306a36Sopenharmony_ci prev_name_size = name_size; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (getnextvariablename.vendor_guid) { 42862306a36Sopenharmony_ci if (copy_from_user(&vendor_guid, 42962306a36Sopenharmony_ci getnextvariablename.vendor_guid, 43062306a36Sopenharmony_ci sizeof(vendor_guid))) 43162306a36Sopenharmony_ci return -EFAULT; 43262306a36Sopenharmony_ci vd = &vendor_guid; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (getnextvariablename.variable_name) { 43662306a36Sopenharmony_ci size_t name_string_size = 0; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci rv = get_ucs2_strsize_from_user( 43962306a36Sopenharmony_ci getnextvariablename.variable_name, 44062306a36Sopenharmony_ci &name_string_size); 44162306a36Sopenharmony_ci if (rv) 44262306a36Sopenharmony_ci return rv; 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * The name_size may be smaller than the real buffer size where 44562306a36Sopenharmony_ci * variable name located in some use cases. The most typical 44662306a36Sopenharmony_ci * case is passing a 0 to get the required buffer size for the 44762306a36Sopenharmony_ci * 1st time call. So we need to copy the content from user 44862306a36Sopenharmony_ci * space for at least the string size of variable name, or else 44962306a36Sopenharmony_ci * the name passed to UEFI may not be terminated as we expected. 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci rv = copy_ucs2_from_user_len(&name, 45262306a36Sopenharmony_ci getnextvariablename.variable_name, 45362306a36Sopenharmony_ci prev_name_size > name_string_size ? 45462306a36Sopenharmony_ci prev_name_size : name_string_size); 45562306a36Sopenharmony_ci if (rv) 45662306a36Sopenharmony_ci return rv; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci status = efi.get_next_variable(ns, name, vd); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (put_user(status, getnextvariablename.status)) { 46262306a36Sopenharmony_ci rv = -EFAULT; 46362306a36Sopenharmony_ci goto out; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (status != EFI_SUCCESS) { 46762306a36Sopenharmony_ci if (status == EFI_BUFFER_TOO_SMALL) { 46862306a36Sopenharmony_ci if (ns && put_user(*ns, 46962306a36Sopenharmony_ci getnextvariablename.variable_name_size)) { 47062306a36Sopenharmony_ci rv = -EFAULT; 47162306a36Sopenharmony_ci goto out; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci rv = -EINVAL; 47562306a36Sopenharmony_ci goto out; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (name) { 47962306a36Sopenharmony_ci if (copy_ucs2_to_user_len(getnextvariablename.variable_name, 48062306a36Sopenharmony_ci name, prev_name_size)) { 48162306a36Sopenharmony_ci rv = -EFAULT; 48262306a36Sopenharmony_ci goto out; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (ns) { 48762306a36Sopenharmony_ci if (put_user(*ns, getnextvariablename.variable_name_size)) { 48862306a36Sopenharmony_ci rv = -EFAULT; 48962306a36Sopenharmony_ci goto out; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (vd) { 49462306a36Sopenharmony_ci if (copy_to_user(getnextvariablename.vendor_guid, vd, 49562306a36Sopenharmony_ci sizeof(efi_guid_t))) 49662306a36Sopenharmony_ci rv = -EFAULT; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ciout: 50062306a36Sopenharmony_ci kfree(name); 50162306a36Sopenharmony_ci return rv; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic long efi_runtime_get_nexthighmonocount(unsigned long arg) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct efi_getnexthighmonotoniccount __user *getnexthighmonocount_user; 50762306a36Sopenharmony_ci struct efi_getnexthighmonotoniccount getnexthighmonocount; 50862306a36Sopenharmony_ci efi_status_t status; 50962306a36Sopenharmony_ci u32 count; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci getnexthighmonocount_user = (struct 51262306a36Sopenharmony_ci efi_getnexthighmonotoniccount __user *)arg; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (copy_from_user(&getnexthighmonocount, 51562306a36Sopenharmony_ci getnexthighmonocount_user, 51662306a36Sopenharmony_ci sizeof(getnexthighmonocount))) 51762306a36Sopenharmony_ci return -EFAULT; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci status = efi.get_next_high_mono_count( 52062306a36Sopenharmony_ci getnexthighmonocount.high_count ? &count : NULL); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (put_user(status, getnexthighmonocount.status)) 52362306a36Sopenharmony_ci return -EFAULT; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (status != EFI_SUCCESS) 52662306a36Sopenharmony_ci return -EINVAL; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (getnexthighmonocount.high_count && 52962306a36Sopenharmony_ci put_user(count, getnexthighmonocount.high_count)) 53062306a36Sopenharmony_ci return -EFAULT; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic long efi_runtime_reset_system(unsigned long arg) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct efi_resetsystem __user *resetsystem_user; 53862306a36Sopenharmony_ci struct efi_resetsystem resetsystem; 53962306a36Sopenharmony_ci void *data = NULL; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci resetsystem_user = (struct efi_resetsystem __user *)arg; 54262306a36Sopenharmony_ci if (copy_from_user(&resetsystem, resetsystem_user, 54362306a36Sopenharmony_ci sizeof(resetsystem))) 54462306a36Sopenharmony_ci return -EFAULT; 54562306a36Sopenharmony_ci if (resetsystem.data_size != 0) { 54662306a36Sopenharmony_ci data = memdup_user((void *)resetsystem.data, 54762306a36Sopenharmony_ci resetsystem.data_size); 54862306a36Sopenharmony_ci if (IS_ERR(data)) 54962306a36Sopenharmony_ci return PTR_ERR(data); 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci efi.reset_system(resetsystem.reset_type, resetsystem.status, 55362306a36Sopenharmony_ci resetsystem.data_size, (efi_char16_t *)data); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci kfree(data); 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic long efi_runtime_query_variableinfo(unsigned long arg) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct efi_queryvariableinfo __user *queryvariableinfo_user; 56262306a36Sopenharmony_ci struct efi_queryvariableinfo queryvariableinfo; 56362306a36Sopenharmony_ci efi_status_t status; 56462306a36Sopenharmony_ci u64 max_storage, remaining, max_size; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci queryvariableinfo_user = (struct efi_queryvariableinfo __user *)arg; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (copy_from_user(&queryvariableinfo, queryvariableinfo_user, 56962306a36Sopenharmony_ci sizeof(queryvariableinfo))) 57062306a36Sopenharmony_ci return -EFAULT; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci status = efi.query_variable_info(queryvariableinfo.attributes, 57362306a36Sopenharmony_ci &max_storage, &remaining, &max_size); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (put_user(status, queryvariableinfo.status)) 57662306a36Sopenharmony_ci return -EFAULT; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (status != EFI_SUCCESS) 57962306a36Sopenharmony_ci return -EINVAL; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (put_user(max_storage, 58262306a36Sopenharmony_ci queryvariableinfo.maximum_variable_storage_size)) 58362306a36Sopenharmony_ci return -EFAULT; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (put_user(remaining, 58662306a36Sopenharmony_ci queryvariableinfo.remaining_variable_storage_size)) 58762306a36Sopenharmony_ci return -EFAULT; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (put_user(max_size, queryvariableinfo.maximum_variable_size)) 59062306a36Sopenharmony_ci return -EFAULT; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic long efi_runtime_query_capsulecaps(unsigned long arg) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct efi_querycapsulecapabilities __user *qcaps_user; 59862306a36Sopenharmony_ci struct efi_querycapsulecapabilities qcaps; 59962306a36Sopenharmony_ci efi_capsule_header_t *capsules; 60062306a36Sopenharmony_ci efi_status_t status; 60162306a36Sopenharmony_ci u64 max_size; 60262306a36Sopenharmony_ci int i, reset_type; 60362306a36Sopenharmony_ci int rv = 0; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci qcaps_user = (struct efi_querycapsulecapabilities __user *)arg; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (copy_from_user(&qcaps, qcaps_user, sizeof(qcaps))) 60862306a36Sopenharmony_ci return -EFAULT; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (qcaps.capsule_count == ULONG_MAX) 61162306a36Sopenharmony_ci return -EINVAL; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci capsules = kcalloc(qcaps.capsule_count + 1, 61462306a36Sopenharmony_ci sizeof(efi_capsule_header_t), GFP_KERNEL); 61562306a36Sopenharmony_ci if (!capsules) 61662306a36Sopenharmony_ci return -ENOMEM; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci for (i = 0; i < qcaps.capsule_count; i++) { 61962306a36Sopenharmony_ci efi_capsule_header_t *c; 62062306a36Sopenharmony_ci /* 62162306a36Sopenharmony_ci * We cannot dereference qcaps.capsule_header_array directly to 62262306a36Sopenharmony_ci * obtain the address of the capsule as it resides in the 62362306a36Sopenharmony_ci * user space 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_ci if (get_user(c, qcaps.capsule_header_array + i)) { 62662306a36Sopenharmony_ci rv = -EFAULT; 62762306a36Sopenharmony_ci goto out; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci if (copy_from_user(&capsules[i], c, 63062306a36Sopenharmony_ci sizeof(efi_capsule_header_t))) { 63162306a36Sopenharmony_ci rv = -EFAULT; 63262306a36Sopenharmony_ci goto out; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci qcaps.capsule_header_array = &capsules; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci status = efi.query_capsule_caps((efi_capsule_header_t **) 63962306a36Sopenharmony_ci qcaps.capsule_header_array, 64062306a36Sopenharmony_ci qcaps.capsule_count, 64162306a36Sopenharmony_ci &max_size, &reset_type); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (put_user(status, qcaps.status)) { 64462306a36Sopenharmony_ci rv = -EFAULT; 64562306a36Sopenharmony_ci goto out; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (status != EFI_SUCCESS) { 64962306a36Sopenharmony_ci rv = -EINVAL; 65062306a36Sopenharmony_ci goto out; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (put_user(max_size, qcaps.maximum_capsule_size)) { 65462306a36Sopenharmony_ci rv = -EFAULT; 65562306a36Sopenharmony_ci goto out; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (put_user(reset_type, qcaps.reset_type)) 65962306a36Sopenharmony_ci rv = -EFAULT; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ciout: 66262306a36Sopenharmony_ci kfree(capsules); 66362306a36Sopenharmony_ci return rv; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic long efi_runtime_get_supported_mask(unsigned long arg) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci unsigned int __user *supported_mask; 66962306a36Sopenharmony_ci int rv = 0; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci supported_mask = (unsigned int *)arg; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (put_user(efi.runtime_supported_mask, supported_mask)) 67462306a36Sopenharmony_ci rv = -EFAULT; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return rv; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic long efi_test_ioctl(struct file *file, unsigned int cmd, 68062306a36Sopenharmony_ci unsigned long arg) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci switch (cmd) { 68362306a36Sopenharmony_ci case EFI_RUNTIME_GET_VARIABLE: 68462306a36Sopenharmony_ci return efi_runtime_get_variable(arg); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci case EFI_RUNTIME_SET_VARIABLE: 68762306a36Sopenharmony_ci return efi_runtime_set_variable(arg); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci case EFI_RUNTIME_GET_TIME: 69062306a36Sopenharmony_ci return efi_runtime_get_time(arg); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci case EFI_RUNTIME_SET_TIME: 69362306a36Sopenharmony_ci return efi_runtime_set_time(arg); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci case EFI_RUNTIME_GET_WAKETIME: 69662306a36Sopenharmony_ci return efi_runtime_get_waketime(arg); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci case EFI_RUNTIME_SET_WAKETIME: 69962306a36Sopenharmony_ci return efi_runtime_set_waketime(arg); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci case EFI_RUNTIME_GET_NEXTVARIABLENAME: 70262306a36Sopenharmony_ci return efi_runtime_get_nextvariablename(arg); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci case EFI_RUNTIME_GET_NEXTHIGHMONOTONICCOUNT: 70562306a36Sopenharmony_ci return efi_runtime_get_nexthighmonocount(arg); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci case EFI_RUNTIME_QUERY_VARIABLEINFO: 70862306a36Sopenharmony_ci return efi_runtime_query_variableinfo(arg); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci case EFI_RUNTIME_QUERY_CAPSULECAPABILITIES: 71162306a36Sopenharmony_ci return efi_runtime_query_capsulecaps(arg); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci case EFI_RUNTIME_RESET_SYSTEM: 71462306a36Sopenharmony_ci return efi_runtime_reset_system(arg); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci case EFI_RUNTIME_GET_SUPPORTED_MASK: 71762306a36Sopenharmony_ci return efi_runtime_get_supported_mask(arg); 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci return -ENOTTY; 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic int efi_test_open(struct inode *inode, struct file *file) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci int ret = security_locked_down(LOCKDOWN_EFI_TEST); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (ret) 72862306a36Sopenharmony_ci return ret; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 73162306a36Sopenharmony_ci return -EACCES; 73262306a36Sopenharmony_ci /* 73362306a36Sopenharmony_ci * nothing special to do here 73462306a36Sopenharmony_ci * We do accept multiple open files at the same time as we 73562306a36Sopenharmony_ci * synchronize on the per call operation. 73662306a36Sopenharmony_ci */ 73762306a36Sopenharmony_ci return 0; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic int efi_test_close(struct inode *inode, struct file *file) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci/* 74662306a36Sopenharmony_ci * The various file operations we support. 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_cistatic const struct file_operations efi_test_fops = { 74962306a36Sopenharmony_ci .owner = THIS_MODULE, 75062306a36Sopenharmony_ci .unlocked_ioctl = efi_test_ioctl, 75162306a36Sopenharmony_ci .open = efi_test_open, 75262306a36Sopenharmony_ci .release = efi_test_close, 75362306a36Sopenharmony_ci .llseek = no_llseek, 75462306a36Sopenharmony_ci}; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic struct miscdevice efi_test_dev = { 75762306a36Sopenharmony_ci MISC_DYNAMIC_MINOR, 75862306a36Sopenharmony_ci "efi_test", 75962306a36Sopenharmony_ci &efi_test_fops 76062306a36Sopenharmony_ci}; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic int __init efi_test_init(void) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci int ret; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci ret = misc_register(&efi_test_dev); 76762306a36Sopenharmony_ci if (ret) { 76862306a36Sopenharmony_ci pr_err("efi_test: can't misc_register on minor=%d\n", 76962306a36Sopenharmony_ci MISC_DYNAMIC_MINOR); 77062306a36Sopenharmony_ci return ret; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci return 0; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic void __exit efi_test_exit(void) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci misc_deregister(&efi_test_dev); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cimodule_init(efi_test_init); 78262306a36Sopenharmony_cimodule_exit(efi_test_exit); 783