brintos

brintos / linux-shallow public Read only

0
0
Text · 17.4 KiB · 9e26287 Raw
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