diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index afaeb734295a..942cd042e93e 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -27,7 +27,12 @@ arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)	+= hw_breakpoint.o
 arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND)	+= sleep.o suspend.o
 arm64-obj-$(CONFIG_JUMP_LABEL)		+= jump_label.o
 arm64-obj-$(CONFIG_KGDB)		+= kgdb.o
-arm64-obj-$(CONFIG_EFI)			+= efi.o efi-stub.o efi-entry.o
+
+arm64-efi-obj-y				:= efi.o
+arm64-efi-obj-$(CONFIG_EFI_STUB)	+= efi-stub.o efi-entry.o
+arm64-efi-obj-$(CONFIG_EFI_LE_STUB)	+= efistub-le/
+arm64-efi-obj-$(CONFIG_CPU_BIG_ENDIAN)	+= efi-be-runtime.o efi-be-call.o
+arm64-obj-$(CONFIG_EFI)			+= $(arm64-efi-obj-y)
 
 obj-y					+= $(arm64-obj-y) vdso/
 obj-m					+= $(arm64-obj-m)
diff --git a/arch/arm64/kernel/efi-entry.S b/arch/arm64/kernel/efi-entry.S
index a0016d3a17da..89f34bb86cfd 100644
--- a/arch/arm64/kernel/efi-entry.S
+++ b/arch/arm64/kernel/efi-entry.S
@@ -34,8 +34,34 @@ ENTRY(efi_stub_entry)
 	 * Create a stack frame to save FP/LR with extra space
 	 * for image_addr variable passed to efi_entry().
 	 */
-	stp	x29, x30, [sp, #-32]!
+	stp	x29, x30, [sp, #-48]!
+	stp	x22, x23, [sp, #32]
 
+#ifdef CONFIG_EFI_LE_STUB
+	adr	x4, efi_stub_entry
+	ldp	w8, w9, [x4, #-32]
+STUB_BE(rev	w8, w8		)
+STUB_BE(rev	w9, w9		)
+	add	x8, x4, w8, sxtw		// x8: base of Image
+	add	x9, x4, w9, sxtw		// x9: offset of linux_banner
+
+	ldp	x22, x23, [x4, #-24]		// x22: size of Image
+STUB_BE(rev	x23, x23	)		// x23: stext offset
+
+	/*
+	 * Get a pointer to linux_banner in the outer image and store it
+	 * in this image.
+	 */
+	adrp	x4, le_linux_banner
+	str	x9, [x4, #:lo12:le_linux_banner]
+#else
+	adrp	x8, _text
+	add	x8, x8, #:lo12:_text		// x8: base of Image
+	adrp	x9, _edata
+	add	x9, x9, #:lo12:_edata
+	sub	x22, x9, x8			// x22: size of Image
+	ldr	x23, =stext_offset		// x23: stext offset
+#endif
 	/*
 	 * Call efi_entry to do the real work.
 	 * x0 and x1 are already set up by firmware. Current runtime
@@ -45,8 +71,6 @@ ENTRY(efi_stub_entry)
 	 *                         efi_system_table_t *sys_table,
 	 *                         unsigned long *image_addr) ;
 	 */
-	adrp	x8, _text
-	add	x8, x8, #:lo12:_text
 	add	x2, sp, 16
 	str	x8, [x2]
 	bl	efi_entry
@@ -61,18 +85,13 @@ ENTRY(efi_stub_entry)
 	 */
 	mov	x20, x0		// DTB address
 	ldr	x0, [sp, #16]	// relocated _text address
-	ldr	x21, =stext_offset
-	add	x21, x0, x21
+	add	x21, x0, x23
 
 	/*
 	 * Flush dcache covering current runtime addresses
 	 * of kernel text/data. Then flush all of icache.
 	 */
-	adrp	x1, _text
-	add	x1, x1, #:lo12:_text
-	adrp	x2, _edata
-	add	x2, x2, #:lo12:_edata
-	sub	x1, x2, x1
+	mov	x1, x22
 
 	bl	__flush_dcache_area
 	ic	ialluis
@@ -103,7 +122,8 @@ ENTRY(efi_stub_entry)
 
 efi_load_fail:
 	mov	x0, #EFI_LOAD_ERROR
-	ldp	x29, x30, [sp], #32
+	ldp	x22, x23, [sp, #32]
+	ldp	x29, x30, [sp], #48
 	ret
 
 ENDPROC(efi_stub_entry)
diff --git a/arch/arm64/kernel/efistub-le/Makefile b/arch/arm64/kernel/efistub-le/Makefile
new file mode 100644
index 000000000000..38347b0633c8
--- /dev/null
+++ b/arch/arm64/kernel/efistub-le/Makefile
@@ -0,0 +1,52 @@
+
+#
+# Build a little endian EFI stub and wrap it into a single .o
+#
+
+# the LE objects making up the LE efi stub
+le-objs := efi-entry.o efi-stub.o strstr.o cache.o			\
+	   lib-memchr.o lib-memcmp.o lib-memcpy.o lib-memmove.o		\
+		lib-memset.o lib-strchr.o lib-strlen.o lib-strncmp.o	\
+	   fdt-fdt.o fdt-fdt_ro.o fdt-fdt_rw.o fdt-fdt_sw.o 		\
+		fdt-fdt_wip.o fdt-fdt_empty_tree.o 			\
+	   libstub-fdt.o libstub-arm-stub.o libstub-efi-stub-helper.o
+
+extra-y := efi-le-stub.bin efi-le-stub.elf $(le-objs)
+
+KBUILD_CFLAGS := $(subst -pg,,$(KBUILD_CFLAGS)) -fno-stack-protector	\
+		 -mlittle-endian -I$(srctree)/scripts/dtc/libfdt
+
+le-targets := $(addprefix $(obj)/, $(le-objs))
+$(le-targets): KBUILD_AFLAGS += -mlittle-endian -include $(srctree)/$(src)/le.h
+
+$(obj)/efi-entry.o: $(obj)/../efi-entry.S FORCE
+	$(call if_changed_dep,as_o_S)
+
+CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
+$(obj)/efi-stub.o: $(obj)/../efi-stub.c FORCE
+	$(call if_changed_dep,cc_o_c)
+
+$(obj)/cache.o: $(src)/../../mm/cache.S FORCE
+	$(call if_changed_dep,as_o_S)
+
+$(obj)/lib-%.o: $(src)/../../lib/%.S FORCE
+	$(call if_changed_dep,as_o_S)
+
+$(obj)/fdt-%.o: $(srctree)/lib/%.c FORCE
+	$(call if_changed_dep,cc_o_c)
+
+$(obj)/libstub-%.o: $(srctree)/drivers/firmware/efi/libstub/%.c FORCE
+	$(call if_changed_dep,cc_o_c)
+
+$(obj)/efi-le-stub.elf: LDFLAGS=-EL -Map $@.map -T
+$(obj)/efi-le-stub.elf: $(src)/efistub-le.lds $(le-targets) FORCE
+	$(call if_changed,ld)
+
+$(obj)/efi-le-stub.bin: OBJCOPYFLAGS=-O binary
+$(obj)/efi-le-stub.bin: $(obj)/efi-le-stub.elf FORCE
+	$(call if_changed,objcopy)
+
+# the BE object containing the entire LE stub
+obj-y := efi-le-entry.o
+
+$(obj)/efi-le-entry.o: $(obj)/efi-le-stub.bin
diff --git a/arch/arm64/kernel/efistub-le/efi-le-entry.S b/arch/arm64/kernel/efistub-le/efi-le-entry.S
new file mode 100644
index 000000000000..f615430209e5
--- /dev/null
+++ b/arch/arm64/kernel/efistub-le/efi-le-entry.S
@@ -0,0 +1,13 @@
+
+#include <linux/linkage.h>
+
+	.text
+	.align		12
+	.long		_text - efi_stub_entry
+	.long		linux_banner - efi_stub_entry
+	.quad		_kernel_size_le
+	.quad		stext_offset
+	.quad		0
+ENTRY(efi_stub_entry)
+	.incbin		"arch/arm64/kernel/efistub-le/efi-le-stub.bin"
+ENDPROC(efi_stub_entry)
diff --git a/arch/arm64/kernel/efistub-le/efistub-le.lds b/arch/arm64/kernel/efistub-le/efistub-le.lds
new file mode 100644
index 000000000000..20361c43aa2e
--- /dev/null
+++ b/arch/arm64/kernel/efistub-le/efistub-le.lds
@@ -0,0 +1,35 @@
+
+ENTRY(efi_stub_entry)
+
+SECTIONS {
+	/*
+	 * The inner and outer alignment of this chunk of code need to be the
+	 * same so that PC relative references using adrp/add or adrp/ldr pairs
+	 * will work correctly.
+	 * Skip 32 bytes here, so we can put the binary blob at an offset of
+	 * 4k + 0x20 in the outer image, and use the gap to share constants
+	 * emitted by the outer linker but required in the stub.
+	 */
+	.text 0x20 : {
+		arch/arm64/kernel/efistub-le/efi-entry.o(.init.text)
+		*(.init.text)
+		*(.text)
+		*(.text*)
+	}
+	.rodata : {
+		. = ALIGN(16);
+		*(.rodata)
+		*(.rodata*)
+		*(.init.rodata)
+	}
+	.data : {
+		. = ALIGN(16);
+		*(.data)
+		*(.data*)
+		le_linux_banner = .;
+		. += 8;
+	}
+	/DISCARD/ : {
+		*(__ex_table)
+	}
+}
diff --git a/arch/arm64/kernel/efistub-le/le.h b/arch/arm64/kernel/efistub-le/le.h
new file mode 100644
index 000000000000..f4a28a5f6815
--- /dev/null
+++ b/arch/arm64/kernel/efistub-le/le.h
@@ -0,0 +1,12 @@
+
+/*
+ * This is a bit of a hack, but it is necessary to correctly compile .S files
+ * that contain CPU_LE()/CPU_BE() statements, as these are defined to depend on
+ * CONFIG_ symbols and not on the endianness of the compiler.
+ */
+#ifdef CONFIG_CPU_BIG_ENDIAN
+#define STUB_BE(code...)	code
+#else
+#define STUB_BE(code...)
+#endif
+#undef CONFIG_CPU_BIG_ENDIAN
diff --git a/arch/arm64/kernel/efistub-le/strstr.c b/arch/arm64/kernel/efistub-le/strstr.c
new file mode 100644
index 000000000000..daed0bbcc0c6
--- /dev/null
+++ b/arch/arm64/kernel/efistub-le/strstr.c
@@ -0,0 +1,20 @@
+
+#include <linux/types.h>
+#include <linux/string.h>
+
+char *strstr(const char *s1, const char *s2)
+{
+	size_t l1, l2;
+
+	l2 = strlen(s2);
+	if (!l2)
+		return (char *)s1;
+	l1 = strlen(s1);
+	while (l1 >= l2) {
+		l1--;
+		if (!memcmp(s1, s2, l2))
+			return (char *)s1;
+		s1++;
+	}
+	return NULL;
+}
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c
index a56bb3528755..651c639a8a18 100644
--- a/drivers/firmware/efi/libstub/fdt.c
+++ b/drivers/firmware/efi/libstub/fdt.c
@@ -22,6 +22,10 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
 			unsigned long map_size, unsigned long desc_size,
 			u32 desc_ver)
 {
+#ifdef CONFIG_EFI_LE_STUB
+	extern char const *le_linux_banner;
+	char const *linux_banner = le_linux_banner;
+#endif
 	int node, prev;
 	int status;
 	u32 fdt_val32;
