Skip to content

Commit 264b82f

Browse files
ardbiesheuvelbp3tk0v
authored andcommitted
x86/decompressor: Don't rely on upper 32 bits of GPRs being preserved
The 4-to-5 level mode switch trampoline disables long mode and paging in order to be able to flick the LA57 bit. According to section 3.4.1.1 of the x86 architecture manual [0], 64-bit GPRs might not retain the upper 32 bits of their contents across such a mode switch. Given that RBP, RBX and RSI are live at this point, preserve them on the stack, along with the return address that might be above 4G as well. [0] Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1: Basic Architecture "Because the upper 32 bits of 64-bit general-purpose registers are undefined in 32-bit modes, the upper 32 bits of any general-purpose register are not preserved when switching from 64-bit mode to a 32-bit mode (to protected mode or compatibility mode). Software must not depend on these bits to maintain a value after a 64-bit to 32-bit mode switch." Fixes: 194a974 ("x86/boot/compressed/64: Handle 5-level paging boot if kernel is above 4G") Signed-off-by: Ard Biesheuvel <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent bee6cf1 commit 264b82f

File tree

1 file changed

+23
-7
lines changed

1 file changed

+23
-7
lines changed

arch/x86/boot/compressed/head_64.S

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -459,18 +459,37 @@ SYM_CODE_START(startup_64)
459459
/* Save the trampoline address in RCX */
460460
movq %rax, %rcx
461461

462+
/* Set up 32-bit addressable stack */
463+
leaq TRAMPOLINE_32BIT_STACK_END(%rcx), %rsp
464+
465+
/*
466+
* Preserve live 64-bit registers on the stack: this is necessary
467+
* because the architecture does not guarantee that GPRs will retain
468+
* their full 64-bit values across a 32-bit mode switch.
469+
*/
470+
pushq %rbp
471+
pushq %rbx
472+
pushq %rsi
473+
462474
/*
463-
* Load the address of trampoline_return() into RDI.
464-
* It will be used by the trampoline to return to the main code.
475+
* Push the 64-bit address of trampoline_return() onto the new stack.
476+
* It will be used by the trampoline to return to the main code. Due to
477+
* the 32-bit mode switch, it cannot be kept it in a register either.
465478
*/
466479
leaq trampoline_return(%rip), %rdi
480+
pushq %rdi
467481

468482
/* Switch to compatibility mode (CS.L = 0 CS.D = 1) via far return */
469483
pushq $__KERNEL32_CS
470484
leaq TRAMPOLINE_32BIT_CODE_OFFSET(%rax), %rax
471485
pushq %rax
472486
lretq
473487
trampoline_return:
488+
/* Restore live 64-bit registers */
489+
popq %rsi
490+
popq %rbx
491+
popq %rbp
492+
474493
/* Restore the stack, the 32-bit trampoline uses its own stack */
475494
leaq rva(boot_stack_end)(%rbx), %rsp
476495

@@ -582,7 +601,7 @@ SYM_FUNC_END(.Lrelocated)
582601
/*
583602
* This is the 32-bit trampoline that will be copied over to low memory.
584603
*
585-
* RDI contains the return address (might be above 4G).
604+
* Return address is at the top of the stack (might be above 4G).
586605
* ECX contains the base address of the trampoline memory.
587606
* Non zero RDX means trampoline needs to enable 5-level paging.
588607
*/
@@ -592,9 +611,6 @@ SYM_CODE_START(trampoline_32bit_src)
592611
movl %eax, %ds
593612
movl %eax, %ss
594613

595-
/* Set up new stack */
596-
leal TRAMPOLINE_32BIT_STACK_END(%ecx), %esp
597-
598614
/* Disable paging */
599615
movl %cr0, %eax
600616
btrl $X86_CR0_PG_BIT, %eax
@@ -671,7 +687,7 @@ SYM_CODE_END(trampoline_32bit_src)
671687
.code64
672688
SYM_FUNC_START_LOCAL_NOALIGN(.Lpaging_enabled)
673689
/* Return from the trampoline */
674-
jmp *%rdi
690+
retq
675691
SYM_FUNC_END(.Lpaging_enabled)
676692

677693
/*

0 commit comments

Comments
 (0)