155716d26SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2022ee6c5SArd Biesheuvel /*
3022ee6c5SArd Biesheuvel  * runtime-wrappers.c - Runtime Services function call wrappers
4022ee6c5SArd Biesheuvel  *
53eb420e7SSai Praneeth  * Implementation summary:
63eb420e7SSai Praneeth  * -----------------------
73eb420e7SSai Praneeth  * 1. When user/kernel thread requests to execute efi_runtime_service(),
83eb420e7SSai Praneeth  * enqueue work to efi_rts_wq.
93eb420e7SSai Praneeth  * 2. Caller thread waits for completion until the work is finished
103eb420e7SSai Praneeth  * because it's dependent on the return status and execution of
113eb420e7SSai Praneeth  * efi_runtime_service().
123eb420e7SSai Praneeth  * For instance, get_variable() and get_next_variable().
133eb420e7SSai Praneeth  *
14022ee6c5SArd Biesheuvel  * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
15022ee6c5SArd Biesheuvel  *
16022ee6c5SArd Biesheuvel  * Split off from arch/x86/platform/efi/efi.c
17022ee6c5SArd Biesheuvel  *
18022ee6c5SArd Biesheuvel  * Copyright (C) 1999 VA Linux Systems
19022ee6c5SArd Biesheuvel  * Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
20022ee6c5SArd Biesheuvel  * Copyright (C) 1999-2002 Hewlett-Packard Co.
21022ee6c5SArd Biesheuvel  * Copyright (C) 2005-2008 Intel Co.
22022ee6c5SArd Biesheuvel  * Copyright (C) 2013 SuSE Labs
23022ee6c5SArd Biesheuvel  */
24022ee6c5SArd Biesheuvel 
25dce48e35SArd Biesheuvel #define pr_fmt(fmt)	"efi: " fmt
26dce48e35SArd Biesheuvel 
27161485e8SArd Biesheuvel #include <linux/bug.h>
28022ee6c5SArd Biesheuvel #include <linux/efi.h>
291d04ba17SMark Rutland #include <linux/irqflags.h>
30161485e8SArd Biesheuvel #include <linux/mutex.h>
31dce48e35SArd Biesheuvel #include <linux/semaphore.h>
321d04ba17SMark Rutland #include <linux/stringify.h>
333eb420e7SSai Praneeth #include <linux/workqueue.h>
343eb420e7SSai Praneeth #include <linux/completion.h>
353eb420e7SSai Praneeth 
36022ee6c5SArd Biesheuvel #include <asm/efi.h>
37022ee6c5SArd Biesheuvel 
3880e75596SAlex Thorlton /*
3980e75596SAlex Thorlton  * Wrap around the new efi_call_virt_generic() macros so that the
4080e75596SAlex Thorlton  * code doesn't get too cluttered:
4180e75596SAlex Thorlton  */
4280e75596SAlex Thorlton #define efi_call_virt(f, args...)   \
4380e75596SAlex Thorlton 	efi_call_virt_pointer(efi.systab->runtime, f, args)
4480e75596SAlex Thorlton #define __efi_call_virt(f, args...) \
4580e75596SAlex Thorlton 	__efi_call_virt_pointer(efi.systab->runtime, f, args)
4680e75596SAlex Thorlton 
479dbbedaaSSai Praneeth struct efi_runtime_work efi_rts_work;
483eb420e7SSai Praneeth 
493eb420e7SSai Praneeth /*
503eb420e7SSai Praneeth  * efi_queue_work:	Queue efi_runtime_service() and wait until it's done
513eb420e7SSai Praneeth  * @rts:		efi_runtime_service() function identifier
523eb420e7SSai Praneeth  * @rts_arg<1-5>:	efi_runtime_service() function arguments
533eb420e7SSai Praneeth  *
543eb420e7SSai Praneeth  * Accesses to efi_runtime_services() are serialized by a binary
553eb420e7SSai Praneeth  * semaphore (efi_runtime_lock) and caller waits until the work is
563eb420e7SSai Praneeth  * finished, hence _only_ one work is queued at a time and the caller
573eb420e7SSai Praneeth  * thread waits for completion.
583eb420e7SSai Praneeth  */
593eb420e7SSai Praneeth #define efi_queue_work(_rts, _arg1, _arg2, _arg3, _arg4, _arg5)		\
603eb420e7SSai Praneeth ({									\
613eb420e7SSai Praneeth 	efi_rts_work.status = EFI_ABORTED;				\
623eb420e7SSai Praneeth 									\
633425d934SSai Praneeth 	if (!efi_enabled(EFI_RUNTIME_SERVICES)) {			\
643425d934SSai Praneeth 		pr_warn_once("EFI Runtime Services are disabled!\n");	\
653425d934SSai Praneeth 		goto exit;						\
663425d934SSai Praneeth 	}								\
673425d934SSai Praneeth 									\
683eb420e7SSai Praneeth 	init_completion(&efi_rts_work.efi_rts_comp);			\
69ef1491e7SWaiman Long 	INIT_WORK(&efi_rts_work.work, efi_call_rts);			\
703eb420e7SSai Praneeth 	efi_rts_work.arg1 = _arg1;					\
713eb420e7SSai Praneeth 	efi_rts_work.arg2 = _arg2;					\
723eb420e7SSai Praneeth 	efi_rts_work.arg3 = _arg3;					\
733eb420e7SSai Praneeth 	efi_rts_work.arg4 = _arg4;					\
743eb420e7SSai Praneeth 	efi_rts_work.arg5 = _arg5;					\
753eb420e7SSai Praneeth 	efi_rts_work.efi_rts_id = _rts;					\
763eb420e7SSai Praneeth 									\
773eb420e7SSai Praneeth 	/*								\
783eb420e7SSai Praneeth 	 * queue_work() returns 0 if work was already on queue,         \
793eb420e7SSai Praneeth 	 * _ideally_ this should never happen.                          \
803eb420e7SSai Praneeth 	 */								\
813eb420e7SSai Praneeth 	if (queue_work(efi_rts_wq, &efi_rts_work.work))			\
823eb420e7SSai Praneeth 		wait_for_completion(&efi_rts_work.efi_rts_comp);	\
833eb420e7SSai Praneeth 	else								\
843eb420e7SSai Praneeth 		pr_err("Failed to queue work to efi_rts_wq.\n");	\
853eb420e7SSai Praneeth 									\
863425d934SSai Praneeth exit:									\
875c418dc7SAnders Roxell 	efi_rts_work.efi_rts_id = EFI_NONE;				\
883eb420e7SSai Praneeth 	efi_rts_work.status;						\
893eb420e7SSai Praneeth })
903eb420e7SSai Praneeth 
9113b210ddSJulien Thierry #ifndef arch_efi_save_flags
9213b210ddSJulien Thierry #define arch_efi_save_flags(state_flags)	local_save_flags(state_flags)
9313b210ddSJulien Thierry #define arch_efi_restore_flags(state_flags)	local_irq_restore(state_flags)
9413b210ddSJulien Thierry #endif
9513b210ddSJulien Thierry 
9613b210ddSJulien Thierry unsigned long efi_call_virt_save_flags(void)
9713b210ddSJulien Thierry {
9813b210ddSJulien Thierry 	unsigned long flags;
9913b210ddSJulien Thierry 
10013b210ddSJulien Thierry 	arch_efi_save_flags(flags);
10113b210ddSJulien Thierry 	return flags;
10213b210ddSJulien Thierry }
10313b210ddSJulien Thierry 
10480e75596SAlex Thorlton void efi_call_virt_check_flags(unsigned long flags, const char *call)
1051d04ba17SMark Rutland {
1061d04ba17SMark Rutland 	unsigned long cur_flags, mismatch;
1071d04ba17SMark Rutland 
10813b210ddSJulien Thierry 	cur_flags = efi_call_virt_save_flags();
1091d04ba17SMark Rutland 
1101d04ba17SMark Rutland 	mismatch = flags ^ cur_flags;
1111d04ba17SMark Rutland 	if (!WARN_ON_ONCE(mismatch & ARCH_EFI_IRQ_FLAGS_MASK))
1121d04ba17SMark Rutland 		return;
1131d04ba17SMark Rutland 
1141d04ba17SMark Rutland 	add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE);
1151d04ba17SMark Rutland 	pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI %s\n",
1161d04ba17SMark Rutland 			   flags, cur_flags, call);
11713b210ddSJulien Thierry 	arch_efi_restore_flags(flags);
1181d04ba17SMark Rutland }
1191d04ba17SMark Rutland 
1201d04ba17SMark Rutland /*
121161485e8SArd Biesheuvel  * According to section 7.1 of the UEFI spec, Runtime Services are not fully
122161485e8SArd Biesheuvel  * reentrant, and there are particular combinations of calls that need to be
123161485e8SArd Biesheuvel  * serialized. (source: UEFI Specification v2.4A)
124161485e8SArd Biesheuvel  *
125161485e8SArd Biesheuvel  * Table 31. Rules for Reentry Into Runtime Services
126161485e8SArd Biesheuvel  * +------------------------------------+-------------------------------+
127161485e8SArd Biesheuvel  * | If previous call is busy in	| Forbidden to call		|
128161485e8SArd Biesheuvel  * +------------------------------------+-------------------------------+
129161485e8SArd Biesheuvel  * | Any				| SetVirtualAddressMap()	|
130161485e8SArd Biesheuvel  * +------------------------------------+-------------------------------+
131161485e8SArd Biesheuvel  * | ConvertPointer()			| ConvertPointer()		|
132161485e8SArd Biesheuvel  * +------------------------------------+-------------------------------+
133161485e8SArd Biesheuvel  * | SetVariable()			| ResetSystem()			|
134161485e8SArd Biesheuvel  * | UpdateCapsule()			|				|
135161485e8SArd Biesheuvel  * | SetTime()				|				|
136161485e8SArd Biesheuvel  * | SetWakeupTime()			|				|
137161485e8SArd Biesheuvel  * | GetNextHighMonotonicCount()	|				|
138161485e8SArd Biesheuvel  * +------------------------------------+-------------------------------+
139161485e8SArd Biesheuvel  * | GetVariable()			| GetVariable()			|
140161485e8SArd Biesheuvel  * | GetNextVariableName()		| GetNextVariableName()		|
141161485e8SArd Biesheuvel  * | SetVariable()			| SetVariable()			|
142161485e8SArd Biesheuvel  * | QueryVariableInfo()		| QueryVariableInfo()		|
143161485e8SArd Biesheuvel  * | UpdateCapsule()			| UpdateCapsule()		|
144161485e8SArd Biesheuvel  * | QueryCapsuleCapabilities()		| QueryCapsuleCapabilities()	|
145161485e8SArd Biesheuvel  * | GetNextHighMonotonicCount()	| GetNextHighMonotonicCount()	|
146161485e8SArd Biesheuvel  * +------------------------------------+-------------------------------+
147161485e8SArd Biesheuvel  * | GetTime()				| GetTime()			|
148161485e8SArd Biesheuvel  * | SetTime()				| SetTime()			|
149161485e8SArd Biesheuvel  * | GetWakeupTime()			| GetWakeupTime()		|
150161485e8SArd Biesheuvel  * | SetWakeupTime()			| SetWakeupTime()		|
151161485e8SArd Biesheuvel  * +------------------------------------+-------------------------------+
152161485e8SArd Biesheuvel  *
153161485e8SArd Biesheuvel  * Due to the fact that the EFI pstore may write to the variable store in
154dce48e35SArd Biesheuvel  * interrupt context, we need to use a lock for at least the groups that
155161485e8SArd Biesheuvel  * contain SetVariable() and QueryVariableInfo(). That leaves little else, as
156161485e8SArd Biesheuvel  * none of the remaining functions are actually ever called at runtime.
157dce48e35SArd Biesheuvel  * So let's just use a single lock to serialize all Runtime Services calls.
158161485e8SArd Biesheuvel  */
159dce48e35SArd Biesheuvel static DEFINE_SEMAPHORE(efi_runtime_lock);
160161485e8SArd Biesheuvel 
1613eb420e7SSai Praneeth /*
162f331e766SHedi Berriche  * Expose the EFI runtime lock to the UV platform
163f331e766SHedi Berriche  */
164f331e766SHedi Berriche #ifdef CONFIG_X86_UV
165f331e766SHedi Berriche extern struct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock);
166f331e766SHedi Berriche #endif
167f331e766SHedi Berriche 
168f331e766SHedi Berriche /*
1693eb420e7SSai Praneeth  * Calls the appropriate efi_runtime_service() with the appropriate
1703eb420e7SSai Praneeth  * arguments.
1713eb420e7SSai Praneeth  *
1723eb420e7SSai Praneeth  * Semantics followed by efi_call_rts() to understand efi_runtime_work:
1733eb420e7SSai Praneeth  * 1. If argument was a pointer, recast it from void pointer to original
1743eb420e7SSai Praneeth  * pointer type.
1753eb420e7SSai Praneeth  * 2. If argument was a value, recast it from void pointer to original
1763eb420e7SSai Praneeth  * pointer type and dereference it.
1773eb420e7SSai Praneeth  */
1783eb420e7SSai Praneeth static void efi_call_rts(struct work_struct *work)
1793eb420e7SSai Praneeth {
1803eb420e7SSai Praneeth 	void *arg1, *arg2, *arg3, *arg4, *arg5;
1813eb420e7SSai Praneeth 	efi_status_t status = EFI_NOT_FOUND;
1823eb420e7SSai Praneeth 
1839dbbedaaSSai Praneeth 	arg1 = efi_rts_work.arg1;
1849dbbedaaSSai Praneeth 	arg2 = efi_rts_work.arg2;
1859dbbedaaSSai Praneeth 	arg3 = efi_rts_work.arg3;
1869dbbedaaSSai Praneeth 	arg4 = efi_rts_work.arg4;
1879dbbedaaSSai Praneeth 	arg5 = efi_rts_work.arg5;
1883eb420e7SSai Praneeth 
1899dbbedaaSSai Praneeth 	switch (efi_rts_work.efi_rts_id) {
1905c418dc7SAnders Roxell 	case EFI_GET_TIME:
1913eb420e7SSai Praneeth 		status = efi_call_virt(get_time, (efi_time_t *)arg1,
1923eb420e7SSai Praneeth 				       (efi_time_cap_t *)arg2);
1933eb420e7SSai Praneeth 		break;
1945c418dc7SAnders Roxell 	case EFI_SET_TIME:
1953eb420e7SSai Praneeth 		status = efi_call_virt(set_time, (efi_time_t *)arg1);
1963eb420e7SSai Praneeth 		break;
1975c418dc7SAnders Roxell 	case EFI_GET_WAKEUP_TIME:
1983eb420e7SSai Praneeth 		status = efi_call_virt(get_wakeup_time, (efi_bool_t *)arg1,
1993eb420e7SSai Praneeth 				       (efi_bool_t *)arg2, (efi_time_t *)arg3);
2003eb420e7SSai Praneeth 		break;
2015c418dc7SAnders Roxell 	case EFI_SET_WAKEUP_TIME:
2023eb420e7SSai Praneeth 		status = efi_call_virt(set_wakeup_time, *(efi_bool_t *)arg1,
2033eb420e7SSai Praneeth 				       (efi_time_t *)arg2);
2043eb420e7SSai Praneeth 		break;
2055c418dc7SAnders Roxell 	case EFI_GET_VARIABLE:
2063eb420e7SSai Praneeth 		status = efi_call_virt(get_variable, (efi_char16_t *)arg1,
2073eb420e7SSai Praneeth 				       (efi_guid_t *)arg2, (u32 *)arg3,
2083eb420e7SSai Praneeth 				       (unsigned long *)arg4, (void *)arg5);
2093eb420e7SSai Praneeth 		break;
2105c418dc7SAnders Roxell 	case EFI_GET_NEXT_VARIABLE:
2113eb420e7SSai Praneeth 		status = efi_call_virt(get_next_variable, (unsigned long *)arg1,
2123eb420e7SSai Praneeth 				       (efi_char16_t *)arg2,
2133eb420e7SSai Praneeth 				       (efi_guid_t *)arg3);
2143eb420e7SSai Praneeth 		break;
2155c418dc7SAnders Roxell 	case EFI_SET_VARIABLE:
2163eb420e7SSai Praneeth 		status = efi_call_virt(set_variable, (efi_char16_t *)arg1,
2173eb420e7SSai Praneeth 				       (efi_guid_t *)arg2, *(u32 *)arg3,
2183eb420e7SSai Praneeth 				       *(unsigned long *)arg4, (void *)arg5);
2193eb420e7SSai Praneeth 		break;
2205c418dc7SAnders Roxell 	case EFI_QUERY_VARIABLE_INFO:
2213eb420e7SSai Praneeth 		status = efi_call_virt(query_variable_info, *(u32 *)arg1,
2223eb420e7SSai Praneeth 				       (u64 *)arg2, (u64 *)arg3, (u64 *)arg4);
2233eb420e7SSai Praneeth 		break;
2245c418dc7SAnders Roxell 	case EFI_GET_NEXT_HIGH_MONO_COUNT:
2253eb420e7SSai Praneeth 		status = efi_call_virt(get_next_high_mono_count, (u32 *)arg1);
2263eb420e7SSai Praneeth 		break;
2275c418dc7SAnders Roxell 	case EFI_UPDATE_CAPSULE:
2283eb420e7SSai Praneeth 		status = efi_call_virt(update_capsule,
2293eb420e7SSai Praneeth 				       (efi_capsule_header_t **)arg1,
2303eb420e7SSai Praneeth 				       *(unsigned long *)arg2,
2313eb420e7SSai Praneeth 				       *(unsigned long *)arg3);
2323eb420e7SSai Praneeth 		break;
2335c418dc7SAnders Roxell 	case EFI_QUERY_CAPSULE_CAPS:
2343eb420e7SSai Praneeth 		status = efi_call_virt(query_capsule_caps,
2353eb420e7SSai Praneeth 				       (efi_capsule_header_t **)arg1,
2363eb420e7SSai Praneeth 				       *(unsigned long *)arg2, (u64 *)arg3,
2373eb420e7SSai Praneeth 				       (int *)arg4);
2383eb420e7SSai Praneeth 		break;
2393eb420e7SSai Praneeth 	default:
2403eb420e7SSai Praneeth 		/*
2413eb420e7SSai Praneeth 		 * Ideally, we should never reach here because a caller of this
2423eb420e7SSai Praneeth 		 * function should have put the right efi_runtime_service()
2433eb420e7SSai Praneeth 		 * function identifier into efi_rts_work->efi_rts_id
2443eb420e7SSai Praneeth 		 */
2453eb420e7SSai Praneeth 		pr_err("Requested executing invalid EFI Runtime Service.\n");
2463eb420e7SSai Praneeth 	}
2479dbbedaaSSai Praneeth 	efi_rts_work.status = status;
2489dbbedaaSSai Praneeth 	complete(&efi_rts_work.efi_rts_comp);
2493eb420e7SSai Praneeth }
2503eb420e7SSai Praneeth 
251022ee6c5SArd Biesheuvel static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
252022ee6c5SArd Biesheuvel {
253022ee6c5SArd Biesheuvel 	efi_status_t status;
254022ee6c5SArd Biesheuvel 
255dce48e35SArd Biesheuvel 	if (down_interruptible(&efi_runtime_lock))
256dce48e35SArd Biesheuvel 		return EFI_ABORTED;
2575c418dc7SAnders Roxell 	status = efi_queue_work(EFI_GET_TIME, tm, tc, NULL, NULL, NULL);
258dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
259022ee6c5SArd Biesheuvel 	return status;
260022ee6c5SArd Biesheuvel }
261022ee6c5SArd Biesheuvel 
262022ee6c5SArd Biesheuvel static efi_status_t virt_efi_set_time(efi_time_t *tm)
263022ee6c5SArd Biesheuvel {
264022ee6c5SArd Biesheuvel 	efi_status_t status;
265022ee6c5SArd Biesheuvel 
266dce48e35SArd Biesheuvel 	if (down_interruptible(&efi_runtime_lock))
267dce48e35SArd Biesheuvel 		return EFI_ABORTED;
2685c418dc7SAnders Roxell 	status = efi_queue_work(EFI_SET_TIME, tm, NULL, NULL, NULL, NULL);
269dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
270022ee6c5SArd Biesheuvel 	return status;
271022ee6c5SArd Biesheuvel }
272022ee6c5SArd Biesheuvel 
273022ee6c5SArd Biesheuvel static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
274022ee6c5SArd Biesheuvel 					     efi_bool_t *pending,
275022ee6c5SArd Biesheuvel 					     efi_time_t *tm)
276022ee6c5SArd Biesheuvel {
277022ee6c5SArd Biesheuvel 	efi_status_t status;
278022ee6c5SArd Biesheuvel 
279dce48e35SArd Biesheuvel 	if (down_interruptible(&efi_runtime_lock))
280dce48e35SArd Biesheuvel 		return EFI_ABORTED;
2815c418dc7SAnders Roxell 	status = efi_queue_work(EFI_GET_WAKEUP_TIME, enabled, pending, tm, NULL,
2823eb420e7SSai Praneeth 				NULL);
283dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
284022ee6c5SArd Biesheuvel 	return status;
285022ee6c5SArd Biesheuvel }
286022ee6c5SArd Biesheuvel 
287022ee6c5SArd Biesheuvel static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
288022ee6c5SArd Biesheuvel {
289022ee6c5SArd Biesheuvel 	efi_status_t status;
290022ee6c5SArd Biesheuvel 
291dce48e35SArd Biesheuvel 	if (down_interruptible(&efi_runtime_lock))
292dce48e35SArd Biesheuvel 		return EFI_ABORTED;
2935c418dc7SAnders Roxell 	status = efi_queue_work(EFI_SET_WAKEUP_TIME, &enabled, tm, NULL, NULL,
2943eb420e7SSai Praneeth 				NULL);
295dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
296022ee6c5SArd Biesheuvel 	return status;
297022ee6c5SArd Biesheuvel }
298022ee6c5SArd Biesheuvel 
299022ee6c5SArd Biesheuvel static efi_status_t virt_efi_get_variable(efi_char16_t *name,
300022ee6c5SArd Biesheuvel 					  efi_guid_t *vendor,
301022ee6c5SArd Biesheuvel 					  u32 *attr,
302022ee6c5SArd Biesheuvel 					  unsigned long *data_size,
303022ee6c5SArd Biesheuvel 					  void *data)
304022ee6c5SArd Biesheuvel {
305161485e8SArd Biesheuvel 	efi_status_t status;
306161485e8SArd Biesheuvel 
307dce48e35SArd Biesheuvel 	if (down_interruptible(&efi_runtime_lock))
308dce48e35SArd Biesheuvel 		return EFI_ABORTED;
3095c418dc7SAnders Roxell 	status = efi_queue_work(EFI_GET_VARIABLE, name, vendor, attr, data_size,
310161485e8SArd Biesheuvel 				data);
311dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
312161485e8SArd Biesheuvel 	return status;
313022ee6c5SArd Biesheuvel }
314022ee6c5SArd Biesheuvel 
315022ee6c5SArd Biesheuvel static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
316022ee6c5SArd Biesheuvel 					       efi_char16_t *name,
317022ee6c5SArd Biesheuvel 					       efi_guid_t *vendor)
318022ee6c5SArd Biesheuvel {
319161485e8SArd Biesheuvel 	efi_status_t status;
320161485e8SArd Biesheuvel 
321dce48e35SArd Biesheuvel 	if (down_interruptible(&efi_runtime_lock))
322dce48e35SArd Biesheuvel 		return EFI_ABORTED;
3235c418dc7SAnders Roxell 	status = efi_queue_work(EFI_GET_NEXT_VARIABLE, name_size, name, vendor,
3243eb420e7SSai Praneeth 				NULL, NULL);
325dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
326161485e8SArd Biesheuvel 	return status;
327022ee6c5SArd Biesheuvel }
328022ee6c5SArd Biesheuvel 
329022ee6c5SArd Biesheuvel static efi_status_t virt_efi_set_variable(efi_char16_t *name,
330022ee6c5SArd Biesheuvel 					  efi_guid_t *vendor,
331022ee6c5SArd Biesheuvel 					  u32 attr,
332022ee6c5SArd Biesheuvel 					  unsigned long data_size,
333022ee6c5SArd Biesheuvel 					  void *data)
334022ee6c5SArd Biesheuvel {
335161485e8SArd Biesheuvel 	efi_status_t status;
336161485e8SArd Biesheuvel 
337dce48e35SArd Biesheuvel 	if (down_interruptible(&efi_runtime_lock))
338dce48e35SArd Biesheuvel 		return EFI_ABORTED;
3395c418dc7SAnders Roxell 	status = efi_queue_work(EFI_SET_VARIABLE, name, vendor, &attr, &data_size,
340161485e8SArd Biesheuvel 				data);
341dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
342161485e8SArd Biesheuvel 	return status;
343022ee6c5SArd Biesheuvel }
344022ee6c5SArd Biesheuvel 
3456d80dba1SMatt Fleming static efi_status_t
3466d80dba1SMatt Fleming virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
3476d80dba1SMatt Fleming 				  u32 attr, unsigned long data_size,
3486d80dba1SMatt Fleming 				  void *data)
3496d80dba1SMatt Fleming {
3506d80dba1SMatt Fleming 	efi_status_t status;
3516d80dba1SMatt Fleming 
352dce48e35SArd Biesheuvel 	if (down_trylock(&efi_runtime_lock))
3536d80dba1SMatt Fleming 		return EFI_NOT_READY;
3546d80dba1SMatt Fleming 
3556d80dba1SMatt Fleming 	status = efi_call_virt(set_variable, name, vendor, attr, data_size,
3566d80dba1SMatt Fleming 			       data);
357dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
3586d80dba1SMatt Fleming 	return status;
3596d80dba1SMatt Fleming }
3606d80dba1SMatt Fleming 
3616d80dba1SMatt Fleming 
362022ee6c5SArd Biesheuvel static efi_status_t virt_efi_query_variable_info(u32 attr,
363022ee6c5SArd Biesheuvel 						 u64 *storage_space,
364022ee6c5SArd Biesheuvel 						 u64 *remaining_space,
365022ee6c5SArd Biesheuvel 						 u64 *max_variable_size)
366022ee6c5SArd Biesheuvel {
367161485e8SArd Biesheuvel 	efi_status_t status;
368161485e8SArd Biesheuvel 
369022ee6c5SArd Biesheuvel 	if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
370022ee6c5SArd Biesheuvel 		return EFI_UNSUPPORTED;
371022ee6c5SArd Biesheuvel 
372dce48e35SArd Biesheuvel 	if (down_interruptible(&efi_runtime_lock))
373dce48e35SArd Biesheuvel 		return EFI_ABORTED;
3745c418dc7SAnders Roxell 	status = efi_queue_work(EFI_QUERY_VARIABLE_INFO, &attr, storage_space,
3753eb420e7SSai Praneeth 				remaining_space, max_variable_size, NULL);
376dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
377161485e8SArd Biesheuvel 	return status;
378022ee6c5SArd Biesheuvel }
379022ee6c5SArd Biesheuvel 
380d3cac1f8SArd Biesheuvel static efi_status_t
381d3cac1f8SArd Biesheuvel virt_efi_query_variable_info_nonblocking(u32 attr,
382d3cac1f8SArd Biesheuvel 					 u64 *storage_space,
383d3cac1f8SArd Biesheuvel 					 u64 *remaining_space,
384d3cac1f8SArd Biesheuvel 					 u64 *max_variable_size)
385d3cac1f8SArd Biesheuvel {
386d3cac1f8SArd Biesheuvel 	efi_status_t status;
387d3cac1f8SArd Biesheuvel 
388d3cac1f8SArd Biesheuvel 	if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
389d3cac1f8SArd Biesheuvel 		return EFI_UNSUPPORTED;
390d3cac1f8SArd Biesheuvel 
391dce48e35SArd Biesheuvel 	if (down_trylock(&efi_runtime_lock))
392d3cac1f8SArd Biesheuvel 		return EFI_NOT_READY;
393d3cac1f8SArd Biesheuvel 
394d3cac1f8SArd Biesheuvel 	status = efi_call_virt(query_variable_info, attr, storage_space,
395d3cac1f8SArd Biesheuvel 			       remaining_space, max_variable_size);
396dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
397d3cac1f8SArd Biesheuvel 	return status;
398d3cac1f8SArd Biesheuvel }
399d3cac1f8SArd Biesheuvel 
400022ee6c5SArd Biesheuvel static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
401022ee6c5SArd Biesheuvel {
402161485e8SArd Biesheuvel 	efi_status_t status;
403161485e8SArd Biesheuvel 
404dce48e35SArd Biesheuvel 	if (down_interruptible(&efi_runtime_lock))
405dce48e35SArd Biesheuvel 		return EFI_ABORTED;
4065c418dc7SAnders Roxell 	status = efi_queue_work(EFI_GET_NEXT_HIGH_MONO_COUNT, count, NULL, NULL,
4073eb420e7SSai Praneeth 				NULL, NULL);
408dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
409161485e8SArd Biesheuvel 	return status;
410022ee6c5SArd Biesheuvel }
411022ee6c5SArd Biesheuvel 
412022ee6c5SArd Biesheuvel static void virt_efi_reset_system(int reset_type,
413022ee6c5SArd Biesheuvel 				  efi_status_t status,
414022ee6c5SArd Biesheuvel 				  unsigned long data_size,
415022ee6c5SArd Biesheuvel 				  efi_char16_t *data)
416022ee6c5SArd Biesheuvel {
417dce48e35SArd Biesheuvel 	if (down_interruptible(&efi_runtime_lock)) {
418dce48e35SArd Biesheuvel 		pr_warn("failed to invoke the reset_system() runtime service:\n"
419dce48e35SArd Biesheuvel 			"could not get exclusive access to the firmware\n");
420dce48e35SArd Biesheuvel 		return;
421dce48e35SArd Biesheuvel 	}
4225c418dc7SAnders Roxell 	efi_rts_work.efi_rts_id = EFI_RESET_SYSTEM;
423022ee6c5SArd Biesheuvel 	__efi_call_virt(reset_system, reset_type, status, data_size, data);
424dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
425022ee6c5SArd Biesheuvel }
426022ee6c5SArd Biesheuvel 
427022ee6c5SArd Biesheuvel static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
428022ee6c5SArd Biesheuvel 					    unsigned long count,
429022ee6c5SArd Biesheuvel 					    unsigned long sg_list)
430022ee6c5SArd Biesheuvel {
431161485e8SArd Biesheuvel 	efi_status_t status;
432161485e8SArd Biesheuvel 
433022ee6c5SArd Biesheuvel 	if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
434022ee6c5SArd Biesheuvel 		return EFI_UNSUPPORTED;
435022ee6c5SArd Biesheuvel 
436dce48e35SArd Biesheuvel 	if (down_interruptible(&efi_runtime_lock))
437dce48e35SArd Biesheuvel 		return EFI_ABORTED;
4385c418dc7SAnders Roxell 	status = efi_queue_work(EFI_UPDATE_CAPSULE, capsules, &count, &sg_list,
4393eb420e7SSai Praneeth 				NULL, NULL);
440dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
441161485e8SArd Biesheuvel 	return status;
442022ee6c5SArd Biesheuvel }
443022ee6c5SArd Biesheuvel 
444022ee6c5SArd Biesheuvel static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
445022ee6c5SArd Biesheuvel 						unsigned long count,
446022ee6c5SArd Biesheuvel 						u64 *max_size,
447022ee6c5SArd Biesheuvel 						int *reset_type)
448022ee6c5SArd Biesheuvel {
449161485e8SArd Biesheuvel 	efi_status_t status;
450161485e8SArd Biesheuvel 
451022ee6c5SArd Biesheuvel 	if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
452022ee6c5SArd Biesheuvel 		return EFI_UNSUPPORTED;
453022ee6c5SArd Biesheuvel 
454dce48e35SArd Biesheuvel 	if (down_interruptible(&efi_runtime_lock))
455dce48e35SArd Biesheuvel 		return EFI_ABORTED;
4565c418dc7SAnders Roxell 	status = efi_queue_work(EFI_QUERY_CAPSULE_CAPS, capsules, &count,
4573eb420e7SSai Praneeth 				max_size, reset_type, NULL);
458dce48e35SArd Biesheuvel 	up(&efi_runtime_lock);
459161485e8SArd Biesheuvel 	return status;
460022ee6c5SArd Biesheuvel }
461022ee6c5SArd Biesheuvel 
462022ee6c5SArd Biesheuvel void efi_native_runtime_setup(void)
463022ee6c5SArd Biesheuvel {
464022ee6c5SArd Biesheuvel 	efi.get_time = virt_efi_get_time;
465022ee6c5SArd Biesheuvel 	efi.set_time = virt_efi_set_time;
466022ee6c5SArd Biesheuvel 	efi.get_wakeup_time = virt_efi_get_wakeup_time;
467022ee6c5SArd Biesheuvel 	efi.set_wakeup_time = virt_efi_set_wakeup_time;
468022ee6c5SArd Biesheuvel 	efi.get_variable = virt_efi_get_variable;
469022ee6c5SArd Biesheuvel 	efi.get_next_variable = virt_efi_get_next_variable;
470022ee6c5SArd Biesheuvel 	efi.set_variable = virt_efi_set_variable;
4716d80dba1SMatt Fleming 	efi.set_variable_nonblocking = virt_efi_set_variable_nonblocking;
472022ee6c5SArd Biesheuvel 	efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count;
473022ee6c5SArd Biesheuvel 	efi.reset_system = virt_efi_reset_system;
474022ee6c5SArd Biesheuvel 	efi.query_variable_info = virt_efi_query_variable_info;
475d3cac1f8SArd Biesheuvel 	efi.query_variable_info_nonblocking = virt_efi_query_variable_info_nonblocking;
476022ee6c5SArd Biesheuvel 	efi.update_capsule = virt_efi_update_capsule;
477022ee6c5SArd Biesheuvel 	efi.query_capsule_caps = virt_efi_query_capsule_caps;
478022ee6c5SArd Biesheuvel }
479