diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index de83b53c7e19..f2224566cc75 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1196,7 +1196,6 @@ config EFI
 	select LIBFDT
 	select UCS2_STRING
 	select EFI_PARAMS_FROM_FDT
-	select EFI_RUNTIME_WRAPPERS
 	select EFI_STUB
 	select EFI_ARMSTUB
 	default y
diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
index b9b09a734719..b96bc4836c37 100644
--- a/arch/arm64/include/asm/efi.h
+++ b/arch/arm64/include/asm/efi.h
@@ -165,4 +165,9 @@ int __init efi_allocate_runtime_regions(struct mm_struct *mm);
 #define EFI_CODE_BASE		0x200000
 #define EFI_CODE_SIZE		PAGE_SIZE
 
+#define EFI_VARBUFFER_BASE	0x210000
+#define EFI_VARBUFFER_SIZE	SZ_64K
+
+#define EFI_DATA_BASE		0x220000
+
 #endif /* _ASM_EFI_H */
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index 68c920b2f4f0..cd23c20fba39 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -11,9 +11,15 @@
  *
  */
 
+#define pr_fmt(fmt)	"efi: " fmt
+
+#include <linux/bug.h>
 #include <linux/efi.h>
 #include <linux/init.h>
-
+#include <linux/irqflags.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/stringify.h>
 #include <asm/efi.h>
 
 /*
@@ -136,6 +142,63 @@ bool on_efi_stack(unsigned long sp)
 	return sp >= EFI_STACK_BASE && sp < (EFI_STACK_BASE + EFI_STACK_SIZE);
 }
 
+#define MAX_CAPSULES	16
+#define MAX_VARNAME_LEN	1024
+
+#define EFI_ADDR(arg)	(&((typeof(__efi_rt_marshall_data)*)EFI_DATA_BASE)->arg)
+#define EFI_DATA(arg)	(__efi_rt_marshall_data.arg)
+
+static union {
+	struct {
+		efi_time_t	tm;
+		efi_time_cap_t	tc;
+	} get_time;
+
+	struct {
+		efi_time_t	tm;
+	} set_time;
+
+	struct {
+		efi_bool_t	enabled;
+		efi_bool_t	pending;
+		efi_time_t	tm;
+	} get_wakeup_time;
+
+	struct {
+		efi_char16_t	name[MAX_VARNAME_LEN];
+		efi_guid_t	vendor;
+		u32		attr;
+		unsigned long	data_size;
+		void		*data;
+	} get_variable;
+
+	struct {
+		unsigned long	name_size;
+		efi_char16_t	name[MAX_VARNAME_LEN];
+		efi_guid_t	vendor;
+	} get_next_variable;
+
+	struct {
+		u64		storage_space;
+		u64		remaining_space;
+		u64		max_variable_size;
+	} query_variable_info;
+
+	struct {
+		u32		count;
+	} get_next_high_mono_count;
+
+	struct {
+		efi_capsule_header_t	capsules[MAX_CAPSULES];
+		efi_capsule_header_t	*capsule_headers[MAX_CAPSULES];
+		u64			max_size;
+		int			reset_type;
+	} query_capsule_caps;
+
+} __efi_rt_marshall_data __page_aligned_bss;
+
+static u8 __efi_rt_varbuffer[EFI_VARBUFFER_SIZE] __page_aligned_bss;
+
 int __init efi_allocate_runtime_regions(struct mm_struct *mm)
 {
 	static u8 stack[EFI_STACK_SIZE] __page_aligned_bss;
@@ -152,5 +215,415 @@ int __init efi_allocate_runtime_regions(struct mm_struct *mm)
 			   __pgprot(pgprot_val(PAGE_KERNEL_ROX) | PTE_NG),
 			   false);
 
+	/* map the marshall data struct */
+	create_pgd_mapping(mm, __pa_symbol(&__efi_rt_marshall_data),
+			   EFI_DATA_BASE, sizeof(__efi_rt_marshall_data),
+			   __pgprot(pgprot_val(PAGE_KERNEL) | PTE_NG),
+			   false);
+
+	/* map the varbuffer */
+	create_pgd_mapping(mm, __pa_symbol(__efi_rt_varbuffer),
+			   EFI_VARBUFFER_BASE, EFI_VARBUFFER_SIZE,
+			   __pgprot(pgprot_val(PAGE_KERNEL) | PTE_NG),
+			   false);
+
 	return 0;
 }
+
+/*
+ * Wrap around the new efi_call_virt_generic() macros so that the
+ * code doesn't get too cluttered:
+ */
+#define efi_call_virt(f, args...)   \
+	efi_call_virt_pointer(efi.systab->runtime, f, args)
+#define __efi_call_virt(f, args...) \
+	__efi_call_virt_pointer(efi.systab->runtime, f, args)
+
+void efi_call_virt_check_flags(unsigned long flags, const char *call)
+{
+	unsigned long cur_flags, mismatch;
+
+	local_save_flags(cur_flags);
+
+	mismatch = flags ^ cur_flags;
+	if (!WARN_ON_ONCE(mismatch & ARCH_EFI_IRQ_FLAGS_MASK))
+		return;
+
+	add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE);
+	pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI %s\n",
+			   flags, cur_flags, call);
+	local_irq_restore(flags);
+}
+
+static int strlen16(efi_char16_t const *name)
+{
+	int ret = 0;
+
+	while (*name++)
+		ret++;
+
+	return ret;
+}
+
+static DEFINE_SEMAPHORE(efi_runtime_lock);
+
+static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
+{
+	efi_status_t status;
+
+	if (!tm)
+		return EFI_INVALID_PARAMETER;
+
+	if (down_interruptible(&efi_runtime_lock))
+		return EFI_ABORTED;
+	status = efi_call_virt(get_time,
+			       EFI_ADDR(get_time.tm),
+			       tc ? EFI_ADDR(get_time.tc): NULL);
+
+	*tm = EFI_DATA(get_time.tm);
+	if (tc)
+		*tc = EFI_DATA(get_time.tc);
+
+	up(&efi_runtime_lock);
+	return status;
+}
+
+static efi_status_t virt_efi_set_time(efi_time_t *tm)
+{
+	efi_status_t status;
+
+	if (!tm)
+		return EFI_INVALID_PARAMETER;
+
+	if (down_interruptible(&efi_runtime_lock))
+		return EFI_ABORTED;
+
+	EFI_DATA(set_time.tm) = *tm;
+
+	status = efi_call_virt(set_time,
+			       EFI_ADDR(set_time.tm));
+	up(&efi_runtime_lock);
+	return status;
+}
+
+static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
+					     efi_bool_t *pending,
+					     efi_time_t *tm)
+{
+	efi_status_t status;
+
+	if (!enabled || !pending || !tm)
+		return EFI_INVALID_PARAMETER;
+
+	if (down_interruptible(&efi_runtime_lock))
+		return EFI_ABORTED;
+	status = efi_call_virt(get_wakeup_time,
+			       EFI_ADDR(get_wakeup_time.enabled),
+			       EFI_ADDR(get_wakeup_time.pending),
+			       EFI_ADDR(get_wakeup_time.tm));
+
+	*enabled = EFI_DATA(get_wakeup_time.enabled);
+	*pending = EFI_DATA(get_wakeup_time.pending);
+	*tm = EFI_DATA(get_wakeup_time.tm);
+
+	up(&efi_runtime_lock);
+	return status;
+}
+
+static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
+{
+	efi_status_t status;
+
+	if (down_interruptible(&efi_runtime_lock))
+		return EFI_ABORTED;
+
+	EFI_DATA(set_time.tm) = *tm;
+
+	status = efi_call_virt(set_wakeup_time, enabled,
+			       EFI_ADDR(set_time.tm));
+	up(&efi_runtime_lock);
+	return status;
+}
+
+static efi_status_t virt_efi_get_variable(efi_char16_t *name,
+					  efi_guid_t *vendor,
+					  u32 *attr,
+					  unsigned long *data_size,
+					  void *data)
+{
+	efi_status_t status;
+	int l = strlen16(name) + 1;
+
+	if (!name || !vendor || !data_size)
+		return EFI_INVALID_PARAMETER;
+
+	if (*data_size > EFI_VARBUFFER_SIZE || l > MAX_VARNAME_LEN)
+		return EFI_NOT_READY;
+
+	if (down_interruptible(&efi_runtime_lock))
+		return EFI_ABORTED;
+
+	memcpy(EFI_DATA(get_variable.name), name, l * sizeof(efi_char16_t));
+	EFI_DATA(get_variable.vendor) = *vendor;
+	EFI_DATA(get_variable.data_size) = *data_size;
+
+	status = efi_call_virt(get_variable,
+			       EFI_ADDR(get_variable.name),
+			       EFI_ADDR(get_variable.vendor),
+			       attr ? EFI_ADDR(get_variable.attr) : NULL,
+			       EFI_ADDR(get_variable.data_size),
+			       data ? (void *)EFI_VARBUFFER_BASE : NULL);
+
+	if (attr)
+		*attr = EFI_DATA(get_variable.attr);
+	*data_size = EFI_DATA(get_variable.data_size);
+	if (data)
+		memcpy(data, __efi_rt_varbuffer, *data_size);
+
+	up(&efi_runtime_lock);
+	return status;
+}
+
+static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
+					       efi_char16_t *name,
+					       efi_guid_t *vendor)
+{
+	efi_status_t status;
+
+	if (*name_size > MAX_VARNAME_LEN)
+		return EFI_NOT_READY;
+
+	if (down_interruptible(&efi_runtime_lock))
+		return EFI_ABORTED;
+
+	EFI_DATA(get_next_variable.name_size) = *name_size;
+	memcpy(EFI_DATA(get_next_variable.name), name, *name_size);
+	EFI_DATA(get_next_variable.vendor) = *vendor;
+
+	status = efi_call_virt(get_next_variable,
+			       EFI_ADDR(get_next_variable.name_size),
+			       EFI_ADDR(get_next_variable.name),
+			       EFI_ADDR(get_next_variable.vendor));
+
+	*name_size = EFI_DATA(get_next_variable.name_size);
+	memcpy(name, EFI_DATA(get_next_variable.name), *name_size);
+	*vendor = EFI_DATA(get_next_variable.vendor);
+
+	up(&efi_runtime_lock);
+	return status;
+}
+
+static efi_status_t virt_efi_set_variable(efi_char16_t *name,
+					  efi_guid_t *vendor,
+					  u32 attr,
+					  unsigned long data_size,
+					  void *data)
+{
+	efi_status_t status;
+	int l = strlen16(name) + 1;
+
+	if (!name || !vendor)
+		return EFI_INVALID_PARAMETER;
+
+	if (data_size > EFI_VARBUFFER_SIZE || l > MAX_VARNAME_LEN)
+		return EFI_NOT_READY;
+
+	if (down_interruptible(&efi_runtime_lock))
+		return EFI_ABORTED;
+
+	memcpy(EFI_DATA(get_variable.name), name, l * sizeof(efi_char16_t));
+	EFI_DATA(get_variable.vendor) = *vendor;
+	if (data)
+		memcpy(__efi_rt_varbuffer, data, data_size);
+
+	status = efi_call_virt(set_variable,
+			       EFI_ADDR(get_variable.name),
+			       EFI_ADDR(get_variable.vendor),
+			       attr,
+			       data_size,
+			       data ? (void *)EFI_VARBUFFER_BASE : NULL);
+
+	up(&efi_runtime_lock);
+	return status;
+}
+
+static efi_status_t
+virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
+				  u32 attr, unsigned long data_size,
+				  void *data)
+{
+	efi_status_t status;
+	int l = strlen16(name) + 1;
+
+	if (!name || !vendor)
+		return EFI_INVALID_PARAMETER;
+
+	if (data_size > EFI_VARBUFFER_SIZE || l > MAX_VARNAME_LEN)
+		return EFI_NOT_READY;
+
+	if (down_trylock(&efi_runtime_lock))
+		return EFI_NOT_READY;
+
+	memcpy(EFI_DATA(get_variable.name), name, l * sizeof(efi_char16_t));
+	EFI_DATA(get_variable.vendor) = *vendor;
+	if (data)
+		memcpy(__efi_rt_varbuffer, data, data_size);
+
+	status = efi_call_virt(set_variable,
+			       EFI_ADDR(get_variable.name),
+			       EFI_ADDR(get_variable.vendor),
+			       attr,
+			       data_size,
+			       data ? (void *)EFI_VARBUFFER_BASE : NULL);
+
+	up(&efi_runtime_lock);
+	return status;
+}
+
+
+static efi_status_t virt_efi_query_variable_info(u32 attr,
+						 u64 *storage_space,
+						 u64 *remaining_space,
+						 u64 *max_variable_size)
+{
+	efi_status_t status;
+
+	if (down_interruptible(&efi_runtime_lock))
+		return EFI_ABORTED;
+
+	status = efi_call_virt(query_variable_info,
+			       attr,
+			       EFI_ADDR(query_variable_info.storage_space),
+			       EFI_ADDR(query_variable_info.remaining_space),
+			       EFI_ADDR(query_variable_info.max_variable_size));
+
+	*storage_space	= EFI_DATA(query_variable_info.storage_space);
+	*remaining_space = EFI_DATA(query_variable_info.remaining_space);
+	*max_variable_size = EFI_DATA(query_variable_info.max_variable_size);
+
+	up(&efi_runtime_lock);
+	return status;
+}
+
+static efi_status_t
+virt_efi_query_variable_info_nonblocking(u32 attr,
+					 u64 *storage_space,
+					 u64 *remaining_space,
+					 u64 *max_variable_size)
+{
+	efi_status_t status;
+
+	if (down_trylock(&efi_runtime_lock))
+		return EFI_NOT_READY;
+
+	status = efi_call_virt(query_variable_info,
+			       attr,
+			       EFI_ADDR(query_variable_info.storage_space),
+			       EFI_ADDR(query_variable_info.remaining_space),
+			       EFI_ADDR(query_variable_info.max_variable_size));
+
+	*storage_space	= EFI_DATA(query_variable_info.storage_space);
+	*remaining_space = EFI_DATA(query_variable_info.remaining_space);
+	*max_variable_size = EFI_DATA(query_variable_info.max_variable_size);
+
+	up(&efi_runtime_lock);
+	return status;
+}
+
+static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
+{
+	efi_status_t status;
+
+	if (down_interruptible(&efi_runtime_lock))
+		return EFI_ABORTED;
+	status = efi_call_virt(get_next_high_mono_count,
+			       EFI_ADDR(get_next_high_mono_count.count));
+
+	*count = EFI_DATA(get_next_high_mono_count.count);
+
+	up(&efi_runtime_lock);
+	return status;
+}
+
+static void virt_efi_reset_system(int reset_type,
+				  efi_status_t status,
+				  unsigned long data_size,
+				  efi_char16_t *data)
+{
+	/* we don't use 'data' in the kernel */
+	pr_warn("efi_reset_system: ignoring 'data' argument\n");
+
+	if (down_interruptible(&efi_runtime_lock)) {
+		pr_warn("failed to invoke the reset_system() runtime service:\n"
+			"could not get exclusive access to the firmware\n");
+		return;
+	}
+	__efi_call_virt(reset_system, reset_type, status, 0, NULL);
+	up(&efi_runtime_lock);
+}
+
+static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
+					    unsigned long count,
+					    unsigned long sg_list)
+{
+//	efi_status_t status;
+
+	return EFI_NOT_READY;
+
+//	if (down_interruptible(&efi_runtime_lock))
+//		return EFI_ABORTED;
+//	status = efi_call_virt(update_capsule, capsules, count, sg_list);
+//	up(&efi_runtime_lock);
+//	return status;
+}
+
+static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
+						unsigned long count,
+						u64 *max_size,
+						int *reset_type)
+{
+	efi_status_t status;
+	int i;
+
+	if (count > MAX_CAPSULES)
+		return EFI_NOT_READY;
+
+	if (down_interruptible(&efi_runtime_lock))
+		return EFI_ABORTED;
+
+	for (i = 0; i < count; i++) {
+		EFI_DATA(query_capsule_caps.capsules[i]) = *capsules[i];
+		EFI_DATA(query_capsule_caps.capsule_headers[i]) =
+				EFI_ADDR(query_capsule_caps.capsules[i]);
+	}
+
+	status = efi_call_virt(query_capsule_caps,
+			       EFI_ADDR(query_capsule_caps.capsule_headers),
+			       count,
+			       EFI_ADDR(query_capsule_caps.max_size),
+			       EFI_ADDR(query_capsule_caps.reset_type));
+
+	*max_size = EFI_DATA(query_capsule_caps.max_size);
+	*reset_type = EFI_DATA(query_capsule_caps.reset_type);
+
+	up(&efi_runtime_lock);
+	return status;
+}
+
+void efi_native_runtime_setup(void)
+{
+	efi.get_time = virt_efi_get_time;
+	efi.set_time = virt_efi_set_time;
+	efi.get_wakeup_time = virt_efi_get_wakeup_time;
+	efi.set_wakeup_time = virt_efi_set_wakeup_time;
+	efi.get_variable = virt_efi_get_variable;
+	efi.get_next_variable = virt_efi_get_next_variable;
+	efi.set_variable = virt_efi_set_variable;
+	efi.set_variable_nonblocking = virt_efi_set_variable_nonblocking;
+	efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count;
+	efi.reset_system = virt_efi_reset_system;
+	efi.query_variable_info = virt_efi_query_variable_info;
+	efi.query_variable_info_nonblocking = virt_efi_query_variable_info_nonblocking;
+	efi.update_capsule = virt_efi_update_capsule;
+	efi.query_capsule_caps = virt_efi_query_capsule_caps;
+}
