From 47226ad4f4cfd1e91ded7f2ec42f83ff1c624663 Mon Sep 17 00:00:00 2001
From: Yinghai Lu <yinghai@kernel.org>
Date: Wed, 3 Sep 2014 21:50:07 -0700
Subject: [PATCH 1/3] x86/efi: Only load initrd above 4g on second try
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Mantas found that after commit 4bf7111f5016 ("x86/efi: Support initrd
loaded above 4G"), the kernel freezes at the earliest possible moment
when trying to boot via UEFI on Asus laptop.

Revert to old way to load initrd under 4G on first try, second try will
use above 4G buffer when initrd is too big and does not fit under 4G.

[ The cause of the freeze appears to be a firmware bug when reading
  file data into buffers above 4GB, though the exact reason is unknown.
  Mantas reports that the hang can be avoid if the file size is a
  multiple of 512 bytes, but I've seen some ASUS firmware simply
  corrupting the file data rather than freezing.

  Laszlo fixed an issue in the upstream EDK2 DiskIO code in Aug 2013
  which may possibly be related, commit 4e39b75e ("MdeModulePkg/DiskIoDxe:
  fix source/destination pointer of overrun transfer").

  Whatever the cause, it's unlikely that a fix will be forthcoming
  from the vendor, hence the workaround - Matt ]

Cc: Laszlo Ersek <lersek@redhat.com>
Reported-by: Mantas Mikulėnas <grawity@gmail.com>
Reported-by: Harald Hoyer <harald@redhat.com>
Tested-by: Anders Darander <anders@chargestorm.se>
Tested-by: Calvin Walton <calvin.walton@kepstin.ca>
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
---
 arch/x86/boot/compressed/eboot.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index f277184e2ac13..dca9842d8f91d 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -1032,7 +1032,6 @@ struct boot_params *make_boot_params(struct efi_config *c)
 	int i;
 	unsigned long ramdisk_addr;
 	unsigned long ramdisk_size;
-	unsigned long initrd_addr_max;
 
 	efi_early = c;
 	sys_table = (efi_system_table_t *)(unsigned long)efi_early->table;
@@ -1095,15 +1094,20 @@ struct boot_params *make_boot_params(struct efi_config *c)
 
 	memset(sdt, 0, sizeof(*sdt));
 
-	if (hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G)
-		initrd_addr_max = -1UL;
-	else
-		initrd_addr_max = hdr->initrd_addr_max;
-
 	status = handle_cmdline_files(sys_table, image,
 				      (char *)(unsigned long)hdr->cmd_line_ptr,
-				      "initrd=", initrd_addr_max,
+				      "initrd=", hdr->initrd_addr_max,
 				      &ramdisk_addr, &ramdisk_size);
+
+	if (status != EFI_SUCCESS &&
+	    hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G) {
+		efi_printk(sys_table, "Trying to load files to higher address\n");
+		status = handle_cmdline_files(sys_table, image,
+				      (char *)(unsigned long)hdr->cmd_line_ptr,
+				      "initrd=", -1UL,
+				      &ramdisk_addr, &ramdisk_size);
+	}
+
 	if (status != EFI_SUCCESS)
 		goto fail2;
 	hdr->ramdisk_image = ramdisk_addr & 0xffffffff;

From 9cb0e394234d244fe5a97e743ec9dd7ddff7e64b Mon Sep 17 00:00:00 2001
From: Matt Fleming <matt.fleming@intel.com>
Date: Fri, 5 Sep 2014 14:52:26 +0100
Subject: [PATCH 2/3] x86/efi: Fixup GOT in all boot code paths

Maarten reported that his Macbook pro 8.2 stopped booting after commit
f23cf8bd5c1f49 ("efi/x86: efistub: Move shared dependencies to
<asm/efi.h>"), the main feature of which is changing the visibility of
symbol 'efi_early' from local to global.

By making 'efi_early' global we end up requiring an entry in the Global
Offset Table. Unfortunately, while we do include code to fixup GOT
entries in the early boot code, it's only called after we've executed
the EFI boot stub.

What this amounts to is that references to 'efi_early' in the EFI boot
stub don't point to the correct place.

Since we've got multiple boot entry points we need to be prepared to
fixup the GOT in multiple places, while ensuring that we never do it
more than once, otherwise the GOT entries will still point to the wrong
place.

Reported-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Tested-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
---
 arch/x86/boot/compressed/head_32.S | 54 ++++++++++++++++++++--------
 arch/x86/boot/compressed/head_64.S | 56 ++++++++++++++++++++++--------
 2 files changed, 81 insertions(+), 29 deletions(-)

diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index cbed1407a5cdb..d6b8aa4c986c0 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -30,6 +30,33 @@
 #include <asm/boot.h>
 #include <asm/asm-offsets.h>
 
+/*
+ * Adjust our own GOT
+ *
+ * The relocation base must be in %ebx
+ *
+ * It is safe to call this macro more than once, because in some of the
+ * code paths multiple invocations are inevitable, e.g. via the efi*
+ * entry points.
+ *
+ * Relocation is only performed the first time.
+ */
+.macro FIXUP_GOT
+	cmpb	$1, got_fixed(%ebx)
+	je	2f
+
+	leal	_got(%ebx), %edx
+	leal	_egot(%ebx), %ecx
+1:
+	cmpl	%ecx, %edx
+	jae	2f
+	addl	%ebx, (%edx)
+	addl	$4, %edx
+	jmp	1b
+2:
+	movb	$1, got_fixed(%ebx)
+.endm
+
 	__HEAD
 ENTRY(startup_32)
 #ifdef CONFIG_EFI_STUB
@@ -56,6 +83,9 @@ ENTRY(efi_pe_entry)
 	add	%esi, 88(%eax)
 	pushl	%eax
 
+	movl	%esi, %ebx
+	FIXUP_GOT
+
 	call	make_boot_params
 	cmpl	$0, %eax
 	je	fail
@@ -81,6 +111,10 @@ ENTRY(efi32_stub_entry)
 	leal	efi32_config(%esi), %eax
 	add	%esi, 88(%eax)
 	pushl	%eax
+
+	movl	%esi, %ebx
+	FIXUP_GOT
+
 2:
 	call	efi_main
 	cmpl	$0, %eax
@@ -190,19 +224,7 @@ relocated:
 	shrl	$2, %ecx
 	rep	stosl
 
-/*
- * Adjust our own GOT
- */
-	leal	_got(%ebx), %edx
-	leal	_egot(%ebx), %ecx
-1:
-	cmpl	%ecx, %edx
-	jae	2f
-	addl	%ebx, (%edx)
-	addl	$4, %edx
-	jmp	1b
-2:
-
+	FIXUP_GOT
 /*
  * Do the decompression, and jump to the new kernel..
  */
@@ -225,8 +247,12 @@ relocated:
 	xorl	%ebx, %ebx
 	jmp	*%eax
 
-#ifdef CONFIG_EFI_STUB
 	.data
+/* Have we relocated the GOT? */
+got_fixed:
+	.byte 0
+
+#ifdef CONFIG_EFI_STUB
 efi32_config:
 	.fill 11,8,0
 	.long efi_call_phys
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 2884e0c3e8a58..50f69c7eaaf4b 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -32,6 +32,33 @@
 #include <asm/processor-flags.h>
 #include <asm/asm-offsets.h>
 
+/*
+ * Adjust our own GOT
+ *
+ * The relocation base must be in %rbx
+ *
+ * It is safe to call this macro more than once, because in some of the
+ * code paths multiple invocations are inevitable, e.g. via the efi*
+ * entry points.
+ *
+ * Relocation is only performed the first time.
+ */
+.macro FIXUP_GOT
+	cmpb	$1, got_fixed(%rip)
+	je	2f
+
+	leaq	_got(%rip), %rdx
+	leaq	_egot(%rip), %rcx
+1:
+	cmpq	%rcx, %rdx
+	jae	2f
+	addq	%rbx, (%rdx)
+	addq	$8, %rdx
+	jmp	1b
+2:
+	movb	$1, got_fixed(%rip)
+.endm
+
 	__HEAD
 	.code32
 ENTRY(startup_32)
@@ -252,10 +279,13 @@ ENTRY(efi_pe_entry)
 	subq	$1b, %rbp
 
 	/*
-	 * Relocate efi_config->call().
+	 * Relocate efi_config->call() and the GOT entries.
 	 */
 	addq	%rbp, efi64_config+88(%rip)
 
+	movq	%rbp, %rbx
+	FIXUP_GOT
+
 	movq	%rax, %rdi
 	call	make_boot_params
 	cmpq	$0,%rax
@@ -271,10 +301,13 @@ handover_entry:
 	subq	$1b, %rbp
 
 	/*
-	 * Relocate efi_config->call().
+	 * Relocate efi_config->call() and the GOT entries.
 	 */
 	movq	efi_config(%rip), %rax
 	addq	%rbp, 88(%rax)
+
+	movq	%rbp, %rbx
+	FIXUP_GOT
 2:
 	movq	efi_config(%rip), %rdi
 	call	efi_main
@@ -385,19 +418,8 @@ relocated:
 	shrq	$3, %rcx
 	rep	stosq
 
-/*
- * Adjust our own GOT
- */
-	leaq	_got(%rip), %rdx
-	leaq	_egot(%rip), %rcx
-1:
-	cmpq	%rcx, %rdx
-	jae	2f
-	addq	%rbx, (%rdx)
-	addq	$8, %rdx
-	jmp	1b
-2:
-	
+	FIXUP_GOT
+
 /*
  * Do the decompression, and jump to the new kernel..
  */
@@ -437,6 +459,10 @@ gdt:
 	.quad   0x0000000000000000	/* TS continued */
 gdt_end:
 
+/* Have we relocated the GOT? */
+got_fixed:
+	.byte	0
+
 #ifdef CONFIG_EFI_STUB
 efi_config:
 	.quad	0

From 0ceac9e094b065fe3fec19669740f338d3480498 Mon Sep 17 00:00:00 2001
From: Mark Salter <msalter@redhat.com>
Date: Mon, 8 Sep 2014 13:01:08 -0400
Subject: [PATCH 3/3] efi/arm64: Fix fdt-related memory reservation

Commit 86c8b27a01cf:
 "arm64: ignore DT memreserve entries when booting in UEFI mode

prevents early_init_fdt_scan_reserved_mem() from being called for
arm64 kernels booting via UEFI. This was done because the kernel
will use the UEFI memory map to determine reserved memory regions.
That approach has problems in that early_init_fdt_scan_reserved_mem()
also reserves the FDT itself and any node-specific reserved memory.
By chance of some kernel configs, the FDT may be overwritten before
it can be unflattened and the kernel will fail to boot. More subtle
problems will result if the FDT has node specific reserved memory
which is not really reserved.

This patch has the UEFI stub remove the memory reserve map entries
from the FDT as it does with the memory nodes. This allows
early_init_fdt_scan_reserved_mem() to be called unconditionally
so that the other needed reservations are made.

Signed-off-by: Mark Salter <msalter@redhat.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
---
 arch/arm64/mm/init.c               |  3 +--
 drivers/firmware/efi/libstub/fdt.c | 10 +++++++++-
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 5472c24018766..a83061f37e431 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -149,8 +149,7 @@ void __init arm64_memblock_init(void)
 		memblock_reserve(__virt_to_phys(initrd_start), initrd_end - initrd_start);
 #endif
 
-	if (!efi_enabled(EFI_MEMMAP))
-		early_init_fdt_scan_reserved_mem();
+	early_init_fdt_scan_reserved_mem();
 
 	/* 4GB maximum for 32-bit only capable devices */
 	if (IS_ENABLED(CONFIG_ZONE_DMA))
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c
index a56bb35287553..c846a9608cbd7 100644
--- a/drivers/firmware/efi/libstub/fdt.c
+++ b/drivers/firmware/efi/libstub/fdt.c
@@ -22,7 +22,7 @@ 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)
 {
-	int node, prev;
+	int node, prev, num_rsv;
 	int status;
 	u32 fdt_val32;
 	u64 fdt_val64;
@@ -73,6 +73,14 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
 		prev = node;
 	}
 
+	/*
+	 * Delete all memory reserve map entries. When booting via UEFI,
+	 * kernel will use the UEFI memory map to find reserved regions.
+	 */
+	num_rsv = fdt_num_mem_rsv(fdt);
+	while (num_rsv-- > 0)
+		fdt_del_mem_rsv(fdt, num_rsv);
+
 	node = fdt_subnode_offset(fdt, 0, "chosen");
 	if (node < 0) {
 		node = fdt_add_subnode(fdt, 0, "chosen");