Skip to content

Commit 34bbb00

Browse files
kirylIngo Molnar
authored andcommitted
x86/boot/compressed: Enable 5-level paging during decompression stage
We need to cover two basic cases: when bootloader left us in 32-bit mode and when bootloader enabled long mode. The patch implements unified codepath to enabled 5-level paging for both cases. It means case when we start in 32-bit mode, we first enable long mode with 4-level and then switch over to 5-level paging. Switching from 4-level to 5-level paging is not trivial. We cannot do it directly. Setting LA57 in long mode would trigger #GP. So we need to switch off long mode first and the then re-enable with 5-level paging. NOTE: The need of switching off long mode means we are in trouble if bootloader put us above 4G boundary. If bootloader wants to boot 5-level paging kernel, it has to put kernel below 4G or enable 5-level paging on it's own, so we could avoid the step. Signed-off-by: Kirill A. Shutemov <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Brian Gerst <[email protected]> Cc: Dave Hansen <[email protected]> Cc: Denys Vlasenko <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Josh Poimboeuf <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: [email protected] Cc: [email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 919a02d commit 34bbb00

File tree

1 file changed

+85
-1
lines changed

1 file changed

+85
-1
lines changed

arch/x86/boot/compressed/head_64.S

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,48 @@ preferred_addr:
346346
/* Set up the stack */
347347
leaq boot_stack_end(%rbx), %rsp
348348

349+
#ifdef CONFIG_X86_5LEVEL
350+
/* Check if 5-level paging has already enabled */
351+
movq %cr4, %rax
352+
testl $X86_CR4_LA57, %eax
353+
jnz lvl5
354+
355+
/*
356+
* At this point we are in long mode with 4-level paging enabled,
357+
* but we want to enable 5-level paging.
358+
*
359+
* The problem is that we cannot do it directly. Setting LA57 in
360+
* long mode would trigger #GP. So we need to switch off long mode
361+
* first.
362+
*
363+
* NOTE: This is not going to work if bootloader put us above 4G
364+
* limit.
365+
*
366+
* The first step is go into compatibility mode.
367+
*/
368+
369+
/* Clear additional page table */
370+
leaq lvl5_pgtable(%rbx), %rdi
371+
xorq %rax, %rax
372+
movq $(PAGE_SIZE/8), %rcx
373+
rep stosq
374+
375+
/*
376+
* Setup current CR3 as the first and only entry in a new top level
377+
* page table.
378+
*/
379+
movq %cr3, %rdi
380+
leaq 0x7 (%rdi), %rax
381+
movq %rax, lvl5_pgtable(%rbx)
382+
383+
/* Switch to compatibility mode (CS.L = 0 CS.D = 1) via far return */
384+
pushq $__KERNEL32_CS
385+
leaq compatible_mode(%rip), %rax
386+
pushq %rax
387+
lretq
388+
lvl5:
389+
#endif
390+
349391
/* Zero EFLAGS */
350392
pushq $0
351393
popfq
@@ -429,6 +471,44 @@ relocated:
429471
jmp *%rax
430472

431473
.code32
474+
#ifdef CONFIG_X86_5LEVEL
475+
compatible_mode:
476+
/* Setup data and stack segments */
477+
movl $__KERNEL_DS, %eax
478+
movl %eax, %ds
479+
movl %eax, %ss
480+
481+
/* Disable paging */
482+
movl %cr0, %eax
483+
btrl $X86_CR0_PG_BIT, %eax
484+
movl %eax, %cr0
485+
486+
/* Point CR3 to 5-level paging */
487+
leal lvl5_pgtable(%ebx), %eax
488+
movl %eax, %cr3
489+
490+
/* Enable PAE and LA57 mode */
491+
movl %cr4, %eax
492+
orl $(X86_CR4_PAE | X86_CR4_LA57), %eax
493+
movl %eax, %cr4
494+
495+
/* Calculate address we are running at */
496+
call 1f
497+
1: popl %edi
498+
subl $1b, %edi
499+
500+
/* Prepare stack for far return to Long Mode */
501+
pushl $__KERNEL_CS
502+
leal lvl5(%edi), %eax
503+
push %eax
504+
505+
/* Enable paging back */
506+
movl $(X86_CR0_PG | X86_CR0_PE), %eax
507+
movl %eax, %cr0
508+
509+
lret
510+
#endif
511+
432512
no_longmode:
433513
/* This isn't an x86-64 CPU so hang */
434514
1:
@@ -442,7 +522,7 @@ gdt:
442522
.word gdt_end - gdt
443523
.long gdt
444524
.word 0
445-
.quad 0x0000000000000000 /* NULL descriptor */
525+
.quad 0x00cf9a000000ffff /* __KERNEL32_CS */
446526
.quad 0x00af9a000000ffff /* __KERNEL_CS */
447527
.quad 0x00cf92000000ffff /* __KERNEL_DS */
448528
.quad 0x0080890000000000 /* TS descriptor */
@@ -486,3 +566,7 @@ boot_stack_end:
486566
.balign 4096
487567
pgtable:
488568
.fill BOOT_PGT_SIZE, 1, 0
569+
#ifdef CONFIG_X86_5LEVEL
570+
lvl5_pgtable:
571+
.fill PAGE_SIZE, 1, 0
572+
#endif

0 commit comments

Comments
 (0)