Skip to content

Commit

Permalink
x86/boot/compressed/64: Set up GOT for paging_prepare() and cleanup_t…
Browse files Browse the repository at this point in the history
…rampoline()

Eric and Hugh have reported instant reboot due to my recent changes in
decompression code.

The root cause is that I didn't realize that we need to adjust GOT to be
able to run C code that early.

The problem is only visible with an older toolchain. Binutils >= 2.24 is
able to eliminate GOT references by replacing them with RIP-relative
address loads:

  https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=80d873266dec

We need to adjust GOT two times:

 - before calling paging_prepare() using the initial load address
 - before calling C code from the relocated kernel

Reported-by: Eric Dumazet <eric.dumazet@gmail.com>
Reported-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Fixes: 194a974 ("x86/boot/compressed/64: Handle 5-level paging boot if kernel is above 4G")
Link: http://lkml.kernel.org/r/20180516080131.27913-2-kirill.shutemov@linux.intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
  • Loading branch information
Kirill A. Shutemov authored and Ingo Molnar committed May 16, 2018
1 parent 2fa9d1c commit 5c9b0b1
Showing 1 changed file with 55 additions and 13 deletions.
68 changes: 55 additions & 13 deletions arch/x86/boot/compressed/head_64.S
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,25 @@ ENTRY(startup_64)
/* Set up the stack */
leaq boot_stack_end(%rbx), %rsp

/*
* paging_prepare() and cleanup_trampoline() below can have GOT
* references. Adjust the table with address we are running at.
*
* Zero RAX for adjust_got: the GOT was not adjusted before;
* there's no adjustment to undo.
*/
xorq %rax, %rax

/*
* Calculate the address the binary is loaded at and use it as
* a GOT adjustment.
*/
call 1f
1: popq %rdi
subq $1b, %rdi

call adjust_got

/*
* At this point we are in long mode with 4-level paging enabled,
* but we might want to enable 5-level paging or vice versa.
Expand Down Expand Up @@ -381,6 +400,21 @@ trampoline_return:
pushq $0
popfq

/*
* Previously we've adjusted the GOT with address the binary was
* loaded at. Now we need to re-adjust for relocation address.
*
* Calculate the address the binary is loaded at, so that we can
* undo the previous GOT adjustment.
*/
call 1f
1: popq %rax
subq $1b, %rax

/* The new adjustment is the relocation address */
movq %rbx, %rdi
call adjust_got

/*
* Copy the compressed kernel to the end of our buffer
* where decompression in place becomes safe.
Expand Down Expand Up @@ -481,19 +515,6 @@ 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:

/*
* Do the extraction, and jump to the new kernel..
*/
Expand All @@ -512,6 +533,27 @@ relocated:
*/
jmp *%rax

/*
* Adjust the global offset table
*
* RAX is the previous adjustment of the table to undo (use 0 if it's the
* first time we touch GOT).
* RDI is the new adjustment to apply.
*/
adjust_got:
/* Walk through the GOT adding the address to the entries */
leaq _got(%rip), %rdx
leaq _egot(%rip), %rcx
1:
cmpq %rcx, %rdx
jae 2f
subq %rax, (%rdx) /* Undo previous adjustment */
addq %rdi, (%rdx) /* Apply the new adjustment */
addq $8, %rdx
jmp 1b
2:
ret

.code32
/*
* This is the 32-bit trampoline that will be copied over to low memory.
Expand Down

0 comments on commit 5c9b0b1

Please sign in to comment.