瀏覽代碼

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>
Matt Fleming 11 年之前
父節點
當前提交
9cb0e39423
共有 2 個文件被更改,包括 81 次插入29 次删除
  1. 40 14
      arch/x86/boot/compressed/head_32.S
  2. 41 15
      arch/x86/boot/compressed/head_64.S

+ 40 - 14
arch/x86/boot/compressed/head_32.S

@@ -30,6 +30,33 @@
 #include <asm/boot.h>
 #include <asm/boot.h>
 #include <asm/asm-offsets.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
 	__HEAD
 ENTRY(startup_32)
 ENTRY(startup_32)
 #ifdef CONFIG_EFI_STUB
 #ifdef CONFIG_EFI_STUB
@@ -56,6 +83,9 @@ ENTRY(efi_pe_entry)
 	add	%esi, 88(%eax)
 	add	%esi, 88(%eax)
 	pushl	%eax
 	pushl	%eax
 
 
+	movl	%esi, %ebx
+	FIXUP_GOT
+
 	call	make_boot_params
 	call	make_boot_params
 	cmpl	$0, %eax
 	cmpl	$0, %eax
 	je	fail
 	je	fail
@@ -81,6 +111,10 @@ ENTRY(efi32_stub_entry)
 	leal	efi32_config(%esi), %eax
 	leal	efi32_config(%esi), %eax
 	add	%esi, 88(%eax)
 	add	%esi, 88(%eax)
 	pushl	%eax
 	pushl	%eax
+
+	movl	%esi, %ebx
+	FIXUP_GOT
+
 2:
 2:
 	call	efi_main
 	call	efi_main
 	cmpl	$0, %eax
 	cmpl	$0, %eax
@@ -190,19 +224,7 @@ relocated:
 	shrl	$2, %ecx
 	shrl	$2, %ecx
 	rep	stosl
 	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..
  * Do the decompression, and jump to the new kernel..
  */
  */
@@ -225,8 +247,12 @@ relocated:
 	xorl	%ebx, %ebx
 	xorl	%ebx, %ebx
 	jmp	*%eax
 	jmp	*%eax
 
 
-#ifdef CONFIG_EFI_STUB
 	.data
 	.data
+/* Have we relocated the GOT? */
+got_fixed:
+	.byte 0
+
+#ifdef CONFIG_EFI_STUB
 efi32_config:
 efi32_config:
 	.fill 11,8,0
 	.fill 11,8,0
 	.long efi_call_phys
 	.long efi_call_phys

+ 41 - 15
arch/x86/boot/compressed/head_64.S

@@ -32,6 +32,33 @@
 #include <asm/processor-flags.h>
 #include <asm/processor-flags.h>
 #include <asm/asm-offsets.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
 	__HEAD
 	.code32
 	.code32
 ENTRY(startup_32)
 ENTRY(startup_32)
@@ -252,10 +279,13 @@ ENTRY(efi_pe_entry)
 	subq	$1b, %rbp
 	subq	$1b, %rbp
 
 
 	/*
 	/*
-	 * Relocate efi_config->call().
+	 * Relocate efi_config->call() and the GOT entries.
 	 */
 	 */
 	addq	%rbp, efi64_config+88(%rip)
 	addq	%rbp, efi64_config+88(%rip)
 
 
+	movq	%rbp, %rbx
+	FIXUP_GOT
+
 	movq	%rax, %rdi
 	movq	%rax, %rdi
 	call	make_boot_params
 	call	make_boot_params
 	cmpq	$0,%rax
 	cmpq	$0,%rax
@@ -271,10 +301,13 @@ handover_entry:
 	subq	$1b, %rbp
 	subq	$1b, %rbp
 
 
 	/*
 	/*
-	 * Relocate efi_config->call().
+	 * Relocate efi_config->call() and the GOT entries.
 	 */
 	 */
 	movq	efi_config(%rip), %rax
 	movq	efi_config(%rip), %rax
 	addq	%rbp, 88(%rax)
 	addq	%rbp, 88(%rax)
+
+	movq	%rbp, %rbx
+	FIXUP_GOT
 2:
 2:
 	movq	efi_config(%rip), %rdi
 	movq	efi_config(%rip), %rdi
 	call	efi_main
 	call	efi_main
@@ -385,19 +418,8 @@ relocated:
 	shrq	$3, %rcx
 	shrq	$3, %rcx
 	rep	stosq
 	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..
  * Do the decompression, and jump to the new kernel..
  */
  */
@@ -437,6 +459,10 @@ gdt:
 	.quad   0x0000000000000000	/* TS continued */
 	.quad   0x0000000000000000	/* TS continued */
 gdt_end:
 gdt_end:
 
 
+/* Have we relocated the GOT? */
+got_fixed:
+	.byte	0
+
 #ifdef CONFIG_EFI_STUB
 #ifdef CONFIG_EFI_STUB
 efi_config:
 efi_config:
 	.quad	0
 	.quad	0